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 <[email protected]> Signed-off-by: Akashdeep Kaur <[email protected]> --- 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

