If the firmware supports CPPC natively in the firmware then we can use the ACPI defined semantics to access the CPPC specific registers.
Signed-off-by: Ashwin Chaugule ashwin.chaugule@linaro.org --- drivers/cpufreq/Kconfig | 9 +++++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cppc.h | 4 +-- drivers/cpufreq/cppc_acpi.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 drivers/cpufreq/cppc_acpi.c
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index d8e8335..c5f3c0b 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -206,6 +206,15 @@ config CPPC_CPUFREQ (e.g. BMC) interpret and optimize it for power and performance in a platform specific manner.
+config CPPC_ACPI + bool "CPPC with ACPI accessors" + depends on ACPI && ACPI_PCC + default n + help + This driver implements the low level accessors to the registers as described + in the ACPI 5.1 spec. Select this driver if you know your platform supports CPPC + and PCC in the firmware. + menu "x86 CPU frequency scaling drivers" depends on X86 source "drivers/cpufreq/Kconfig.x86" diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index b392c8c..d49a999 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o obj-$(CONFIG_CPPC_CPUFREQ) += cppc.o +obj-$(CONFIG_CPPC_ACPI) += cppc_acpi.o
################################################################################## # x86 drivers. diff --git a/drivers/cpufreq/cppc.h b/drivers/cpufreq/cppc.h index 3adbd3d..a119c3b 100644 --- a/drivers/cpufreq/cppc.h +++ b/drivers/cpufreq/cppc.h @@ -175,7 +175,7 @@ struct cpc_funcs { };
extern struct cpc_funcs *cppc_func_ops; -extern u64 cpc_read64(struct cpc_register_resource *reg); -extern int cpc_write64(u64 val, struct cpc_register_resource *reg); +extern u64 cpc_read64(struct cpc_register_resource *reg, void __iomem *base_addr); +extern int cpc_write64(u64 val, struct cpc_register_resource *reg, void __iomem *base_addr);
#endif /* _CPPC_H */ diff --git a/drivers/cpufreq/cppc_acpi.c b/drivers/cpufreq/cppc_acpi.c new file mode 100644 index 0000000..1835fe7 --- /dev/null +++ b/drivers/cpufreq/cppc_acpi.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Ashwin Chaugule ashwin.chaugule@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 <linux/acpi.h> + +#include "cppc.h" + +static u32 acpi_get_highest_perf(struct cpudata *cpu) +{ + struct cpc_register_resource *high_perf = &cpu->cpc_desc->cpc_regs[HIGHEST_PERF]; + + return cpc_read64(high_perf, cpu->pcc_comm_address); +} + +static u64 acpi_get_ref_perf_ctr(struct cpudata *cpu) +{ + struct cpc_register_resource *ref_perf = &cpu->cpc_desc->cpc_regs[REFERENCE_CTR]; + return cpc_read64(ref_perf, cpu->pcc_comm_address); +} + +static u32 acpi_get_lowest_perf(struct cpudata *cpu) +{ + struct cpc_register_resource *low_perf = &cpu->cpc_desc->cpc_regs[LOWEST_PERF]; + return cpc_read64(low_perf, cpu->pcc_comm_address); +} + +static void acpi_set_desired_perf(struct cpudata *cpu, u32 val) +{ + struct cpc_register_resource *desired_perf = &cpu->cpc_desc->cpc_regs[DESIRED_PERF]; + cpc_write64(val, desired_perf, cpu->pcc_comm_address); +} + +static u64 acpi_get_delivered_ctr(struct cpudata *cpu) +{ + struct cpc_register_resource *delivered_ctr = &cpu->cpc_desc->cpc_regs[DELIVERED_CTR]; + return cpc_read64(delivered_ctr, cpu->pcc_comm_address); +} + +struct cpc_funcs acpi_cppc_func_ops = { + .pid_policy = { + .sample_rate_ms = 10, + .deadband = 0, + .setpoint = 97, + .p_gain_pct = 20, + .d_gain_pct = 0, + .i_gain_pct = 0, + }, + .get_highest_perf = acpi_get_highest_perf, + .get_ref_perf_ctr = acpi_get_ref_perf_ctr, + .get_lowest_perf = acpi_get_lowest_perf, + .set_desired_perf = acpi_set_desired_perf, + .get_delivered_ctr = acpi_get_delivered_ctr, +}; + +static int __init acpi_cppc_init(void) +{ + if (acpi_disabled) + return 0; + + cppc_func_ops = &acpi_cppc_func_ops; + + pr_info("Registered ACPI CPPC function ops\n"); + + return 0; +} +early_initcall(acpi_cppc_init); +