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.
Signed-off-by: Markus Schneider-Pargmann (TI.com) <[email protected]> --- drivers/ram/k3-ddrss/Makefile | 1 + drivers/ram/k3-ddrss/k3-ddrss-lpm.c | 181 ++++++++++++++++++++++++++++++++++++ drivers/ram/k3-ddrss/k3-ddrss-lpm.h | 20 ++++ drivers/ram/k3-ddrss/k3-ddrss.c | 26 +++++- 4 files changed, 227 insertions(+), 1 deletion(-) diff --git a/drivers/ram/k3-ddrss/Makefile b/drivers/ram/k3-ddrss/Makefile index 823d1887178174021778296e6626cadfd11170de..8a2ece1c27eeae51593467259337483a852f5f85 100644 --- a/drivers/ram/k3-ddrss/Makefile +++ b/drivers/ram/k3-ddrss/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_K3_DDRSS) += k3-ddrss.o +obj-$(CONFIG_K3_DDRSS) += k3-ddrss-lpm.o obj-$(CONFIG_K3_DDRSS) += lpddr4_obj_if.o obj-$(CONFIG_K3_DDRSS) += lpddr4.o ccflags-$(CONFIG_K3_DDRSS) += -Idrivers/ram/k3-ddrss/ diff --git a/drivers/ram/k3-ddrss/k3-ddrss-lpm.c b/drivers/ram/k3-ddrss/k3-ddrss-lpm.c new file mode 100644 index 0000000000000000000000000000000000000000..20181d0a876dbb723920efb78ed980948eec117f --- /dev/null +++ b/drivers/ram/k3-ddrss/k3-ddrss-lpm.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * K3 DDRSS LPM specific functions + * + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2025 BayLibre, SAS + */ + +#include <asm/io.h> +#include <linux/compiler_types.h> +#include <linux/types.h> +#include <time.h> +#include <vsprintf.h> +#include <wait_bit.h> + +#include "k3-ddrss-lpm.h" + +#define AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL 0x430080d0 +#define AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD BIT(31) +#define AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RETENTION_MASK GENMASK(3, 0) + +#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_STAT1 0x4301830c +#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE BIT(0) + +#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT 0x43018318 +#define AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT_MW 0x555555 + +#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 AM62XX_WKUP_CTRL_DDRSS_RETENTION_TIMEOUT_MS 5000 +#define K3_DDRSS_LPM_TIMEOUT_MS 5000 + +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); +} + +void k3_ddrss_self_refresh_exit(void __iomem *ddrss_ctl_cfg) +{ + k3_ddrss_reg_update_bits(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_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_ctl_cfg, + K3_DDRSS_CFG_DENALI_CTL_106, + 0, + K3_DDRSS_CFG_DENALI_CTL_106_PWRUP_SREFRESH_EXIT); + writel(0, ddrss_ctl_cfg + K3_DDRSS_CFG_DENALI_PI_146); + k3_ddrss_reg_update_bits(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_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_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_ctl_cfg, + K3_DDRSS_CFG_DENALI_CTL_20, + 0, + K3_DDRSS_CFG_DENALI_CTL_20_PHY_INDEP_TRAIN_MODE); +} + +void k3_ddrss_lpm_resume(void __iomem *ddrss_ctl_cfg) +{ + int ret; + unsigned long timeout_start; + + k3_ddrss_reg_update_bits(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_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_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_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; + } +} + +void am62xx_ddrss_deassert_retention(void) +{ + int ret; + + k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL, + 0x0, + AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD | + AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RETENTION_MASK, + 0); + k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL, + 0x0, + AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD, + AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD); + + ret = wait_for_bit_32((void *)AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL, + AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD, + true, AM62XX_WKUP_CTRL_DDRSS_RETENTION_TIMEOUT_MS, false); + if (ret) + panic("Failed waiting for latching of retention %d\n", ret); + + k3_ddrss_reg_update_bits((void *)AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL, + 0x0, + AM62XX_WKUP_CTRL_MMR0_DDR16SS_PMCTRL_DATA_RET_LD, + 0); +} + +static bool am62xx_wkup_conf_canuart_wakeup_active(void) +{ + u32 active; + + active = readl((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_STAT1); + + return !!(active & AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_STAT1_CANUART_IO_MODE); +} + +static bool am62xx_wkup_conf_canuart_magic_word_set(void) +{ + u32 magic_word; + + magic_word = readl((void *)AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT); + + return magic_word == AM62XX_WKUP_CTRL_MMR_CANUART_WAKE_OFF_MODE_STAT_MW; +} + +bool am62xx_wkup_conf_boot_is_resume(void) +{ + return IS_ENABLED(CONFIG_K3_IODDR) && + am62xx_wkup_conf_canuart_wakeup_active() && + am62xx_wkup_conf_canuart_magic_word_set(); +} diff --git a/drivers/ram/k3-ddrss/k3-ddrss-lpm.h b/drivers/ram/k3-ddrss/k3-ddrss-lpm.h new file mode 100644 index 0000000000000000000000000000000000000000..742d224327e45b926508cf6b1fb14f4f7323cd76 --- /dev/null +++ b/drivers/ram/k3-ddrss/k3-ddrss-lpm.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * K3 DDRSS LPM specific functions + * + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2025 BayLibre, SAS + */ + +#ifndef _K3_DDRSS_LPM_H_ +#define _K3_DDRSS_LPM_H_ + +#include <linux/compiler_types.h> +#include <linux/types.h> + +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); + +#endif /* _K3_DDRSS_LPM_H_ */ diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 5144470b931384534e78fbfcea5e66b7083c9f07..27823e91d5efbd7e6629f60921aebc6c834f6989 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -20,6 +20,7 @@ #include <wait_bit.h> #include <power/regulator.h> +#include "k3-ddrss-lpm.h" #include "lpddr4_obj_if.h" #include "lpddr4_if.h" #include "lpddr4_structs_if.h" @@ -119,6 +120,8 @@ enum intrlv_size { struct k3_ddrss_data { u32 flags; + bool (*is_lpm_resume)(void); + void (*ddrss_deassert_retention)(void); }; enum ecc_enable { @@ -866,6 +869,7 @@ static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) static int k3_ddrss_probe(struct udevice *dev) { + struct k3_ddrss_data *ddrss_data = (struct k3_ddrss_data *)dev_get_driver_data(dev); u64 end, bank0, bank1, bank0_size; int ret; struct k3_ddrss_desc *ddrss = dev_get_priv(dev); @@ -873,6 +877,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 +885,10 @@ static int k3_ddrss_probe(struct udevice *dev) if (ret) return ret; + 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"); + ddrss->dev = dev; ret = k3_ddrss_power_on(ddrss); if (ret) @@ -895,12 +904,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->ddrss_ctl_cfg); + ret = k3_ddrss_init_freq(ddrss); if (ret) return ret; + if (is_lpm_resume && ddrss_data->ddrss_deassert_retention) + ddrss_data->ddrss_deassert_retention(); + k3_lpddr4_start(ddrss); + if (is_lpm_resume) + k3_ddrss_lpm_resume(ddrss->ddrss_ctl_cfg); + 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.", @@ -1020,12 +1038,18 @@ static const struct k3_ddrss_data k3_data = { .flags = SINGLE_DDR_SUBSYSTEM, }; +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, +}; + static const struct k3_ddrss_data j721s2_data = { .flags = MULTI_DDR_SUBSYSTEM, }; static const struct udevice_id k3_ddrss_ids[] = { - {.compatible = "ti,am62a-ddrss", .data = (ulong)&k3_data, }, + {.compatible = "ti,am62a-ddrss", .data = (ulong)&am62xx_data, }, {.compatible = "ti,am64-ddrss", .data = (ulong)&k3_data, }, {.compatible = "ti,j721e-ddrss", .data = (ulong)&k3_data, }, {.compatible = "ti,j721s2-ddrss", .data = (ulong)&j721s2_data, }, -- 2.51.0

