diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index d9ce4b162e2ce0533039cbe2d5ef4704ecb24642..9432515a20ea9c179419a659652b636e5a6be5ca 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 0cd8eb76ad592b9e3c5531380c6f74e4d539b594..07a8d31e2148c01f22a756a5eabbf3dd4c338f67 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 30f3021497304a5abc9868e54df2c41d17b093e3..75226bb0233ada69a98a424c901f368013da39d6 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 8e0b8250a139975ffd49cea4e2ce724ba464bd9d..3469ec4517d165f3e80c7cc2c1123ab3416039e1 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*/