cpufreq/amd-pstate: Add support for the "Requested CPU Min frequency" BIOS option
authorDhananjay Ugwekar <dhananjay.ugwekar@amd.com>
Mon, 28 Apr 2025 07:16:24 +0000 (07:16 +0000)
committerMario Limonciello <mario.limonciello@amd.com>
Mon, 28 Apr 2025 21:09:35 +0000 (16:09 -0500)
Initialize lower frequency limit to the "Requested CPU Min frequency"
BIOS option (if it is set) value as part of the driver->init()
callback. The BIOS specified value is passed by the PMFW as min_perf in
CPPC_REQ MSR. To ensure that we don't mistake a stale min_perf value in
CPPC_REQ value as the "Requested CPU Min frequency" during a kexec wakeup,
reset the CPPC_REQ.min_perf value back to the BIOS specified one in the
offline, exit and suspend callbacks.

amd_pstate_target() and amd_pstate_epp_update_limit() which are invoked
as part of the resume() and online() callbacks will take care of restoring
the CPPC_REQ back to the correct values.

Signed-off-by: Dhananjay Ugwekar <dhananjay.ugwekar@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
Link: https://lore.kernel.org/r/20250428071623.4309-1-dhananjay.ugwekar@amd.com
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
drivers/cpufreq/amd-pstate.c
drivers/cpufreq/amd-pstate.h

index 9a80040..fa4eb5a 100644 (file)
@@ -389,7 +389,8 @@ static inline int amd_pstate_cppc_enable(struct cpufreq_policy *policy)
 static int msr_init_perf(struct amd_cpudata *cpudata)
 {
        union perf_cached perf = READ_ONCE(cpudata->perf);
-       u64 cap1, numerator;
+       u64 cap1, numerator, cppc_req;
+       u8 min_perf;
 
        int ret = rdmsrl_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
                                     &cap1);
@@ -400,6 +401,22 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
        if (ret)
                return ret;
 
+       ret = rdmsrl_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req);
+       if (ret)
+               return ret;
+
+       WRITE_ONCE(cpudata->cppc_req_cached, cppc_req);
+       min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
+
+       /*
+        * Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an
+        * indication that the min_perf value is the one specified through the BIOS option
+        */
+       cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK);
+
+       if (!cppc_req)
+               perf.bios_min_perf = min_perf;
+
        perf.highest_perf = numerator;
        perf.max_limit_perf = numerator;
        perf.min_limit_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
@@ -580,20 +597,26 @@ static int amd_pstate_verify(struct cpufreq_policy_data *policy_data)
 {
        /*
         * Initialize lower frequency limit (i.e.policy->min) with
-        * lowest_nonlinear_frequency which is the most energy efficient
-        * frequency. Override the initial value set by cpufreq core and
-        * amd-pstate qos_requests.
+        * lowest_nonlinear_frequency or the min frequency (if) specified in BIOS,
+        * Override the initial value set by cpufreq core and amd-pstate qos_requests.
         */
        if (policy_data->min == FREQ_QOS_MIN_DEFAULT_VALUE) {
                struct cpufreq_policy *policy __free(put_cpufreq_policy) =
                                              cpufreq_cpu_get(policy_data->cpu);
                struct amd_cpudata *cpudata;
+               union perf_cached perf;
 
                if (!policy)
                        return -EINVAL;
 
                cpudata = policy->driver_data;
-               policy_data->min = cpudata->lowest_nonlinear_freq;
+               perf = READ_ONCE(cpudata->perf);
+
+               if (perf.bios_min_perf)
+                       policy_data->min = perf_to_freq(perf, cpudata->nominal_freq,
+                                                       perf.bios_min_perf);
+               else
+                       policy_data->min = cpudata->lowest_nonlinear_freq;
        }
 
        cpufreq_verify_within_cpu_limits(policy_data);
@@ -1024,6 +1047,10 @@ free_cpudata1:
 static void amd_pstate_cpu_exit(struct cpufreq_policy *policy)
 {
        struct amd_cpudata *cpudata = policy->driver_data;
+       union perf_cached perf = READ_ONCE(cpudata->perf);
+
+       /* Reset CPPC_REQ MSR to the BIOS value */
+       amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
 
        freq_qos_remove_request(&cpudata->req[1]);
        freq_qos_remove_request(&cpudata->req[0]);
@@ -1419,7 +1446,6 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
        struct amd_cpudata *cpudata;
        union perf_cached perf;
        struct device *dev;
-       u64 value;
        int ret;
 
        /*
@@ -1484,12 +1510,6 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
                cpudata->epp_default = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
        }
 
-       if (cpu_feature_enabled(X86_FEATURE_CPPC)) {
-               ret = rdmsrl_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &value);
-               if (ret)
-                       return ret;
-               WRITE_ONCE(cpudata->cppc_req_cached, value);
-       }
        ret = amd_pstate_set_epp(policy, cpudata->epp_default);
        if (ret)
                return ret;
@@ -1509,6 +1529,11 @@ static void amd_pstate_epp_cpu_exit(struct cpufreq_policy *policy)
        struct amd_cpudata *cpudata = policy->driver_data;
 
        if (cpudata) {
+               union perf_cached perf = READ_ONCE(cpudata->perf);
+
+               /* Reset CPPC_REQ MSR to the BIOS value */
+               amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
+
                kfree(cpudata);
                policy->driver_data = NULL;
        }
@@ -1566,12 +1591,31 @@ static int amd_pstate_cpu_online(struct cpufreq_policy *policy)
 
 static int amd_pstate_cpu_offline(struct cpufreq_policy *policy)
 {
-       return 0;
+       struct amd_cpudata *cpudata = policy->driver_data;
+       union perf_cached perf = READ_ONCE(cpudata->perf);
+
+       /*
+        * Reset CPPC_REQ MSR to the BIOS value, this will allow us to retain the BIOS specified
+        * min_perf value across kexec reboots. If this CPU is just onlined normally after this, the
+        * limits, epp and desired perf will get reset to the cached values in cpudata struct
+        */
+       return amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
 }
 
 static int amd_pstate_suspend(struct cpufreq_policy *policy)
 {
        struct amd_cpudata *cpudata = policy->driver_data;
+       union perf_cached perf = READ_ONCE(cpudata->perf);
+       int ret;
+
+       /*
+        * Reset CPPC_REQ MSR to the BIOS value, this will allow us to retain the BIOS specified
+        * min_perf value across kexec reboots. If this CPU is just resumed back without kexec,
+        * the limits, epp and desired perf will get reset to the cached values in cpudata struct
+        */
+       ret = amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
+       if (ret)
+               return ret;
 
        /* invalidate to ensure it's rewritten during resume */
        cpudata->cppc_req_cached = 0;
@@ -1582,6 +1626,17 @@ static int amd_pstate_suspend(struct cpufreq_policy *policy)
        return 0;
 }
 
+static int amd_pstate_resume(struct cpufreq_policy *policy)
+{
+       struct amd_cpudata *cpudata = policy->driver_data;
+       union perf_cached perf = READ_ONCE(cpudata->perf);
+       int cur_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->cur);
+
+       /* Set CPPC_REQ to last sane value until the governor updates it */
+       return amd_pstate_update_perf(policy, perf.min_limit_perf, cur_perf, perf.max_limit_perf,
+                                     0U, false);
+}
+
 static int amd_pstate_epp_resume(struct cpufreq_policy *policy)
 {
        struct amd_cpudata *cpudata = policy->driver_data;
@@ -1610,6 +1665,7 @@ static struct cpufreq_driver amd_pstate_driver = {
        .online         = amd_pstate_cpu_online,
        .offline        = amd_pstate_cpu_offline,
        .suspend        = amd_pstate_suspend,
+       .resume         = amd_pstate_resume,
        .set_boost      = amd_pstate_set_boost,
        .update_limits  = amd_pstate_update_limits,
        .name           = "amd-pstate",
index fbe1c08..2f7ae36 100644 (file)
@@ -30,6 +30,7 @@
  * @lowest_perf: the absolute lowest performance level of the processor
  * @min_limit_perf: Cached value of the performance corresponding to policy->min
  * @max_limit_perf: Cached value of the performance corresponding to policy->max
+ * @bios_min_perf: Cached perf value corresponding to the "Requested CPU Min Frequency" BIOS option
  */
 union perf_cached {
        struct {
@@ -39,6 +40,7 @@ union perf_cached {
                u8      lowest_perf;
                u8      min_limit_perf;
                u8      max_limit_perf;
+               u8      bios_min_perf;
        };
        u64     val;
 };