Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/kernel/acpi/cppc.c
26489 views
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
17
enum amd_pref_core {
18
AMD_PREF_CORE_UNKNOWN = 0,
19
AMD_PREF_CORE_SUPPORTED,
20
AMD_PREF_CORE_UNSUPPORTED,
21
};
22
static enum amd_pref_core amd_pref_core_detected;
23
static u64 boost_numerator;
24
25
/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */
26
27
bool 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
43
bool cpc_ffh_supported(void)
44
{
45
return true;
46
}
47
48
int 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
63
int 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
82
static 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_warn("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_warn("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_warn("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
113
static DEFINE_MUTEX(freq_invariance_lock);
114
115
static 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
132
void 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
*/
144
int 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);
162
out:
163
return ret;
164
}
165
EXPORT_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
*/
180
int 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_present_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
}
225
EXPORT_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
*/
242
int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
243
{
244
enum x86_topology_cpu_type core_type = get_topology_cpu_type(&cpu_data(cpu));
245
bool prefcore;
246
int ret;
247
u32 tmp;
248
249
ret = amd_detect_prefcore(&prefcore);
250
if (ret)
251
return ret;
252
253
/* without preferred cores, return the highest perf register value */
254
if (!prefcore) {
255
*numerator = boost_numerator;
256
return 0;
257
}
258
259
/*
260
* For AMD CPUs with Family ID 19H and Model ID range 0x70 to 0x7f,
261
* the highest performance level is set to 196.
262
* https://bugzilla.kernel.org/show_bug.cgi?id=218759
263
*/
264
if (cpu_feature_enabled(X86_FEATURE_ZEN4)) {
265
switch (boot_cpu_data.x86_model) {
266
case 0x70 ... 0x7f:
267
*numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
268
return 0;
269
default:
270
break;
271
}
272
}
273
274
/* detect if running on heterogeneous design */
275
if (cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES)) {
276
switch (core_type) {
277
case TOPO_CPU_TYPE_UNKNOWN:
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_EFFICIENCY:
285
/* use the highest perf value for efficiency cores */
286
ret = amd_get_highest_perf(cpu, &tmp);
287
if (ret)
288
return ret;
289
*numerator = tmp;
290
return 0;
291
}
292
}
293
294
*numerator = CPPC_HIGHEST_PERF_PREFCORE;
295
296
return 0;
297
}
298
EXPORT_SYMBOL_GPL(amd_get_boost_ratio_numerator);
299
300