From: Ye Li Date: Fri, 28 Apr 2023 04:08:39 +0000 (+0800) Subject: ddr: imx9: Add workaround for DDRPHY rank-to-rank errata X-Git-Url: http://git.dujemihanovic.xyz/html/static/%7B%7B%20.Permalink%20%7D%7D?a=commitdiff_plain;h=662f05fcb6798277c69700c7deadd1c3c6a1b363;p=u-boot.git ddr: imx9: Add workaround for DDRPHY rank-to-rank errata According to DDRPHY errata, the Rank-to-Rank Spacing and tphy_rdcsgap specification does not include the Critical Delay Difference (CDD) to properly define the required rank-to-rank read command spacing after executing PHY training firmware. Following the errata workaround, at the end of data training, we get all CDD values through the MessageBlock, then re-configure the DDRC timing of WWT/WRT/RRT/RWT with comparing MAX CDD values. Signed-off-by: Ye Li Acked-by: Peng Fan --- diff --git a/arch/arm/include/asm/arch-imx9/ddr.h b/arch/arm/include/asm/arch-imx9/ddr.h index 62e6f7dda5..8e4f946e5b 100644 --- a/arch/arm/include/asm/arch-imx9/ddr.h +++ b/arch/arm/include/asm/arch-imx9/ddr.h @@ -14,7 +14,9 @@ #define DDRMIX_BLK_CTRL_BASE 0x4E010000 #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 SRC_BASE_ADDR (0x44460000) diff --git a/drivers/ddr/imx/imx9/ddr_init.c b/drivers/ddr/imx/imx9/ddr_init.c index 8b8ec7f8de..a1d953026f 100644 --- a/drivers/ddr/imx/imx9/ddr_init.c +++ b/drivers/ddr/imx/imx9/ddr_init.c @@ -12,6 +12,13 @@ #include #include +static unsigned int g_cdd_rr_max[4]; +static unsigned int g_cdd_rw_max[4]; +static unsigned int g_cdd_wr_max[4]; +static unsigned int g_cdd_ww_max[4]; + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + void ddrphy_coldreset(void) { /* dramphy_apb_n default 1 , assert -> 0, de_assert -> 1 */ @@ -74,8 +81,121 @@ void ddrc_config(struct dram_cfg_param *ddrc_config, int num) } } +static unsigned int look_for_max(unsigned int data[], unsigned int addr_start, + unsigned int addr_end) +{ + unsigned int i, imax = 0; + + for (i = addr_start; i <= addr_end; i++) { + if (((data[i] >> 7) == 0) && data[i] > imax) + imax = data[i]; + } + + return imax; +} + void get_trained_CDD(u32 fsp) { + unsigned int i, tmp; + unsigned int cdd_cha[12], cdd_chb[12]; + unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max; + unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max; + + for (i = 0; i < 6; i++) { + tmp = dwc_ddrphy_apb_rd(0x54013 + i); + cdd_cha[i * 2] = tmp & 0xff; + cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff; + } + + for (i = 0; i < 7; i++) { + tmp = dwc_ddrphy_apb_rd(0x5402c + i); + + if (i == 0) { + cdd_chb[0] = (tmp >> 8) & 0xff; + } else if (i == 6) { + cdd_chb[11] = tmp & 0xff; + } else { + cdd_chb[i * 2 - 1] = tmp & 0xff; + cdd_chb[i * 2] = (tmp >> 8) & 0xff; + } + } + + cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1); + cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5); + cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9); + cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11); + cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1); + cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5); + cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9); + cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11); + g_cdd_rr_max[fsp] = cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max; + g_cdd_rw_max[fsp] = cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max; + g_cdd_wr_max[fsp] = cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max; + g_cdd_ww_max[fsp] = cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max; +} + +void update_umctl2_rank_space_setting(unsigned int pstat_num) +{ + u32 tmp, tmp_t; + + int wwt, rrt, wrt, rwt; + int ext_wwt, ext_rrt, ext_wrt, ext_rwt; + int max_wwt, max_rrt, max_wrt, max_rwt; + + /* read wwt, rrt, wrt, rwt fields from timing_cfg_0 */ + tmp = readl(REG_DDR_TIMING_CFG_0); + wwt = (tmp >> 24) & 0x3; + rrt = (tmp >> 26) & 0x3; + wrt = (tmp >> 28) & 0x3; + rwt = (tmp >> 30) & 0x3; + + /* read rxt_wwt, ext_rrt, ext_wrt, ext_rwt fields from timing_cfg_4 */ + tmp_t = readl(REG_DDR_TIMING_CFG_4); + ext_wwt = (tmp >> 8) & 0x1; + ext_rrt = (tmp >> 10) & 0x1; + ext_wrt = (tmp >> 12) & 0x1; + ext_rwt = (tmp >> 14) & 0x3; + + wwt = (ext_wwt << 2) | wwt; + rrt = (ext_rrt << 2) | wwt; + wrt = (ext_wrt << 2) | wrt; + rwt = (ext_rwt << 2) | rwt; + + /* calculate the maximum between controller and cdd values */ + max_wwt = MAX(g_cdd_ww_max[0], wwt); + max_rrt = MAX(g_cdd_rr_max[0], rrt); + max_wrt = MAX(g_cdd_wr_max[0], wrt); + max_rwt = MAX(g_cdd_rw_max[0], rwt); + + /* verify values to see if are bigger then 7 or 15 (3 bits or 4 bits) */ + if (max_wwt > 7) + max_wwt = 7; + if (max_rrt > 7) + max_rrt = 7; + if (max_wrt > 7) + max_wrt = 7; + if (max_rwt > 15) + max_rwt = 15; + + /* recalculate timings for controller registers */ + wwt = max_wwt & 0x3; + rrt = max_rrt & 0x3; + wrt = max_wrt & 0x3; + rwt = max_rwt & 0x3; + + ext_wwt = (max_wwt & 0x4) >> 2; + ext_rrt = (max_rrt & 0x4) >> 2; + ext_wrt = (max_wrt & 0x4) >> 2; + ext_rwt = (max_rwt & 0xC) >> 2; + + /* update timing_cfg_0 and timing_cfg_4 */ + tmp = (tmp & 0x00ffffff) | (rwt << 30) | (wrt << 28) | + (rrt << 26) | (wwt << 24); + writel(tmp, REG_DDR_TIMING_CFG_0); + + tmp_t = (tmp_t & 0xFFFF2AFF) | (ext_rwt << 14) | + (ext_wrt << 12) | (ext_rrt << 10) | (ext_wwt << 8); + writel(tmp_t, REG_DDR_TIMING_CFG_4); } int ddr_init(struct dram_timing_info *dram_timing) @@ -112,6 +232,8 @@ int ddr_init(struct dram_timing_info *dram_timing) ddrc_config(dram_timing->ddrc_cfg, dram_timing->ddrc_cfg_num); debug("DDRINFO: ddrc config done\n"); + update_umctl2_rank_space_setting(dram_timing->fsp_msg_num - 1); + #ifdef CONFIG_IMX9_DRAM_PM_COUNTER writel(0x200000, REG_DDR_DEBUG_19); #endif