ACPI / CPPC: Add support for functional fixed hardware address
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Thu, 1 Sep 2016 20:37:10 +0000 (13:37 -0700)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 8 Sep 2016 21:02:14 +0000 (23:02 +0200)
The CPPC registers can also be accessed via functional fixed hardware
addresse(FFH) in X86. Add support by modifying cpc_read and cpc_write to
be able to read/write MSRs on x86 platform on per cpu basis.
Also with this change, acpi_cppc_processor_probe doesn't bail out if
address space id is not equal to PCC or memory address space and FFH
is supported on the system.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
arch/x86/kernel/acpi/Makefile
arch/x86/kernel/acpi/cppc_msr.c [new file with mode: 0644]
drivers/acpi/cppc_acpi.c

index 3242e591fa8207ec6d1c0b3aef0a9fce91987d9d..26b78d86f25a1b54d4811801df682a5d50ed0427 100644 (file)
@@ -1,6 +1,7 @@
 obj-$(CONFIG_ACPI)             += boot.o
 obj-$(CONFIG_ACPI_SLEEP)       += sleep.o wakeup_$(BITS).o
 obj-$(CONFIG_ACPI_APEI)                += apei.o
+obj-$(CONFIG_ACPI_CPPC_LIB)    += cppc_msr.o
 
 ifneq ($(CONFIG_ACPI_PROCESSOR),)
 obj-y                          += cstate.o
diff --git a/arch/x86/kernel/acpi/cppc_msr.c b/arch/x86/kernel/acpi/cppc_msr.c
new file mode 100644 (file)
index 0000000..6fb478b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * cppc_msr.c:  MSR Interface for CPPC
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <acpi/cppc_acpi.h>
+#include <asm/msr.h>
+
+/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */
+
+bool cpc_ffh_supported(void)
+{
+       return true;
+}
+
+int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
+{
+       int err;
+
+       err = rdmsrl_safe_on_cpu(cpunum, reg->address, val);
+       if (!err) {
+               u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
+                                      reg->bit_offset);
+
+               *val &= mask;
+               *val >>= reg->bit_offset;
+       }
+       return err;
+}
+
+int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
+{
+       u64 rd_val;
+       int err;
+
+       err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val);
+       if (!err) {
+               u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
+                                      reg->bit_offset);
+
+               val <<= reg->bit_offset;
+               val &= mask;
+               rd_val &= ~mask;
+               rd_val |= val;
+               err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val);
+       }
+       return err;
+}
index ed58883d35ee06fcdc484d71bebfcfada9d08b03..715fe8001d591df54fc759b42428bead17a44632 100644 (file)
@@ -579,6 +579,19 @@ static int register_pcc_channel(int pcc_subspace_idx)
        return 0;
 }
 
+/**
+ * cpc_ffh_supported() - check if FFH reading supported
+ *
+ * Check if the architecture has support for functional fixed hardware
+ * read/write capability.
+ *
+ * Return: true for supported, false for not supported
+ */
+bool __weak cpc_ffh_supported(void)
+{
+       return false;
+}
+
 /*
  * An example CPC table looks like the following.
  *
@@ -728,9 +741,11 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
                                        cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
                                }
                        } else {
-                               /* Support only PCC and SYS MEM type regs */
-                               pr_debug("Unsupported register type: %d\n", gas_t->space_id);
-                               goto out_free;
+                               if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) {
+                                       /* Support only PCC ,SYS MEM and FFH type regs */
+                                       pr_debug("Unsupported register type: %d\n", gas_t->space_id);
+                                       goto out_free;
+                               }
                        }
 
                        cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
@@ -819,13 +834,43 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
 }
 EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
 
+/**
+ * cpc_read_ffh() - Read FFH register
+ * @cpunum:    cpu number to read
+ * @reg:       cppc register information
+ * @val:       place holder for return value
+ *
+ * Read bit_width bits from a specified address and bit_offset
+ *
+ * Return: 0 for success and error code
+ */
+int __weak cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
+{
+       return -ENOTSUPP;
+}
+
+/**
+ * cpc_write_ffh() - Write FFH register
+ * @cpunum:    cpu number to write
+ * @reg:       cppc register information
+ * @val:       value to write
+ *
+ * Write value of bit_width bits to a specified address and bit_offset
+ *
+ * Return: 0 for success and error code
+ */
+int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
+{
+       return -ENOTSUPP;
+}
+
 /*
  * Since cpc_read and cpc_write are called while holding pcc_lock, it should be
  * as fast as possible. We have already mapped the PCC subspace during init, so
  * we can directly write to it.
  */
 
-static int cpc_read(struct cpc_register_resource *reg_res, u64 *val)
+static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
 {
        int ret_val = 0;
        void __iomem *vaddr = 0;
@@ -841,6 +886,8 @@ static int cpc_read(struct cpc_register_resource *reg_res, u64 *val)
                vaddr = GET_PCC_VADDR(reg->address);
        else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
                vaddr = reg_res->sys_mem_vaddr;
+       else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
+               return cpc_read_ffh(cpu, reg, val);
        else
                return acpi_os_read_memory((acpi_physical_address)reg->address,
                                val, reg->bit_width);
@@ -867,7 +914,7 @@ static int cpc_read(struct cpc_register_resource *reg_res, u64 *val)
        return ret_val;
 }
 
-static int cpc_write(struct cpc_register_resource *reg_res, u64 val)
+static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
 {
        int ret_val = 0;
        void __iomem *vaddr = 0;
@@ -877,6 +924,8 @@ static int cpc_write(struct cpc_register_resource *reg_res, u64 val)
                vaddr = GET_PCC_VADDR(reg->address);
        else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
                vaddr = reg_res->sys_mem_vaddr;
+       else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
+               return cpc_write_ffh(cpu, reg, val);
        else
                return acpi_os_write_memory((acpi_physical_address)reg->address,
                                val, reg->bit_width);
@@ -941,13 +990,13 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
                }
        }
 
-       cpc_read(highest_reg, &high);
+       cpc_read(cpunum, highest_reg, &high);
        perf_caps->highest_perf = high;
 
-       cpc_read(lowest_reg, &low);
+       cpc_read(cpunum, lowest_reg, &low);
        perf_caps->lowest_perf = low;
 
-       cpc_read(nom_perf, &nom);
+       cpc_read(cpunum, nom_perf, &nom);
        perf_caps->nominal_perf = nom;
 
        if (!high || !low || !nom)
@@ -1004,9 +1053,9 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
                }
        }
 
-       cpc_read(delivered_reg, &delivered);
-       cpc_read(reference_reg, &reference);
-       cpc_read(ref_perf_reg, &ref_perf);
+       cpc_read(cpunum, delivered_reg, &delivered);
+       cpc_read(cpunum, reference_reg, &reference);
+       cpc_read(cpunum, ref_perf_reg, &ref_perf);
 
        /*
         * Per spec, if ctr_wrap_time optional register is unsupported, then the
@@ -1015,7 +1064,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
         */
        ctr_wrap_time = (u64)(~((u64)0));
        if (CPC_SUPPORTED(ctr_wrap_reg))
-               cpc_read(ctr_wrap_reg, &ctr_wrap_time);
+               cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time);
 
        if (!delivered || !reference || !ref_perf) {
                ret = -EFAULT;
@@ -1082,7 +1131,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
         * Skip writing MIN/MAX until Linux knows how to come up with
         * useful values.
         */
-       cpc_write(desired_reg, perf_ctrls->desired_perf);
+       cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
 
        if (CPC_IN_PCC(desired_reg))
                up_read(&pcc_data.pcc_lock);    /* END Phase-I */