In IO+DDR the DDR is kept in self-refresh while the SoC cores are
powered off completely. During boot the normal initialization routine of
DDR is slightly different to exit self-refresh and keep the DDR contents.

Signed-off-by: Markus Schneider-Pargmann <m...@baylibre.com>
---
 drivers/ram/k3-ddrss/k3-ddrss.c | 165 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)

diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c
index 
ff87faf6a22419e54d3639817ad2b884a97a3911..d7ae6e9ef24d5aebdb61656e6ca797b1799ca0f6
 100644
--- a/drivers/ram/k3-ddrss/k3-ddrss.c
+++ b/drivers/ram/k3-ddrss/k3-ddrss.c
@@ -57,6 +57,16 @@
 #define DDRSS_V2A_INT_SET_REG_ECC2BERR_EN      BIT(4)
 #define DDRSS_V2A_INT_SET_REG_ECCM1BERR_EN     BIT(5)
 
+#define K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL                       0x430080d0
+#define K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD           BIT(31)
+#define K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RETENTION_MASK   GENMASK(3, 0)
+
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1                    0x430c
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE    BIT(0)
+
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT            0x4318
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT_MW         0x555555
+
 #define SINGLE_DDR_SUBSYSTEM   0x1
 #define MULTI_DDR_SUBSYSTEM    0x2
 
@@ -189,6 +199,32 @@ struct reginitdata {
 #define DENALI_CTL_0_DRAM_CLASS_DDR4           0xA
 #define DENALI_CTL_0_DRAM_CLASS_LPDDR4         0xB
 
+#define K3_DDRSS_CFG_DENALI_CTL_20                             0x0050
+#define K3_DDRSS_CFG_DENALI_CTL_20_PHY_INDEP_TRAIN_MODE                BIT(24)
+#define K3_DDRSS_CFG_DENALI_CTL_21                             0x0054
+#define K3_DDRSS_CFG_DENALI_CTL_21_PHY_INDEP_INIT_MODE         BIT(8)
+#define K3_DDRSS_CFG_DENALI_CTL_106                            0x01a8
+#define K3_DDRSS_CFG_DENALI_CTL_106_PWRUP_SREFRESH_EXIT                BIT(16)
+#define K3_DDRSS_CFG_DENALI_CTL_160                            0x0280
+#define K3_DDRSS_CFG_DENALI_CTL_160_LP_CMD_MASK                        
GENMASK(14, 8)
+#define K3_DDRSS_CFG_DENALI_CTL_160_LP_CMD_ENTRY               BIT(9)
+#define K3_DDRSS_CFG_DENALI_CTL_169                            0x02a4
+#define K3_DDRSS_CFG_DENALI_CTL_169_LP_AUTO_EXIT_EN_MASK       GENMASK(27, 24)
+#define K3_DDRSS_CFG_DENALI_CTL_169_LP_AUTO_ENTRY_EN_MASK      GENMASK(19, 16)
+#define K3_DDRSS_CFG_DENALI_CTL_169_LP_STATE_MASK              GENMASK(14, 8)
+#define K3_DDRSS_CFG_DENALI_CTL_169_LP_STATE_SHIFT             8
+#define K3_DDRSS_CFG_DENALI_CTL_345                            0x0564
+#define K3_DDRSS_CFG_DENALI_CTL_345_INT_STATUS_LOWPOWER_SHIFT  16
+#define K3_DDRSS_CFG_DENALI_CTL_353                            0x0584
+#define K3_DDRSS_CFG_DENALI_CTL_353_INT_ACK_LOWPOWER_SHIFT     16
+#define K3_DDRSS_CFG_DENALI_PI_6                               0x2018
+#define K3_DDRSS_CFG_DENALI_PI_6_PI_DFI_PHYMSTR_STATE_SEL_R    BIT(8)
+#define K3_DDRSS_CFG_DENALI_PI_146                             0x2248
+#define K3_DDRSS_CFG_DENALI_PI_150                             0x2258
+#define K3_DDRSS_CFG_DENALI_PI_150_PI_DRAM_INIT_EN             BIT(8)
+#define K3_DDRSS_CFG_DENALI_PHY_1820                           0x5C70
+#define K3_DDRSS_CFG_DENALI_PHY_1820_SET_DFI_INPUT_2_SHIFT     16
+
 #define TH_OFFSET_FROM_REG(REG, SHIFT, offset) do {\
        char *i, *pstr = xstr(REG); offset = 0;\
        for (i = &pstr[SHIFT]; *i != '\0'; ++i) {\
@@ -775,10 +811,125 @@ static void k3_ddrss_lpddr4_ecc_init(struct 
k3_ddrss_desc *ddrss)
        writel(val, base + DDRSS_ECC_CTRL_REG);
 }
 
+static void k3_ddrss_reg_update_bits(void __iomem *addr, u32 offset, u32 mask, 
u32 set)
+{
+       u32 val = readl(addr + offset);
+
+       val &= ~mask;
+       val |= set;
+       writel(val, addr + offset);
+}
+
+static void k3_ddrss_self_refresh_exit(struct k3_ddrss_desc *ddrss)
+{
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_CTL_169,
+                                
K3_DDRSS_CFG_DENALI_CTL_169_LP_AUTO_EXIT_EN_MASK |
+                                
K3_DDRSS_CFG_DENALI_CTL_169_LP_AUTO_ENTRY_EN_MASK,
+                                0x0);
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_PHY_1820,
+                                0,
+                                BIT(2) << 
K3_DDRSS_CFG_DENALI_PHY_1820_SET_DFI_INPUT_2_SHIFT);
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_CTL_106,
+                                0,
+                                
K3_DDRSS_CFG_DENALI_CTL_106_PWRUP_SREFRESH_EXIT);
+       writel(0, ddrss->ddrss_ctl_cfg + K3_DDRSS_CFG_DENALI_PI_146);
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_PI_150,
+                                K3_DDRSS_CFG_DENALI_PI_150_PI_DRAM_INIT_EN,
+                                0x0);
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_PI_6,
+                                0,
+                                
K3_DDRSS_CFG_DENALI_PI_6_PI_DFI_PHYMSTR_STATE_SEL_R);
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_CTL_21,
+                                K3_DDRSS_CFG_DENALI_CTL_21_PHY_INDEP_INIT_MODE,
+                                0);
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_CTL_20,
+                                0,
+                                
K3_DDRSS_CFG_DENALI_CTL_20_PHY_INDEP_TRAIN_MODE);
+}
+
+static void k3_ddrss_lpm_resume(struct k3_ddrss_desc *ddrss)
+{
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_CTL_160,
+                                K3_DDRSS_CFG_DENALI_CTL_160_LP_CMD_MASK,
+                                K3_DDRSS_CFG_DENALI_CTL_160_LP_CMD_ENTRY);
+       while (!(readl(ddrss->ddrss_ctl_cfg + K3_DDRSS_CFG_DENALI_CTL_345) &
+                (1 << K3_DDRSS_CFG_DENALI_CTL_345_INT_STATUS_LOWPOWER_SHIFT)))
+               ;
+
+       k3_ddrss_reg_update_bits(ddrss->ddrss_ctl_cfg,
+                                K3_DDRSS_CFG_DENALI_CTL_353,
+                                0,
+                                1 << 
K3_DDRSS_CFG_DENALI_CTL_353_INT_ACK_LOWPOWER_SHIFT);
+       while ((readl(ddrss->ddrss_ctl_cfg + K3_DDRSS_CFG_DENALI_CTL_169) &
+               K3_DDRSS_CFG_DENALI_CTL_169_LP_STATE_MASK) !=
+              0x40 << K3_DDRSS_CFG_DENALI_CTL_169_LP_STATE_SHIFT)
+               ;
+}
+
+static void k3_ddrss_deassert_retention(struct k3_ddrss_desc *ddrss)
+{
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL,
+                                0x0,
+                                K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD |
+                                
K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RETENTION_MASK,
+                                0);
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL,
+                                0x0,
+                                K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD,
+                                K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD);
+
+       while (true) {
+               u32 val;
+
+               val = readl((void *)K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL);
+               if (val & K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD)
+                       break;
+       }
+
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL,
+                                0x0,
+                                K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD,
+                                0);
+}
+
+static bool k3_ddrss_wkup_conf_canuart_wakeup_active(struct k3_ddrss_desc 
*ddrss)
+{
+       u32 active;
+
+       active = readl(ddrss->ddrss_ctrl_mmr + 
K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1);
+
+       return !!(active & K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE);
+}
+
+static bool k3_ddrss_wkup_conf_canuart_magic_word_set(struct k3_ddrss_desc 
*ddrss)
+{
+       u32 magic_word;
+
+       magic_word = readl(ddrss->ddrss_ctrl_mmr + 
K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT);
+
+       return magic_word == K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT_MW;
+}
+
+static bool k3_ddrss_wkup_conf_boot_is_resume(struct k3_ddrss_desc *ddrss)
+{
+       return IS_ENABLED(CONFIG_K3_IODDR) &&
+               k3_ddrss_wkup_conf_canuart_wakeup_active(ddrss) &&
+               k3_ddrss_wkup_conf_canuart_magic_word_set(ddrss);
+}
+
 static int k3_ddrss_probe(struct udevice *dev)
 {
        int ret;
        struct k3_ddrss_desc *ddrss = dev_get_priv(dev);
+       bool is_lpm_resume;
 
        debug("%s(dev=%p)\n", __func__, dev);
 
@@ -786,6 +937,11 @@ static int k3_ddrss_probe(struct udevice *dev)
        if (ret)
                return ret;
 
+       is_lpm_resume = k3_ddrss_wkup_conf_boot_is_resume(ddrss);
+
+       if (is_lpm_resume)
+               dev_info(dev, "Detected IO+DDR resume\n");
+
        ddrss->dev = dev;
        ret = k3_ddrss_power_on(ddrss);
        if (ret)
@@ -801,12 +957,21 @@ static int k3_ddrss_probe(struct udevice *dev)
        k3_lpddr4_init(ddrss);
        k3_lpddr4_hardware_reg_init(ddrss);
 
+       if (is_lpm_resume)
+               k3_ddrss_self_refresh_exit(ddrss);
+
        ret = k3_ddrss_init_freq(ddrss);
        if (ret)
                return ret;
 
+       if (is_lpm_resume)
+               k3_ddrss_deassert_retention(ddrss);
+
        k3_lpddr4_start(ddrss);
 
+       if (is_lpm_resume)
+               k3_ddrss_lpm_resume(ddrss);
+
        if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) {
                if (!ddrss->ddrss_ss_cfg) {
                        printf("%s: ss_cfg is required if ecc is enabled but 
not provided.",

-- 
2.47.2

Reply via email to