From a407fafcd94eec0ff83d8d1b82f9a901c06cd7b1 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Thu, 25 Feb 2021 22:45:30 +0530 Subject: [PATCH] cpufreq: qcom: cpufreq-hw: Add support for SDPM rail voting Add support for shared digital rail vote for MX/CX when there is a transition in the frequency of the CPU. Also add the suspend/resume callbacks where the driver will vote for the minimum CPU frequency during suspend and restore back once back in resume. Change-Id: I26a5f65b0001551bd09c28902810c72fa9c9719a Signed-off-by: Taniya Das --- drivers/cpufreq/qcom-cpufreq-hw.c | 73 ++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 3626244e2e10..f5235a8eaa76 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. */ #include @@ -31,6 +31,7 @@ #define GT_IRQ_STATUS BIT(2) #define MAX_FN_SIZE 20 #define LIMITS_POLLING_DELAY_MS 10 +#define MAX_ROW 2 #define CYCLE_CNTR_OFFSET(c, m, acc_count) \ (acc_count ? ((c - cpumask_first(m) + 1) * 4) : 0) @@ -60,6 +61,8 @@ struct cpufreq_qcom { struct cpufreq_frequency_table *table; void __iomem *base; void __iomem *pdmem_base; + void __iomem **sdpm_base; + int sdpm_base_count; cpumask_t related_cpus; unsigned long dcvsh_freq_limit; struct delayed_work freq_poll_work; @@ -236,19 +239,25 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, unsigned int index) { - struct cpufreq_qcom *c; + struct cpufreq_qcom *c = qcom_freq_domain_map[policy->cpu]; + unsigned long freq = policy->freq_table[index].frequency; + int i; if (perf_lock_support) { - c = qcom_freq_domain_map[policy->cpu]; if (c->pdmem_base) writel_relaxed(index, c->pdmem_base); } + for (i = 0; i < c->sdpm_base_count && freq > policy->cur; i++) + writel_relaxed(freq / 1000, c->sdpm_base[i]); + writel_relaxed(index, policy->driver_data + offsets[REG_PERF_STATE]); - arch_set_freq_scale(policy->related_cpus, - policy->freq_table[index].frequency, + arch_set_freq_scale(policy->related_cpus, freq, policy->cpuinfo.max_freq); + for (i = 0; i < c->sdpm_base_count && freq < policy->cur; i++) + writel_relaxed(freq / 1000, c->sdpm_base[i]); + return 0; } @@ -379,6 +388,30 @@ static void qcom_cpufreq_ready(struct cpufreq_policy *policy) of_node_put(np); } +static int qcom_cpufreq_hw_suspend(struct cpufreq_policy *policy) +{ + struct cpufreq_qcom *c = qcom_freq_domain_map[policy->cpu]; + unsigned long freq = policy->freq_table[0].frequency; + int i; + + for (i = 0; i < c->sdpm_base_count && freq < policy->cur; i++) + writel_relaxed(freq / 1000, c->sdpm_base[i]); + + return 0; +} + +static int qcom_cpufreq_hw_resume(struct cpufreq_policy *policy) +{ + struct cpufreq_qcom *c = qcom_freq_domain_map[policy->cpu]; + unsigned long freq = policy->cur; + int i; + + for (i = 0; i < c->sdpm_base_count; i++) + writel_relaxed(freq / 1000, c->sdpm_base[i]); + + return 0; +} + static struct cpufreq_driver cpufreq_qcom_hw_driver = { .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_HAVE_GOVERNOR_PER_POLICY, @@ -391,6 +424,8 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = { .attr = qcom_cpufreq_hw_attr, .boost_enabled = true, .ready = qcom_cpufreq_ready, + .suspend = qcom_cpufreq_hw_suspend, + .resume = qcom_cpufreq_hw_resume, }; static int qcom_cpufreq_hw_read_lut(struct platform_device *pdev, @@ -486,7 +521,8 @@ static int qcom_cpu_resources_init(struct platform_device *pdev, struct device *dev = &pdev->dev; void __iomem *base; char pdmem_name[MAX_FN_SIZE] = {}; - int ret, cpu_r; + char sdpm_name[MAX_FN_SIZE] = {}; + int ret, cpu_r, len, reg, size, i, j; c = devm_kzalloc(dev, sizeof(*c), GFP_KERNEL); if (!c) @@ -543,6 +579,31 @@ static int qcom_cpu_resources_init(struct platform_device *pdev, c->pdmem_base = base; } + snprintf(sdpm_name, sizeof(sdpm_name), "qcom,sdpm-cx-mx-%d", index); + len = of_property_count_u32_elems(dev->of_node, sdpm_name); + if (len > 0) { + c->sdpm_base_count = len / MAX_ROW; + c->sdpm_base = devm_kcalloc(dev, c->sdpm_base_count, + sizeof(*c->sdpm_base), GFP_KERNEL); + if (!c->sdpm_base) + return -ENOMEM; + + for (i = 0, j = 0; i < c->sdpm_base_count; i++, j += 2) { + of_property_read_u32_index(dev->of_node, sdpm_name, + j, ®); + of_property_read_u32_index(dev->of_node, sdpm_name, + j + 1, &size); + base = devm_ioremap(dev, reg, size); + if (IS_ERR(base)) { + dev_err(dev, "Failed to map base %d domain-%d\n", + i, index); + return PTR_ERR(base); + } + + c->sdpm_base[i] = base; + } + } + if (of_find_property(dev->of_node, "interrupts", NULL)) { c->dcvsh_irq = of_irq_get(dev->of_node, index); if (c->dcvsh_irq > 0) {