@@ -34,6 +34,7 @@
printk(XENLOG_WARNING "AMD_CPPC: CPU%u warning: " fmt, cpu, ## args)
static bool __ro_after_init opt_cpufreq_active;
+static uint8_t __read_mostly epp_init;
struct amd_cppc_drv_data
{
@@ -258,14 +259,27 @@ static void amd_cppc_write_request_msrs(void *info)
}
static int cf_check amd_cppc_write_request(int cpu, uint8_t min_perf,
- uint8_t des_perf, uint8_t max_perf)
+ uint8_t des_perf, uint8_t max_perf,
+ int epp)
{
struct amd_cppc_drv_data *data = per_cpu(amd_cppc_drv_data, cpu);
uint64_t prev = data->req.raw;
data->req.min_perf = min_perf;
data->req.max_perf = max_perf;
- data->req.des_perf = des_perf;
+ if ( !opt_cpufreq_active )
+ data->req.des_perf = des_perf;
+ else
+ {
+ data->req.des_perf = 0;
+ /* Get pre-defined BIOS value */
+ if ( epp < 0 )
+ data->req.epp = epp_init;
+ else if ( epp > UINT8_MAX )
+ return -EINVAL;
+ else
+ data->req.epp = epp;
+ }
if ( prev == data->req.raw )
return 0;
@@ -292,7 +306,25 @@ static int cf_check amd_cppc_cpufreq_target(struct cpufreq_policy *policy,
return res;
return amd_cppc_write_request(policy->cpu, data->caps.lowest_nonlinear_perf,
- des_perf, data->caps.highest_perf);
+ des_perf, data->caps.highest_perf, -1);
+}
+
+static int read_epp_init_once(void)
+{
+ uint64_t val;
+ static bool read_once = false;
+
+ if ( !read_once )
+ {
+ if ( rdmsr_safe(MSR_AMD_CPPC_REQ, val) )
+ return -EINVAL;
+ epp_init = (val >> 24) & 0xFF;
+
+ /* Read pre-defined BIOS value once */
+ read_once = true;
+ }
+
+ return 0;
}
static void cf_check amd_cppc_init_msrs(void *info)
@@ -381,7 +413,8 @@ static void cf_check amd_cppc_init_msrs(void *info)
data->nominal_freq = nominal_freq;
data->max_freq = max_freq;
- return;
+ if ( !read_epp_init_once() )
+ return;
err:
data->err = -EINVAL;
@@ -402,7 +435,7 @@ static void amd_cppc_boost_init(struct cpufreq_policy *policy, const struct amd_
policy->turbo = CPUFREQ_TURBO_ENABLED;
}
-static int cf_check amd_cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
+static int amd_cppc_cpufreq_init_perf(struct cpufreq_policy *policy)
{
unsigned int cpu = policy->cpu;
struct amd_cppc_drv_data *data;
@@ -429,6 +462,17 @@ static int cf_check amd_cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
amd_cppc_boost_init(policy, data);
+ return 0;
+}
+
+static int cf_check amd_cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ ret = amd_cppc_cpufreq_init_perf(policy);
+ if ( ret )
+ return ret;
+
amd_cppc_verbose("CPU %u initialized with amd-cppc passive mode\n", policy->cpu);
return 0;
}
@@ -443,6 +487,52 @@ static int cf_check amd_cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
return 0;
}
+static int cf_check amd_cppc_epp_cpu_init(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ ret = amd_cppc_cpufreq_init_perf(policy);
+ if ( ret )
+ return ret;
+
+ policy->policy = cpufreq_parse_policy(policy->governor);
+
+ amd_cppc_verbose("CPU %u initialized with amd-cppc active mode\n", policy->cpu);
+
+ return 0;
+}
+
+static int amd_cppc_epp_update_limit(const struct cpufreq_policy *policy)
+{
+ const struct amd_cppc_drv_data *data = per_cpu(amd_cppc_drv_data, policy->cpu);
+ uint8_t max_perf, min_perf, des_perf;
+ int epp = -1;
+
+ /* Initial min/max values for CPPC Performance Controls Register */
+ max_perf = data->caps.highest_perf;
+ min_perf = data->caps.lowest_perf;
+
+ /* CPPC EPP feature require to set zero to the desire perf bit */
+ des_perf = 0;
+
+ if ( policy->policy == CPUFREQ_POLICY_PERFORMANCE )
+ {
+ /* Force the epp value to be zero for performance policy */
+ epp = CPPC_ENERGY_PERF_MAX_PERFORMANCE;
+ min_perf = max_perf;
+ }
+ else if ( policy->policy == CPUFREQ_POLICY_POWERSAVE )
+ /* Force the epp value to be 0xff for powersave policy */
+ epp = CPPC_ENERGY_PERF_MAX_POWERSAVE;
+
+ return amd_cppc_write_request(policy->cpu, min_perf, des_perf, max_perf, epp);
+}
+
+static int cf_check amd_cppc_epp_set_policy(struct cpufreq_policy *policy)
+{
+ return amd_cppc_epp_update_limit(policy);
+}
+
static const struct cpufreq_driver __initconst_cf_clobber amd_cppc_cpufreq_driver =
{
.name = XEN_AMD_CPPC_DRIVER_NAME,
@@ -452,10 +542,22 @@ static const struct cpufreq_driver __initconst_cf_clobber amd_cppc_cpufreq_drive
.exit = amd_cppc_cpufreq_cpu_exit,
};
+static const struct cpufreq_driver __initconst_cf_clobber amd_cppc_epp_driver =
+{
+ .name = XEN_AMD_CPPC_EPP_DRIVER_NAME,
+ .verify = amd_cppc_cpufreq_verify,
+ .setpolicy = amd_cppc_epp_set_policy,
+ .init = amd_cppc_epp_cpu_init,
+ .exit = amd_cppc_cpufreq_cpu_exit,
+};
+
int __init amd_cppc_register_driver(void)
{
if ( !cpu_has_cppc )
return -ENODEV;
- return cpufreq_register_driver(&amd_cppc_cpufreq_driver);
+ if ( !opt_cpufreq_active )
+ return cpufreq_register_driver(&amd_cppc_cpufreq_driver);
+ else
+ return cpufreq_register_driver(&amd_cppc_epp_driver);
}
@@ -484,3 +484,14 @@ int __cpufreq_set_policy(struct cpufreq_policy *data,
return __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
}
+
+unsigned int cpufreq_parse_policy(const struct cpufreq_governor *gov)
+{
+ if ( !strncasecmp(gov->name, "performance", CPUFREQ_NAME_LEN) )
+ return CPUFREQ_POLICY_PERFORMANCE;
+
+ if ( !strncasecmp(gov->name, "powersave", CPUFREQ_NAME_LEN) )
+ return CPUFREQ_POLICY_POWERSAVE;
+
+ return CPUFREQ_POLICY_UNKNOWN;
+}
@@ -83,6 +83,7 @@ struct cpufreq_policy {
int8_t turbo; /* tristate flag: 0 for unsupported
* -1 for disable, 1 for enabled
* See CPUFREQ_TURBO_* below for defines */
+ unsigned int policy;
};
DECLARE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_policy);
@@ -133,6 +134,17 @@ extern int cpufreq_register_governor(struct cpufreq_governor *governor);
extern struct cpufreq_governor *__find_governor(const char *governor);
#define CPUFREQ_DEFAULT_GOVERNOR &cpufreq_gov_dbs
+#define CPUFREQ_POLICY_UNKNOWN 0
+/*
+ * If cpufreq_driver->target() exists, the ->governor decides what frequency
+ * within the limits is used. If cpufreq_driver->setpolicy() exists, these
+ * two generic policies are available:
+ */
+#define CPUFREQ_POLICY_POWERSAVE 1
+#define CPUFREQ_POLICY_PERFORMANCE 2
+
+unsigned int cpufreq_parse_policy(const struct cpufreq_governor *gov);
+
/* pass a target to the cpufreq driver */
extern int __cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int target_freq,
@@ -424,6 +424,7 @@ struct xen_set_cppc_para {
};
#define XEN_AMD_CPPC_DRIVER_NAME "amd-cppc"
+#define XEN_AMD_CPPC_EPP_DRIVER_NAME "amd-cppc-epp"
#define XEN_HWP_DRIVER_NAME "hwp"
/*
amd-cppc has 2 operation modes: autonomous (active) mode, non-autonomous (passive) mode. In active mode, platform ignores the requestd done in the Desired Performance Target register and takes into account only the values set to the minimum, maximum and energy performance preference(EPP) registers. The EPP is used in the CCLK DPM controller to drive the frequency that a core is going to operate during short periods of activity. The SOC EPP targets are configured on a scale from 0 to 255 where 0 represents maximum performance and 255 represents maximum efficiency. This commit implements one new AMD CPU frequency driver `amd-cppc-epp` for active mode. Signed-off-by: Penny Zheng <Penny.Zheng@amd.com> --- v1 -> v2: - Remove redundant epp_mode - Remove pointless initializer - Define sole caller read_epp_init_once and epp_init value to read pre-defined BIOS epp value only once - Combine the commit "xen/cpufreq: introduce policy type when cpufreq_driver->setpolicy exists" --- xen/arch/x86/acpi/cpufreq/amd-cppc.c | 114 +++++++++++++++++++++++++-- xen/drivers/cpufreq/utility.c | 11 +++ xen/include/acpi/cpufreq/cpufreq.h | 12 +++ xen/include/public/sysctl.h | 1 + 4 files changed, 132 insertions(+), 6 deletions(-)