[PATCH v6 03/13] ram: k3-ddrss: Add support for DDR in self-refresh

Kumar, Udit u-kumar1 at ti.com
Mon Nov 24 04:56:05 CET 2025


On 11/20/2025 9:33 PM, Markus Schneider-Pargmann (TI.com) wrote:
> 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.
>
> Tested-by: Anshul Dalal <anshuld at ti.com>
> Reviewed-by: Dhruva Gole <d-gole at ti.com>
> Signed-off-by: Markus Schneider-Pargmann (TI.com) <msp at baylibre.com>
> ---
>   drivers/ram/k3-ddrss/k3-ddrss.c | 180 ++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 180 insertions(+)
>
> diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c
> index 5144470b931384534e78fbfcea5e66b7083c9f07..e049b36519521213260da7aef933236b62a2b33b 100644
> --- a/drivers/ram/k3-ddrss/k3-ddrss.c
> +++ b/drivers/ram/k3-ddrss/k3-ddrss.c
> @@ -57,6 +57,19 @@
>   #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			0x4301830c
> +#define K3_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE	BIT(0)
> +
> +#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_DDRSS_LPM_TIMEOUT_MS 5000
> +#define K3_DDRSS_RETENTION_TIMEOUT_MS 5000

Please move add LPM related code into different file.

Here LPM and DDR code is getting mixed into DDR driver.


> +
>   #define SINGLE_DDR_SUBSYSTEM	0x1
>   #define MULTI_DDR_SUBSYSTEM	0x2
>   #define MAX_MULTI_DDR 4
> @@ -211,6 +224,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) {\
> @@ -864,6 +903,132 @@ 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)
> +{
> +	int ret;
> +	unsigned long timeout_start;
> +
> +	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);
> +	ret = wait_for_bit_32(ddrss->ddrss_ctl_cfg + K3_DDRSS_CFG_DENALI_CTL_345,
> +			      (1 << K3_DDRSS_CFG_DENALI_CTL_345_INT_STATUS_LOWPOWER_SHIFT),
> +			      true, 5000, false);
> +	if (ret)
> +		panic("Failed waiting for low power command %d\n", ret);
> +
> +	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);
> +
> +	timeout_start = get_timer(0);
> +	while (1) {
> +		if (get_timer(timeout_start) > K3_DDRSS_LPM_TIMEOUT_MS)
> +			panic("Failed waiting for low power state\n");
> +
> +		if ((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)
> +			break;
> +	}
> +}
> +
> +static void k3_ddrss_deassert_retention(struct k3_ddrss_desc *ddrss)
> +{
> +	int ret;
> +
> +	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);
> +
> +	ret = wait_for_bit_32((void *)K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL,
> +			      K3_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD,
> +			      true, K3_DDRSS_RETENTION_TIMEOUT_MS, false);
> +	if (ret)
> +		panic("Failed waiting for latching of retention %d\n", ret);
> +
> +	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((void *)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((void *)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)
>   {
>   	u64 end, bank0, bank1, bank0_size;
> @@ -873,6 +1038,7 @@ static int k3_ddrss_probe(struct udevice *dev)
>   	__maybe_unused u32 inst;
>   	__maybe_unused struct k3_ddrss_ecc_region *range = ddrss->ecc_ranges;
>   	__maybe_unused struct k3_msmc *msmc_parent = NULL;
> +	bool is_lpm_resume;
>   
>   	debug("%s(dev=%p)\n", __func__, dev);
>   
> @@ -880,6 +1046,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)
> @@ -895,12 +1066,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.",
>


More information about the U-Boot mailing list