uncore-frequency.c (6580B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Intel Uncore Frequency Setting 4 * Copyright (c) 2022, Intel Corporation. 5 * All rights reserved. 6 * 7 * Provide interface to set MSR 620 at a granularity of per die. On CPU online, 8 * one control CPU is identified per die to read/write limit. This control CPU 9 * is changed, if the CPU state is changed to offline. When the last CPU is 10 * offline in a die then remove the sysfs object for that die. 11 * The majority of actual code is related to sysfs create and read/write 12 * attributes. 13 * 14 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 15 */ 16 17#include <linux/cpu.h> 18#include <linux/module.h> 19#include <linux/slab.h> 20#include <linux/suspend.h> 21#include <asm/cpu_device_id.h> 22#include <asm/intel-family.h> 23 24#include "uncore-frequency-common.h" 25 26/* Max instances for uncore data, one for each die */ 27static int uncore_max_entries __read_mostly; 28/* Storage for uncore data for all instances */ 29static struct uncore_data *uncore_instances; 30/* Stores the CPU mask of the target CPUs to use during uncore read/write */ 31static cpumask_t uncore_cpu_mask; 32/* CPU online callback register instance */ 33static enum cpuhp_state uncore_hp_state __read_mostly; 34 35#define MSR_UNCORE_RATIO_LIMIT 0x620 36#define MSR_UNCORE_PERF_STATUS 0x621 37#define UNCORE_FREQ_KHZ_MULTIPLIER 100000 38 39static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min, 40 unsigned int *max) 41{ 42 u64 cap; 43 int ret; 44 45 if (data->control_cpu < 0) 46 return -ENXIO; 47 48 ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap); 49 if (ret) 50 return ret; 51 52 *max = (cap & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER; 53 *min = ((cap & GENMASK(14, 8)) >> 8) * UNCORE_FREQ_KHZ_MULTIPLIER; 54 55 return 0; 56} 57 58static int uncore_write_control_freq(struct uncore_data *data, unsigned int input, 59 unsigned int min_max) 60{ 61 int ret; 62 u64 cap; 63 64 input /= UNCORE_FREQ_KHZ_MULTIPLIER; 65 if (!input || input > 0x7F) 66 return -EINVAL; 67 68 if (data->control_cpu < 0) 69 return -ENXIO; 70 71 ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap); 72 if (ret) 73 return ret; 74 75 if (min_max) { 76 cap &= ~0x7F; 77 cap |= input; 78 } else { 79 cap &= ~GENMASK(14, 8); 80 cap |= (input << 8); 81 } 82 83 ret = wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, cap); 84 if (ret) 85 return ret; 86 87 data->stored_uncore_data = cap; 88 89 return 0; 90} 91 92static int uncore_read_freq(struct uncore_data *data, unsigned int *freq) 93{ 94 u64 ratio; 95 int ret; 96 97 if (data->control_cpu < 0) 98 return -ENXIO; 99 100 ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_PERF_STATUS, &ratio); 101 if (ret) 102 return ret; 103 104 *freq = (ratio & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER; 105 106 return 0; 107} 108 109/* Caller provides protection */ 110static struct uncore_data *uncore_get_instance(unsigned int cpu) 111{ 112 int id = topology_logical_die_id(cpu); 113 114 if (id >= 0 && id < uncore_max_entries) 115 return &uncore_instances[id]; 116 117 return NULL; 118} 119 120static int uncore_event_cpu_online(unsigned int cpu) 121{ 122 struct uncore_data *data; 123 int target; 124 125 /* Check if there is an online cpu in the package for uncore MSR */ 126 target = cpumask_any_and(&uncore_cpu_mask, topology_die_cpumask(cpu)); 127 if (target < nr_cpu_ids) 128 return 0; 129 130 /* Use this CPU on this die as a control CPU */ 131 cpumask_set_cpu(cpu, &uncore_cpu_mask); 132 133 data = uncore_get_instance(cpu); 134 if (!data) 135 return 0; 136 137 data->package_id = topology_physical_package_id(cpu); 138 data->die_id = topology_die_id(cpu); 139 140 return uncore_freq_add_entry(data, cpu); 141} 142 143static int uncore_event_cpu_offline(unsigned int cpu) 144{ 145 struct uncore_data *data; 146 int target; 147 148 data = uncore_get_instance(cpu); 149 if (!data) 150 return 0; 151 152 /* Check if existing cpu is used for uncore MSRs */ 153 if (!cpumask_test_and_clear_cpu(cpu, &uncore_cpu_mask)) 154 return 0; 155 156 /* Find a new cpu to set uncore MSR */ 157 target = cpumask_any_but(topology_die_cpumask(cpu), cpu); 158 159 if (target < nr_cpu_ids) { 160 cpumask_set_cpu(target, &uncore_cpu_mask); 161 uncore_freq_add_entry(data, target); 162 } else { 163 uncore_freq_remove_die_entry(data); 164 } 165 166 return 0; 167} 168 169static int uncore_pm_notify(struct notifier_block *nb, unsigned long mode, 170 void *_unused) 171{ 172 int i; 173 174 switch (mode) { 175 case PM_POST_HIBERNATION: 176 case PM_POST_RESTORE: 177 case PM_POST_SUSPEND: 178 for (i = 0; i < uncore_max_entries; ++i) { 179 struct uncore_data *data = &uncore_instances[i]; 180 181 if (!data || !data->valid || !data->stored_uncore_data) 182 return 0; 183 184 wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, 185 data->stored_uncore_data); 186 } 187 break; 188 default: 189 break; 190 } 191 return 0; 192} 193 194static struct notifier_block uncore_pm_nb = { 195 .notifier_call = uncore_pm_notify, 196}; 197 198static const struct x86_cpu_id intel_uncore_cpu_ids[] = { 199 X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, NULL), 200 X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL), 201 X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, NULL), 202 X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL), 203 X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, NULL), 204 X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL), 205 X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL), 206 {} 207}; 208MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids); 209 210static int __init intel_uncore_init(void) 211{ 212 const struct x86_cpu_id *id; 213 int ret; 214 215 if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) 216 return -ENODEV; 217 218 id = x86_match_cpu(intel_uncore_cpu_ids); 219 if (!id) 220 return -ENODEV; 221 222 uncore_max_entries = topology_max_packages() * 223 topology_max_die_per_package(); 224 uncore_instances = kcalloc(uncore_max_entries, 225 sizeof(*uncore_instances), GFP_KERNEL); 226 if (!uncore_instances) 227 return -ENOMEM; 228 229 ret = uncore_freq_common_init(uncore_read_control_freq, uncore_write_control_freq, 230 uncore_read_freq); 231 if (ret) 232 goto err_free; 233 234 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, 235 "platform/x86/uncore-freq:online", 236 uncore_event_cpu_online, 237 uncore_event_cpu_offline); 238 if (ret < 0) 239 goto err_rem_kobj; 240 241 uncore_hp_state = ret; 242 243 ret = register_pm_notifier(&uncore_pm_nb); 244 if (ret) 245 goto err_rem_state; 246 247 return 0; 248 249err_rem_state: 250 cpuhp_remove_state(uncore_hp_state); 251err_rem_kobj: 252 uncore_freq_common_exit(); 253err_free: 254 kfree(uncore_instances); 255 256 return ret; 257} 258module_init(intel_uncore_init) 259 260static void __exit intel_uncore_exit(void) 261{ 262 int i; 263 264 unregister_pm_notifier(&uncore_pm_nb); 265 cpuhp_remove_state(uncore_hp_state); 266 for (i = 0; i < uncore_max_entries; ++i) 267 uncore_freq_remove_die_entry(&uncore_instances[i]); 268 uncore_freq_common_exit(); 269 kfree(uncore_instances); 270} 271module_exit(intel_uncore_exit) 272 273MODULE_IMPORT_NS(INTEL_UNCORE_FREQUENCY); 274MODULE_LICENSE("GPL v2"); 275MODULE_DESCRIPTION("Intel Uncore Frequency Limits Driver");