[PATCH v1 06/13] pmdomain: mediatek: Add support for RTFF Hardware in MT8196/MT6991
From: AngeloGioacchino Del Regno
Date: Mon Jun 23 2025 - 08:15:18 EST
New generation SoCs use a new RTFF Hardware to save power during
operation of various IPs, other than managing isolation of the
internal buck converters during powerup/down of power domains.
Since some of the power domains need different RTFF handling, add
a new scpys_rtff_type enumeration and hold the value for each
power domain in struct scpsys_domain_data.
If RTFF HW is available, the RTFF additional power sequences are
handled in scpsys_ctl_pwrseq_{on,off}().
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@xxxxxxxxxxxxx>
---
drivers/pmdomain/mediatek/mtk-pm-domains.c | 94 +++++++++++++++++++++-
drivers/pmdomain/mediatek/mtk-pm-domains.h | 18 +++++
2 files changed, 111 insertions(+), 1 deletion(-)
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c
index d14616d4aaab..8711773e75d4 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.c
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c
@@ -39,6 +39,12 @@
#define PWR_SRAM_CLKISO_BIT BIT(5)
#define PWR_SRAM_ISOINT_B_BIT BIT(6)
+#define PWR_RTFF_SAVE BIT(24)
+#define PWR_RTFF_NRESTORE BIT(25)
+#define PWR_RTFF_CLK_DIS BIT(26)
+#define PWR_RTFF_SAVE_FLAG BIT(27)
+#define PWR_RTFF_UFS_CLK_DIS BIT(28)
+
struct scpsys_domain {
struct generic_pm_domain genpd;
const struct scpsys_domain_data *data;
@@ -247,7 +253,7 @@ static int scpsys_regulator_disable(struct regulator *supply)
static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd)
{
struct scpsys *scpsys = pd->scpsys;
- bool tmp;
+ bool do_rtff_nrestore, tmp;
int ret;
/* subsys power on */
@@ -260,10 +266,72 @@ static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd)
if (ret < 0)
return ret;
+ if (pd->data->rtff_type == SCPSYS_RTFF_TYPE_PCIE_PHY)
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_CLK_DIS);
+
regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT);
regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT);
+
+ /* Wait for RTFF HW to sync buck isolation state if this is PCIe PHY RTFF */
+ if (pd->data->rtff_type == SCPSYS_RTFF_TYPE_PCIE_PHY)
+ udelay(5);
+
regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT);
+ /*
+ * RTFF HW state may be modified by secure world or remote processors.
+ *
+ * With the only exception of STOR_UFS, which always needs save/restore,
+ * check if this power domain's RTFF is already on before trying to do
+ * the NRESTORE procedure, otherwise the system will lock up.
+ */
+ switch (pd->data->rtff_type) {
+ case SCPSYS_RTFF_TYPE_GENERIC:
+ case SCPSYS_RTFF_TYPE_PCIE_PHY:
+ {
+ u32 ctl_status;
+
+ regmap_read(scpsys->base, pd->data->ctl_offs, &ctl_status);
+ do_rtff_nrestore = ctl_status & PWR_RTFF_SAVE_FLAG;
+ break;
+ }
+ case SCPSYS_RTFF_TYPE_STOR_UFS:
+ /* STOR_UFS always needs NRESTORE */
+ do_rtff_nrestore = true;
+ break;
+ default:
+ do_rtff_nrestore = false;
+ break;
+ }
+
+ /* Return early if RTFF NRESTORE shall not be done */
+ if (!do_rtff_nrestore)
+ return 0;
+
+ switch (pd->data->rtff_type) {
+ case SCPSYS_RTFF_TYPE_GENERIC:
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_SAVE_FLAG);
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_CLK_DIS);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_NRESTORE);
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_NRESTORE);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_CLK_DIS);
+ break;
+ case SCPSYS_RTFF_TYPE_PCIE_PHY:
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_SAVE_FLAG);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_NRESTORE);
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_NRESTORE);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_CLK_DIS);
+ break;
+ case SCPSYS_RTFF_TYPE_STOR_UFS:
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_UFS_CLK_DIS);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_NRESTORE);
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_NRESTORE);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_UFS_CLK_DIS);
+ break;
+ default:
+ break;
+ }
+
return 0;
}
@@ -271,8 +339,32 @@ static void scpsys_ctl_pwrseq_off(struct scpsys_domain *pd)
{
struct scpsys *scpsys = pd->scpsys;
+ switch (pd->data->rtff_type) {
+ case SCPSYS_RTFF_TYPE_GENERIC:
+ case SCPSYS_RTFF_TYPE_PCIE_PHY:
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_CLK_DIS);
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_SAVE);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_SAVE);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_CLK_DIS);
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_SAVE_FLAG);
+ break;
+ case SCPSYS_RTFF_TYPE_STOR_UFS:
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_UFS_CLK_DIS);
+ regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_SAVE);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_SAVE);
+ regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_UFS_CLK_DIS);
+ break;
+ default:
+ break;
+ }
+
/* subsys power off */
regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT);
+
+ /* Wait for RTFF HW to sync buck isolation state if this is PCIe PHY RTFF */
+ if (pd->data->rtff_type == SCPSYS_RTFF_TYPE_PCIE_PHY)
+ udelay(1);
+
regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT);
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);
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h
index 4abdc8de25c8..e1dae6409d4a 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.h
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h
@@ -108,6 +108,22 @@ struct scpsys_bus_prot_data {
u8 flags;
};
+/**
+ * enum scpsys_rtff_type - Type of RTFF Hardware for power domain
+ * @SCPSYS_RTFF_NONE: RTFF HW not present or domain not RTFF managed
+ * @SCPSYS_RTFF_TYPE_GENERIC: Non-CPU, peripheral-generic RTFF HW
+ * @SCPSYS_RTFF_TYPE_PCIE_PHY: PCI-Express PHY specific RTFF HW
+ * @SCPSYS_RTFF_TYPE_STOR_UFS: Storage (UFS) specific RTFF HW
+ * @SCPSYS_RTFF_TYPE_MAX: Number of supported RTFF HW Types
+ */
+enum scpsys_rtff_type {
+ SCPSYS_RTFF_NONE = 0,
+ SCPSYS_RTFF_TYPE_GENERIC,
+ SCPSYS_RTFF_TYPE_PCIE_PHY,
+ SCPSYS_RTFF_TYPE_STOR_UFS,
+ SCPSYS_RTFF_TYPE_MAX
+};
+
/**
* struct scpsys_domain_data - scp domain data for power on/off flow
* @name: The name of the power domain.
@@ -118,6 +134,7 @@ struct scpsys_bus_prot_data {
* @ext_buck_iso_offs: The offset for external buck isolation
* @ext_buck_iso_mask: The mask for external buck isolation
* @caps: The flag for active wake-up action.
+ * @rtff_type: The power domain RTFF HW type
* @bp_cfg: bus protection configuration for any subsystem
*/
struct scpsys_domain_data {
@@ -129,6 +146,7 @@ struct scpsys_domain_data {
int ext_buck_iso_offs;
u32 ext_buck_iso_mask;
u16 caps;
+ enum scpsys_rtff_type rtff_type;
const struct scpsys_bus_prot_data bp_cfg[SPM_MAX_BUS_PROT_DATA];
int pwr_sta_offs;
int pwr_sta2nd_offs;
--
2.49.0