ACPI: CPPC: Add auto select register read/write support
authorWyes Karny <wyes.karny@amd.com>
Tue, 7 Mar 2023 11:27:36 +0000 (11:27 +0000)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 17 Mar 2023 18:06:23 +0000 (19:06 +0100)
For some AMD shared memory based systems, the autonomous selection bit
needed to be set explicitly. Add autonomous selection register related
APIs to acpi driver, which amd_pstate driver uses later.

Acked-by: Huang Rui <ray.huang@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
Tested-by: Oleksandr Natalenko <oleksandr@natalenko.name>
Signed-off-by: Wyes Karny <wyes.karny@amd.com>
[ rjw: Fixed up kerneldoc comments, white space adjustment, subject edits ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/cppc_acpi.c
include/acpi/cppc_acpi.h

index 523bad4..7ff269a 100644 (file)
@@ -1434,6 +1434,102 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
 EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
 
 /**
+ * cppc_get_auto_sel_caps - Read autonomous selection register.
+ * @cpunum : CPU from which to read register.
+ * @perf_caps : struct where autonomous selection register value is updated.
+ */
+int cppc_get_auto_sel_caps(int cpunum, struct cppc_perf_caps *perf_caps)
+{
+       struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
+       struct cpc_register_resource *auto_sel_reg;
+       u64  auto_sel;
+
+       if (!cpc_desc) {
+               pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
+               return -ENODEV;
+       }
+
+       auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
+
+       if (!CPC_SUPPORTED(auto_sel_reg))
+               pr_warn_once("Autonomous mode is not unsupported!\n");
+
+       if (CPC_IN_PCC(auto_sel_reg)) {
+               int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
+               struct cppc_pcc_data *pcc_ss_data = NULL;
+               int ret = 0;
+
+               if (pcc_ss_id < 0)
+                       return -ENODEV;
+
+               pcc_ss_data = pcc_data[pcc_ss_id];
+
+               down_write(&pcc_ss_data->pcc_lock);
+
+               if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) {
+                       cpc_read(cpunum, auto_sel_reg, &auto_sel);
+                       perf_caps->auto_sel = (bool)auto_sel;
+               } else {
+                       ret = -EIO;
+               }
+
+               up_write(&pcc_ss_data->pcc_lock);
+
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppc_get_auto_sel_caps);
+
+/**
+ * cppc_set_auto_sel - Write autonomous selection register.
+ * @cpu    : CPU to which to write register.
+ * @enable : the desired value of autonomous selection resiter to be updated.
+ */
+int cppc_set_auto_sel(int cpu, bool enable)
+{
+       int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
+       struct cpc_register_resource *auto_sel_reg;
+       struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+       struct cppc_pcc_data *pcc_ss_data = NULL;
+       int ret = -EINVAL;
+
+       if (!cpc_desc) {
+               pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+               return -ENODEV;
+       }
+
+       auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
+
+       if (CPC_IN_PCC(auto_sel_reg)) {
+               if (pcc_ss_id < 0) {
+                       pr_debug("Invalid pcc_ss_id\n");
+                       return -ENODEV;
+               }
+
+               if (CPC_SUPPORTED(auto_sel_reg)) {
+                       ret = cpc_write(cpu, auto_sel_reg, enable);
+                       if (ret)
+                               return ret;
+               }
+
+               pcc_ss_data = pcc_data[pcc_ss_id];
+
+               down_write(&pcc_ss_data->pcc_lock);
+               /* after writing CPC, transfer the ownership of PCC to platform */
+               ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
+               up_write(&pcc_ss_data->pcc_lock);
+       } else {
+               ret = -ENOTSUPP;
+               pr_debug("_CPC in PCC is not supported\n");
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
+
+/**
  * cppc_set_enable - Set to enable CPPC on the processor by writing the
  * Continuous Performance Control package EnableRegister field.
  * @cpu: CPU for which to enable CPPC register.
index 6b487a5..6126c97 100644 (file)
@@ -109,6 +109,7 @@ struct cppc_perf_caps {
        u32 lowest_freq;
        u32 nominal_freq;
        u32 energy_perf;
+       bool auto_sel;
 };
 
 struct cppc_perf_ctrls {
@@ -153,6 +154,8 @@ extern int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val);
 extern int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val);
 extern int cppc_get_epp_perf(int cpunum, u64 *epp_perf);
 extern int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable);
+extern int cppc_get_auto_sel_caps(int cpunum, struct cppc_perf_caps *perf_caps);
+extern int cppc_set_auto_sel(int cpu, bool enable);
 #else /* !CONFIG_ACPI_CPPC_LIB */
 static inline int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
 {
@@ -214,6 +217,14 @@ static inline int cppc_get_epp_perf(int cpunum, u64 *epp_perf)
 {
        return -ENOTSUPP;
 }
+static inline int cppc_set_auto_sel(int cpu, bool enable)
+{
+       return -ENOTSUPP;
+}
+static inline int cppc_get_auto_sel_caps(int cpunum, struct cppc_perf_caps *perf_caps)
+{
+       return -ENOTSUPP;
+}
 #endif /* !CONFIG_ACPI_CPPC_LIB */
 
 #endif /* _CPPC_ACPI_H*/