From 328fc9b29810819c4496c6e9fd74491ca458045e Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 30 Jul 2024 15:02:22 +0200 Subject: [PATCH 01/39] pmdomain: amlogic: remove obsolete vpu domain driver meson-gx-pwrc-vpu has been superseded by meson-ee-pwrc since commit 53773f2dfd9c ("soc: amlogic: meson-ee-pwrc: add support for the Meson GX SoCs"), so v5.8. This driver is obsolete and no longer used or tested. There is no reason to keep it around so remove it. Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240730130227.712894-1-jbrunet@baylibre.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/amlogic/Kconfig | 11 - drivers/pmdomain/amlogic/Makefile | 1 - drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c | 380 ------------------- 3 files changed, 392 deletions(-) delete mode 100644 drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c diff --git a/drivers/pmdomain/amlogic/Kconfig b/drivers/pmdomain/amlogic/Kconfig index 2108729909b5..e72b664174af 100644 --- a/drivers/pmdomain/amlogic/Kconfig +++ b/drivers/pmdomain/amlogic/Kconfig @@ -1,17 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "Amlogic PM Domains" -config MESON_GX_PM_DOMAINS - tristate "Amlogic Meson GX Power Domains driver" - depends on ARCH_MESON || COMPILE_TEST - depends on PM && OF - default ARCH_MESON - select PM_GENERIC_DOMAINS - select PM_GENERIC_DOMAINS_OF - help - Say yes to expose Amlogic Meson GX Power Domains as - Generic Power Domains. - config MESON_EE_PM_DOMAINS tristate "Amlogic Meson Everything-Else Power Domains driver" depends on ARCH_MESON || COMPILE_TEST diff --git a/drivers/pmdomain/amlogic/Makefile b/drivers/pmdomain/amlogic/Makefile index 3d58abd574f9..99f195f09957 100644 --- a/drivers/pmdomain/amlogic/Makefile +++ b/drivers/pmdomain/amlogic/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o obj-$(CONFIG_MESON_SECURE_PM_DOMAINS) += meson-secure-pwrc.o diff --git a/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c b/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c deleted file mode 100644 index 6028e91664a4..000000000000 --- a/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright (c) 2017 BayLibre, SAS - * Author: Neil Armstrong - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AO Offsets */ - -#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) - -#define GEN_PWR_VPU_HDMI BIT(8) -#define GEN_PWR_VPU_HDMI_ISO BIT(9) - -/* HHI Offsets */ - -#define HHI_MEM_PD_REG0 (0x40 << 2) -#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) -#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) -#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) - -struct meson_gx_pwrc_vpu { - struct generic_pm_domain genpd; - struct regmap *regmap_ao; - struct regmap *regmap_hhi; - struct reset_control *rstc; - struct clk *vpu_clk; - struct clk *vapb_clk; -}; - -static inline -struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d) -{ - return container_of(d, struct meson_gx_pwrc_vpu, genpd); -} - -static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) -{ - struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); - int i; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); - udelay(20); - - /* Power Down Memories */ - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 8; i < 16; i++) { - regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, - BIT(i), BIT(i)); - udelay(5); - } - udelay(20); - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); - - msleep(20); - - clk_disable_unprepare(pd->vpu_clk); - clk_disable_unprepare(pd->vapb_clk); - - return 0; -} - -static int meson_g12a_pwrc_vpu_power_off(struct generic_pm_domain *genpd) -{ - struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); - int i; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); - udelay(20); - - /* Power Down Memories */ - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 8; i < 16; i++) { - regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, - BIT(i), BIT(i)); - udelay(5); - } - udelay(20); - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); - - msleep(20); - - clk_disable_unprepare(pd->vpu_clk); - clk_disable_unprepare(pd->vapb_clk); - - return 0; -} - -static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) -{ - int ret; - - ret = clk_prepare_enable(pd->vpu_clk); - if (ret) - return ret; - - ret = clk_prepare_enable(pd->vapb_clk); - if (ret) - clk_disable_unprepare(pd->vpu_clk); - - return ret; -} - -static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) -{ - struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); - int ret; - int i; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI, 0); - udelay(20); - - /* Power Up Memories */ - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x3 << i, 0); - udelay(5); - } - - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x3 << i, 0); - udelay(5); - } - - for (i = 8; i < 16; i++) { - regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, - BIT(i), 0); - udelay(5); - } - udelay(20); - - ret = reset_control_assert(pd->rstc); - if (ret) - return ret; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI_ISO, 0); - - ret = reset_control_deassert(pd->rstc); - if (ret) - return ret; - - ret = meson_gx_pwrc_vpu_setup_clk(pd); - if (ret) - return ret; - - return 0; -} - -static int meson_g12a_pwrc_vpu_power_on(struct generic_pm_domain *genpd) -{ - struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); - int ret; - int i; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI, 0); - udelay(20); - - /* Power Up Memories */ - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x3 << i, 0); - udelay(5); - } - - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x3 << i, 0); - udelay(5); - } - - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, - 0x3 << i, 0); - udelay(5); - } - - for (i = 8; i < 16; i++) { - regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, - BIT(i), 0); - udelay(5); - } - udelay(20); - - ret = reset_control_assert(pd->rstc); - if (ret) - return ret; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI_ISO, 0); - - ret = reset_control_deassert(pd->rstc); - if (ret) - return ret; - - ret = meson_gx_pwrc_vpu_setup_clk(pd); - if (ret) - return ret; - - return 0; -} - -static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) -{ - u32 reg; - - regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, ®); - - return (reg & GEN_PWR_VPU_HDMI); -} - -static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { - .genpd = { - .name = "vpu_hdmi", - .power_off = meson_gx_pwrc_vpu_power_off, - .power_on = meson_gx_pwrc_vpu_power_on, - }, -}; - -static struct meson_gx_pwrc_vpu vpu_hdmi_pd_g12a = { - .genpd = { - .name = "vpu_hdmi", - .power_off = meson_g12a_pwrc_vpu_power_off, - .power_on = meson_g12a_pwrc_vpu_power_on, - }, -}; - -static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) -{ - const struct meson_gx_pwrc_vpu *vpu_pd_match; - struct regmap *regmap_ao, *regmap_hhi; - struct meson_gx_pwrc_vpu *vpu_pd; - struct device_node *parent_np; - struct reset_control *rstc; - struct clk *vpu_clk; - struct clk *vapb_clk; - bool powered_off; - int ret; - - vpu_pd_match = of_device_get_match_data(&pdev->dev); - if (!vpu_pd_match) { - dev_err(&pdev->dev, "failed to get match data\n"); - return -ENODEV; - } - - vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL); - if (!vpu_pd) - return -ENOMEM; - - memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd)); - - parent_np = of_get_parent(pdev->dev.of_node); - regmap_ao = syscon_node_to_regmap(parent_np); - of_node_put(parent_np); - if (IS_ERR(regmap_ao)) { - dev_err(&pdev->dev, "failed to get regmap\n"); - return PTR_ERR(regmap_ao); - } - - regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "amlogic,hhi-sysctrl"); - if (IS_ERR(regmap_hhi)) { - dev_err(&pdev->dev, "failed to get HHI regmap\n"); - return PTR_ERR(regmap_hhi); - } - - rstc = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(rstc)) - return dev_err_probe(&pdev->dev, PTR_ERR(rstc), - "failed to get reset lines\n"); - - vpu_clk = devm_clk_get(&pdev->dev, "vpu"); - if (IS_ERR(vpu_clk)) { - dev_err(&pdev->dev, "vpu clock request failed\n"); - return PTR_ERR(vpu_clk); - } - - vapb_clk = devm_clk_get(&pdev->dev, "vapb"); - if (IS_ERR(vapb_clk)) { - dev_err(&pdev->dev, "vapb clock request failed\n"); - return PTR_ERR(vapb_clk); - } - - vpu_pd->regmap_ao = regmap_ao; - vpu_pd->regmap_hhi = regmap_hhi; - vpu_pd->rstc = rstc; - vpu_pd->vpu_clk = vpu_clk; - vpu_pd->vapb_clk = vapb_clk; - - platform_set_drvdata(pdev, vpu_pd); - - powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); - - /* If already powered, sync the clock states */ - if (!powered_off) { - ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd); - if (ret) - return ret; - } - - vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; - pm_genpd_init(&vpu_pd->genpd, NULL, powered_off); - - return of_genpd_add_provider_simple(pdev->dev.of_node, - &vpu_pd->genpd); -} - -static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) -{ - struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev); - bool powered_off; - - powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); - if (!powered_off) - vpu_pd->genpd.power_off(&vpu_pd->genpd); -} - -static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { - { .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd }, - { - .compatible = "amlogic,meson-g12a-pwrc-vpu", - .data = &vpu_hdmi_pd_g12a - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, meson_gx_pwrc_vpu_match_table); - -static struct platform_driver meson_gx_pwrc_vpu_driver = { - .probe = meson_gx_pwrc_vpu_probe, - .shutdown = meson_gx_pwrc_vpu_shutdown, - .driver = { - .name = "meson_gx_pwrc_vpu", - .of_match_table = meson_gx_pwrc_vpu_match_table, - }, -}; -module_platform_driver(meson_gx_pwrc_vpu_driver); -MODULE_DESCRIPTION("Amlogic Meson GX Power Domains driver"); -MODULE_LICENSE("GPL v2"); From d7bdb8e6aabe218fd980768a1486434e42761539 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 27 May 2024 16:25:51 +0200 Subject: [PATCH 02/39] pmdomain: core: Enable s2idle for CPU PM domains on PREEMPT_RT To allow a genpd provider for a CPU PM domain to enter a domain-idle-state during s2idle on a PREEMPT_RT based configuration, we can't use the regular spinlock, as they are turned into sleepable locks on PREEMPT_RT. To address this problem, let's convert into using the raw spinlock, but only for genpd providers that have the GENPD_FLAG_CPU_DOMAIN bit set. In this way, the lock can still be acquired/released in atomic context, which is needed in the idle-path for PREEMPT_RT. Do note that the genpd power-on/off notifiers may also be fired during s2idle, but these are already prepared for PREEMPT_RT as they are based on the raw notifiers. However, consumers of them may need to adopt accordingly to work properly on PREEMPT_RT. Signed-off-by: Ulf Hansson Tested-by: Raghavendra Kakarla # qcm6490 with PREEMPT_RT set Acked-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20240527142557.321610-2-ulf.hansson@linaro.org --- drivers/pmdomain/core.c | 47 ++++++++++++++++++++++++++++++++++++++- include/linux/pm_domain.h | 5 ++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 7a61aa88c061..8c798a46ffec 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -117,6 +117,48 @@ static const struct genpd_lock_ops genpd_spin_ops = { .unlock = genpd_unlock_spin, }; +static void genpd_lock_raw_spin(struct generic_pm_domain *genpd) + __acquires(&genpd->raw_slock) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&genpd->raw_slock, flags); + genpd->raw_lock_flags = flags; +} + +static void genpd_lock_nested_raw_spin(struct generic_pm_domain *genpd, + int depth) + __acquires(&genpd->raw_slock) +{ + unsigned long flags; + + raw_spin_lock_irqsave_nested(&genpd->raw_slock, flags, depth); + genpd->raw_lock_flags = flags; +} + +static int genpd_lock_interruptible_raw_spin(struct generic_pm_domain *genpd) + __acquires(&genpd->raw_slock) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&genpd->raw_slock, flags); + genpd->raw_lock_flags = flags; + return 0; +} + +static void genpd_unlock_raw_spin(struct generic_pm_domain *genpd) + __releases(&genpd->raw_slock) +{ + raw_spin_unlock_irqrestore(&genpd->raw_slock, genpd->raw_lock_flags); +} + +static const struct genpd_lock_ops genpd_raw_spin_ops = { + .lock = genpd_lock_raw_spin, + .lock_nested = genpd_lock_nested_raw_spin, + .lock_interruptible = genpd_lock_interruptible_raw_spin, + .unlock = genpd_unlock_raw_spin, +}; + #define genpd_lock(p) p->lock_ops->lock(p) #define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d) #define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p) @@ -2143,7 +2185,10 @@ static void genpd_free_data(struct generic_pm_domain *genpd) static void genpd_lock_init(struct generic_pm_domain *genpd) { - if (genpd_is_irq_safe(genpd)) { + if (genpd_is_cpu_domain(genpd)) { + raw_spin_lock_init(&genpd->raw_slock); + genpd->lock_ops = &genpd_raw_spin_ops; + } else if (genpd_is_irq_safe(genpd)) { spin_lock_init(&genpd->slock); genpd->lock_ops = &genpd_spin_ops; } else { diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 858c8e7851fb..b86bb52858ac 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -198,8 +198,11 @@ struct generic_pm_domain { spinlock_t slock; unsigned long lock_flags; }; + struct { + raw_spinlock_t raw_slock; + unsigned long raw_lock_flags; + }; }; - }; static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) From b87eee38605c396f0e1fa435939960e5c6cd41d6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 27 May 2024 16:25:52 +0200 Subject: [PATCH 03/39] pmdomain: core: Don't hold the genpd-lock when calling dev_pm_domain_set() There is no need to hold the genpd-lock, while assigning the dev->pm_domain. In fact, it becomes a problem on a PREEMPT_RT based configuration as the genpd-lock may be a raw spinlock, while the lock acquired through the call to dev_pm_domain_set() is a regular spinlock. To fix the problem, let's simply move the calls to dev_pm_domain_set() outside the genpd-lock. Signed-off-by: Ulf Hansson Tested-by: Raghavendra Kakarla # qcm6490 with PREEMPT_RT set Acked-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20240527142557.321610-3-ulf.hansson@linaro.org --- drivers/pmdomain/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 8c798a46ffec..ef61486d41ee 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -1800,7 +1800,6 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, genpd_lock(genpd); genpd_set_cpumask(genpd, gpd_data->cpu); - dev_pm_domain_set(dev, &genpd->domain); genpd->device_count++; if (gd) @@ -1809,6 +1808,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); genpd_unlock(genpd); + dev_pm_domain_set(dev, &genpd->domain); out: if (ret) genpd_free_dev_data(dev, gpd_data); @@ -1865,12 +1865,13 @@ static int genpd_remove_device(struct generic_pm_domain *genpd, genpd->gd->max_off_time_changed = true; genpd_clear_cpumask(genpd, gpd_data->cpu); - dev_pm_domain_set(dev, NULL); list_del_init(&pdd->list_node); genpd_unlock(genpd); + dev_pm_domain_set(dev, NULL); + if (genpd->detach_dev) genpd->detach_dev(genpd, dev); From 9094e53ff5c86ebe372ad3960c3216c9817a1a04 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 27 May 2024 16:25:53 +0200 Subject: [PATCH 04/39] pmdomain: core: Use dev_name() instead of kobject_get_path() in debugfs Using kobject_get_path() means a dynamic memory allocation gets done, which doesn't work on a PREEMPT_RT based configuration while holding genpd's raw spinlock. To fix the problem, let's convert into using the simpler dev_name(). This means the information about the path doesn't get presented in debugfs, but hopefully this shouldn't be an issue. Signed-off-by: Ulf Hansson Tested-by: Raghavendra Kakarla # qcm6490 with PREEMPT_RT set Acked-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20240527142557.321610-4-ulf.hansson@linaro.org --- drivers/pmdomain/core.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index ef61486d41ee..2731b285e017 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -3255,7 +3255,6 @@ static int genpd_summary_one(struct seq_file *s, [GENPD_STATE_OFF] = "off" }; struct pm_domain_data *pm_data; - const char *kobj_path; struct gpd_link *link; char state[16]; int ret; @@ -3288,17 +3287,10 @@ static int genpd_summary_one(struct seq_file *s, } list_for_each_entry(pm_data, &genpd->dev_list, list_node) { - kobj_path = kobject_get_path(&pm_data->dev->kobj, - genpd_is_irq_safe(genpd) ? - GFP_ATOMIC : GFP_KERNEL); - if (kobj_path == NULL) - continue; - - seq_printf(s, "\n %-50s ", kobj_path); + seq_printf(s, "\n %-50s ", dev_name(pm_data->dev)); rtpm_status_str(s, pm_data->dev); perf_status_str(s, pm_data->dev); mode_status_str(s, pm_data->dev); - kfree(kobj_path); } seq_puts(s, "\n"); @@ -3467,23 +3459,14 @@ static int devices_show(struct seq_file *s, void *data) { struct generic_pm_domain *genpd = s->private; struct pm_domain_data *pm_data; - const char *kobj_path; int ret = 0; ret = genpd_lock_interruptible(genpd); if (ret) return -ERESTARTSYS; - list_for_each_entry(pm_data, &genpd->dev_list, list_node) { - kobj_path = kobject_get_path(&pm_data->dev->kobj, - genpd_is_irq_safe(genpd) ? - GFP_ATOMIC : GFP_KERNEL); - if (kobj_path == NULL) - continue; - - seq_printf(s, "%s\n", kobj_path); - kfree(kobj_path); - } + list_for_each_entry(pm_data, &genpd->dev_list, list_node) + seq_printf(s, "%s\n", dev_name(pm_data->dev)); genpd_unlock(genpd); return ret; From c7b45284ab3012d21a02cf4448df32c655c32afb Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 27 May 2024 16:25:54 +0200 Subject: [PATCH 05/39] cpuidle: psci-domain: Enable system-wide suspend on PREEMPT_RT The domain-idle-states are currently disabled on a PREEMPT_RT based configuration for the cpuidle-psci-domain. To enable them to be used for system-wide suspend and in particular during s2idle, let's set the GENPD_FLAG_RPM_ALWAYS_ON instead of GENPD_FLAG_ALWAYS_ON for the corresponding genpd provider. In this way, the runtime PM path remains disabled in genpd for its attached devices, while powering-on/off the PM domain during system-wide suspend becomes allowed. Signed-off-by: Ulf Hansson Tested-by: Raghavendra Kakarla # qcm6490 with PREEMPT_RT set Acked-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20240527142557.321610-5-ulf.hansson@linaro.org --- drivers/cpuidle/cpuidle-psci-domain.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index fae958794339..ea28b73ef3fb 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -67,12 +67,16 @@ static int psci_pd_init(struct device_node *np, bool use_osi) /* * Allow power off when OSI has been successfully enabled. - * PREEMPT_RT is not yet ready to enter domain idle states. + * On a PREEMPT_RT based configuration the domain idle states are + * supported, but only during system-wide suspend. */ - if (use_osi && !IS_ENABLED(CONFIG_PREEMPT_RT)) + if (use_osi) { pd->power_off = psci_pd_power_off; - else + if (IS_ENABLED(CONFIG_PREEMPT_RT)) + pd->flags |= GENPD_FLAG_RPM_ALWAYS_ON; + } else { pd->flags |= GENPD_FLAG_ALWAYS_ON; + } /* Use governor for CPU PM domains if it has some states to manage. */ pd_gov = pd->states ? &pm_domain_cpu_gov : NULL; From 88bf68b76694873c3b101b65a26c4b315fab06df Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 27 May 2024 16:25:55 +0200 Subject: [PATCH 06/39] cpuidle: psci: Drop redundant assignment of CPUIDLE_FLAG_RCU_IDLE When using the hierarchical topology and PSCI OSI-mode we may end up overriding the deepest idle-state's ->enter|enter_s2idle() callbacks, but there is no point to also re-assign the CPUIDLE_FLAG_RCU_IDLE for the idle-state in question, as that has already been set when parsing the states from DT. See init_state_node(). Signed-off-by: Ulf Hansson Tested-by: Raghavendra Kakarla # qcm6490 with PREEMPT_RT set Acked-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20240527142557.321610-6-ulf.hansson@linaro.org --- drivers/cpuidle/cpuidle-psci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index 782030a27703..d82a8bc1b194 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -234,7 +234,6 @@ static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv, * of a shared state for the domain, assumes the domain states are all * deeper states. */ - drv->states[state_count - 1].flags |= CPUIDLE_FLAG_RCU_IDLE; drv->states[state_count - 1].enter = psci_enter_domain_idle_state; drv->states[state_count - 1].enter_s2idle = psci_enter_s2idle_domain_idle_state; psci_cpuidle_use_cpuhp = true; From 4517b1c383807fadac8374f99f2361fe7eb4c0b4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 27 May 2024 16:25:56 +0200 Subject: [PATCH 07/39] cpuidle: psci: Enable the hierarchical topology for s2ram on PREEMPT_RT The hierarchical PM domain topology are currently disabled on a PREEMPT_RT based configuration. As a first step to enable it to be used, let's try to attach the CPU devices to their PM domains on PREEMPT_RT. In this way the syscore ops becomes available, allowing the PM domain topology to be managed during s2ram. For the moment let's leave the support for CPU hotplug outside PREEMPT_RT, as it's depending on using runtime PM. For s2ram, this isn't a problem as all CPUs are managed via the syscore ops. Signed-off-by: Ulf Hansson Tested-by: Raghavendra Kakarla # qcm6490 with PREEMPT_RT set Acked-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20240527142557.321610-7-ulf.hansson@linaro.org --- drivers/cpuidle/cpuidle-psci.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index d82a8bc1b194..ad6ce9fe12b4 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -37,6 +37,7 @@ struct psci_cpuidle_data { static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data); static DEFINE_PER_CPU(u32, domain_state); +static bool psci_cpuidle_use_syscore; static bool psci_cpuidle_use_cpuhp; void psci_set_domain_state(u32 state) @@ -166,6 +167,12 @@ static struct syscore_ops psci_idle_syscore_ops = { .resume = psci_idle_syscore_resume, }; +static void psci_idle_init_syscore(void) +{ + if (psci_cpuidle_use_syscore) + register_syscore_ops(&psci_idle_syscore_ops); +} + static void psci_idle_init_cpuhp(void) { int err; @@ -173,8 +180,6 @@ static void psci_idle_init_cpuhp(void) if (!psci_cpuidle_use_cpuhp) return; - register_syscore_ops(&psci_idle_syscore_ops); - err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING, "cpuidle/psci:online", psci_idle_cpuhp_up, @@ -222,13 +227,16 @@ static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv, if (!psci_has_osi_support()) return 0; - if (IS_ENABLED(CONFIG_PREEMPT_RT)) - return 0; - data->dev = dt_idle_attach_cpu(cpu, "psci"); if (IS_ERR_OR_NULL(data->dev)) return PTR_ERR_OR_ZERO(data->dev); + psci_cpuidle_use_syscore = true; + + /* The hierarchical topology is limited to s2ram on PREEMPT_RT. */ + if (IS_ENABLED(CONFIG_PREEMPT_RT)) + return 0; + /* * Using the deepest state for the CPU to trigger a potential selection * of a shared state for the domain, assumes the domain states are all @@ -312,6 +320,7 @@ static void psci_cpu_deinit_idle(int cpu) struct psci_cpuidle_data *data = per_cpu_ptr(&psci_cpuidle_data, cpu); dt_idle_detach_cpu(data->dev); + psci_cpuidle_use_syscore = false; psci_cpuidle_use_cpuhp = false; } @@ -408,6 +417,7 @@ static int psci_cpuidle_probe(struct platform_device *pdev) goto out_fail; } + psci_idle_init_syscore(); psci_idle_init_cpuhp(); return 0; From 1c4b2932bd629fe800282114ceb465d3eb5d0737 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 27 May 2024 16:25:57 +0200 Subject: [PATCH 08/39] cpuidle: psci: Enable the hierarchical topology for s2idle on PREEMPT_RT To enable the domain-idle-states to be used during s2idle on a PREEMPT_RT based configuration, let's allow the re-assignment of the ->enter_s2idle() callback to psci_enter_s2idle_domain_idle_state(). Similar to s2ram, let's leave the support for CPU hotplug outside PREEMPT_RT, as it's depending on using runtime PM. For s2idle, this means that an offline CPU's PM domain will remain powered-on. In practise this may lead to that a shallower idle-state than necessary gets selected, which shouldn't be an issue (besides wasting power). Signed-off-by: Ulf Hansson Tested-by: Raghavendra Kakarla # qcm6490 with PREEMPT_RT set Acked-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20240527142557.321610-8-ulf.hansson@linaro.org --- drivers/cpuidle/cpuidle-psci.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index ad6ce9fe12b4..2562dc001fc1 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -233,18 +233,17 @@ static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv, psci_cpuidle_use_syscore = true; - /* The hierarchical topology is limited to s2ram on PREEMPT_RT. */ - if (IS_ENABLED(CONFIG_PREEMPT_RT)) - return 0; - /* * Using the deepest state for the CPU to trigger a potential selection * of a shared state for the domain, assumes the domain states are all - * deeper states. + * deeper states. On PREEMPT_RT the hierarchical topology is limited to + * s2ram and s2idle. */ - drv->states[state_count - 1].enter = psci_enter_domain_idle_state; drv->states[state_count - 1].enter_s2idle = psci_enter_s2idle_domain_idle_state; - psci_cpuidle_use_cpuhp = true; + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) { + drv->states[state_count - 1].enter = psci_enter_domain_idle_state; + psci_cpuidle_use_cpuhp = true; + } return 0; } From 49d2a1ec68eebc436851ccc64135661bad5fc27d Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sun, 28 Jul 2024 13:41:47 +0200 Subject: [PATCH 09/39] pmdomain: raspberrypi-power: Adjust packet definition According to the official Mailbox property interface the second part of RPI_FIRMWARE_SET_POWER_STATE ( and so on ...) is named state because it represent u32 flags and just the lowest bit is for on/off. So rename it to align with documentation and prepare the driver for further changes. Link: https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface Signed-off-by: Stefan Wahren Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20240728114200.75559-4-wahrenst@gmx.net Signed-off-by: Ulf Hansson --- drivers/pmdomain/bcm/raspberrypi-power.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pmdomain/bcm/raspberrypi-power.c b/drivers/pmdomain/bcm/raspberrypi-power.c index 06196ebfe03b..39d9a52200c3 100644 --- a/drivers/pmdomain/bcm/raspberrypi-power.c +++ b/drivers/pmdomain/bcm/raspberrypi-power.c @@ -41,7 +41,7 @@ struct rpi_power_domains { */ struct rpi_power_domain_packet { u32 domain; - u32 on; + u32 state; }; /* @@ -53,7 +53,7 @@ static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on) struct rpi_power_domain_packet packet; packet.domain = rpi_domain->domain; - packet.on = on; + packet.state = on; return rpi_firmware_property(rpi_domain->fw, rpi_domain->old_interface ? RPI_FIRMWARE_SET_POWER_STATE : @@ -142,13 +142,13 @@ rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains) int ret; packet.domain = RPI_POWER_DOMAIN_ARM; - packet.on = ~0; + packet.state = ~0; ret = rpi_firmware_property(rpi_domains->fw, RPI_FIRMWARE_GET_DOMAIN_STATE, &packet, sizeof(packet)); - return ret == 0 && packet.on != ~0; + return ret == 0 && packet.state != ~0; } static int rpi_power_probe(struct platform_device *pdev) From eb3896ea9e2829c8c3aa2e86d3f90765b06a41c6 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sun, 28 Jul 2024 13:41:48 +0200 Subject: [PATCH 10/39] pmdomain: raspberrypi-power: Add logging to rpi_firmware_set_power The Raspberry Pi power driver heavily relies on the logging of the underlying firmware driver. This results in disadvantages like unspecific error messages or limited debugging options. So implement the logging for the most important function rpi_firmware_set_power. Signed-off-by: Stefan Wahren Link: https://lore.kernel.org/r/20240728114200.75559-5-wahrenst@gmx.net Signed-off-by: Ulf Hansson --- drivers/pmdomain/bcm/raspberrypi-power.c | 34 ++++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/pmdomain/bcm/raspberrypi-power.c b/drivers/pmdomain/bcm/raspberrypi-power.c index 39d9a52200c3..fadedfc9c645 100644 --- a/drivers/pmdomain/bcm/raspberrypi-power.c +++ b/drivers/pmdomain/bcm/raspberrypi-power.c @@ -48,33 +48,39 @@ struct rpi_power_domain_packet { * Asks the firmware to enable or disable power on a specific power * domain. */ -static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on) +static int rpi_firmware_set_power(struct generic_pm_domain *domain, bool on) { + struct rpi_power_domain *rpi_domain = + container_of(domain, struct rpi_power_domain, base); + bool old_interface = rpi_domain->old_interface; struct rpi_power_domain_packet packet; + int ret; packet.domain = rpi_domain->domain; packet.state = on; - return rpi_firmware_property(rpi_domain->fw, - rpi_domain->old_interface ? - RPI_FIRMWARE_SET_POWER_STATE : - RPI_FIRMWARE_SET_DOMAIN_STATE, - &packet, sizeof(packet)); + + ret = rpi_firmware_property(rpi_domain->fw, old_interface ? + RPI_FIRMWARE_SET_POWER_STATE : + RPI_FIRMWARE_SET_DOMAIN_STATE, + &packet, sizeof(packet)); + if (ret) + dev_err(&domain->dev, "Failed to set %s to %u (%d)\n", + old_interface ? "power" : "domain", on, ret); + else + dev_dbg(&domain->dev, "Set %s to %u\n", + old_interface ? "power" : "domain", on); + + return ret; } static int rpi_domain_off(struct generic_pm_domain *domain) { - struct rpi_power_domain *rpi_domain = - container_of(domain, struct rpi_power_domain, base); - - return rpi_firmware_set_power(rpi_domain, false); + return rpi_firmware_set_power(domain, false); } static int rpi_domain_on(struct generic_pm_domain *domain) { - struct rpi_power_domain *rpi_domain = - container_of(domain, struct rpi_power_domain, base); - - return rpi_firmware_set_power(rpi_domain, true); + return rpi_firmware_set_power(domain, true); } static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains, From 562cdeadac06176b3a99bd457d9463a3b58df60f Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sun, 28 Jul 2024 13:41:49 +0200 Subject: [PATCH 11/39] pmdomain: raspberrypi-power: set flag GENPD_FLAG_ACTIVE_WAKEUP Set flag GENPD_FLAG_ACTIVE_WAKEUP to rpi_power genpd, then when a device is set as wakeup source using device_set_wakeup_enable, the power domain could be kept on to make sure the device could wakeup the system. Signed-off-by: Stefan Wahren Link: https://lore.kernel.org/r/20240728114200.75559-6-wahrenst@gmx.net Signed-off-by: Ulf Hansson --- drivers/pmdomain/bcm/raspberrypi-power.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pmdomain/bcm/raspberrypi-power.c b/drivers/pmdomain/bcm/raspberrypi-power.c index fadedfc9c645..b87ea7adb7be 100644 --- a/drivers/pmdomain/bcm/raspberrypi-power.c +++ b/drivers/pmdomain/bcm/raspberrypi-power.c @@ -91,6 +91,7 @@ static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains, dom->fw = rpi_domains->fw; dom->base.name = name; + dom->base.flags = GENPD_FLAG_ACTIVE_WAKEUP; dom->base.power_on = rpi_domain_on; dom->base.power_off = rpi_domain_off; From 77c5e7b623032502ee49fe7e7868eaca6786d7ed Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Wed, 14 Aug 2024 18:26:41 -0400 Subject: [PATCH 12/39] dt-bindings: power: Add support for RK3576 SoC Define power domain IDs as described in the TRM and add compatible for rockchip,rk3576-power-controller Signed-off-by: Finley Xiao Co-Developed-by: Detlev Casanova Signed-off-by: Detlev Casanova Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20240814222824.3170-2-detlev.casanova@collabora.com Signed-off-by: Ulf Hansson --- .../power/rockchip,power-controller.yaml | 1 + .../dt-bindings/power/rockchip,rk3576-power.h | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 include/dt-bindings/power/rockchip,rk3576-power.h diff --git a/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml b/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml index 0d5e999a58f1..650dc0aae6f5 100644 --- a/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml +++ b/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml @@ -41,6 +41,7 @@ properties: - rockchip,rk3368-power-controller - rockchip,rk3399-power-controller - rockchip,rk3568-power-controller + - rockchip,rk3576-power-controller - rockchip,rk3588-power-controller - rockchip,rv1126-power-controller diff --git a/include/dt-bindings/power/rockchip,rk3576-power.h b/include/dt-bindings/power/rockchip,rk3576-power.h new file mode 100644 index 000000000000..324a056aa851 --- /dev/null +++ b/include/dt-bindings/power/rockchip,rk3576-power.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +#ifndef __DT_BINDINGS_POWER_RK3576_POWER_H__ +#define __DT_BINDINGS_POWER_RK3576_POWER_H__ + +/* VD_NPU */ +#define RK3576_PD_NPU 0 +#define RK3576_PD_NPUTOP 1 +#define RK3576_PD_NPU0 2 +#define RK3576_PD_NPU1 3 + +/* VD_GPU */ +#define RK3576_PD_GPU 4 + +/* VD_LOGIC */ +#define RK3576_PD_NVM 5 +#define RK3576_PD_SDGMAC 6 +#define RK3576_PD_USB 7 +#define RK3576_PD_PHP 8 +#define RK3576_PD_SUBPHP 9 +#define RK3576_PD_AUDIO 10 +#define RK3576_PD_VEPU0 11 +#define RK3576_PD_VEPU1 12 +#define RK3576_PD_VPU 13 +#define RK3576_PD_VDEC 14 +#define RK3576_PD_VI 15 +#define RK3576_PD_VO0 16 +#define RK3576_PD_VO1 17 +#define RK3576_PD_VOP 18 + +#endif From cfee1b50775869de9076d021ea11a8438854dcba Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Wed, 14 Aug 2024 18:26:42 -0400 Subject: [PATCH 13/39] pmdomain: rockchip: Add support for RK3576 SoC Add configuration for RK3576 SoC and list the power domains. Signed-off-by: Finley Xiao Signed-off-by: Detlev Casanova Reviewed-by: Elaine Zhang Link: https://lore.kernel.org/r/20240814222824.3170-3-detlev.casanova@collabora.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/rockchip/pm-domains.c | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 9b76b62869d0..64b4d7120d83 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -33,6 +33,7 @@ #include #include #include +#include #include struct rockchip_domain_info { @@ -175,6 +176,9 @@ struct rockchip_pmu { #define DOMAIN_RK3568(name, pwr, req, wakeup) \ DOMAIN_M(name, pwr, pwr, req, req, req, wakeup) +#define DOMAIN_RK3576(name, p_offset, pwr, status, r_status, r_offset, req, idle, wakeup) \ + DOMAIN_M_O_R(name, p_offset, pwr, status, 0, r_status, r_status, r_offset, req, idle, idle, wakeup) + /* * Dynamic Memory Controller may need to coordinate with us -- see * rockchip_pmu_block(). @@ -1106,6 +1110,28 @@ static const struct rockchip_domain_info rk3568_pm_domains[] = { [RK3568_PD_PIPE] = DOMAIN_RK3568("pipe", BIT(8), BIT(11), false), }; +static const struct rockchip_domain_info rk3576_pm_domains[] = { + [RK3576_PD_NPU] = DOMAIN_RK3576("npu", 0x0, BIT(0), BIT(0), 0, 0x0, 0, 0, false), + [RK3576_PD_NVM] = DOMAIN_RK3576("nvm", 0x0, BIT(6), 0, BIT(6), 0x4, BIT(2), BIT(18), false), + [RK3576_PD_SDGMAC] = DOMAIN_RK3576("sdgmac", 0x0, BIT(7), 0, BIT(7), 0x4, BIT(1), BIT(17), false), + [RK3576_PD_AUDIO] = DOMAIN_RK3576("audio", 0x0, BIT(8), 0, BIT(8), 0x4, BIT(0), BIT(16), false), + [RK3576_PD_PHP] = DOMAIN_RK3576("php", 0x0, BIT(9), 0, BIT(9), 0x0, BIT(15), BIT(15), false), + [RK3576_PD_SUBPHP] = DOMAIN_RK3576("subphp", 0x0, BIT(10), 0, BIT(10), 0x0, 0, 0, false), + [RK3576_PD_VOP] = DOMAIN_RK3576("vop", 0x0, BIT(11), 0, BIT(11), 0x0, 0x6000, 0x6000, false), + [RK3576_PD_VO1] = DOMAIN_RK3576("vo1", 0x0, BIT(14), 0, BIT(14), 0x0, BIT(12), BIT(12), false), + [RK3576_PD_VO0] = DOMAIN_RK3576("vo0", 0x0, BIT(15), 0, BIT(15), 0x0, BIT(11), BIT(11), false), + [RK3576_PD_USB] = DOMAIN_RK3576("usb", 0x4, BIT(0), 0, BIT(16), 0x0, BIT(10), BIT(10), true), + [RK3576_PD_VI] = DOMAIN_RK3576("vi", 0x4, BIT(1), 0, BIT(17), 0x0, BIT(9), BIT(9), false), + [RK3576_PD_VEPU0] = DOMAIN_RK3576("vepu0", 0x4, BIT(2), 0, BIT(18), 0x0, BIT(7), BIT(7), false), + [RK3576_PD_VEPU1] = DOMAIN_RK3576("vepu1", 0x4, BIT(3), 0, BIT(19), 0x0, BIT(8), BIT(8), false), + [RK3576_PD_VDEC] = DOMAIN_RK3576("vdec", 0x4, BIT(4), 0, BIT(20), 0x0, BIT(6), BIT(6), false), + [RK3576_PD_VPU] = DOMAIN_RK3576("vpu", 0x4, BIT(5), 0, BIT(21), 0x0, BIT(5), BIT(5), false), + [RK3576_PD_NPUTOP] = DOMAIN_RK3576("nputop", 0x4, BIT(6), 0, BIT(22), 0x0, 0x18, 0x18, false), + [RK3576_PD_NPU0] = DOMAIN_RK3576("npu0", 0x4, BIT(7), 0, BIT(23), 0x0, BIT(1), BIT(1), false), + [RK3576_PD_NPU1] = DOMAIN_RK3576("npu1", 0x4, BIT(8), 0, BIT(24), 0x0, BIT(2), BIT(2), false), + [RK3576_PD_GPU] = DOMAIN_RK3576("gpu", 0x4, BIT(9), 0, BIT(25), 0x0, BIT(0), BIT(0), false), +}; + static const struct rockchip_domain_info rk3588_pm_domains[] = { [RK3588_PD_GPU] = DOMAIN_RK3588("gpu", 0x0, BIT(0), 0, 0x0, 0, BIT(1), 0x0, BIT(0), BIT(0), false), [RK3588_PD_NPU] = DOMAIN_RK3588("npu", 0x0, BIT(1), BIT(1), 0x0, 0, 0, 0x0, 0, 0, false), @@ -1284,6 +1310,21 @@ static const struct rockchip_pmu_info rk3568_pmu = { .domain_info = rk3568_pm_domains, }; +static const struct rockchip_pmu_info rk3576_pmu = { + .pwr_offset = 0x210, + .status_offset = 0x230, + .chain_status_offset = 0x248, + .mem_status_offset = 0x250, + .mem_pwr_offset = 0x300, + .req_offset = 0x110, + .idle_offset = 0x128, + .ack_offset = 0x120, + .repair_status_offset = 0x570, + + .num_domains = ARRAY_SIZE(rk3576_pm_domains), + .domain_info = rk3576_pm_domains, +}; + static const struct rockchip_pmu_info rk3588_pmu = { .pwr_offset = 0x14c, .status_offset = 0x180, @@ -1359,6 +1400,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .compatible = "rockchip,rk3568-power-controller", .data = (void *)&rk3568_pmu, }, + { + .compatible = "rockchip,rk3576-power-controller", + .data = (void *)&rk3576_pmu, + }, { .compatible = "rockchip,rk3588-power-controller", .data = (void *)&rk3588_pmu, From b6cee6544d01b8ac01232d343b1b2b594d94d61c Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Mon, 19 Aug 2024 15:59:09 +0530 Subject: [PATCH 14/39] PM: domains: add device managed version of dev_pm_domain_attach|detach_list() Add the devres-enabled version of dev_pm_domain_attach|detach_list. If client drivers use devm_pm_domain_attach_list() to attach the PM domains, devm_pm_domain_detach_list() will be invoked implicitly during remove phase. Signed-off-by: Dikshita Agarwal Link: https://lore.kernel.org/r/1724063350-11993-2-git-send-email-quic_dikshita@quicinc.com Signed-off-by: Ulf Hansson --- drivers/base/power/common.c | 45 +++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 11 +++++++++ 2 files changed, 56 insertions(+) diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index 327d168dd37a..8c34ae1cd8d5 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -276,6 +276,51 @@ err_attach: } EXPORT_SYMBOL_GPL(dev_pm_domain_attach_list); +/** + * devm_pm_domain_detach_list - devres-enabled version of dev_pm_domain_detach_list. + * @_list: The list of PM domains to detach. + * + * This function reverse the actions from devm_pm_domain_attach_list(). + * it will be invoked during the remove phase from drivers implicitly if driver + * uses devm_pm_domain_attach_list() to attach the PM domains. + */ +static void devm_pm_domain_detach_list(void *_list) +{ + struct dev_pm_domain_list *list = _list; + + dev_pm_domain_detach_list(list); +} + +/** + * devm_pm_domain_attach_list - devres-enabled version of dev_pm_domain_attach_list + * @dev: The device used to lookup the PM domains for. + * @data: The data used for attaching to the PM domains. + * @list: An out-parameter with an allocated list of attached PM domains. + * + * NOTE: this will also handle calling devm_pm_domain_detach_list() for + * you during remove phase. + * + * Returns the number of attached PM domains or a negative error code in case of + * a failure. + */ +int devm_pm_domain_attach_list(struct device *dev, + const struct dev_pm_domain_attach_data *data, + struct dev_pm_domain_list **list) +{ + int ret, num_pds; + + num_pds = dev_pm_domain_attach_list(dev, data, list); + if (num_pds <= 0) + return num_pds; + + ret = devm_add_action_or_reset(dev, devm_pm_domain_detach_list, *list); + if (ret) + return ret; + + return num_pds; +} +EXPORT_SYMBOL_GPL(devm_pm_domain_attach_list); + /** * dev_pm_domain_detach - Detach a device from its PM domain. * @dev: Device to detach. diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index b86bb52858ac..b637ec14025f 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -476,6 +476,9 @@ struct device *dev_pm_domain_attach_by_name(struct device *dev, int dev_pm_domain_attach_list(struct device *dev, const struct dev_pm_domain_attach_data *data, struct dev_pm_domain_list **list); +int devm_pm_domain_attach_list(struct device *dev, + const struct dev_pm_domain_attach_data *data, + struct dev_pm_domain_list **list); void dev_pm_domain_detach(struct device *dev, bool power_off); void dev_pm_domain_detach_list(struct dev_pm_domain_list *list); int dev_pm_domain_start(struct device *dev); @@ -502,6 +505,14 @@ static inline int dev_pm_domain_attach_list(struct device *dev, { return 0; } + +static inline int devm_pm_domain_attach_list(struct device *dev, + const struct dev_pm_domain_attach_data *data, + struct dev_pm_domain_list **list) +{ + return 0; +} + static inline void dev_pm_domain_detach(struct device *dev, bool power_off) {} static inline void dev_pm_domain_detach_list(struct dev_pm_domain_list *list) {} static inline int dev_pm_domain_start(struct device *dev) From 3b019409ce9a280db53dbf6beb7ee42c17951782 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Mon, 19 Aug 2024 15:59:10 +0530 Subject: [PATCH 15/39] media: venus: use device managed APIs for power domains Use devres-enabled version of power domain attach APIs. Signed-off-by: Dikshita Agarwal Reviewed-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/1724063350-11993-3-git-send-email-quic_dikshita@quicinc.com Signed-off-by: Ulf Hansson --- drivers/media/platform/qcom/venus/pm_helpers.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 4ce76ce6dd4d..ea8a2bd9419e 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -876,7 +876,7 @@ static int vcodec_domains_get(struct venus_core *core) if (!res->vcodec_pmdomains_num) goto skip_pmdomains; - ret = dev_pm_domain_attach_list(dev, &vcodec_data, &core->pmdomains); + ret = devm_pm_domain_attach_list(dev, &vcodec_data, &core->pmdomains); if (ret < 0) return ret; @@ -902,14 +902,11 @@ skip_pmdomains: return 0; opp_attach_err: - dev_pm_domain_detach_list(core->pmdomains); return ret; } static void vcodec_domains_put(struct venus_core *core) { - dev_pm_domain_detach_list(core->pmdomains); - if (!core->has_opp_table) return; From 9b37f971f31354fcfd84b7c2f219138644c7edd3 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 19 Aug 2024 19:59:56 +0800 Subject: [PATCH 16/39] pmdomain: apple: Make apple_pmgr_reset_ops static The sparse tool complains as follows: drivers/pmdomain/apple/pmgr-pwrstate.c:180:32: warning: symbol 'apple_pmgr_reset_ops' was not declared. Should it be static: This symbol is not used outside of pmgr-pwrstate.c, so marks it static. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20240819115956.3884847-1-ruanjinjie@huawei.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/apple/pmgr-pwrstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c index d62a776c89a1..9467235110f4 100644 --- a/drivers/pmdomain/apple/pmgr-pwrstate.c +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -177,7 +177,7 @@ static int apple_pmgr_reset_status(struct reset_controller_dev *rcdev, unsigned return !!(reg & APPLE_PMGR_RESET); } -const struct reset_control_ops apple_pmgr_reset_ops = { +static const struct reset_control_ops apple_pmgr_reset_ops = { .assert = apple_pmgr_reset_assert, .deassert = apple_pmgr_reset_deassert, .reset = apple_pmgr_reset_reset, From 7aa1204d086e1eb8fc09203d2cdc3f798683d6d4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Aug 2024 17:09:28 +0200 Subject: [PATCH 17/39] cpuidle: psci: Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20240816150931.142208-1-krzysztof.kozlowski@linaro.org Signed-off-by: Ulf Hansson --- drivers/cpuidle/cpuidle-psci-domain.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index ea28b73ef3fb..146f97068022 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -142,7 +142,6 @@ static const struct of_device_id psci_of_match[] = { static int psci_cpuidle_domain_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct device_node *node; bool use_osi = psci_has_osi_support(); int ret = 0, pd_count = 0; @@ -153,15 +152,13 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev) * Parse child nodes for the "#power-domain-cells" property and * initialize a genpd/genpd-of-provider pair when it's found. */ - for_each_child_of_node(np, node) { + for_each_child_of_node_scoped(np, node) { if (!of_property_present(node, "#power-domain-cells")) continue; ret = psci_pd_init(node, use_osi); - if (ret) { - of_node_put(node); + if (ret) goto exit; - } pd_count++; } From 157519c026ec0773401111e33aed20a7a6800d5f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Aug 2024 17:09:31 +0200 Subject: [PATCH 18/39] cpuidle: dt_idle_genpd: Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20240816150931.142208-4-krzysztof.kozlowski@linaro.org Signed-off-by: Ulf Hansson --- drivers/cpuidle/dt_idle_genpd.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/cpuidle/dt_idle_genpd.c b/drivers/cpuidle/dt_idle_genpd.c index 1af63c189039..203e9b754aea 100644 --- a/drivers/cpuidle/dt_idle_genpd.c +++ b/drivers/cpuidle/dt_idle_genpd.c @@ -130,11 +130,10 @@ out: int dt_idle_pd_init_topology(struct device_node *np) { - struct device_node *node; struct of_phandle_args child, parent; int ret; - for_each_child_of_node(np, node) { + for_each_child_of_node_scoped(np, node) { if (of_parse_phandle_with_args(node, "power-domains", "#power-domain-cells", 0, &parent)) continue; @@ -143,10 +142,8 @@ int dt_idle_pd_init_topology(struct device_node *np) child.args_count = 0; ret = of_genpd_add_subdomain(&parent, &child); of_node_put(parent.np); - if (ret) { - of_node_put(node); + if (ret) return ret; - } } return 0; @@ -154,11 +151,10 @@ int dt_idle_pd_init_topology(struct device_node *np) int dt_idle_pd_remove_topology(struct device_node *np) { - struct device_node *node; struct of_phandle_args child, parent; int ret; - for_each_child_of_node(np, node) { + for_each_child_of_node_scoped(np, node) { if (of_parse_phandle_with_args(node, "power-domains", "#power-domain-cells", 0, &parent)) continue; @@ -167,10 +163,8 @@ int dt_idle_pd_remove_topology(struct device_node *np) child.args_count = 0; ret = of_genpd_remove_subdomain(&parent, &child); of_node_put(parent.np); - if (ret) { - of_node_put(node); + if (ret) return ret; - } } return 0; From f253f6d922da29d0d7091801cbc9b4166c3959fe Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Wed, 21 Aug 2024 11:40:21 +0800 Subject: [PATCH 19/39] pmdomain: qcom-cpr: Use helper function for_each_available_child_of_node() for_each_available_child_of_node() can help to iterate through the device_node, and we don't need to use while loop. Besides, the purpose of the while loop is to find a device_node which fits the condition "child_req_np == ref_np", we can just read the property of "child_np" directly in for_each_available_child_of_node(). No functional change with such conversion. Signed-off-by: Zhang Zekun Link: https://lore.kernel.org/r/20240821034022.27394-2-zhangzekun11@huawei.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/cpr.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/pmdomain/qcom/cpr.c b/drivers/pmdomain/qcom/cpr.c index c64e84a27cc7..1834b3861232 100644 --- a/drivers/pmdomain/qcom/cpr.c +++ b/drivers/pmdomain/qcom/cpr.c @@ -1054,14 +1054,14 @@ static unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, if (!ref_np) goto out_ref; - do { + for_each_available_child_of_node(desc_np, child_np) { of_node_put(child_req_np); - child_np = of_get_next_available_child(desc_np, child_np); child_req_np = of_parse_phandle(child_np, "required-opps", 0); - } while (child_np && child_req_np != ref_np); - - if (child_np && child_req_np == ref_np) - of_property_read_u64(child_np, "opp-hz", &rate); + if (child_req_np == ref_np) { + of_property_read_u64(child_np, "opp-hz", &rate); + break; + } + } of_node_put(child_req_np); of_node_put(child_np); From 181c8148556a2a7dd3047ea687873937b1be4f00 Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Wed, 21 Aug 2024 11:40:22 +0800 Subject: [PATCH 20/39] pmdomain: qcom-cpr: Use scope based of_node_put() to simplify code. Use scope based of_node_put() to simplify the code logic, and we don't need to call of_node_put(). This will simplify the code a lot. Signed-off-by: Zhang Zekun Link: https://lore.kernel.org/r/20240821034022.27394-3-zhangzekun11@huawei.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/cpr.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/pmdomain/qcom/cpr.c b/drivers/pmdomain/qcom/cpr.c index 1834b3861232..37b318bf505a 100644 --- a/drivers/pmdomain/qcom/cpr.c +++ b/drivers/pmdomain/qcom/cpr.c @@ -1040,36 +1040,30 @@ static unsigned int cpr_get_fuse_corner(struct dev_pm_opp *opp) static unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, struct device *cpu_dev) { - u64 rate = 0; - struct device_node *ref_np; - struct device_node *desc_np; - struct device_node *child_np = NULL; - struct device_node *child_req_np = NULL; + struct device_node *ref_np __free(device_node) = NULL; + struct device_node *desc_np __free(device_node) = + dev_pm_opp_of_get_opp_desc_node(cpu_dev); - desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); if (!desc_np) return 0; ref_np = dev_pm_opp_get_of_node(ref); if (!ref_np) - goto out_ref; + return 0; + + for_each_available_child_of_node_scoped(desc_np, child_np) { + struct device_node *child_req_np __free(device_node) = + of_parse_phandle(child_np, "required-opps", 0); - for_each_available_child_of_node(desc_np, child_np) { - of_node_put(child_req_np); - child_req_np = of_parse_phandle(child_np, "required-opps", 0); if (child_req_np == ref_np) { + u64 rate; + of_property_read_u64(child_np, "opp-hz", &rate); - break; + return (unsigned long) rate; } } - of_node_put(child_req_np); - of_node_put(child_np); - of_node_put(ref_np); -out_ref: - of_node_put(desc_np); - - return (unsigned long) rate; + return 0; } static int cpr_corner_init(struct cpr_drv *drv) From 0d946ef4646092a23de2baf7b9d3063fe5571e82 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Aug 2024 14:51:05 +0200 Subject: [PATCH 21/39] pmdomain: rockchip: Simplify with scoped for each OF child loop Use scoped for_each_available_child_of_node_scoped() and for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20240823-cleanup-h-guard-pm-domain-v1-1-8320722eaf39@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/rockchip/pm-domains.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 64b4d7120d83..5679ad336a11 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -804,11 +804,10 @@ static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu, static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, struct device_node *parent) { - struct device_node *np; struct generic_pm_domain *child_domain, *parent_domain; int error; - for_each_child_of_node(parent, np) { + for_each_child_of_node_scoped(parent, np) { u32 idx; error = of_property_read_u32(parent, "reg", &idx); @@ -816,7 +815,7 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, dev_err(pmu->dev, "%pOFn: failed to retrieve domain id (reg): %d\n", parent, error); - goto err_out; + return error; } parent_domain = pmu->genpd_data.domains[idx]; @@ -824,7 +823,7 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, if (error) { dev_err(pmu->dev, "failed to handle node %pOFn: %d\n", np, error); - goto err_out; + return error; } error = of_property_read_u32(np, "reg", &idx); @@ -832,7 +831,7 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, dev_err(pmu->dev, "%pOFn: failed to retrieve domain id (reg): %d\n", np, error); - goto err_out; + return error; } child_domain = pmu->genpd_data.domains[idx]; @@ -840,7 +839,7 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, if (error) { dev_err(pmu->dev, "%s failed to add subdomain %s: %d\n", parent_domain->name, child_domain->name, error); - goto err_out; + return error; } else { dev_dbg(pmu->dev, "%s add subdomain: %s\n", parent_domain->name, child_domain->name); @@ -850,17 +849,12 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, } return 0; - -err_out: - of_node_put(np); - return error; } static int rockchip_pm_domain_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct device_node *node; struct device *parent; struct rockchip_pmu *pmu; const struct rockchip_pmu_info *pmu_info; @@ -918,12 +912,11 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev) */ mutex_lock(&dmc_pmu_mutex); - for_each_available_child_of_node(np, node) { + for_each_available_child_of_node_scoped(np, node) { error = rockchip_pm_add_one_domain(pmu, node); if (error) { dev_err(dev, "failed to handle node %pOFn: %d\n", node, error); - of_node_put(node); goto err_out; } @@ -931,7 +924,6 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev) if (error < 0) { dev_err(dev, "failed to handle subdomain node %pOFn: %d\n", node, error); - of_node_put(node); goto err_out; } } From da64dae42672a03eb877ebf21bde847215f5fa29 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Aug 2024 14:51:06 +0200 Subject: [PATCH 22/39] pmdomain: rockchip: Simplify locking with guard() Simplify error handling (smaller error handling) over locks with guard(). Signed-off-by: Krzysztof Kozlowski Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20240823-cleanup-h-guard-pm-domain-v1-2-8320722eaf39@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/rockchip/pm-domains.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 5679ad336a11..538dde58d924 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -910,7 +910,7 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev) * Prevent any rockchip_pmu_block() from racing with the remainder of * setup (clocks, register initialization). */ - mutex_lock(&dmc_pmu_mutex); + guard(mutex)(&dmc_pmu_mutex); for_each_available_child_of_node_scoped(np, node) { error = rockchip_pm_add_one_domain(pmu, node); @@ -943,13 +943,10 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev) if (!WARN_ON_ONCE(dmc_pmu)) dmc_pmu = pmu; - mutex_unlock(&dmc_pmu_mutex); - return 0; err_out: rockchip_pm_domain_cleanup(pmu); - mutex_unlock(&dmc_pmu_mutex); return error; } From 3e4d109ee8fca30b59f5f0382498a9a9ee90f3ea Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Aug 2024 14:51:07 +0200 Subject: [PATCH 23/39] pmdomain: imx: gpc: Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240823-cleanup-h-guard-pm-domain-v1-3-8320722eaf39@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/gpc.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c index 9517cce93d8a..80a4dcc77199 100644 --- a/drivers/pmdomain/imx/gpc.c +++ b/drivers/pmdomain/imx/gpc.c @@ -455,7 +455,6 @@ static int imx_gpc_probe(struct platform_device *pdev) } else { struct imx_pm_domain *domain; struct platform_device *pd_pdev; - struct device_node *np; struct clk *ipg_clk; unsigned int ipg_rate_mhz; int domain_index; @@ -465,28 +464,24 @@ static int imx_gpc_probe(struct platform_device *pdev) return PTR_ERR(ipg_clk); ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000; - for_each_child_of_node(pgc_node, np) { + for_each_child_of_node_scoped(pgc_node, np) { ret = of_property_read_u32(np, "reg", &domain_index); - if (ret) { - of_node_put(np); + if (ret) return ret; - } + if (domain_index >= of_id_data->num_domains) continue; pd_pdev = platform_device_alloc("imx-pgc-power-domain", domain_index); - if (!pd_pdev) { - of_node_put(np); + if (!pd_pdev) return -ENOMEM; - } ret = platform_device_add_data(pd_pdev, &imx_gpc_domains[domain_index], sizeof(imx_gpc_domains[domain_index])); if (ret) { platform_device_put(pd_pdev); - of_node_put(np); return ret; } domain = pd_pdev->dev.platform_data; @@ -500,7 +495,6 @@ static int imx_gpc_probe(struct platform_device *pdev) ret = platform_device_add(pd_pdev); if (ret) { platform_device_put(pd_pdev); - of_node_put(np); return ret; } } From 13bd778c900537f3fff7cfb671ff2eb0e92feee6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Aug 2024 14:51:08 +0200 Subject: [PATCH 24/39] pmdomain: imx: gpcv2: Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240823-cleanup-h-guard-pm-domain-v1-4-8320722eaf39@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/gpcv2.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/pmdomain/imx/gpcv2.c b/drivers/pmdomain/imx/gpcv2.c index 856eaac0ec14..963d61c5af6d 100644 --- a/drivers/pmdomain/imx/gpcv2.c +++ b/drivers/pmdomain/imx/gpcv2.c @@ -1458,7 +1458,7 @@ static int imx_gpcv2_probe(struct platform_device *pdev) .max_register = SZ_4K, }; struct device *dev = &pdev->dev; - struct device_node *pgc_np, *np; + struct device_node *pgc_np; struct regmap *regmap; void __iomem *base; int ret; @@ -1480,7 +1480,7 @@ static int imx_gpcv2_probe(struct platform_device *pdev) return ret; } - for_each_child_of_node(pgc_np, np) { + for_each_child_of_node_scoped(pgc_np, np) { struct platform_device *pd_pdev; struct imx_pgc_domain *domain; u32 domain_index; @@ -1491,7 +1491,6 @@ static int imx_gpcv2_probe(struct platform_device *pdev) ret = of_property_read_u32(np, "reg", &domain_index); if (ret) { dev_err(dev, "Failed to read 'reg' property\n"); - of_node_put(np); return ret; } @@ -1506,7 +1505,6 @@ static int imx_gpcv2_probe(struct platform_device *pdev) domain_index); if (!pd_pdev) { dev_err(dev, "Failed to allocate platform device\n"); - of_node_put(np); return -ENOMEM; } @@ -1515,7 +1513,6 @@ static int imx_gpcv2_probe(struct platform_device *pdev) sizeof(domain_data->domains[domain_index])); if (ret) { platform_device_put(pd_pdev); - of_node_put(np); return ret; } @@ -1532,7 +1529,6 @@ static int imx_gpcv2_probe(struct platform_device *pdev) ret = platform_device_add(pd_pdev); if (ret) { platform_device_put(pd_pdev); - of_node_put(np); return ret; } } From 584dc41b3d5750a5a57be46c96708eee1092eb30 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Aug 2024 14:51:09 +0200 Subject: [PATCH 25/39] pmdomain: qcom: cpr: Simplify with dev_err_probe() Use dev_err_probe() to make defer code handling simpler. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240823-cleanup-h-guard-pm-domain-v1-5-8320722eaf39@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/cpr.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/pmdomain/qcom/cpr.c b/drivers/pmdomain/qcom/cpr.c index 37b318bf505a..87d1db1a0019 100644 --- a/drivers/pmdomain/qcom/cpr.c +++ b/drivers/pmdomain/qcom/cpr.c @@ -1464,9 +1464,8 @@ static int cpr_pd_attach_dev(struct generic_pm_domain *domain, */ drv->cpu_clk = devm_clk_get(dev, NULL); if (IS_ERR(drv->cpu_clk)) { - ret = PTR_ERR(drv->cpu_clk); - if (ret != -EPROBE_DEFER) - dev_err(drv->dev, "could not get cpu clk: %d\n", ret); + ret = dev_err_probe(drv->dev, PTR_ERR(drv->cpu_clk), + "could not get cpu clk\n"); goto unlock; } drv->attached_cpu_dev = dev; From ba3a65c69bdbff04ea5699597bf491de5cbe82e9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Aug 2024 14:51:10 +0200 Subject: [PATCH 26/39] pmdomain: qcom: cpr: Simplify locking with guard() Simplify error handling (less gotos) over locks with guard(). Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240823-cleanup-h-guard-pm-domain-v1-6-8320722eaf39@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/cpr.c | 57 +++++++++++++++---------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/drivers/pmdomain/qcom/cpr.c b/drivers/pmdomain/qcom/cpr.c index 87d1db1a0019..e1fca65b80be 100644 --- a/drivers/pmdomain/qcom/cpr.c +++ b/drivers/pmdomain/qcom/cpr.c @@ -4,6 +4,7 @@ * Copyright (c) 2019, Linaro Limited */ +#include #include #include #include @@ -747,9 +748,9 @@ static int cpr_set_performance_state(struct generic_pm_domain *domain, struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); struct corner *corner, *end; enum voltage_change_dir dir; - int ret = 0, new_uV; + int ret, new_uV; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); dev_dbg(drv->dev, "%s: setting perf state: %u (prev state: %u)\n", __func__, state, cpr_get_cur_perf_state(drv)); @@ -760,10 +761,8 @@ static int cpr_set_performance_state(struct generic_pm_domain *domain, */ corner = drv->corners + state - 1; end = &drv->corners[drv->num_corners - 1]; - if (corner > end || corner < drv->corners) { - ret = -EINVAL; - goto unlock; - } + if (corner > end || corner < drv->corners) + return -EINVAL; /* Determine direction */ if (drv->corner > corner) @@ -783,7 +782,7 @@ static int cpr_set_performance_state(struct generic_pm_domain *domain, ret = cpr_scale_voltage(drv, corner, new_uV, dir); if (ret) - goto unlock; + return ret; if (cpr_is_allowed(drv)) { cpr_irq_clr(drv); @@ -794,10 +793,7 @@ static int cpr_set_performance_state(struct generic_pm_domain *domain, drv->corner = corner; -unlock: - mutex_unlock(&drv->lock); - - return ret; + return 0; } static int @@ -1437,9 +1433,9 @@ static int cpr_pd_attach_dev(struct generic_pm_domain *domain, { struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); const struct acc_desc *acc_desc = drv->acc_desc; - int ret = 0; + int ret; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); dev_dbg(drv->dev, "attach callback for: %s\n", dev_name(dev)); @@ -1451,7 +1447,7 @@ static int cpr_pd_attach_dev(struct generic_pm_domain *domain, * additional initialization when further CPUs get attached. */ if (drv->attached_cpu_dev) - goto unlock; + return 0; /* * cpr_scale_voltage() requires the direction (if we are changing @@ -1463,11 +1459,10 @@ static int cpr_pd_attach_dev(struct generic_pm_domain *domain, * the first time cpr_set_performance_state() is called. */ drv->cpu_clk = devm_clk_get(dev, NULL); - if (IS_ERR(drv->cpu_clk)) { - ret = dev_err_probe(drv->dev, PTR_ERR(drv->cpu_clk), - "could not get cpu clk\n"); - goto unlock; - } + if (IS_ERR(drv->cpu_clk)) + return dev_err_probe(drv->dev, PTR_ERR(drv->cpu_clk), + "could not get cpu clk\n"); + drv->attached_cpu_dev = dev; dev_dbg(drv->dev, "using cpu clk from: %s\n", @@ -1484,42 +1479,39 @@ static int cpr_pd_attach_dev(struct generic_pm_domain *domain, ret = dev_pm_opp_get_opp_count(&drv->pd.dev); if (ret < 0) { dev_err(drv->dev, "could not get OPP count\n"); - goto unlock; + return ret; } drv->num_corners = ret; if (drv->num_corners < 2) { dev_err(drv->dev, "need at least 2 OPPs to use CPR\n"); - ret = -EINVAL; - goto unlock; + return -EINVAL; } drv->corners = devm_kcalloc(drv->dev, drv->num_corners, sizeof(*drv->corners), GFP_KERNEL); - if (!drv->corners) { - ret = -ENOMEM; - goto unlock; - } + if (!drv->corners) + return -ENOMEM; ret = cpr_corner_init(drv); if (ret) - goto unlock; + return ret; cpr_set_loop_allowed(drv); ret = cpr_init_parameters(drv); if (ret) - goto unlock; + return ret; /* Configure CPR HW but keep it disabled */ ret = cpr_config(drv); if (ret) - goto unlock; + return ret; ret = cpr_find_initial_corner(drv); if (ret) - goto unlock; + return ret; if (acc_desc->config) regmap_multi_reg_write(drv->tcsr, acc_desc->config, @@ -1534,10 +1526,7 @@ static int cpr_pd_attach_dev(struct generic_pm_domain *domain, dev_info(drv->dev, "driver initialized with %u OPPs\n", drv->num_corners); -unlock: - mutex_unlock(&drv->lock); - - return ret; + return 0; } static int cpr_debug_info_show(struct seq_file *s, void *unused) From 005d29ac591a8d3cf0fb6f34c2045d0690992148 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Aug 2024 14:51:11 +0200 Subject: [PATCH 27/39] pmdomain: qcom: rpmhpd: Simplify locking with guard() Simplify error handling (less gotos) over locks with guard(). Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240823-cleanup-h-guard-pm-domain-v1-7-8320722eaf39@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index d2cb4271a1ca..65505e1e2219 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ +#include #include #include #include @@ -775,9 +776,9 @@ static int rpmhpd_set_performance_state(struct generic_pm_domain *domain, unsigned int level) { struct rpmhpd *pd = domain_to_rpmhpd(domain); - int ret = 0, i; + int ret, i; - mutex_lock(&rpmhpd_lock); + guard(mutex)(&rpmhpd_lock); for (i = 0; i < pd->level_count; i++) if (level <= pd->level[i]) @@ -797,14 +798,12 @@ static int rpmhpd_set_performance_state(struct generic_pm_domain *domain, ret = rpmhpd_aggregate_corner(pd, i); if (ret) - goto out; + return ret; } pd->corner = i; -out: - mutex_unlock(&rpmhpd_lock); - return ret; + return 0; } static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) From f3185222ccce1026cf077baa7fc438c12fa4cf59 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Aug 2024 14:51:12 +0200 Subject: [PATCH 28/39] pmdomain: qcom: rpmpd: Simplify locking with guard() Simplify error handling (less gotos) over locks with guard(). Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240823-cleanup-h-guard-pm-domain-v1-8-8320722eaf39@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmpd.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/pmdomain/qcom/rpmpd.c b/drivers/pmdomain/qcom/rpmpd.c index 5e6280b4cf70..0be6b3026e3a 100644 --- a/drivers/pmdomain/qcom/rpmpd.c +++ b/drivers/pmdomain/qcom/rpmpd.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. */ +#include #include #include #include @@ -1024,20 +1025,17 @@ static int rpmpd_power_on(struct generic_pm_domain *domain) int ret; struct rpmpd *pd = domain_to_rpmpd(domain); - mutex_lock(&rpmpd_lock); + guard(mutex)(&rpmpd_lock); ret = rpmpd_send_enable(pd, true); if (ret) - goto out; + return ret; pd->enabled = true; if (pd->corner) ret = rpmpd_aggregate_corner(pd); -out: - mutex_unlock(&rpmpd_lock); - return ret; } @@ -1060,27 +1058,21 @@ static int rpmpd_power_off(struct generic_pm_domain *domain) static int rpmpd_set_performance(struct generic_pm_domain *domain, unsigned int state) { - int ret = 0; struct rpmpd *pd = domain_to_rpmpd(domain); if (state > pd->max_state) state = pd->max_state; - mutex_lock(&rpmpd_lock); + guard(mutex)(&rpmpd_lock); pd->corner = state; /* Always send updates for vfc and vfl */ if (!pd->enabled && pd->key != cpu_to_le32(KEY_FLOOR_CORNER) && pd->key != cpu_to_le32(KEY_FLOOR_LEVEL)) - goto out; + return 0; - ret = rpmpd_aggregate_corner(pd); - -out: - mutex_unlock(&rpmpd_lock); - - return ret; + return rpmpd_aggregate_corner(pd); } static int rpmpd_probe(struct platform_device *pdev) From 06cee3c6b3844b0ee46dc15ce1bf938eeba2bb28 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Sun, 25 Aug 2024 16:34:00 +0200 Subject: [PATCH 29/39] pmdomain: imx93-pd: replace dev_err() with dev_err_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way, the code becomes more compact, and dev_err_probe() is used in every error path of the probe() function. Signed-off-by: Dario Binacchi Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20240825143428.556439-1-dario.binacchi@amarulasolutions.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/imx93-pd.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pmdomain/imx/imx93-pd.c b/drivers/pmdomain/imx/imx93-pd.c index d750a7dc58d2..44daecbe5cc3 100644 --- a/drivers/pmdomain/imx/imx93-pd.c +++ b/drivers/pmdomain/imx/imx93-pd.c @@ -125,11 +125,10 @@ static int imx93_pd_probe(struct platform_device *pdev) /* Just to sync the status of hardware */ if (!domain->init_off) { ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); - if (ret) { - dev_err(domain->dev, "failed to enable clocks for domain: %s\n", - domain->genpd.name); - return ret; - } + if (ret) + return dev_err_probe(domain->dev, ret, + "failed to enable clocks for domain: %s\n", + domain->genpd.name); } ret = pm_genpd_init(&domain->genpd, NULL, domain->init_off); From 28717ec8b948eedca5855ac4f587b45bcb1d57e5 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Sun, 25 Aug 2024 16:34:01 +0200 Subject: [PATCH 30/39] pmdomain: imx93-pd: don't unprepare clocks on driver remove The removed code was added to handle the case where the power domain is already on during the driver's probing. In this use case, the "is_off" parameter is passed as false to pm_genpd_init() to inform it not to call the power_on() callback, as it's unnecessary to perform the hardware power-on procedure since the power domain is already on. Therefore, with the call to clk_bulk_prepare_enable() by probe(), the system is in the same operational state as when "is_off" is passed as true after the power_on() callback execution: probe() -> is_off == true -> clk_bulk_prepare_enable() called by power_on() probe() -> is_off == false -> clk_bulk_prepare_enable() called by probe() Reaching the same logical and operational state, it follows that upon driver removal, there is no need to perform different actions depending on the power domain's on/off state during probing. Signed-off-by: Dario Binacchi Link: https://lore.kernel.org/r/20240825143428.556439-2-dario.binacchi@amarulasolutions.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/imx93-pd.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pmdomain/imx/imx93-pd.c b/drivers/pmdomain/imx/imx93-pd.c index 44daecbe5cc3..fb53a8e359bc 100644 --- a/drivers/pmdomain/imx/imx93-pd.c +++ b/drivers/pmdomain/imx/imx93-pd.c @@ -90,9 +90,6 @@ static void imx93_pd_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - if (!domain->init_off) - clk_bulk_disable_unprepare(domain->num_clks, domain->clks); - of_genpd_del_provider(np); pm_genpd_remove(&domain->genpd); } From 1a2e369aa2f7a187f0737355ec951bdb1bbc2e84 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Sun, 25 Aug 2024 16:34:02 +0200 Subject: [PATCH 31/39] pmdomain: imx93-pd: drop the context variable "init_off" This variable is only used within the probe() function, so let's remove it from the context and define it locally within the same function. Signed-off-by: Dario Binacchi Link: https://lore.kernel.org/r/20240825143428.556439-3-dario.binacchi@amarulasolutions.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/imx93-pd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pmdomain/imx/imx93-pd.c b/drivers/pmdomain/imx/imx93-pd.c index fb53a8e359bc..25ab592945bd 100644 --- a/drivers/pmdomain/imx/imx93-pd.c +++ b/drivers/pmdomain/imx/imx93-pd.c @@ -28,7 +28,6 @@ struct imx93_power_domain { void __iomem *addr; struct clk_bulk_data *clks; int num_clks; - bool init_off; }; #define to_imx93_pd(_genpd) container_of(_genpd, struct imx93_power_domain, genpd) @@ -99,6 +98,7 @@ static int imx93_pd_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct imx93_power_domain *domain; + bool init_off; int ret; domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL); @@ -118,9 +118,9 @@ static int imx93_pd_probe(struct platform_device *pdev) domain->genpd.power_on = imx93_pd_on; domain->dev = dev; - domain->init_off = readl(domain->addr + MIX_FUNC_STAT_OFF) & FUNC_STAT_ISO_STAT_MASK; + init_off = readl(domain->addr + MIX_FUNC_STAT_OFF) & FUNC_STAT_ISO_STAT_MASK; /* Just to sync the status of hardware */ - if (!domain->init_off) { + if (!init_off) { ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); if (ret) return dev_err_probe(domain->dev, ret, @@ -128,7 +128,7 @@ static int imx93_pd_probe(struct platform_device *pdev) domain->genpd.name); } - ret = pm_genpd_init(&domain->genpd, NULL, domain->init_off); + ret = pm_genpd_init(&domain->genpd, NULL, init_off); if (ret) goto err_clk_unprepare; @@ -144,7 +144,7 @@ err_genpd_remove: pm_genpd_remove(&domain->genpd); err_clk_unprepare: - if (!domain->init_off) + if (!init_off) clk_bulk_disable_unprepare(domain->num_clks, domain->clks); return ret; From 391a2e64d757a0d22a99676930b5aee29f4a4f35 Mon Sep 17 00:00:00 2001 From: Hongbo Li Date: Wed, 28 Aug 2024 20:12:30 +0800 Subject: [PATCH 32/39] pmdomain: mediatek: make use of dev_err_cast_probe() Using dev_err_cast_probe() to simplify the code. Signed-off-by: Hongbo Li Reviewed-by: Matthias Brugger Link: https://lore.kernel.org/r/20240828121230.3696315-1-lihongbo22@huawei.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index e274e3315fe7..88406e9ac63c 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -398,12 +398,10 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no scpsys->dev->of_node = node; pd->supply = devm_regulator_get(scpsys->dev, "domain"); scpsys->dev->of_node = root_node; - if (IS_ERR(pd->supply)) { - dev_err_probe(scpsys->dev, PTR_ERR(pd->supply), + if (IS_ERR(pd->supply)) + return dev_err_cast_probe(scpsys->dev, pd->supply, "%pOF: failed to get power supply.\n", node); - return ERR_CAST(pd->supply); - } } pd->infracfg = syscon_regmap_lookup_by_phandle_optional(node, "mediatek,infracfg"); From 4c621d6e667af6a41a0434fed6774abec7857801 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 25 Aug 2024 20:31:16 +0200 Subject: [PATCH 33/39] pmdomain: rockchip: Simplify dropping OF node reference Drop OF node reference immediately after using it in syscon_node_to_regmap(), which is both simpler and typical/expected code pattern. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20240825183116.102953-1-krzysztof.kozlowski@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/rockchip/pm-domains.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 538dde58d924..66e473065b6b 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -716,12 +716,11 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, goto err_unprepare_clocks; } pd->qos_regmap[j] = syscon_node_to_regmap(qos_node); + of_node_put(qos_node); if (IS_ERR(pd->qos_regmap[j])) { error = -ENODEV; - of_node_put(qos_node); goto err_unprepare_clocks; } - of_node_put(qos_node); } } From 8b579881de295d49a75f6312547f7813b1551a83 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 29 Aug 2024 16:20:47 -0400 Subject: [PATCH 34/39] pmdomain: rockchip: Add gating support Some rockchip SoC need to ungate power domains before their power status can be changed. Each power domain has a gate mask that is set to 1 to ungate it when manipulating power status, then set back to 0 to gate it again. Signed-off-by: Detlev Casanova Link: https://lore.kernel.org/r/20240829202732.75961-2-detlev.casanova@collabora.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/rockchip/pm-domains.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 66e473065b6b..b9e3dc45cb30 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -46,6 +46,7 @@ struct rockchip_domain_info { bool active_wakeup; int pwr_w_mask; int req_w_mask; + int clk_ungate_mask; int mem_status_mask; int repair_status_mask; u32 pwr_offset; @@ -63,6 +64,7 @@ struct rockchip_pmu_info { u32 chain_status_offset; u32 mem_status_offset; u32 repair_status_offset; + u32 clk_ungate_offset; u32 core_pwrcnt_offset; u32 gpu_pwrcnt_offset; @@ -303,6 +305,26 @@ static unsigned int rockchip_pmu_read_ack(struct rockchip_pmu *pmu) return val; } +static int rockchip_pmu_ungate_clk(struct rockchip_pm_domain *pd, bool ungate) +{ + const struct rockchip_domain_info *pd_info = pd->info; + struct rockchip_pmu *pmu = pd->pmu; + unsigned int val; + int clk_ungate_w_mask = pd_info->clk_ungate_mask << 16; + + if (!pd_info->clk_ungate_mask) + return 0; + + if (!pmu->info->clk_ungate_offset) + return 0; + + val = ungate ? (pd_info->clk_ungate_mask | clk_ungate_w_mask) : + clk_ungate_w_mask; + regmap_write(pmu->regmap, pmu->info->clk_ungate_offset, val); + + return 0; +} + static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd, bool idle) { @@ -543,6 +565,8 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on) return ret; } + rockchip_pmu_ungate_clk(pd, true); + if (!power_on) { rockchip_pmu_save_qos(pd); @@ -559,6 +583,7 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on) rockchip_pmu_restore_qos(pd); } + rockchip_pmu_ungate_clk(pd, false); clk_bulk_disable(pd->num_clks, pd->clks); } From d030e94d8127d79d941a94211250060431720614 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 29 Aug 2024 16:20:48 -0400 Subject: [PATCH 35/39] pmdomain: rockchip: Add gating masks for rk3576 The RK3576 SoC needs to ungate the power domains before their status can be modified. The values have been taken from the rockchip downstream driver. Signed-off-by: Detlev Casanova Link: https://lore.kernel.org/r/20240829202732.75961-3-detlev.casanova@collabora.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/rockchip/pm-domains.c | 62 +++++++++++++++++--------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index b9e3dc45cb30..cb0f93800138 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -147,6 +147,25 @@ struct rockchip_pmu { .active_wakeup = wakeup, \ } +#define DOMAIN_M_O_R_G(_name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, ack, g_mask, wakeup) \ +{ \ + .name = _name, \ + .pwr_offset = p_offset, \ + .pwr_w_mask = (pwr) << 16, \ + .pwr_mask = (pwr), \ + .status_mask = (status), \ + .mem_offset = m_offset, \ + .mem_status_mask = (m_status), \ + .repair_status_mask = (r_status), \ + .req_offset = r_offset, \ + .req_w_mask = (req) << 16, \ + .req_mask = (req), \ + .idle_mask = (idle), \ + .clk_ungate_mask = (g_mask), \ + .ack_mask = (ack), \ + .active_wakeup = wakeup, \ +} + #define DOMAIN_RK3036(_name, req, ack, idle, wakeup) \ { \ .name = _name, \ @@ -178,8 +197,8 @@ struct rockchip_pmu { #define DOMAIN_RK3568(name, pwr, req, wakeup) \ DOMAIN_M(name, pwr, pwr, req, req, req, wakeup) -#define DOMAIN_RK3576(name, p_offset, pwr, status, r_status, r_offset, req, idle, wakeup) \ - DOMAIN_M_O_R(name, p_offset, pwr, status, 0, r_status, r_status, r_offset, req, idle, idle, wakeup) +#define DOMAIN_RK3576(name, p_offset, pwr, status, r_status, r_offset, req, idle, g_mask, wakeup) \ + DOMAIN_M_O_R_G(name, p_offset, pwr, status, 0, r_status, r_status, r_offset, req, idle, idle, g_mask, wakeup) /* * Dynamic Memory Controller may need to coordinate with us -- see @@ -1124,25 +1143,25 @@ static const struct rockchip_domain_info rk3568_pm_domains[] = { }; static const struct rockchip_domain_info rk3576_pm_domains[] = { - [RK3576_PD_NPU] = DOMAIN_RK3576("npu", 0x0, BIT(0), BIT(0), 0, 0x0, 0, 0, false), - [RK3576_PD_NVM] = DOMAIN_RK3576("nvm", 0x0, BIT(6), 0, BIT(6), 0x4, BIT(2), BIT(18), false), - [RK3576_PD_SDGMAC] = DOMAIN_RK3576("sdgmac", 0x0, BIT(7), 0, BIT(7), 0x4, BIT(1), BIT(17), false), - [RK3576_PD_AUDIO] = DOMAIN_RK3576("audio", 0x0, BIT(8), 0, BIT(8), 0x4, BIT(0), BIT(16), false), - [RK3576_PD_PHP] = DOMAIN_RK3576("php", 0x0, BIT(9), 0, BIT(9), 0x0, BIT(15), BIT(15), false), - [RK3576_PD_SUBPHP] = DOMAIN_RK3576("subphp", 0x0, BIT(10), 0, BIT(10), 0x0, 0, 0, false), - [RK3576_PD_VOP] = DOMAIN_RK3576("vop", 0x0, BIT(11), 0, BIT(11), 0x0, 0x6000, 0x6000, false), - [RK3576_PD_VO1] = DOMAIN_RK3576("vo1", 0x0, BIT(14), 0, BIT(14), 0x0, BIT(12), BIT(12), false), - [RK3576_PD_VO0] = DOMAIN_RK3576("vo0", 0x0, BIT(15), 0, BIT(15), 0x0, BIT(11), BIT(11), false), - [RK3576_PD_USB] = DOMAIN_RK3576("usb", 0x4, BIT(0), 0, BIT(16), 0x0, BIT(10), BIT(10), true), - [RK3576_PD_VI] = DOMAIN_RK3576("vi", 0x4, BIT(1), 0, BIT(17), 0x0, BIT(9), BIT(9), false), - [RK3576_PD_VEPU0] = DOMAIN_RK3576("vepu0", 0x4, BIT(2), 0, BIT(18), 0x0, BIT(7), BIT(7), false), - [RK3576_PD_VEPU1] = DOMAIN_RK3576("vepu1", 0x4, BIT(3), 0, BIT(19), 0x0, BIT(8), BIT(8), false), - [RK3576_PD_VDEC] = DOMAIN_RK3576("vdec", 0x4, BIT(4), 0, BIT(20), 0x0, BIT(6), BIT(6), false), - [RK3576_PD_VPU] = DOMAIN_RK3576("vpu", 0x4, BIT(5), 0, BIT(21), 0x0, BIT(5), BIT(5), false), - [RK3576_PD_NPUTOP] = DOMAIN_RK3576("nputop", 0x4, BIT(6), 0, BIT(22), 0x0, 0x18, 0x18, false), - [RK3576_PD_NPU0] = DOMAIN_RK3576("npu0", 0x4, BIT(7), 0, BIT(23), 0x0, BIT(1), BIT(1), false), - [RK3576_PD_NPU1] = DOMAIN_RK3576("npu1", 0x4, BIT(8), 0, BIT(24), 0x0, BIT(2), BIT(2), false), - [RK3576_PD_GPU] = DOMAIN_RK3576("gpu", 0x4, BIT(9), 0, BIT(25), 0x0, BIT(0), BIT(0), false), + [RK3576_PD_NPU] = DOMAIN_RK3576("npu", 0x0, BIT(0), BIT(0), 0, 0x0, 0, 0, 0, false), + [RK3576_PD_NVM] = DOMAIN_RK3576("nvm", 0x0, BIT(6), 0, BIT(6), 0x4, BIT(2), BIT(18), BIT(2), false), + [RK3576_PD_SDGMAC] = DOMAIN_RK3576("sdgmac", 0x0, BIT(7), 0, BIT(7), 0x4, BIT(1), BIT(17), 0x6, false), + [RK3576_PD_AUDIO] = DOMAIN_RK3576("audio", 0x0, BIT(8), 0, BIT(8), 0x4, BIT(0), BIT(16), BIT(0), false), + [RK3576_PD_PHP] = DOMAIN_RK3576("php", 0x0, BIT(9), 0, BIT(9), 0x0, BIT(15), BIT(15), BIT(15), false), + [RK3576_PD_SUBPHP] = DOMAIN_RK3576("subphp", 0x0, BIT(10), 0, BIT(10), 0x0, 0, 0, 0, false), + [RK3576_PD_VOP] = DOMAIN_RK3576("vop", 0x0, BIT(11), 0, BIT(11), 0x0, 0x6000, 0x6000, 0x6000, false), + [RK3576_PD_VO1] = DOMAIN_RK3576("vo1", 0x0, BIT(14), 0, BIT(14), 0x0, BIT(12), BIT(12), 0x7000, false), + [RK3576_PD_VO0] = DOMAIN_RK3576("vo0", 0x0, BIT(15), 0, BIT(15), 0x0, BIT(11), BIT(11), 0x6800, false), + [RK3576_PD_USB] = DOMAIN_RK3576("usb", 0x4, BIT(0), 0, BIT(16), 0x0, BIT(10), BIT(10), 0x6400, true), + [RK3576_PD_VI] = DOMAIN_RK3576("vi", 0x4, BIT(1), 0, BIT(17), 0x0, BIT(9), BIT(9), BIT(9), false), + [RK3576_PD_VEPU0] = DOMAIN_RK3576("vepu0", 0x4, BIT(2), 0, BIT(18), 0x0, BIT(7), BIT(7), 0x280, false), + [RK3576_PD_VEPU1] = DOMAIN_RK3576("vepu1", 0x4, BIT(3), 0, BIT(19), 0x0, BIT(8), BIT(8), BIT(8), false), + [RK3576_PD_VDEC] = DOMAIN_RK3576("vdec", 0x4, BIT(4), 0, BIT(20), 0x0, BIT(6), BIT(6), BIT(6), false), + [RK3576_PD_VPU] = DOMAIN_RK3576("vpu", 0x4, BIT(5), 0, BIT(21), 0x0, BIT(5), BIT(5), BIT(5), false), + [RK3576_PD_NPUTOP] = DOMAIN_RK3576("nputop", 0x4, BIT(6), 0, BIT(22), 0x0, 0x18, 0x18, 0x18, false), + [RK3576_PD_NPU0] = DOMAIN_RK3576("npu0", 0x4, BIT(7), 0, BIT(23), 0x0, BIT(1), BIT(1), 0x1a, false), + [RK3576_PD_NPU1] = DOMAIN_RK3576("npu1", 0x4, BIT(8), 0, BIT(24), 0x0, BIT(2), BIT(2), 0x1c, false), + [RK3576_PD_GPU] = DOMAIN_RK3576("gpu", 0x4, BIT(9), 0, BIT(25), 0x0, BIT(0), BIT(0), BIT(0), false), }; static const struct rockchip_domain_info rk3588_pm_domains[] = { @@ -1333,6 +1352,7 @@ static const struct rockchip_pmu_info rk3576_pmu = { .idle_offset = 0x128, .ack_offset = 0x120, .repair_status_offset = 0x570, + .clk_ungate_offset = 0x140, .num_domains = ARRAY_SIZE(rk3576_pm_domains), .domain_info = rk3576_pm_domains, From 692c20c4d075bd452acfbbc68200fc226c7c9496 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 4 Sep 2024 16:30:45 +0200 Subject: [PATCH 36/39] pmdomain: core: Harden inter-column space in debug summary The inter-column space in the debug summary is two spaces. However, in one case, the extra space is handled implicitly in a field width specifier. Make inter-column space explicit to ease future maintenance. Fixes: 45fbc464b047 ("PM: domains: Add "performance" column to debug summary") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/ae61eb363621b981edde878e1e74d701702a579f.1725459707.git.geert+renesas@glider.be Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 2731b285e017..fa9d538045fb 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -3271,7 +3271,7 @@ static int genpd_summary_one(struct seq_file *s, else snprintf(state, sizeof(state), "%s", status_lookup[genpd->status]); - seq_printf(s, "%-30s %-50s %u", genpd->name, state, genpd->performance_state); + seq_printf(s, "%-30s %-49s %u", genpd->name, state, genpd->performance_state); /* * Modifications on the list require holding locks on both From 987a43e89ec67cc68518c0558db42ba542581597 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 4 Sep 2024 16:30:46 +0200 Subject: [PATCH 37/39] pmdomain: core: Fix "managed by" alignment in debug summary The "performance" column contains variable-width values. Hence when their printed values contain more than one digit, all values in successive columns become misaligned. Fix this by formatting it as a fixed-width field. Adjust successive spaces and field widths to retain the exiting layout. Fixes: 0155aaf95a2a ("PM: domains: Add the domain HW-managed mode to the summary") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/e004f9d2a75e9a49c269507bb8a4514001751e85.1725459707.git.geert+renesas@glider.be Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index fa9d538045fb..5e6aa8747303 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -3236,7 +3236,7 @@ static void mode_status_str(struct seq_file *s, struct device *dev) gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); - seq_printf(s, "%20s", gpd_data->hw_mode ? "HW" : "SW"); + seq_printf(s, "%9s", gpd_data->hw_mode ? "HW" : "SW"); } static void perf_status_str(struct seq_file *s, struct device *dev) @@ -3244,7 +3244,7 @@ static void perf_status_str(struct seq_file *s, struct device *dev) struct generic_pm_domain_data *gpd_data; gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); - seq_put_decimal_ull(s, "", gpd_data->performance_state); + seq_printf(s, "%-10u ", gpd_data->performance_state); } static int genpd_summary_one(struct seq_file *s, From 2fc934190e7118f7c7ddd748302df44bde1015f6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 4 Sep 2024 16:30:47 +0200 Subject: [PATCH 38/39] pmdomain: core: Move mode_status_str() Move mode_status_str() below perf_status_str(), to make declaration order match calling order of the various *_status_str() helpers. While at it, add a blank line for consistency among the three helpers. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/18ed6fb2bb92860f3af1bc7e5e4a01e9dacf2126.1725459707.git.geert+renesas@glider.be Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 5e6aa8747303..c2d2aba4c3e6 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -3230,6 +3230,15 @@ static void rtpm_status_str(struct seq_file *s, struct device *dev) seq_printf(s, "%-25s ", p); } +static void perf_status_str(struct seq_file *s, struct device *dev) +{ + struct generic_pm_domain_data *gpd_data; + + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); + + seq_printf(s, "%-10u ", gpd_data->performance_state); +} + static void mode_status_str(struct seq_file *s, struct device *dev) { struct generic_pm_domain_data *gpd_data; @@ -3239,14 +3248,6 @@ static void mode_status_str(struct seq_file *s, struct device *dev) seq_printf(s, "%9s", gpd_data->hw_mode ? "HW" : "SW"); } -static void perf_status_str(struct seq_file *s, struct device *dev) -{ - struct generic_pm_domain_data *gpd_data; - - gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); - seq_printf(s, "%-10u ", gpd_data->performance_state); -} - static int genpd_summary_one(struct seq_file *s, struct generic_pm_domain *genpd) { From c6ccb691d484544636bc4a097574c5c135ccccda Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 4 Sep 2024 16:30:48 +0200 Subject: [PATCH 39/39] pmdomain: core: Reduce debug summary table width Commit 9094e53ff5c86ebe ("pmdomain: core: Use dev_name() instead of kobject_get_path() in debugfs") severely shortened the names of devices in a PM Domain. Now the most common format[1] consists of a 32-bit unit-address (8 characters), followed by a dot and a node name (20 characters for "air-pollution-sensor" and "interrupt-controller", which are the longest generic node names documented in the Devicetree Specification), for a typical maximum of 29 characters. This offers a good opportunity to reduce the table width of the debug summary: - Reduce the device name field width from 50 to 30 characters, which matches the PM Domain name width, - Reduce the large inter-column space between the "performance" and "managed by" columns. Visual impact: - The "performance" column now starts at a position that is a multiple of 16, just like the "status" and "children" columns, - All of the "/device", "runtime status", and "managed by" columns are now indented 4 characters more than the columns right above them, - Everything fits in (one less than) 80 characters again ;-) [1] Note that some device names (e.g. TI AM335x interconnect target modules) do not follow this convention, and may be much longer, but these didn't fit in the old 50-character column width either. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/f8e1821364b6d5d11350447c128f6d2b470f33fe.1725459707.git.geert+renesas@glider.be Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index c2d2aba4c3e6..5ede0f7eda09 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -3227,7 +3227,7 @@ static void rtpm_status_str(struct seq_file *s, struct device *dev) else WARN_ON(1); - seq_printf(s, "%-25s ", p); + seq_printf(s, "%-26s ", p); } static void perf_status_str(struct seq_file *s, struct device *dev) @@ -3245,7 +3245,7 @@ static void mode_status_str(struct seq_file *s, struct device *dev) gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); - seq_printf(s, "%9s", gpd_data->hw_mode ? "HW" : "SW"); + seq_printf(s, "%2s", gpd_data->hw_mode ? "HW" : "SW"); } static int genpd_summary_one(struct seq_file *s, @@ -3272,7 +3272,7 @@ static int genpd_summary_one(struct seq_file *s, else snprintf(state, sizeof(state), "%s", status_lookup[genpd->status]); - seq_printf(s, "%-30s %-49s %u", genpd->name, state, genpd->performance_state); + seq_printf(s, "%-30s %-30s %u", genpd->name, state, genpd->performance_state); /* * Modifications on the list require holding locks on both @@ -3288,7 +3288,7 @@ static int genpd_summary_one(struct seq_file *s, } list_for_each_entry(pm_data, &genpd->dev_list, list_node) { - seq_printf(s, "\n %-50s ", dev_name(pm_data->dev)); + seq_printf(s, "\n %-30s ", dev_name(pm_data->dev)); rtpm_status_str(s, pm_data->dev); perf_status_str(s, pm_data->dev); mode_status_str(s, pm_data->dev); @@ -3306,9 +3306,9 @@ static int summary_show(struct seq_file *s, void *data) struct generic_pm_domain *genpd; int ret = 0; - seq_puts(s, "domain status children performance\n"); - seq_puts(s, " /device runtime status managed by\n"); - seq_puts(s, "------------------------------------------------------------------------------------------------------------\n"); + seq_puts(s, "domain status children performance\n"); + seq_puts(s, " /device runtime status managed by\n"); + seq_puts(s, "------------------------------------------------------------------------------\n"); ret = mutex_lock_interruptible(&gpd_list_lock); if (ret)