提交 5f085d45 编写于 作者: X Xiongfeng Wang 提交者: Xie XiuQi

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: NXiongfeng Wang <wangxiongfeng2@huawei.com>
Reviewed-by: NHanjun Guo <guohanjun@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 142ed329
......@@ -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.
......
......@@ -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
......
......@@ -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;
......
......@@ -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*/
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册