From 2e4e0984c7d696cc74cf2fd7e7f62997f0e9ebe6 Mon Sep 17 00:00:00 2001 From: Christoph Niedermaier Date: Wed, 22 Nov 2023 14:41:13 +0100 Subject: cpufreq: imx6q: Don't disable 792 Mhz OPP unnecessarily For a 900MHz i.MX6ULL CPU the 792MHz OPP is disabled. There is no convincing reason to disable this OPP. If a CPU can run at 900MHz, it should also be able to cope with 792MHz. Looking at the voltage level of 792MHz in [1] (page 24, table 10. "Operating Ranges") the current defined OPP is above the minimum. So the voltage level shouldn't be a problem. However in [2] (page 24, table 10. "Operating Ranges"), it is not mentioned that 792MHz OPP isn't allowed. Change it to only disable 792MHz OPP for i.MX6ULL types below 792 MHz. [1] https://www.nxp.com/docs/en/data-sheet/IMX6ULLIEC.pdf [2] https://www.nxp.com/docs/en/data-sheet/IMX6ULLCEC.pdf Fixes: 0aa9abd4c212 ("cpufreq: imx6q: check speed grades for i.MX6ULL") Signed-off-by: Christoph Niedermaier Reviewed-by: Marek Vasut Reviewed-by: Fabio Estevam [ Viresh: Edited subject ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx6q-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 494d044b9e72..33728c242f66 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -327,7 +327,7 @@ static int imx6ul_opp_check_speed_grading(struct device *dev) imx6x_disable_freq_in_opp(dev, 696000000); if (of_machine_is_compatible("fsl,imx6ull")) { - if (val != OCOTP_CFG3_6ULL_SPEED_792MHZ) + if (val < OCOTP_CFG3_6ULL_SPEED_792MHZ) imx6x_disable_freq_in_opp(dev, 792000000); if (val != OCOTP_CFG3_6ULL_SPEED_900MHZ) -- cgit v1.2.3 From 5cbff51e709a72a60b0416b00521b835a1f9a3d5 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 14 Nov 2023 11:07:43 +0100 Subject: cpufreq: qcom-nvmem: Enable virtual power domain devices The genpd core caches performance state votes from devices that are runtime suspended as of commit 3c5a272202c2 ("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: 1cb8339ca225 ("cpufreq: qcom: Add support for qcs404 on nvmem driver") Signed-off-by: Stephan Gerhold Reviewed-by: Ulf Hansson Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-nvmem.c | 46 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index 6355a39418c5..d239a45ed497 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,7 @@ struct qcom_cpufreq_match_data { struct qcom_cpufreq_drv_cpu { int opp_token; + struct device **virt_devs; }; struct qcom_cpufreq_drv { @@ -424,6 +426,18 @@ static const struct qcom_cpufreq_match_data match_data_ipq8074 = { .get_version = qcom_cpufreq_ipq8074_name_version, }; +static void qcom_cpufreq_put_virt_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu) +{ + const char * const *name = drv->data->genpd_names; + int i; + + if (!drv->cpus[cpu].virt_devs) + return; + + for (i = 0; *name; i++, name++) + pm_runtime_put(drv->cpus[cpu].virt_devs[i]); +} + static int qcom_cpufreq_probe(struct platform_device *pdev) { struct qcom_cpufreq_drv *drv; @@ -478,6 +492,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) of_node_put(np); for_each_possible_cpu(cpu) { + struct device **virt_devs = NULL; struct dev_pm_opp_config config = { .supported_hw = NULL, }; @@ -498,7 +513,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) if (drv->data->genpd_names) { config.genpd_names = drv->data->genpd_names; - config.virt_devs = NULL; + config.virt_devs = &virt_devs; } if (config.supported_hw || config.genpd_names) { @@ -509,6 +524,27 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) goto free_opp; } } + + if (virt_devs) { + const char * const *name = config.genpd_names; + int i, j; + + for (i = 0; *name; i++, name++) { + ret = pm_runtime_resume_and_get(virt_devs[i]); + if (ret) { + dev_err(cpu_dev, "failed to resume %s: %d\n", + *name, ret); + + /* Rollback previous PM runtime calls */ + name = config.genpd_names; + for (j = 0; *name && j < i; j++, name++) + pm_runtime_put(virt_devs[j]); + + goto free_opp; + } + } + drv->cpus[cpu].virt_devs = virt_devs; + } } cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, @@ -522,8 +558,10 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) dev_err(cpu_dev, "Failed to register platform device\n"); free_opp: - for_each_possible_cpu(cpu) + for_each_possible_cpu(cpu) { + qcom_cpufreq_put_virt_devs(drv, cpu); dev_pm_opp_clear_config(drv->cpus[cpu].opp_token); + } return ret; } @@ -534,8 +572,10 @@ static void qcom_cpufreq_remove(struct platform_device *pdev) platform_device_unregister(cpufreq_dt_pdev); - for_each_possible_cpu(cpu) + for_each_possible_cpu(cpu) { + qcom_cpufreq_put_virt_devs(drv, cpu); dev_pm_opp_clear_config(drv->cpus[cpu].opp_token); + } } static struct platform_driver qcom_cpufreq_driver = { -- cgit v1.2.3 From d6048a19a7104ce90dcc140d03dbe5796af3acd4 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 14 Nov 2023 11:07:44 +0100 Subject: cpufreq: qcom-nvmem: Preserve PM domain votes in system suspend >From the Linux point of view, the power domains used by the CPU must stay always-on. This is because we still need the CPU to keep running until the last instruction, which will typically be a firmware call that shuts down the CPU cleanly. At the moment the power domain votes (enable + performance state) are dropped during system suspend, which means the CPU could potentially malfunction while entering suspend. We need to distinguish between two different setups used with qcom-cpufreq-nvmem: 1. CPR power domain: The backing regulator used by CPR should stay always-on in Linux; it is typically disabled automatically by hardware when the CPU enters a deep idle state. However, we should pause the CPR state machine during system suspend. 2. RPMPD: The power domains used by the CPU should stay always-on in Linux (also across system suspend). The CPU typically only uses the *_AO ("active-only") variants of the power domains in RPMPD. For those, the RPM firmware will automatically drop the votes internally when the CPU enters a deep idle state. Make this work correctly by calling device_set_awake_path() on the virtual genpd devices, so that the votes are maintained across system suspend. The power domain drivers need to set GENPD_FLAG_ACTIVE_WAKEUP to opt into staying on during system suspend. For now we only set this for the RPMPD case. For CPR, not setting it will ensure the state machine is still paused during system suspend, while the backing regulator will stay on with "regulator-always-on". Signed-off-by: Stephan Gerhold Reviewed-by: Ulf Hansson Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-nvmem.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index d239a45ed497..ea05d9d67490 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -426,6 +427,18 @@ static const struct qcom_cpufreq_match_data match_data_ipq8074 = { .get_version = qcom_cpufreq_ipq8074_name_version, }; +static void qcom_cpufreq_suspend_virt_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu) +{ + const char * const *name = drv->data->genpd_names; + int i; + + if (!drv->cpus[cpu].virt_devs) + return; + + for (i = 0; *name; i++, name++) + device_set_awake_path(drv->cpus[cpu].virt_devs[i]); +} + static void qcom_cpufreq_put_virt_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu) { const char * const *name = drv->data->genpd_names; @@ -578,11 +591,25 @@ static void qcom_cpufreq_remove(struct platform_device *pdev) } } +static int qcom_cpufreq_suspend(struct device *dev) +{ + struct qcom_cpufreq_drv *drv = dev_get_drvdata(dev); + unsigned int cpu; + + for_each_possible_cpu(cpu) + qcom_cpufreq_suspend_virt_devs(drv, cpu); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(qcom_cpufreq_pm_ops, qcom_cpufreq_suspend, NULL); + static struct platform_driver qcom_cpufreq_driver = { .probe = qcom_cpufreq_probe, .remove_new = qcom_cpufreq_remove, .driver = { .name = "qcom-cpufreq-nvmem", + .pm = pm_sleep_ptr(&qcom_cpufreq_pm_ops), }, }; -- cgit v1.2.3 From 8f96e29aae31354191227ad476dc7f6147ef1d75 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 14 Nov 2023 11:07:45 +0100 Subject: pmdomain: qcom: rpmpd: Set GENPD_FLAG_ACTIVE_WAKEUP Set GENPD_FLAG_ACTIVE_WAKEUP for all RPM power domains so that power domains necessary for wakeup/"awake path" devices are kept on across suspend. This is needed for example for the *_AO ("active-only") variants of the RPMPDs used by the CPU. Those should maintain their votes also across system suspend to ensure the CPU can keep running for the whole suspend process (ending in a firmware call). When the RPM firmware detects that the CPUs are in a deep idle state it will drop those votes automatically. Signed-off-by: Stephan Gerhold Reviewed-by: Ulf Hansson Signed-off-by: Viresh Kumar --- drivers/pmdomain/qcom/rpmpd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pmdomain/qcom/rpmpd.c b/drivers/pmdomain/qcom/rpmpd.c index 07590a3ef19c..7796d65f96e8 100644 --- a/drivers/pmdomain/qcom/rpmpd.c +++ b/drivers/pmdomain/qcom/rpmpd.c @@ -1044,6 +1044,7 @@ static int rpmpd_probe(struct platform_device *pdev) rpmpds[i]->pd.power_off = rpmpd_power_off; rpmpds[i]->pd.power_on = rpmpd_power_on; rpmpds[i]->pd.set_performance_state = rpmpd_set_performance; + rpmpds[i]->pd.flags = GENPD_FLAG_ACTIVE_WAKEUP; pm_genpd_init(&rpmpds[i]->pd, NULL, true); data->domains[i] = &rpmpds[i]->pd; -- cgit v1.2.3