From 5f085d45f4b3a99eac34ff3175fc14e8c37b6b59 Mon Sep 17 00:00:00 2001 From: Xiongfeng Wang Date: Wed, 30 Jan 2019 11:24:08 +0800 Subject: [PATCH] cpufreq / cppc: Work around for Hisilicon CPPC cpufreq euler inclusion category: feature Bugzilla: 5520 CVE: N/A ------------------------------------------------------------------------ Hisilicon chips do not support delivered performance counter register and reference performance counter register. But the platform can calculate the real performance using its own method. This patch provide a workaround for this problem, and other platforms can also use this workaround framework. We reuse the desired performance register to store the real performance calculated by the platform. After the platform finished the frequency adjust, it gets the real performance and writes it into desired performance register. OS can use it to calculate the real frequency. Signed-off-by: Xiongfeng Wang Reviewed-by: Hanjun Guo Signed-off-by: Yang Yingliang --- drivers/acpi/cppc_acpi.c | 30 +++++++++++++++ drivers/cpufreq/Kconfig.arm | 7 ++++ drivers/cpufreq/cppc_cpufreq.c | 70 ++++++++++++++++++++++++++++++++++ include/acpi/cppc_acpi.h | 4 ++ 4 files changed, 111 insertions(+) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index d9ce4b162e2c..9432515a20ea 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1050,6 +1050,36 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) return ret_val; } +#ifdef CONFIG_HISILICON_CPPC_CPUFREQ_WORKAROUND +/* + * We reuse the desired performance register to store the real performance + * calculated by the platform. + */ +u64 hisi_cppc_get_real_perf(unsigned int cpunum) +{ + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + struct cpc_register_resource *desired_reg; + u64 desired_perf; + int ret; + + /* + * Make sure that the platform has finished the frequency adjustment + * and has written the real performance into the desired performance + * register. + */ + ret = check_pcc_chan(pcc_ss_id, false); + if (ret) + return 0; + + desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; + cpc_read(cpunum, desired_reg, &desired_perf); + + return desired_perf; +} +EXPORT_SYMBOL_GPL(hisi_cppc_get_real_perf); +#endif + /** * cppc_get_perf_caps - Get a CPUs performance capabilities. * @cpunum: CPU from which to get capabilities info. diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 0cd8eb76ad59..07a8d31e2148 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -18,6 +18,13 @@ config ACPI_CPPC_CPUFREQ If in doubt, say N. +config HISILICON_CPPC_CPUFREQ_WORKAROUND + bool "Workaround for Hisilicon CPPC Cpufreq" + default y + depends on ACPI_CPPC_CPUFREQ && ARM64 + help + This option enables a workaround for Hisilicon CPPC Cpufreq. + config ARM_ARMADA_37XX_CPUFREQ tristate "Armada 37xx CPUFreq support" depends on ARCH_MVEBU && CPUFREQ_DT diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 30f302149730..75226bb0233a 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -33,6 +33,16 @@ /* Offest in the DMI processor structure for the max frequency */ #define DMI_PROCESSOR_MAX_SPEED 0x14 +struct cppc_workaround_info { + char oem_id[ACPI_OEM_ID_SIZE +1]; + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; + u32 oem_revision; + unsigned int (*get_rate)(unsigned int cpu); +}; + +/* CPPC workaround for get_rate callback */ +unsigned int (*cppc_wa_get_rate)(unsigned int cpu); + /* * These structs contain information parsed from per CPU * ACPI _CPC structures. @@ -334,6 +344,9 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum) struct cppc_cpudata *cpu = all_cpu_data[cpunum]; int ret; + if (cppc_wa_get_rate) + return cppc_wa_get_rate(cpunum); + ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0); if (ret) return ret; @@ -357,6 +370,61 @@ static struct cpufreq_driver cppc_cpufreq_driver = { .name = "cppc_cpufreq", }; +#ifdef CONFIG_HISILICON_CPPC_CPUFREQ_WORKAROUND +/* + * When the platform does not support delivered performance counter or + * reference performance counter, it can calculate the performance using the + * platform specific mechanism. We reuse the desired performance register to + * store the real performance calculated by the platform. + */ +static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum) +{ + struct cppc_cpudata *cpu = all_cpu_data[cpunum]; + u64 desired_perf = hisi_cppc_get_real_perf(cpunum); + + return cppc_cpufreq_perf_to_khz(cpu, desired_perf); +} +#endif + +static struct cppc_workaround_info wa_info[] = { +#ifdef CONFIG_HISILICON_CPPC_CPUFREQ_WORKAROUND + { + .oem_id = "HISI ", + .oem_table_id = "HIP07 ", + .oem_revision = 0, + .get_rate = hisi_cppc_cpufreq_get_rate, + }, { + .oem_id = "HISI ", + .oem_table_id = "HIP08 ", + .oem_revision = 0, + .get_rate = hisi_cppc_cpufreq_get_rate, + }, +#endif + {} +}; + +static int cppc_check_workaround(void) +{ + struct acpi_table_header *tbl; + acpi_status status = AE_OK; + int i; + + status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); + if (ACPI_FAILURE(status) || !tbl) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(wa_info) - 1; i++) { + if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && + !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && + wa_info[i].oem_revision == tbl->oem_revision) { + cppc_wa_get_rate = wa_info[i].get_rate; + return 0; + } + } + + return -EINVAL; +} + static int __init cppc_cpufreq_init(void) { int i, ret = 0; @@ -386,6 +454,8 @@ static int __init cppc_cpufreq_init(void) goto out; } + cppc_check_workaround(); + ret = cpufreq_register_driver(&cppc_cpufreq_driver); if (ret) goto out; diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 8e0b8250a139..3469ec4517d1 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -142,4 +142,8 @@ extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); extern int acpi_get_psd_map(struct cppc_cpudata **); extern unsigned int cppc_get_transition_latency(int cpu); +#ifdef CONFIG_HISILICON_CPPC_CPUFREQ_WORKAROUND +u64 hisi_cppc_get_real_perf(unsigned int cpunum); +#endif + #endif /* _CPPC_ACPI_H*/ -- GitLab