[PATCH 8/9] ddr: imx9: Implement Synopsys LPDDR PHY QuickBoot flow
Peng Fan (OSS)
peng.fan at oss.nxp.com
Mon Jul 28 05:24:54 CEST 2025
From: Viorel Suman <viorel.suman at nxp.com>
Implement Synopsys LPDDR PHY QuickBoot flow.
Signed-off-by: Viorel Suman <viorel.suman at nxp.com>
Signed-off-by: Peng Fan <peng.fan at nxp.com>
---
arch/arm/include/asm/arch-imx9/ddr.h | 7 ++-
drivers/ddr/imx/imx9/ddr_init.c | 9 +++-
drivers/ddr/imx/phy/Kconfig | 6 +++
drivers/ddr/imx/phy/Makefile | 1 +
drivers/ddr/imx/phy/ddrphy_qb.c | 100 +++++++++++++++++++++++++++++++++++
drivers/ddr/imx/phy/helper.c | 39 +++++++++++++-
6 files changed, 158 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/arch-imx9/ddr.h b/arch/arm/include/asm/arch-imx9/ddr.h
index 4c092509111c66c09cd006711131db107dbf17e3..6e5a3e3b6793d0171c2fbe6415821968ac1d3ba4 100644
--- a/arch/arm/include/asm/arch-imx9/ddr.h
+++ b/arch/arm/include/asm/arch-imx9/ddr.h
@@ -101,7 +101,7 @@ struct dram_timing_info {
extern struct dram_timing_info dram_timing;
-#if defined(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN)
+#if (defined(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN) || defined(CONFIG_IMX_SNPS_DDR_PHY_QB))
#define DDRPHY_QB_FSP_SIZE 3
#define DDRPHY_QB_CSR_SIZE 1792
#define DDRPHY_QB_FLAG_2D BIT(0) /* =1 if First boot used 2D training, =0 otherwise */
@@ -118,8 +118,13 @@ struct ddrphy_qb_state {
extern struct ddrphy_qb_state qb_state;
+#if defined(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN)
int ddrphy_qb_save(void);
#endif
+#if defined(CONFIG_IMX_SNPS_DDR_PHY_QB)
+int ddr_cfg_phy_qb(struct dram_timing_info *timing_info, int fsp_id);
+#endif
+#endif
void ddr_load_train_firmware(enum fw_type type);
int ddr_init(struct dram_timing_info *timing_info);
diff --git a/drivers/ddr/imx/imx9/ddr_init.c b/drivers/ddr/imx/imx9/ddr_init.c
index 758a4049139228198a86449bdf2f9106c0773040..7d40e199ad9900c51c689c530999dd83dc983144 100644
--- a/drivers/ddr/imx/imx9/ddr_init.c
+++ b/drivers/ddr/imx/imx9/ddr_init.c
@@ -375,6 +375,12 @@ int ddr_init(struct dram_timing_info *dram_timing)
/* default to the frequency point 0 clock */
ddrphy_init_set_dfi_clk(initial_drate);
+#if defined(CONFIG_IMX_SNPS_DDR_PHY_QB)
+ /* Configure PHY in QuickBoot mode */
+ ret = ddr_cfg_phy_qb(dram_timing, 0);
+ if (ret)
+ return ret;
+#else
/*
* Start PHY initialization and training by
* accessing relevant PUB registers
@@ -391,6 +397,7 @@ int ddr_init(struct dram_timing_info *dram_timing)
#if defined(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN)
ddrphy_qb_save();
+#endif
#endif
/* save the ddr PHY trained CSR in memory for low power use */
ddrphy_trained_csr_save(dram_timing->ddrphy_trained_csr,
@@ -400,7 +407,7 @@ int ddr_init(struct dram_timing_info *dram_timing)
update_umctl2_rank_space_setting(dram_timing, dram_timing->fsp_msg_num - 1);
- /* rogram the ddrc registers */
+ /* program the ddrc registers */
debug("DDRINFO: ddrc config start\n");
ddrc_config(dram_timing);
debug("DDRINFO: ddrc config done\n");
diff --git a/drivers/ddr/imx/phy/Kconfig b/drivers/ddr/imx/phy/Kconfig
index 28a9eca0788f96355e8459427a92832446be0e02..b0554172af3654da50d8ed929c382ef54506464b 100644
--- a/drivers/ddr/imx/phy/Kconfig
+++ b/drivers/ddr/imx/phy/Kconfig
@@ -8,3 +8,9 @@ config IMX_SNPS_DDR_PHY_QB_GEN
depends on IMX_SNPS_DDR_PHY
help
Select the DDR PHY training data generation for QuickBoot support on i.MX9 SOC.
+
+config IMX_SNPS_DDR_PHY_QB
+ bool "i.MX Synopsys DDR PHY QuickBoot mode"
+ depends on IMX_SNPS_DDR_PHY && !IMX_SNPS_DDR_PHY_QB_GEN
+ help
+ Select the DDR PHY QuickBoot mode on i.MX9 SOC.
diff --git a/drivers/ddr/imx/phy/Makefile b/drivers/ddr/imx/phy/Makefile
index d82842759e72d104aba41110b9c6e01b997d78fb..2310e69ab5c3036a2903465f4c4e95a79bf0b2c5 100644
--- a/drivers/ddr/imx/phy/Makefile
+++ b/drivers/ddr/imx/phy/Makefile
@@ -7,4 +7,5 @@
ifdef CONFIG_XPL_BUILD
obj-$(CONFIG_IMX_SNPS_DDR_PHY) += helper.o ddrphy_utils.o ddrphy_train.o
obj-$(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN) += ddrphy_qb_gen.o
+obj-$(CONFIG_IMX_SNPS_DDR_PHY_QB) += ddrphy_qb.o
endif
diff --git a/drivers/ddr/imx/phy/ddrphy_qb.c b/drivers/ddr/imx/phy/ddrphy_qb.c
new file mode 100644
index 0000000000000000000000000000000000000000..04a6520be5464ac26e30a516265b1b6993eec38c
--- /dev/null
+++ b/drivers/ddr/imx/phy/ddrphy_qb.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <asm/arch/ddr.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+struct ddrphy_qb_state qb_state;
+
+static void ddrphy_w(u32 addr, u32 offset, u32 value)
+{
+ u32 val = dwc_ddrphy_apb_rd(addr);
+ bool hight = (offset % 2);
+
+ val &= (hight ? 0x00FF : 0xFF00);
+ val |= value << (hight ? 8 : 0);
+
+ dwc_ddrphy_apb_wr(addr, val);
+}
+
+static int ddrphy_qb_restore(struct dram_timing_info *info, int fsp_id)
+{
+ int i;
+ u32 to_addr;
+ struct dram_fsp_msg *fsp_msg;
+ struct dram_cfg_param *dram_cfg;
+
+ fsp_msg = &info->fsp_msg[fsp_id];
+ for (i = 0; i < fsp_msg->fsp_cfg_num; i++) {
+ dram_cfg = &fsp_msg->fsp_cfg[i];
+ dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val);
+ }
+
+ /* enable the ddrphy apb */
+ dwc_ddrphy_apb_wr(0xd0000, 0x00);
+ dwc_ddrphy_apb_wr(0x54008, 0x01); /* SequenceCtrl = 0x1 (DevInit Only)*/
+ ddrphy_w(0x5400c, 0x19, 0x1); /* Lp4Quickboot = 0x1 */
+
+ /* Adjust MR14_xy if pstate=0 and 2D training data collected during training phase */
+ if (fsp_id == 0 && (qb_state.flags & DDRPHY_QB_FLAG_2D)) {
+ ddrphy_w(0x5401c, 0x39, qb_state.fsp[0] >> 8); /* TrainedVREFDQ_A0 -> MR14_A0 */
+ ddrphy_w(0x54022, 0x45, qb_state.fsp[1] & 0xFF); /* TrainedVREFDQ_A1 -> MR14_A1 */
+ ddrphy_w(0x54036, 0x6c, qb_state.fsp[2] & 0xFF); /* TrainedVREFDQ_B0 -> MR14_B0 */
+ ddrphy_w(0x5403c, 0x78, qb_state.fsp[2] >> 8); /* TrainedVREFDQ_B1 -> MR14_B1 */
+ }
+
+ /* save CSRs to address starting with 0x54800 */
+ for (i = 0, to_addr = 0x54800; i < DDRPHY_QB_CSR_SIZE; i++, to_addr++)
+ dwc_ddrphy_apb_wr(to_addr, qb_state.csr[i]);
+
+ /* disable the ddrphy apb */
+ dwc_ddrphy_apb_wr(0xd0000, 0x01);
+
+ return 0;
+}
+
+int ddr_cfg_phy_qb(struct dram_timing_info *dram_timing, int fsp_id)
+{
+ int ret;
+
+ /* MemReset Toggle */
+ dwc_ddrphy_apb_wr(0xd0000, 0x0);
+ dwc_ddrphy_apb_wr(0x20060, 0x3);
+ dwc_ddrphy_apb_wr(0x2008F, 0x1);
+
+ /* load the dram quick boot firmware image */
+ dwc_ddrphy_apb_wr(0xd0000, 0x0);
+ ddr_load_train_firmware(FW_1D_IMAGE);
+ ddrphy_qb_restore(dram_timing, fsp_id);
+
+ /* excute the firmware */
+ dwc_ddrphy_apb_wr(0xd0000, 0x0);
+ dwc_ddrphy_apb_wr(0xc0080, 0x3);
+ dwc_ddrphy_apb_wr(0xd0031, 0x1);
+ dwc_ddrphy_apb_wr(0xd0000, 0x1);
+ dwc_ddrphy_apb_wr(0xd0099, 0x9);
+ dwc_ddrphy_apb_wr(0xd0099, 0x1);
+ dwc_ddrphy_apb_wr(0xd0099, 0x0);
+
+ /* Wait for the quick boot firmware to complete */
+ ret = wait_ddrphy_training_complete();
+ if (ret)
+ return ret;
+
+ /* Halt the microcontroller. */
+ dwc_ddrphy_apb_wr(0xd0099, 0x1);
+ dwc_ddrphy_apb_wr(0xd0000, 0x0);
+
+ get_trained_CDD(0);
+
+ /* step I Configure PHY for hardware control */
+ dwc_ddrphy_apb_wr(0xd00e7, 0x400);
+ dwc_ddrphy_apb_wr(0xc0080, 0x2);
+ dwc_ddrphy_apb_wr(0xd0000, 0x1);
+
+ return 0;
+}
diff --git a/drivers/ddr/imx/phy/helper.c b/drivers/ddr/imx/phy/helper.c
index b0dfc3a0b4f3f8cbbefc27b122b360e64d6cd6a2..877cc4fb42d72222cb889f0edb1182265b853d76 100644
--- a/drivers/ddr/imx/phy/helper.c
+++ b/drivers/ddr/imx/phy/helper.c
@@ -13,6 +13,9 @@
#include <asm/arch/ddr.h>
#include <asm/arch/ddr.h>
#include <asm/sections.h>
+#if defined(CONFIG_IMX_SNPS_DDR_PHY_QB)
+#include <u-boot/crc.h>
+#endif
DECLARE_GLOBAL_DATA_PTR;
@@ -30,7 +33,12 @@ binman_sym_declare(ulong, ddr_1d_imem_fw, size);
binman_sym_declare(ulong, ddr_1d_dmem_fw, image_pos);
binman_sym_declare(ulong, ddr_1d_dmem_fw, size);
-#if !IS_ENABLED(CONFIG_IMX8M_DDR3L)
+#if IS_ENABLED(CONFIG_IMX_SNPS_DDR_PHY_QB)
+binman_sym_declare(ulong, ddr_qb_state, image_pos);
+binman_sym_declare(ulong, ddr_qb_state, size);
+#endif
+
+#if !IS_ENABLED(CONFIG_IMX8M_DDR3L) && !IS_ENABLED(CONFIG_IMX_SNPS_DDR_PHY_QB)
binman_sym_declare(ulong, ddr_2d_imem_fw, image_pos);
binman_sym_declare(ulong, ddr_2d_imem_fw, size);
@@ -42,13 +50,21 @@ binman_sym_declare(ulong, ddr_2d_dmem_fw, size);
void ddr_load_train_firmware(enum fw_type type)
{
u32 tmp32, i;
- u32 error = 0;
unsigned long pr_to32, pr_from32;
uint32_t fw_offset = type ? IMEM_2D_OFFSET : 0;
unsigned long imem_start = (unsigned long)_end + fw_offset;
unsigned long dmem_start;
unsigned long imem_len = IMEM_LEN, dmem_len = DMEM_LEN;
static enum fw_type last_type = -1;
+#if defined(CONFIG_IMX_SNPS_DDR_PHY_QB)
+ unsigned long qbst_start;
+ unsigned long qbst_len = sizeof(struct ddrphy_qb_state);
+ int size;
+ u32 *to32 = (u32 *)&qb_state;
+ u32 crc;
+#else
+ u32 error = 0;
+#endif
/* If FW doesn't change, we can save the loading. */
if (last_type == type)
@@ -65,6 +81,9 @@ void ddr_load_train_firmware(enum fw_type type)
#endif
dmem_start = imem_start + imem_len;
+#if IS_ENABLED(CONFIG_IMX_SNPS_DDR_PHY_QB)
+ qbst_start = dmem_start + dmem_len;
+#endif
if (BINMAN_SYMS_OK) {
switch (type) {
@@ -73,6 +92,10 @@ void ddr_load_train_firmware(enum fw_type type)
imem_len = binman_sym(ulong, ddr_1d_imem_fw, size);
dmem_start = binman_sym(ulong, ddr_1d_dmem_fw, image_pos);
dmem_len = binman_sym(ulong, ddr_1d_dmem_fw, size);
+#if IS_ENABLED(CONFIG_IMX_SNPS_DDR_PHY_QB)
+ qbst_start = binman_sym(ulong, ddr_qb_state, image_pos);
+ qbst_len = binman_sym(ulong, ddr_qb_state, size);
+#endif
break;
case FW_2D_IMAGE:
#if !IS_ENABLED(CONFIG_IMX8M_DDR3L)
@@ -111,6 +134,17 @@ void ddr_load_train_firmware(enum fw_type type)
i += 4;
}
+#if IS_ENABLED(CONFIG_IMX_SNPS_DDR_PHY_QB)
+ size = qbst_len / sizeof(*to32);
+ pr_from32 = qbst_start;
+ for (i = 0; i < size; i += 1, pr_from32 += 4, to32 += 1)
+ (*to32) = readl(pr_from32);
+
+ crc = crc32(0, (void *)&qb_state.flags, DDRPHY_QB_STATE_SIZE);
+ if (crc != qb_state.crc)
+ log_err("DDRPHY TD CRC error FW BIN -> U-Boot: fw=0x%08x, uboot=0x%08x\n",
+ qb_state.crc, crc);
+#else
debug("check ddr_pmu_train_imem code\n");
pr_from32 = imem_start;
pr_to32 = IMEM_OFFSET_ADDR;
@@ -154,6 +188,7 @@ void ddr_load_train_firmware(enum fw_type type)
printf("check ddr_pmu_train_dmem code fail=%d", error);
else
debug("check ddr_pmu_train_dmem code pass\n");
+#endif
}
void ddrphy_trained_csr_save(struct dram_cfg_param *ddrphy_csr,
--
2.35.3
More information about the U-Boot
mailing list