platform/x86: intel-uncore-frequency: Move to intel sub-directory
authorKate Hsuan <hpa@redhat.com>
Fri, 20 Aug 2021 11:04:50 +0000 (14:04 +0300)
committerHans de Goede <hdegoede@redhat.com>
Fri, 20 Aug 2021 18:11:22 +0000 (20:11 +0200)
Move Intel Uncore frequency driver to intel sub-directory to improve
readability and rename it from intel-uncore-frequency.c to
uncore-frequency.c.

Signed-off-by: Kate Hsuan <hpa@redhat.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20210820110458.73018-13-andriy.shevchenko@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
MAINTAINERS
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/intel-uncore-frequency.c [deleted file]
drivers/platform/x86/intel/Kconfig
drivers/platform/x86/intel/Makefile
drivers/platform/x86/intel/uncore-frequency.c [new file with mode: 0644]

index bb0fcf3..5fe92c8 100644 (file)
@@ -9565,7 +9565,7 @@ INTEL UNCORE FREQUENCY CONTROL
 M:     Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
 L:     platform-driver-x86@vger.kernel.org
 S:     Maintained
-F:     drivers/platform/x86/intel-uncore-frequency.c
+F:     drivers/platform/x86/intel/uncore-frequency.c
 
 INTEL VIRTUAL BUTTON DRIVER
 M:     AceLan Kao <acelan.kao@canonical.com>
index a9acd44..dc861ec 100644 (file)
@@ -1112,17 +1112,6 @@ config INTEL_IPS
 
 source "drivers/platform/x86/intel_speed_select_if/Kconfig"
 
-config INTEL_UNCORE_FREQ_CONTROL
-       tristate "Intel Uncore frequency control driver"
-       depends on X86_64
-       help
-         This driver allows control of uncore frequency limits on
-         supported server platforms.
-         Uncore frequency controls RING/LLC (last-level cache) clocks.
-
-         To compile this driver as a module, choose M here: the module
-         will be called intel-uncore-frequency.
-
 config INTEL_SCU_IPC
        bool
 
index 15d0754..dfc5cde 100644 (file)
@@ -121,7 +121,6 @@ obj-$(CONFIG_WIRELESS_HOTKEY)               += wireless-hotkey.o
 # Intel uncore drivers
 obj-$(CONFIG_INTEL_IPS)                                += intel_ips.o
 obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE)     += intel_speed_select_if/
-obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL)                += intel-uncore-frequency.o
 
 # Intel PMIC / PMC / P-Unit devices
 obj-$(CONFIG_INTEL_SCU_IPC)            += intel_scu_ipc.o
diff --git a/drivers/platform/x86/intel-uncore-frequency.c b/drivers/platform/x86/intel-uncore-frequency.c
deleted file mode 100644 (file)
index 3ee4c5c..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Intel Uncore Frequency Setting
- * Copyright (c) 2019, Intel Corporation.
- * All rights reserved.
- *
- * Provide interface to set MSR 620 at a granularity of per die. On CPU online,
- * one control CPU is identified per die to read/write limit. This control CPU
- * is changed, if the CPU state is changed to offline. When the last CPU is
- * offline in a die then remove the sysfs object for that die.
- * The majority of actual code is related to sysfs create and read/write
- * attributes.
- *
- * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
- */
-
-#include <linux/cpu.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/suspend.h>
-#include <asm/cpu_device_id.h>
-#include <asm/intel-family.h>
-
-#define MSR_UNCORE_RATIO_LIMIT                 0x620
-#define UNCORE_FREQ_KHZ_MULTIPLIER             100000
-
-/**
- * struct uncore_data -        Encapsulate all uncore data
- * @stored_uncore_data:        Last user changed MSR 620 value, which will be restored
- *                     on system resume.
- * @initial_min_freq_khz: Sampled minimum uncore frequency at driver init
- * @initial_max_freq_khz: Sampled maximum uncore frequency at driver init
- * @control_cpu:       Designated CPU for a die to read/write
- * @valid:             Mark the data valid/invalid
- *
- * This structure is used to encapsulate all data related to uncore sysfs
- * settings for a die/package.
- */
-struct uncore_data {
-       struct kobject kobj;
-       struct completion kobj_unregister;
-       u64 stored_uncore_data;
-       u32 initial_min_freq_khz;
-       u32 initial_max_freq_khz;
-       int control_cpu;
-       bool valid;
-};
-
-#define to_uncore_data(a) container_of(a, struct uncore_data, kobj)
-
-/* Max instances for uncore data, one for each die */
-static int uncore_max_entries __read_mostly;
-/* Storage for uncore data for all instances */
-static struct uncore_data *uncore_instances;
-/* Root of the all uncore sysfs kobjs */
-static struct kobject *uncore_root_kobj;
-/* Stores the CPU mask of the target CPUs to use during uncore read/write */
-static cpumask_t uncore_cpu_mask;
-/* CPU online callback register instance */
-static enum cpuhp_state uncore_hp_state __read_mostly;
-/* Mutex to control all mutual exclusions */
-static DEFINE_MUTEX(uncore_lock);
-
-struct uncore_attr {
-       struct attribute attr;
-       ssize_t (*show)(struct kobject *kobj,
-                       struct attribute *attr, char *buf);
-       ssize_t (*store)(struct kobject *kobj,
-                        struct attribute *attr, const char *c, ssize_t count);
-};
-
-#define define_one_uncore_ro(_name) \
-static struct uncore_attr _name = \
-__ATTR(_name, 0444, show_##_name, NULL)
-
-#define define_one_uncore_rw(_name) \
-static struct uncore_attr _name = \
-__ATTR(_name, 0644, show_##_name, store_##_name)
-
-#define show_uncore_data(member_name)                                  \
-       static ssize_t show_##member_name(struct kobject *kobj,         \
-                                         struct attribute *attr,       \
-                                         char *buf)                    \
-       {                                                               \
-               struct uncore_data *data = to_uncore_data(kobj);        \
-               return scnprintf(buf, PAGE_SIZE, "%u\n",                \
-                                data->member_name);                    \
-       }                                                               \
-       define_one_uncore_ro(member_name)
-
-show_uncore_data(initial_min_freq_khz);
-show_uncore_data(initial_max_freq_khz);
-
-/* Common function to read MSR 0x620 and read min/max */
-static int uncore_read_ratio(struct uncore_data *data, unsigned int *min,
-                            unsigned int *max)
-{
-       u64 cap;
-       int ret;
-
-       if (data->control_cpu < 0)
-               return -ENXIO;
-
-       ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap);
-       if (ret)
-               return ret;
-
-       *max = (cap & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER;
-       *min = ((cap & GENMASK(14, 8)) >> 8) * UNCORE_FREQ_KHZ_MULTIPLIER;
-
-       return 0;
-}
-
-/* Common function to set min/max ratios to be used by sysfs callbacks */
-static int uncore_write_ratio(struct uncore_data *data, unsigned int input,
-                             int set_max)
-{
-       int ret;
-       u64 cap;
-
-       mutex_lock(&uncore_lock);
-
-       if (data->control_cpu < 0) {
-               ret = -ENXIO;
-               goto finish_write;
-       }
-
-       input /= UNCORE_FREQ_KHZ_MULTIPLIER;
-       if (!input || input > 0x7F) {
-               ret = -EINVAL;
-               goto finish_write;
-       }
-
-       ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap);
-       if (ret)
-               goto finish_write;
-
-       if (set_max) {
-               cap &= ~0x7F;
-               cap |= input;
-       } else  {
-               cap &= ~GENMASK(14, 8);
-               cap |= (input << 8);
-       }
-
-       ret = wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, cap);
-       if (ret)
-               goto finish_write;
-
-       data->stored_uncore_data = cap;
-
-finish_write:
-       mutex_unlock(&uncore_lock);
-
-       return ret;
-}
-
-static ssize_t store_min_max_freq_khz(struct kobject *kobj,
-                                     struct attribute *attr,
-                                     const char *buf, ssize_t count,
-                                     int min_max)
-{
-       struct uncore_data *data = to_uncore_data(kobj);
-       unsigned int input;
-
-       if (kstrtouint(buf, 10, &input))
-               return -EINVAL;
-
-       uncore_write_ratio(data, input, min_max);
-
-       return count;
-}
-
-static ssize_t show_min_max_freq_khz(struct kobject *kobj,
-                                    struct attribute *attr,
-                                    char *buf, int min_max)
-{
-       struct uncore_data *data = to_uncore_data(kobj);
-       unsigned int min, max;
-       int ret;
-
-       mutex_lock(&uncore_lock);
-       ret = uncore_read_ratio(data, &min, &max);
-       mutex_unlock(&uncore_lock);
-       if (ret)
-               return ret;
-
-       if (min_max)
-               return sprintf(buf, "%u\n", max);
-
-       return sprintf(buf, "%u\n", min);
-}
-
-#define store_uncore_min_max(name, min_max)                            \
-       static ssize_t store_##name(struct kobject *kobj,               \
-                                   struct attribute *attr,             \
-                                   const char *buf, ssize_t count)     \
-       {                                                               \
-                                                                       \
-               return store_min_max_freq_khz(kobj, attr, buf, count,   \
-                                             min_max);                 \
-       }
-
-#define show_uncore_min_max(name, min_max)                             \
-       static ssize_t show_##name(struct kobject *kobj,                \
-                                  struct attribute *attr, char *buf)   \
-       {                                                               \
-                                                                       \
-               return show_min_max_freq_khz(kobj, attr, buf, min_max); \
-       }
-
-store_uncore_min_max(min_freq_khz, 0);
-store_uncore_min_max(max_freq_khz, 1);
-
-show_uncore_min_max(min_freq_khz, 0);
-show_uncore_min_max(max_freq_khz, 1);
-
-define_one_uncore_rw(min_freq_khz);
-define_one_uncore_rw(max_freq_khz);
-
-static struct attribute *uncore_attrs[] = {
-       &initial_min_freq_khz.attr,
-       &initial_max_freq_khz.attr,
-       &max_freq_khz.attr,
-       &min_freq_khz.attr,
-       NULL
-};
-
-static void uncore_sysfs_entry_release(struct kobject *kobj)
-{
-       struct uncore_data *data = to_uncore_data(kobj);
-
-       complete(&data->kobj_unregister);
-}
-
-static struct kobj_type uncore_ktype = {
-       .release = uncore_sysfs_entry_release,
-       .sysfs_ops = &kobj_sysfs_ops,
-       .default_attrs = uncore_attrs,
-};
-
-/* Caller provides protection */
-static struct uncore_data *uncore_get_instance(unsigned int cpu)
-{
-       int id = topology_logical_die_id(cpu);
-
-       if (id >= 0 && id < uncore_max_entries)
-               return &uncore_instances[id];
-
-       return NULL;
-}
-
-static void uncore_add_die_entry(int cpu)
-{
-       struct uncore_data *data;
-
-       mutex_lock(&uncore_lock);
-       data = uncore_get_instance(cpu);
-       if (!data) {
-               mutex_unlock(&uncore_lock);
-               return;
-       }
-
-       if (data->valid) {
-               /* control cpu changed */
-               data->control_cpu = cpu;
-       } else {
-               char str[64];
-               int ret;
-
-               memset(data, 0, sizeof(*data));
-               sprintf(str, "package_%02d_die_%02d",
-                       topology_physical_package_id(cpu),
-                       topology_die_id(cpu));
-
-               uncore_read_ratio(data, &data->initial_min_freq_khz,
-                                 &data->initial_max_freq_khz);
-
-               init_completion(&data->kobj_unregister);
-
-               ret = kobject_init_and_add(&data->kobj, &uncore_ktype,
-                                          uncore_root_kobj, str);
-               if (!ret) {
-                       data->control_cpu = cpu;
-                       data->valid = true;
-               }
-       }
-       mutex_unlock(&uncore_lock);
-}
-
-/* Last CPU in this die is offline, make control cpu invalid */
-static void uncore_remove_die_entry(int cpu)
-{
-       struct uncore_data *data;
-
-       mutex_lock(&uncore_lock);
-       data = uncore_get_instance(cpu);
-       if (data)
-               data->control_cpu = -1;
-       mutex_unlock(&uncore_lock);
-}
-
-static int uncore_event_cpu_online(unsigned int cpu)
-{
-       int target;
-
-       /* Check if there is an online cpu in the package for uncore MSR */
-       target = cpumask_any_and(&uncore_cpu_mask, topology_die_cpumask(cpu));
-       if (target < nr_cpu_ids)
-               return 0;
-
-       /* Use this CPU on this die as a control CPU */
-       cpumask_set_cpu(cpu, &uncore_cpu_mask);
-       uncore_add_die_entry(cpu);
-
-       return 0;
-}
-
-static int uncore_event_cpu_offline(unsigned int cpu)
-{
-       int target;
-
-       /* Check if existing cpu is used for uncore MSRs */
-       if (!cpumask_test_and_clear_cpu(cpu, &uncore_cpu_mask))
-               return 0;
-
-       /* Find a new cpu to set uncore MSR */
-       target = cpumask_any_but(topology_die_cpumask(cpu), cpu);
-
-       if (target < nr_cpu_ids) {
-               cpumask_set_cpu(target, &uncore_cpu_mask);
-               uncore_add_die_entry(target);
-       } else {
-               uncore_remove_die_entry(cpu);
-       }
-
-       return 0;
-}
-
-static int uncore_pm_notify(struct notifier_block *nb, unsigned long mode,
-                           void *_unused)
-{
-       int cpu;
-
-       switch (mode) {
-       case PM_POST_HIBERNATION:
-       case PM_POST_RESTORE:
-       case PM_POST_SUSPEND:
-               for_each_cpu(cpu, &uncore_cpu_mask) {
-                       struct uncore_data *data;
-                       int ret;
-
-                       data = uncore_get_instance(cpu);
-                       if (!data || !data->valid || !data->stored_uncore_data)
-                               continue;
-
-                       ret = wrmsrl_on_cpu(cpu, MSR_UNCORE_RATIO_LIMIT,
-                                           data->stored_uncore_data);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       default:
-               break;
-       }
-       return 0;
-}
-
-static struct notifier_block uncore_pm_nb = {
-       .notifier_call = uncore_pm_notify,
-};
-
-static const struct x86_cpu_id intel_uncore_cpu_ids[] = {
-       X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, NULL),
-       X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL),
-       X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, NULL),
-       X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X,   NULL),
-       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X,   NULL),
-       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D,   NULL),
-       X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL),
-       {}
-};
-
-static int __init intel_uncore_init(void)
-{
-       const struct x86_cpu_id *id;
-       int ret;
-
-       id = x86_match_cpu(intel_uncore_cpu_ids);
-       if (!id)
-               return -ENODEV;
-
-       uncore_max_entries = topology_max_packages() *
-                                       topology_max_die_per_package();
-       uncore_instances = kcalloc(uncore_max_entries,
-                                  sizeof(*uncore_instances), GFP_KERNEL);
-       if (!uncore_instances)
-               return -ENOMEM;
-
-       uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
-                                                 &cpu_subsys.dev_root->kobj);
-       if (!uncore_root_kobj) {
-               ret = -ENOMEM;
-               goto err_free;
-       }
-
-       ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
-                               "platform/x86/uncore-freq:online",
-                               uncore_event_cpu_online,
-                               uncore_event_cpu_offline);
-       if (ret < 0)
-               goto err_rem_kobj;
-
-       uncore_hp_state = ret;
-
-       ret = register_pm_notifier(&uncore_pm_nb);
-       if (ret)
-               goto err_rem_state;
-
-       return 0;
-
-err_rem_state:
-       cpuhp_remove_state(uncore_hp_state);
-err_rem_kobj:
-       kobject_put(uncore_root_kobj);
-err_free:
-       kfree(uncore_instances);
-
-       return ret;
-}
-module_init(intel_uncore_init)
-
-static void __exit intel_uncore_exit(void)
-{
-       int i;
-
-       unregister_pm_notifier(&uncore_pm_nb);
-       cpuhp_remove_state(uncore_hp_state);
-       for (i = 0; i < uncore_max_entries; ++i) {
-               if (uncore_instances[i].valid) {
-                       kobject_put(&uncore_instances[i].kobj);
-                       wait_for_completion(&uncore_instances[i].kobj_unregister);
-               }
-       }
-       kobject_put(uncore_root_kobj);
-       kfree(uncore_instances);
-}
-module_exit(intel_uncore_exit)
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Intel Uncore Frequency Limits Driver");
index 7014793..7e3eb44 100644 (file)
@@ -98,4 +98,16 @@ config INTEL_TURBO_MAX_3
          This driver is only required when the system is not using Hardware
          P-States (HWP). In HWP mode, priority can be read from ACPI tables.
 
+config INTEL_UNCORE_FREQ_CONTROL
+       tristate "Intel Uncore frequency control driver"
+       depends on X86_64
+       help
+         This driver allows control of Uncore frequency limits on
+         supported server platforms.
+
+         Uncore frequency controls RING/LLC (last-level cache) clocks.
+
+         To compile this driver as a module, choose M here: the module
+         will be called intel-uncore-frequency.
+
 endif # X86_PLATFORM_DRIVERS_INTEL
index 1ecdf77..8080e85 100644 (file)
@@ -28,3 +28,5 @@ intel-smartconnect-y                  := smartconnect.o
 obj-$(CONFIG_INTEL_SMARTCONNECT)       += intel-smartconnect.o
 intel_turbo_max_3-y                    := turbo_max_3.o
 obj-$(CONFIG_INTEL_TURBO_MAX_3)                += intel_turbo_max_3.o
+intel-uncore-frequency-y               := uncore-frequency.o
+obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL)        += intel-uncore-frequency.o
diff --git a/drivers/platform/x86/intel/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency.c
new file mode 100644 (file)
index 0000000..3ee4c5c
--- /dev/null
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Uncore Frequency Setting
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Provide interface to set MSR 620 at a granularity of per die. On CPU online,
+ * one control CPU is identified per die to read/write limit. This control CPU
+ * is changed, if the CPU state is changed to offline. When the last CPU is
+ * offline in a die then remove the sysfs object for that die.
+ * The majority of actual code is related to sysfs create and read/write
+ * attributes.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+#define MSR_UNCORE_RATIO_LIMIT                 0x620
+#define UNCORE_FREQ_KHZ_MULTIPLIER             100000
+
+/**
+ * struct uncore_data -        Encapsulate all uncore data
+ * @stored_uncore_data:        Last user changed MSR 620 value, which will be restored
+ *                     on system resume.
+ * @initial_min_freq_khz: Sampled minimum uncore frequency at driver init
+ * @initial_max_freq_khz: Sampled maximum uncore frequency at driver init
+ * @control_cpu:       Designated CPU for a die to read/write
+ * @valid:             Mark the data valid/invalid
+ *
+ * This structure is used to encapsulate all data related to uncore sysfs
+ * settings for a die/package.
+ */
+struct uncore_data {
+       struct kobject kobj;
+       struct completion kobj_unregister;
+       u64 stored_uncore_data;
+       u32 initial_min_freq_khz;
+       u32 initial_max_freq_khz;
+       int control_cpu;
+       bool valid;
+};
+
+#define to_uncore_data(a) container_of(a, struct uncore_data, kobj)
+
+/* Max instances for uncore data, one for each die */
+static int uncore_max_entries __read_mostly;
+/* Storage for uncore data for all instances */
+static struct uncore_data *uncore_instances;
+/* Root of the all uncore sysfs kobjs */
+static struct kobject *uncore_root_kobj;
+/* Stores the CPU mask of the target CPUs to use during uncore read/write */
+static cpumask_t uncore_cpu_mask;
+/* CPU online callback register instance */
+static enum cpuhp_state uncore_hp_state __read_mostly;
+/* Mutex to control all mutual exclusions */
+static DEFINE_MUTEX(uncore_lock);
+
+struct uncore_attr {
+       struct attribute attr;
+       ssize_t (*show)(struct kobject *kobj,
+                       struct attribute *attr, char *buf);
+       ssize_t (*store)(struct kobject *kobj,
+                        struct attribute *attr, const char *c, ssize_t count);
+};
+
+#define define_one_uncore_ro(_name) \
+static struct uncore_attr _name = \
+__ATTR(_name, 0444, show_##_name, NULL)
+
+#define define_one_uncore_rw(_name) \
+static struct uncore_attr _name = \
+__ATTR(_name, 0644, show_##_name, store_##_name)
+
+#define show_uncore_data(member_name)                                  \
+       static ssize_t show_##member_name(struct kobject *kobj,         \
+                                         struct attribute *attr,       \
+                                         char *buf)                    \
+       {                                                               \
+               struct uncore_data *data = to_uncore_data(kobj);        \
+               return scnprintf(buf, PAGE_SIZE, "%u\n",                \
+                                data->member_name);                    \
+       }                                                               \
+       define_one_uncore_ro(member_name)
+
+show_uncore_data(initial_min_freq_khz);
+show_uncore_data(initial_max_freq_khz);
+
+/* Common function to read MSR 0x620 and read min/max */
+static int uncore_read_ratio(struct uncore_data *data, unsigned int *min,
+                            unsigned int *max)
+{
+       u64 cap;
+       int ret;
+
+       if (data->control_cpu < 0)
+               return -ENXIO;
+
+       ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap);
+       if (ret)
+               return ret;
+
+       *max = (cap & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER;
+       *min = ((cap & GENMASK(14, 8)) >> 8) * UNCORE_FREQ_KHZ_MULTIPLIER;
+
+       return 0;
+}
+
+/* Common function to set min/max ratios to be used by sysfs callbacks */
+static int uncore_write_ratio(struct uncore_data *data, unsigned int input,
+                             int set_max)
+{
+       int ret;
+       u64 cap;
+
+       mutex_lock(&uncore_lock);
+
+       if (data->control_cpu < 0) {
+               ret = -ENXIO;
+               goto finish_write;
+       }
+
+       input /= UNCORE_FREQ_KHZ_MULTIPLIER;
+       if (!input || input > 0x7F) {
+               ret = -EINVAL;
+               goto finish_write;
+       }
+
+       ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap);
+       if (ret)
+               goto finish_write;
+
+       if (set_max) {
+               cap &= ~0x7F;
+               cap |= input;
+       } else  {
+               cap &= ~GENMASK(14, 8);
+               cap |= (input << 8);
+       }
+
+       ret = wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, cap);
+       if (ret)
+               goto finish_write;
+
+       data->stored_uncore_data = cap;
+
+finish_write:
+       mutex_unlock(&uncore_lock);
+
+       return ret;
+}
+
+static ssize_t store_min_max_freq_khz(struct kobject *kobj,
+                                     struct attribute *attr,
+                                     const char *buf, ssize_t count,
+                                     int min_max)
+{
+       struct uncore_data *data = to_uncore_data(kobj);
+       unsigned int input;
+
+       if (kstrtouint(buf, 10, &input))
+               return -EINVAL;
+
+       uncore_write_ratio(data, input, min_max);
+
+       return count;
+}
+
+static ssize_t show_min_max_freq_khz(struct kobject *kobj,
+                                    struct attribute *attr,
+                                    char *buf, int min_max)
+{
+       struct uncore_data *data = to_uncore_data(kobj);
+       unsigned int min, max;
+       int ret;
+
+       mutex_lock(&uncore_lock);
+       ret = uncore_read_ratio(data, &min, &max);
+       mutex_unlock(&uncore_lock);
+       if (ret)
+               return ret;
+
+       if (min_max)
+               return sprintf(buf, "%u\n", max);
+
+       return sprintf(buf, "%u\n", min);
+}
+
+#define store_uncore_min_max(name, min_max)                            \
+       static ssize_t store_##name(struct kobject *kobj,               \
+                                   struct attribute *attr,             \
+                                   const char *buf, ssize_t count)     \
+       {                                                               \
+                                                                       \
+               return store_min_max_freq_khz(kobj, attr, buf, count,   \
+                                             min_max);                 \
+       }
+
+#define show_uncore_min_max(name, min_max)                             \
+       static ssize_t show_##name(struct kobject *kobj,                \
+                                  struct attribute *attr, char *buf)   \
+       {                                                               \
+                                                                       \
+               return show_min_max_freq_khz(kobj, attr, buf, min_max); \
+       }
+
+store_uncore_min_max(min_freq_khz, 0);
+store_uncore_min_max(max_freq_khz, 1);
+
+show_uncore_min_max(min_freq_khz, 0);
+show_uncore_min_max(max_freq_khz, 1);
+
+define_one_uncore_rw(min_freq_khz);
+define_one_uncore_rw(max_freq_khz);
+
+static struct attribute *uncore_attrs[] = {
+       &initial_min_freq_khz.attr,
+       &initial_max_freq_khz.attr,
+       &max_freq_khz.attr,
+       &min_freq_khz.attr,
+       NULL
+};
+
+static void uncore_sysfs_entry_release(struct kobject *kobj)
+{
+       struct uncore_data *data = to_uncore_data(kobj);
+
+       complete(&data->kobj_unregister);
+}
+
+static struct kobj_type uncore_ktype = {
+       .release = uncore_sysfs_entry_release,
+       .sysfs_ops = &kobj_sysfs_ops,
+       .default_attrs = uncore_attrs,
+};
+
+/* Caller provides protection */
+static struct uncore_data *uncore_get_instance(unsigned int cpu)
+{
+       int id = topology_logical_die_id(cpu);
+
+       if (id >= 0 && id < uncore_max_entries)
+               return &uncore_instances[id];
+
+       return NULL;
+}
+
+static void uncore_add_die_entry(int cpu)
+{
+       struct uncore_data *data;
+
+       mutex_lock(&uncore_lock);
+       data = uncore_get_instance(cpu);
+       if (!data) {
+               mutex_unlock(&uncore_lock);
+               return;
+       }
+
+       if (data->valid) {
+               /* control cpu changed */
+               data->control_cpu = cpu;
+       } else {
+               char str[64];
+               int ret;
+
+               memset(data, 0, sizeof(*data));
+               sprintf(str, "package_%02d_die_%02d",
+                       topology_physical_package_id(cpu),
+                       topology_die_id(cpu));
+
+               uncore_read_ratio(data, &data->initial_min_freq_khz,
+                                 &data->initial_max_freq_khz);
+
+               init_completion(&data->kobj_unregister);
+
+               ret = kobject_init_and_add(&data->kobj, &uncore_ktype,
+                                          uncore_root_kobj, str);
+               if (!ret) {
+                       data->control_cpu = cpu;
+                       data->valid = true;
+               }
+       }
+       mutex_unlock(&uncore_lock);
+}
+
+/* Last CPU in this die is offline, make control cpu invalid */
+static void uncore_remove_die_entry(int cpu)
+{
+       struct uncore_data *data;
+
+       mutex_lock(&uncore_lock);
+       data = uncore_get_instance(cpu);
+       if (data)
+               data->control_cpu = -1;
+       mutex_unlock(&uncore_lock);
+}
+
+static int uncore_event_cpu_online(unsigned int cpu)
+{
+       int target;
+
+       /* Check if there is an online cpu in the package for uncore MSR */
+       target = cpumask_any_and(&uncore_cpu_mask, topology_die_cpumask(cpu));
+       if (target < nr_cpu_ids)
+               return 0;
+
+       /* Use this CPU on this die as a control CPU */
+       cpumask_set_cpu(cpu, &uncore_cpu_mask);
+       uncore_add_die_entry(cpu);
+
+       return 0;
+}
+
+static int uncore_event_cpu_offline(unsigned int cpu)
+{
+       int target;
+
+       /* Check if existing cpu is used for uncore MSRs */
+       if (!cpumask_test_and_clear_cpu(cpu, &uncore_cpu_mask))
+               return 0;
+
+       /* Find a new cpu to set uncore MSR */
+       target = cpumask_any_but(topology_die_cpumask(cpu), cpu);
+
+       if (target < nr_cpu_ids) {
+               cpumask_set_cpu(target, &uncore_cpu_mask);
+               uncore_add_die_entry(target);
+       } else {
+               uncore_remove_die_entry(cpu);
+       }
+
+       return 0;
+}
+
+static int uncore_pm_notify(struct notifier_block *nb, unsigned long mode,
+                           void *_unused)
+{
+       int cpu;
+
+       switch (mode) {
+       case PM_POST_HIBERNATION:
+       case PM_POST_RESTORE:
+       case PM_POST_SUSPEND:
+               for_each_cpu(cpu, &uncore_cpu_mask) {
+                       struct uncore_data *data;
+                       int ret;
+
+                       data = uncore_get_instance(cpu);
+                       if (!data || !data->valid || !data->stored_uncore_data)
+                               continue;
+
+                       ret = wrmsrl_on_cpu(cpu, MSR_UNCORE_RATIO_LIMIT,
+                                           data->stored_uncore_data);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static struct notifier_block uncore_pm_nb = {
+       .notifier_call = uncore_pm_notify,
+};
+
+static const struct x86_cpu_id intel_uncore_cpu_ids[] = {
+       X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, NULL),
+       X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL),
+       X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, NULL),
+       X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X,   NULL),
+       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X,   NULL),
+       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D,   NULL),
+       X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL),
+       {}
+};
+
+static int __init intel_uncore_init(void)
+{
+       const struct x86_cpu_id *id;
+       int ret;
+
+       id = x86_match_cpu(intel_uncore_cpu_ids);
+       if (!id)
+               return -ENODEV;
+
+       uncore_max_entries = topology_max_packages() *
+                                       topology_max_die_per_package();
+       uncore_instances = kcalloc(uncore_max_entries,
+                                  sizeof(*uncore_instances), GFP_KERNEL);
+       if (!uncore_instances)
+               return -ENOMEM;
+
+       uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
+                                                 &cpu_subsys.dev_root->kobj);
+       if (!uncore_root_kobj) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
+       ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+                               "platform/x86/uncore-freq:online",
+                               uncore_event_cpu_online,
+                               uncore_event_cpu_offline);
+       if (ret < 0)
+               goto err_rem_kobj;
+
+       uncore_hp_state = ret;
+
+       ret = register_pm_notifier(&uncore_pm_nb);
+       if (ret)
+               goto err_rem_state;
+
+       return 0;
+
+err_rem_state:
+       cpuhp_remove_state(uncore_hp_state);
+err_rem_kobj:
+       kobject_put(uncore_root_kobj);
+err_free:
+       kfree(uncore_instances);
+
+       return ret;
+}
+module_init(intel_uncore_init)
+
+static void __exit intel_uncore_exit(void)
+{
+       int i;
+
+       unregister_pm_notifier(&uncore_pm_nb);
+       cpuhp_remove_state(uncore_hp_state);
+       for (i = 0; i < uncore_max_entries; ++i) {
+               if (uncore_instances[i].valid) {
+                       kobject_put(&uncore_instances[i].kobj);
+                       wait_for_completion(&uncore_instances[i].kobj_unregister);
+               }
+       }
+       kobject_put(uncore_root_kobj);
+       kfree(uncore_instances);
+}
+module_exit(intel_uncore_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Uncore Frequency Limits Driver");