summaryrefslogtreecommitdiff
path: root/drivers/ddr/imx/imx9/ddr_init.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ddr/imx/imx9/ddr_init.c')
-rw-r--r--drivers/ddr/imx/imx9/ddr_init.c421
1 files changed, 421 insertions, 0 deletions
diff --git a/drivers/ddr/imx/imx9/ddr_init.c b/drivers/ddr/imx/imx9/ddr_init.c
new file mode 100644
index 0000000000..17b4b259ac
--- /dev/null
+++ b/drivers/ddr/imx/imx9/ddr_init.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 NXP
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <log.h>
+#include <asm/io.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+#include <linux/delay.h>
+
+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 */
+ /* dramphy_reset_n default 0 , assert -> 0, de_assert -> 1 */
+ /* dramphy_PwrOKIn default 0 , assert -> 1, de_assert -> 0 */
+
+ /* src_gen_dphy_apb_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+ /* src_gen_dphy_PwrOKIn_sw_rst_de_assert() */
+ setbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(0));
+ mdelay(10);
+
+ /* src_gen_dphy_apb_sw_rst_assert */
+ setbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_assert */
+ setbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+ mdelay(10);
+ /* src_gen_dphy_PwrOKIn_sw_rst_assert */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(0));
+ mdelay(10);
+
+ /* src_gen_dphy_apb_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_de_assert() */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+}
+
+void check_ddrc_idle(void){
+ uint32_t regval;
+
+ do{
+ regval = readl(REG_DDRDSR_2);
+ if(regval & BIT(31))
+ break;
+ }while(1);
+}
+
+void check_dfi_init_complete(void)
+{
+ uint32_t regval;
+
+ do{
+ regval = readl(REG_DDRDSR_2);
+ if(regval & BIT(2))
+ break;
+ }while(1);
+ setbits_le32(REG_DDRDSR_2, BIT(2));
+}
+
+void ddrc_config(struct dram_cfg_param *ddrc_config, int num)
+{
+ int i = 0;
+
+ for (i = 0; i < num; i++) {
+ writel(ddrc_config->val, (ulong)ddrc_config->reg);
+ ddrc_config++;
+ }
+}
+
+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);
+}
+
+void update_inline_ecc_setting(void)
+{
+ u32 val, sa, ea;
+
+ val = readl(REG_DDR_CS0_BNDS);
+ if (val != 0) {
+ sa = (val >> 16) & 0xff;
+ ea = val & 0xff;
+
+ /* 1/8 size is used for inline ecc */
+ ea = ea - ((ea + 1 - sa) >> 3);
+ writel((sa << 16) | ea, REG_DDR_CS0_BNDS);
+ }
+
+ val = readl(REG_DDR_CS1_BNDS);
+ if (val != 0) {
+ sa = (val >> 16) & 0xff;
+ ea = val & 0xff;
+
+ /* 1/8 size is used for inline ecc */
+ ea = ea - ((ea + 1 - sa) >> 3);
+ writel((sa << 16) | ea, REG_DDR_CS1_BNDS);
+ }
+
+ /* Enable Inline ECC */
+ setbits_le32(REG_DDR_ERR_EN, BIT(31) | BIT(30));
+
+ /* Enable data initialization */
+ setbits_le32(REG_DDR_SDRAM_CFG2, BIT(4));
+}
+
+int ddr_init(struct dram_timing_info *dram_timing)
+{
+ unsigned int initial_drate;
+ int ret;
+ u32 regval;
+
+ debug("DDRINFO: start DRAM init\n");
+
+ /* reset ddrphy */
+ ddrphy_coldreset();
+
+ debug("DDRINFO: cfg clk\n");
+
+ initial_drate = dram_timing->fsp_msg[0].drate;
+ /* default to the frequency point 0 clock */
+ ddrphy_init_set_dfi_clk(initial_drate);
+
+ /*
+ * Start PHY initialization and training by
+ * accessing relevant PUB registers
+ */
+ debug("DDRINFO:ddrphy config start\n");
+
+ ret = ddr_cfg_phy(dram_timing);
+ if (ret)
+ return ret;
+
+ debug("DDRINFO: ddrphy config done\n");
+
+ /* rogram the ddrc registers */
+ debug("DDRINFO: ddrc config start\n");
+ 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_INLINE_ECC
+ update_inline_ecc_setting();
+#endif
+
+#ifdef CONFIG_IMX9_DRAM_PM_COUNTER
+ writel(0x200000, REG_DDR_DEBUG_19);
+#endif
+
+ check_dfi_init_complete();
+
+ regval=readl(REG_DDR_SDRAM_CFG);
+ writel((regval|0x80000000), REG_DDR_SDRAM_CFG);
+
+ check_ddrc_idle();
+
+ /* save the dram timing config into memory */
+ dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
+
+ return 0;
+}
+
+ulong ddrphy_addr_remap(uint32_t paddr_apb_from_ctlr)
+{
+ uint32_t paddr_apb_qual;
+ uint32_t paddr_apb_unqual_dec_22_13;
+ uint32_t paddr_apb_unqual_dec_19_13;
+ uint32_t paddr_apb_unqual_dec_12_1;
+ uint32_t paddr_apb_unqual;
+ uint32_t paddr_apb_phy;
+
+ paddr_apb_qual = (paddr_apb_from_ctlr << 1);
+ paddr_apb_unqual_dec_22_13 = ((paddr_apb_qual & 0x7fe000) >> 13);
+ paddr_apb_unqual_dec_12_1 = ((paddr_apb_qual & 0x1ffe) >> 1);
+
+ switch(paddr_apb_unqual_dec_22_13) {
+ case 0x000 : paddr_apb_unqual_dec_19_13 = 0x00;break;
+ case 0x001 : paddr_apb_unqual_dec_19_13 = 0x01;break;
+ case 0x002 : paddr_apb_unqual_dec_19_13 = 0x02;break;
+ case 0x003 : paddr_apb_unqual_dec_19_13 = 0x03;break;
+ case 0x004 : paddr_apb_unqual_dec_19_13 = 0x04;break;
+ case 0x005 : paddr_apb_unqual_dec_19_13 = 0x05;break;
+ case 0x006 : paddr_apb_unqual_dec_19_13 = 0x06;break;
+ case 0x007 : paddr_apb_unqual_dec_19_13 = 0x07;break;
+ case 0x008 : paddr_apb_unqual_dec_19_13 = 0x08;break;
+ case 0x009 : paddr_apb_unqual_dec_19_13 = 0x09;break;
+ case 0x00a : paddr_apb_unqual_dec_19_13 = 0x0a;break;
+ case 0x00b : paddr_apb_unqual_dec_19_13 = 0x0b;break;
+ case 0x100 : paddr_apb_unqual_dec_19_13 = 0x0c;break;
+ case 0x101 : paddr_apb_unqual_dec_19_13 = 0x0d;break;
+ case 0x102 : paddr_apb_unqual_dec_19_13 = 0x0e;break;
+ case 0x103 : paddr_apb_unqual_dec_19_13 = 0x0f;break;
+ case 0x104 : paddr_apb_unqual_dec_19_13 = 0x10;break;
+ case 0x105 : paddr_apb_unqual_dec_19_13 = 0x11;break;
+ case 0x106 : paddr_apb_unqual_dec_19_13 = 0x12;break;
+ case 0x107 : paddr_apb_unqual_dec_19_13 = 0x13;break;
+ case 0x108 : paddr_apb_unqual_dec_19_13 = 0x14;break;
+ case 0x109 : paddr_apb_unqual_dec_19_13 = 0x15;break;
+ case 0x10a : paddr_apb_unqual_dec_19_13 = 0x16;break;
+ case 0x10b : paddr_apb_unqual_dec_19_13 = 0x17;break;
+ case 0x200 : paddr_apb_unqual_dec_19_13 = 0x18;break;
+ case 0x201 : paddr_apb_unqual_dec_19_13 = 0x19;break;
+ case 0x202 : paddr_apb_unqual_dec_19_13 = 0x1a;break;
+ case 0x203 : paddr_apb_unqual_dec_19_13 = 0x1b;break;
+ case 0x204 : paddr_apb_unqual_dec_19_13 = 0x1c;break;
+ case 0x205 : paddr_apb_unqual_dec_19_13 = 0x1d;break;
+ case 0x206 : paddr_apb_unqual_dec_19_13 = 0x1e;break;
+ case 0x207 : paddr_apb_unqual_dec_19_13 = 0x1f;break;
+ case 0x208 : paddr_apb_unqual_dec_19_13 = 0x20;break;
+ case 0x209 : paddr_apb_unqual_dec_19_13 = 0x21;break;
+ case 0x20a : paddr_apb_unqual_dec_19_13 = 0x22;break;
+ case 0x20b : paddr_apb_unqual_dec_19_13 = 0x23;break;
+ case 0x300 : paddr_apb_unqual_dec_19_13 = 0x24;break;
+ case 0x301 : paddr_apb_unqual_dec_19_13 = 0x25;break;
+ case 0x302 : paddr_apb_unqual_dec_19_13 = 0x26;break;
+ case 0x303 : paddr_apb_unqual_dec_19_13 = 0x27;break;
+ case 0x304 : paddr_apb_unqual_dec_19_13 = 0x28;break;
+ case 0x305 : paddr_apb_unqual_dec_19_13 = 0x29;break;
+ case 0x306 : paddr_apb_unqual_dec_19_13 = 0x2a;break;
+ case 0x307 : paddr_apb_unqual_dec_19_13 = 0x2b;break;
+ case 0x308 : paddr_apb_unqual_dec_19_13 = 0x2c;break;
+ case 0x309 : paddr_apb_unqual_dec_19_13 = 0x2d;break;
+ case 0x30a : paddr_apb_unqual_dec_19_13 = 0x2e;break;
+ case 0x30b : paddr_apb_unqual_dec_19_13 = 0x2f;break;
+ case 0x010 : paddr_apb_unqual_dec_19_13 = 0x30;break;
+ case 0x011 : paddr_apb_unqual_dec_19_13 = 0x31;break;
+ case 0x012 : paddr_apb_unqual_dec_19_13 = 0x32;break;
+ case 0x013 : paddr_apb_unqual_dec_19_13 = 0x33;break;
+ case 0x014 : paddr_apb_unqual_dec_19_13 = 0x34;break;
+ case 0x015 : paddr_apb_unqual_dec_19_13 = 0x35;break;
+ case 0x016 : paddr_apb_unqual_dec_19_13 = 0x36;break;
+ case 0x017 : paddr_apb_unqual_dec_19_13 = 0x37;break;
+ case 0x018 : paddr_apb_unqual_dec_19_13 = 0x38;break;
+ case 0x019 : paddr_apb_unqual_dec_19_13 = 0x39;break;
+ case 0x110 : paddr_apb_unqual_dec_19_13 = 0x3a;break;
+ case 0x111 : paddr_apb_unqual_dec_19_13 = 0x3b;break;
+ case 0x112 : paddr_apb_unqual_dec_19_13 = 0x3c;break;
+ case 0x113 : paddr_apb_unqual_dec_19_13 = 0x3d;break;
+ case 0x114 : paddr_apb_unqual_dec_19_13 = 0x3e;break;
+ case 0x115 : paddr_apb_unqual_dec_19_13 = 0x3f;break;
+ case 0x116 : paddr_apb_unqual_dec_19_13 = 0x40;break;
+ case 0x117 : paddr_apb_unqual_dec_19_13 = 0x41;break;
+ case 0x118 : paddr_apb_unqual_dec_19_13 = 0x42;break;
+ case 0x119 : paddr_apb_unqual_dec_19_13 = 0x43;break;
+ case 0x210 : paddr_apb_unqual_dec_19_13 = 0x44;break;
+ case 0x211 : paddr_apb_unqual_dec_19_13 = 0x45;break;
+ case 0x212 : paddr_apb_unqual_dec_19_13 = 0x46;break;
+ case 0x213 : paddr_apb_unqual_dec_19_13 = 0x47;break;
+ case 0x214 : paddr_apb_unqual_dec_19_13 = 0x48;break;
+ case 0x215 : paddr_apb_unqual_dec_19_13 = 0x49;break;
+ case 0x216 : paddr_apb_unqual_dec_19_13 = 0x4a;break;
+ case 0x217 : paddr_apb_unqual_dec_19_13 = 0x4b;break;
+ case 0x218 : paddr_apb_unqual_dec_19_13 = 0x4c;break;
+ case 0x219 : paddr_apb_unqual_dec_19_13 = 0x4d;break;
+ case 0x310 : paddr_apb_unqual_dec_19_13 = 0x4e;break;
+ case 0x311 : paddr_apb_unqual_dec_19_13 = 0x4f;break;
+ case 0x312 : paddr_apb_unqual_dec_19_13 = 0x50;break;
+ case 0x313 : paddr_apb_unqual_dec_19_13 = 0x51;break;
+ case 0x314 : paddr_apb_unqual_dec_19_13 = 0x52;break;
+ case 0x315 : paddr_apb_unqual_dec_19_13 = 0x53;break;
+ case 0x316 : paddr_apb_unqual_dec_19_13 = 0x54;break;
+ case 0x317 : paddr_apb_unqual_dec_19_13 = 0x55;break;
+ case 0x318 : paddr_apb_unqual_dec_19_13 = 0x56;break;
+ case 0x319 : paddr_apb_unqual_dec_19_13 = 0x57;break;
+ case 0x020 : paddr_apb_unqual_dec_19_13 = 0x58;break;
+ case 0x120 : paddr_apb_unqual_dec_19_13 = 0x59;break;
+ case 0x220 : paddr_apb_unqual_dec_19_13 = 0x5a;break;
+ case 0x320 : paddr_apb_unqual_dec_19_13 = 0x5b;break;
+ case 0x040 : paddr_apb_unqual_dec_19_13 = 0x5c;break;
+ case 0x140 : paddr_apb_unqual_dec_19_13 = 0x5d;break;
+ case 0x240 : paddr_apb_unqual_dec_19_13 = 0x5e;break;
+ case 0x340 : paddr_apb_unqual_dec_19_13 = 0x5f;break;
+ case 0x050 : paddr_apb_unqual_dec_19_13 = 0x60;break;
+ case 0x051 : paddr_apb_unqual_dec_19_13 = 0x61;break;
+ case 0x052 : paddr_apb_unqual_dec_19_13 = 0x62;break;
+ case 0x053 : paddr_apb_unqual_dec_19_13 = 0x63;break;
+ case 0x054 : paddr_apb_unqual_dec_19_13 = 0x64;break;
+ case 0x055 : paddr_apb_unqual_dec_19_13 = 0x65;break;
+ case 0x056 : paddr_apb_unqual_dec_19_13 = 0x66;break;
+ case 0x057 : paddr_apb_unqual_dec_19_13 = 0x67;break;
+ case 0x070 : paddr_apb_unqual_dec_19_13 = 0x68;break;
+ case 0x090 : paddr_apb_unqual_dec_19_13 = 0x69;break;
+ case 0x190 : paddr_apb_unqual_dec_19_13 = 0x6a;break;
+ case 0x290 : paddr_apb_unqual_dec_19_13 = 0x6b;break;
+ case 0x390 : paddr_apb_unqual_dec_19_13 = 0x6c;break;
+ case 0x0c0 : paddr_apb_unqual_dec_19_13 = 0x6d;break;
+ case 0x0d0 : paddr_apb_unqual_dec_19_13 = 0x6e;break;
+ default : paddr_apb_unqual_dec_19_13 = 0x00;
+ }
+
+ paddr_apb_unqual = ((paddr_apb_unqual_dec_19_13 << 13) | (paddr_apb_unqual_dec_12_1 << 1));
+
+ paddr_apb_phy = (paddr_apb_unqual << 1);
+
+ return paddr_apb_phy;
+}