[RFC PATCH v2 3/5] driver: hwpf: Add support for Intel to hardware prefetch driver

From: Kohei Tarumizu
Date: Thu Nov 04 2021 - 01:22:28 EST


This adds module init/exit code, and creates sysfs attribute files for
"enable". It works on only INTEL_FAM6_BROADWELL_X.

Signed-off-by: Kohei Tarumizu <tarumizu.kohei@xxxxxxxxxxx>
---
drivers/hwpf/intel_hwpf.c | 219 ++++++++++++++++++++++++++++++++++++++
1 file changed, 219 insertions(+)
create mode 100644 drivers/hwpf/intel_hwpf.c

diff --git a/drivers/hwpf/intel_hwpf.c b/drivers/hwpf/intel_hwpf.c
new file mode 100644
index 000000000..391a58200
--- /dev/null
+++ b/drivers/hwpf/intel_hwpf.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0.
+/*
+ * Copyright 2021 FUJITSU LIMITED
+ *
+ * Intel Hardware Prefetch support
+ */
+
+#include <linux/bitfield.h>
+#include <linux/hwpf.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/intel-family.h>
+#include <asm/msr.h>
+
+enum cache_level {
+ CACHE_LEVEL_L1 = 0,
+ CACHE_LEVEL_L2,
+ CACHE_LEVEL_NOCACHE,
+};
+
+/*
+ * Classify the register specification of MSR_MISC_FEATURE_CONTROL depending on
+ * the target model.
+ *
+ * Note: This enum type is added for future extension. Currently, this driver
+ * supports only one TYPE. However, the register has several different
+ * specifications. For example, XEON_PHI series (06_57H, 06_85H) has different
+ * specification for each bits as follow:
+ * [0] DCU Hardware Prefetcher Disable (R/W)
+ * [1] L2 Hardware Prefetcher Disable (R/W)
+ * [63:2] Reserved
+ */
+enum register_type {
+ /*
+ * The register specification for each bits of REGISTER_TYPE_BROADWELL
+ * is as follow:
+ * [0] L2 Hardware Prefetcher Disable (R/W)
+ * [1] L2 Adjacent Cache Line Prefetcher Disable (R/W)
+ * [2] DCU Hardware Prefetcher Disable (R/W)
+ * [3] DCU IP Prefetcher Disable (R/W)
+ * [63:4] Reserved
+ */
+ REGISTER_TYPE_BROADWELL,
+ REGISTER_TYPE_NONE, /* Hardware prefetch is not supported */
+};
+
+#define TYPE_BROADWELL_L1_FIELD GENMASK_ULL(3, 2)
+#define TYPE_BROADWELL_L2_FIELD GENMASK_ULL(1, 0)
+
+static enum register_type hwpf_rtype;
+
+static unsigned int cache_leaves;
+
+static enum cache_level get_cache_level(unsigned int level)
+{
+ if (level >= cache_leaves)
+ return CACHE_LEVEL_NOCACHE;
+
+ return level;
+}
+
+/*
+ * Return: 0 if enabled, 1 if disabled, negative errno if an error occurs.
+ * If any bit in TYPE_XXX_FIELD is set to 1, it is considered disabled.
+ */
+static int get_enable(unsigned int cpu, unsigned int level)
+{
+ u64 reg;
+ int ret, val;
+ enum cache_level clevel;
+
+ clevel = get_cache_level(level);
+ if (clevel == CACHE_LEVEL_NOCACHE)
+ return -EINVAL;
+
+ ret = rdmsrl_on_cpu(cpu, MSR_MISC_FEATURE_CONTROL, &reg);
+ if (ret)
+ return ret;
+
+ switch (hwpf_rtype) {
+ case REGISTER_TYPE_BROADWELL:
+ switch (clevel) {
+ case CACHE_LEVEL_L1:
+ val = FIELD_GET(TYPE_BROADWELL_L1_FIELD, reg);
+ break;
+ case CACHE_LEVEL_L2:
+ val = FIELD_GET(TYPE_BROADWELL_L2_FIELD, reg);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (val > 0)
+ return 1;
+ else if (val == 0)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int set_enable(unsigned int cpu, unsigned int level, int val)
+{
+ int ret;
+ u64 reg;
+ enum cache_level clevel;
+
+ clevel = get_cache_level(level);
+ if (clevel == CACHE_LEVEL_NOCACHE)
+ return -EINVAL;
+
+ ret = rdmsrl_on_cpu(cpu, MSR_MISC_FEATURE_CONTROL, &reg);
+ if (ret)
+ return ret;
+
+ switch (hwpf_rtype) {
+ case REGISTER_TYPE_BROADWELL:
+ if (val == 1)
+ val = 0x3;
+ else if (val == 0)
+ val = 0x0;
+ else
+ return -EINVAL;
+
+ switch (clevel) {
+ case CACHE_LEVEL_L1:
+ reg &= ~TYPE_BROADWELL_L1_FIELD;
+ reg |= FIELD_PREP(TYPE_BROADWELL_L1_FIELD, val);
+ break;
+ case CACHE_LEVEL_L2:
+ reg &= ~TYPE_BROADWELL_L2_FIELD;
+ reg |= FIELD_PREP(TYPE_BROADWELL_L2_FIELD, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = wrmsrl_on_cpu(cpu, MSR_MISC_FEATURE_CONTROL, reg);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+
+struct hwpf_driver intel_hwpf_driver = {
+ .get_enable = get_enable,
+ .set_enable = set_enable,
+};
+
+enum register_type __init get_register_type(void)
+{
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
+ return REGISTER_TYPE_NONE;
+
+ switch (boot_cpu_data.x86_model) {
+ /*
+ * Note: In addition to BROADWELL, NEHALEM and others have same register
+ * specifications as REGISTER_TYPE_BROADWELL. If you want to add support
+ * for these processor, add the target model case here.
+ */
+ case INTEL_FAM6_BROADWELL_X:
+ return REGISTER_TYPE_BROADWELL;
+ default:
+ return REGISTER_TYPE_NONE;
+ }
+}
+
+static int __init setup_hwpf_driver_params(void)
+{
+ hwpf_rtype = get_register_type();
+
+ switch (hwpf_rtype) {
+ case REGISTER_TYPE_BROADWELL:
+ cache_leaves = 2;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ intel_hwpf_driver.cache_leaves = cache_leaves;
+
+ return 0;
+}
+
+static int __init hwpf_init(void)
+{
+ int ret;
+
+ ret = setup_hwpf_driver_params();
+ if (ret < 0)
+ return ret;
+
+ ret = hwpf_register_driver(&intel_hwpf_driver);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void __exit hwpf_exit(void)
+{
+ hwpf_unregister_driver(&intel_hwpf_driver);
+}
+
+module_init(hwpf_init);
+module_exit(hwpf_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("FUJITSU LIMITED");
+MODULE_DESCRIPTION("Intel Hardware Prefetch Driver");
--
2.27.0