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