From: Viorel Suman <viorel.su...@nxp.com>

Implement Synopsys LPDDR PHY QuickBoot flow.

Signed-off-by: Viorel Suman <viorel.su...@nxp.com>
Signed-off-by: Peng Fan <peng....@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

Reply via email to