1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * AMD Processor P-state Frequency Driver Unit Test
4 *
5 * Copyright (C) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
6 *
7 * Author: Meng Li <[email protected]>
8 *
9 * The AMD P-State Unit Test is a test module for testing the amd-pstate
10 * driver. 1) It can help all users to verify their processor support
11 * (SBIOS/Firmware or Hardware). 2) Kernel can have a basic function
12 * test to avoid the kernel regression during the update. 3) We can
13 * introduce more functional or performance tests to align the result
14 * together, it will benefit power and performance scale optimization.
15 *
16 * This driver implements basic framework with plans to enhance it with
17 * additional test cases to improve the depth and coverage of the test.
18 *
19 * See Documentation/admin-guide/pm/amd-pstate.rst Unit Tests for
20 * amd-pstate to get more detail.
21 */
22
23 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
25 #include <linux/bitfield.h>
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/moduleparam.h>
29 #include <linux/fs.h>
30 #include <linux/cleanup.h>
31
32 #include <acpi/cppc_acpi.h>
33
34 #include "amd-pstate.h"
35
36
37 struct amd_pstate_ut_struct {
38 const char *name;
39 int (*func)(u32 index);
40 };
41
42 /*
43 * Kernel module for testing the AMD P-State unit test
44 */
45 static int amd_pstate_ut_acpi_cpc_valid(u32 index);
46 static int amd_pstate_ut_check_enabled(u32 index);
47 static int amd_pstate_ut_check_perf(u32 index);
48 static int amd_pstate_ut_check_freq(u32 index);
49 static int amd_pstate_ut_check_driver(u32 index);
50
51 static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
52 {"amd_pstate_ut_acpi_cpc_valid", amd_pstate_ut_acpi_cpc_valid },
53 {"amd_pstate_ut_check_enabled", amd_pstate_ut_check_enabled },
54 {"amd_pstate_ut_check_perf", amd_pstate_ut_check_perf },
55 {"amd_pstate_ut_check_freq", amd_pstate_ut_check_freq },
56 {"amd_pstate_ut_check_driver", amd_pstate_ut_check_driver }
57 };
58
get_shared_mem(void)59 static bool get_shared_mem(void)
60 {
61 bool result = false;
62
63 if (!boot_cpu_has(X86_FEATURE_CPPC))
64 result = true;
65
66 return result;
67 }
68
69 /*
70 * check the _CPC object is present in SBIOS.
71 */
amd_pstate_ut_acpi_cpc_valid(u32 index)72 static int amd_pstate_ut_acpi_cpc_valid(u32 index)
73 {
74 if (!acpi_cpc_valid()) {
75 pr_err("%s the _CPC object is not present in SBIOS!\n", __func__);
76 return -EINVAL;
77 }
78
79 return 0;
80 }
81
82 /*
83 * check if amd pstate is enabled
84 */
amd_pstate_ut_check_enabled(u32 index)85 static int amd_pstate_ut_check_enabled(u32 index)
86 {
87 u64 cppc_enable = 0;
88 int ret;
89
90 if (get_shared_mem())
91 return 0;
92
93 ret = rdmsrl_safe(MSR_AMD_CPPC_ENABLE, &cppc_enable);
94 if (ret) {
95 pr_err("%s rdmsrl_safe MSR_AMD_CPPC_ENABLE ret=%d error!\n", __func__, ret);
96 return ret;
97 }
98
99 if (!cppc_enable) {
100 pr_err("%s amd pstate must be enabled!\n", __func__);
101 return -EINVAL;
102 }
103
104 return 0;
105 }
106
107 /*
108 * check if performance values are reasonable.
109 * highest_perf >= nominal_perf > lowest_nonlinear_perf > lowest_perf > 0
110 */
amd_pstate_ut_check_perf(u32 index)111 static int amd_pstate_ut_check_perf(u32 index)
112 {
113 int cpu = 0, ret = 0;
114 u32 highest_perf = 0, nominal_perf = 0, lowest_nonlinear_perf = 0, lowest_perf = 0;
115 u64 cap1 = 0;
116 struct cppc_perf_caps cppc_perf;
117 union perf_cached cur_perf;
118
119 for_each_online_cpu(cpu) {
120 struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
121 struct amd_cpudata *cpudata;
122
123 policy = cpufreq_cpu_get(cpu);
124 if (!policy)
125 continue;
126 cpudata = policy->driver_data;
127
128 if (get_shared_mem()) {
129 ret = cppc_get_perf_caps(cpu, &cppc_perf);
130 if (ret) {
131 pr_err("%s cppc_get_perf_caps ret=%d error!\n", __func__, ret);
132 return ret;
133 }
134
135 highest_perf = cppc_perf.highest_perf;
136 nominal_perf = cppc_perf.nominal_perf;
137 lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf;
138 lowest_perf = cppc_perf.lowest_perf;
139 } else {
140 ret = rdmsrl_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &cap1);
141 if (ret) {
142 pr_err("%s read CPPC_CAP1 ret=%d error!\n", __func__, ret);
143 return ret;
144 }
145
146 highest_perf = FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1);
147 nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1);
148 lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1);
149 lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
150 }
151
152 cur_perf = READ_ONCE(cpudata->perf);
153 if (highest_perf != cur_perf.highest_perf && !cpudata->hw_prefcore) {
154 pr_err("%s cpu%d highest=%d %d highest perf doesn't match\n",
155 __func__, cpu, highest_perf, cur_perf.highest_perf);
156 return -EINVAL;
157 }
158 if (nominal_perf != cur_perf.nominal_perf ||
159 (lowest_nonlinear_perf != cur_perf.lowest_nonlinear_perf) ||
160 (lowest_perf != cur_perf.lowest_perf)) {
161 pr_err("%s cpu%d nominal=%d %d lowest_nonlinear=%d %d lowest=%d %d, they should be equal!\n",
162 __func__, cpu, nominal_perf, cur_perf.nominal_perf,
163 lowest_nonlinear_perf, cur_perf.lowest_nonlinear_perf,
164 lowest_perf, cur_perf.lowest_perf);
165 return -EINVAL;
166 }
167
168 if (!((highest_perf >= nominal_perf) &&
169 (nominal_perf > lowest_nonlinear_perf) &&
170 (lowest_nonlinear_perf >= lowest_perf) &&
171 (lowest_perf > 0))) {
172 pr_err("%s cpu%d highest=%d >= nominal=%d > lowest_nonlinear=%d > lowest=%d > 0, the formula is incorrect!\n",
173 __func__, cpu, highest_perf, nominal_perf,
174 lowest_nonlinear_perf, lowest_perf);
175 return -EINVAL;
176 }
177 }
178
179 return 0;
180 }
181
182 /*
183 * Check if frequency values are reasonable.
184 * max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0
185 * check max freq when set support boost mode.
186 */
amd_pstate_ut_check_freq(u32 index)187 static int amd_pstate_ut_check_freq(u32 index)
188 {
189 int cpu = 0;
190
191 for_each_online_cpu(cpu) {
192 struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
193 struct amd_cpudata *cpudata;
194
195 policy = cpufreq_cpu_get(cpu);
196 if (!policy)
197 continue;
198 cpudata = policy->driver_data;
199
200 if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) &&
201 (cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
202 (cpudata->lowest_nonlinear_freq >= policy->cpuinfo.min_freq) &&
203 (policy->cpuinfo.min_freq > 0))) {
204 pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",
205 __func__, cpu, policy->cpuinfo.max_freq, cpudata->nominal_freq,
206 cpudata->lowest_nonlinear_freq, policy->cpuinfo.min_freq);
207 return -EINVAL;
208 }
209
210 if (cpudata->lowest_nonlinear_freq != policy->min) {
211 pr_err("%s cpu%d cpudata_lowest_nonlinear_freq=%d policy_min=%d, they should be equal!\n",
212 __func__, cpu, cpudata->lowest_nonlinear_freq, policy->min);
213 return -EINVAL;
214 }
215
216 if (cpudata->boost_supported) {
217 if ((policy->max != policy->cpuinfo.max_freq) &&
218 (policy->max != cpudata->nominal_freq)) {
219 pr_err("%s cpu%d policy_max=%d should be equal cpu_max=%d or cpu_nominal=%d !\n",
220 __func__, cpu, policy->max, policy->cpuinfo.max_freq,
221 cpudata->nominal_freq);
222 return -EINVAL;
223 }
224 } else {
225 pr_err("%s cpu%d must support boost!\n", __func__, cpu);
226 return -EINVAL;
227 }
228 }
229
230 return 0;
231 }
232
amd_pstate_set_mode(enum amd_pstate_mode mode)233 static int amd_pstate_set_mode(enum amd_pstate_mode mode)
234 {
235 const char *mode_str = amd_pstate_get_mode_string(mode);
236
237 pr_debug("->setting mode to %s\n", mode_str);
238
239 return amd_pstate_update_status(mode_str, strlen(mode_str));
240 }
241
amd_pstate_ut_check_driver(u32 index)242 static int amd_pstate_ut_check_driver(u32 index)
243 {
244 enum amd_pstate_mode mode1, mode2 = AMD_PSTATE_DISABLE;
245
246 for (mode1 = AMD_PSTATE_DISABLE; mode1 < AMD_PSTATE_MAX; mode1++) {
247 int ret = amd_pstate_set_mode(mode1);
248 if (ret)
249 return ret;
250 for (mode2 = AMD_PSTATE_DISABLE; mode2 < AMD_PSTATE_MAX; mode2++) {
251 if (mode1 == mode2)
252 continue;
253 ret = amd_pstate_set_mode(mode2);
254 if (ret) {
255 pr_err("%s: failed to update status for %s->%s\n", __func__,
256 amd_pstate_get_mode_string(mode1),
257 amd_pstate_get_mode_string(mode2));
258 return ret;
259 }
260 }
261 }
262
263 return 0;
264 }
265
amd_pstate_ut_init(void)266 static int __init amd_pstate_ut_init(void)
267 {
268 u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
269
270 for (i = 0; i < arr_size; i++) {
271 int ret = amd_pstate_ut_cases[i].func(i);
272
273 if (ret)
274 pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret);
275 else
276 pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);
277 }
278
279 return 0;
280 }
281
amd_pstate_ut_exit(void)282 static void __exit amd_pstate_ut_exit(void)
283 {
284 }
285
286 module_init(amd_pstate_ut_init);
287 module_exit(amd_pstate_ut_exit);
288
289 MODULE_AUTHOR("Meng Li <[email protected]>");
290 MODULE_DESCRIPTION("AMD P-state driver Test module");
291 MODULE_LICENSE("GPL");
292