From ed9034eff1f34335980fc50083f84a4874f9dc79 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 11 Jun 2026 10:52:39 +0200 Subject: [PATCH 01/24] pmdomain: qcom: rpmhpd: Sort table entries by index name List of power domains in arrays of 'struct rpmhpd' are sorted according to index name (the define, e.g. RPMHPD_MX), not by actual numerical value. Fix few sorting mistakes, so comparing the tables between different devices will be easier. No functional impact - same binary. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 63120e703923..c12127d8e8ae 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -305,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, }; @@ -325,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, @@ -480,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 = { @@ -698,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, }; @@ -809,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 = { @@ -834,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 = { @@ -860,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, }; From c011b7c92514196bac6746a81ae2437d264c0fe6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 3 Jun 2026 17:18:43 +0200 Subject: [PATCH 02/24] pmdomain: arm: Grammar s/may needed/may be needed/ Fix grammar in the help text for the ARM_SCMI_POWER_DOMAIN symbol. Signed-off-by: Geert Uytterhoeven Acked-by: Sudeep Holla Signed-off-by: Ulf Hansson --- drivers/pmdomain/arm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From a1d9d3b958d69a13783613304f524f489fecdd1f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 21:59:37 +0800 Subject: [PATCH 03/24] pmdomain: bcm: bcm2835: handle genpd provider registration errors bcm2835_power_probe() initializes all power domains and then registers the onecell genpd provider, but ignores of_genpd_add_provider_onecell() failures. Probe can therefore return success even though no provider was published. Check the provider registration return value and jump to the existing cleanup path on failure. Fixes: 670c672608a1 ("soc: bcm: bcm2835-pm: Add support for power domains under a new binding.") Signed-off-by: Pengpeng Hou Reviewed-by: Stefan Wahren Signed-off-by: Ulf Hansson --- drivers/pmdomain/bcm/bcm2835-power.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/bcm/bcm2835-power.c b/drivers/pmdomain/bcm/bcm2835-power.c index b76d74e3849b..68a0a7a8cee3 100644 --- a/drivers/pmdomain/bcm/bcm2835-power.c +++ b/drivers/pmdomain/bcm/bcm2835-power.c @@ -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; From 7178817f190496f2bfdec181fb4aa72832448468 Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Mon, 29 Jun 2026 13:33:36 -0700 Subject: [PATCH 04/24] pmdomain: qcom: rpmhpd: Skip retention by default Retention is very rarely an operational corner. In the majority of cases, HW cannot operate properly at Retention levels and so the minimum operational level when enabling a rail is the first corner above Retention. A small subset of always-on use cases can operate at Retention, but those aren't controlled from HLOS. Currently, we allow Retention by default and only disallow it special cases. But this leaves us open to various failures when the PD is enabled without first being voted to an OPP above Retention. Such as when a child GDSC PD requests to enable its parent RPMh PD. In this case, the GDSC would get stuck powering on. Newer chips have started supporting Retention on rails that didn't previously (such as for MMCX). Instead of adding more special cases to skip Retention on MMCX, start skipping Retention by default since it's almost never desired from an HLOS perspective. Signed-off-by: Mike Tipton Reviewed-by: Konrad Dybcio Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index c12127d8e8ae..90743275942d 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 = { @@ -1093,7 +1089,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]; From 52777caee78154a68523d78cec725b781d64e9ca Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 9 Jul 2026 10:46:31 +0200 Subject: [PATCH 05/24] dt-bindings: power: mediatek: Add support for MT8196 direct HFRP Add support for the HFRPSYS direct control power domains in the MT8196 SoC, controlling power for the DisplayPort and for the Embedded DisplayPort Transmitter IPs. Reviewed-by: Matthias Brugger Acked-by: Krzysztof Kozlowski Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Ulf Hansson --- .../devicetree/bindings/power/mediatek,power-controller.yaml | 1 + include/dt-bindings/power/mediatek,mt8196-power.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml index 07f046277f8a..070c6e5666dc 100644 --- a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml +++ b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml @@ -34,6 +34,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 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 From 1f1bbce068b76ca04b3c23164a91360da0c06764 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 9 Jul 2026 10:46:33 +0200 Subject: [PATCH 06/24] pmdomain: mediatek: Respect PD relationships during error cleanup In case any probe error occurs (usually, a probe deferral) the power domains shall be cleaned up while respecting their child to parent relationship, or the system may freeze. In order to do that without any memory footprint impacts after the fact, allocate a temporary array in the probe function and use it to store the indices of the added power domains in the correct order. This will be used in the error cleanup path and will be freed at the end regardless of the probe status as, when the probing succeeds, the genpd API takes care of unregistering all PDs in the correct order anyway. Reviewed-by: Matthias Brugger Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 43 +++++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index f0a6339affd7..8d49d9ee3cd2 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -750,7 +750,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; @@ -925,6 +926,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]; @@ -936,7 +938,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; @@ -959,7 +962,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", @@ -968,7 +971,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; @@ -1017,14 +1020,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); @@ -1241,6 +1246,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 +1261,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 +1304,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 +1328,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; } From 02a2a92c122d018730fcf8ff9ec88f0768d161ec Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 9 Jul 2026 10:46:34 +0200 Subject: [PATCH 07/24] pmdomain: mediatek: Add support for Direct CTL simple power sequence Some new SoCs like MT8196, MT6991, and others, have got one additional power controller (usually in the HFRP Multimedia block) which needs a simplified power on/off sequence while using Direct Control strategy. Domains using the "simple power sequence" are not backed by the RTFF hardware, have no Bus Protection mechanism, lacks the ISO, PWR_ON, PWR_ON_2ND bits, and therefore get enabled automatically after getting out of reset. This simple power sequence is then a subset of the full one as only needs the enablement of the specific power domain's clock input and reset (where, again, after getting out of reset, the ISO and PWR_ON bits are automatically internally getting flipped) to enable or disable (power on or off). Moreover, the simple power sequence power domains guarantee that they always get enabled/disabled after executing the relevant power sequence (on/off) so, differently from the others, there is also no need to poll for a PWR_ACK. Reviewed-by: Matthias Brugger Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 97 ++++++++++++++++++---- drivers/pmdomain/mediatek/mtk-pm-domains.h | 1 + 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index 8d49d9ee3cd2..632c5dcdceda 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -555,9 +555,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: @@ -589,6 +591,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) @@ -611,14 +648,24 @@ 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 int scpsys_power_on(struct generic_pm_domain *genpd) @@ -641,6 +688,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) 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); @@ -668,9 +717,11 @@ 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)) { + ret = scpsys_sram_enable(pd); + if (ret < 0) + goto err_disable_subsys_clks; + } ret = scpsys_bus_protect_disable(pd, 0); if (ret < 0) @@ -688,7 +739,8 @@ 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)) + 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, @@ -703,16 +755,17 @@ 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_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, @@ -725,15 +778,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); + ret = scpsys_modem_pwrseq_off(pd); + else if (MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) + ret = scpsys_simple_pwrseq_off(pd); else - scpsys_ctl_pwrseq_off(pd); + 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); @@ -1109,6 +1168,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) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h index a5dca24cbc2f..092403de66fa 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.h +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h @@ -17,6 +17,7 @@ #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_CAPS(_scpd, _x) ((_scpd)->data ? \ (_scpd)->data->caps & (_x) : \ (_scpd)->hwv_data->caps & (_x)) From 11c4b1688e7104bb052842292c94e5193c553106 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 9 Jul 2026 10:46:35 +0200 Subject: [PATCH 08/24] pmdomain: mediatek: Add support for MT8196 HFRP DirectCTL domains Add support for the power domains provided by the HFRPSYS Power Controller of the MT8196 SoC. Those control power to the eDP and DP Transmitter IPs. Reviewed-by: Matthias Brugger Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mt8196-pm-domains.h | 27 +++++++++++++++++++ drivers/pmdomain/mediatek/mtk-pm-domains.c | 6 ++++- 2 files changed, 32 insertions(+), 1 deletion(-) 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 632c5dcdceda..8309a4b46afb 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -1071,7 +1071,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); } @@ -1288,6 +1288,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, From 752840328152f0a8b98cc10d7e53cc89a44ef951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Sat, 30 May 2026 17:46:34 -0300 Subject: [PATCH 09/24] pmdomain: bcm: bcm2835-power: Raise ASB poll timeout to 100us MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 18605b1b936b ("pmdomain: bcm: bcm2835-power: Increase ASB control timeout") raised the ASB handshake polling budget from 1us to 5us. Surveying the pmdomain subsystem, 5us is still one of the smallest polling budgets by a wide margin. Comparable handshakes in other drivers use: - 100us : starfive jh71xx-pmu, apple pmgr-pwrstate - 1ms : renesas rcar-sysc, rmobile-sysc (power-on) - 10ms : renesas rcar-gen4-sysc, sunxi sun55i-pck600 - 1s : mediatek mtk-pm-domains, mtk-scpsys Raise the BCM2835 timeout to 100us, matching analogous drivers. 100us is still negligible relative to a power-domain transition and gives the V3D master ASB substantially more headroom to drain under heavy workloads, assuring us that the timeout is enough for any scenario. Signed-off-by: Maíra Canal Acked-by: Florian Fainelli Signed-off-by: Ulf Hansson --- drivers/pmdomain/bcm/bcm2835-power.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/bcm/bcm2835-power.c b/drivers/pmdomain/bcm/bcm2835-power.c index 68a0a7a8cee3..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; From 9956ad2afd44699933188f388e6f1cf1943987b7 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 2 Jun 2026 20:32:48 +0100 Subject: [PATCH 10/24] pmdomain: tegra: Add support for multi-socket platforms On multi-socket platforms each socket has its own BPMP that is registered with the kernel. For such platforms prefix the NUMA ID for each socket to the BPMP powergate name to ensure there is a unique name for each power-domain. Note that we only add the NUMA ID for powergates that return a valid name because an invalid name indicates that the powergate ID is not supported. Note the check for the NULL string is moved into the function tegra_bpmp_powergate_get_name(), because in the multi-socket case we must only add the prefix if we receive a valid name. A NULL string indicates that there is no valid powergate associated with the ID that is being queried. Signed-off-by: Jon Hunter Signed-off-by: Ulf Hansson --- drivers/pmdomain/tegra/powergate-bpmp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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; } From 5250a38c788033ebba1277a96d87b7a25c46bc19 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 9 Jun 2026 22:43:31 -0700 Subject: [PATCH 11/24] dt-bindings: power: qcom,rpmpd: document RPMh power domain for Maili Qualcomm Maili SoC has exactly the same RPMh power domains as Qualcomm Hawi SoC. Add "qcom,maili-rpmhpd" string as a compatible entry for "qcom,hawi-rpmhpd". Signed-off-by: Fenglin Wu Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 4 ++++ 1 file changed, 4 insertions(+) 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 From ce2bf9837092be587050697a4d64ee43a1ead2f2 Mon Sep 17 00:00:00 2001 From: Zhipeng Wang Date: Mon, 20 Apr 2026 11:22:54 +0900 Subject: [PATCH 12/24] pmdomain: imx: Make IMX8M/IMX9 BLK_CTRL tristate Convert IMX8M_BLK_CTRL and IMX9_BLK_CTRL from bool to tristate to allow building as loadable modules. This change is required to support Android devices using Generic Kernel Image (GKI) kernels, where SoC-specific drivers must be built as loadable modules rather than built into the core kernel image. For i.MX8M and i.MX9 devices running Android with GKI kernels, the BLK_CTRL drivers therefore need to be loadable. Without tristate support, power domains cannot be initialized correctly, making these systems non-functional under GKI. Add prompt strings to make these options visible and configurable in menuconfig, keeping them enabled by default on appropriate platforms. Also remove the IMX_GPCV2_PM_DOMAINS dependency from IMX9_BLK_CTRL. This dependency was incorrect from the beginning because i.MX93 uses a different power domain architecture compared to i.MX8M series: - i.MX8M uses GPCv2 (General Power Controller v2) for power domain management, hence IMX8M_BLK_CTRL correctly depends on it. - i.MX93 uses BLK_CTRL directly without GPCv2. The hardware doesn't have GPCv2 at all. Signed-off-by: Zhipeng Wang Reviewed-by: Frank Li Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/Kconfig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/pmdomain/imx/Kconfig b/drivers/pmdomain/imx/Kconfig index 00203615c65e..9168d183b0c5 100644 --- a/drivers/pmdomain/imx/Kconfig +++ b/drivers/pmdomain/imx/Kconfig @@ -10,15 +10,18 @@ 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" From c8e1c83f9ad7d3218fed87bc3fb7587c32d31449 Mon Sep 17 00:00:00 2001 From: Midgy BALON Date: Thu, 9 Jul 2026 01:46:14 +0200 Subject: [PATCH 13/24] pmdomain: rockchip: Add a regulator to the RK3568 NPU power domain The RK3568 NPU rail (vdd_npu) needs to be enabled before the domain is powered on and disabled after it is powered off. Give DOMAIN_RK3568 a regulator parameter (like DOMAIN_RK3588 already has) so the NPU domain can set need_regulator, letting genpd manage the rail wired up as the domain's domain-supply instead of marking it always-on in DT. Suggested-by: Chaoyi Chen Signed-off-by: Midgy BALON Reviewed-by: Sebastian Reichel Reviewed-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- drivers/pmdomain/rockchip/pm-domains.c | 36 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) 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[] = { From 61fe51f6d84674490440bb58ecb8d1ee0031dd99 Mon Sep 17 00:00:00 2001 From: Nikolai Burov Date: Mon, 20 Jul 2026 23:46:46 +0300 Subject: [PATCH 14/24] dt-bindings: power: Add MediaTek MT6858 power domain controller Add a new compatible and document bindings for the power domain controller of the MT6858 SoC. Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Krzysztof Kozlowski Signed-off-by: Nikolai Burov Signed-off-by: Ulf Hansson --- .../power/mediatek,power-controller.yaml | 21 ++++++++++++++++- .../dt-bindings/power/mediatek,mt6858-power.h | 23 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 include/dt-bindings/power/mediatek,mt6858-power.h diff --git a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml index 070c6e5666dc..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 @@ -56,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]+$": @@ -103,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. @@ -156,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/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 */ From 86cb2f65aab3792e0233ae9eeed0005e92ddfe4f Mon Sep 17 00:00:00 2001 From: Nikolai Burov Date: Mon, 20 Jul 2026 23:46:47 +0300 Subject: [PATCH 15/24] pmdomain: mediatek: Add support for secure modem power domain control On recent MediaTek SoCs such as MT6858, the kernel is required to use a secure monitor call (SMC) to enable or disable the modem power domain. The power domain control register can be read, but firmware prevents it from being modified directly. Some other parts of the power sequence, such as setting the ext_buck_iso register, still need to be performed on the kernel side. In preparation for modem support, add a flag to enable this new power sequence for SoCs that need it. Power domains using this flag are not expected to configure any bus protection registers, since these are handled internally by the SMC call. Signed-off-by: Nikolai Burov Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Matthias Brugger Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 50 +++++++++++++++++++--- drivers/pmdomain/mediatek/mtk-pm-domains.h | 1 + include/linux/soc/mediatek/mtk_sip_svc.h | 3 ++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index 8309a4b46afb..5b4d860318a4 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -57,6 +57,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; @@ -668,6 +672,34 @@ static int scpsys_modem_pwrseq_off(struct scpsys_domain *pd) 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) { struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); @@ -686,7 +718,9 @@ 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); @@ -717,7 +751,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) goto err_pwr_ack; } - if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) { + 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; @@ -739,7 +774,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) err_enable_bus_protect: scpsys_bus_protect_enable(pd, 0); err_disable_sram: - if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) + 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)) @@ -761,7 +797,11 @@ static int scpsys_power_off_internal(struct scpsys_domain *pd) if (ret < 0) return ret; - if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) { + 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; @@ -781,7 +821,7 @@ static int scpsys_power_off_internal(struct scpsys_domain *pd) ret = scpsys_modem_pwrseq_off(pd); else if (MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) ret = scpsys_simple_pwrseq_off(pd); - else + else if (!MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) ret = scpsys_ctl_pwrseq_off(pd); if (ret < 0) { diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h index 092403de66fa..8690690335ad 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.h +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h @@ -18,6 +18,7 @@ #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)) 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) From 20b68457538fcaf1dc9a0ab15ce391832dffa292 Mon Sep 17 00:00:00 2001 From: Nikolai Burov Date: Mon, 20 Jul 2026 23:46:48 +0300 Subject: [PATCH 16/24] pmdomain: mediatek: Add support for MT6858 SoC Add support for the power domains found in the MediaTek MT6858 SoC. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nikolai Burov Reviewed-by: Matthias Brugger Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mt6858-pm-domains.h | 466 ++++++++++++++++++ drivers/pmdomain/mediatek/mtk-pm-domains.c | 5 + drivers/pmdomain/mediatek/mtk-pm-domains.h | 5 + 3 files changed, 476 insertions(+) create mode 100644 drivers/pmdomain/mediatek/mt6858-pm-domains.h 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/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index 5b4d860318a4..b43f8b4a6a01 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" @@ -1288,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, diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h index 8690690335ad..45da83e9d649 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.h +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h @@ -66,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, }; From 3ac5e4fe7053db96ff9a386d9929c62ca67e6bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 16 Jul 2026 15:26:03 +0200 Subject: [PATCH 17/24] pmdomain: imx8m{p,}-blk-ctrl: Add MODULE_DESCRIPTION MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without a module description modpost emits a build warning: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pmdomain/imx/imx8m-blk-ctrl.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pmdomain/imx/imx8mp-blk-ctrl.o So add the required string to these two drivers. Signed-off-by: Uwe Kleine-König Reviewed-by: Frank Li Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/imx8m-blk-ctrl.c | 1 + drivers/pmdomain/imx/imx8mp-blk-ctrl.c | 1 + 2 files changed, 2 insertions(+) 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"); From fad41cdd09a2fdd09220d1dfd190e1cbcacc7634 Mon Sep 17 00:00:00 2001 From: Zhipeng Wang Date: Thu, 23 Jul 2026 11:05:22 +0900 Subject: [PATCH 18/24] of: export of_stdout symbol of_stdout is declared extern in include/linux/of.h alongside of_root and of_chosen, but unlike those two it is not exported, preventing modules from referencing it. Export it with EXPORT_SYMBOL_GPL() so drivers that need the stdout device node can be built as modules. Signed-off-by: Zhipeng Wang Acked-by: Rob Herring (Arm) Signed-off-by: Ulf Hansson --- drivers/of/base.c | 1 + 1 file changed, 1 insertion(+) 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; From daa5fba89c27a1ac09918177e564e42eb9d44921 Mon Sep 17 00:00:00 2001 From: Zhipeng Wang Date: Thu, 23 Jul 2026 11:05:23 +0900 Subject: [PATCH 19/24] pmdomain: imx: scu-pd: allow building as a module Convert CONFIG_IMX_SCU_PD from bool to tristate to allow building as a loadable module. This is needed on Android devices using the Generic Kernel Image (GKI), where SoC-specific drivers must be built as modules rather than built into the core kernel image. For i.MX8Q devices running Android with a GKI kernel, the SCU power domain driver must be loadable. Without tristate support, power domains cannot be properly initialized, preventing these systems from functioning under GKI. Use subsys_initcall() so that when built-in the power domain provider probes before its consumers (e.g. the SCU clock driver at device_initcall level), fixing "failed to attached the power domain" warnings at boot. When built as a module, subsys_initcall() is equivalent to module_init(). No module_exit() is provided because the SCU power domain provider is a system-level resource that cannot be safely removed at runtime. Add MODULE_DEVICE_TABLE() for OF-based module autoloading. Signed-off-by: Zhipeng Wang Reviewed-by: Peng Fan Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/Kconfig | 2 +- drivers/pmdomain/imx/scu-pd.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/imx/Kconfig b/drivers/pmdomain/imx/Kconfig index 9168d183b0c5..33e1567e5d17 100644 --- a/drivers/pmdomain/imx/Kconfig +++ b/drivers/pmdomain/imx/Kconfig @@ -24,7 +24,7 @@ config IMX9_BLK_CTRL 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/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"); From aadb7d1f2ca0f5c4c945cca808f9a15b6a42289c Mon Sep 17 00:00:00 2001 From: Zhipeng Wang Date: Thu, 23 Jul 2026 11:05:24 +0900 Subject: [PATCH 20/24] clk: imx: imx8qxp: add soft dependency on SCU power domain driver The i.MX8QXP SCU clock driver manually attaches power domains to each clock device via of_genpd_add_device() in imx_clk_scu_attach_pd(). If the SCU power domain provider has not probed yet, the attachment fails silently with a pr_warn() and the clock device is registered without a power domain. When both drivers are built as modules, there is no implicit dependency to guarantee load order. Add MODULE_SOFTDEP("pre: scu_pd") so that modprobe loads the SCU power domain module before the clock module. Signed-off-by: Zhipeng Wang Reviewed-by: Peng Fan Reviewed-by: Brian Masney Signed-off-by: Ulf Hansson --- drivers/clk/imx/clk-imx8qxp.c | 1 + 1 file changed, 1 insertion(+) 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"); From 2d055fb2ee1d16dab6e6087aeeb49243236dcdea Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Wed, 22 Jul 2026 08:39:31 +0000 Subject: [PATCH 21/24] dt-bindings: power: Add Amlogic A9 power domains Add devicetree binding document and related header file for Amlogic A9 secure power domains. Signed-off-by: Xianwei Zhao Signed-off-by: Ulf Hansson --- .../power/amlogic,meson-sec-pwrc.yaml | 1 + include/dt-bindings/power/amlogic,a9-pwrc.h | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 include/dt-bindings/power/amlogic,a9-pwrc.h diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml index 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/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 From b00065ccf66c1acaca6a9ec730b76c266a1d95ad Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Wed, 22 Jul 2026 08:39:32 +0000 Subject: [PATCH 22/24] pmdomain: amlogic: Add support for A9 power domains controller Add support for the A9 power controller, whose registers are in the secure domain and should be accessed via SMC. Signed-off-by: Xianwei Zhao Reviewed-by: Neil Armstrong Signed-off-by: Ulf Hansson --- drivers/pmdomain/amlogic/meson-secure-pwrc.c | 69 ++++++++++++++++++++ 1 file changed, 69 insertions(+) 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, From 4a096d54f99e1bd6faa4fec49838789fd34e7c8d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 5 Aug 2026 17:20:50 +0200 Subject: [PATCH 23/24] dt-bindings: power: Document Renesas R-Car X5H Module Controller Document support for Renesas R-Car X5H Module Controllers, and add binding definitions for power domains not backed by registers. Signed-off-by: Geert Uytterhoeven Reviewed-by: Marek Vasut Acked-by: Conor Dooley Signed-off-by: Ulf Hansson --- .../bindings/power/renesas,r8a78000-mdlc.yaml | 61 +++++++++++++++++++ .../dt-bindings/power/renesas,r8a78000-mdlc.h | 15 +++++ 2 files changed, 76 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/renesas,r8a78000-mdlc.yaml create mode 100644 include/dt-bindings/power/renesas,r8a78000-mdlc.h 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/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__ */ From 9f79f93963b0fd0a2bcee31d9871d11706caafb3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 11 Aug 2026 21:04:44 +0200 Subject: [PATCH 24/24] pmdomain: renesas: Add R-Car X5H MDLC driver Add a minimal Module Controller driver for the R-Car X5H (R8A78000) SoC. For now this just supports the always-on power domains, and dummy module clocks and resets for the serial console (which is enabled by the boot loader). Signed-off-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/pmdomain/renesas/Kconfig | 4 + drivers/pmdomain/renesas/Makefile | 1 + drivers/pmdomain/renesas/r8a78000-mdlc.c | 325 +++++++++++++++++++++++ drivers/soc/renesas/Kconfig | 1 + 4 files changed, 331 insertions(+) create mode 100644 drivers/pmdomain/renesas/r8a78000-mdlc.c 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/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 2ab150d04bb1..d4055250de72 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -358,6 +358,7 @@ config ARCH_R8A78000 default y if ARCH_RENESAS default ARCH_RENESAS select ARCH_RCAR_GEN5 + select MDLC_R8A78000 help This enables support for the Renesas R-Car X5H SoC.