projects/linux·blob master · refs

299 lines · 7.1 KiB

1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * cppc.c: CPPC Interface for x86
4 * Copyright (c) 2016, Intel Corporation.
5 */
6
7#include <linux/bitfield.h>
8
9#include <acpi/cppc_acpi.h>
10#include <asm/msr.h>
11#include <asm/processor.h>
12#include <asm/topology.h>
13
14#define CPPC_HIGHEST_PERF_PERFORMANCE 196
15#define CPPC_HIGHEST_PERF_PREFCORE 166
16
17enum amd_pref_core {
18 AMD_PREF_CORE_UNKNOWN = 0,
19 AMD_PREF_CORE_SUPPORTED,
20 AMD_PREF_CORE_UNSUPPORTED,
21};
22static enum amd_pref_core amd_pref_core_detected;
23static u64 boost_numerator;
24
25/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */
26
27bool cpc_supported_by_cpu(void)
28{
29 switch (boot_cpu_data.x86_vendor) {
30 case X86_VENDOR_AMD:
31 case X86_VENDOR_HYGON:
32 if (boot_cpu_data.x86 == 0x19 && ((boot_cpu_data.x86_model <= 0x0f) ||
33 (boot_cpu_data.x86_model >= 0x20 && boot_cpu_data.x86_model <= 0x2f)))
34 return true;
35 else if (boot_cpu_data.x86 == 0x17 &&
36 boot_cpu_data.x86_model >= 0x30 && boot_cpu_data.x86_model <= 0x7f)
37 return true;
38 return boot_cpu_has(X86_FEATURE_CPPC);
39 }
40 return false;
41}
42
43bool cpc_ffh_supported(void)
44{
45 return true;
46}
47
48int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
49{
50 int err;
51
52 err = rdmsrq_safe_on_cpu(cpunum, reg->address, val);
53 if (!err) {
54 u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
55 reg->bit_offset);
56
57 *val &= mask;
58 *val >>= reg->bit_offset;
59 }
60 return err;
61}
62
63int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
64{
65 u64 rd_val;
66 int err;
67
68 err = rdmsrq_safe_on_cpu(cpunum, reg->address, &rd_val);
69 if (!err) {
70 u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
71 reg->bit_offset);
72
73 val <<= reg->bit_offset;
74 val &= mask;
75 rd_val &= ~mask;
76 rd_val |= val;
77 err = wrmsrq_safe_on_cpu(cpunum, reg->address, rd_val);
78 }
79 return err;
80}
81
82static void amd_set_max_freq_ratio(void)
83{
84 struct cppc_perf_caps perf_caps;
85 u64 numerator, nominal_perf;
86 u64 perf_ratio;
87 int rc;
88
89 rc = cppc_get_perf_caps(0, &perf_caps);
90 if (rc) {
91 pr_debug("Could not retrieve perf counters (%d)\n", rc);
92 return;
93 }
94
95 rc = amd_get_boost_ratio_numerator(0, &numerator);
96 if (rc) {
97 pr_debug("Could not retrieve highest performance (%d)\n", rc);
98 return;
99 }
100 nominal_perf = perf_caps.nominal_perf;
101
102 if (!nominal_perf) {
103 pr_debug("Could not retrieve nominal performance\n");
104 return;
105 }
106
107 /* midpoint between max_boost and max_P */
108 perf_ratio = (div_u64(numerator * SCHED_CAPACITY_SCALE, nominal_perf) + SCHED_CAPACITY_SCALE) >> 1;
109
110 freq_invariance_set_perf_ratio(perf_ratio, false);
111}
112
113static DEFINE_MUTEX(freq_invariance_lock);
114
115static inline void init_freq_invariance_cppc(void)
116{
117 static bool init_done;
118
119 if (!cpu_feature_enabled(X86_FEATURE_APERFMPERF))
120 return;
121
122 if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
123 return;
124
125 mutex_lock(&freq_invariance_lock);
126 if (!init_done)
127 amd_set_max_freq_ratio();
128 init_done = true;
129 mutex_unlock(&freq_invariance_lock);
130}
131
132void acpi_processor_init_invariance_cppc(void)
133{
134 init_freq_invariance_cppc();
135}
136
137/*
138 * Get the highest performance register value.
139 * @cpu: CPU from which to get highest performance.
140 * @highest_perf: Return address for highest performance value.
141 *
142 * Return: 0 for success, negative error code otherwise.
143 */
144int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
145{
146 u64 val;
147 int ret;
148
149 if (cpu_feature_enabled(X86_FEATURE_CPPC)) {
150 ret = rdmsrq_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &val);
151 if (ret)
152 goto out;
153
154 val = FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, val);
155 } else {
156 ret = cppc_get_highest_perf(cpu, &val);
157 if (ret)
158 goto out;
159 }
160
161 WRITE_ONCE(*highest_perf, (u32)val);
162out:
163 return ret;
164}
165EXPORT_SYMBOL_GPL(amd_get_highest_perf);
166
167/**
168 * amd_detect_prefcore: Detect if CPUs in the system support preferred cores
169 * @detected: Output variable for the result of the detection.
170 *
171 * Determine whether CPUs in the system support preferred cores. On systems
172 * that support preferred cores, different highest perf values will be found
173 * on different cores. On other systems, the highest perf value will be the
174 * same on all cores.
175 *
176 * The result of the detection will be stored in the 'detected' parameter.
177 *
178 * Return: 0 for success, negative error code otherwise
179 */
180int amd_detect_prefcore(bool *detected)
181{
182 int cpu, count = 0;
183 u64 highest_perf[2] = {0};
184
185 if (WARN_ON(!detected))
186 return -EINVAL;
187
188 switch (amd_pref_core_detected) {
189 case AMD_PREF_CORE_SUPPORTED:
190 *detected = true;
191 return 0;
192 case AMD_PREF_CORE_UNSUPPORTED:
193 *detected = false;
194 return 0;
195 default:
196 break;
197 }
198
199 for_each_online_cpu(cpu) {
200 u32 tmp;
201 int ret;
202
203 ret = amd_get_highest_perf(cpu, &tmp);
204 if (ret)
205 return ret;
206
207 if (!count || (count == 1 && tmp != highest_perf[0]))
208 highest_perf[count++] = tmp;
209
210 if (count == 2)
211 break;
212 }
213
214 *detected = (count == 2);
215 boost_numerator = highest_perf[0];
216
217 amd_pref_core_detected = *detected ? AMD_PREF_CORE_SUPPORTED :
218 AMD_PREF_CORE_UNSUPPORTED;
219
220 pr_debug("AMD CPPC preferred core is %ssupported (highest perf: 0x%llx)\n",
221 *detected ? "" : "un", highest_perf[0]);
222
223 return 0;
224}
225EXPORT_SYMBOL_GPL(amd_detect_prefcore);
226
227/**
228 * amd_get_boost_ratio_numerator: Get the numerator to use for boost ratio calculation
229 * @cpu: CPU to get numerator for.
230 * @numerator: Output variable for numerator.
231 *
232 * Determine the numerator to use for calculating the boost ratio on
233 * a CPU. On systems that support preferred cores, this will be a hardcoded
234 * value. On other systems this will the highest performance register value.
235 *
236 * If booting the system with amd-pstate enabled but preferred cores disabled then
237 * the correct boost numerator will be returned to match hardware capabilities
238 * even if the preferred cores scheduling hints are not enabled.
239 *
240 * Return: 0 for success, negative error code otherwise.
241 */
242int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
243{
244 bool prefcore;
245 int ret;
246 u32 tmp;
247
248 ret = amd_detect_prefcore(&prefcore);
249 if (ret)
250 return ret;
251
252 /* without preferred cores, return the highest perf register value */
253 if (!prefcore) {
254 *numerator = boost_numerator;
255 return 0;
256 }
257
258 /*
259 * For AMD CPUs with Family ID 19H and Model ID range 0x70 to 0x7f,
260 * the highest performance level is set to 196.
261 * https://bugzilla.kernel.org/show_bug.cgi?id=218759
262 */
263 if (cpu_feature_enabled(X86_FEATURE_ZEN4)) {
264 switch (boot_cpu_data.x86_model) {
265 case 0x70 ... 0x7f:
266 *numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
267 return 0;
268 default:
269 break;
270 }
271 }
272
273 /* detect if running on heterogeneous design */
274 if (cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES)) {
275 switch (cpu_data(cpu).topo.cpu_type) {
276 case TOPO_CPU_TYPE_UNKNOWN:
277 case TOPO_CPU_TYPE_ANY:
278 pr_warn("Undefined core type found for cpu %d\n", cpu);
279 break;
280 case TOPO_CPU_TYPE_PERFORMANCE:
281 /* use the max scale for performance cores */
282 *numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
283 return 0;
284 case TOPO_CPU_TYPE_LOW_POWER:
285 case TOPO_CPU_TYPE_EFFICIENCY:
286 /* use the highest perf value for efficiency and low-power cores */
287 ret = amd_get_highest_perf(cpu, &tmp);
288 if (ret)
289 return ret;
290 *numerator = tmp;
291 return 0;
292 }
293 }
294
295 *numerator = CPPC_HIGHEST_PERF_PREFCORE;
296
297 return 0;
298}
299EXPORT_SYMBOL_GPL(amd_get_boost_ratio_numerator);