diff options
Diffstat (limited to 'drivers/pmdomain')
46 files changed, 1407 insertions, 874 deletions
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-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 deleted file mode 100644 index 33df520eab95..000000000000 --- a/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (c) 2017 BayLibre, SAS - * Author: Neil Armstrong <narmstrong@baylibre.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <linux/platform_device.h> -#include <linux/pm_domain.h> -#include <linux/bitfield.h> -#include <linux/regmap.h> -#include <linux/mfd/syscon.h> -#include <linux/of.h> -#include <linux/reset.h> -#include <linux/clk.h> -#include <linux/module.h> - -/* 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_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c index 4d5bda0d60fc..ff76ea36835e 100644 --- a/drivers/pmdomain/amlogic/meson-secure-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -14,6 +14,8 @@ #include <dt-bindings/power/amlogic,c3-pwrc.h> #include <dt-bindings/power/meson-s4-power.h> #include <dt-bindings/power/amlogic,t7-pwrc.h> +#include <dt-bindings/power/amlogic,a4-pwrc.h> +#include <dt-bindings/power/amlogic,a5-pwrc.h> #include <linux/arm-smccc.h> #include <linux/firmware/meson/meson_sm.h> #include <linux/module.h> @@ -45,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) @@ -109,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 */ @@ -136,7 +138,41 @@ static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { SEC_PD(RSA, 0), }; -static struct meson_secure_pwrc_domain_desc c3_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), + 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 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), + 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 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), @@ -153,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), @@ -165,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), @@ -185,7 +221,7 @@ static struct meson_secure_pwrc_domain_desc t7_pwrc_domains[] = { SEC_PD(T7_VI_CLK2, 0), /* ETH is for ethernet online wakeup, and should be always on */ SEC_PD(T7_ETH, GENPD_FLAG_ALWAYS_ON), - SEC_PD(T7_ISP, 0), + TOP_PD(T7_ISP, 0, PWRC_T7_MIPI_ISP_ID), SEC_PD(T7_MIPI_ISP, 0), TOP_PD(T7_GDC, 0, PWRC_T7_NIC3_ID), TOP_PD(T7_DEWARP, 0, PWRC_T7_NIC3_ID), @@ -311,6 +347,16 @@ 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_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), @@ -332,6 +378,14 @@ static const struct of_device_id meson_secure_pwrc_match_table[] = { .data = &meson_secure_a1_pwrc_data, }, { + .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, }, @@ -355,4 +409,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"); 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, diff --git a/drivers/pmdomain/arm/scmi_perf_domain.c b/drivers/pmdomain/arm/scmi_perf_domain.c index d7ef46ccd9b8..3693423459c9 100644 --- a/drivers/pmdomain/arm/scmi_perf_domain.c +++ b/drivers/pmdomain/arm/scmi_perf_domain.c @@ -125,7 +125,8 @@ static int scmi_perf_domain_probe(struct scmi_device *sdev) scmi_pd->ph = ph; scmi_pd->genpd.name = scmi_pd->info->name; scmi_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON | - GENPD_FLAG_OPP_TABLE_FW; + GENPD_FLAG_OPP_TABLE_FW | + GENPD_FLAG_DEV_NAME_FW; scmi_pd->genpd.set_performance_state = scmi_pd_set_perf_state; scmi_pd->genpd.attach_dev = scmi_pd_attach_dev; scmi_pd->genpd.detach_dev = scmi_pd_detach_dev; diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c index 0e05a79de82d..86b531e15b85 100644 --- a/drivers/pmdomain/arm/scmi_pm_domain.c +++ b/drivers/pmdomain/arm/scmi_pm_domain.c @@ -96,12 +96,21 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev) continue; } + /* + * Register the explicit power on request to the firmware so + * that it is tracked as used by OSPM agent and not + * accidentally turned off with OSPM's knowledge + */ + if (state == SCMI_POWER_STATE_GENERIC_ON) + power_ops->state_set(ph, i, state); + scmi_pd->domain = i; scmi_pd->ph = ph; scmi_pd->name = power_ops->name_get(ph, i); 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); diff --git a/drivers/pmdomain/bcm/raspberrypi-power.c b/drivers/pmdomain/bcm/raspberrypi-power.c index 06196ebfe03b..b87ea7adb7be 100644 --- a/drivers/pmdomain/bcm/raspberrypi-power.c +++ b/drivers/pmdomain/bcm/raspberrypi-power.c @@ -41,40 +41,46 @@ struct rpi_power_domains { */ struct rpi_power_domain_packet { u32 domain; - u32 on; + u32 state; }; /* * 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.on = 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)); + packet.state = on; + + 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, @@ -85,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; @@ -142,13 +149,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) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 18e232b5ed53..6c94137865c9 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) "PM: " fmt #include <linux/delay.h> +#include <linux/idr.h> #include <linux/kernel.h> #include <linux/io.h> #include <linux/platform_device.h> @@ -23,6 +24,9 @@ #include <linux/cpu.h> #include <linux/debugfs.h> +/* Provides a unique ID for each genpd device */ +static DEFINE_IDA(genpd_ida); + #define GENPD_RETRY_MAX_MS 250 /* Approximate */ #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ @@ -117,6 +121,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) @@ -129,6 +175,7 @@ static const struct genpd_lock_ops genpd_spin_ops = { #define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN) #define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON) #define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW) +#define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW) static inline bool irq_safe_dev_in_sleep_domain(struct device *dev, const struct generic_pm_domain *genpd) @@ -147,7 +194,7 @@ static inline bool irq_safe_dev_in_sleep_domain(struct device *dev, if (ret) dev_warn_once(dev, "PM domain %s will not be powered off\n", - genpd->name); + dev_name(&genpd->dev)); return ret; } @@ -184,6 +231,16 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev) return pd_to_genpd(dev->pm_domain); } +struct device *dev_to_genpd_dev(struct device *dev) +{ + struct generic_pm_domain *genpd = dev_to_genpd(dev); + + if (IS_ERR(genpd)) + return ERR_CAST(genpd); + + return &genpd->dev; +} + static int genpd_stop_dev(const struct generic_pm_domain *genpd, struct device *dev) { @@ -222,7 +279,7 @@ static void genpd_debug_remove(struct generic_pm_domain *genpd) if (!genpd_debugfs_dir) return; - debugfs_lookup_and_remove(genpd->name, genpd_debugfs_dir); + debugfs_lookup_and_remove(dev_name(&genpd->dev), genpd_debugfs_dir); } static void genpd_update_accounting(struct generic_pm_domain *genpd) @@ -311,72 +368,102 @@ static int genpd_xlate_performance_state(struct generic_pm_domain *genpd, } static int _genpd_set_performance_state(struct generic_pm_domain *genpd, - unsigned int state, int depth) + unsigned int state, int depth); + +static void _genpd_rollback_parent_state(struct gpd_link *link, int depth) { - struct generic_pm_domain *parent; - struct gpd_link *link; - int parent_state, ret; + struct generic_pm_domain *parent = link->parent; + int parent_state; - if (state == genpd->performance_state) - return 0; + genpd_lock_nested(parent, depth + 1); - /* Propagate to parents of genpd */ - list_for_each_entry(link, &genpd->child_links, child_node) { - parent = link->parent; + parent_state = link->prev_performance_state; + link->performance_state = parent_state; - /* Find parent's performance state */ - ret = genpd_xlate_performance_state(genpd, parent, state); - if (unlikely(ret < 0)) - goto err; + parent_state = _genpd_reeval_performance_state(parent, parent_state); + if (_genpd_set_performance_state(parent, parent_state, depth + 1)) { + pr_err("%s: Failed to roll back to %d performance state\n", + parent->name, parent_state); + } - parent_state = ret; + genpd_unlock(parent); +} - genpd_lock_nested(parent, depth + 1); +static int _genpd_set_parent_state(struct generic_pm_domain *genpd, + struct gpd_link *link, + unsigned int state, int depth) +{ + struct generic_pm_domain *parent = link->parent; + int parent_state, ret; - link->prev_performance_state = link->performance_state; - link->performance_state = parent_state; - parent_state = _genpd_reeval_performance_state(parent, - parent_state); - ret = _genpd_set_performance_state(parent, parent_state, depth + 1); - if (ret) - link->performance_state = link->prev_performance_state; + /* Find parent's performance state */ + ret = genpd_xlate_performance_state(genpd, parent, state); + if (unlikely(ret < 0)) + return ret; - genpd_unlock(parent); + parent_state = ret; - if (ret) - goto err; - } + genpd_lock_nested(parent, depth + 1); - if (genpd->set_performance_state) { - ret = genpd->set_performance_state(genpd, state); - if (ret) - goto err; - } + link->prev_performance_state = link->performance_state; + link->performance_state = parent_state; - genpd->performance_state = state; - return 0; + parent_state = _genpd_reeval_performance_state(parent, parent_state); + ret = _genpd_set_performance_state(parent, parent_state, depth + 1); + if (ret) + link->performance_state = link->prev_performance_state; -err: - /* Encountered an error, lets rollback */ - list_for_each_entry_continue_reverse(link, &genpd->child_links, - child_node) { - parent = link->parent; + genpd_unlock(parent); - genpd_lock_nested(parent, depth + 1); + return ret; +} + +static int _genpd_set_performance_state(struct generic_pm_domain *genpd, + unsigned int state, int depth) +{ + struct gpd_link *link = NULL; + int ret; - parent_state = link->prev_performance_state; - link->performance_state = parent_state; + if (state == genpd->performance_state) + return 0; - parent_state = _genpd_reeval_performance_state(parent, - parent_state); - if (_genpd_set_performance_state(parent, parent_state, depth + 1)) { - pr_err("%s: Failed to roll back to %d performance state\n", - parent->name, parent_state); + /* When scaling up, propagate to parents first in normal order */ + if (state > genpd->performance_state) { + list_for_each_entry(link, &genpd->child_links, child_node) { + ret = _genpd_set_parent_state(genpd, link, state, depth); + if (ret) + goto rollback_parents_up; } + } - genpd_unlock(parent); + if (genpd->set_performance_state) { + ret = genpd->set_performance_state(genpd, state); + if (ret) { + if (link) + goto rollback_parents_up; + return ret; + } + } + + /* When scaling down, propagate to parents last in reverse order */ + if (state < genpd->performance_state) { + list_for_each_entry_reverse(link, &genpd->child_links, child_node) { + ret = _genpd_set_parent_state(genpd, link, state, depth); + if (ret) + goto rollback_parents_down; + } } + genpd->performance_state = state; + return 0; + +rollback_parents_up: + list_for_each_entry_continue_reverse(link, &genpd->child_links, child_node) + _genpd_rollback_parent_state(link, depth); + return ret; +rollback_parents_down: + list_for_each_entry_continue(link, &genpd->child_links, child_node) + _genpd_rollback_parent_state(link, depth); return ret; } @@ -548,6 +635,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; @@ -587,7 +736,7 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) genpd->states[state_idx].power_on_latency_ns = elapsed_ns; genpd->gd->max_off_time_changed = true; pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", - genpd->name, "on", elapsed_ns); + dev_name(&genpd->dev), "on", elapsed_ns); out: raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL); @@ -638,7 +787,7 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) genpd->states[state_idx].power_off_latency_ns = elapsed_ns; genpd->gd->max_off_time_changed = true; pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", - genpd->name, "off", elapsed_ns); + dev_name(&genpd->dev), "off", elapsed_ns); out: raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF, @@ -1100,6 +1249,7 @@ static int __init genpd_power_off_unused(void) return 0; } + pr_info("genpd: Disabling unused power domains\n"); mutex_lock(&gpd_list_lock); list_for_each_entry(genpd, &gpd_list, gpd_list_node) @@ -1147,8 +1297,12 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, /* Choose the deepest state when suspending */ genpd->state_idx = genpd->state_count - 1; - if (_genpd_power_off(genpd, false)) + if (_genpd_power_off(genpd, false)) { + genpd->states[genpd->state_idx].rejected++; return; + } else { + genpd->states[genpd->state_idx].usage++; + } genpd->status = GENPD_STATE_OFF; @@ -1220,10 +1374,7 @@ static int genpd_prepare(struct device *dev) return -EINVAL; genpd_lock(genpd); - - if (genpd->prepared_count++ == 0) - genpd->suspended_count = 0; - + genpd->prepared_count++; genpd_unlock(genpd); ret = pm_generic_prepare(dev); @@ -1576,6 +1727,7 @@ static void genpd_free_dev_data(struct device *dev, spin_unlock_irq(&dev->power.lock); + dev_pm_opp_clear_config(gpd_data->opp_token); kfree(gpd_data->td); kfree(gpd_data); dev_pm_put_subsys_data(dev); @@ -1645,6 +1797,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; @@ -1652,7 +1806,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) @@ -1661,6 +1814,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); @@ -1717,12 +1871,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); @@ -1791,7 +1946,7 @@ int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb) if (ret) { dev_warn(dev, "failed to add notifier for PM domain %s\n", - genpd->name); + dev_name(&genpd->dev)); return ret; } @@ -1838,7 +1993,7 @@ int dev_pm_genpd_remove_notifier(struct device *dev) if (ret) { dev_warn(dev, "failed to remove notifier for PM domain %s\n", - genpd->name); + dev_name(&genpd->dev)); return ret; } @@ -1864,7 +2019,7 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd, */ if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", - genpd->name, subdomain->name); + dev_name(&genpd->dev), subdomain->name); return -EINVAL; } @@ -1939,7 +2094,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, if (!list_empty(&subdomain->parent_links) || subdomain->device_count) { pr_warn("%s: unable to remove subdomain %s\n", - genpd->name, subdomain->name); + dev_name(&genpd->dev), subdomain->name); ret = -EBUSY; goto out; } @@ -1987,6 +2142,11 @@ static int genpd_set_default_power_state(struct generic_pm_domain *genpd) return 0; } +static void genpd_provider_release(struct device *dev) +{ + /* nothing to be done here */ +} + static int genpd_alloc_data(struct generic_pm_domain *genpd) { struct genpd_governor_data *gd = NULL; @@ -2017,8 +2177,25 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd) } genpd->gd = gd; - return 0; + device_initialize(&genpd->dev); + genpd->dev.release = genpd_provider_release; + if (!genpd_is_dev_name_fw(genpd)) { + dev_set_name(&genpd->dev, "%s", genpd->name); + } else { + ret = ida_alloc(&genpd_ida, GFP_KERNEL); + if (ret < 0) + goto put; + + genpd->device_id = ret; + dev_set_name(&genpd->dev, "%s_%u", genpd->name, genpd->device_id); + } + + return 0; +put: + put_device(&genpd->dev); + if (genpd->free_states == genpd_free_default_power_state) + kfree(genpd->states); free: if (genpd_is_cpu_domain(genpd)) free_cpumask_var(genpd->cpus); @@ -2028,6 +2205,9 @@ free: static void genpd_free_data(struct generic_pm_domain *genpd) { + put_device(&genpd->dev); + if (genpd->device_id != -ENXIO) + ida_free(&genpd_ida, genpd->device_id); if (genpd_is_cpu_domain(genpd)) free_cpumask_var(genpd->cpus); if (genpd->free_states) @@ -2037,7 +2217,10 @@ 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_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 { @@ -2073,6 +2256,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd, genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON; genpd->device_count = 0; genpd->provider = NULL; + genpd->device_id = -ENXIO; genpd->has_provider = false; genpd->accounting_time = ktime_get_mono_fast_ns(); genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; @@ -2112,9 +2296,6 @@ int pm_genpd_init(struct generic_pm_domain *genpd, if (ret) return ret; - device_initialize(&genpd->dev); - dev_set_name(&genpd->dev, "%s", genpd->name); - mutex_lock(&gpd_list_lock); list_add(&genpd->gpd_list_node, &gpd_list); mutex_unlock(&gpd_list_lock); @@ -2135,13 +2316,13 @@ static int genpd_remove(struct generic_pm_domain *genpd) if (genpd->has_provider) { genpd_unlock(genpd); - pr_err("Provider present, unable to remove %s\n", genpd->name); + pr_err("Provider present, unable to remove %s\n", dev_name(&genpd->dev)); return -EBUSY; } if (!list_empty(&genpd->parent_links) || genpd->device_count) { genpd_unlock(genpd); - pr_err("%s: unable to remove %s\n", __func__, genpd->name); + pr_err("%s: unable to remove %s\n", __func__, dev_name(&genpd->dev)); return -EBUSY; } @@ -2157,7 +2338,7 @@ static int genpd_remove(struct generic_pm_domain *genpd) cancel_work_sync(&genpd->power_off_work); genpd_free_data(genpd); - pr_debug("%s: removed %s\n", __func__, genpd->name); + pr_debug("%s: removed %s\n", __func__, dev_name(&genpd->dev)); return 0; } @@ -2235,7 +2416,7 @@ static DEFINE_MUTEX(of_genpd_mutex); * to be a valid pointer to struct generic_pm_domain. */ static struct generic_pm_domain *genpd_xlate_simple( - struct of_phandle_args *genpdspec, + const struct of_phandle_args *genpdspec, void *data) { return data; @@ -2252,7 +2433,7 @@ static struct generic_pm_domain *genpd_xlate_simple( * the genpd_onecell_data struct when registering the provider. */ static struct generic_pm_domain *genpd_xlate_onecell( - struct of_phandle_args *genpdspec, + const struct of_phandle_args *genpdspec, void *data) { struct genpd_onecell_data *genpd_data = data; @@ -2495,7 +2676,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider); * on failure. */ static struct generic_pm_domain *genpd_get_from_provider( - struct of_phandle_args *genpdspec) + const struct of_phandle_args *genpdspec) { struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); struct of_genpd_provider *provider; @@ -2526,7 +2707,7 @@ static struct generic_pm_domain *genpd_get_from_provider( * Looks-up an I/O PM domain based upon phandle args provided and adds * the device to the PM domain. Returns a negative error code on failure. */ -int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) +int of_genpd_add_device(const struct of_phandle_args *genpdspec, struct device *dev) { struct generic_pm_domain *genpd; int ret; @@ -2560,8 +2741,8 @@ EXPORT_SYMBOL_GPL(of_genpd_add_device); * provided and adds the subdomain to the parent PM domain. Returns a * negative error code on failure. */ -int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, - struct of_phandle_args *subdomain_spec) +int of_genpd_add_subdomain(const struct of_phandle_args *parent_spec, + const struct of_phandle_args *subdomain_spec) { struct generic_pm_domain *parent, *subdomain; int ret; @@ -2598,8 +2779,8 @@ EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); * provided and removes the subdomain from the parent PM domain. Returns a * negative error code on failure. */ -int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec, - struct of_phandle_args *subdomain_spec) +int of_genpd_remove_subdomain(const struct of_phandle_args *parent_spec, + const struct of_phandle_args *subdomain_spec) { struct generic_pm_domain *parent, *subdomain; int ret; @@ -2732,12 +2913,58 @@ static void genpd_dev_pm_sync(struct device *dev) genpd_queue_power_off_work(pd); } +static int genpd_set_required_opp_dev(struct device *dev, + struct device *base_dev) +{ + struct dev_pm_opp_config config = { + .required_dev = dev, + }; + int ret; + + /* Limit support to non-providers for now. */ + if (of_property_present(base_dev->of_node, "#power-domain-cells")) + return 0; + + if (!dev_pm_opp_of_has_required_opp(base_dev)) + return 0; + + ret = dev_pm_opp_set_config(base_dev, &config); + if (ret < 0) + return ret; + + dev_gpd_data(dev)->opp_token = ret; + return 0; +} + +static int genpd_set_required_opp(struct device *dev, unsigned int index) +{ + int ret, pstate; + + /* Set the default performance state */ + pstate = of_get_required_opp_performance_state(dev->of_node, index); + if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) { + ret = pstate; + goto err; + } else if (pstate > 0) { + ret = dev_pm_genpd_set_performance_state(dev, pstate); + if (ret) + goto err; + dev_gpd_data(dev)->default_pstate = pstate; + } + + return 0; +err: + dev_err(dev, "failed to set required performance state for power-domain %s: %d\n", + dev_to_genpd(dev)->name, ret); + return ret; +} + static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, - unsigned int index, bool power_on) + unsigned int index, unsigned int num_domains, + bool power_on) { struct of_phandle_args pd_args; struct generic_pm_domain *pd; - int pstate; int ret; ret = of_parse_phandle_with_args(dev->of_node, "power-domains", @@ -2766,18 +2993,21 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->sync = genpd_dev_pm_sync; - /* Set the default performance state */ - pstate = of_get_required_opp_performance_state(dev->of_node, index); - if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) { - ret = pstate; - goto err; - } else if (pstate > 0) { - ret = dev_pm_genpd_set_performance_state(dev, pstate); + /* + * For a single PM domain the index of the required OPP must be zero, so + * let's try to assign a required dev in that case. In the multiple PM + * domains case, we need platform code to specify the index. + */ + if (num_domains == 1) { + ret = genpd_set_required_opp_dev(dev, base_dev); if (ret) goto err; - dev_gpd_data(dev)->default_pstate = pstate; } + ret = genpd_set_required_opp(dev, index); + if (ret) + goto err; + if (power_on) { genpd_lock(pd); ret = genpd_power_on(pd, 0); @@ -2798,8 +3028,6 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, return 1; err: - dev_err(dev, "failed to set required performance state for power-domain %s: %d\n", - pd->name, ret); genpd_remove_device(pd, dev); return ret; } @@ -2830,7 +3058,7 @@ int genpd_dev_pm_attach(struct device *dev) "#power-domain-cells") != 1) return 0; - return __genpd_dev_pm_attach(dev, dev, 0, true); + return __genpd_dev_pm_attach(dev, dev, 0, 1, true); } EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); @@ -2883,7 +3111,7 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev, } /* Try to attach the device to the PM domain at the specified index. */ - ret = __genpd_dev_pm_attach(virt_dev, dev, index, false); + ret = __genpd_dev_pm_attach(virt_dev, dev, index, num_domains, false); if (ret < 1) { device_unregister(virt_dev); return ret ? ERR_PTR(ret) : NULL; @@ -2952,6 +3180,8 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, if (!err) genpd_state->residency_ns = 1000LL * residency; + of_property_read_string(state_node, "idle-state-name", &genpd_state->name); + genpd_state->power_on_latency_ns = 1000LL * exit_latency; genpd_state->power_off_latency_ns = 1000LL * entry_latency; genpd_state->fwnode = &state_node->fwnode; @@ -3075,7 +3305,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) @@ -3083,7 +3313,17 @@ 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 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, "%2s", gpd_data->hw_mode ? "HW" : "SW"); } static int genpd_summary_one(struct seq_file *s, @@ -3094,7 +3334,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; @@ -3111,12 +3350,12 @@ 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 %-30s %u", dev_name(&genpd->dev), state, genpd->performance_state); /* * Modifications on the list require holding locks on both * parent and child, so we are safe. - * Also genpd->name is immutable. + * Also the device name is immutable. */ list_for_each_entry(link, &genpd->parent_links, parent_node) { if (list_is_first(&link->parent_node, &genpd->parent_links)) @@ -3127,16 +3366,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 %-30s ", dev_name(pm_data->dev)); rtpm_status_str(s, pm_data->dev); perf_status_str(s, pm_data->dev); - kfree(kobj_path); + mode_status_str(s, pm_data->dev); } seq_puts(s, "\n"); @@ -3151,9 +3384,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\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) @@ -3227,7 +3460,10 @@ static int idle_states_show(struct seq_file *s, void *data) seq_puts(s, "State Time Spent(ms) Usage Rejected\n"); for (i = 0; i < genpd->state_count; i++) { - idle_time += genpd->states[i].idle_time; + struct genpd_power_state *state = &genpd->states[i]; + char state_name[15]; + + idle_time += state->idle_time; if (genpd->status == GENPD_STATE_OFF && genpd->state_idx == i) { now = ktime_get_mono_fast_ns(); @@ -3237,9 +3473,13 @@ static int idle_states_show(struct seq_file *s, void *data) } } + if (!state->name) + snprintf(state_name, ARRAY_SIZE(state_name), "S%-13d", i); + do_div(idle_time, NSEC_PER_MSEC); - seq_printf(s, "S%-13i %-14llu %-14llu %llu\n", i, idle_time, - genpd->states[i].usage, genpd->states[i].rejected); + seq_printf(s, "%-14s %-14llu %-14llu %llu\n", + state->name ?: state_name, idle_time, + state->usage, state->rejected); } genpd_unlock(genpd); @@ -3305,23 +3545,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; @@ -3356,7 +3587,7 @@ static void genpd_debug_add(struct generic_pm_domain *genpd) if (!genpd_debugfs_dir) return; - d = debugfs_create_dir(genpd->name, genpd_debugfs_dir); + d = debugfs_create_dir(dev_name(&genpd->dev), genpd_debugfs_dir); debugfs_create_file("current_state", 0444, d, genpd, &status_fops); diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c index 9517cce93d8a..f18c7e6e75dd 100644 --- a/drivers/pmdomain/imx/gpc.c +++ b/drivers/pmdomain/imx/gpc.c @@ -233,7 +233,7 @@ static struct platform_driver imx_pgc_power_domain_driver = { .name = "imx-pgc-pd", }, .probe = imx_pgc_power_domain_probe, - .remove_new = imx_pgc_power_domain_remove, + .remove = imx_pgc_power_domain_remove, .id_table = imx_pgc_power_domain_id, }; builtin_platform_driver(imx_pgc_power_domain_driver) @@ -411,7 +411,7 @@ static int imx_gpc_probe(struct platform_device *pdev) pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); /* bail out if DT too old and doesn't provide the necessary info */ - if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && + if (!of_property_present(pdev->dev.of_node, "#power-domain-cells") && !pgc_node) return 0; @@ -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; } } @@ -517,7 +511,7 @@ static void imx_gpc_remove(struct platform_device *pdev) pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); /* bail out if DT too old and doesn't provide the necessary info */ - if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && + if (!of_property_present(pdev->dev.of_node, "#power-domain-cells") && !pgc_node) return; @@ -551,6 +545,6 @@ static struct platform_driver imx_gpc_driver = { .of_match_table = imx_gpc_dt_ids, }, .probe = imx_gpc_probe, - .remove_new = imx_gpc_remove, + .remove = imx_gpc_remove, }; builtin_platform_driver(imx_gpc_driver) diff --git a/drivers/pmdomain/imx/gpcv2.c b/drivers/pmdomain/imx/gpcv2.c index 4b828d74a606..958d34d4821b 100644 --- a/drivers/pmdomain/imx/gpcv2.c +++ b/drivers/pmdomain/imx/gpcv2.c @@ -393,6 +393,17 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd) * automatically there. Just add a delay and suppose the handshake finish * after that. */ + + /* + * For some BLK-CTL module (eg. AudioMix on i.MX8MP) doesn't have BUS + * clk-en bit, it is better to add delay here, as the BLK-CTL module + * doesn't need to care about how it is powered up. + * + * regmap_read_bypassed() is to make sure the above write IO transaction + * already reaches target before udelay() + */ + regmap_read_bypassed(domain->regmap, domain->regs->hsk, ®_val); + udelay(10); } /* Disable reset clocks for all devices in the domain */ @@ -1345,7 +1356,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) ret = pm_genpd_init(&domain->genpd, NULL, true); if (ret) { - dev_err(domain->dev, "Failed to init power domain\n"); + dev_err_probe(domain->dev, ret, "Failed to init power domain\n"); goto out_domain_unmap; } @@ -1356,7 +1367,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) ret = of_genpd_add_provider_simple(domain->dev->of_node, &domain->genpd); if (ret) { - dev_err(domain->dev, "Failed to add genpd provider\n"); + dev_err_probe(domain->dev, ret, "Failed to add genpd provider\n"); goto out_genpd_remove; } @@ -1426,9 +1437,10 @@ static struct platform_driver imx_pgc_domain_driver = { .driver = { .name = "imx-pgc", .pm = &imx_pgc_domain_pm_ops, + .suppress_bind_attrs = true, }, .probe = imx_pgc_domain_probe, - .remove_new = imx_pgc_domain_remove, + .remove = imx_pgc_domain_remove, .id_table = imx_pgc_domain_id, }; builtin_platform_driver(imx_pgc_domain_driver) @@ -1447,12 +1459,12 @@ 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 __free(device_node) = + of_get_child_by_name(dev->of_node, "pgc"); struct regmap *regmap; void __iomem *base; int ret; - pgc_np = of_get_child_by_name(dev->of_node, "pgc"); if (!pgc_np) { dev_err(dev, "No power domains specified in DT\n"); return -EINVAL; @@ -1469,7 +1481,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; @@ -1480,7 +1492,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; } @@ -1495,7 +1506,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; } @@ -1504,7 +1514,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; } @@ -1521,7 +1530,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; } } @@ -1542,6 +1550,7 @@ static struct platform_driver imx_gpc_driver = { .driver = { .name = "imx-gpcv2", .of_match_table = imx_gpcv2_dt_ids, + .suppress_bind_attrs = true, }, .probe = imx_gpcv2_probe, }; diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index 1341a707f61b..912802b5215b 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -258,11 +258,14 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev) domain->power_dev = dev_pm_domain_attach_by_name(dev, data->gpc_name); - if (IS_ERR(domain->power_dev)) { - dev_err_probe(dev, PTR_ERR(domain->power_dev), + if (IS_ERR_OR_NULL(domain->power_dev)) { + if (!domain->power_dev) + ret = -ENODEV; + else + ret = PTR_ERR(domain->power_dev); + dev_err_probe(dev, ret, "failed to attach power domain \"%s\"\n", data->gpc_name); - ret = PTR_ERR(domain->power_dev); goto cleanup_pds; } @@ -886,11 +889,12 @@ MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match); static struct platform_driver imx8m_blk_ctrl_driver = { .probe = imx8m_blk_ctrl_probe, - .remove_new = imx8m_blk_ctrl_remove, + .remove = imx8m_blk_ctrl_remove, .driver = { .name = "imx8m-blk-ctrl", .pm = &imx8m_blk_ctrl_pm_ops, .of_match_table = imx8m_blk_ctrl_of_match, + .suppress_bind_attrs = true, }, }; module_platform_driver(imx8m_blk_ctrl_driver); diff --git a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c index e3203eb6a022..34576be606e3 100644 --- a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c @@ -55,7 +55,7 @@ struct imx8mp_blk_ctrl_domain_data { const char *gpc_name; }; -#define DOMAIN_MAX_CLKS 2 +#define DOMAIN_MAX_CLKS 3 #define DOMAIN_MAX_PATHS 3 struct imx8mp_blk_ctrl_domain { @@ -457,8 +457,8 @@ static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = { }, [IMX8MP_HDMIBLK_PD_LCDIF] = { .name = "hdmiblk-lcdif", - .clk_names = (const char *[]){ "axi", "apb" }, - .num_clks = 2, + .clk_names = (const char *[]){ "axi", "apb", "fdcc" }, + .num_clks = 3, .gpc_name = "lcdif", .path_names = (const char *[]){"lcdif-hdmi"}, .num_paths = 1, @@ -483,8 +483,8 @@ static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = { }, [IMX8MP_HDMIBLK_PD_HDMI_TX] = { .name = "hdmiblk-hdmi-tx", - .clk_names = (const char *[]){ "apb", "ref_266m" }, - .num_clks = 2, + .clk_names = (const char *[]){ "apb", "ref_266m", "fdcc" }, + .num_clks = 3, .gpc_name = "hdmi-tx", }, [IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = { @@ -687,11 +687,14 @@ static int imx8mp_blk_ctrl_probe(struct platform_device *pdev) domain->power_dev = dev_pm_domain_attach_by_name(dev, data->gpc_name); - if (IS_ERR(domain->power_dev)) { - dev_err_probe(dev, PTR_ERR(domain->power_dev), + if (IS_ERR_OR_NULL(domain->power_dev)) { + if (!domain->power_dev) + ret = -ENODEV; + else + ret = PTR_ERR(domain->power_dev); + dev_err_probe(dev, ret, "failed to attach power domain %s\n", data->gpc_name); - ret = PTR_ERR(domain->power_dev); goto cleanup_pds; } @@ -767,7 +770,7 @@ static void imx8mp_blk_ctrl_remove(struct platform_device *pdev) of_genpd_del_provider(pdev->dev.of_node); - for (i = 0; bc->onecell_data.num_domains; i++) { + for (i = 0; i < bc->onecell_data.num_domains; i++) { struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; pm_genpd_remove(&domain->genpd); @@ -854,11 +857,12 @@ MODULE_DEVICE_TABLE(of, imx8mp_blk_ctrl_of_match); static struct platform_driver imx8mp_blk_ctrl_driver = { .probe = imx8mp_blk_ctrl_probe, - .remove_new = imx8mp_blk_ctrl_remove, + .remove = imx8mp_blk_ctrl_remove, .driver = { .name = "imx8mp-blk-ctrl", .pm = &imx8mp_blk_ctrl_pm_ops, .of_match_table = imx8mp_blk_ctrl_of_match, + .suppress_bind_attrs = true, }, }; module_platform_driver(imx8mp_blk_ctrl_driver); diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c index 904ffa55b8f4..0e2ba8ec55d7 100644 --- a/drivers/pmdomain/imx/imx93-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c @@ -313,7 +313,9 @@ static void imx93_blk_ctrl_remove(struct platform_device *pdev) of_genpd_del_provider(pdev->dev.of_node); - for (i = 0; bc->onecell_data.num_domains; i++) { + pm_runtime_disable(&pdev->dev); + + for (i = 0; i < bc->onecell_data.num_domains; i++) { struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; pm_genpd_remove(&domain->genpd); @@ -436,7 +438,7 @@ MODULE_DEVICE_TABLE(of, imx93_blk_ctrl_of_match); static struct platform_driver imx93_blk_ctrl_driver = { .probe = imx93_blk_ctrl_probe, - .remove_new = imx93_blk_ctrl_remove, + .remove = imx93_blk_ctrl_remove, .driver = { .name = "imx93-blk-ctrl", .of_match_table = imx93_blk_ctrl_of_match, diff --git a/drivers/pmdomain/imx/imx93-pd.c b/drivers/pmdomain/imx/imx93-pd.c index 1e94b499c19b..d68273330687 100644 --- a/drivers/pmdomain/imx/imx93-pd.c +++ b/drivers/pmdomain/imx/imx93-pd.c @@ -20,6 +20,7 @@ #define FUNC_STAT_PSW_STAT_MASK BIT(0) #define FUNC_STAT_RST_STAT_MASK BIT(2) #define FUNC_STAT_ISO_STAT_MASK BIT(4) +#define FUNC_STAT_SSAR_STAT_MASK BIT(8) struct imx93_power_domain { struct generic_pm_domain genpd; @@ -27,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) @@ -50,7 +50,7 @@ static int imx93_pd_on(struct generic_pm_domain *genpd) writel(val, addr + MIX_SLICE_SW_CTRL_OFF); ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, - !(val & FUNC_STAT_ISO_STAT_MASK), 1, 10000); + !(val & FUNC_STAT_SSAR_STAT_MASK), 1, 10000); if (ret) { dev_err(domain->dev, "pd_on timeout: name: %s, stat: %x\n", genpd->name, val); return ret; @@ -72,7 +72,7 @@ static int imx93_pd_off(struct generic_pm_domain *genpd) writel(val, addr + MIX_SLICE_SW_CTRL_OFF); ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, - val & FUNC_STAT_PSW_STAT_MASK, 1, 1000); + val & FUNC_STAT_PSW_STAT_MASK, 1, 10000); if (ret) { dev_err(domain->dev, "pd_off timeout: name: %s, stat: %x\n", genpd->name, val); return ret; @@ -89,9 +89,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); } @@ -101,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); @@ -120,18 +118,17 @@ 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) { - 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); + ret = pm_genpd_init(&domain->genpd, NULL, init_off); if (ret) goto err_clk_unprepare; @@ -147,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; @@ -165,7 +162,7 @@ static struct platform_driver imx93_power_domain_driver = { .of_match_table = imx93_pd_ids, }, .probe = imx93_pd_probe, - .remove_new = imx93_pd_remove, + .remove = imx93_pd_remove, }; module_platform_driver(imx93_power_domain_driver); diff --git a/drivers/pmdomain/imx/scu-pd.c b/drivers/pmdomain/imx/scu-pd.c index 891c1d925a9d..01d465d88f60 100644 --- a/drivers/pmdomain/imx/scu-pd.c +++ b/drivers/pmdomain/imx/scu-pd.c @@ -223,11 +223,6 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "lvds1-pwm", IMX_SC_R_LVDS_1_PWM_0, 1, false, 0 }, { "lvds1-lpi2c", IMX_SC_R_LVDS_1_I2C_0, 2, true, 0 }, - { "mipi1", IMX_SC_R_MIPI_1, 1, 0 }, - { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, 0 }, - { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, 1 }, - { "lvds1", IMX_SC_R_LVDS_1, 1, 0 }, - /* DC SS */ { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, @@ -393,7 +388,7 @@ static int imx_sc_pd_power_off(struct generic_pm_domain *domain) return imx_sc_pd_power(domain, false); } -static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, +static struct generic_pm_domain *imx_scu_pd_xlate(const struct of_phandle_args *spec, void *data) { struct generic_pm_domain *domain = ERR_PTR(-ENOENT); diff --git a/drivers/pmdomain/mediatek/Kconfig b/drivers/pmdomain/mediatek/Kconfig index 21305c4f17fe..0e34a517ab7d 100644 --- a/drivers/pmdomain/mediatek/Kconfig +++ b/drivers/pmdomain/mediatek/Kconfig @@ -26,4 +26,16 @@ config MTK_SCPSYS_PM_DOMAINS Control Processor System (SCPSYS) has several power management related tasks in the system. +config AIROHA_CPU_PM_DOMAIN + tristate "Airoha CPU power domain" + default ARCH_AIROHA + depends on HAVE_ARM_SMCCC + depends on PM + select PM_GENERIC_DOMAINS + help + Say y here to enable CPU power domain support for Airoha SoC. + + CPU frequency and power is controlled by ATF with SMC command to + set performance states. + endmenu diff --git a/drivers/pmdomain/mediatek/Makefile b/drivers/pmdomain/mediatek/Makefile index 8cde09e654b3..18ba92e3c418 100644 --- a/drivers/pmdomain/mediatek/Makefile +++ b/drivers/pmdomain/mediatek/Makefile @@ -1,3 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o +obj-$(CONFIG_AIROHA_CPU_PM_DOMAIN) += airoha-cpu-pmdomain.o + +ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy) +# The use of R7 in the SMCCC conflicts with the compiler's use of R7 as a frame +# pointer in Thumb2 mode, which is forcibly enabled by Clang when profiling +# hooks are inserted via the -pg switch. +CFLAGS_REMOVE_airoha-cpu-pmdomain.o += $(CC_FLAGS_FTRACE) +endif diff --git a/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c b/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c new file mode 100644 index 000000000000..0fd88d2f9ac2 --- /dev/null +++ b/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/arm-smccc.h> +#include <linux/bitfield.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/slab.h> + +#define AIROHA_SIP_AVS_HANDLE 0x82000301 +#define AIROHA_AVS_OP_BASE 0xddddddd0 +#define AIROHA_AVS_OP_MASK GENMASK(1, 0) +#define AIROHA_AVS_OP_FREQ_DYN_ADJ (AIROHA_AVS_OP_BASE | \ + FIELD_PREP(AIROHA_AVS_OP_MASK, 0x1)) +#define AIROHA_AVS_OP_GET_FREQ (AIROHA_AVS_OP_BASE | \ + FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2)) + +struct airoha_cpu_pmdomain_priv { + struct clk_hw hw; + struct generic_pm_domain pd; +}; + +static long airoha_cpu_pmdomain_clk_round(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static unsigned long airoha_cpu_pmdomain_clk_get(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(AIROHA_SIP_AVS_HANDLE, AIROHA_AVS_OP_GET_FREQ, + 0, 0, 0, 0, 0, 0, &res); + + /* SMCCC returns freq in MHz */ + return (int)(res.a0 * 1000 * 1000); +} + +/* Airoha CPU clk SMCC is always enabled */ +static int airoha_cpu_pmdomain_clk_is_enabled(struct clk_hw *hw) +{ + return true; +} + +static const struct clk_ops airoha_cpu_pmdomain_clk_ops = { + .recalc_rate = airoha_cpu_pmdomain_clk_get, + .is_enabled = airoha_cpu_pmdomain_clk_is_enabled, + .round_rate = airoha_cpu_pmdomain_clk_round, +}; + +static int airoha_cpu_pmdomain_set_performance_state(struct generic_pm_domain *domain, + unsigned int state) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(AIROHA_SIP_AVS_HANDLE, AIROHA_AVS_OP_FREQ_DYN_ADJ, + 0, state, 0, 0, 0, 0, &res); + + /* SMC signal correct apply by unsetting BIT 0 */ + return res.a0 & BIT(0) ? -EINVAL : 0; +} + +static int airoha_cpu_pmdomain_probe(struct platform_device *pdev) +{ + struct airoha_cpu_pmdomain_priv *priv; + struct device *dev = &pdev->dev; + const struct clk_init_data init = { + .name = "cpu", + .ops = &airoha_cpu_pmdomain_clk_ops, + /* Clock with no set_rate, can't cache */ + .flags = CLK_GET_RATE_NOCACHE, + }; + struct generic_pm_domain *pd; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Init and register a get-only clk for Cpufreq */ + priv->hw.init = &init; + ret = devm_clk_hw_register(dev, &priv->hw); + if (ret) + return ret; + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &priv->hw); + if (ret) + return ret; + + /* Init and register a PD for CPU */ + pd = &priv->pd; + pd->name = "cpu_pd"; + pd->flags = GENPD_FLAG_ALWAYS_ON; + pd->set_performance_state = airoha_cpu_pmdomain_set_performance_state; + + ret = pm_genpd_init(pd, NULL, false); + if (ret) + return ret; + + ret = of_genpd_add_provider_simple(dev->of_node, pd); + if (ret) + goto err_add_provider; + + platform_set_drvdata(pdev, priv); + + return 0; + +err_add_provider: + pm_genpd_remove(pd); + + return ret; +} + +static void airoha_cpu_pmdomain_remove(struct platform_device *pdev) +{ + struct airoha_cpu_pmdomain_priv *priv = platform_get_drvdata(pdev); + + of_genpd_del_provider(pdev->dev.of_node); + pm_genpd_remove(&priv->pd); +} + +static const struct of_device_id airoha_cpu_pmdomain_of_match[] = { + { .compatible = "airoha,en7581-cpufreq" }, + { }, +}; +MODULE_DEVICE_TABLE(of, airoha_cpu_pmdomain_of_match); + +static struct platform_driver airoha_cpu_pmdomain_driver = { + .probe = airoha_cpu_pmdomain_probe, + .remove = airoha_cpu_pmdomain_remove, + .driver = { + .name = "airoha-cpu-pmdomain", + .of_match_table = airoha_cpu_pmdomain_of_match, + }, +}; +module_platform_driver(airoha_cpu_pmdomain_driver); + +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); +MODULE_DESCRIPTION("CPU PM domain driver for Airoha SoCs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/mediatek/mt6735-pm-domains.h b/drivers/pmdomain/mediatek/mt6735-pm-domains.h new file mode 100644 index 000000000000..71896be68e22 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt6735-pm-domains.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MT6735_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT6735_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include <dt-bindings/power/mediatek,mt6735-power-controller.h> + +/* + * MT6735 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt6735[] = { + [MT6735_POWER_DOMAIN_MD1] = { + .name = "md1", + .sta_mask = PWR_STATUS_MD1, + .ctl_offs = SPM_MD1_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = 0, + .bp_cfg = { + BUS_PROT_INFRA_UPDATE_TOPAXI(MT6735_TOP_AXI_PROT_EN_MD1), + }, + }, + [MT6735_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = PWR_STATUS_CONN, + .ctl_offs = SPM_CONN_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = 0, + .bp_cfg = { + BUS_PROT_INFRA_UPDATE_TOPAXI(MT6735_TOP_AXI_PROT_EN_CONN), + }, + }, + [MT6735_POWER_DOMAIN_DIS] = { + .name = "dis", + .sta_mask = PWR_STATUS_DISP, + .ctl_offs = SPM_DIS_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_cfg = { + BUS_PROT_INFRA_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MM_M0), + }, + }, + [MT6735_POWER_DOMAIN_MFG] = { + .name = "mfg", + .sta_mask = PWR_STATUS_MFG, + .ctl_offs = SPM_MFG_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_cfg = { + BUS_PROT_INFRA_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MFG_S), + }, + }, + [MT6735_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = PWR_STATUS_ISP, + .ctl_offs = SPM_ISP_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + }, + [MT6735_POWER_DOMAIN_VDE] = { + .name = "vde", + .sta_mask = PWR_STATUS_VDEC, + .ctl_offs = SPM_VDE_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT6735_POWER_DOMAIN_VEN] = { + .name = "ven", + .sta_mask = BIT(8), + .ctl_offs = SPM_VEN_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + }, +}; + +static const struct scpsys_soc_data mt6735_scpsys_data = { + .domains_data = scpsys_domain_data_mt6735, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt6735), +}; + +#endif /* __SOC_MEDIATEK_MT6735_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mt8188-pm-domains.h b/drivers/pmdomain/mediatek/mt8188-pm-domains.h index 06834ab6597c..007235be9efe 100644 --- a/drivers/pmdomain/mediatek/mt8188-pm-domains.h +++ b/drivers/pmdomain/mediatek/mt8188-pm-domains.h @@ -175,6 +175,8 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8188[] = { .ctl_offs = 0x35C, .pwr_sta_offs = 0x16C, .pwr_sta2nd_offs = 0x170, + .ext_buck_iso_offs = 0x3EC, + .ext_buck_iso_mask = BIT(10), .bp_cfg = { BUS_PROT_WR(INFRA, MT8188_TOP_AXI_PROT_EN_2_ADSP_AO_STEP1, @@ -187,7 +189,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8188[] = { MT8188_TOP_AXI_PROT_EN_2_CLR, MT8188_TOP_AXI_PROT_EN_2_STA), }, - .caps = MTK_SCPD_ALWAYS_ON, + .caps = MTK_SCPD_ALWAYS_ON | MTK_SCPD_EXT_BUCK_ISO, }, [MT8188_POWER_DOMAIN_ADSP_INFRA] = { .name = "adsp_infra", @@ -524,6 +526,8 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8188[] = { .ctl_offs = 0x3A4, .pwr_sta_offs = 0x16C, .pwr_sta2nd_offs = 0x170, + .ext_buck_iso_offs = 0x3EC, + .ext_buck_iso_mask = BIT(12), .bp_cfg = { BUS_PROT_WR(INFRA, MT8188_TOP_AXI_PROT_EN_MM_IMG_VCORE_STEP1, @@ -541,7 +545,8 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8188[] = { MT8188_TOP_AXI_PROT_EN_MM_2_CLR, MT8188_TOP_AXI_PROT_EN_MM_2_STA), }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY | + MTK_SCPD_EXT_BUCK_ISO, }, [MT8188_POWER_DOMAIN_IMG_MAIN] = { .name = "img_main", @@ -591,6 +596,8 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8188[] = { .ctl_offs = 0x3A0, .pwr_sta_offs = 0x16C, .pwr_sta2nd_offs = 0x170, + .ext_buck_iso_offs = 0x3EC, + .ext_buck_iso_mask = BIT(11), .bp_cfg = { BUS_PROT_WR(INFRA, MT8188_TOP_AXI_PROT_EN_MM_CAM_VCORE_STEP1, @@ -618,7 +625,8 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8188[] = { MT8188_TOP_AXI_PROT_EN_MM_2_CLR, MT8188_TOP_AXI_PROT_EN_MM_2_STA), }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY | + MTK_SCPD_EXT_BUCK_ISO, }, [MT8188_POWER_DOMAIN_CAM_MAIN] = { .name = "cam_main", diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index e274e3315fe7..b866b006af69 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -16,6 +16,7 @@ #include <linux/regulator/consumer.h> #include <linux/soc/mediatek/infracfg.h> +#include "mt6735-pm-domains.h" #include "mt6795-pm-domains.h" #include "mt8167-pm-domains.h" #include "mt8173-pm-domains.h" @@ -353,7 +354,6 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no { const struct scpsys_domain_data *domain_data; struct scpsys_domain *pd; - struct device_node *root_node = scpsys->dev->of_node; struct device_node *smi_node; struct property *prop; const char *clk_name; @@ -388,22 +388,11 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no pd->scpsys = scpsys; if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY)) { - /* - * Find regulator in current power domain node. - * devm_regulator_get() finds regulator in a node and its child - * node, so set of_node to current power domain node then change - * back to original node after regulator is found for current - * power domain node. - */ - 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), + pd->supply = devm_of_regulator_get_optional(scpsys->dev, node, "domain"); + 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"); @@ -621,6 +610,10 @@ static void scpsys_domain_cleanup(struct scpsys *scpsys) static const struct of_device_id scpsys_of_match[] = { { + .compatible = "mediatek,mt6735-power-controller", + .data = &mt6735_scpsys_data, + }, + { .compatible = "mediatek,mt6795-power-controller", .data = &mt6795_scpsys_data, }, diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h index aaba5e6b0536..2ac96804b985 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.h +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h @@ -21,6 +21,7 @@ #define SPM_ISP_PWR_CON 0x0238 #define SPM_DIS_PWR_CON 0x023c #define SPM_CONN_PWR_CON 0x0280 +#define SPM_MD1_PWR_CON 0x0284 #define SPM_VEN2_PWR_CON 0x0298 #define SPM_AUDIO_PWR_CON 0x029c #define SPM_MFG_2D_PWR_CON 0x02c0 @@ -30,6 +31,7 @@ #define SPM_PWR_STATUS 0x060c #define SPM_PWR_STATUS_2ND 0x0610 +#define PWR_STATUS_MD1 BIT(0) #define PWR_STATUS_CONN BIT(1) #define PWR_STATUS_DISP BIT(3) #define PWR_STATUS_MFG BIT(4) diff --git a/drivers/pmdomain/mediatek/mtk-scpsys.c b/drivers/pmdomain/mediatek/mtk-scpsys.c index b374d01fdac7..1a80c1537a43 100644 --- a/drivers/pmdomain/mediatek/mtk-scpsys.c +++ b/drivers/pmdomain/mediatek/mtk-scpsys.c @@ -425,7 +425,6 @@ static struct scp *init_scp(struct platform_device *pdev, bool bus_prot_reg_update) { struct genpd_onecell_data *pd_data; - struct resource *res; int i, j; struct scp *scp; struct clk *clk[CLK_MAX]; @@ -441,8 +440,7 @@ static struct scp *init_scp(struct platform_device *pdev, scp->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - scp->base = devm_ioremap_resource(&pdev->dev, res); + scp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(scp->base)) return ERR_CAST(scp->base); @@ -1140,7 +1138,6 @@ static struct platform_driver scpsys_drv = { .driver = { .name = "mtk-scpsys", .suppress_bind_attrs = true, - .owner = THIS_MODULE, .of_match_table = of_scpsys_match_tbl, }, }; diff --git a/drivers/pmdomain/qcom/cpr.c b/drivers/pmdomain/qcom/cpr.c index c64e84a27cc7..3ee8184e4be3 100644 --- a/drivers/pmdomain/qcom/cpr.c +++ b/drivers/pmdomain/qcom/cpr.c @@ -4,6 +4,7 @@ * Copyright (c) 2019, Linaro Limited */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/err.h> #include <linux/debugfs.h> @@ -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 @@ -1040,36 +1036,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; - do { - 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); + 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); - if (child_np && child_req_np == ref_np) - of_property_read_u64(child_np, "opp-hz", &rate); + if (child_req_np == ref_np) { + u64 rate = 0; - of_node_put(child_req_np); - of_node_put(child_np); - of_node_put(ref_np); -out_ref: - of_node_put(desc_np); + of_property_read_u64(child_np, "opp-hz", &rate); + return (unsigned long) rate; + } + } - return (unsigned long) rate; + return 0; } static int cpr_corner_init(struct cpr_drv *drv) @@ -1443,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)); @@ -1457,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 @@ -1469,12 +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 = PTR_ERR(drv->cpu_clk); - if (ret != -EPROBE_DEFER) - dev_err(drv->dev, "could not get cpu clk: %d\n", ret); - 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", @@ -1491,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, @@ -1541,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) @@ -1735,7 +1717,7 @@ MODULE_DEVICE_TABLE(of, cpr_match_table); static struct platform_driver cpr_driver = { .probe = cpr_probe, - .remove_new = cpr_remove, + .remove = cpr_remove, .driver = { .name = "qcom-cpr", .of_match_table = cpr_match_table, diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 47df910645f6..dfd0f80154e4 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 <linux/cleanup.h> #include <linux/err.h> #include <linux/init.h> #include <linux/kernel.h> @@ -40,6 +41,7 @@ * @addr: Resource address as looped up using resource name from * cmd-db * @state_synced: Indicator that sync_state has been invoked for the rpmhpd resource + * @skip_retention_level: Indicate that retention level should not be used for the power domain */ struct rpmhpd { struct device *dev; @@ -56,6 +58,7 @@ struct rpmhpd { const char *res_name; u32 addr; bool state_synced; + bool skip_retention_level; }; struct rpmhpd_desc { @@ -173,6 +176,7 @@ static struct rpmhpd mxc = { .pd = { .name = "mxc", }, .peer = &mxc_ao, .res_name = "mxc.lvl", + .skip_retention_level = true, }; static struct rpmhpd mxc_ao = { @@ -180,6 +184,7 @@ static struct rpmhpd mxc_ao = { .active_only = true, .peer = &mxc, .res_name = "mxc.lvl", + .skip_retention_level = true, }; static struct rpmhpd nsp = { @@ -217,7 +222,6 @@ static struct rpmhpd *sa8540p_rpmhpds[] = { [SC8280XP_CX] = &cx, [SC8280XP_CX_AO] = &cx_ao, [SC8280XP_EBI] = &ebi, - [SC8280XP_GFX] = &gfx, [SC8280XP_LCX] = &lcx, [SC8280XP_LMX] = &lmx, [SC8280XP_MMCX] = &mmcx, @@ -255,6 +259,30 @@ static const struct rpmhpd_desc sa8775p_desc = { .num_pds = ARRAY_SIZE(sa8775p_rpmhpds), }; +/* SAR2130P RPMH powerdomains */ +static struct rpmhpd *sar2130p_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx_w_cx_parent, + [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, + [RPMHPD_MSS] = &mss, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_NSP] = &nsp, + [RPMHPD_QPHY] = &qphy, +}; + +static const struct rpmhpd_desc sar2130p_desc = { + .rpmhpds = sar2130p_rpmhpds, + .num_pds = ARRAY_SIZE(sar2130p_rpmhpds), +}; + /* SDM670 RPMH powerdomains */ static struct rpmhpd *sdm670_rpmhpds[] = { [SDM670_CX] = &cx_w_mx_parent, @@ -509,6 +537,31 @@ static const struct rpmhpd_desc sm8650_desc = { .num_pds = ARRAY_SIZE(sm8650_rpmhpds), }; +/* SM8750 RPMH powerdomains */ +static struct rpmhpd *sm8750_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_GMXC] = &gmxc, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MMCX] = &mmcx_w_cx_parent, + [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, + [RPMHPD_MSS] = &mss, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_NSP] = &nsp, + [RPMHPD_NSP2] = &nsp2, +}; + +static const struct rpmhpd_desc sm8750_desc = { + .rpmhpds = sm8750_rpmhpds, + .num_pds = ARRAY_SIZE(sm8750_rpmhpds), +}; + /* QDU1000/QRU1000 RPMH powerdomains */ static struct rpmhpd *qdu1000_rpmhpds[] = { [QDU1000_CX] = &cx, @@ -620,11 +673,48 @@ static const struct rpmhpd_desc x1e80100_desc = { .num_pds = ARRAY_SIZE(x1e80100_rpmhpds), }; +/* QCS8300 RPMH power domains */ +static struct rpmhpd *qcs8300_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx_w_cx_parent, + [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_NSP0] = &nsp0, + [RPMHPD_NSP1] = &nsp1, +}; + +static const struct rpmhpd_desc qcs8300_desc = { + .rpmhpds = qcs8300_rpmhpds, + .num_pds = ARRAY_SIZE(qcs8300_rpmhpds), +}; + +/* QCS615 RPMH powerdomains */ +static struct rpmhpd *qcs615_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, +}; + +static const struct rpmhpd_desc qcs615_desc = { + .rpmhpds = qcs615_rpmhpds, + .num_pds = ARRAY_SIZE(qcs615_rpmhpds), +}; + static const struct of_device_id rpmhpd_match_table[] = { + { .compatible = "qcom,qcs615-rpmhpd", .data = &qcs615_desc }, + { .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc }, { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, { .compatible = "qcom,sa8155p-rpmhpd", .data = &sa8155p_desc }, { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, { .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc }, + { .compatible = "qcom,sar2130p-rpmhpd", .data = &sar2130p_desc}, { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, @@ -642,6 +732,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sm8450-rpmhpd", .data = &sm8450_desc }, { .compatible = "qcom,sm8550-rpmhpd", .data = &sm8550_desc }, { .compatible = "qcom,sm8650-rpmhpd", .data = &sm8650_desc }, + { .compatible = "qcom,sm8750-rpmhpd", .data = &sm8750_desc }, { .compatible = "qcom,x1e80100-rpmhpd", .data = &x1e80100_desc }, { } }; @@ -772,9 +863,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]) @@ -794,14 +885,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) @@ -820,6 +909,9 @@ static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) return -EINVAL; for (i = 0; i < rpmhpd->level_count; i++) { + if (rpmhpd->skip_retention_level && buf[i] == RPMH_REGULATOR_LEVEL_RETENTION) + continue; + rpmhpd->level[i] = buf[i]; /* Remember the first corner with non-zero level */ diff --git a/drivers/pmdomain/qcom/rpmpd.c b/drivers/pmdomain/qcom/rpmpd.c index 7796d65f96e8..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 <linux/cleanup.h> #include <linux/err.h> #include <linux/init.h> #include <linux/kernel.h> @@ -16,6 +17,8 @@ #define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd) +static struct qcom_smd_rpm *rpmpd_smd_rpm; + /* Resource types: * RPMPD_X is X encoded as a little-endian, lower-case, ASCII string */ #define RPMPD_SMPA 0x61706d73 @@ -54,7 +57,6 @@ struct rpmpd { bool enabled; const int res_type; const int res_id; - struct qcom_smd_rpm *rpm; unsigned int max_state; __le32 key; bool state_synced; @@ -226,7 +228,46 @@ static struct rpmpd cx_s3a_vfl = { .key = KEY_FLOOR_LEVEL, }; +static struct rpmpd cx_s2b_corner_ao; +static struct rpmpd cx_s2b_corner = { + .pd = { .name = "cx", }, + .peer = &cx_s2b_corner_ao, + .res_type = RPMPD_SMPB, + .res_id = 2, + .key = KEY_CORNER, +}; + +static struct rpmpd cx_s2b_corner_ao = { + .pd = { .name = "cx_ao", }, + .peer = &cx_s2b_corner, + .active_only = true, + .res_type = RPMPD_SMPB, + .res_id = 2, + .key = KEY_CORNER, +}; + +static struct rpmpd cx_s2b_vfc = { + .pd = { .name = "cx_vfc", }, + .res_type = RPMPD_SMPB, + .res_id = 2, + .key = KEY_FLOOR_CORNER, +}; + /* G(F)X */ +static struct rpmpd gfx_s7a_corner = { + .pd = { .name = "gfx", }, + .res_type = RPMPD_SMPA, + .res_id = 7, + .key = KEY_CORNER, +}; + +static struct rpmpd gfx_s7a_vfc = { + .pd = { .name = "gfx_vfc", }, + .res_type = RPMPD_SMPA, + .res_id = 7, + .key = KEY_FLOOR_CORNER, +}; + static struct rpmpd gfx_s2b_corner = { .pd = { .name = "gfx", }, .res_type = RPMPD_SMPB, @@ -241,6 +282,20 @@ static struct rpmpd gfx_s2b_vfc = { .key = KEY_FLOOR_CORNER, }; +static struct rpmpd gfx_s4b_corner = { + .pd = { .name = "gfx", }, + .res_type = RPMPD_SMPB, + .res_id = 4, + .key = KEY_CORNER, +}; + +static struct rpmpd gfx_s4b_vfc = { + .pd = { .name = "gfx_vfc", }, + .res_type = RPMPD_SMPB, + .res_id = 4, + .key = KEY_FLOOR_CORNER, +}; + static struct rpmpd mx_rwmx0_lvl; static struct rpmpd gx_rwgx0_lvl_ao; static struct rpmpd gx_rwgx0_lvl = { @@ -663,6 +718,34 @@ static const struct rpmpd_desc msm8953_desc = { .max_state = RPM_SMD_LEVEL_TURBO, }; +static struct rpmpd *msm8974_rpmpds[] = { + [MSM8974_VDDCX] = &cx_s2b_corner, + [MSM8974_VDDCX_AO] = &cx_s2b_corner_ao, + [MSM8974_VDDCX_VFC] = &cx_s2b_vfc, + [MSM8974_VDDGFX] = &gfx_s4b_corner, + [MSM8974_VDDGFX_VFC] = &gfx_s4b_vfc, +}; + +static const struct rpmpd_desc msm8974_desc = { + .rpmpds = msm8974_rpmpds, + .num_pds = ARRAY_SIZE(msm8974_rpmpds), + .max_state = MAX_CORNER_RPMPD_STATE, +}; + +static struct rpmpd *msm8974pro_pma8084_rpmpds[] = { + [MSM8974_VDDCX] = &cx_s2a_corner, + [MSM8974_VDDCX_AO] = &cx_s2a_corner_ao, + [MSM8974_VDDCX_VFC] = &cx_s2a_vfc, + [MSM8974_VDDGFX] = &gfx_s7a_corner, + [MSM8974_VDDGFX_VFC] = &gfx_s7a_vfc, +}; + +static const struct rpmpd_desc msm8974pro_pma8084_desc = { + .rpmpds = msm8974pro_pma8084_rpmpds, + .num_pds = ARRAY_SIZE(msm8974pro_pma8084_rpmpds), + .max_state = MAX_CORNER_RPMPD_STATE, +}; + static struct rpmpd *msm8976_rpmpds[] = { [MSM8976_VDDCX] = &cx_s2a_lvl, [MSM8976_VDDCX_AO] = &cx_s2a_lvl_ao, @@ -856,6 +939,8 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,msm8917-rpmpd", .data = &msm8917_desc }, { .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc }, { .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc }, + { .compatible = "qcom,msm8974-rpmpd", .data = &msm8974_desc }, + { .compatible = "qcom,msm8974pro-pma8084-rpmpd", .data = &msm8974pro_pma8084_desc }, { .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc }, { .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc }, { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, @@ -879,7 +964,7 @@ static int rpmpd_send_enable(struct rpmpd *pd, bool enable) .value = cpu_to_le32(enable), }; - return qcom_rpm_smd_write(pd->rpm, QCOM_SMD_RPM_ACTIVE_STATE, + return qcom_rpm_smd_write(rpmpd_smd_rpm, QCOM_SMD_RPM_ACTIVE_STATE, pd->res_type, pd->res_id, &req, sizeof(req)); } @@ -891,7 +976,7 @@ static int rpmpd_send_corner(struct rpmpd *pd, int state, unsigned int corner) .value = cpu_to_le32(corner), }; - return qcom_rpm_smd_write(pd->rpm, state, pd->res_type, pd->res_id, + return qcom_rpm_smd_write(rpmpd_smd_rpm, state, pd->res_type, pd->res_id, &req, sizeof(req)); }; @@ -940,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; } @@ -976,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; - - ret = rpmpd_aggregate_corner(pd); + return 0; -out: - mutex_unlock(&rpmpd_lock); - - return ret; + return rpmpd_aggregate_corner(pd); } static int rpmpd_probe(struct platform_device *pdev) @@ -1004,12 +1080,11 @@ static int rpmpd_probe(struct platform_device *pdev) int i; size_t num; struct genpd_onecell_data *data; - struct qcom_smd_rpm *rpm; struct rpmpd **rpmpds; const struct rpmpd_desc *desc; - rpm = dev_get_drvdata(pdev->dev.parent); - if (!rpm) { + rpmpd_smd_rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpmpd_smd_rpm) { dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); return -ENODEV; } @@ -1039,7 +1114,6 @@ static int rpmpd_probe(struct platform_device *pdev) continue; } - rpmpds[i]->rpm = rpm; rpmpds[i]->max_state = desc->max_state; rpmpds[i]->pd.power_off = rpmpd_power_off; rpmpds[i]->pd.power_on = rpmpd_power_on; diff --git a/drivers/pmdomain/renesas/Kconfig b/drivers/pmdomain/renesas/Kconfig index 80bf2cf8b60e..54acb4b1ec7c 100644 --- a/drivers/pmdomain/renesas/Kconfig +++ b/drivers/pmdomain/renesas/Kconfig @@ -71,6 +71,10 @@ config SYSC_R8A779G0 bool "System Controller support for R-Car V4H" if COMPILE_TEST select SYSC_RCAR_GEN4 +config SYSC_R8A779H0 + bool "System Controller support for R-Car V4M" if COMPILE_TEST + select SYSC_RCAR_GEN4 + config SYSC_RMOBILE bool "System Controller support for R-Mobile" if COMPILE_TEST diff --git a/drivers/pmdomain/renesas/Makefile b/drivers/pmdomain/renesas/Makefile index e306e396fc8c..0391e6e67440 100644 --- a/drivers/pmdomain/renesas/Makefile +++ b/drivers/pmdomain/renesas/Makefile @@ -14,8 +14,8 @@ obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o -obj-$(CONFIG_SYSC_R8A77960) += r8a7796-sysc.o -obj-$(CONFIG_SYSC_R8A77961) += r8a7796-sysc.o +obj-$(CONFIG_SYSC_R8A77960) += r8a77960-sysc.o +obj-$(CONFIG_SYSC_R8A77961) += r8a77961-sysc.o obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o @@ -24,6 +24,7 @@ obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o obj-$(CONFIG_SYSC_R8A779A0) += r8a779a0-sysc.o obj-$(CONFIG_SYSC_R8A779F0) += r8a779f0-sysc.o obj-$(CONFIG_SYSC_R8A779G0) += r8a779g0-sysc.o +obj-$(CONFIG_SYSC_R8A779H0) += r8a779h0-sysc.o # Family obj-$(CONFIG_SYSC_RCAR) += rcar-sysc.o obj-$(CONFIG_SYSC_RCAR_GEN4) += rcar-gen4-sysc.o diff --git a/drivers/pmdomain/renesas/r8a77960-sysc.c b/drivers/pmdomain/renesas/r8a77960-sysc.c new file mode 100644 index 000000000000..2ab3f565d2b0 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a77960-sysc.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car M3-W System Controller + * + * Copyright (C) 2016 Glider bvba + * Copyright (C) 2018-2019 Renesas Electronics Corporation + */ + +#include <linux/bits.h> +#include <linux/kernel.h> + +#include <dt-bindings/power/r8a7796-sysc.h> + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77960_areas[] __initconst = { + { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A7796_PD_CA57_CPU0, R8A7796_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A7796_PD_CA57_CPU1, R8A7796_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca53-scu", 0x140, 0, R8A7796_PD_CA53_SCU, R8A7796_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A7796_PD_CA53_CPU0, R8A7796_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A7796_PD_CA53_CPU1, R8A7796_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A7796_PD_CA53_CPU2, R8A7796_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A7796_PD_CA53_CPU3, R8A7796_PD_CA53_SCU, + PD_CPU_NOCR }, + { "cr7", 0x240, 0, R8A7796_PD_CR7, R8A7796_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A7796_PD_A3VC, R8A7796_PD_ALWAYS_ON, + PD_OFF_DELAY }, + { "a2vc0", 0x3c0, 0, R8A7796_PD_A2VC0, R8A7796_PD_A3VC }, + { "a2vc1", 0x3c0, 1, R8A7796_PD_A2VC1, R8A7796_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A7796_PD_3DG_A, R8A7796_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A7796_PD_3DG_B, R8A7796_PD_3DG_A }, + { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON, + PD_OFF_DELAY }, +}; + + +const struct rcar_sysc_info r8a77960_sysc_info __initconst = { + .areas = r8a77960_areas, + .num_areas = ARRAY_SIZE(r8a77960_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a7796-sysc.c b/drivers/pmdomain/renesas/r8a77961-sysc.c index 471bd5b3b6ad..a1155068ee7a 100644 --- a/drivers/pmdomain/renesas/r8a7796-sysc.c +++ b/drivers/pmdomain/renesas/r8a77961-sysc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Renesas R-Car M3-W/W+ System Controller + * Renesas R-Car M3-W+ System Controller * * Copyright (C) 2016 Glider bvba * Copyright (C) 2018-2019 Renesas Electronics Corporation @@ -13,7 +13,7 @@ #include "rcar-sysc.h" -static struct rcar_sysc_area r8a7796_areas[] __initdata = { +static const struct rcar_sysc_area r8a77961_areas[] __initconst = { { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON, PD_SCU }, @@ -33,35 +33,15 @@ static struct rcar_sysc_area r8a7796_areas[] __initdata = { PD_CPU_NOCR }, { "cr7", 0x240, 0, R8A7796_PD_CR7, R8A7796_PD_ALWAYS_ON }, { "a3vc", 0x380, 0, R8A7796_PD_A3VC, R8A7796_PD_ALWAYS_ON }, - { "a2vc0", 0x3c0, 0, R8A7796_PD_A2VC0, R8A7796_PD_A3VC }, { "a2vc1", 0x3c0, 1, R8A7796_PD_A2VC1, R8A7796_PD_A3VC }, { "3dg-a", 0x100, 0, R8A7796_PD_3DG_A, R8A7796_PD_ALWAYS_ON }, { "3dg-b", 0x100, 1, R8A7796_PD_3DG_B, R8A7796_PD_3DG_A }, { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON }, }; - -#ifdef CONFIG_SYSC_R8A77960 -const struct rcar_sysc_info r8a77960_sysc_info __initconst = { - .areas = r8a7796_areas, - .num_areas = ARRAY_SIZE(r8a7796_areas), -}; -#endif /* CONFIG_SYSC_R8A77960 */ - -#ifdef CONFIG_SYSC_R8A77961 -static int __init r8a77961_sysc_init(void) -{ - rcar_sysc_nullify(r8a7796_areas, ARRAY_SIZE(r8a7796_areas), - R8A7796_PD_A2VC0); - - return 0; -} - const struct rcar_sysc_info r8a77961_sysc_info __initconst = { - .init = r8a77961_sysc_init, - .areas = r8a7796_areas, - .num_areas = ARRAY_SIZE(r8a7796_areas), + .areas = r8a77961_areas, + .num_areas = ARRAY_SIZE(r8a77961_areas), .extmask_offs = 0x2f8, .extmask_val = BIT(0), }; -#endif /* CONFIG_SYSC_R8A77961 */ diff --git a/drivers/pmdomain/renesas/r8a779a0-sysc.c b/drivers/pmdomain/renesas/r8a779a0-sysc.c index 04f1bc322ae7..54cdf250f7c2 100644 --- a/drivers/pmdomain/renesas/r8a779a0-sysc.c +++ b/drivers/pmdomain/renesas/r8a779a0-sysc.c @@ -5,19 +5,7 @@ * Copyright (C) 2020 Renesas Electronics Corp. */ -#include <linux/bits.h> -#include <linux/clk/renesas.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/iopoll.h> #include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/of_address.h> -#include <linux/pm_domain.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/types.h> #include <dt-bindings/power/r8a779a0-sysc.h> diff --git a/drivers/pmdomain/renesas/r8a779f0-sysc.c b/drivers/pmdomain/renesas/r8a779f0-sysc.c index 5602aa6bd7ed..6ed13cd1cb24 100644 --- a/drivers/pmdomain/renesas/r8a779f0-sysc.c +++ b/drivers/pmdomain/renesas/r8a779f0-sysc.c @@ -5,19 +5,7 @@ * Copyright (C) 2021 Renesas Electronics Corp. */ -#include <linux/bits.h> -#include <linux/clk/renesas.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/iopoll.h> #include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/of_address.h> -#include <linux/pm_domain.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/types.h> #include <dt-bindings/power/r8a779f0-sysc.h> diff --git a/drivers/pmdomain/renesas/r8a779g0-sysc.c b/drivers/pmdomain/renesas/r8a779g0-sysc.c index b932eba1b804..249cf43af45b 100644 --- a/drivers/pmdomain/renesas/r8a779g0-sysc.c +++ b/drivers/pmdomain/renesas/r8a779g0-sysc.c @@ -5,19 +5,7 @@ * Copyright (C) 2022 Renesas Electronics Corp. */ -#include <linux/bits.h> -#include <linux/clk/renesas.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/iopoll.h> #include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/of_address.h> -#include <linux/pm_domain.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/types.h> #include <dt-bindings/power/r8a779g0-sysc.h> diff --git a/drivers/pmdomain/renesas/r8a779h0-sysc.c b/drivers/pmdomain/renesas/r8a779h0-sysc.c new file mode 100644 index 000000000000..e13372cb80ee --- /dev/null +++ b/drivers/pmdomain/renesas/r8a779h0-sysc.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car V4M System Controller + * + * Copyright (C) 2023 Renesas Electronics Corp + */ + +#include <linux/kernel.h> + +#include <dt-bindings/power/renesas,r8a779h0-sysc.h> + +#include "rcar-gen4-sysc.h" + +static struct rcar_gen4_sysc_area r8a779h0_areas[] __initdata = { + { "always-on", R8A779H0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "c4", R8A779H0_PD_C4, R8A779H0_PD_ALWAYS_ON }, + { "a2e0d0", R8A779H0_PD_A2E0D0, R8A779H0_PD_C4, PD_SCU }, + { "a1e0d0c0", R8A779H0_PD_A1E0D0C0, R8A779H0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d0c1", R8A779H0_PD_A1E0D0C1, R8A779H0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d0c2", R8A779H0_PD_A1E0D0C2, R8A779H0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d0c3", R8A779H0_PD_A1E0D0C3, R8A779H0_PD_A2E0D0, PD_CPU_NOCR }, + { "a3cr0", R8A779H0_PD_A3CR0, R8A779H0_PD_ALWAYS_ON, PD_CPU_NOCR }, + { "a3cr1", R8A779H0_PD_A3CR1, R8A779H0_PD_ALWAYS_ON, PD_CPU_NOCR }, + { "a3cr2", R8A779H0_PD_A3CR2, R8A779H0_PD_ALWAYS_ON, PD_CPU_NOCR }, + { "a33dga", R8A779H0_PD_A33DGA, R8A779H0_PD_C4 }, + { "a23dgb", R8A779H0_PD_A23DGB, R8A779H0_PD_A33DGA }, + { "a3vip0", R8A779H0_PD_A3VIP0, R8A779H0_PD_C4 }, + { "a3vip2", R8A779H0_PD_A3VIP2, R8A779H0_PD_C4 }, + { "a3dul", R8A779H0_PD_A3DUL, R8A779H0_PD_C4 }, + { "a3isp0", R8A779H0_PD_A3ISP0, R8A779H0_PD_C4 }, + { "a2cn0", R8A779H0_PD_A2CN0, R8A779H0_PD_C4 }, + { "a1cn0", R8A779H0_PD_A1CN0, R8A779H0_PD_A2CN0 }, + { "a1dsp0", R8A779H0_PD_A1DSP0, R8A779H0_PD_A2CN0 }, + { "a1dsp1", R8A779H0_PD_A1DSP1, R8A779H0_PD_A2CN0 }, + { "a2imp01", R8A779H0_PD_A2IMP01, R8A779H0_PD_C4 }, + { "a2psc", R8A779H0_PD_A2PSC, R8A779H0_PD_C4 }, + { "a2dma", R8A779H0_PD_A2DMA, R8A779H0_PD_C4 }, + { "a2cv0", R8A779H0_PD_A2CV0, R8A779H0_PD_C4 }, + { "a2cv1", R8A779H0_PD_A2CV1, R8A779H0_PD_C4 }, + { "a2cv2", R8A779H0_PD_A2CV2, R8A779H0_PD_C4 }, + { "a2cv3", R8A779H0_PD_A2CV3, R8A779H0_PD_C4 }, + { "a3imr0", R8A779H0_PD_A3IMR0, R8A779H0_PD_C4 }, + { "a3imr1", R8A779H0_PD_A3IMR1, R8A779H0_PD_C4 }, + { "a3imr2", R8A779H0_PD_A3IMR2, R8A779H0_PD_C4 }, + { "a3imr3", R8A779H0_PD_A3IMR3, R8A779H0_PD_C4 }, + { "a3vc", R8A779H0_PD_A3VC, R8A779H0_PD_C4 }, + { "a3pci", R8A779H0_PD_A3PCI, R8A779H0_PD_C4 }, + { "a2pciphy", R8A779H0_PD_A2PCIPHY, R8A779H0_PD_A3PCI }, +}; + +const struct rcar_gen4_sysc_info r8a779h0_sysc_info __initconst = { + .areas = r8a779h0_areas, + .num_areas = ARRAY_SIZE(r8a779h0_areas), +}; diff --git a/drivers/pmdomain/renesas/rcar-gen4-sysc.c b/drivers/pmdomain/renesas/rcar-gen4-sysc.c index 9e5e6e077abc..66409cff2083 100644 --- a/drivers/pmdomain/renesas/rcar-gen4-sysc.c +++ b/drivers/pmdomain/renesas/rcar-gen4-sysc.c @@ -50,13 +50,13 @@ #define SYSCSR_BUSY GENMASK(1, 0) /* All bit sets is not busy */ #define SYSCSR_TIMEOUT 10000 -#define SYSCSR_DELAY_US 10 +#define SYSCSR_DELAY_US 1 -#define PDRESR_RETRIES 1000 -#define PDRESR_DELAY_US 10 +#define PDRESR_RETRIES 10000 +#define PDRESR_DELAY_US 1 -#define SYSCISR_TIMEOUT 10000 -#define SYSCISR_DELAY_US 10 +#define SYSCISCR_TIMEOUT 10000 +#define SYSCISCR_DELAY_US 1 #define RCAR_GEN4_PD_ALWAYS_ON 64 #define NUM_DOMAINS_EACH_REG BITS_PER_TYPE(u32) @@ -97,7 +97,7 @@ static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask) ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx), val, !(val & isr_mask), - SYSCISR_DELAY_US, SYSCISR_TIMEOUT); + SYSCISCR_DELAY_US, SYSCISCR_TIMEOUT); if (ret < 0) { pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__); return -EIO; @@ -157,7 +157,7 @@ static int rcar_gen4_sysc_power(u8 pdr, bool on) /* Wait until the power shutoff or resume request has completed * */ ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx), val, (val & isr_mask), - SYSCISR_DELAY_US, SYSCISR_TIMEOUT); + SYSCISCR_DELAY_US, SYSCISCR_TIMEOUT); if (ret < 0) { ret = -EIO; goto out; @@ -285,6 +285,9 @@ static const struct of_device_id rcar_gen4_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A779G0 { .compatible = "renesas,r8a779g0-sysc", .data = &r8a779g0_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A779H0 + { .compatible = "renesas,r8a779h0-sysc", .data = &r8a779h0_sysc_info }, +#endif { /* sentinel */ } }; diff --git a/drivers/pmdomain/renesas/rcar-gen4-sysc.h b/drivers/pmdomain/renesas/rcar-gen4-sysc.h index 388cfa8f8f9f..fdf843aa5113 100644 --- a/drivers/pmdomain/renesas/rcar-gen4-sysc.h +++ b/drivers/pmdomain/renesas/rcar-gen4-sysc.h @@ -40,5 +40,6 @@ struct rcar_gen4_sysc_info { extern const struct rcar_gen4_sysc_info r8a779a0_sysc_info; extern const struct rcar_gen4_sysc_info r8a779f0_sysc_info; extern const struct rcar_gen4_sysc_info r8a779g0_sysc_info; +extern const struct rcar_gen4_sysc_info r8a779h0_sysc_info; #endif /* __SOC_RENESAS_RCAR_GEN4_SYSC_H__ */ diff --git a/drivers/pmdomain/renesas/rcar-sysc.c b/drivers/pmdomain/renesas/rcar-sysc.c index eed47696e825..b99326917330 100644 --- a/drivers/pmdomain/renesas/rcar-sysc.c +++ b/drivers/pmdomain/renesas/rcar-sysc.c @@ -45,10 +45,10 @@ #define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ -#define SYSCSR_TIMEOUT 100 +#define SYSCSR_TIMEOUT 1000 #define SYSCSR_DELAY_US 1 -#define PWRER_RETRIES 100 +#define PWRER_RETRIES 1000 #define PWRER_DELAY_US 1 #define SYSCISR_TIMEOUT 1000 @@ -56,17 +56,20 @@ #define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */ -struct rcar_sysc_ch { +struct rcar_sysc_pd { + struct generic_pm_domain genpd; u16 chan_offs; u8 chan_bit; u8 isr_bit; + unsigned int flags; + char name[]; }; static void __iomem *rcar_sysc_base; static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val; -static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) +static int rcar_sysc_pwr_on_off(const struct rcar_sysc_pd *pd, bool on) { unsigned int sr_bit, reg_offs; u32 val; @@ -87,17 +90,20 @@ static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) if (ret) return -EAGAIN; + /* Power-off delay quirk */ + if (!on && (pd->flags & PD_OFF_DELAY)) + udelay(1); + /* Submit power shutoff or power resume request */ - iowrite32(BIT(sysc_ch->chan_bit), - rcar_sysc_base + sysc_ch->chan_offs + reg_offs); + iowrite32(BIT(pd->chan_bit), rcar_sysc_base + pd->chan_offs + reg_offs); return 0; } -static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) +static int rcar_sysc_power(const struct rcar_sysc_pd *pd, bool on) { - unsigned int isr_mask = BIT(sysc_ch->isr_bit); - unsigned int chan_mask = BIT(sysc_ch->chan_bit); + unsigned int isr_mask = BIT(pd->isr_bit); + unsigned int chan_mask = BIT(pd->chan_bit); unsigned int status, k; unsigned long flags; int ret; @@ -125,12 +131,11 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) /* Submit power shutoff or resume request until it was accepted */ for (k = 0; k < PWRER_RETRIES; k++) { - ret = rcar_sysc_pwr_on_off(sysc_ch, on); + ret = rcar_sysc_pwr_on_off(pd, on); if (ret) goto out; - status = ioread32(rcar_sysc_base + - sysc_ch->chan_offs + PWRER_OFFS); + status = ioread32(rcar_sysc_base + pd->chan_offs + PWRER_OFFS); if (!(status & chan_mask)) break; @@ -158,28 +163,21 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) spin_unlock_irqrestore(&rcar_sysc_lock, flags); pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", - sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); + pd->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); return ret; } -static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) +static bool rcar_sysc_power_is_off(const struct rcar_sysc_pd *pd) { unsigned int st; - st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); - if (st & BIT(sysc_ch->chan_bit)) + st = ioread32(rcar_sysc_base + pd->chan_offs + PWRSR_OFFS); + if (st & BIT(pd->chan_bit)) return true; return false; } -struct rcar_sysc_pd { - struct generic_pm_domain genpd; - struct rcar_sysc_ch ch; - unsigned int flags; - char name[]; -}; - static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d) { return container_of(d, struct rcar_sysc_pd, genpd); @@ -190,7 +188,7 @@ static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd) struct rcar_sysc_pd *pd = to_rcar_pd(genpd); pr_debug("%s: %s\n", __func__, genpd->name); - return rcar_sysc_power(&pd->ch, false); + return rcar_sysc_power(pd, false); } static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) @@ -198,7 +196,7 @@ static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) struct rcar_sysc_pd *pd = to_rcar_pd(genpd); pr_debug("%s: %s\n", __func__, genpd->name); - return rcar_sysc_power(&pd->ch, true); + return rcar_sysc_power(pd, true); } static bool has_cpg_mstp; @@ -252,12 +250,12 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) goto finalize; } - if (!rcar_sysc_power_is_off(&pd->ch)) { + if (!rcar_sysc_power_is_off(pd)) { pr_debug("%s: %s is already powered\n", __func__, genpd->name); goto finalize; } - rcar_sysc_power(&pd->ch, true); + rcar_sysc_power(pd, true); finalize: error = pm_genpd_init(genpd, &simple_qos_governor, false); @@ -412,9 +410,9 @@ static int __init rcar_sysc_pd_init(void) memcpy(pd->name, area->name, n); pd->genpd.name = pd->name; - pd->ch.chan_offs = area->chan_offs; - pd->ch.chan_bit = area->chan_bit; - pd->ch.isr_bit = area->isr_bit; + pd->chan_offs = area->chan_offs; + pd->chan_bit = area->chan_bit; + pd->isr_bit = area->isr_bit; pd->flags = area->flags; error = rcar_sysc_pd_setup(pd); @@ -445,18 +443,6 @@ out_put: } early_initcall(rcar_sysc_pd_init); -void __init rcar_sysc_nullify(struct rcar_sysc_area *areas, - unsigned int num_areas, u8 id) -{ - unsigned int i; - - for (i = 0; i < num_areas; i++) - if (areas[i].isr_bit == id) { - areas[i].name = NULL; - return; - } -} - #ifdef CONFIG_ARCH_R8A7779 static int rcar_sysc_power_cpu(unsigned int idx, bool on) { @@ -473,10 +459,10 @@ static int rcar_sysc_power_cpu(unsigned int idx, bool on) continue; pd = to_rcar_pd(genpd); - if (!(pd->flags & PD_CPU) || pd->ch.chan_bit != idx) + if (!(pd->flags & PD_CPU) || pd->chan_bit != idx) continue; - return rcar_sysc_power(&pd->ch, on); + return rcar_sysc_power(pd, on); } return -ENOENT; diff --git a/drivers/pmdomain/renesas/rcar-sysc.h b/drivers/pmdomain/renesas/rcar-sysc.h index 266c599a0a9b..07ffce310686 100644 --- a/drivers/pmdomain/renesas/rcar-sysc.h +++ b/drivers/pmdomain/renesas/rcar-sysc.h @@ -16,6 +16,7 @@ #define PD_CPU BIT(0) /* Area contains main CPU core */ #define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ #define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ +#define PD_OFF_DELAY BIT(3) /* Area is subject to power-off delay quirk */ #define PD_CPU_CR PD_CPU /* CPU area has CR (R-Car H1) */ #define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR (R-Car Gen2/3) */ @@ -71,12 +72,4 @@ extern const struct rcar_sysc_info r8a77980_sysc_info; extern const struct rcar_sysc_info r8a77990_sysc_info; extern const struct rcar_sysc_info r8a77995_sysc_info; - - /* - * Helpers for fixing up power area tables depending on SoC revision - */ - -extern void rcar_sysc_nullify(struct rcar_sysc_area *areas, - unsigned int num_areas, u8 id); - #endif /* __SOC_RENESAS_RCAR_SYSC_H__ */ 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; diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 9b76b62869d0..cb0f93800138 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -33,6 +33,7 @@ #include <dt-bindings/power/rk3368-power.h> #include <dt-bindings/power/rk3399-power.h> #include <dt-bindings/power/rk3568-power.h> +#include <dt-bindings/power/rockchip,rk3576-power.h> #include <dt-bindings/power/rk3588-power.h> struct rockchip_domain_info { @@ -45,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; @@ -62,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; @@ -144,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, \ @@ -175,6 +197,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, 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 * rockchip_pmu_block(). @@ -299,6 +324,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) { @@ -539,6 +584,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); @@ -555,6 +602,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); } @@ -712,12 +760,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); } } @@ -800,11 +847,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); @@ -812,7 +858,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]; @@ -820,7 +866,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); @@ -828,7 +874,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]; @@ -836,7 +882,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); @@ -846,17 +892,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; @@ -912,14 +953,13 @@ 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(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; } @@ -927,7 +967,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; } } @@ -947,13 +986,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; } @@ -1106,6 +1142,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, 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[] = { [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 +1342,22 @@ 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, + .clk_ungate_offset = 0x140, + + .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, @@ -1360,6 +1434,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .data = (void *)&rk3568_pmu, }, { + .compatible = "rockchip,rk3576-power-controller", + .data = (void *)&rk3576_pmu, + }, + { .compatible = "rockchip,rk3588-power-controller", .data = (void *)&rk3588_pmu, }, diff --git a/drivers/pmdomain/tegra/powergate-bpmp.c b/drivers/pmdomain/tegra/powergate-bpmp.c index 179ed895c279..b0138ca9f851 100644 --- a/drivers/pmdomain/tegra/powergate-bpmp.c +++ b/drivers/pmdomain/tegra/powergate-bpmp.c @@ -305,7 +305,7 @@ static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp) } static struct generic_pm_domain * -tegra_powergate_xlate(struct of_phandle_args *spec, void *data) +tegra_powergate_xlate(const struct of_phandle_args *spec, void *data) { struct generic_pm_domain *domain = ERR_PTR(-ENOENT); struct genpd_onecell_data *genpd = data; diff --git a/drivers/pmdomain/ti/omap_prm.c b/drivers/pmdomain/ti/omap_prm.c index c2feae3a634c..b8ceb3c2b81c 100644 --- a/drivers/pmdomain/ti/omap_prm.c +++ b/drivers/pmdomain/ti/omap_prm.c @@ -695,6 +695,8 @@ static int omap_prm_domain_init(struct device *dev, struct omap_prm *prm) data = prm->data; name = devm_kasprintf(dev, GFP_KERNEL, "prm_%s", data->name); + if (!name) + return -ENOMEM; prmd->dev = dev; prmd->prm = prm; diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c index c091d569ecd5..82df7e44250b 100644 --- a/drivers/pmdomain/ti/ti_sci_pm_domains.c +++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c @@ -12,6 +12,8 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> +#include <linux/pm_qos.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/soc/ti/ti_sci_protocol.h> #include <dt-bindings/soc/ti,sci_pm_domain.h> @@ -51,6 +53,56 @@ struct ti_sci_pm_domain { #define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd) +static inline bool ti_sci_pd_is_valid_constraint(s32 val) +{ + return val != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; +} + +#ifdef CONFIG_PM_SLEEP +static void ti_sci_pd_set_lat_constraint(struct device *dev, s32 val) +{ + struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain); + struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(genpd); + const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; + u16 val_ms; + int ret; + + /* PM QoS latency unit is usecs, TI SCI uses msecs */ + val_ms = val / USEC_PER_MSEC; + ret = ti_sci->ops.pm_ops.set_latency_constraint(ti_sci, val_ms, TISCI_MSG_CONSTRAINT_SET); + if (ret) + dev_err(dev, "ti_sci_pd: set latency constraint failed: ret=%d\n", + ret); + else + dev_dbg(dev, "ti_sci_pd: ID:%d set latency constraint %d\n", + pd->idx, val); +} +#endif + +static inline void ti_sci_pd_set_wkup_constraint(struct device *dev) +{ + struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain); + struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(genpd); + const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; + int ret; + + if (device_may_wakeup(dev)) { + /* + * If device can wakeup using IO daisy chain wakeups, + * we do not want to set a constraint. + */ + if (dev->power.wakeirq) { + dev_dbg(dev, "%s: has wake IRQ, not setting constraints\n", __func__); + return; + } + + ret = ti_sci->ops.pm_ops.set_device_constraint(ti_sci, pd->idx, + TISCI_MSG_CONSTRAINT_SET); + if (!ret) + dev_dbg(dev, "ti_sci_pd: ID:%d set device constraint.\n", pd->idx); + } +} + /* * ti_sci_pd_power_off(): genpd power down hook * @domain: pointer to the powerdomain to power off @@ -79,13 +131,35 @@ static int ti_sci_pd_power_on(struct generic_pm_domain *domain) return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx); } +#ifdef CONFIG_PM_SLEEP +static int ti_sci_pd_suspend(struct device *dev) +{ + int ret; + s32 val; + + ret = pm_generic_suspend(dev); + if (ret) + return ret; + + val = dev_pm_qos_read_value(dev, DEV_PM_QOS_RESUME_LATENCY); + if (ti_sci_pd_is_valid_constraint(val)) + ti_sci_pd_set_lat_constraint(dev, val); + + ti_sci_pd_set_wkup_constraint(dev); + + return 0; +} +#else +#define ti_sci_pd_suspend NULL +#endif + /* * ti_sci_pd_xlate(): translation service for TI SCI genpds * @genpdspec: DT identification data for the genpd * @data: genpd core data for all the powerdomains on the device */ static struct generic_pm_domain *ti_sci_pd_xlate( - struct of_phandle_args *genpdspec, + const struct of_phandle_args *genpdspec, void *data) { struct genpd_onecell_data *genpd_data = data; @@ -114,14 +188,25 @@ static const struct of_device_id ti_sci_pm_domain_matches[] = { }; MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches); +static bool ti_sci_pm_idx_exists(struct ti_sci_genpd_provider *pd_provider, u32 idx) +{ + struct ti_sci_pm_domain *pd; + + list_for_each_entry(pd, &pd_provider->pd_list, node) { + if (pd->idx == idx) + return true; + } + + return false; +} + static int ti_sci_pm_domain_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ti_sci_genpd_provider *pd_provider; struct ti_sci_pm_domain *pd; - struct device_node *np; + struct device_node *np __free(device_node) = NULL; struct of_phandle_args args; - int ret; u32 max_id = 0; int index; @@ -141,40 +226,51 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) for_each_node_with_property(np, "power-domains") { index = 0; - while (1) { - ret = of_parse_phandle_with_args(np, "power-domains", - "#power-domain-cells", - index, &args); - if (ret) - break; + while (!of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", + index, &args)) { if (args.args_count >= 1 && args.np == dev->of_node) { - if (args.args[0] > max_id) + of_node_put(args.np); + if (args.args[0] > max_id) { max_id = args.args[0]; + } else { + if (ti_sci_pm_idx_exists(pd_provider, args.args[0])) { + index++; + continue; + } + } pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) { - of_node_put(np); + if (!pd) return -ENOMEM; - } pd->pd.name = devm_kasprintf(dev, GFP_KERNEL, "pd:%d", args.args[0]); - if (!pd->pd.name) { - of_node_put(np); + if (!pd->pd.name) return -ENOMEM; - } pd->pd.power_off = ti_sci_pd_power_off; pd->pd.power_on = ti_sci_pd_power_on; + pd->pd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; pd->idx = args.args[0]; pd->parent = pd_provider; + /* + * If SCI constraint functions are present, then firmware + * supports the constraints API. + */ + if (pd_provider->ti_sci->ops.pm_ops.set_device_constraint && + pd_provider->ti_sci->ops.pm_ops.set_latency_constraint) + pd->pd.domain.ops.suspend = ti_sci_pd_suspend; pm_genpd_init(&pd->pd, NULL, true); list_add(&pd->node, &pd_provider->pd_list); + } else { + of_node_put(args.np); } + index++; } } diff --git a/drivers/pmdomain/xilinx/zynqmp-pm-domains.c b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c index 6fd514286d82..d579220a4500 100644 --- a/drivers/pmdomain/xilinx/zynqmp-pm-domains.c +++ b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c @@ -210,7 +210,7 @@ static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, } static struct generic_pm_domain *zynqmp_gpd_xlate - (struct of_phandle_args *genpdspec, void *data) + (const struct of_phandle_args *genpdspec, void *data) { struct genpd_onecell_data *genpd_data = data; unsigned int i, idx = genpdspec->args[0]; @@ -313,7 +313,7 @@ static struct platform_driver zynqmp_power_domain_driver = { .sync_state = zynqmp_gpd_sync_state, }, .probe = zynqmp_gpd_probe, - .remove_new = zynqmp_gpd_remove, + .remove = zynqmp_gpd_remove, }; module_platform_driver(zynqmp_power_domain_driver); |