From: Thomas Richard <[email protected]> Add functions that implements retention exit sequence of DDR.
Typical DDR resume sequence is: - exit DDR from retention - de-assert the DDR_RET pin - restore DDR max frequency - exit DDR from low power We have to separate each action in order to de-assert the DDR_RET pins at the right time, because the DDR_RET pins are all tied together in hardware. Additional interleaving to be taken care and configuring of PMIC differs device to device depending on number of PMIC. Signed-off-by: Thomas Richard <[email protected]> Signed-off-by: Prasanth Babu Mantena <[email protected]> --- arch/arm/mach-k3/include/mach/k3-ddr.h | 16 ++ drivers/ram/k3-ddrss/k3-ddrss.c | 253 +++++++++++++++++++++++++ drivers/ram/k3-ddrss/lpddr4_k3_reg.h | 120 ++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 drivers/ram/k3-ddrss/lpddr4_k3_reg.h diff --git a/arch/arm/mach-k3/include/mach/k3-ddr.h b/arch/arm/mach-k3/include/mach/k3-ddr.h index 207e60b2763..c82c25ba285 100644 --- a/arch/arm/mach-k3/include/mach/k3-ddr.h +++ b/arch/arm/mach-k3/include/mach/k3-ddr.h @@ -7,6 +7,7 @@ #define _K3_DDR_H_ #include <spl.h> +#include <compiler.h> /* We need 3 extra entries for: * SoC peripherals, flash and the sentinel value. @@ -14,10 +15,25 @@ #define K3_MEM_MAP_LEN ((CONFIG_NR_DRAM_BANKS) + 3) #define K3_MEM_MAP_FIRST_BANK_IDX 2 +struct k3_ddrss_regs { + u32 ctl_141; + u32 phy_1305; + u32 ctl_88; + u32 pi_134; + u32 pi_7; + u32 ctl_20; + u32 wdqlvl_f1; + u32 wdqlvl_f2; +}; + int dram_init(void); int dram_init_banksize(void); void fixup_ddr_driver_for_ecc(struct spl_image_info *spl_image); void fixup_memory_node(struct spl_image_info *spl_image); +/* LPDDR4 power management functions */ +void k3_ddrss_lpddr4_exit_retention(struct udevice *dev, struct k3_ddrss_regs *regs); +void k3_ddrss_lpddr4_change_freq(struct udevice *dev); +void k3_ddrss_lpddr4_exit_low_power(struct udevice *dev, struct k3_ddrss_regs *regs); #endif /* _K3_DDR_H_ */ diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 5144470b931..140bd7678bb 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -19,11 +19,13 @@ #include <power-domain.h> #include <wait_bit.h> #include <power/regulator.h> +#include <mach/k3-ddr.h> #include "lpddr4_obj_if.h" #include "lpddr4_if.h" #include "lpddr4_structs_if.h" #include "lpddr4_ctl_regs.h" +#include "lpddr4_k3_reg.h" #define SRAM_MAX 512 @@ -438,6 +440,257 @@ static int k3_ddrss_ofdata_to_priv(struct udevice *dev) return ret; } +#if defined(CONFIG_K3_J721E_DDRSS) + +void k3_ddrss_lpddr4_exit_retention(struct udevice *dev, + struct k3_ddrss_regs *regs) +{ + struct k3_ddrss_desc *ddrss = dev_get_priv(dev); + u32 regval; + unsigned int pll_ctrl; + volatile unsigned int val; + + /* + * Saving registers + */ + lpddr4_k3_readreg_ctl(ddrss, DENALI_CTL_141, ®s->ctl_141); + lpddr4_k3_readreg_phy(ddrss, DENALI_PHY_1305, ®s->phy_1305); + lpddr4_k3_readreg_ctl(ddrss, DENALI_CTL_88, ®s->ctl_88); + lpddr4_k3_readreg_pi(ddrss, DENALI_PI_134, ®s->pi_134); + // PI_139 cannot be restored + lpddr4_k3_readreg_pi(ddrss, DENALI_PI_7, ®s->pi_7); + lpddr4_k3_readreg_ctl(ddrss, DENALI_CTL_20, ®s->ctl_20); + + /* disable auto entry / exit */ + lpddr4_k3_clr_ctl(ddrss, DENALI_CTL_141, (0xF << 24) | (0xF << 16)); + + /* Configure DFI Interface, DDR retention exit occurs through PHY */ + lpddr4_k3_readreg_phy(ddrss, LPDDR4__PHY_SET_DFI_INPUT_0__REG, ®val); + regval &= ~0xF0F; // Set DFI_Input_1 = 0 + regval |= 0x01; // Set DFI Input_0 = 1 + lpddr4_k3_writereg_phy(ddrss, LPDDR4__PHY_SET_DFI_INPUT_0__REG, regval); + + /* PWRUP_SREFRESH_EXIT = 1 */ + lpddr4_k3_set_ctl(ddrss, LPDDR4__PWRUP_SREFRESH_EXIT__REG, 0x1); + + /* PI_PWRUP_SREFRESH_EXIT = 0 */ + lpddr4_k3_clr_pi(ddrss, LPDDR4__PI_PWRUP_SREFRESH_EXIT__REG, 0x1 << 16); + + /* PI_DRAM_INIT_EN = 0 */ + lpddr4_k3_clr_pi(ddrss, LPDDR4__PI_DRAM_INIT_EN__REG, 0x1 << 8); + + /* PI_DFI_PHYMSTR_STATE_SEL_R = 1 (force memory into self-refresh) */ + lpddr4_k3_set_pi(ddrss, LPDDR4__PI_DFI_PHYMSTR_STATE_SEL_R__REG, (1 << 24)); + + /* PHY_INDEP_INIT_MODE = 0 */ + lpddr4_k3_clr_ctl(ddrss, LPDDR4__PHY_INDEP_INIT_MODE__REG, (0x1 << 16)); + + /* PHY_INDEP_TRAIN_MODE = 1 */ + lpddr4_k3_set_ctl(ddrss, LPDDR4__PHY_INDEP_TRAIN_MODE__REG, 0x1); + + lpddr4_k3_readreg_pi(ddrss, LPDDR4__PI_WDQLVL_EN_F1__REG, ®s->wdqlvl_f1); + regs->wdqlvl_f1 &= LPDDR4__DENALI_PI_214__PI_WDQLVL_EN_F1_MASK; + + lpddr4_k3_readreg_pi(ddrss, LPDDR4__PI_WDQLVL_EN_F2__REG, ®s->wdqlvl_f2); + regs->wdqlvl_f2 &= LPDDR4__DENALI_PI_217__PI_WDQLVL_EN_F2_MASK; + + /* clear periodic WDQLVL for F0 */ + lpddr4_k3_clr_pi(ddrss, LPDDR4__PI_WDQLVL_EN_F0__REG, + 0x2 << LPDDR4__DENALI_PI_212__PI_WDQLVL_EN_F0_SHIFT); + + /* clear periodic WDQLVL for F1 */ + lpddr4_k3_clr_pi(ddrss, LPDDR4__PI_WDQLVL_EN_F1__REG, + 0x2 << LPDDR4__DENALI_PI_214__PI_WDQLVL_EN_F1_SHIFT); + + /* clear periodic WDQLVL for F2 */ + lpddr4_k3_clr_pi(ddrss, LPDDR4__PI_WDQLVL_EN_F2__REG, + 0x2 << LPDDR4__DENALI_PI_217__PI_WDQLVL_EN_F2_SHIFT); + +#define PLL_CTRL_OFF 0x20 +#define PLL_CFG 0x00680000 + switch (ddrss->instance) { + case 0: + pll_ctrl = PLL_CFG + 12 * 0x1000 + PLL_CTRL_OFF; + break; + case 1: + pll_ctrl = PLL_CFG + 26 * 0x1000 + PLL_CTRL_OFF; + break; + case 2: + pll_ctrl = PLL_CFG + 27 * 0x1000 + PLL_CTRL_OFF; + break; + case 3: + pll_ctrl = PLL_CFG + 28 * 0x1000 + PLL_CTRL_OFF; + break; + } + + *(unsigned int *)pll_ctrl |= 0x80000000; + val = *(volatile unsigned int *)pll_ctrl; + if ((val & 0x80000000) != 0x80000000) + val = *(volatile unsigned int *)pll_ctrl; +} + +void k3_ddrss_lpddr4_change_freq(struct udevice *dev) +{ + struct k3_ddrss_desc *ddrss = dev_get_priv(dev); + u32 regval; + unsigned long tmo; + + /* PI_START=1 */ + lpddr4_k3_set_pi(ddrss, LPDDR4__PI_START__REG, 0x01); + /* START=1 */ + lpddr4_k3_set_ctl(ddrss, LPDDR4__START__REG, 0x01); + + k3_lpddr4_freq_update(ddrss); + + tmo = timer_get_us() + 100000; + do { + lpddr4_k3_readreg_pi(ddrss, LPDDR4__PI_INT_STATUS__REG, ®val); + if (timer_get_us() > tmo) { + printf("%s:%d timeout error\n", __func__, __LINE__); + hang(); + } + } while ((regval & (1 << 0)) == 0x00); + + tmo = timer_get_us() + 4000; + do { + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__INT_STATUS_0__REG, ®val); + if (timer_get_us() > tmo) { + printf("%s:%d timeout error\n", __func__, __LINE__); + hang(); + } + } while ((regval & (1 << 9)) == 0x00); + + lpddr4_k3_readreg_pi(ddrss, LPDDR4__PI_INT_STATUS__REG, ®val); + debug("%s: PI interrupt status: 0x%08x\n", __func__, regval); + + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__INT_STATUS_0__REG, ®val); + debug("%s: Controller interrupt status: 0x%08x\n", __func__, regval); + + debug("%s: Successfully exited Retention\n", __func__); +} + +void k3_ddrss_lpddr4_exit_low_power(struct udevice *dev, + struct k3_ddrss_regs *regs) +{ + struct k3_ddrss_desc *ddrss = dev_get_priv(dev); + u32 regval, fspop, fspwr; + unsigned long tmo; + + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__LP_STATE_CS0__REG, ®val); + debug("%s: LP State: 0x%08x\n", __func__, regval); + + /* make sure that LP flag is clear before going through this process */ + lpddr4_k3_writereg_ctl(ddrss, LPDDR4__INT_ACK_0__REG, (0x1 << 10)); + + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__CKSRX_F1__REG, ®val); + regval &= ~(0x7F << 24); + regval |= (0x2 << 24); // set low power mode exit + lpddr4_k3_writereg_ctl(ddrss, LPDDR4__CKSRX_F1__REG, regval); + + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__LP_STATE_CS0__REG, ®val); + debug("%s: LP State: 0x%08x\n", __func__, regval); + + /* wait until low power operation has been completed */ + tmo = timer_get_us() + 4000; + do { + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__INT_STATUS_0__REG, ®val); + if (timer_get_us() > tmo) { + printf("%s:%d timeout error\n", __func__, __LINE__); + hang(); + } + } while ((regval & (0x1 << 10)) == 0); + + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__LP_STATE_CS0__REG, ®val); + debug("%s: LP State: 0x%08x\n", __func__, regval); + + lpddr4_k3_writereg_ctl(ddrss, LPDDR4__INT_ACK_0__REG, (0x1 << 10)); + + /* + * bit 6 / 14 -- lp_state valid + * bits 13:8 / 5:0 0x0F SRPD Long with Mem and Controller Clk Gating + */ + tmo = timer_get_us() + 4000; + do { + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__LP_STATE_CS0__REG, ®val); + if (timer_get_us() > tmo) { + printf("%s:%d timeout error\n", __func__, __LINE__); + hang(); + } + } while ((regval & 0x4F4F) != 0x4040); + + lpddr4_k3_readreg_ctl(ddrss, LPDDR4__LP_STATE_CS0__REG, ®val); + debug("%s: LP State: 0x%08x\n", __func__, regval); + + /* + * MR13 data within the PI is based upon CS but NOT based upon + * frequency set point. + * MR13 is the current MR13 value + */ + lpddr4_k3_readreg_pi(ddrss, LPDDR4__PI_MR13_DATA_0__REG, ®val); + fspop = (regval & ((u32)1 << 31)) >> 31; + fspwr = (regval & ((u32)1 << 30)) >> 30; + + lpddr4_k3_set_ctl(ddrss, LPDDR4__FSP_OP_CURRENT__REG, + fspop << LPDDR4__DENALI_CTL_192__FSP_OP_CURRENT_SHIFT); + + lpddr4_k3_set_ctl(ddrss, LPDDR4__FSP_WR_CURRENT__REG, + fspwr << LPDDR4__DENALI_CTL_192__FSP_WR_CURRENT_SHIFT); + + // do not allow CTL to update MR + lpddr4_k3_set_ctl(ddrss, LPDDR4__FSP_PHY_UPDATE_MRW__REG, + 1 << LPDDR4__DENALI_CTL_191__FSP_PHY_UPDATE_MRW_SHIFT); + + // do not allow PI to update MR + lpddr4_k3_clr_pi(ddrss, DENALI_PI_64, 1 << 0); + + // PI_FREQ_MAP defines supported working frequencies + lpddr4_k3_readreg_pi(ddrss, LPDDR4__PI_FREQ_MAP__REG, ®val); + if (regval & (0x1 << 1)) { + // define FSP0 and FSP1 as trained + lpddr4_k3_set_ctl(ddrss, LPDDR4__MR_FSP_DATA_VALID_F0__REG, + LPDDR4__DENALI_CTL_190__MR_FSP_DATA_VALID_F0_MASK); + lpddr4_k3_set_ctl(ddrss, LPDDR4__MR_FSP_DATA_VALID_F1__REG, + LPDDR4__DENALI_CTL_190__MR_FSP_DATA_VALID_F1_MASK); + } + if (regval & (0x1 << 2)) { + // define FSP0 and FSP2 as trained + lpddr4_k3_set_ctl(ddrss, LPDDR4__MR_FSP_DATA_VALID_F0__REG, + LPDDR4__DENALI_CTL_190__MR_FSP_DATA_VALID_F0_MASK); + lpddr4_k3_set_ctl(ddrss, LPDDR4__MR_FSP_DATA_VALID_F2__REG, + LPDDR4__DENALI_CTL_190__MR_FSP_DATA_VALID_F2_MASK); + } + + /* + * Restore registers + */ + lpddr4_k3_writereg_ctl(ddrss, DENALI_CTL_141, regs->ctl_141); + lpddr4_k3_writereg_phy(ddrss, DENALI_PHY_1305, regs->phy_1305); + lpddr4_k3_writereg_ctl(ddrss, DENALI_CTL_88, regs->ctl_88); + lpddr4_k3_writereg_pi(ddrss, DENALI_PI_134, regs->pi_134); + // PI_139 cannot be restored + lpddr4_k3_writereg_pi(ddrss, DENALI_PI_7, regs->pi_7); + lpddr4_k3_writereg_ctl(ddrss, DENALI_CTL_20, regs->ctl_20); + + lpddr4_k3_readreg_ctl(ddrss, DENALI_CTL_141, ®s->ctl_141); + lpddr4_k3_readreg_phy(ddrss, DENALI_PHY_1305, ®s->phy_1305); + lpddr4_k3_readreg_ctl(ddrss, DENALI_CTL_88, ®s->ctl_88); + // PI_139 cannot be restored + lpddr4_k3_readreg_pi(ddrss, DENALI_PI_7, ®s->pi_7); + + lpddr4_k3_readreg_pi(ddrss, DENALI_PI_79, ®val); + lpddr4_k3_writereg_pi(ddrss, DENALI_PI_80, regval); + + lpddr4_k3_readreg_ctl(ddrss, DENALI_CTL_293, ®val); + lpddr4_k3_writereg_ctl(ddrss, DENALI_CTL_295, regval); + + lpddr4_k3_readreg_ctl(ddrss, DENALI_CTL_294, ®val); + lpddr4_k3_writereg_ctl(ddrss, DENALI_CTL_296, regval); + + lpddr4_k3_set_pi(ddrss, DENALI_PI_214, regs->wdqlvl_f1); + lpddr4_k3_set_pi(ddrss, DENALI_PI_217, regs->wdqlvl_f2); +} +#endif /* CONFIG_K3_J721E_DDRSS */ + void k3_lpddr4_probe(struct k3_ddrss_desc *ddrss) { u32 status = 0U; diff --git a/drivers/ram/k3-ddrss/lpddr4_k3_reg.h b/drivers/ram/k3-ddrss/lpddr4_k3_reg.h new file mode 100644 index 00000000000..ba8e8b619fb --- /dev/null +++ b/drivers/ram/k3-ddrss/lpddr4_k3_reg.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef LPDDR4_K3_REG +#define LPDDR4_K3_REG + +#define lpddr4_k3_readreg_ctl(ddrss, reg, pt) do { \ + u16 offset = 0U; \ + u32 result = 0U; \ + TH_OFFSET_FROM_REG(reg, CTL_SHIFT, offset); \ + result = ddrss->driverdt->readctlconfig(&ddrss->pd, pt, (u16 *)(&offset), 1); \ + if (result > 0U) { \ + printf("%s: Failed to read %s\n", __func__, xstr(reg)); \ + hang(); \ + } \ + } while (0) + +#define lpddr4_k3_readreg_pi(ddrss, reg, pt) do { \ + u16 offset = 0U; \ + u32 result = 0U; \ + TH_OFFSET_FROM_REG(reg, PI_SHIFT, offset); \ + result = ddrss->driverdt->readphyindepconfig(&ddrss->pd, pt, (u16 *)(&offset), 1); \ + if (result > 0U) { \ + printf("%s: Failed to read %s\n", __func__, xstr(reg)); \ + hang(); \ + } \ + } while (0) + +#define lpddr4_k3_readreg_phy(ddrss, reg, pt) do { \ + u16 offset = 0U; \ + u32 result = 0U; \ + TH_OFFSET_FROM_REG(reg, PHY_SHIFT, offset); \ + result = ddrss->driverdt->readphyconfig(&ddrss->pd, pt, (u16 *)(&offset), 1); \ + if (result > 0U) { \ + printf("%s: Failed to read %s\n", __func__, xstr(reg)); \ + hang(); \ + } \ + } while (0) + +#define lpddr4_k3_writereg_ctl(ddrss, reg, value) do { \ + u16 offset = 0U; \ + u32 result = 0U; \ + u32 writeval = value; \ + TH_OFFSET_FROM_REG(reg, CTL_SHIFT, offset); \ + result = ddrss->driverdt->writectlconfig(&ddrss->pd, &writeval, (u16 *)(&offset), 1); \ + if (result > 0U) { \ + printf("%s: Failed to write %s\n", __func__, xstr(reg)); \ + hang(); \ + } \ + } while (0) + +#define lpddr4_k3_writereg_pi(ddrss, reg, value) do { \ + u16 offset = 0U; \ + u32 result = 0U; \ + u32 writeval = value; \ + TH_OFFSET_FROM_REG(reg, PI_SHIFT, offset); \ + result = ddrss->driverdt->writephyindepconfig(&ddrss->pd, &writeval, (u16 *)(&offset), 1); \ + if (result > 0U) { \ + printf("%s: Failed to write %s\n", __func__, xstr(reg)); \ + hang(); \ + } \ + } while (0) + +#define lpddr4_k3_writereg_phy(ddrss, reg, value) do { \ + u16 offset = 0U; \ + u32 result = 0U; \ + u32 writeval = value; \ + TH_OFFSET_FROM_REG(reg, PHY_SHIFT, offset); \ + result = ddrss->driverdt->writephyconfig(&ddrss->pd, &writeval, (u16 *)(&offset), 1); \ + if (result > 0U) { \ + printf("%s: Failed to write %s\n", __func__, xstr(reg)); \ + hang(); \ + } \ + } while (0) + +#define lpddr4_k3_set_ctl(k3_ddrss, reg, mask) do { \ + u32 val; \ + lpddr4_k3_readreg_ctl(ddrss, reg, &val); \ + val |= mask; \ + lpddr4_k3_writereg_ctl(ddrss, reg, val); \ + } while (0) + +#define lpddr4_k3_clr_ctl(k3_ddrss, reg, mask) do { \ + u32 val; \ + lpddr4_k3_readreg_ctl(ddrss, reg, &val); \ + val &= ~(mask); \ + lpddr4_k3_writereg_ctl(ddrss, reg, val); \ + } while (0) + +#define lpddr4_k3_set_pi(k3_ddrss, reg, mask) do { \ + u32 val; \ + lpddr4_k3_readreg_pi(ddrss, reg, &val); \ + val |= mask; \ + lpddr4_k3_writereg_pi(ddrss, reg, val); \ + } while (0) + +#define lpddr4_k3_clr_pi(k3_ddrss, reg, mask) do { \ + u32 val; \ + lpddr4_k3_readreg_pi(ddrss, reg, &val); \ + val &= ~(mask); \ + lpddr4_k3_writereg_pi(ddrss, reg, val); \ + } while (0) + +#define lpddr4_k3_set_phy(ddrss, reg, mask) do { \ + u32 val; \ + lpddr4_k3_readreg_phy(ddrss, reg, &val); \ + val |= mask; \ + lpddr4_k3_writereg_phy(ddrss, reg, val); \ + } while (0) + +#define lpddr4_k3_clr_phy(ddrss, reg, mask) do { \ + u32 val; \ + lpddr4_k3_readreg_phy(ddrss, reg, &val); \ + val &= ~(mask); \ + lpddr4_k3_writereg_phy(ddrss, reg, val); \ + } while (0) + +#endif /* LPDDR4_K3_REG */ -- 2.34.1

