Some MediaTek power domains cannot be brought up with the plain
sram_pdn sequence the driver implements today. Two variations exist,
matching the MTK_SCPD_SRAM_ISO and MTK_SCPD_SRAM_PDN_INVERTED flags in
the Linux driver:

  - the domain's SRAM sits behind an isolation cell that has to be
    released once the SRAM is powered, and re-asserted before powering
    it down again, and

  - the sram_pdn bits are active low, so they are set to power the SRAM
    up rather than cleared, and the ack then reads back as all ones.

Add a caps field to struct mtk_scp_domain_data and implement both
variations in the power on and power off paths, so that a per-SoC driver
can describe such a domain.

Signed-off-by: David Lechner <[email protected]>
---
 drivers/power/domain/mtk-power-domain.c | 51 +++++++++++++++++++++++++++------
 drivers/power/domain/mtk-power-domain.h | 17 +++++++++++
 2 files changed, 60 insertions(+), 8 deletions(-)

diff --git a/drivers/power/domain/mtk-power-domain.c 
b/drivers/power/domain/mtk-power-domain.c
index 768ab8bab9c..60dd9ee9d2f 100644
--- a/drivers/power/domain/mtk-power-domain.c
+++ b/drivers/power/domain/mtk-power-domain.c
@@ -9,6 +9,7 @@
 #include <regmap.h>
 #include <syscon.h>
 #include <asm/io.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/iopoll.h>
 #include <dm/device_compat.h>
@@ -240,13 +241,32 @@ static int mtk_scpsys_power_on(struct power_domain 
*power_domain)
        if (ret)
                return ret;
 
-       val &= ~data->sram_pdn_bits;
-       writel(val, ctl_addr);
-
-       ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100);
+       /*
+        * An inverted sram_pdn bit is set, not cleared, to power the SRAM up,
+        * and the ack is then expected to read back as all ones.
+        */
+       if (data->caps & MTK_SCPD_SRAM_PDN_INVERTED) {
+               val |= data->sram_pdn_bits;
+               writel(val, ctl_addr);
+               ret = readl_poll_timeout(ctl_addr, tmp,
+                                        (tmp & pdn_ack) == pdn_ack, 100);
+       } else {
+               val &= ~data->sram_pdn_bits;
+               writel(val, ctl_addr);
+               ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100);
+       }
        if (ret < 0)
                return ret;
 
+       /* Release the SRAM isolation now that it is powered */
+       if (data->caps & MTK_SCPD_SRAM_ISO) {
+               val |= PWR_SRAM_ISOINT_B_BIT;
+               writel(val, ctl_addr);
+               udelay(1);
+               val &= ~PWR_SRAM_CLKISO_BIT;
+               writel(val, ctl_addr);
+       }
+
        if (data->bus_prot_mask) {
                ret = mtk_infracfg_clear_bus_protection(infracfg,
                                                        data->bus_prot_mask);
@@ -295,11 +315,26 @@ static int mtk_scpsys_power_off(struct power_domain 
*power_domain)
                return ret;
 
        val = readl(ctl_addr);
-       val |= data->sram_pdn_bits;
-       writel(val, ctl_addr);
 
-       ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack,
-                                100);
+       /* Isolate the SRAM again before powering it down */
+       if (data->caps & MTK_SCPD_SRAM_ISO) {
+               val |= PWR_SRAM_CLKISO_BIT;
+               writel(val, ctl_addr);
+               udelay(1);
+               val &= ~PWR_SRAM_ISOINT_B_BIT;
+               writel(val, ctl_addr);
+       }
+
+       if (data->caps & MTK_SCPD_SRAM_PDN_INVERTED) {
+               val &= ~data->sram_pdn_bits;
+               writel(val, ctl_addr);
+               ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100);
+       } else {
+               val |= data->sram_pdn_bits;
+               writel(val, ctl_addr);
+               ret = readl_poll_timeout(ctl_addr, tmp,
+                                        (tmp & pdn_ack) == pdn_ack, 100);
+       }
        if (ret < 0)
                return ret;
 
diff --git a/drivers/power/domain/mtk-power-domain.h 
b/drivers/power/domain/mtk-power-domain.h
index 06760286a3a..fa5c12b47d3 100644
--- a/drivers/power/domain/mtk-power-domain.h
+++ b/drivers/power/domain/mtk-power-domain.h
@@ -39,6 +39,22 @@ struct udevice;
 #define PWR_ON_BIT             BIT(2)
 #define PWR_ON_2ND_BIT         BIT(3)
 #define PWR_CLK_DIS_BIT                BIT(4)
+#define PWR_SRAM_CLKISO_BIT    BIT(5)
+#define PWR_SRAM_ISOINT_B_BIT  BIT(6)
+
+/*
+ * Domain capabilities, matching the semantics of the MTK_SCPD_* flags in the
+ * Linux driver. The values are this driver's own: only the handful of flags
+ * U-Boot has a use for are defined, so they are packed from bit 0 rather than
+ * carrying Linux's numbering.
+ *
+ * SRAM_ISO:           the domain's SRAM sits behind an isolation cell that
+ *                     has to be released separately once it is powered.
+ * SRAM_PDN_INVERTED:  @sram_pdn_bits is active low, so the bit is set to
+ *                     power the SRAM up rather than cleared.
+ */
+#define MTK_SCPD_SRAM_ISO              BIT(0)
+#define MTK_SCPD_SRAM_PDN_INVERTED     BIT(1)
 
 #define PWR_STATUS_CONN                BIT(1)
 #define PWR_STATUS_DISP                BIT(3)
@@ -100,6 +116,7 @@ struct mtk_scpsys_bus_prot_data {
 
 struct mtk_scp_domain_data {
        u32 sta_mask;
+       u32 caps;
        int ctl_offs;
        u32 sram_pdn_bits;
        u32 sram_pdn_ack_bits;

-- 
2.43.0

Reply via email to