From: Jacky Bai <ping....@nxp.com>

Update the DDR init flow for multi-setpoint support on i.MX93. A new
fsp_cfg struct need to be added in the timing file to store the diff
part of the DDRC and DRAM MR register for each setpoint.

Signed-off-by: Jacky Bai <ping....@nxp.com>
Reviewed-by: Ye Li <ye...@nxp.com>
Signed-off-by: Peng Fan <peng....@nxp.com>
---
 arch/arm/include/asm/arch-imx8m/ddr.h |   2 +-
 arch/arm/include/asm/arch-imx9/ddr.h  |  21 ++++-
 drivers/ddr/imx/imx9/ddr_init.c       | 113 +++++++++++++++++++++++++-
 drivers/ddr/imx/phy/helper.c          |   5 +-
 4 files changed, 134 insertions(+), 7 deletions(-)

diff --git a/arch/arm/include/asm/arch-imx8m/ddr.h 
b/arch/arm/include/asm/arch-imx8m/ddr.h
index 2f76e7d69b9..c14855d177e 100644
--- a/arch/arm/include/asm/arch-imx8m/ddr.h
+++ b/arch/arm/include/asm/arch-imx8m/ddr.h
@@ -709,7 +709,7 @@ int ddr_init(struct dram_timing_info *timing_info);
 int ddr_cfg_phy(struct dram_timing_info *timing_info);
 void load_lpddr4_phy_pie(void);
 void ddrphy_trained_csr_save(struct dram_cfg_param *param, unsigned int num);
-void dram_config_save(struct dram_timing_info *info, unsigned long base);
+void *dram_config_save(struct dram_timing_info *info, unsigned long base);
 void board_dram_ecc_scrub(void);
 void ddrc_inline_ecc_scrub(unsigned int start_address,
                           unsigned int range_address);
diff --git a/arch/arm/include/asm/arch-imx9/ddr.h 
b/arch/arm/include/asm/arch-imx9/ddr.h
index 8e4f946e5b0..2b22f3a5bea 100644
--- a/arch/arm/include/asm/arch-imx9/ddr.h
+++ b/arch/arm/include/asm/arch-imx9/ddr.h
@@ -13,11 +13,21 @@
 #define DDR_PHY_BASE                   0x4E100000
 #define DDRMIX_BLK_CTRL_BASE           0x4E010000
 
+#define REG_DDR_SDRAM_MD_CNTL  (DDR_CTL_BASE + 0x120)
+#define REG_DDR_CS0_BNDS        (DDR_CTL_BASE + 0x0)
+#define REG_DDR_CS1_BNDS        (DDR_CTL_BASE + 0x8)
 #define REG_DDRDSR_2                   (DDR_CTL_BASE + 0xB24)
 #define REG_DDR_TIMING_CFG_0   (DDR_CTL_BASE + 0x104)
 #define REG_DDR_SDRAM_CFG              (DDR_CTL_BASE + 0x110)
 #define REG_DDR_TIMING_CFG_4   (DDR_CTL_BASE + 0x160)
 #define REG_DDR_DEBUG_19               (DDR_CTL_BASE + 0xF48)
+#define REG_DDR_SDRAM_CFG_3    (DDR_CTL_BASE + 0x260)
+#define REG_DDR_SDRAM_CFG_4    (DDR_CTL_BASE + 0x264)
+#define REG_DDR_SDRAM_MD_CNTL_2        (DDR_CTL_BASE + 0x270)
+#define REG_DDR_SDRAM_MPR4     (DDR_CTL_BASE + 0x28C)
+#define REG_DDR_SDRAM_MPR5     (DDR_CTL_BASE + 0x290)
+
+#define REG_DDR_ERR_EN         (DDR_CTL_BASE + 0x1000)
 
 #define SRC_BASE_ADDR                  (0x44460000)
 #define SRC_DPHY_BASE_ADDR             (SRC_BASE_ADDR + 0x1400)
@@ -52,6 +62,12 @@ struct dram_cfg_param {
        unsigned int val;
 };
 
+struct dram_fsp_cfg {
+       struct dram_cfg_param ddrc_cfg[20];
+       struct dram_cfg_param mr_cfg[10];
+       unsigned int bypass;
+};
+
 struct dram_fsp_msg {
        unsigned int drate;
        enum fw_type fw_type;
@@ -63,6 +79,9 @@ struct dram_timing_info {
        /* umctl2 config */
        struct dram_cfg_param *ddrc_cfg;
        unsigned int ddrc_cfg_num;
+       /* fsp config */
+       struct dram_fsp_cfg *fsp_cfg;
+       unsigned int fsp_cfg_num;
        /* ddrphy config */
        struct dram_cfg_param *ddrphy_cfg;
        unsigned int ddrphy_cfg_num;
@@ -86,7 +105,7 @@ int ddr_init(struct dram_timing_info *timing_info);
 int ddr_cfg_phy(struct dram_timing_info *timing_info);
 void load_lpddr4_phy_pie(void);
 void ddrphy_trained_csr_save(struct dram_cfg_param *param, unsigned int num);
-void dram_config_save(struct dram_timing_info *info, unsigned long base);
+void *dram_config_save(struct dram_timing_info *info, unsigned long base);
 void board_dram_ecc_scrub(void);
 void ddrc_inline_ecc_scrub(unsigned int start_address,
                           unsigned int range_address);
diff --git a/drivers/ddr/imx/imx9/ddr_init.c b/drivers/ddr/imx/imx9/ddr_init.c
index a1d953026f3..a2158094b28 100644
--- a/drivers/ddr/imx/imx9/ddr_init.c
+++ b/drivers/ddr/imx/imx9/ddr_init.c
@@ -71,14 +71,25 @@ void check_dfi_init_complete(void)
        setbits_le32(REG_DDRDSR_2, BIT(2));
 }
 
-void ddrc_config(struct dram_cfg_param *ddrc_config, int num)
+void ddrc_config(struct dram_timing_info *dram_timing)
 {
+       u32 num = dram_timing->ddrc_cfg_num;
+       struct dram_cfg_param *ddrc_config;
        int i = 0;
 
+       ddrc_config = dram_timing->ddrc_cfg;
        for (i = 0; i < num; i++) {
                writel(ddrc_config->val, (ulong)ddrc_config->reg);
                ddrc_config++;
        }
+
+       if (dram_timing->fsp_cfg) {
+               ddrc_config = dram_timing->fsp_cfg[0].ddrc_cfg;
+               while (ddrc_config->reg != 0) {
+                       writel(ddrc_config->val, (ulong)ddrc_config->reg);
+                       ddrc_config++;
+               }
+       }
 }
 
 static unsigned int look_for_max(unsigned int data[], unsigned int addr_start,
@@ -198,10 +209,85 @@ void update_umctl2_rank_space_setting(unsigned int 
pstat_num)
        writel(tmp_t, REG_DDR_TIMING_CFG_4);
 }
 
+u32 ddrc_mrr(u32 chip_select, u32 mode_reg_num, u32 *mode_reg_val)
+{
+       u32 temp;
+
+       writel(0x80000000, REG_DDR_SDRAM_MD_CNTL_2);
+       temp = 0x80000000 | (chip_select << 28) | (mode_reg_num << 0);
+       writel(temp, REG_DDR_SDRAM_MD_CNTL);
+       while ((readl(REG_DDR_SDRAM_MD_CNTL) & 0x80000000) == 0x80000000)
+               ;
+       while (!(readl(REG_DDR_SDRAM_MPR5)))
+               ;
+       *mode_reg_val = (readl(REG_DDR_SDRAM_MPR4) & 0xFF0000) >> 16;
+       writel(0x0, REG_DDR_SDRAM_MPR5);
+       while ((readl(REG_DDR_SDRAM_MPR5)))
+               ;
+       writel(0x0, REG_DDR_SDRAM_MPR4);
+       writel(0x0, REG_DDR_SDRAM_MD_CNTL_2);
+
+       return 0;
+}
+
+void ddrc_mrs(u32 cs_sel, u32 opcode, u32 mr)
+{
+       u32 regval;
+
+       regval = (cs_sel << 28) | (opcode << 6) | (mr);
+       writel(regval, REG_DDR_SDRAM_MD_CNTL);
+       setbits_le32(REG_DDR_SDRAM_MD_CNTL, BIT(31));
+       check_ddrc_idle();
+}
+
+u32 lpddr4_mr_read(u32 mr_rank, u32 mr_addr)
+{
+       u32 chip_select, regval;
+
+       if (mr_rank == 1)
+               chip_select = 0; /* CS0 */
+       else if (mr_rank == 2)
+               chip_select = 1; /* CS1 */
+       else
+               chip_select = 4; /* CS0 & CS1 */
+
+       ddrc_mrr(chip_select, mr_addr, &regval);
+
+       return regval;
+}
+
+void update_mr_fsp_op0(struct dram_cfg_param *cfg, unsigned int num)
+{
+       int i;
+
+       ddrc_mrs(0x4, 0x88, 13); /* FSP-OP->1, FSP-WR->0, VRCG=1, DMD=0 */
+       for (i = 0; i < num; i++) {
+               if (cfg[i].reg)
+                       ddrc_mrs(0x4, cfg[i].val, cfg[i].reg);
+       }
+       ddrc_mrs(0x4, 0xc0, 13); /* FSP-OP->1, FSP-WR->1, VRCG=0, DMD=0 */
+}
+
+void save_trained_mr12_14(struct dram_cfg_param *cfg, u32 cfg_num, u32 mr12, 
u32 mr14)
+{
+       int i;
+
+       for (i = 0; i < cfg_num; i++) {
+               if (cfg->reg == 12)
+                       cfg->val = mr12;
+               else if (cfg->reg == 14)
+                       cfg->val = mr14;
+               cfg++;
+       }
+}
+
 int ddr_init(struct dram_timing_info *dram_timing)
 {
        unsigned int initial_drate;
+       struct dram_timing_info *saved_timing;
+       void *fsp;
        int ret;
+       u32 mr12, mr14;
        u32 regval;
 
        debug("DDRINFO: start DRAM init\n");
@@ -229,7 +315,7 @@ int ddr_init(struct dram_timing_info *dram_timing)
 
        /* rogram the ddrc registers */
        debug("DDRINFO: ddrc config start\n");
-       ddrc_config(dram_timing->ddrc_cfg, dram_timing->ddrc_cfg_num);
+       ddrc_config(dram_timing);
        debug("DDRINFO: ddrc config done\n");
 
        update_umctl2_rank_space_setting(dram_timing->fsp_msg_num - 1);
@@ -245,8 +331,29 @@ int ddr_init(struct dram_timing_info *dram_timing)
 
        check_ddrc_idle();
 
+       mr12 = lpddr4_mr_read(1, 12);
+       mr14 = lpddr4_mr_read(1, 14);
+
        /* save the dram timing config into memory */
-       dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
+       fsp = dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
+
+       saved_timing = (struct dram_timing_info *)CONFIG_SAVED_DRAM_TIMING_BASE;
+       saved_timing->fsp_cfg = fsp;
+       saved_timing->fsp_cfg_num = dram_timing->fsp_cfg_num;
+       if (saved_timing->fsp_cfg_num) {
+               memcpy(saved_timing->fsp_cfg, dram_timing->fsp_cfg,
+                      dram_timing->fsp_cfg_num * sizeof(struct dram_fsp_cfg));
+
+               save_trained_mr12_14(saved_timing->fsp_cfg[0].mr_cfg,
+                                    
ARRAY_SIZE(saved_timing->fsp_cfg[0].mr_cfg), mr12, mr14);
+               /*
+                * Configure mode registers in fsp1 to mode register 0 because 
DDRC
+                * doesn't automatically set.
+                */
+               if (saved_timing->fsp_cfg_num > 1)
+                       update_mr_fsp_op0(saved_timing->fsp_cfg[1].mr_cfg,
+                                         
ARRAY_SIZE(saved_timing->fsp_cfg[1].mr_cfg));
+       }
 
        return 0;
 }
diff --git a/drivers/ddr/imx/phy/helper.c b/drivers/ddr/imx/phy/helper.c
index e9e0294f87d..8cd438791e5 100644
--- a/drivers/ddr/imx/phy/helper.c
+++ b/drivers/ddr/imx/phy/helper.c
@@ -167,8 +167,7 @@ void ddrphy_trained_csr_save(struct dram_cfg_param 
*ddrphy_csr,
        dwc_ddrphy_apb_wr(0xd0000, 0x1);
 }
 
-void dram_config_save(struct dram_timing_info *timing_info,
-                     unsigned long saved_timing_base)
+void *dram_config_save(struct dram_timing_info *timing_info, unsigned long 
saved_timing_base)
 {
        int i = 0;
        struct dram_timing_info *saved_timing = (struct dram_timing_info 
*)saved_timing_base;
@@ -217,4 +216,6 @@ void dram_config_save(struct dram_timing_info *timing_info,
                cfg->val = timing_info->ddrphy_pie[i].val;
                cfg++;
        }
+
+       return (void *)cfg;
 }
-- 
2.40.0

Reply via email to