Skip to content

Commit 5cbff51

Browse files
stephan-ghvireshk
authored andcommitted
cpufreq: qcom-nvmem: Enable virtual power domain devices
The genpd core caches performance state votes from devices that are runtime suspended as of commit 3c5a272 ("PM: domains: Improve runtime PM performance state handling"). They get applied once the device becomes active again. To attach the power domains needed by qcom-cpufreq-nvmem the OPP core calls genpd_dev_pm_attach_by_id(). This results in "virtual" dummy devices that use runtime PM only to control the enable and performance state for the attached power domain. However, at the moment nothing ever resumes the virtual devices created for qcom-cpufreq-nvmem. They remain permanently runtime suspended. This means that performance state votes made during cpufreq scaling get always cached and never applied to the hardware. Fix this by enabling the devices after attaching them. Without this fix performance states votes are silently ignored, and the CPU/CPR voltage is never adjusted. This has been broken since 5.14 but for some reason no one noticed this on QCS404 so far. Fixes: 1cb8339 ("cpufreq: qcom: Add support for qcs404 on nvmem driver") Signed-off-by: Stephan Gerhold <stephan.gerhold@kernkonzept.com> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
1 parent 2e4e098 commit 5cbff51

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

drivers/cpufreq/qcom-cpufreq-nvmem.c

+43-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/platform_device.h>
2626
#include <linux/pm_domain.h>
2727
#include <linux/pm_opp.h>
28+
#include <linux/pm_runtime.h>
2829
#include <linux/slab.h>
2930
#include <linux/soc/qcom/smem.h>
3031

@@ -55,6 +56,7 @@ struct qcom_cpufreq_match_data {
5556

5657
struct qcom_cpufreq_drv_cpu {
5758
int opp_token;
59+
struct device **virt_devs;
5860
};
5961

6062
struct qcom_cpufreq_drv {
@@ -424,6 +426,18 @@ static const struct qcom_cpufreq_match_data match_data_ipq8074 = {
424426
.get_version = qcom_cpufreq_ipq8074_name_version,
425427
};
426428

429+
static void qcom_cpufreq_put_virt_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu)
430+
{
431+
const char * const *name = drv->data->genpd_names;
432+
int i;
433+
434+
if (!drv->cpus[cpu].virt_devs)
435+
return;
436+
437+
for (i = 0; *name; i++, name++)
438+
pm_runtime_put(drv->cpus[cpu].virt_devs[i]);
439+
}
440+
427441
static int qcom_cpufreq_probe(struct platform_device *pdev)
428442
{
429443
struct qcom_cpufreq_drv *drv;
@@ -478,6 +492,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
478492
of_node_put(np);
479493

480494
for_each_possible_cpu(cpu) {
495+
struct device **virt_devs = NULL;
481496
struct dev_pm_opp_config config = {
482497
.supported_hw = NULL,
483498
};
@@ -498,7 +513,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
498513

499514
if (drv->data->genpd_names) {
500515
config.genpd_names = drv->data->genpd_names;
501-
config.virt_devs = NULL;
516+
config.virt_devs = &virt_devs;
502517
}
503518

504519
if (config.supported_hw || config.genpd_names) {
@@ -509,6 +524,27 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
509524
goto free_opp;
510525
}
511526
}
527+
528+
if (virt_devs) {
529+
const char * const *name = config.genpd_names;
530+
int i, j;
531+
532+
for (i = 0; *name; i++, name++) {
533+
ret = pm_runtime_resume_and_get(virt_devs[i]);
534+
if (ret) {
535+
dev_err(cpu_dev, "failed to resume %s: %d\n",
536+
*name, ret);
537+
538+
/* Rollback previous PM runtime calls */
539+
name = config.genpd_names;
540+
for (j = 0; *name && j < i; j++, name++)
541+
pm_runtime_put(virt_devs[j]);
542+
543+
goto free_opp;
544+
}
545+
}
546+
drv->cpus[cpu].virt_devs = virt_devs;
547+
}
512548
}
513549

514550
cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
@@ -522,8 +558,10 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
522558
dev_err(cpu_dev, "Failed to register platform device\n");
523559

524560
free_opp:
525-
for_each_possible_cpu(cpu)
561+
for_each_possible_cpu(cpu) {
562+
qcom_cpufreq_put_virt_devs(drv, cpu);
526563
dev_pm_opp_clear_config(drv->cpus[cpu].opp_token);
564+
}
527565
return ret;
528566
}
529567

@@ -534,8 +572,10 @@ static void qcom_cpufreq_remove(struct platform_device *pdev)
534572

535573
platform_device_unregister(cpufreq_dt_pdev);
536574

537-
for_each_possible_cpu(cpu)
575+
for_each_possible_cpu(cpu) {
576+
qcom_cpufreq_put_virt_devs(drv, cpu);
538577
dev_pm_opp_clear_config(drv->cpus[cpu].opp_token);
578+
}
539579
}
540580

541581
static struct platform_driver qcom_cpufreq_driver = {

0 commit comments

Comments
 (0)