From 5bbe5872fed4497a192556426d75fd9223c5bfeb Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Wed, 29 May 2024 11:10:33 +0800 Subject: dt-bindings: power: add Amlogic A4 power domains Add devicetree binding document and related header file for Amlogic A4 secure power domains. Acked-by: Krzysztof Kozlowski Signed-off-by: Xianwei Zhao Link: https://lore.kernel.org/r/20240529-a4_secpowerdomain-v2-1-47502fc0eaf3@amlogic.com Signed-off-by: Ulf Hansson --- .../bindings/power/amlogic,meson-sec-pwrc.yaml | 1 + include/dt-bindings/power/amlogic,a4-pwrc.h | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 include/dt-bindings/power/amlogic,a4-pwrc.h diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml index dab3d92bc273..59915f9d4860 100644 --- a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml +++ b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml @@ -20,6 +20,7 @@ properties: enum: - amlogic,meson-a1-pwrc - amlogic,meson-s4-pwrc + - amlogic,a4-pwrc - amlogic,c3-pwrc - amlogic,t7-pwrc diff --git a/include/dt-bindings/power/amlogic,a4-pwrc.h b/include/dt-bindings/power/amlogic,a4-pwrc.h new file mode 100644 index 000000000000..bd2f9c558d22 --- /dev/null +++ b/include/dt-bindings/power/amlogic,a4-pwrc.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ +#ifndef _DT_BINDINGS_AMLOGIC_A4_POWER_H +#define _DT_BINDINGS_AMLOGIC_A4_POWER_H + +#define PWRC_A4_AUDIO_ID 0 +#define PWRC_A4_SDIOA_ID 1 +#define PWRC_A4_EMMC_ID 2 +#define PWRC_A4_USB_COMB_ID 3 +#define PWRC_A4_ETH_ID 4 +#define PWRC_A4_VOUT_ID 5 +#define PWRC_A4_AUDIO_PDM_ID 6 +#define PWRC_A4_DMC_ID 7 +#define PWRC_A4_SYS_WRAP_ID 8 +#define PWRC_A4_AO_I2C_S_ID 9 +#define PWRC_A4_AO_UART_ID 10 +#define PWRC_A4_AO_IR_ID 11 + +#endif -- cgit From ee4f8b74ecec3d9b338175fe2733c6188b69ff09 Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Wed, 29 May 2024 11:10:34 +0800 Subject: pmdomain: amlogic: Add support for A4 power domains controller Add support for A4 power controller. A4 power control registers are in secure domain, and should be accessed by SMC. Reviewed-by: Neil Armstrong Signed-off-by: Xianwei Zhao Link: https://lore.kernel.org/r/20240529-a4_secpowerdomain-v2-2-47502fc0eaf3@amlogic.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/amlogic/meson-secure-pwrc.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c index 4d5bda0d60fc..3a84d8a74a5e 100644 --- a/drivers/pmdomain/amlogic/meson-secure-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,24 @@ static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { SEC_PD(RSA, 0), }; +static struct meson_secure_pwrc_domain_desc a4_pwrc_domains[] = { + SEC_PD(A4_AUDIO, 0), + SEC_PD(A4_SDIOA, 0), + SEC_PD(A4_EMMC, 0), + SEC_PD(A4_USB_COMB, 0), + SEC_PD(A4_ETH, 0), + SEC_PD(A4_VOUT, 0), + SEC_PD(A4_AUDIO_PDM, 0), + /* DMC is for DDR PHY ana/dig and DMC, and should be always on */ + SEC_PD(A4_DMC, GENPD_FLAG_ALWAYS_ON), + /* WRAP is secure_top, a lot of modules are included, and should be always on */ + SEC_PD(A4_SYS_WRAP, GENPD_FLAG_ALWAYS_ON), + SEC_PD(A4_AO_I2C_S, 0), + SEC_PD(A4_AO_UART, 0), + /* IR is wake up trigger source, and should be always on */ + SEC_PD(A4_AO_IR, GENPD_FLAG_ALWAYS_ON), +}; + static struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { SEC_PD(C3_NNA, 0), SEC_PD(C3_AUDIO, 0), @@ -311,6 +330,11 @@ static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = { .count = ARRAY_SIZE(a1_pwrc_domains), }; +static struct meson_secure_pwrc_domain_data amlogic_secure_a4_pwrc_data = { + .domains = a4_pwrc_domains, + .count = ARRAY_SIZE(a4_pwrc_domains), +}; + static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = { .domains = c3_pwrc_domains, .count = ARRAY_SIZE(c3_pwrc_domains), @@ -331,6 +355,10 @@ static const struct of_device_id meson_secure_pwrc_match_table[] = { .compatible = "amlogic,meson-a1-pwrc", .data = &meson_secure_a1_pwrc_data, }, + { + .compatible = "amlogic,a4-pwrc", + .data = &amlogic_secure_a4_pwrc_data, + }, { .compatible = "amlogic,c3-pwrc", .data = &amlogic_secure_c3_pwrc_data, -- cgit From 0ddaf2c1a2c6c3a7bdc275581cd3b6098e0e5353 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 29 May 2024 11:19:12 +0200 Subject: pmdomain: core: Use genpd_is_irq_safe() helper Use the genpd_is_irq_safe() helper instead of open-coding the same operation. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/3cd8131b4f1284bdffa23a2b6a9a1e92fd17973e.1716973951.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 623d15b68707..83d978743659 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -2079,7 +2079,7 @@ static void genpd_free_data(struct generic_pm_domain *genpd) static void genpd_lock_init(struct generic_pm_domain *genpd) { - if (genpd->flags & GENPD_FLAG_IRQ_SAFE) { + if (genpd_is_irq_safe(genpd)) { spin_lock_init(&genpd->slock); genpd->lock_ops = &genpd_spin_ops; } else { -- cgit From 196e9f5bf2c566682f52fb6a25276794dded2fe9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 29 May 2024 11:20:20 +0200 Subject: pmdomain: renesas: rmobile-sysc: Use for_each_child_of_node_scoped() Use the scoped variant of for_each_child_of_node() to simplify cleanup handling. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/5e684d7b236904e5f79324a5e5357c2930f7402d.1716974368.git.geert+renesas@glider.be Signed-off-by: Ulf Hansson --- drivers/pmdomain/renesas/rmobile-sysc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/pmdomain/renesas/rmobile-sysc.c b/drivers/pmdomain/renesas/rmobile-sysc.c index 0b77f37787d5..5848e79aa438 100644 --- a/drivers/pmdomain/renesas/rmobile-sysc.c +++ b/drivers/pmdomain/renesas/rmobile-sysc.c @@ -268,9 +268,7 @@ static int __init rmobile_add_pm_domains(void __iomem *base, struct device_node *parent, struct generic_pm_domain *genpd_parent) { - struct device_node *np; - - for_each_child_of_node(parent, np) { + for_each_child_of_node_scoped(parent, np) { struct rmobile_pm_domain *pd; u32 idx = ~0; @@ -279,10 +277,8 @@ static int __init rmobile_add_pm_domains(void __iomem *base, } pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) { - of_node_put(np); + if (!pd) return -ENOMEM; - } pd->genpd.name = np->name; pd->base = base; -- cgit From e5cddc34067acf7848f63c014761ed232172f027 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 14 May 2024 21:18:33 +0800 Subject: pmdomain: arm: scmi_pm_domain: set flag GENPD_FLAG_ACTIVE_WAKEUP Set flag GENPD_FLAG_ACTIVE_WAKEUP to scmi 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: Peng Fan Acked-by: Sudeep Holla Link: https://lore.kernel.org/r/20240514131833.911703-1-peng.fan@oss.nxp.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/arm/scmi_pm_domain.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c index 0e05a79de82d..a7784a8bb5db 100644 --- a/drivers/pmdomain/arm/scmi_pm_domain.c +++ b/drivers/pmdomain/arm/scmi_pm_domain.c @@ -102,6 +102,7 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev) scmi_pd->genpd.name = scmi_pd->name; scmi_pd->genpd.power_off = scmi_pd_power_off; scmi_pd->genpd.power_on = scmi_pd_power_on; + scmi_pd->genpd.flags = GENPD_FLAG_ACTIVE_WAKEUP; pm_genpd_init(&scmi_pd->genpd, NULL, state == SCMI_POWER_STATE_GENERIC_OFF); -- cgit From cc647e931a249605e06d3eb3ec89a7e320fc28cc Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 10 Jun 2024 16:13:55 -0700 Subject: pmdomain: amlogic: add missing MODULE_DESCRIPTION() macros On x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pmdomain/amlogic/meson-ee-pwrc.o Add the missing invocation of the MODULE_DESCRIPTION() macro to all files which have a MODULE_LICENSE(). This includes meson-secure-pwrc.c which, although it did not produce a warning with the x86 allmodconfig configuration, may cause this warning with other configurations where CONFIG_MESON_SM is enabled. Signed-off-by: Jeff Johnson Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20240610-md-drivers-pmdomain-amlogic-v1-1-b49ddb1a8bdf@quicinc.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/amlogic/meson-ee-pwrc.c | 1 + drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c | 1 + drivers/pmdomain/amlogic/meson-secure-pwrc.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/pmdomain/amlogic/meson-ee-pwrc.c b/drivers/pmdomain/amlogic/meson-ee-pwrc.c index fcec6eb610e4..fbb2b4103930 100644 --- a/drivers/pmdomain/amlogic/meson-ee-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-ee-pwrc.c @@ -648,4 +648,5 @@ static struct platform_driver meson_ee_pwrc_driver = { }, }; module_platform_driver(meson_ee_pwrc_driver); +MODULE_DESCRIPTION("Amlogic Meson Everything-Else Power Domains driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c b/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c index 33df520eab95..6028e91664a4 100644 --- a/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c +++ b/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c @@ -376,4 +376,5 @@ static struct platform_driver meson_gx_pwrc_vpu_driver = { }, }; module_platform_driver(meson_gx_pwrc_vpu_driver); +MODULE_DESCRIPTION("Amlogic Meson GX Power Domains driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c index 3a84d8a74a5e..df5567418226 100644 --- a/drivers/pmdomain/amlogic/meson-secure-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -383,4 +383,5 @@ static struct platform_driver meson_secure_pwrc_driver = { }, }; module_platform_driver(meson_secure_pwrc_driver); +MODULE_DESCRIPTION("Amlogic Meson Secure Power Domains driver"); MODULE_LICENSE("Dual MIT/GPL"); -- cgit From 32625ac9e110da530db4f0faf918b1f5674e7ca3 Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Thu, 27 Jun 2024 19:47:51 +0800 Subject: dt-bindings: power: add Amlogic A5 power domains Add devicetree binding document and related header file for Amlogic A5 secure power domains. Signed-off-by: Hongyu Chen Signed-off-by: Xianwei Zhao Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20240627-a5_secpower-v1-1-1f47dde1270c@amlogic.com Signed-off-by: Ulf Hansson --- .../bindings/power/amlogic,meson-sec-pwrc.yaml | 1 + include/dt-bindings/power/amlogic,a5-pwrc.h | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 include/dt-bindings/power/amlogic,a5-pwrc.h diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml index 59915f9d4860..15d74138baa3 100644 --- a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml +++ b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml @@ -21,6 +21,7 @@ properties: - amlogic,meson-a1-pwrc - amlogic,meson-s4-pwrc - amlogic,a4-pwrc + - amlogic,a5-pwrc - amlogic,c3-pwrc - amlogic,t7-pwrc diff --git a/include/dt-bindings/power/amlogic,a5-pwrc.h b/include/dt-bindings/power/amlogic,a5-pwrc.h new file mode 100644 index 000000000000..3a6f53eb959f --- /dev/null +++ b/include/dt-bindings/power/amlogic,a5-pwrc.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#ifndef _DT_BINDINGS_AMLOGIC_A5_POWER_H +#define _DT_BINDINGS_AMLOGIC_A5_POWER_H + +#define PWRC_A5_NNA_ID 0 +#define PWRC_A5_AUDIO_ID 1 +#define PWRC_A5_SDIOA_ID 2 +#define PWRC_A5_EMMC_ID 3 +#define PWRC_A5_USB_COMB_ID 4 +#define PWRC_A5_ETH_ID 5 +#define PWRC_A5_RSA_ID 6 +#define PWRC_A5_AUDIO_PDM_ID 7 +#define PWRC_A5_DMC_ID 8 +#define PWRC_A5_SYS_WRAP_ID 9 +#define PWRC_A5_DSPA_ID 10 + +#endif -- cgit From 8fbed2d7fbb5e1141ec2fbd6d4e426d9bf572e7c Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Thu, 27 Jun 2024 19:47:52 +0800 Subject: pmdomain: amlogic: Add support for A5 power domains controller Add support for the A5 power controller, whose registers are in the secure domain and should be accessed via SMC. Signed-off-by: Hongyu Chen Signed-off-by: Xianwei Zhao Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20240627-a5_secpower-v1-2-1f47dde1270c@amlogic.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/amlogic/meson-secure-pwrc.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c index df5567418226..f6729eea6b8c 100644 --- a/drivers/pmdomain/amlogic/meson-secure-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -155,6 +156,22 @@ static struct meson_secure_pwrc_domain_desc a4_pwrc_domains[] = { SEC_PD(A4_AO_IR, GENPD_FLAG_ALWAYS_ON), }; +static struct meson_secure_pwrc_domain_desc a5_pwrc_domains[] = { + SEC_PD(A5_NNA, 0), + SEC_PD(A5_AUDIO, 0), + SEC_PD(A5_SDIOA, 0), + SEC_PD(A5_EMMC, 0), + SEC_PD(A5_USB_COMB, 0), + SEC_PD(A5_ETH, 0), + SEC_PD(A5_RSA, 0), + SEC_PD(A5_AUDIO_PDM, 0), + /* DMC is for DDR PHY ana/dig and DMC, and should be always on */ + SEC_PD(A5_DMC, GENPD_FLAG_ALWAYS_ON), + /* WRAP is secure_top, a lot of modules are included, and should be always on */ + SEC_PD(A5_SYS_WRAP, GENPD_FLAG_ALWAYS_ON), + SEC_PD(A5_DSPA, 0), +}; + static struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { SEC_PD(C3_NNA, 0), SEC_PD(C3_AUDIO, 0), @@ -335,6 +352,11 @@ static struct meson_secure_pwrc_domain_data amlogic_secure_a4_pwrc_data = { .count = ARRAY_SIZE(a4_pwrc_domains), }; +static struct meson_secure_pwrc_domain_data amlogic_secure_a5_pwrc_data = { + .domains = a5_pwrc_domains, + .count = ARRAY_SIZE(a5_pwrc_domains), +}; + static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = { .domains = c3_pwrc_domains, .count = ARRAY_SIZE(c3_pwrc_domains), @@ -359,6 +381,10 @@ static const struct of_device_id meson_secure_pwrc_match_table[] = { .compatible = "amlogic,a4-pwrc", .data = &amlogic_secure_a4_pwrc_data, }, + { + .compatible = "amlogic,a5-pwrc", + .data = &amlogic_secure_a5_pwrc_data, + }, { .compatible = "amlogic,c3-pwrc", .data = &amlogic_secure_c3_pwrc_data, -- cgit From 95f6454da936e4e6418facddf66596442a842d74 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 24 Jun 2024 10:18:05 +0530 Subject: PM: domains: Allow devices attached to genpd to be managed by HW Some power-domains may be capable of relying on the HW to control the power for a device that's hooked up to it. Typically, for these kinds of configurations the consumer driver should be able to change the behavior of power domain at runtime, control the power domain in SW mode for certain configurations and handover the control to HW mode for other usecases. To allow a consumer driver to change the behaviour of the PM domain for its device, let's provide a new function, dev_pm_genpd_set_hwmode(). Moreover, let's add a corresponding optional genpd callback, ->set_hwmode_dev(), which the genpd provider should implement if it can support switching between HW controlled mode and SW controlled mode. Similarly, add the dev_pm_genpd_get_hwmode() to allow consumers to read the current mode and its corresponding optional genpd callback, ->get_hwmode_dev(), which the genpd provider can also implement to synchronize the initial HW mode state in genpd_add_device() by reading back the mode from the hardware. Signed-off-by: Ulf Hansson Signed-off-by: Abel Vesa Signed-off-by: Jagadeesh Kona Reviewed-by: Dmitry Baryshkov Reviewed-by: Dhruva Gole Reviewed-by: Taniya Das Link: https://lore.kernel.org/r/20240624044809.17751-2-quic_jkona@quicinc.com --- drivers/pmdomain/core.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 17 +++++++++++++ 2 files changed, 81 insertions(+) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 83d978743659..a9a409b2d25c 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -588,6 +588,68 @@ void dev_pm_genpd_synced_poweroff(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_genpd_synced_poweroff); +/** + * dev_pm_genpd_set_hwmode() - Set the HW mode for the device and its PM domain. + * + * @dev: Device for which the HW-mode should be changed. + * @enable: Value to set or unset the HW-mode. + * + * Some PM domains can rely on HW signals to control the power for a device. To + * allow a consumer driver to switch the behaviour for its device in runtime, + * which may be beneficial from a latency or energy point of view, this function + * may be called. + * + * It is assumed that the users guarantee that the genpd wouldn't be detached + * while this routine is getting called. + * + * Return: Returns 0 on success and negative error values on failures. + */ +int dev_pm_genpd_set_hwmode(struct device *dev, bool enable) +{ + struct generic_pm_domain *genpd; + int ret = 0; + + genpd = dev_to_genpd_safe(dev); + if (!genpd) + return -ENODEV; + + if (!genpd->set_hwmode_dev) + return -EOPNOTSUPP; + + genpd_lock(genpd); + + if (dev_gpd_data(dev)->hw_mode == enable) + goto out; + + ret = genpd->set_hwmode_dev(genpd, dev, enable); + if (!ret) + dev_gpd_data(dev)->hw_mode = enable; + +out: + genpd_unlock(genpd); + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_genpd_set_hwmode); + +/** + * dev_pm_genpd_get_hwmode() - Get the HW mode setting for the device. + * + * @dev: Device for which the current HW-mode setting should be fetched. + * + * This helper function allows consumer drivers to fetch the current HW mode + * setting of its the device. + * + * It is assumed that the users guarantee that the genpd wouldn't be detached + * while this routine is getting called. + * + * Return: Returns the HW mode setting of device from SW cached hw_mode. + */ +bool dev_pm_genpd_get_hwmode(struct device *dev) +{ + return dev_gpd_data(dev)->hw_mode; +} +EXPORT_SYMBOL_GPL(dev_pm_genpd_get_hwmode); + static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) { unsigned int state_idx = genpd->state_idx; @@ -1687,6 +1749,8 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, gpd_data->cpu = genpd_get_cpu(genpd, base_dev); + gpd_data->hw_mode = genpd->get_hwmode_dev ? genpd->get_hwmode_dev(genpd, dev) : false; + ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; if (ret) goto out; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index f24546a3d3db..015751b64746 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -175,6 +175,10 @@ struct generic_pm_domain { int (*set_performance_state)(struct generic_pm_domain *genpd, unsigned int state); struct gpd_dev_ops dev_ops; + int (*set_hwmode_dev)(struct generic_pm_domain *domain, + struct device *dev, bool enable); + bool (*get_hwmode_dev)(struct generic_pm_domain *domain, + struct device *dev); int (*attach_dev)(struct generic_pm_domain *domain, struct device *dev); void (*detach_dev)(struct generic_pm_domain *domain, @@ -237,6 +241,7 @@ struct generic_pm_domain_data { unsigned int performance_state; unsigned int default_pstate; unsigned int rpm_pstate; + bool hw_mode; void *data; }; @@ -267,6 +272,8 @@ int dev_pm_genpd_remove_notifier(struct device *dev); void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next); ktime_t dev_pm_genpd_get_next_hrtimer(struct device *dev); void dev_pm_genpd_synced_poweroff(struct device *dev); +int dev_pm_genpd_set_hwmode(struct device *dev, bool enable); +bool dev_pm_genpd_get_hwmode(struct device *dev); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov; @@ -340,6 +347,16 @@ static inline ktime_t dev_pm_genpd_get_next_hrtimer(struct device *dev) static inline void dev_pm_genpd_synced_poweroff(struct device *dev) { } +static inline int dev_pm_genpd_set_hwmode(struct device *dev, bool enable) +{ + return -EOPNOTSUPP; +} + +static inline bool dev_pm_genpd_get_hwmode(struct device *dev) +{ + return false; +} + #define simple_qos_governor (*(struct dev_power_governor *)(NULL)) #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL)) #endif -- cgit From 0155aaf95a2a09bad567e5f775bfa8559e79f395 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 24 Jun 2024 10:18:06 +0530 Subject: PM: domains: Add the domain HW-managed mode to the summary Now that genpd supports dynamically switching the control for an attached device between hardware- and software-mode, let's add this information to the genpd summary under managed by column in debugfs. Suggested-by: Taniya Das Signed-off-by: Abel Vesa Signed-off-by: Jagadeesh Kona Reviewed-by: Ulf Hansson Reviewed-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Reviewed-by: Dhruva Gole Reviewed-by: Taniya Das Link: https://lore.kernel.org/r/20240624044809.17751-3-quic_jkona@quicinc.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index a9a409b2d25c..7a61aa88c061 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -3184,6 +3184,15 @@ static void rtpm_status_str(struct seq_file *s, struct device *dev) seq_printf(s, "%-25s ", p); } +static void mode_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, "%20s", 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; @@ -3242,6 +3251,7 @@ static int genpd_summary_one(struct seq_file *s, seq_printf(s, "\n %-50s ", kobj_path); rtpm_status_str(s, pm_data->dev); perf_status_str(s, pm_data->dev); + mode_status_str(s, pm_data->dev); kfree(kobj_path); } @@ -3258,8 +3268,8 @@ static int summary_show(struct seq_file *s, void *data) int ret = 0; seq_puts(s, "domain status children performance\n"); - seq_puts(s, " /device runtime status\n"); - seq_puts(s, "----------------------------------------------------------------------------------------------\n"); + seq_puts(s, " /device runtime status managed by\n"); + seq_puts(s, "------------------------------------------------------------------------------------------------------------\n"); ret = mutex_lock_interruptible(&gpd_list_lock); if (ret) -- cgit From f7ccdaad612a093c4a7c1840245c77eaffce09ab Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Mon, 24 Jun 2024 10:18:07 +0530 Subject: clk: qcom: gdsc: Add set and get hwmode callbacks to switch GDSC mode Some GDSC client drivers require the GDSC mode to be switched dynamically to HW mode at runtime to gain the power benefits. Typically such client drivers require the GDSC to be brought up in SW mode initially to enable the required dependent clocks and configure the hardware to proper state. Once initial hardware set up is done, they switch the GDSC to HW mode to save power. At the end of usecase, they switch the GDSC back to SW mode and disable the GDSC. Introduce HW_CTRL_TRIGGER flag to register the set_hwmode_dev and get_hwmode_dev callbacks for GDSC's whose respective client drivers require the GDSC mode to be switched dynamically at runtime using dev_pm_genpd_set_hwmode() API. Signed-off-by: Jagadeesh Kona Signed-off-by: Abel Vesa Reviewed-by: Bryan O'Donoghue Reviewed-by: Taniya Das Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20240624044809.17751-4-quic_jkona@quicinc.com Signed-off-by: Ulf Hansson --- drivers/clk/qcom/gdsc.c | 41 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 1 + 2 files changed, 42 insertions(+) diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index df9618ab7eea..fa5fe4c2a2ee 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -363,6 +363,43 @@ static int gdsc_disable(struct generic_pm_domain *domain) return 0; } +static int gdsc_set_hwmode(struct generic_pm_domain *domain, struct device *dev, bool mode) +{ + struct gdsc *sc = domain_to_gdsc(domain); + int ret; + + ret = gdsc_hwctrl(sc, mode); + if (ret) + return ret; + + /* + * Wait for the GDSC to go through a power down and + * up cycle. If we poll the status register before the + * power cycle is finished we might read incorrect values. + */ + udelay(1); + + /* + * When the GDSC is switched to HW mode, HW can disable the GDSC. + * When the GDSC is switched back to SW mode, the GDSC will be enabled + * again, hence we need to poll for GDSC to complete the power up. + */ + if (!mode) + return gdsc_poll_status(sc, GDSC_ON); + + return 0; +} + +static bool gdsc_get_hwmode(struct generic_pm_domain *domain, struct device *dev) +{ + struct gdsc *sc = domain_to_gdsc(domain); + u32 val; + + regmap_read(sc->regmap, sc->gdscr, &val); + + return !!(val & HW_CONTROL_MASK); +} + static int gdsc_init(struct gdsc *sc) { u32 mask, val; @@ -451,6 +488,10 @@ static int gdsc_init(struct gdsc *sc) sc->pd.power_off = gdsc_disable; if (!sc->pd.power_on) sc->pd.power_on = gdsc_enable; + if (sc->flags & HW_CTRL_TRIGGER) { + sc->pd.set_hwmode_dev = gdsc_set_hwmode; + sc->pd.get_hwmode_dev = gdsc_get_hwmode; + } ret = pm_genpd_init(&sc->pd, NULL, !on); if (ret) diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h index 803512688336..1e2779b823d1 100644 --- a/drivers/clk/qcom/gdsc.h +++ b/drivers/clk/qcom/gdsc.h @@ -67,6 +67,7 @@ struct gdsc { #define ALWAYS_ON BIT(6) #define RETAIN_FF_ENABLE BIT(7) #define NO_RET_PERIPH BIT(8) +#define HW_CTRL_TRIGGER BIT(9) struct reset_controller_dev *rcdev; unsigned int *resets; unsigned int reset_count; -- cgit From 4c2aecac62a442327f1e770055ecc3d5f7894101 Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Mon, 24 Jun 2024 10:18:08 +0530 Subject: clk: qcom: videocc: Use HW_CTRL_TRIGGER for SM8250, SC7280 vcodec GDSC's For Venus V6 variant SoCs(sm8250, sc7280), the venus driver uses the newly introduced dev_pm_genpd_set_hwmode() API to switch the vcodec GDSC to HW/SW control modes at runtime. Hence use HW_CTRL_TRIGGER flag for vcodec GDSC's on sm8250, sc7280 to register the set_hwmode_dev & get_hwmode_dev callbacks for vcodec GDSC and allow the GDSC mode to be changed using dev_pm_genpd_set_hwmode() API. Signed-off-by: Jagadeesh Kona Signed-off-by: Abel Vesa Reviewed-by: Taniya Das Acked-by: Bjorn Andersson Link: https://lore.kernel.org/r/20240624044809.17751-5-quic_jkona@quicinc.com Signed-off-by: Ulf Hansson --- drivers/clk/qcom/videocc-sc7280.c | 2 +- drivers/clk/qcom/videocc-sm8250.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/qcom/videocc-sc7280.c b/drivers/clk/qcom/videocc-sc7280.c index cdd59c6f60df..d55613a47ff7 100644 --- a/drivers/clk/qcom/videocc-sc7280.c +++ b/drivers/clk/qcom/videocc-sc7280.c @@ -236,7 +236,7 @@ static struct gdsc mvs0_gdsc = { .name = "mvs0_gdsc", }, .pwrsts = PWRSTS_OFF_ON, - .flags = HW_CTRL | RETAIN_FF_ENABLE, + .flags = HW_CTRL_TRIGGER | RETAIN_FF_ENABLE, }; static struct gdsc mvsc_gdsc = { diff --git a/drivers/clk/qcom/videocc-sm8250.c b/drivers/clk/qcom/videocc-sm8250.c index 016b596e03b3..cac10ccd362e 100644 --- a/drivers/clk/qcom/videocc-sm8250.c +++ b/drivers/clk/qcom/videocc-sm8250.c @@ -293,7 +293,7 @@ static struct gdsc mvs0_gdsc = { .pd = { .name = "mvs0_gdsc", }, - .flags = HW_CTRL, + .flags = HW_CTRL_TRIGGER, .pwrsts = PWRSTS_OFF_ON, }; @@ -302,7 +302,7 @@ static struct gdsc mvs1_gdsc = { .pd = { .name = "mvs1_gdsc", }, - .flags = HW_CTRL, + .flags = HW_CTRL_TRIGGER, .pwrsts = PWRSTS_OFF_ON, }; -- cgit From ec9a652e514903df887791b669b70e86ab4e3ec5 Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Mon, 24 Jun 2024 10:18:09 +0530 Subject: venus: pm_helpers: Use dev_pm_genpd_set_hwmode to switch GDSC mode on V6 The Venus driver requires vcodec GDSC to be ON in SW mode for clock operations and move it back to HW mode to gain power benefits. Earlier, as there is no interface to switch the GDSC mode from GenPD framework, the GDSC is moved to HW control mode as part of GDSC enable callback and venus driver is writing to its POWER_CONTROL register to keep the GDSC ON from SW whereever required. But the POWER_CONTROL register addresses are not constant and can vary across the variants. Also as per the HW recommendation, the GDSC mode switching needs to be controlled from respective GDSC register and this is a uniform approach across all the targets. Hence use dev_pm_genpd_set_hwmode() API which controls GDSC mode switching using its respective GDSC register. In venus V6 variants, the vcodec gdsc gets enabled in SW mode by default with new HW_CTRL_TRIGGER flag and there is no need to switch it to SW mode again after enable, hence add check to avoid switching gdsc to SW mode again after gdsc enable. Similarly add check to avoid switching GDSC to HW mode before disabling the GDSC, so GDSC gets enabled in SW mode in the next enable. Signed-off-by: Jagadeesh Kona Signed-off-by: Abel Vesa Tested-by: Bryan O'Donoghue Reviewed-by: Taniya Das Link: https://lore.kernel.org/r/20240624044809.17751-6-quic_jkona@quicinc.com Signed-off-by: Ulf Hansson --- drivers/media/platform/qcom/venus/pm_helpers.c | 39 +++++++++++++++----------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 502822059498..4ce76ce6dd4d 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -412,10 +412,9 @@ static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable) u32 val; int ret; - if (IS_V6(core)) { - ctrl = core->wrapper_base + WRAPPER_CORE_POWER_CONTROL_V6; - stat = core->wrapper_base + WRAPPER_CORE_POWER_STATUS_V6; - } else if (coreid == VIDC_CORE_ID_1) { + if (IS_V6(core)) + return dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable); + else if (coreid == VIDC_CORE_ID_1) { ctrl = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; stat = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; } else { @@ -451,9 +450,11 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) vcodec_clks_disable(core, core->vcodec0_clks); - ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); - if (ret) - return ret; + if (!IS_V6(core)) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); + if (ret) + return ret; + } ret = pm_runtime_put_sync(core->pmdomains->pd_devs[1]); if (ret < 0) @@ -467,9 +468,11 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) vcodec_clks_disable(core, core->vcodec1_clks); - ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); - if (ret) - return ret; + if (!IS_V6(core)) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); + if (ret) + return ret; + } ret = pm_runtime_put_sync(core->pmdomains->pd_devs[2]); if (ret < 0) @@ -488,9 +491,11 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) if (ret < 0) return ret; - ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); - if (ret) - return ret; + if (!IS_V6(core)) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + } ret = vcodec_clks_enable(core, core->vcodec0_clks); if (ret) @@ -506,9 +511,11 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) if (ret < 0) return ret; - ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); - if (ret) - return ret; + if (!IS_V6(core)) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + } ret = vcodec_clks_enable(core, core->vcodec1_clks); if (ret) -- cgit From 3324a8196ab91051bd4528ca16dce1dbc7bfabb7 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 1 Jul 2024 21:53:16 +0200 Subject: pmdomain: amlogic: Constify struct meson_secure_pwrc_domain_desc 'struct meson_secure_pwrc_domain_desc' is not modified in this driver. Constifying this structure moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 4909 4072 0 8981 2315 drivers/pmdomain/amlogic/meson-secure-pwrc.o After: ===== text data bss dec hex filename 8605 392 0 8997 2325 drivers/pmdomain/amlogic/meson-secure-pwrc.o Signed-off-by: Christophe JAILLET Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/871d6b708de8bb42e1fabd8a601dc9a9a217cf00.1719863475.git.christophe.jaillet@wanadoo.fr Signed-off-by: Ulf Hansson --- drivers/pmdomain/amlogic/meson-secure-pwrc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c index f6729eea6b8c..42ce41a2fe3a 100644 --- a/drivers/pmdomain/amlogic/meson-secure-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -47,7 +47,7 @@ struct meson_secure_pwrc_domain_desc { struct meson_secure_pwrc_domain_data { unsigned int count; - struct meson_secure_pwrc_domain_desc *domains; + const struct meson_secure_pwrc_domain_desc *domains; }; static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain) @@ -111,7 +111,7 @@ static int meson_secure_pwrc_on(struct generic_pm_domain *domain) .parent = __parent, \ } -static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { +static const struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { SEC_PD(DSPA, 0), SEC_PD(DSPB, 0), /* UART should keep working in ATF after suspend and before resume */ @@ -138,7 +138,7 @@ static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { SEC_PD(RSA, 0), }; -static struct meson_secure_pwrc_domain_desc a4_pwrc_domains[] = { +static const struct meson_secure_pwrc_domain_desc a4_pwrc_domains[] = { SEC_PD(A4_AUDIO, 0), SEC_PD(A4_SDIOA, 0), SEC_PD(A4_EMMC, 0), @@ -156,7 +156,7 @@ static struct meson_secure_pwrc_domain_desc a4_pwrc_domains[] = { SEC_PD(A4_AO_IR, GENPD_FLAG_ALWAYS_ON), }; -static struct meson_secure_pwrc_domain_desc a5_pwrc_domains[] = { +static const struct meson_secure_pwrc_domain_desc a5_pwrc_domains[] = { SEC_PD(A5_NNA, 0), SEC_PD(A5_AUDIO, 0), SEC_PD(A5_SDIOA, 0), @@ -172,7 +172,7 @@ static struct meson_secure_pwrc_domain_desc a5_pwrc_domains[] = { SEC_PD(A5_DSPA, 0), }; -static struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { +static const struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { SEC_PD(C3_NNA, 0), SEC_PD(C3_AUDIO, 0), SEC_PD(C3_SDIOA, 0), @@ -189,7 +189,7 @@ static struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { SEC_PD(C3_VCODEC, 0), }; -static struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = { +static const struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = { SEC_PD(S4_DOS_HEVC, 0), SEC_PD(S4_DOS_VDEC, 0), SEC_PD(S4_VPU_HDMI, 0), @@ -201,7 +201,7 @@ static struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = { SEC_PD(S4_AUDIO, 0), }; -static struct meson_secure_pwrc_domain_desc t7_pwrc_domains[] = { +static const struct meson_secure_pwrc_domain_desc t7_pwrc_domains[] = { SEC_PD(T7_DSPA, 0), SEC_PD(T7_DSPB, 0), TOP_PD(T7_DOS_HCODEC, 0, PWRC_T7_NIC3_ID), -- cgit