diff mbox series

[v2,09/11] xen/x86: implement EPP support for the amd-cppc driver in active mode

Message ID 20250206083255.1296363-10-Penny.Zheng@amd.com (mailing list archive)
State New
Headers show
Series amd-cppc CPU Performance Scaling Driver | expand

Commit Message

Penny, Zheng Feb. 6, 2025, 8:32 a.m. UTC
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(-)
diff mbox series

Patch

diff --git a/xen/arch/x86/acpi/cpufreq/amd-cppc.c b/xen/arch/x86/acpi/cpufreq/amd-cppc.c
index 1742c57170..241cce330b 100644
--- a/xen/arch/x86/acpi/cpufreq/amd-cppc.c
+++ b/xen/arch/x86/acpi/cpufreq/amd-cppc.c
@@ -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);
 }
diff --git a/xen/drivers/cpufreq/utility.c b/xen/drivers/cpufreq/utility.c
index e690a484f1..13342e8469 100644
--- a/xen/drivers/cpufreq/utility.c
+++ b/xen/drivers/cpufreq/utility.c
@@ -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;
+}
diff --git a/xen/include/acpi/cpufreq/cpufreq.h b/xen/include/acpi/cpufreq/cpufreq.h
index 3c2b951830..fb42d61567 100644
--- a/xen/include/acpi/cpufreq/cpufreq.h
+++ b/xen/include/acpi/cpufreq/cpufreq.h
@@ -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,
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index 42997252ef..fa431fd983 100644
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -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"
 
 /*