[PATCH v2 1/4] ram: ti: k3-ddrss: Add workaround sequence for errata i2487
Akashdeep Kaur
a-kaur at ti.com
Tue Mar 10 11:06:11 CET 2026
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
Tested-by Sebin Francis <sebin.francis at ti.com>
Signed-off-by: Akashdeep Kaur <a-kaur at ti.com>
---
drivers/ram/k3-ddrss/k3-ddrss-lpm.c | 80 +++++++++++++++++++++++++++++
drivers/ram/k3-ddrss/k3-ddrss-lpm.h | 1 +
drivers/ram/k3-ddrss/k3-ddrss.c | 5 ++
3 files changed, 86 insertions(+)
diff --git a/drivers/ram/k3-ddrss/k3-ddrss-lpm.c b/drivers/ram/k3-ddrss/k3-ddrss-lpm.c
index 20181d0a876..85fceb080d2 100644
--- a/drivers/ram/k3-ddrss/k3-ddrss-lpm.c
+++ b/drivers/ram/k3-ddrss/k3-ddrss-lpm.c
@@ -25,6 +25,15 @@
#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT 0x43018318
#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT_MW 0x555555
+#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL 0x43018300
+#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_LD BIT(0)
+#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW 0x55555554
+#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_MASK GENMASK(31, 1)
+
+#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE 0x43018310
+#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW 0xDD555555
+#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW_MASK GENMASK(31, 0)
+
#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
@@ -51,6 +60,7 @@
#define K3_DDRSS_CFG_DENALI_PHY_1820 0x5C70
#define K3_DDRSS_CFG_DENALI_PHY_1820_SET_DFI_INPUT_2_SHIFT 16
+#define AM62XX_WKUP_CTRL_DDRSS_RETENTION_LATCH_CLR_TIMEOUT_MS 500
#define AM62XX_WKUP_CTRL_DDRSS_RETENTION_TIMEOUT_MS 5000
#define K3_DDRSS_LPM_TIMEOUT_MS 5000
@@ -179,3 +189,73 @@ bool am62xx_wkup_conf_boot_is_resume(void)
am62xx_wkup_conf_canuart_wakeup_active() &&
am62xx_wkup_conf_canuart_magic_word_set();
}
+
+static void am62xx_ddrss_clear_retention_latch_and_magic_words(void)
+{
+ int ret;
+
+ k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE,
+ 0,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW_MASK,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW);
+
+ k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL,
+ 0,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_MASK |
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_LD,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW |
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_LD);
+
+ ret = wait_for_bit_32((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_STAT1,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE,
+ true, AM62XX_WKUP_CTRL_DDRSS_RETENTION_LATCH_CLR_TIMEOUT_MS,
+ false);
+ if (ret)
+ panic("Timeout during latch clearing sequence %d\n", ret);
+
+ k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL,
+ 0,
+ AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD |
+ AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RETENTION_MASK,
+ AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD);
+
+ k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL,
+ 0,
+ AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD,
+ 0);
+
+ k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL,
+ 0,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_LD,
+ 0);
+
+ ret = wait_for_bit_32((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_STAT1,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE,
+ false, AM62XX_WKUP_CTRL_DDRSS_RETENTION_LATCH_CLR_TIMEOUT_MS,
+ false);
+ if (ret)
+ panic("Timeout during latch clearing sequence %d\n", ret);
+
+ k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE,
+ 0,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_MW_MASK,
+ 0);
+
+ k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL,
+ 0,
+ AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_CTRL_MW_MASK,
+ 0);
+}
+
+void am62xx_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))
+ am62xx_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 742d224327e..0b292d06138 100644
--- a/drivers/ram/k3-ddrss/k3-ddrss-lpm.h
+++ b/drivers/ram/k3-ddrss/k3-ddrss-lpm.h
@@ -16,5 +16,6 @@ void k3_ddrss_self_refresh_exit(void __iomem *ddrss_ctl_cfg);
void k3_ddrss_lpm_resume(void __iomem *ddrss_ctl_cfg);
void am62xx_ddrss_deassert_retention(void);
bool am62xx_wkup_conf_boot_is_resume(void);
+void am62xx_ddrss_run_retention_latch_clear_sequence(void);
#endif /* _K3_DDRSS_LPM_H_ */
diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c
index 27823e91d5e..1c313948611 100644
--- a/drivers/ram/k3-ddrss/k3-ddrss.c
+++ b/drivers/ram/k3-ddrss/k3-ddrss.c
@@ -122,6 +122,7 @@ struct k3_ddrss_data {
u32 flags;
bool (*is_lpm_resume)(void);
void (*ddrss_deassert_retention)(void);
+ void (*ddrss_clear_retention_latch)(void);
};
enum ecc_enable {
@@ -888,6 +889,9 @@ static int k3_ddrss_probe(struct udevice *dev)
is_lpm_resume = ddrss_data->is_lpm_resume && ddrss_data->is_lpm_resume();
if (is_lpm_resume)
dev_info(dev, "Detected IO+DDR resume\n");
+ else if (ddrss_data->ddrss_clear_retention_latch)
+ /* Clear the latch after any reset or partial I/O exit */
+ ddrss_data->ddrss_clear_retention_latch();
ddrss->dev = dev;
ret = k3_ddrss_power_on(ddrss);
@@ -1042,6 +1046,7 @@ static const struct k3_ddrss_data am62xx_data = {
.flags = SINGLE_DDR_SUBSYSTEM,
.is_lpm_resume = am62xx_wkup_conf_boot_is_resume,
.ddrss_deassert_retention = am62xx_ddrss_deassert_retention,
+ .ddrss_clear_retention_latch = am62xx_ddrss_run_retention_latch_clear_sequence,
};
static const struct k3_ddrss_data j721s2_data = {
--
2.34.1
More information about the U-Boot
mailing list