diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml index 12b71688dd34..e608a0c75961 100644 --- a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml +++ b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml @@ -22,6 +22,7 @@ properties: - amlogic,meson-s4-pwrc - amlogic,a4-pwrc - amlogic,a5-pwrc + - amlogic,a9-pwrc - amlogic,c3-pwrc - amlogic,t7-pwrc - amlogic,s6-pwrc diff --git a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml index 07f046277f8a..d03e4a925163 100644 --- a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml +++ b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml @@ -25,6 +25,7 @@ properties: enum: - mediatek,mt6735-power-controller - mediatek,mt6795-power-controller + - mediatek,mt6858-power-controller - mediatek,mt6893-power-controller - mediatek,mt8167-power-controller - mediatek,mt8173-power-controller @@ -34,6 +35,7 @@ properties: - mediatek,mt8189-power-controller - mediatek,mt8192-power-controller - mediatek,mt8195-power-controller + - mediatek,mt8196-hfrp-power-controller - mediatek,mt8196-hwv-hfrp-power-controller - mediatek,mt8196-hwv-scp-power-controller - mediatek,mt8196-power-controller @@ -55,7 +57,7 @@ properties: faults while enabling or disabling a power domain. For example, this may hold phandles to INFRACFG and SMI. minItems: 1 - maxItems: 3 + maxItems: 6 patternProperties: "^power-domain@[0-9a-f]+$": @@ -102,6 +104,7 @@ $defs: description: | Power domain index. Valid values are defined in: "include/dt-bindings/power/mt6795-power.h" - for MT8167 type power domain. + "include/dt-bindings/power/mediatek,mt6858-power.h" - for MT6858 type power domain. "include/dt-bindings/power/mediatek,mt6893-power.h" - for MT6893 type power domain. "include/dt-bindings/power/mt8167-power.h" - for MT8167 type power domain. "include/dt-bindings/power/mt8173-power.h" - for MT8173 type power domain. @@ -155,6 +158,23 @@ required: - compatible allOf: + - if: + properties: + compatible: + contains: + enum: + - mediatek,mt6858-power-controller + then: + properties: + access-controllers: + items: + - description: handle to INFRACFG register block + - description: handle to IMG_SUB0 register block + - description: handle to CAM_SUB1 register block + - description: handle to CAM_SUB0 register block + - description: handle to IPE_SUB0 register block + - description: handle to VLPCFG register block + - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 0744a867c79e..0a45e1f55062 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -75,6 +75,10 @@ properties: - enum: - qcom,msm8937-rpmpd - const: qcom,msm8917-rpmpd + - items: + - enum: + - qcom,maili-rpmhpd + - const: qcom,hawi-rpmhpd '#power-domain-cells': const: 1 diff --git a/Documentation/devicetree/bindings/power/renesas,r8a78000-mdlc.yaml b/Documentation/devicetree/bindings/power/renesas,r8a78000-mdlc.yaml new file mode 100644 index 000000000000..1dba1e1b1c5b --- /dev/null +++ b/Documentation/devicetree/bindings/power/renesas,r8a78000-mdlc.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/renesas,r8a78000-mdlc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R-Car X5H Module Controller + +maintainers: + - Geert Uytterhoeven + +description: + Each instance of the R-Car X5H Module Controller (MODULE CONTROL) provides + Power Gating for up to 64 Power Domains, and Module Standby and Reset for up + to 256 modules in the Power Domain of each Module hierarchy. + +properties: + compatible: + const: renesas,r8a78000-mdlc + + reg: + maxItems: 1 + + '#power-domain-cells': + description: | + - The first power domain specifier cell is the power domain part, and + must be either the Module Power Domain Gating (MPDG) register index + (0x00-0x3f) from the datasheet, for power domains which are backed by + MPDG bits and which can be controlled in that manner, or a Power + Domain number, as defined in + , for power domains which + are always on, and for which there are no MPDG bits to control them. + + - The second power domain specifier cell is the module standby part, and + must be the module number (0x00-0xff), composed of the Module System + Reset (MSRES) register index in the high nibble, and the Module Reset + Destination bitfield index in the low nibble. + const: 2 + + '#reset-cells': + description: + The single reset specifier cell must be the module number (0x00-0xff), + and thus identical to the second power domain specifier cell. + const: 1 + +required: + - compatible + - reg + - '#power-domain-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + system-controller@c3060000 { + compatible = "renesas,r8a78000-mdlc"; + reg = <0xc3060000 0x1000>; + #power-domain-cells = <2>; + #reset-cells = <1>; + }; diff --git a/drivers/clk/imx/clk-imx8qxp.c b/drivers/clk/imx/clk-imx8qxp.c index c781425a005e..17302ee628b7 100644 --- a/drivers/clk/imx/clk-imx8qxp.c +++ b/drivers/clk/imx/clk-imx8qxp.c @@ -373,3 +373,4 @@ module_exit(imx8qxp_clk_exit); MODULE_AUTHOR("Aisheng Dong "); MODULE_DESCRIPTION("NXP i.MX8QXP clock driver"); MODULE_LICENSE("GPL v2"); +MODULE_SOFTDEP("pre: scu_pd"); diff --git a/drivers/of/base.c b/drivers/of/base.c index 6e7a42dedad3..a25c95800719 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -39,6 +39,7 @@ struct device_node *of_chosen; EXPORT_SYMBOL(of_chosen); struct device_node *of_aliases; struct device_node *of_stdout; +EXPORT_SYMBOL_GPL(of_stdout); static const char *of_stdout_options; struct kset *of_kset; diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c index 1d2f371d2d7f..133549956efa 100644 --- a/drivers/pmdomain/amlogic/meson-secure-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -175,6 +176,65 @@ static const struct meson_secure_pwrc_domain_desc a5_pwrc_domains[] = { SEC_PD(A5_DSPA, 0), }; +static const struct meson_secure_pwrc_domain_desc a9_pwrc_domains[] = { + SEC_PD(A9_DSPA, 0), + SEC_PD(A9_U3HSG_U3, 0), + SEC_PD(A9_DP, 0), + SEC_PD(A9_DOS_HCODEC, 0), + SEC_PD(A9_TAHOE, 0), + SEC_PD(A9_DOS_HEVC, 0), + SEC_PD(A9_U2H, 0), + SEC_PD(A9_U3DRD_B, 0), + SEC_PD(A9_VPU_HDMI, 0), + SEC_PD(A9_U2DRD, 0), + SEC_PD(A9_U3DRD_A, 0), + SEC_PD(A9_SD_EMMC_C, 0), + SEC_PD(A9_GE2D, 0), + SEC_PD(A9_AMFC, 0), + SEC_PD(A9_EDPTX, 0), + SEC_PD(A9_OPP, 0), + SEC_PD(A9_VICP, 0), + SEC_PD(A9_SD_EMMC_A, 0), + SEC_PD(A9_SD_EMMC_B, 0), + SEC_PD(A9_ETH, 0), + SEC_PD(A9_PCIE_A, 0), + SEC_PD(A9_PCIE_B, 0), + SEC_PD(A9_NNA_4T, 0), + SEC_PD(A9_HDMIRX, 0), + SEC_PD(A9_CVE, 0), + SEC_PD(A9_ISP, 0), + SEC_PD(A9_ETH_1G, 0), + SEC_PD(A9_U3HSG_HSG, 0), + SEC_PD(A9_U3DPPHY_U3, 0), + SEC_PD(A9_U3DPPHY_DP, 0), + SEC_PD(A9_PCIE3PHY, 0), + SEC_PD(A9_U3HSG_PCIE2, 0), + SEC_PD(A9_MALI_TOP, 0), + SEC_PD(A9_AO_SED, 0), + SEC_PD(A9_AO_IR, 0), + /* UART_B is for console log, and should be always on */ + SEC_PD(A9_AO_UART_B, GENPD_FLAG_ALWAYS_ON), + SEC_PD(A9_AO_UART_C, 0), + SEC_PD(A9_AO_UART_D, 0), + SEC_PD(A9_AO_SPISG, 0), + SEC_PD(A9_AO_UART_E, 0), + SEC_PD(A9_AO_CEC, 0), + /* SRAMA is used as ATF runtime memory, and should be always on */ + SEC_PD(A9_EE_SRAMA, GENPD_FLAG_ALWAYS_ON), + SEC_PD(A9_AUDIO, 0), + /* DMC0 is for DDR PHY ana/dig and DMC, and should be always on */ + SEC_PD(A9_DMC0, GENPD_FLAG_ALWAYS_ON), + /*GIC needs to be used consistently, and should be always on */ + SEC_PD(A9_GIC, GENPD_FLAG_ALWAYS_ON), + /* DDRPHY is for DDR Work, and should be always on */ + SEC_PD(A9_DDRPHY, GENPD_FLAG_ALWAYS_ON), + SEC_PD(A9_AUCPU, 0), + SEC_PD(A9_DSI0, 0), + SEC_PD(A9_DSI1, 0), + SEC_PD(A9_CAN0, 0), + SEC_PD(A9_CAN1, 0), +}; + static const struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { SEC_PD(C3_NNA, 0), SEC_PD(C3_AUDIO, 0), @@ -425,6 +485,11 @@ static const struct meson_secure_pwrc_domain_data amlogic_secure_a5_pwrc_data = .count = ARRAY_SIZE(a5_pwrc_domains), }; +static const struct meson_secure_pwrc_domain_data amlogic_secure_a9_pwrc_data = { + .domains = a9_pwrc_domains, + .count = ARRAY_SIZE(a9_pwrc_domains), +}; + static const struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = { .domains = c3_pwrc_domains, .count = ARRAY_SIZE(c3_pwrc_domains), @@ -468,6 +533,10 @@ static const struct of_device_id meson_secure_pwrc_match_table[] = { .compatible = "amlogic,a5-pwrc", .data = &amlogic_secure_a5_pwrc_data, }, + { + .compatible = "amlogic,a9-pwrc", + .data = &amlogic_secure_a9_pwrc_data, + }, { .compatible = "amlogic,c3-pwrc", .data = &amlogic_secure_c3_pwrc_data, diff --git a/drivers/pmdomain/arm/Kconfig b/drivers/pmdomain/arm/Kconfig index afed10d382ad..ba0b190a63d6 100644 --- a/drivers/pmdomain/arm/Kconfig +++ b/drivers/pmdomain/arm/Kconfig @@ -21,7 +21,7 @@ config ARM_SCMI_POWER_DOMAIN enabled or disabled via the SCP firmware This driver can also be built as a module. If so, the module - will be called scmi_pm_domain. Note this may needed early in boot + will be called scmi_pm_domain. Note this may be needed early in boot before rootfs may be available. config ARM_SCPI_POWER_DOMAIN diff --git a/drivers/pmdomain/bcm/bcm2835-power.c b/drivers/pmdomain/bcm/bcm2835-power.c index b76d74e3849b..fad3113093d5 100644 --- a/drivers/pmdomain/bcm/bcm2835-power.c +++ b/drivers/pmdomain/bcm/bcm2835-power.c @@ -175,7 +175,7 @@ static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable writel(PM_PASSWORD | val, base + reg); if (readl_poll_timeout_atomic(base + reg, val, - !!(val & ASB_ACK) != enable, 0, 5)) + !!(val & ASB_ACK) != enable, 0, 100)) return -ETIMEDOUT; return 0; @@ -677,7 +677,12 @@ static int bcm2835_power_probe(struct platform_device *pdev) if (ret) goto fail; - of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate); + ret = of_genpd_add_provider_onecell(dev->parent->of_node, + &power->pd_xlate); + if (ret) { + dev_err_probe(dev, ret, "failed to add genpd provider\n"); + goto fail; + } dev_info(dev, "Broadcom BCM2835 power domains driver"); return 0; diff --git a/drivers/pmdomain/imx/Kconfig b/drivers/pmdomain/imx/Kconfig index 00203615c65e..33e1567e5d17 100644 --- a/drivers/pmdomain/imx/Kconfig +++ b/drivers/pmdomain/imx/Kconfig @@ -10,18 +10,21 @@ config IMX_GPCV2_PM_DOMAINS default y if SOC_IMX7D config IMX8M_BLK_CTRL - bool - default SOC_IMX8M && IMX_GPCV2_PM_DOMAINS + tristate "i.MX8M BLK CTRL driver" + depends on SOC_IMX8M + depends on IMX_GPCV2_PM_DOMAINS depends on PM_GENERIC_DOMAINS depends on COMMON_CLK + default y config IMX9_BLK_CTRL - bool - default SOC_IMX9 && IMX_GPCV2_PM_DOMAINS + tristate "i.MX93 BLK CTRL driver" + depends on SOC_IMX9 depends on PM_GENERIC_DOMAINS + default y config IMX_SCU_PD - bool "IMX SCU Power Domain driver" + tristate "IMX SCU Power Domain driver" depends on IMX_SCU help The System Controller Firmware (SCFW) based power domain driver. diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index 99d100e1d923..479789009c7f 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -953,4 +953,5 @@ static struct platform_driver imx8m_blk_ctrl_driver = { }, }; module_platform_driver(imx8m_blk_ctrl_driver); +MODULE_DESCRIPTION("NXP i.MX8M power domain driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c index 3f5b9499d30a..d255c20f58e2 100644 --- a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c @@ -890,4 +890,5 @@ static struct platform_driver imx8mp_blk_ctrl_driver = { }, }; module_platform_driver(imx8mp_blk_ctrl_driver); +MODULE_DESCRIPTION("NXP i.MX8MP power domain driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/imx/scu-pd.c b/drivers/pmdomain/imx/scu-pd.c index 3ec33667a308..e3f9e741daf7 100644 --- a/drivers/pmdomain/imx/scu-pd.c +++ b/drivers/pmdomain/imx/scu-pd.c @@ -531,6 +531,7 @@ static const struct of_device_id imx_sc_pd_match[] = { { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_sc_pd_match); static struct platform_driver imx_sc_pd_driver = { .driver = { @@ -540,7 +541,12 @@ static struct platform_driver imx_sc_pd_driver = { }, .probe = imx_sc_pd_probe, }; -builtin_platform_driver(imx_sc_pd_driver); + +static int __init imx_sc_pd_driver_init(void) +{ + return platform_driver_register(&imx_sc_pd_driver); +} +subsys_initcall(imx_sc_pd_driver_init); MODULE_AUTHOR("Dong Aisheng "); MODULE_DESCRIPTION("IMX SCU Power Domain driver"); diff --git a/drivers/pmdomain/mediatek/mt6858-pm-domains.h b/drivers/pmdomain/mediatek/mt6858-pm-domains.h new file mode 100644 index 000000000000..177b2e86ab1e --- /dev/null +++ b/drivers/pmdomain/mediatek/mt6858-pm-domains.h @@ -0,0 +1,466 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 MediaTek Inc. + * KY Liu + * Copyright (c) 2026 Jolla Mobile Ltd + * Nikolai Burov + */ + +#ifndef __SOC_MEDIATEK_MT6858_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT6858_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* TOP_AXI registers */ +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET 0x0c14 +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR 0x0c18 +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY 0x0c1c + +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_DIS0 (BIT(0) | BIT(1) | BIT(18)) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IPE BIT(2) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IMG1 BIT(3) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VEN0 BIT(12) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VDE0 BIT(20) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CAM_MAIN (BIT(30) | BIT(31)) + +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET 0x0c24 +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR 0x0c28 +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY 0x0c2c + +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IMG1 BIT(7) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IPE BIT(8) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CAM_MAIN (BIT(9) | BIT(10)) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VEN0 BIT(12) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VDE0 BIT(13) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA (GENMASK(3, 1) | BIT(6)) +#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA_2ND (BIT(0) | BIT(5) | GENMASK(15, 7)) + +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET 0x0c44 +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR 0x0c48 +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY 0x0c4c + +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CONN BIT(8) +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_MM_INFRA BIT(16) + +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET 0x0c54 +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR 0x0c58 +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY 0x0c5c + +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_MM_INFRA BIT(11) +#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CONN BIT(12) + +#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_SET 0x0c64 +#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_CLR 0x0c68 +#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_RDY 0x0c6c + +#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_MM_INFRA (BIT(20) | BIT(21)) + +#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET 0x0c84 +#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR 0x0c88 +#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY 0x0c8c + +#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_AUDIO BIT(6) +#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SSUSB BIT(7) + +#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET 0x0c94 +#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR 0x0c98 +#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY 0x0c9c + +#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN BIT(1) +#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN_2ND BIT(0) + +/* {IMG,IPE,CAM}_SUBx registers */ +#define MT6858_SUBx_PROT_EN_SET 0x03c4 +#define MT6858_SUBx_PROT_EN_CLR 0x03c8 +#define MT6858_SUBx_PROT_EN_STA 0x03cc + +#define MT6858_IMG_SUB0_PROT_EN_SMI_ISP_IMG1 (BIT(0) | BIT(1)) + +#define MT6858_IPE_SUB0_PROT_EN_SMI_ISP_IPE (BIT(0) | BIT(1)) + +#define MT6858_CAM_SUB0_PROT_EN_SMI_CAM_MAIN BIT(0) +#define MT6858_CAM_SUB0_PROT_EN_SMI_CAM_SUBB BIT(1) + +#define MT6858_CAM_SUB1_PROT_EN_SMI_CAM_MAIN BIT(0) +#define MT6858_CAM_SUB1_PROT_EN_SMI_CAM_SUBA BIT(1) + +/* VLP_AXI registers */ +#define MT6858_VLP_AXI_PROT_EN_SET 0x0214 +#define MT6858_VLP_AXI_PROT_EN_CLR 0x0218 +#define MT6858_VLP_AXI_PROT_EN_STA 0x021c + +#define MT6858_VLP_AXI_PROT_EN_MM_PROC BIT(8) +#define MT6858_VLP_AXI_PROT_EN_MM_PROC_2ND (BIT(9) | BIT(10)) + +/* PWR_CON registers */ +#define MT6858_PWR_ACK BIT(30) +#define MT6858_PWR_ACK_2ND BIT(31) + +static enum scpsys_bus_prot_block scpsys_bus_prot_blocks_mt6858[] = { + BUS_PROT_BLOCK_INFRA, + BUS_PROT_BLOCK_IMG_SUB0, + BUS_PROT_BLOCK_CAM_SUB1, + BUS_PROT_BLOCK_CAM_SUB0, + BUS_PROT_BLOCK_IPE_SUB0, + BUS_PROT_BLOCK_VLP, +}; + +static const struct scpsys_domain_data scpsys_domain_data_mt6858[] = { + [MT6858_POWER_DOMAIN_MD] = { + .name = "md", + /* + * Note: the PWR_ACK_2ND bit is not used for the modem domain. + * Skip it and fall back to checking the 1st bit twice. + */ + .sta_mask = MT6858_PWR_ACK, + .ctl_offs = 0xe00, + .pwr_sta_offs = 0xe00, + .pwr_sta2nd_offs = 0xe00, + .ext_buck_iso_offs = 0xf20, + .ext_buck_iso_mask = GENMASK(1, 0), + .caps = MTK_SCPD_MODEM_SECURE_PWRSEQ | MTK_SCPD_EXT_BUCK_ISO | + MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe04, + .pwr_sta_offs = 0xe04, + .pwr_sta2nd_offs = 0xe04, + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN, + MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CONN, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN_2ND, + MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CONN, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe18, + .pwr_sta_offs = 0xe18, + .pwr_sta2nd_offs = 0xe18, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_AUDIO, + MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY), + }, + }, + [MT6858_POWER_DOMAIN_ISP_IMG1] = { + .name = "isp-img1", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe28, + .pwr_sta_offs = 0xe28, + .pwr_sta2nd_offs = 0xe28, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IMG1, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IMG1, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY), + BUS_PROT_WR_IGN(IMG_SUB0, + MT6858_IMG_SUB0_PROT_EN_SMI_ISP_IMG1, + MT6858_SUBx_PROT_EN_SET, + MT6858_SUBx_PROT_EN_CLR, + MT6858_SUBx_PROT_EN_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_ISP_IMG2] = { + .name = "isp-img2", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe2c, + .pwr_sta_offs = 0xe2c, + .pwr_sta2nd_offs = 0xe2c, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_ISP_IPE] = { + .name = "isp-ipe", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe30, + .pwr_sta_offs = 0xe30, + .pwr_sta2nd_offs = 0xe30, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IPE, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IPE, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY), + BUS_PROT_WR_IGN(IPE_SUB0, + MT6858_IPE_SUB0_PROT_EN_SMI_ISP_IPE, + MT6858_SUBx_PROT_EN_SET, + MT6858_SUBx_PROT_EN_CLR, + MT6858_SUBx_PROT_EN_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_VDE0] = { + .name = "vde0", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe34, + .pwr_sta_offs = 0xe34, + .pwr_sta2nd_offs = 0xe34, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VDE0, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VDE0, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY), + }, + }, + [MT6858_POWER_DOMAIN_VEN0] = { + .name = "ven0", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe3c, + .pwr_sta_offs = 0xe3c, + .pwr_sta2nd_offs = 0xe3c, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VEN0, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VEN0, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY), + }, + }, + [MT6858_POWER_DOMAIN_CAM_MAIN] = { + .name = "cam-main", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe44, + .pwr_sta_offs = 0xe44, + .pwr_sta2nd_offs = 0xe44, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CAM_MAIN, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CAM_MAIN, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY), + BUS_PROT_WR_IGN(CAM_SUB0, + MT6858_CAM_SUB0_PROT_EN_SMI_CAM_MAIN, + MT6858_SUBx_PROT_EN_SET, + MT6858_SUBx_PROT_EN_CLR, + MT6858_SUBx_PROT_EN_STA), + BUS_PROT_WR_IGN(CAM_SUB1, + MT6858_CAM_SUB1_PROT_EN_SMI_CAM_MAIN, + MT6858_SUBx_PROT_EN_SET, + MT6858_SUBx_PROT_EN_CLR, + MT6858_SUBx_PROT_EN_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_CAM_SUBA] = { + .name = "cam-suba", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe4c, + .pwr_sta_offs = 0xe4c, + .pwr_sta2nd_offs = 0xe4c, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(CAM_SUB1, + MT6858_CAM_SUB1_PROT_EN_SMI_CAM_SUBA, + MT6858_SUBx_PROT_EN_SET, + MT6858_SUBx_PROT_EN_CLR, + MT6858_SUBx_PROT_EN_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_CAM_SUBB] = { + .name = "cam-subb", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe50, + .pwr_sta_offs = 0xe50, + .pwr_sta2nd_offs = 0xe50, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(CAM_SUB0, + MT6858_CAM_SUB0_PROT_EN_SMI_CAM_SUBB, + MT6858_SUBx_PROT_EN_SET, + MT6858_SUBx_PROT_EN_CLR, + MT6858_SUBx_PROT_EN_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_DIS0] = { + .name = "dis0", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe6c, + .pwr_sta_offs = 0xe6c, + .pwr_sta2nd_offs = 0xe6c, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_DIS0, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY), + }, + }, + [MT6858_POWER_DOMAIN_MM_INFRA] = { + .name = "mm-infra", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe74, + .pwr_sta_offs = 0xe74, + .pwr_sta2nd_offs = 0xe74, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_MM_INFRA, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA_2ND, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR, + MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_MM_INFRA, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY), + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_MM_INFRA, + MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_RDY), + }, + }, + [MT6858_POWER_DOMAIN_MM_PROC_DORMANT] = { + .name = "mm-proc-dormant", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe78, + .pwr_sta_offs = 0xe78, + .pwr_sta2nd_offs = 0xe78, + .sram_pdn_bits = BIT(9), + .sram_pdn_ack_bits = BIT(13), + .bp_cfg = { + BUS_PROT_WR_IGN(VLP, + MT6858_VLP_AXI_PROT_EN_MM_PROC, + MT6858_VLP_AXI_PROT_EN_SET, + MT6858_VLP_AXI_PROT_EN_CLR, + MT6858_VLP_AXI_PROT_EN_STA), + BUS_PROT_WR_IGN(VLP, + MT6858_VLP_AXI_PROT_EN_MM_PROC_2ND, + MT6858_VLP_AXI_PROT_EN_SET, + MT6858_VLP_AXI_PROT_EN_CLR, + MT6858_VLP_AXI_PROT_EN_STA), + }, + .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_SRAM_PDN_INVERTED, + }, + [MT6858_POWER_DOMAIN_CSI_RX] = { + .name = "csi-rx", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xe98, + .pwr_sta_offs = 0xe98, + .pwr_sta2nd_offs = 0xe98, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6858_POWER_DOMAIN_SSUSB] = { + .name = "ssusb", + .sta_mask = MT6858_PWR_ACK, + .sta2nd_mask = MT6858_PWR_ACK_2ND, + .ctl_offs = 0xea4, + .pwr_sta_offs = 0xea4, + .pwr_sta2nd_offs = 0xea4, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR_IGN(INFRA, + MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SSUSB, + MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET, + MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR, + MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY), + }, + }, +}; + +static const struct scpsys_soc_data mt6858_scpsys_data = { + .domains_data = scpsys_domain_data_mt6858, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt6858), + .bus_prot_blocks = scpsys_bus_prot_blocks_mt6858, + .num_bus_prot_blocks = ARRAY_SIZE(scpsys_bus_prot_blocks_mt6858), + .type = SCPSYS_MTCMOS_TYPE_DIRECT_CTL, +}; + +#endif /* __SOC_MEDIATEK_MT6858_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mt8196-pm-domains.h b/drivers/pmdomain/mediatek/mt8196-pm-domains.h index 2e4b28720659..d704c9fa9337 100644 --- a/drivers/pmdomain/mediatek/mt8196-pm-domains.h +++ b/drivers/pmdomain/mediatek/mt8196-pm-domains.h @@ -602,6 +602,27 @@ static const struct scpsys_hwv_domain_data hfrpsys_hwv_domain_data_mt8196[] = { }, }; +static const struct scpsys_domain_data hfrpsys_domain_data_mt8196[] = { + [MT8196_POWER_DOMAIN_EDPTX] = { + .name = "edp-tx", + .sta_mask = MT8196_PWR_ACK, + .sta2nd_mask = MT8196_PWR_ACK_2ND, + .ctl_offs = 0x74, + .pwr_sta_offs = 0x74, + .pwr_sta2nd_offs = 0x74, + .caps = MTK_SCPD_SIMPLE_PWRSEQ, + }, + [MT8196_POWER_DOMAIN_DPTX] = { + .name = "dp-tx", + .sta_mask = MT8196_PWR_ACK, + .sta2nd_mask = MT8196_PWR_ACK_2ND, + .ctl_offs = 0x78, + .pwr_sta_offs = 0x78, + .pwr_sta2nd_offs = 0x78, + .caps = MTK_SCPD_SIMPLE_PWRSEQ, + }, +}; + static const struct scpsys_soc_data mt8196_scpsys_data = { .domains_data = scpsys_domain_data_mt8196, .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8196), @@ -616,6 +637,12 @@ static const struct scpsys_soc_data mt8196_scpsys_hwv_data = { .type = SCPSYS_MTCMOS_TYPE_HW_VOTER, }; +static const struct scpsys_soc_data mt8196_hfrpsys_data = { + .domains_data = hfrpsys_domain_data_mt8196, + .num_domains = ARRAY_SIZE(hfrpsys_domain_data_mt8196), + .type = SCPSYS_MTCMOS_TYPE_DIRECT_CTL, +}; + static const struct scpsys_soc_data mt8196_hfrpsys_hwv_data = { .hwv_domains_data = hfrpsys_hwv_domain_data_mt8196, .num_hwv_domains = ARRAY_SIZE(hfrpsys_hwv_domain_data_mt8196), diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index 6089c8cf5bbc..f0fa79e01f9c 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -20,6 +20,7 @@ #include "mt6735-pm-domains.h" #include "mt6795-pm-domains.h" +#include "mt6858-pm-domains.h" #include "mt6893-pm-domains.h" #include "mt8167-pm-domains.h" #include "mt8173-pm-domains.h" @@ -57,6 +58,10 @@ #define MTK_SIP_KERNEL_HWCCF_CONTROL MTK_SIP_SMC_CMD(0x540) +/* Secure MTCMOS commands for modem subsystem */ +#define MTK_MD_MTCMOS_ENABLE 18 +#define MTK_MD_MTCMOS_DISABLE 19 + struct scpsys_domain { struct generic_pm_domain genpd; const struct scpsys_domain_data *data; @@ -547,9 +552,11 @@ static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd) return 0; } -static void scpsys_ctl_pwrseq_off(struct scpsys_domain *pd) +static int scpsys_ctl_pwrseq_off(struct scpsys_domain *pd) { struct scpsys *scpsys = pd->scpsys; + bool tmp; + int ret; switch (pd->data->rtff_type) { case SCPSYS_RTFF_TYPE_GENERIC: @@ -581,6 +588,41 @@ static void scpsys_ctl_pwrseq_off(struct scpsys_domain *pd) regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_2ND_BIT); regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_BIT); + + /* wait until PWR_ACK = 0 */ + ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, !tmp, MTK_POLL_DELAY_US, + MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + + return 0; +} + +static int scpsys_simple_pwrseq_on(struct scpsys_domain *pd) +{ + struct scpsys *scpsys = pd->scpsys; + + /* Enable subsys clock input and trigger power domain reset state */ + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT); + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); + + /* Wait for the hardware to stabilize */ + udelay(1); + + /* Get out of reset: set power on */ + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); + + return 0; +} + +static int scpsys_simple_pwrseq_off(struct scpsys_domain *pd) +{ + struct scpsys *scpsys = pd->scpsys; + + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT); + + return 0; } static int scpsys_modem_pwrseq_on(struct scpsys_domain *pd) @@ -603,14 +645,52 @@ static int scpsys_modem_pwrseq_on(struct scpsys_domain *pd) return 0; } -static void scpsys_modem_pwrseq_off(struct scpsys_domain *pd) +static int scpsys_modem_pwrseq_off(struct scpsys_domain *pd) { struct scpsys *scpsys = pd->scpsys; + bool tmp; + int ret; regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_BIT); if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SKIP_RESET_B)) regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); + + /* wait until PWR_ACK = 0 */ + ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, !tmp, MTK_POLL_DELAY_US, + MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + + return 0; +} + +static bool scpsys_modem_sec_poll(unsigned long cmd) +{ + struct arm_smccc_res res; + + arm_smccc_smc(MTK_SIP_KERNEL_CCCI_CONTROL, cmd, 1, 0, 0, 0, 0, 0, &res); + + return res.a0 == 0; +} + +static int scpsys_modem_sec_power_on(bool on) +{ + struct arm_smccc_res res; + unsigned long cmd = on ? MTK_MD_MTCMOS_ENABLE : MTK_MD_MTCMOS_DISABLE; + bool tmp; + int ret; + + arm_smccc_smc(MTK_SIP_KERNEL_CCCI_CONTROL, cmd, 0, 0, 0, 0, 0, 0, &res); + if (res.a0 == 0) + return 0; + + ret = readx_poll_timeout(scpsys_modem_sec_poll, cmd, tmp, tmp, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + + return 0; } static int scpsys_power_on(struct generic_pm_domain *genpd) @@ -631,8 +711,12 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) regmap_clear_bits(scpsys->base, pd->data->ext_buck_iso_offs, pd->data->ext_buck_iso_mask); - if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_PWRSEQ)) + if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) + ret = scpsys_modem_sec_power_on(true); + else if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_PWRSEQ)) ret = scpsys_modem_pwrseq_on(pd); + else if (MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) + ret = scpsys_simple_pwrseq_on(pd); else ret = scpsys_ctl_pwrseq_on(pd); @@ -660,9 +744,12 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) goto err_pwr_ack; } - ret = scpsys_sram_enable(pd); - if (ret < 0) - goto err_disable_subsys_clks; + if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ) && + !MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) { + ret = scpsys_sram_enable(pd); + if (ret < 0) + goto err_disable_subsys_clks; + } ret = scpsys_bus_protect_disable(pd, 0); if (ret < 0) @@ -680,7 +767,9 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) err_enable_bus_protect: scpsys_bus_protect_enable(pd, 0); err_disable_sram: - scpsys_sram_disable(pd); + if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ) && + !MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) + scpsys_sram_disable(pd); err_disable_subsys_clks: if (!MTK_SCPD_CAPS(pd, MTK_SCPD_STRICT_BUS_PROTECTION)) clk_bulk_disable_unprepare(pd->num_subsys_clks, @@ -695,16 +784,21 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) static int scpsys_power_off_internal(struct scpsys_domain *pd) { struct scpsys *scpsys = pd->scpsys; - bool tmp; int ret; ret = scpsys_bus_protect_enable(pd, 0); if (ret < 0) return ret; - ret = scpsys_sram_disable(pd); - if (ret < 0) - return ret; + if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) { + ret = scpsys_modem_sec_power_on(false); + if (ret) + return ret; + } else if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) { + ret = scpsys_sram_disable(pd); + if (ret < 0) + return ret; + } if (pd->data->ext_buck_iso_offs && MTK_SCPD_CAPS(pd, MTK_SCPD_EXT_BUCK_ISO)) regmap_set_bits(scpsys->base, pd->data->ext_buck_iso_offs, @@ -717,15 +811,21 @@ static int scpsys_power_off_internal(struct scpsys_domain *pd) return ret; if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_PWRSEQ)) - scpsys_modem_pwrseq_off(pd); - else - scpsys_ctl_pwrseq_off(pd); + ret = scpsys_modem_pwrseq_off(pd); + else if (MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) + ret = scpsys_simple_pwrseq_off(pd); + else if (!MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) + ret = scpsys_ctl_pwrseq_off(pd); + + if (ret < 0) { + /* Re-enable clocks so that next power off doesn't break the refcount */ + int r = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks); + + if (r) + dev_warn(scpsys->dev, "Could not re-enable clocks: %d\n", r); - /* wait until PWR_ACK = 0 */ - ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, !tmp, MTK_POLL_DELAY_US, - MTK_POLL_TIMEOUT); - if (ret < 0) return ret; + } clk_bulk_disable_unprepare(pd->num_clks, pd->clks); @@ -742,7 +842,8 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) } static struct -generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node) +generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node, + u8 *domains_idx, u8 *num_domains) { const struct scpsys_domain_data *domain_data; const struct scpsys_hwv_domain_data *hwv_domain_data; @@ -917,6 +1018,7 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no else pm_genpd_init(&pd->genpd, NULL, false); + domains_idx[(*num_domains)++] = (u8) id; scpsys->domains[id] = &pd->genpd; return scpsys->pd_data.domains[id]; @@ -928,7 +1030,8 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no return ERR_PTR(ret); } -static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *parent) +static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *parent, + u8 *domains_idx, u8 *num_domains) { struct generic_pm_domain *child_pd, *parent_pd; struct device_node *child; @@ -951,7 +1054,7 @@ static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *paren parent_pd = scpsys->pd_data.domains[id]; - child_pd = scpsys_add_one_domain(scpsys, child); + child_pd = scpsys_add_one_domain(scpsys, child, domains_idx, num_domains); if (IS_ERR(child_pd)) { ret = PTR_ERR(child_pd); dev_err_probe(scpsys->dev, ret, "%pOF: failed to get child domain id\n", @@ -960,7 +1063,7 @@ static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *paren } /* recursive call to add all subdomains */ - ret = scpsys_add_subdomain(scpsys, child); + ret = scpsys_add_subdomain(scpsys, child, domains_idx, num_domains); if (ret) goto err_put_node; @@ -1001,7 +1104,7 @@ static void scpsys_remove_one_domain(struct scpsys_domain *pd) if (scpsys_hwv_domain_is_enable_done(pd)) scpsys_hwv_power_off_internal(pd); } else { - if (scpsys_domain_is_on(pd)) + if (scpsys_domain_is_on(pd) || MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) scpsys_power_off_internal(pd); } @@ -1009,14 +1112,16 @@ static void scpsys_remove_one_domain(struct scpsys_domain *pd) clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); } -static void scpsys_domain_cleanup(struct scpsys *scpsys) +static void scpsys_domain_cleanup(struct scpsys *scpsys, u8 *domains_idx, u8 num_probed) { struct generic_pm_domain *genpd; struct scpsys_domain *pd; int i; - for (i = scpsys->pd_data.num_domains - 1; i >= 0; i--) { - genpd = scpsys->pd_data.domains[i]; + for (i = num_probed - 1; i >= 0; i--) { + u8 pd_idx = domains_idx[i]; + + genpd = scpsys->pd_data.domains[pd_idx]; if (genpd) { pd = to_scpsys_domain(genpd); scpsys_remove_one_domain(pd); @@ -1104,6 +1209,12 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s regmap[2] = NULL; } + /* If no access controllers are needed, don't allocate and don't fail */ + if (num_regmaps == 0) { + scpsys->bus_prot = NULL; + return 0; + } + scpsys->bus_prot = devm_kmalloc_array(dev, num_regmaps, sizeof(*scpsys->bus_prot), GFP_KERNEL); if (!scpsys->bus_prot) @@ -1178,6 +1289,10 @@ static const struct of_device_id scpsys_of_match[] = { .compatible = "mediatek,mt6795-power-controller", .data = &mt6795_scpsys_data, }, + { + .compatible = "mediatek,mt6858-power-controller", + .data = &mt6858_scpsys_data, + }, { .compatible = "mediatek,mt6893-power-controller", .data = &mt6893_scpsys_data, @@ -1218,6 +1333,10 @@ static const struct of_device_id scpsys_of_match[] = { .compatible = "mediatek,mt8196-power-controller", .data = &mt8196_scpsys_data, }, + { + .compatible = "mediatek,mt8196-hfrp-power-controller", + .data = &mt8196_hfrpsys_data, + }, { .compatible = "mediatek,mt8196-hwv-hfrp-power-controller", .data = &mt8196_hfrpsys_hwv_data, @@ -1241,6 +1360,8 @@ static int scpsys_probe(struct platform_device *pdev) struct device *parent; struct scpsys *scpsys; int num_domains, ret; + u8 num_added_pds = 0; + u8 *added_pds_idx; soc = of_device_get_match_data(&pdev->dev); if (!soc) { @@ -1254,6 +1375,19 @@ static int scpsys_probe(struct platform_device *pdev) if (!scpsys) return -ENOMEM; + /* + * Temporarily store the IDs of the power domains that are added as in + * case of a probe deferral this can be used to correctly cleanup all + * of what was added before. + * + * Note that this array is used only in the probe function and must be + * freed at the end, regardless of whether all of the power domains were + * probed successfully or any failure happened. + */ + added_pds_idx = devm_kmalloc_array(dev, num_domains, sizeof(*added_pds_idx), GFP_KERNEL); + if (!added_pds_idx) + return -ENOMEM; + scpsys->dev = dev; scpsys->soc_data = soc; @@ -1284,13 +1418,15 @@ static int scpsys_probe(struct platform_device *pdev) for_each_available_child_of_node_scoped(np, node) { struct generic_pm_domain *domain; - domain = scpsys_add_one_domain(scpsys, node); + domain = scpsys_add_one_domain(scpsys, node, + added_pds_idx, &num_added_pds); if (IS_ERR(domain)) { ret = PTR_ERR(domain); goto err_cleanup_domains; } - ret = scpsys_add_subdomain(scpsys, node); + ret = scpsys_add_subdomain(scpsys, node, + added_pds_idx, &num_added_pds); if (ret) goto err_cleanup_domains; } @@ -1306,10 +1442,11 @@ static int scpsys_probe(struct platform_device *pdev) goto err_cleanup_domains; } + devm_kfree(dev, added_pds_idx); return 0; err_cleanup_domains: - scpsys_domain_cleanup(scpsys); + scpsys_domain_cleanup(scpsys, added_pds_idx, num_added_pds); return ret; } diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h index a5dca24cbc2f..45da83e9d649 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.h +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h @@ -17,6 +17,8 @@ #define MTK_SCPD_MODEM_PWRSEQ BIT(10) #define MTK_SCPD_SKIP_RESET_B BIT(11) #define MTK_SCPD_INFRA_PWR_CTL BIT(12) +#define MTK_SCPD_SIMPLE_PWRSEQ BIT(13) +#define MTK_SCPD_MODEM_SECURE_PWRSEQ BIT(14) #define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data ? \ (_scpd)->data->caps & (_x) : \ (_scpd)->hwv_data->caps & (_x)) @@ -64,6 +66,11 @@ enum scpsys_bus_prot_block { BUS_PROT_BLOCK_INFRA_NAO, BUS_PROT_BLOCK_SMI, BUS_PROT_BLOCK_SPM, + BUS_PROT_BLOCK_IMG_SUB0, + BUS_PROT_BLOCK_CAM_SUB1, + BUS_PROT_BLOCK_CAM_SUB0, + BUS_PROT_BLOCK_IPE_SUB0, + BUS_PROT_BLOCK_VLP, BUS_PROT_BLOCK_COUNT, }; diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index ffc46de8d07a..96e4bd2f5a14 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -41,7 +41,6 @@ * @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; @@ -58,7 +57,6 @@ struct rpmhpd { const char *res_name; u32 addr; bool state_synced; - bool skip_retention_level; }; struct rpmhpd_desc { @@ -191,7 +189,6 @@ static struct rpmhpd mxc = { .pd = { .name = "mxc", }, .peer = &mxc_ao, .res_name = "mxc.lvl", - .skip_retention_level = true, }; static struct rpmhpd mxc_ao = { @@ -199,7 +196,6 @@ static struct rpmhpd mxc_ao = { .active_only = true, .peer = &mxc, .res_name = "mxc.lvl", - .skip_retention_level = true, }; static struct rpmhpd nsp = { @@ -309,10 +305,10 @@ static struct rpmhpd *sa8775p_rpmhpds[] = { [SA8775P_LMX] = &lmx, [SA8775P_MMCX] = &mmcx, [SA8775P_MMCX_AO] = &mmcx_ao, - [SA8775P_MXC] = &mxc, - [SA8775P_MXC_AO] = &mxc_ao, [SA8775P_MX] = &mx, [SA8775P_MX_AO] = &mx_ao, + [SA8775P_MXC] = &mxc, + [SA8775P_MXC_AO] = &mxc_ao, [SA8775P_NSP0] = &nsp0, [SA8775P_NSP1] = &nsp1, }; @@ -329,10 +325,10 @@ static struct rpmhpd *nord_rpmhpds[] = { [RPMHPD_EBI] = &ebi, [RPMHPD_GFX] = &gfx, [RPMHPD_GFX1] = &gfx1, - [RPMHPD_MX] = &mx, - [RPMHPD_MX_AO] = &mx_ao, [RPMHPD_MMCX] = &mmcx, [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, [RPMHPD_MXC] = &mxc, [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_NSP0] = &nsp0, @@ -484,9 +480,9 @@ static struct rpmhpd *sm7150_rpmhpds[] = { [RPMHPD_GFX] = &gfx, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, + [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_MSS] = &mss, }; static const struct rpmhpd_desc sm7150_desc = { @@ -702,11 +698,11 @@ static struct rpmhpd *hawi_rpmhpds[] = { [RPMHPD_LMX] = &lmx, [RPMHPD_MMCX] = &mmcx, [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, [RPMHPD_MXC] = &mxc, [RPMHPD_MXC_AO] = &mxc_ao, - [RPMHPD_MSS] = &mss, [RPMHPD_NSP] = &nsp, [RPMHPD_NSP2] = &nsp2, }; @@ -813,18 +809,18 @@ static struct rpmhpd *glymur_rpmhpds[] = { [RPMHPD_CX_AO] = &cx_ao, [RPMHPD_EBI] = &ebi, [RPMHPD_GFX] = &gfx, + [RPMHPD_GMXC] = &gmxc, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, [RPMHPD_MMCX] = &mmcx, [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, [RPMHPD_MXC] = &mxc, [RPMHPD_MXC_AO] = &mxc_ao, - [RPMHPD_MSS] = &mss, [RPMHPD_NSP] = &nsp, [RPMHPD_NSP2] = &nsp2, - [RPMHPD_GMXC] = &gmxc, }; static const struct rpmhpd_desc glymur_desc = { @@ -838,15 +834,15 @@ static struct rpmhpd *x1e80100_rpmhpds[] = { [RPMHPD_CX_AO] = &cx_ao, [RPMHPD_EBI] = &ebi, [RPMHPD_GFX] = &gfx, + [RPMHPD_GMXC] = &gmxc, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, [RPMHPD_MMCX] = &mmcx, [RPMHPD_MMCX_AO] = &mmcx_ao, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_NSP] = &nsp, [RPMHPD_MXC] = &mxc, - [RPMHPD_GMXC] = &gmxc, + [RPMHPD_NSP] = &nsp, }; static const struct rpmhpd_desc x1e80100_desc = { @@ -864,10 +860,10 @@ static struct rpmhpd *qcs8300_rpmhpds[] = { [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_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_NSP0] = &nsp0, [RPMHPD_NSP1] = &nsp1, }; @@ -1097,7 +1093,15 @@ 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) + /* + * Most HW won't function properly at Retention. The minimum + * operational level is the first level above Retention. The + * small subset of HW that can operate at Retention isn't + * controlled by HLOS. Skip the Retention level to avoid HW + * failures when the PD is enabled without first having an + * explicit OPP level set. + */ + if (buf[i] == RPMH_REGULATOR_LEVEL_RETENTION) continue; rpmhpd->level[i] = buf[i]; diff --git a/drivers/pmdomain/renesas/Kconfig b/drivers/pmdomain/renesas/Kconfig index b507c3e0d723..f2f52d3c29a0 100644 --- a/drivers/pmdomain/renesas/Kconfig +++ b/drivers/pmdomain/renesas/Kconfig @@ -13,6 +13,10 @@ config SYSC_RMOBILE bool "System Controller support for R-Mobile" if COMPILE_TEST # SoC +config MDLC_R8A78000 + bool "Module Controller support for R8A78000 (R-Car X5H)" if COMPILE_TEST + select RESET_CONTROLLER + config SYSC_R8A7742 bool "System Controller support for R8A7742 (RZ/G1H)" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/pmdomain/renesas/Makefile b/drivers/pmdomain/renesas/Makefile index 0391e6e67440..17849aad37a5 100644 --- a/drivers/pmdomain/renesas/Makefile +++ b/drivers/pmdomain/renesas/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 # SoC +obj-$(CONFIG_MDLC_R8A78000) += r8a78000-mdlc.o obj-$(CONFIG_SYSC_R8A7742) += r8a7742-sysc.o obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o diff --git a/drivers/pmdomain/renesas/r8a78000-mdlc.c b/drivers/pmdomain/renesas/r8a78000-mdlc.c new file mode 100644 index 000000000000..2668fc2ac9cb --- /dev/null +++ b/drivers/pmdomain/renesas/r8a78000-mdlc.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car X5H Module Controller + * + * Copyright (C) 2026 Glider bv + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct mod_map { + int hw_id; /* Hardware module ID or -1 sentinel */ +}; + +struct mdlc_info { + u32 base; + const struct mod_map *mod_map; +}; + +/** + * struct r8a78000_mdlc_priv - Module Controller Private Data + * + * @link: Link into list of MDLC instances + * @genpd_data: PM domain provider data + * @rcdev: Reset controller entity + * @dev: MDLC device + * @np: Device node in DT representing the MDLC + * @mod_map: Mapping from hardware module IDs + */ +struct r8a78000_mdlc_priv { + struct hlist_node link; + struct genpd_onecell_data genpd_data; + struct reset_controller_dev rcdev; + struct device *dev; + struct device_node *np; + const struct mod_map *mod_map; +}; + +static struct generic_pm_domain *r8a78000_genpd_always_on; +static HLIST_HEAD(r8a78000_mdlc_list); +static DEFINE_MUTEX(r8a78000_mdlc_lock); /* protects the two above */ + +static struct generic_pm_domain *r8a78000_genpd_xlate( + const struct of_phandle_args *spec, void *data) +{ + struct r8a78000_mdlc_priv *priv = container_of(data, + struct r8a78000_mdlc_priv, genpd_data); + struct device *dev = priv->dev; + u32 id; + + if (spec->args_count != 2) + return ERR_PTR(-EINVAL); + + id = spec->args[0]; + + if (id >= R8A78000_MDLC_PD_AON) { + dev_dbg(dev, + "Mapping HW power domain 0x%x to always-on domain\n", + id); + return r8a78000_genpd_always_on; + } + + /* For now only always-on domains are supported */ + dev_err(dev, "Unknown power domain 0x%x\n", id); + return ERR_PTR(-ENOENT); +} + +#define rcdev_to_priv(_rcdev) \ + container_of(_rcdev, struct r8a78000_mdlc_priv, rcdev) + +static const struct mod_map *mod_map_find(const struct mod_map *map, u32 id) +{ + if (!map) + return NULL; + + for (; map->hw_id >= 0; map++) { + if (map->hw_id == id) + return map; + } + + return NULL; +} + +static int r8a78000_mdlc_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *spec) +{ + struct r8a78000_mdlc_priv *priv = rcdev_to_priv(rcdev); + struct device *dev = priv->dev; + const struct mod_map *map; + u32 id; + + if (spec->args_count != 1) + return -EINVAL; + + id = spec->args[0]; + + map = mod_map_find(priv->mod_map, id); + if (!map) { + dev_err(dev, "Unknown reset 0x%x\n", id); + return -ENOENT; + } + + dev_dbg(dev, "Ignoring HW reset 0x%x\n", id); + return id; +} + +#define DEFINE_MDLC_RESET_WRAPPER(op) \ + static int r8a78000_mdlc_ ## op(struct reset_controller_dev *rcdev, \ + unsigned long id) \ + { \ + struct r8a78000_mdlc_priv *priv = rcdev_to_priv(rcdev); \ + \ + dev_dbg(priv->dev, "%s: Ignoring\n", __func__); \ + return 0; \ + } + +DEFINE_MDLC_RESET_WRAPPER(reset) +DEFINE_MDLC_RESET_WRAPPER(assert) +DEFINE_MDLC_RESET_WRAPPER(deassert) +DEFINE_MDLC_RESET_WRAPPER(status) + +static const struct reset_control_ops r8a78000_mdlc_reset_ops = { + .reset = r8a78000_mdlc_reset, + .assert = r8a78000_mdlc_assert, + .deassert = r8a78000_mdlc_deassert, + .status = r8a78000_mdlc_status, +}; + +static int r8a78000_mdlc_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct device_node *np = dev->of_node; + struct r8a78000_mdlc_priv *priv; + struct of_phandle_args pd_spec; + const struct mod_map *map; + unsigned int id; + int ret; + + ret = of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", 0, &pd_spec); + if (ret < 0) + return ret; + + scoped_guard(mutex, &r8a78000_mdlc_lock) { + hlist_for_each_entry(priv, &r8a78000_mdlc_list, link) { + if (priv->np == pd_spec.np) + break; + } + } + + if (!priv) { + dev_err(dev, "%s: MDLC %pOF not found\n", __func__, pd_spec.np); + of_node_put(pd_spec.np); + return -ENODEV; + } + + id = pd_spec.args[1]; + of_node_put(pd_spec.np); + + map = mod_map_find(priv->mod_map, id); + if (!map) { + dev_err(dev, "Unknown module 0x%x\n", id); + return -ENOENT; + } + + dev_dbg(dev, "Ignoring HW module 0x%x\n", id); + return 0; +} + +static void r8a78000_mdlc_unlink(void *data) +{ + struct r8a78000_mdlc_priv *priv = data; + + scoped_guard(mutex, &r8a78000_mdlc_lock) { + hlist_del(&priv->link); + } +} + +static void r8a78000_genpd_del_provider(void *data) +{ + of_genpd_del_provider(data); +} + +static int r8a78000_genpd_always_on_singleton(struct device *dev) +{ + struct generic_pm_domain *genpd; + int ret; + + guard(mutex)(&r8a78000_mdlc_lock); + + if (r8a78000_genpd_always_on) + return 0; + + genpd = kzalloc_obj(*genpd); + if (!genpd) + return -ENOMEM; + + genpd->name = "always-on"; + genpd->attach_dev = r8a78000_mdlc_attach_dev; + + ret = pm_genpd_init(genpd, &pm_domain_always_on_gov, false); + if (ret) { + kfree(genpd); + return dev_err_probe(dev, ret, + "Failed to create always-on domain\n"); + } + + r8a78000_genpd_always_on = genpd; + return 0; +} + +static int r8a78000_mdlc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct r8a78000_mdlc_priv *priv; + const struct mdlc_info *info; + struct resource *res; + int ret; + + ret = r8a78000_genpd_always_on_singleton(dev); + if (ret) + return ret; + + info = of_device_get_match_data(dev); + if (!info) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->np = np; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + for (; info->base; info++) { + if (info->base == res->start) + break; + } + + if (!info->base) { + dev_dbg(dev, "Unsupported MDLC instance 0x%pa\n", &res->start); + return -ENODEV; + } + + priv->mod_map = info->mod_map; + + scoped_guard(mutex, &r8a78000_mdlc_lock) { + hlist_add_head(&priv->link, &r8a78000_mdlc_list); + } + + ret = devm_add_action_or_reset(dev, r8a78000_mdlc_unlink, priv); + if (ret) + return dev_err_probe(dev, ret, "failed to add action\n"); + + /* Note that no actual domains are registered, just need translation */ + priv->genpd_data.xlate = r8a78000_genpd_xlate; + ret = of_genpd_add_provider_onecell(np, &priv->genpd_data); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register genpd provider\n"); + + ret = devm_add_action_or_reset(dev, r8a78000_genpd_del_provider, np); + if (ret) + return dev_err_probe(dev, ret, + "failed to add unregister action\n"); + + priv->rcdev.ops = &r8a78000_mdlc_reset_ops; + priv->rcdev.of_node = np; + priv->rcdev.of_reset_n_cells = 1; + priv->rcdev.of_xlate = r8a78000_mdlc_reset_xlate; + + ret = devm_reset_controller_register(dev, &priv->rcdev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register reset controller\n"); + + return 0; +} + +static const struct mod_map r8a78000_mdlc_perw_mod_default[] = { + { 0x54 }, /* HSCIF0 */ + { -1 } +}; + +static const struct mdlc_info r8a78000_mdlc_default[] = { + { + .base = 0xc05d0000 /* mdlc_perw */, + .mod_map = r8a78000_mdlc_perw_mod_default, + }, + { /* sentinel */ } +}; + +static const struct of_device_id r8a78000_mdlc_match[] = { + { + .compatible = "renesas,r8a78000-mdlc", + .data = &r8a78000_mdlc_default, + }, + { /* sentinel */ } +}; + +static struct platform_driver r8a78000_mdlc_driver = { + .probe = r8a78000_mdlc_probe, + .driver = { + .name = "r8a78000-mdlc", + .of_match_table = r8a78000_mdlc_match, + .suppress_bind_attrs = true, + }, +}; + +builtin_platform_driver(r8a78000_mdlc_driver) + +MODULE_DESCRIPTION("R-Car X5H MDLC Driver"); diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 490bbb1d1d8e..ba66ae719428 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -204,6 +204,20 @@ struct rockchip_pmu { .active_wakeup = wakeup, \ } +#define DOMAIN_M_R(_name, pwr, status, req, idle, ack, wakeup, regulator) \ +{ \ + .name = _name, \ + .pwr_w_mask = (pwr) << 16, \ + .pwr_mask = (pwr), \ + .status_mask = (status), \ + .req_w_mask = (req) << 16, \ + .req_mask = (req), \ + .idle_mask = (idle), \ + .ack_mask = (ack), \ + .active_wakeup = wakeup, \ + .need_regulator = regulator, \ +} + #define DOMAIN_RK3036(_name, req, ack, idle, wakeup) \ { \ .name = _name, \ @@ -241,8 +255,8 @@ struct rockchip_pmu { #define DOMAIN_RK3562(name, pwr, req, g_mask, mem, wakeup) \ DOMAIN_M_G_SD(name, pwr, pwr, req, req, req, g_mask, mem, wakeup, false) -#define DOMAIN_RK3568(name, pwr, req, wakeup) \ - DOMAIN_M(name, pwr, pwr, req, req, req, wakeup) +#define DOMAIN_RK3568(name, pwr, req, wakeup, regulator) \ + DOMAIN_M_R(name, pwr, pwr, req, req, req, wakeup, regulator) #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) @@ -1274,15 +1288,15 @@ static const struct rockchip_domain_info rk3562_pm_domains[] = { }; static const struct rockchip_domain_info rk3568_pm_domains[] = { - [RK3568_PD_NPU] = DOMAIN_RK3568("npu", BIT(1), BIT(2), false), - [RK3568_PD_GPU] = DOMAIN_RK3568("gpu", BIT(0), BIT(1), false), - [RK3568_PD_VI] = DOMAIN_RK3568("vi", BIT(6), BIT(3), false), - [RK3568_PD_VO] = DOMAIN_RK3568("vo", BIT(7), BIT(4), false), - [RK3568_PD_RGA] = DOMAIN_RK3568("rga", BIT(5), BIT(5), false), - [RK3568_PD_VPU] = DOMAIN_RK3568("vpu", BIT(2), BIT(6), false), - [RK3568_PD_RKVDEC] = DOMAIN_RK3568("vdec", BIT(4), BIT(8), false), - [RK3568_PD_RKVENC] = DOMAIN_RK3568("venc", BIT(3), BIT(7), false), - [RK3568_PD_PIPE] = DOMAIN_RK3568("pipe", BIT(8), BIT(11), false), + [RK3568_PD_NPU] = DOMAIN_RK3568("npu", BIT(1), BIT(2), false, true), + [RK3568_PD_GPU] = DOMAIN_RK3568("gpu", BIT(0), BIT(1), false, false), + [RK3568_PD_VI] = DOMAIN_RK3568("vi", BIT(6), BIT(3), false, false), + [RK3568_PD_VO] = DOMAIN_RK3568("vo", BIT(7), BIT(4), false, false), + [RK3568_PD_RGA] = DOMAIN_RK3568("rga", BIT(5), BIT(5), false, false), + [RK3568_PD_VPU] = DOMAIN_RK3568("vpu", BIT(2), BIT(6), false, false), + [RK3568_PD_RKVDEC] = DOMAIN_RK3568("vdec", BIT(4), BIT(8), false, false), + [RK3568_PD_RKVENC] = DOMAIN_RK3568("venc", BIT(3), BIT(7), false, false), + [RK3568_PD_PIPE] = DOMAIN_RK3568("pipe", BIT(8), BIT(11), false, false), }; static const struct rockchip_domain_info rk3576_pm_domains[] = { diff --git a/drivers/pmdomain/tegra/powergate-bpmp.c b/drivers/pmdomain/tegra/powergate-bpmp.c index 8cde4f384846..e6a7e025b012 100644 --- a/drivers/pmdomain/tegra/powergate-bpmp.c +++ b/drivers/pmdomain/tegra/powergate-bpmp.c @@ -137,6 +137,13 @@ static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp, if (err < 0 || msg.rx.ret < 0) return NULL; + if (response.get_name.name[0] == '\0') + return NULL; + + if (dev_to_node(bpmp->dev) != NUMA_NO_NODE) + return kasprintf(GFP_KERNEL, "%d-%s", dev_to_node(bpmp->dev), + response.get_name.name); + return kstrdup(response.get_name.name, GFP_KERNEL); } @@ -234,7 +241,7 @@ tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp, struct tegra_powergate_info *info = &powergates[count]; info->name = tegra_bpmp_powergate_get_name(bpmp, id); - if (!info->name || info->name[0] == '\0') { + if (!info->name) { num_holes++; continue; } diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index fdf18ed2dfc2..eb8ac0d47d9d 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -357,6 +357,7 @@ config ARCH_R8A78000 bool "ARM64 Platform support for R8A78000 (R-Car X5H)" default y if ARCH_RENESAS select ARCH_RCAR_GEN5 + select MDLC_R8A78000 help This enables support for the Renesas R-Car X5H SoC. diff --git a/include/dt-bindings/power/amlogic,a9-pwrc.h b/include/dt-bindings/power/amlogic,a9-pwrc.h new file mode 100644 index 000000000000..c1c2308a0d0c --- /dev/null +++ b/include/dt-bindings/power/amlogic,a9-pwrc.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2026 Amlogic, Inc. All rights reserved + */ + +#ifndef _DT_BINDINGS_AMLOGIC_A9_POWER_H +#define _DT_BINDINGS_AMLOGIC_A9_POWER_H + +#define PWRC_A9_DSPA_ID 0 +#define PWRC_A9_U3HSG_U3_ID 1 +#define PWRC_A9_DP_ID 2 +#define PWRC_A9_DOS_HCODEC_ID 3 +#define PWRC_A9_TAHOE_ID 4 +#define PWRC_A9_DOS_HEVC_ID 5 +#define PWRC_A9_U2H_ID 6 +#define PWRC_A9_U3DRD_B_ID 7 +#define PWRC_A9_VPU_HDMI_ID 8 +#define PWRC_A9_U2DRD_ID 9 +#define PWRC_A9_U3DRD_A_ID 10 +#define PWRC_A9_SD_EMMC_C_ID 11 +#define PWRC_A9_GE2D_ID 12 +#define PWRC_A9_AMFC_ID 13 +#define PWRC_A9_EDPTX_ID 14 +#define PWRC_A9_OPP_ID 15 +#define PWRC_A9_VICP_ID 16 +#define PWRC_A9_SD_EMMC_A_ID 17 +#define PWRC_A9_SD_EMMC_B_ID 18 +#define PWRC_A9_ETH_ID 19 +#define PWRC_A9_PCIE_A_ID 20 +#define PWRC_A9_PCIE_B_ID 21 +#define PWRC_A9_NNA_4T_ID 22 +#define PWRC_A9_HDMIRX_ID 23 +#define PWRC_A9_CVE_ID 24 +#define PWRC_A9_ISP_ID 25 +#define PWRC_A9_ETH_1G_ID 26 +#define PWRC_A9_U3HSG_HSG_ID 27 +#define PWRC_A9_U3DPPHY_U3_ID 28 +#define PWRC_A9_U3DPPHY_DP_ID 29 +#define PWRC_A9_PCIE3PHY_ID 30 +#define PWRC_A9_U3HSG_PCIE2_ID 31 +#define PWRC_A9_MALI_TOP_ID 32 +#define PWRC_A9_AO_SED_ID 33 +#define PWRC_A9_AO_IR_ID 34 +#define PWRC_A9_AO_UART_B_ID 35 +#define PWRC_A9_AO_UART_C_ID 36 +#define PWRC_A9_AO_UART_D_ID 37 +#define PWRC_A9_AO_SPISG_ID 38 +#define PWRC_A9_AO_UART_E_ID 39 +#define PWRC_A9_AO_CEC_ID 40 +#define PWRC_A9_EE_SRAMA_ID 41 +#define PWRC_A9_AUDIO_ID 42 +#define PWRC_A9_DMC0_ID 43 +#define PWRC_A9_GIC_ID 44 +#define PWRC_A9_DDRPHY_ID 45 +#define PWRC_A9_AUCPU_ID 46 +#define PWRC_A9_DSI0_ID 47 +#define PWRC_A9_DSI1_ID 48 +#define PWRC_A9_CAN0_ID 49 +#define PWRC_A9_CAN1_ID 50 + +#endif diff --git a/include/dt-bindings/power/mediatek,mt6858-power.h b/include/dt-bindings/power/mediatek,mt6858-power.h new file mode 100644 index 000000000000..6ed9e82d4ad3 --- /dev/null +++ b/include/dt-bindings/power/mediatek,mt6858-power.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef _DT_BINDINGS_POWER_MT6858_POWER_H +#define _DT_BINDINGS_POWER_MT6858_POWER_H + +#define MT6858_POWER_DOMAIN_MD 0 +#define MT6858_POWER_DOMAIN_CONN 1 +#define MT6858_POWER_DOMAIN_AUDIO 2 +#define MT6858_POWER_DOMAIN_MM_INFRA 3 +#define MT6858_POWER_DOMAIN_ISP_IMG1 4 +#define MT6858_POWER_DOMAIN_ISP_IMG2 5 +#define MT6858_POWER_DOMAIN_ISP_IPE 6 +#define MT6858_POWER_DOMAIN_VDE0 7 +#define MT6858_POWER_DOMAIN_VEN0 8 +#define MT6858_POWER_DOMAIN_CAM_MAIN 9 +#define MT6858_POWER_DOMAIN_CAM_SUBA 10 +#define MT6858_POWER_DOMAIN_CAM_SUBB 11 +#define MT6858_POWER_DOMAIN_DIS0 12 +#define MT6858_POWER_DOMAIN_MM_PROC_DORMANT 13 +#define MT6858_POWER_DOMAIN_CSI_RX 14 +#define MT6858_POWER_DOMAIN_SSUSB 15 + +#endif /* _DT_BINDINGS_POWER_MT6858_POWER_H */ diff --git a/include/dt-bindings/power/mediatek,mt8196-power.h b/include/dt-bindings/power/mediatek,mt8196-power.h index 0f622a93c807..085790bf8124 100644 --- a/include/dt-bindings/power/mediatek,mt8196-power.h +++ b/include/dt-bindings/power/mediatek,mt8196-power.h @@ -30,6 +30,10 @@ #define MT8196_POWER_DOMAIN_MM_PROC_DORMANT 0 #define MT8196_POWER_DOMAIN_SSR 1 +/* HFRPSYS Multimedia Power Control (MMPC) - Direct Control */ +#define MT8196_POWER_DOMAIN_EDPTX 0 +#define MT8196_POWER_DOMAIN_DPTX 1 + /* HFRPSYS MultiMedia Power Control (MMPC) - HW Voter */ #define MT8196_POWER_DOMAIN_VDE0 0 #define MT8196_POWER_DOMAIN_VDE1 1 diff --git a/include/dt-bindings/power/renesas,r8a78000-mdlc.h b/include/dt-bindings/power/renesas,r8a78000-mdlc.h new file mode 100644 index 000000000000..6f3d1ae23648 --- /dev/null +++ b/include/dt-bindings/power/renesas,r8a78000-mdlc.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (C) 2026 Glider bv + */ +#ifndef __DT_BINDINGS_POWER_RENESAS_R8A78000_MDLC_H__ +#define __DT_BINDINGS_POWER_RENESAS_R8A78000_MDLC_H__ + +/* R-Car X5H Module Controller Always-On Power Domains */ + +#define R8A78000_MDLC_PD_AON 0x40 +#define R8A78000_MDLC_PD_SCP 0x41 +#define R8A78000_MDLC_PD_APL 0x42 +#define R8A78000_MDLC_PD_ACL 0x43 + +#endif /* __DT_BINDINGS_POWER_RENESAS_R8A78000_MDLC_H__ */ diff --git a/include/linux/soc/mediatek/mtk_sip_svc.h b/include/linux/soc/mediatek/mtk_sip_svc.h index abe24a73ee19..6c95a29b79fa 100644 --- a/include/linux/soc/mediatek/mtk_sip_svc.h +++ b/include/linux/soc/mediatek/mtk_sip_svc.h @@ -22,6 +22,9 @@ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, MTK_SIP_SMC_CONVENTION, \ ARM_SMCCC_OWNER_SIP, fn_id) +/* Modem related SMC call */ +#define MTK_SIP_KERNEL_CCCI_CONTROL MTK_SIP_SMC_CMD(0x505) + /* DVFSRC SMC calls */ #define MTK_SIP_DVFSRC_VCOREFS_CONTROL MTK_SIP_SMC_CMD(0x506)