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

