1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* cppc.c: CPPC Interface for x86
* Copyright (c) 2016, Intel Corporation.
*/
#include <acpi/cppc_acpi.h>
#include <asm/msr.h>
#include <asm/processor.h>
#include <asm/topology.h>
/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */
bool cpc_supported_by_cpu(void)
{
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
case X86_VENDOR_HYGON:
if (boot_cpu_data.x86 == 0x19 && ((boot_cpu_data.x86_model <= 0x0f) ||
(boot_cpu_data.x86_model >= 0x20 && boot_cpu_data.x86_model <= 0x2f)))
return true;
else if (boot_cpu_data.x86 == 0x17 &&
boot_cpu_data.x86_model >= 0x30 && boot_cpu_data.x86_model <= 0x7f)
return true;
return boot_cpu_has(X86_FEATURE_CPPC);
}
return false;
}
bool cpc_ffh_supported(void)
{
return true;
}
int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
{
int err;
err = rdmsrl_safe_on_cpu(cpunum, reg->address, val);
if (!err) {
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
reg->bit_offset);
*val &= mask;
*val >>= reg->bit_offset;
}
return err;
}
int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
{
u64 rd_val;
int err;
err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val);
if (!err) {
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
reg->bit_offset);
val <<= reg->bit_offset;
val &= mask;
rd_val &= ~mask;
rd_val |= val;
err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val);
}
return err;
}
static void amd_set_max_freq_ratio(void)
{
struct cppc_perf_caps perf_caps;
u64 numerator, nominal_perf;
u64 perf_ratio;
int rc;
rc = cppc_get_perf_caps(0, &perf_caps);
if (rc) {
pr_warn("Could not retrieve perf counters (%d)\n", rc);
return;
}
rc = amd_get_boost_ratio_numerator(0, &numerator);
if (rc) {
pr_warn("Could not retrieve highest performance (%d)\n", rc);
return;
}
nominal_perf = perf_caps.nominal_perf;
if (!nominal_perf) {
pr_warn("Could not retrieve nominal performance\n");
return;
}
/* midpoint between max_boost and max_P */
perf_ratio = (div_u64(numerator * SCHED_CAPACITY_SCALE, nominal_perf) + SCHED_CAPACITY_SCALE) >> 1;
freq_invariance_set_perf_ratio(perf_ratio, false);
}
static DEFINE_MUTEX(freq_invariance_lock);
void init_freq_invariance_cppc(void)
{
static bool init_done;
if (!cpu_feature_enabled(X86_FEATURE_APERFMPERF))
return;
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
return;
mutex_lock(&freq_invariance_lock);
if (!init_done)
amd_set_max_freq_ratio();
init_done = true;
mutex_unlock(&freq_invariance_lock);
}
/*
* Get the highest performance register value.
* @cpu: CPU from which to get highest performance.
* @highest_perf: Return address for highest performance value.
*
* Return: 0 for success, negative error code otherwise.
*/
int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
{
u64 val;
int ret;
if (cpu_feature_enabled(X86_FEATURE_CPPC)) {
ret = rdmsrl_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &val);
if (ret)
goto out;
val = AMD_CPPC_HIGHEST_PERF(val);
} else {
ret = cppc_get_highest_perf(cpu, &val);
if (ret)
goto out;
}
WRITE_ONCE(*highest_perf, (u32)val);
out:
return ret;
}
EXPORT_SYMBOL_GPL(amd_get_highest_perf);
/**
* amd_get_boost_ratio_numerator: Get the numerator to use for boost ratio calculation
* @cpu: CPU to get numerator for.
* @numerator: Output variable for numerator.
*
* Determine the numerator to use for calculating the boost ratio on
* a CPU. On systems that support preferred cores, this will be a hardcoded
* value. On other systems this will the highest performance register value.
*
* Return: 0 for success, negative error code otherwise.
*/
int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
if (c->x86 == 0x17 && ((c->x86_model >= 0x30 && c->x86_model < 0x40) ||
(c->x86_model >= 0x70 && c->x86_model < 0x80))) {
*numerator = 166;
return 0;
}
if (c->x86 == 0x19 && ((c->x86_model >= 0x20 && c->x86_model < 0x30) ||
(c->x86_model >= 0x40 && c->x86_model < 0x70))) {
*numerator = 166;
return 0;
}
*numerator = 255;
return 0;
}
EXPORT_SYMBOL_GPL(amd_get_boost_ratio_numerator);
|