ACPI: CPPC: Implement support for SystemIO registers
authorSteven Noonan <steven@valvesoftware.com>
Fri, 24 Dec 2021 01:04:57 +0000 (09:04 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 30 Dec 2021 17:51:39 +0000 (18:51 +0100)
According to the ACPI v6.2 (and later) specification, SystemIO can be
used for _CPC registers. This teaches cppc_acpi how to handle such
registers.

This patch was tested using the amd_pstate driver on my Zephyrus G15
(model GA503QS) using the current version 410 BIOS, which uses
a SystemIO register for the HighestPerformance element in _CPC.

Signed-off-by: Steven Noonan <steven@valvesoftware.com>
Signed-off-by: Huang Rui <ray.huang@amd.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/cppc_acpi.c

index b62c87b8ce4a99650c0ed3140ac449ce45238d10..69e0746470033296c2ada10e8dcc1a3769a8dbbd 100644 (file)
@@ -118,6 +118,8 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
  */
 #define NUM_RETRIES 500ULL
 
+#define OVER_16BTS_MASK ~0xFFFFULL
+
 #define define_one_cppc_ro(_name)              \
 static struct kobj_attribute _name =           \
 __ATTR(_name, 0444, show_##_name, NULL)
@@ -746,9 +748,26 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
                                                goto out_free;
                                        cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
                                }
+                       } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+                               if (gas_t->access_width < 1 || gas_t->access_width > 3) {
+                                       /*
+                                        * 1 = 8-bit, 2 = 16-bit, and 3 = 32-bit.
+                                        * SystemIO doesn't implement 64-bit
+                                        * registers.
+                                        */
+                                       pr_debug("Invalid access width %d for SystemIO register\n",
+                                               gas_t->access_width);
+                                       goto out_free;
+                               }
+                               if (gas_t->address & OVER_16BTS_MASK) {
+                                       /* SystemIO registers use 16-bit integer addresses */
+                                       pr_debug("Invalid IO port %llu for SystemIO register\n",
+                                               gas_t->address);
+                                       goto out_free;
+                               }
                        } else {
                                if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) {
-                                       /* Support only PCC ,SYS MEM and FFH type regs */
+                                       /* Support only PCC, SystemMemory, SystemIO, and FFH type regs. */
                                        pr_debug("Unsupported register type: %d\n", gas_t->space_id);
                                        goto out_free;
                                }
@@ -923,7 +942,21 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
        }
 
        *val = 0;
-       if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
+
+       if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+               u32 width = 8 << (reg->access_width - 1);
+               acpi_status status;
+
+               status = acpi_os_read_port((acpi_io_address)reg->address,
+                                          (u32 *)val, width);
+               if (ACPI_FAILURE(status)) {
+                       pr_debug("Error: Failed to read SystemIO port %llx\n",
+                                reg->address);
+                       return -EFAULT;
+               }
+
+               return 0;
+       } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
                vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
        else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
                vaddr = reg_res->sys_mem_vaddr;
@@ -962,7 +995,20 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
        int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
        struct cpc_reg *reg = &reg_res->cpc_entry.reg;
 
-       if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
+       if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+               u32 width = 8 << (reg->access_width - 1);
+               acpi_status status;
+
+               status = acpi_os_write_port((acpi_io_address)reg->address,
+                                           (u32)val, width);
+               if (ACPI_FAILURE(status)) {
+                       pr_debug("Error: Failed to write SystemIO port %llx\n",
+                                reg->address);
+                       return -EFAULT;
+               }
+
+               return 0;
+       } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
                vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
        else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
                vaddr = reg_res->sys_mem_vaddr;