As per errata
i2487: "the low power modes may inadvertently corrupt DDR contents"
for AM62AX[0] and AM62PX[1].

Based on the recommendation, add SW workaround sequence to
clear data retention latch on every boot and/or abort scenarios to
make sure that latch is initialized to zero rather than unknown value.

Executing this sequence during boot helps to successfully resume the SoCs
from IO+DDR low power mode.
This sequence is executed in partial I/O resume as well as all the
other conditions when reset is issued during low power mode entry/exit
or when the SoC is in idle mode.

The sequence should be executed for SoCs that support IO+DDR mode, that
is AM62AX and AM62PX.

[0]: https://www.ti.com/lit/er/sprz544c/sprz544c.pdf
[1]: https://www.ti.com/lit/er/sprz574b/sprz574b.pdf

Signed-off-by: Akashdeep Kaur <[email protected]>
---

Depends-On: 
https://lore.kernel.org/all/20251210-topic-am62-ioddr-v2025-04-rc1-v7-0-f113b156f...@baylibre.com/

---
 drivers/ram/k3-ddrss/k3-ddrss-lpm.c | 78 +++++++++++++++++++++++++++++
 drivers/ram/k3-ddrss/k3-ddrss-lpm.h |  1 +
 drivers/ram/k3-ddrss/k3-ddrss.c     |  3 ++
 3 files changed, 82 insertions(+)

diff --git a/drivers/ram/k3-ddrss/k3-ddrss-lpm.c 
b/drivers/ram/k3-ddrss/k3-ddrss-lpm.c
index 2556632df88..523f0cb6219 100644
--- a/drivers/ram/k3-ddrss/k3-ddrss-lpm.c
+++ b/drivers/ram/k3-ddrss/k3-ddrss-lpm.c
@@ -19,8 +19,18 @@
 #define K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT            0x43018318
 #define K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT_MW         0x555555
 
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL                     0x43018300
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_LD       BIT(0)
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW          0x55555554
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_MASK     GENMASK(31, 1)
+
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE                 0x43018310
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW              0xDD555555
+#define K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW_MASK GENMASK(31, 0)
+
 #define K3_DDRSS_LPM_TIMEOUT_MS 5000
 #define K3_DDRSS_RETENTION_TIMEOUT_MS 5000
+#define K3_DDRSS_RETENTION_LATCH_CLR_TIMEOUT_MS 500
 
 #define K3_DDRSS_CFG_DENALI_CTL_20                             0x0050
 #define K3_DDRSS_CFG_DENALI_CTL_20_PHY_INDEP_TRAIN_MODE                BIT(24)
@@ -149,6 +159,61 @@ void k3_ddrss_deassert_retention(void)
                                 0);
 }
 
+static void k3_ddrss_clear_retention_latch_and_magic_words(void)
+{
+       int ret;
+
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE,
+                                0,
+                          K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW_MASK,
+                          K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW);
+
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL,
+                                0,
+                          K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_MASK |
+                                  K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_LD,
+                          K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW |
+                                  K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_LD);
+
+       ret = wait_for_bit_32((void *)K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1,
+                             
K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE,
+                             true, K3_DDRSS_RETENTION_LATCH_CLR_TIMEOUT_MS, 
false);
+       if (ret)
+               panic("Timeout during latch clearing sequence %d\n", ret);
+
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL,
+                                0,
+                          K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD |
+                          K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RETENTION_MASK,
+                          K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD);
+
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL,
+                                0,
+                          K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD,
+                          0);
+
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL,
+                                0,
+                          K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_LD,
+                          0);
+
+       ret = wait_for_bit_32((void *)K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1,
+                             
K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE,
+                             false, K3_DDRSS_RETENTION_LATCH_CLR_TIMEOUT_MS, 
false);
+       if (ret)
+               panic("Timeout during latch clearing sequence %d\n", ret);
+
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE,
+                                0,
+                          K3_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW_MASK,
+                          0);
+
+       k3_ddrss_reg_update_bits((void *)K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL,
+                                0,
+                          K3_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_MASK,
+                          0);
+}
+
 static bool k3_ddrss_wkup_conf_canuart_wakeup_active(void)
 {
        u32 active;
@@ -173,3 +238,16 @@ bool k3_ddrss_wkup_conf_boot_is_resume(void)
                k3_ddrss_wkup_conf_canuart_wakeup_active() &&
                k3_ddrss_wkup_conf_canuart_magic_word_set();
 }
+
+void k3_ddrss_run_retention_latch_clear_sequence(void)
+{
+       /*
+        * Workaround of errata i12487
+        * Errata states that During entry to the Deep Sleep or RTC+IO+DDR
+        * low-power modes, SoC may not properly transition the attached
+        * DDR into retention mode, which will lead to corruption of the DDR
+        * data.
+        */
+       if (IS_ENABLED(CONFIG_K3_IODDR))
+               k3_ddrss_clear_retention_latch_and_magic_words();
+}
diff --git a/drivers/ram/k3-ddrss/k3-ddrss-lpm.h 
b/drivers/ram/k3-ddrss/k3-ddrss-lpm.h
index cb2c270b7b2..1c2343924fb 100644
--- a/drivers/ram/k3-ddrss/k3-ddrss-lpm.h
+++ b/drivers/ram/k3-ddrss/k3-ddrss-lpm.h
@@ -7,3 +7,4 @@ void k3_ddrss_self_refresh_exit(void __iomem *ddrss_ctl_cfg);
 void k3_ddrss_lpm_resume(void __iomem *ddrss_ctl_cfg);
 void k3_ddrss_deassert_retention(void);
 bool k3_ddrss_wkup_conf_boot_is_resume(void);
+void k3_ddrss_run_retention_latch_clear_sequence(void);
diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c
index cedec616e23..ebe56da72cf 100644
--- a/drivers/ram/k3-ddrss/k3-ddrss.c
+++ b/drivers/ram/k3-ddrss/k3-ddrss.c
@@ -886,6 +886,9 @@ static int k3_ddrss_probe(struct udevice *dev)
 
        if (is_lpm_resume)
                dev_info(dev, "Detected IO+DDR resume\n");
+       else
+               /* Clear the latch after any reset or partial I/O exit */
+               k3_ddrss_run_retention_latch_clear_sequence();
 
        ddrss->dev = dev;
        ret = k3_ddrss_power_on(ddrss);
-- 
2.34.1

Reply via email to