// SPDX-License-Identifier: GPL-2.0+ /* * Allwinner D1/D1s/R528/T113-sx DRAM initialisation * * As usual there is no documentation for the memory controller or PHY IP * used here. The baseline of this code was lifted from awboot[1], which * seems to be based on some form of de-compilation of some original Allwinner * code bits (with a GPL2 license tag from the very beginning). * This version here is a reworked version, to match the U-Boot coding style * and style of the other Allwinner DRAM drivers. * * [1] https://github.com/szemzoa/awboot.git */ #include #include #ifdef CONFIG_RAM #include #include #endif #include #include "dram_sun20i_d1.h" #ifndef SUNXI_SID_BASE #define SUNXI_SID_BASE 0x3006200 #endif #ifndef SUNXI_CCM_BASE #define SUNXI_CCM_BASE 0x2001000 #endif static void sid_read_ldoB_cal(const dram_para_t *para) { uint32_t reg; reg = (readl(SUNXI_SID_BASE + 0x1c) & 0xff00) >> 8; if (reg == 0) return; switch (para->dram_type) { case SUNXI_DRAM_TYPE_DDR2: break; case SUNXI_DRAM_TYPE_DDR3: if (reg > 0x20) reg -= 0x16; break; default: reg = 0; break; } clrsetbits_le32(0x3000150, 0xff00, reg << 8); } static void dram_voltage_set(const dram_para_t *para) { int vol; switch (para->dram_type) { case SUNXI_DRAM_TYPE_DDR2: vol = 47; break; case SUNXI_DRAM_TYPE_DDR3: vol = 25; break; default: vol = 0; break; } clrsetbits_le32(0x3000150, 0x20ff00, vol << 8); udelay(1); sid_read_ldoB_cal(para); } static void dram_enable_all_master(void) { writel(~0, 0x3102020); writel(0xff, 0x3102024); writel(0xffff, 0x3102028); udelay(10); } static void dram_disable_all_master(void) { writel(1, 0x3102020); writel(0, 0x3102024); writel(0, 0x3102028); udelay(10); } static void eye_delay_compensation(const dram_para_t *para) { uint32_t delay; unsigned long ptr; // DATn0IOCR, n = 0...7 delay = (para->dram_tpr11 & 0xf) << 9; delay |= (para->dram_tpr12 & 0xf) << 1; for (ptr = 0x3103310; ptr < 0x3103334; ptr += 4) setbits_le32(ptr, delay); // DATn1IOCR, n = 0...7 delay = (para->dram_tpr11 & 0xf0) << 5; delay |= (para->dram_tpr12 & 0xf0) >> 3; for (ptr = 0x3103390; ptr != 0x31033b4; ptr += 4) setbits_le32(ptr, delay); // PGCR0: assert AC loopback FIFO reset clrbits_le32(0x3103100, 0x04000000); // ?? delay = (para->dram_tpr11 & 0xf0000) >> 7; delay |= (para->dram_tpr12 & 0xf0000) >> 15; setbits_le32(0x3103334, delay); setbits_le32(0x3103338, delay); delay = (para->dram_tpr11 & 0xf00000) >> 11; delay |= (para->dram_tpr12 & 0xf00000) >> 19; setbits_le32(0x31033b4, delay); setbits_le32(0x31033b8, delay); setbits_le32(0x310333c, (para->dram_tpr11 & 0xf0000) << 9); setbits_le32(0x31033bc, (para->dram_tpr11 & 0xf00000) << 5); // PGCR0: release AC loopback FIFO reset setbits_le32(0x3103100, BIT(26)); udelay(1); delay = (para->dram_tpr10 & 0xf0) << 4; for (ptr = 0x3103240; ptr != 0x310327c; ptr += 4) setbits_le32(ptr, delay); for (ptr = 0x3103228; ptr != 0x3103240; ptr += 4) setbits_le32(ptr, delay); setbits_le32(0x3103218, (para->dram_tpr10 & 0x0f) << 8); setbits_le32(0x310321c, (para->dram_tpr10 & 0x0f) << 8); setbits_le32(0x3103280, (para->dram_tpr10 & 0xf00) >> 4); } /* * Main purpose of the auto_set_timing routine seems to be to calculate all * timing settings for the specific type of sdram used. Read together with * an sdram datasheet for context on the various variables. */ static void mctl_set_timing_params(const dram_para_t *para, const dram_config_t *config) { /* DRAM_TPR0 */ u8 tccd = 2; u8 tfaw; u8 trrd; u8 trcd; u8 trc; /* DRAM_TPR1 */ u8 txp; u8 twtr; u8 trtp = 4; u8 twr; u8 trp; u8 tras; /* DRAM_TPR2 */ u16 trefi; u16 trfc; u8 tcksrx; u8 tckesr; u8 trd2wr; u8 twr2rd; u8 trasmax; u8 twtp; u8 tcke; u8 tmod; u8 tmrd; u8 tmrw; u8 tcl; u8 tcwl; u8 t_rdata_en; u8 wr_latency; u32 mr0; u32 mr1; u32 mr2; u32 mr3; u32 tdinit0; u32 tdinit1; u32 tdinit2; u32 tdinit3; switch (para->dram_type) { case SUNXI_DRAM_TYPE_DDR2: /* DRAM_TPR0 */ tfaw = ns_to_t(50); trrd = ns_to_t(10); trcd = ns_to_t(20); trc = ns_to_t(65); /* DRAM_TPR1 */ txp = 2; twtr = ns_to_t(8); twr = ns_to_t(15); trp = ns_to_t(15); tras = ns_to_t(45); /* DRAM_TRP2 */ trfc = ns_to_t(328); trefi = ns_to_t(7800) / 32; trasmax = CONFIG_DRAM_CLK / 30; if (CONFIG_DRAM_CLK < 409) { t_rdata_en = 1; tcl = 3; mr0 = 0x06a3; } else { t_rdata_en = 2; tcl = 4; mr0 = 0x0e73; } tmrd = 2; twtp = twr + 5; tcksrx = 5; tckesr = 4; trd2wr = 4; tcke = 3; tmod = 12; wr_latency = 1; tmrw = 0; twr2rd = twtr + 5; tcwl = 0; mr1 = para->dram_mr1; mr2 = 0; mr3 = 0; tdinit0 = 200 * CONFIG_DRAM_CLK + 1; tdinit1 = 100 * CONFIG_DRAM_CLK / 1000 + 1; tdinit2 = 200 * CONFIG_DRAM_CLK + 1; tdinit3 = 1 * CONFIG_DRAM_CLK + 1; break; case SUNXI_DRAM_TYPE_DDR3: trfc = ns_to_t(350); trefi = ns_to_t(7800) / 32 + 1; // XXX twtr = ns_to_t(8) + 2; // + 2 ? XXX /* Only used by trd2wr calculation, which gets discard below */ // twr = max(ns_to_t(15), 2); trrd = max(ns_to_t(10), 2); txp = max(ns_to_t(10), 2); if (CONFIG_DRAM_CLK <= 800) { tfaw = ns_to_t(50); trcd = ns_to_t(15); trp = ns_to_t(15); trc = ns_to_t(53); tras = ns_to_t(38); mr0 = 0x1c70; mr2 = 0x18; tcl = 6; wr_latency = 2; tcwl = 4; t_rdata_en = 4; } else { tfaw = ns_to_t(35); trcd = ns_to_t(14); trp = ns_to_t(14); trc = ns_to_t(48); tras = ns_to_t(34); mr0 = 0x1e14; mr2 = 0x20; tcl = 7; wr_latency = 3; tcwl = 5; t_rdata_en = 5; } trasmax = CONFIG_DRAM_CLK / 30; twtp = tcwl + 2 + twtr; // WL+BL/2+tWTR /* Gets overwritten below */ // trd2wr = tcwl + 2 + twr; // WL+BL/2+tWR twr2rd = tcwl + twtr; // WL+tWTR tdinit0 = 500 * CONFIG_DRAM_CLK + 1; // 500 us tdinit1 = 360 * CONFIG_DRAM_CLK / 1000 + 1; // 360 ns tdinit2 = 200 * CONFIG_DRAM_CLK + 1; // 200 us tdinit3 = 1 * CONFIG_DRAM_CLK + 1; // 1 us mr1 = para->dram_mr1; mr3 = 0; tcke = 3; tcksrx = 5; tckesr = 4; if (((config->dram_tpr13 & 0xc) == 0x04) || CONFIG_DRAM_CLK < 912) trd2wr = 5; else trd2wr = 6; tmod = 12; tmrd = 4; tmrw = 0; break; case SUNXI_DRAM_TYPE_LPDDR2: tfaw = max(ns_to_t(50), 4); trrd = max(ns_to_t(10), 1); trcd = max(ns_to_t(24), 2); trc = ns_to_t(70); txp = ns_to_t(8); if (txp < 2) { txp++; twtr = 2; } else { twtr = txp; } twr = max(ns_to_t(15), 2); trp = ns_to_t(17); tras = ns_to_t(42); trefi = ns_to_t(3900) / 32; trfc = ns_to_t(210); trasmax = CONFIG_DRAM_CLK / 60; mr3 = para->dram_mr3; twtp = twr + 5; mr2 = 6; mr1 = 5; tcksrx = 5; tckesr = 5; trd2wr = 10; tcke = 2; tmod = 5; tmrd = 5; tmrw = 3; tcl = 4; wr_latency = 1; t_rdata_en = 1; tdinit0 = 200 * CONFIG_DRAM_CLK + 1; tdinit1 = 100 * CONFIG_DRAM_CLK / 1000 + 1; tdinit2 = 11 * CONFIG_DRAM_CLK + 1; tdinit3 = 1 * CONFIG_DRAM_CLK + 1; twr2rd = twtr + 5; tcwl = 2; mr1 = 195; mr0 = 0; break; case SUNXI_DRAM_TYPE_LPDDR3: tfaw = max(ns_to_t(50), 4); trrd = max(ns_to_t(10), 1); trcd = max(ns_to_t(24), 2); trc = ns_to_t(70); twtr = max(ns_to_t(8), 2); twr = max(ns_to_t(15), 2); trp = ns_to_t(17); tras = ns_to_t(42); trefi = ns_to_t(3900) / 32; trfc = ns_to_t(210); txp = twtr; trasmax = CONFIG_DRAM_CLK / 60; if (CONFIG_DRAM_CLK < 800) { tcwl = 4; wr_latency = 3; t_rdata_en = 6; mr2 = 12; } else { tcwl = 3; tcke = 6; wr_latency = 2; t_rdata_en = 5; mr2 = 10; } twtp = tcwl + 5; tcl = 7; mr3 = para->dram_mr3; tcksrx = 5; tckesr = 5; trd2wr = 13; tcke = 3; tmod = 12; tdinit0 = 400 * CONFIG_DRAM_CLK + 1; tdinit1 = 500 * CONFIG_DRAM_CLK / 1000 + 1; tdinit2 = 11 * CONFIG_DRAM_CLK + 1; tdinit3 = 1 * CONFIG_DRAM_CLK + 1; tmrd = 5; tmrw = 5; twr2rd = tcwl + twtr + 5; mr1 = 195; mr0 = 0; break; default: trfc = 128; trp = 6; trefi = 98; txp = 10; twr = 8; twtr = 3; tras = 14; tfaw = 16; trc = 20; trcd = 6; trrd = 3; twr2rd = 8; tcksrx = 4; tckesr = 3; trd2wr = 4; trasmax = 27; twtp = 12; tcke = 2; tmod = 6; tmrd = 2; tmrw = 0; tcwl = 3; tcl = 3; wr_latency = 1; t_rdata_en = 1; mr3 = 0; mr2 = 0; mr1 = 0; mr0 = 0; tdinit3 = 0; tdinit2 = 0; tdinit1 = 0; tdinit0 = 0; break; } /* Set mode registers */ writel(mr0, 0x3103030); writel(mr1, 0x3103034); writel(mr2, 0x3103038); writel(mr3, 0x310303c); /* TODO: dram_odt_en is either 0x0 or 0x1, so right shift looks weird */ writel((para->dram_odt_en >> 4) & 0x3, 0x310302c); /* Set dram timing DRAMTMG0 - DRAMTMG5 */ writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0), 0x3103058); writel((txp << 16) | (trtp << 8) | (trc << 0), 0x310305c); writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0), 0x3103060); writel((tmrw << 16) | (tmrd << 12) | (tmod << 0), 0x3103064); writel((trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0), 0x3103068); writel((tcksrx << 24) | (tcksrx << 16) | (tckesr << 8) | (tcke << 0), 0x310306c); /* Set dual rank timing */ clrsetbits_le32(0x3103078, 0xf000ffff, (CONFIG_DRAM_CLK < 800) ? 0xf0006610 : 0xf0007610); /* Set phy interface time PITMG0, PTR3, PTR4 */ writel((0x2 << 24) | (t_rdata_en << 16) | BIT(8) | (wr_latency << 0), 0x3103080); writel(((tdinit0 << 0) | (tdinit1 << 20)), 0x3103050); writel(((tdinit2 << 0) | (tdinit3 << 20)), 0x3103054); /* Set refresh timing and mode */ writel((trefi << 16) | (trfc << 0), 0x3103090); writel((trefi << 15) & 0x0fff0000, 0x3103094); } // Purpose of this routine seems to be to initialize the PLL driving // the MBUS and sdram. // static int ccu_set_pll_ddr_clk(int index, const dram_para_t *para, const dram_config_t *config) { unsigned int val, clk, n; if (config->dram_tpr13 & BIT(6)) clk = para->dram_tpr9; else clk = para->dram_clk; // set VCO clock divider n = (clk * 2) / 24; val = readl(SUNXI_CCM_BASE + 0x10); val &= ~0x0007ff03; // clear dividers val |= (n - 1) << 8; // set PLL division val |= BIT(31) | BIT(30); // enable PLL and LDO writel(val | BIT(29), SUNXI_CCM_BASE + 0x10); // wait for PLL to lock while ((readl(SUNXI_CCM_BASE + 0x10) & BIT(28)) == 0) ; udelay(20); // enable PLL output setbits_le32(SUNXI_CCM_BASE + 0x0, BIT(27)); // turn clock gate on val = readl(SUNXI_CCM_BASE + 0x800); val &= ~0x03000303; // select DDR clk source, n=1, m=1 val |= BIT(31); // turn clock on writel(val, SUNXI_CCM_BASE + 0x800); return n * 24; } /* Set up the PLL and clock gates for the DRAM controller and MBUS clocks. */ static void mctl_sys_init(const dram_para_t *para, const dram_config_t *config) { // assert MBUS reset clrbits_le32(SUNXI_CCM_BASE + 0x540, BIT(30)); // turn off sdram clock gate, assert sdram reset clrbits_le32(SUNXI_CCM_BASE + 0x80c, 0x10001); clrsetbits_le32(SUNXI_CCM_BASE + 0x800, BIT(31) | BIT(30), BIT(27)); udelay(10); // set ddr pll clock ccu_set_pll_ddr_clk(0, para, config); udelay(100); dram_disable_all_master(); // release sdram reset setbits_le32(SUNXI_CCM_BASE + 0x80c, BIT(16)); // release MBUS reset setbits_le32(SUNXI_CCM_BASE + 0x540, BIT(30)); setbits_le32(SUNXI_CCM_BASE + 0x800, BIT(30)); udelay(5); // turn on sdram clock gate setbits_le32(SUNXI_CCM_BASE + 0x80c, BIT(0)); // turn dram clock gate on, trigger sdr clock update setbits_le32(SUNXI_CCM_BASE + 0x800, BIT(31) | BIT(27)); udelay(5); // mCTL clock enable writel(0x8000, 0x310300c); udelay(10); } // The main purpose of this routine seems to be to copy an address configuration // from the dram_para1 and dram_para2 fields to the PHY configuration registers // (0x3102000, 0x3102004). // static void mctl_com_init(const dram_para_t *para, const dram_config_t *config) { uint32_t val, width; unsigned long ptr; int i; // purpose ?? clrsetbits_le32(0x3102008, 0x3f00, 0x2000); // set SDRAM type and word width val = readl(0x3102000) & ~0x00fff000; val |= (para->dram_type & 0x7) << 16; // DRAM type val |= (~config->dram_para2 & 0x1) << 12; // DQ width val |= BIT(22); // ?? if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 || para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) { val |= BIT(19); // type 6 and 7 must use 1T } else { if (config->dram_tpr13 & BIT(5)) val |= BIT(19); } writel(val, 0x3102000); // init rank / bank / row for single/dual or two different ranks if ((config->dram_para2 & BIT(8)) && ((config->dram_para2 & 0xf000) != 0x1000)) width = 32; else width = 16; ptr = 0x3102000; for (i = 0; i < width; i += 16) { val = readl(ptr) & 0xfffff000; val |= (config->dram_para2 >> 12) & 0x3; // rank val |= ((config->dram_para1 >> (i + 12)) << 2) & 0x4; // bank - 2 val |= (((config->dram_para1 >> (i + 4)) - 1) << 4) & 0xff; // row - 1 // convert from page size to column addr width - 3 switch ((config->dram_para1 >> i) & 0xf) { case 8: val |= 0xa00; break; case 4: val |= 0x900; break; case 2: val |= 0x800; break; case 1: val |= 0x700; break; default: val |= 0x600; break; } writel(val, ptr); ptr += 4; } // set ODTMAP based on number of ranks in use val = (readl(0x3102000) & 0x1) ? 0x303 : 0x201; writel(val, 0x3103120); // set mctl reg 3c4 to zero when using half DQ if (config->dram_para2 & BIT(0)) writel(0, 0x31033c4); // purpose ?? if (para->dram_tpr4) { setbits_le32(0x3102000, (para->dram_tpr4 & 0x3) << 25); setbits_le32(0x3102004, (para->dram_tpr4 & 0x7fc) << 10); } } static const uint8_t ac_remapping_tables[][22] = { [0] = { 0 }, [1] = { 1, 9, 3, 7, 8, 18, 4, 13, 5, 6, 10, 2, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 }, [2] = { 4, 9, 3, 7, 8, 18, 1, 13, 2, 6, 10, 5, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 }, [3] = { 1, 7, 8, 12, 10, 18, 4, 13, 5, 6, 3, 2, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 }, [4] = { 4, 12, 10, 7, 8, 18, 1, 13, 2, 6, 3, 5, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 }, [5] = { 13, 2, 7, 9, 12, 19, 5, 1, 6, 3, 4, 8, 10, 0, 0, 0, 21, 22, 18, 17, 11, 20 }, [6] = { 3, 10, 7, 13, 9, 11, 1, 2, 4, 6, 8, 5, 12, 0, 0, 0, 20, 1, 0, 21, 22, 17 }, [7] = { 3, 2, 4, 7, 9, 1, 17, 12, 18, 14, 13, 8, 15, 6, 10, 5, 19, 22, 16, 21, 20, 11 }, }; /* * This routine chooses one of several remapping tables for 22 lines. * It is unclear which lines are being remapped. It seems to pick * table cfg7 for the Nezha board. */ static void mctl_phy_ac_remapping(const dram_para_t *para, const dram_config_t *config) { const uint8_t *cfg; uint32_t fuse, val; /* * It is unclear whether the LPDDRx types don't need any remapping, * or whether the original code just didn't provide tables. */ if (para->dram_type != SUNXI_DRAM_TYPE_DDR2 && para->dram_type != SUNXI_DRAM_TYPE_DDR3) return; fuse = (readl(SUNXI_SID_BASE + 0x28) & 0xf00) >> 8; debug("DDR efuse: 0x%x\n", fuse); if (para->dram_type == SUNXI_DRAM_TYPE_DDR2) { if (fuse == 15) return; cfg = ac_remapping_tables[6]; } else { if (config->dram_tpr13 & 0xc0000) { cfg = ac_remapping_tables[7]; } else { switch (fuse) { case 8: cfg = ac_remapping_tables[2]; break; case 9: cfg = ac_remapping_tables[3]; break; case 10: cfg = ac_remapping_tables[5]; break; case 11: cfg = ac_remapping_tables[4]; break; default: case 12: cfg = ac_remapping_tables[1]; break; case 13: case 14: cfg = ac_remapping_tables[0]; break; } } } val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) | (cfg[1] << 10) | (cfg[0] << 5); writel(val, 0x3102500); val = (cfg[10] << 25) | (cfg[9] << 20) | (cfg[8] << 15) | (cfg[ 7] << 10) | (cfg[6] << 5) | cfg[5]; writel(val, 0x3102504); val = (cfg[15] << 20) | (cfg[14] << 15) | (cfg[13] << 10) | (cfg[12] << 5) | cfg[11]; writel(val, 0x3102508); val = (cfg[21] << 25) | (cfg[20] << 20) | (cfg[19] << 15) | (cfg[18] << 10) | (cfg[17] << 5) | cfg[16]; writel(val, 0x310250c); val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) | (cfg[1] << 10) | (cfg[0] << 5) | 1; writel(val, 0x3102500); } // Init the controller channel. The key part is placing commands in the main // command register (PIR, 0x3103000) and checking command status (PGSR0, 0x3103010). // static unsigned int mctl_channel_init(unsigned int ch_index, const dram_para_t *para, const dram_config_t *config) { unsigned int val, dqs_gating_mode; dqs_gating_mode = (config->dram_tpr13 & 0xc) >> 2; // set DDR clock to half of CPU clock clrsetbits_le32(0x310200c, 0xfff, (para->dram_clk / 2) - 1); // MRCTRL0 nibble 3 undocumented clrsetbits_le32(0x3103108, 0xf00, 0x300); if (para->dram_odt_en) val = 0; else val = BIT(5); // DX0GCR0 if (para->dram_clk > 672) clrsetbits_le32(0x3103344, 0xf63e, val); else clrsetbits_le32(0x3103344, 0xf03e, val); // DX1GCR0 if (para->dram_clk > 672) { setbits_le32(0x3103344, 0x400); clrsetbits_le32(0x31033c4, 0xf63e, val); } else { clrsetbits_le32(0x31033c4, 0xf03e, val); } // 0x3103208 undocumented setbits_le32(0x3103208, BIT(1)); eye_delay_compensation(para); // set PLL SSCG ? val = readl(0x3103108); if (dqs_gating_mode == 1) { clrsetbits_le32(0x3103108, 0xc0, 0); clrbits_le32(0x31030bc, 0x107); } else if (dqs_gating_mode == 2) { clrsetbits_le32(0x3103108, 0xc0, 0x80); clrsetbits_le32(0x31030bc, 0x107, (((config->dram_tpr13 >> 16) & 0x1f) - 2) | 0x100); clrsetbits_le32(0x310311c, BIT(31), BIT(27)); } else { clrbits_le32(0x3103108, 0x40); udelay(10); setbits_le32(0x3103108, 0xc0); } if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 || para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) { if (dqs_gating_mode == 1) clrsetbits_le32(0x310311c, 0x080000c0, 0x80000000); else clrsetbits_le32(0x310311c, 0x77000000, 0x22000000); } clrsetbits_le32(0x31030c0, 0x0fffffff, (config->dram_para2 & BIT(12)) ? 0x03000001 : 0x01000007); if (readl(0x70005d4) & BIT(16)) { clrbits_le32(0x7010250, 0x2); udelay(10); } // Set ZQ config clrsetbits_le32(0x3103140, 0x3ffffff, (para->dram_zq & 0x00ffffff) | BIT(25)); // Initialise DRAM controller if (dqs_gating_mode == 1) { //writel(0x52, 0x3103000); // prep PHY reset + PLL init + z-cal writel(0x53, 0x3103000); // Go while ((readl(0x3103010) & 0x1) == 0) { } // wait for IDONE udelay(10); // 0x520 = prep DQS gating + DRAM init + d-cal if (para->dram_type == SUNXI_DRAM_TYPE_DDR3) writel(0x5a0, 0x3103000); // + DRAM reset else writel(0x520, 0x3103000); } else { if ((readl(0x70005d4) & (1 << 16)) == 0) { // prep DRAM init + PHY reset + d-cal + PLL init + z-cal if (para->dram_type == SUNXI_DRAM_TYPE_DDR3) writel(0x1f2, 0x3103000); // + DRAM reset else writel(0x172, 0x3103000); } else { // prep PHY reset + d-cal + z-cal writel(0x62, 0x3103000); } } setbits_le32(0x3103000, 0x1); // GO udelay(10); while ((readl(0x3103010) & 0x1) == 0) { } // wait for IDONE if (readl(0x70005d4) & BIT(16)) { clrsetbits_le32(0x310310c, 0x06000000, 0x04000000); udelay(10); setbits_le32(0x3103004, 0x1); while ((readl(0x3103018) & 0x7) != 0x3) { } clrbits_le32(0x7010250, 0x1); udelay(10); clrbits_le32(0x3103004, 0x1); while ((readl(0x3103018) & 0x7) != 0x1) { } udelay(15); if (dqs_gating_mode == 1) { clrbits_le32(0x3103108, 0xc0); clrsetbits_le32(0x310310c, 0x06000000, 0x02000000); udelay(1); writel(0x401, 0x3103000); while ((readl(0x3103010) & 0x1) == 0) { } } } // Check for training error if (readl(0x3103010) & BIT(20)) { printf("ZQ calibration error, check external 240 ohm resistor\n"); return 0; } // STATR = Zynq STAT? Wait for status 'normal'? while ((readl(0x3103018) & 0x1) == 0) { } setbits_le32(0x310308c, BIT(31)); udelay(10); clrbits_le32(0x310308c, BIT(31)); udelay(10); setbits_le32(0x3102014, BIT(31)); udelay(10); clrbits_le32(0x310310c, 0x06000000); if (dqs_gating_mode == 1) clrsetbits_le32(0x310311c, 0xc0, 0x40); return 1; } static unsigned int calculate_rank_size(uint32_t regval) { unsigned int bits; bits = (regval >> 8) & 0xf; /* page size - 3 */ bits += (regval >> 4) & 0xf; /* row width - 1 */ bits += (regval >> 2) & 0x3; /* bank count - 2 */ bits -= 14; /* 1MB = 20 bits, minus above 6 = 14 */ return 1U << bits; } /* * The below routine reads the dram config registers and extracts * the number of address bits in each rank available. It then calculates * total memory size in MB. */ static unsigned int DRAMC_get_dram_size(void) { uint32_t val; unsigned int size; val = readl(0x3102000); /* MC_WORK_MODE0 */ size = calculate_rank_size(val); if ((val & 0x3) == 0) /* single rank? */ return size; val = readl(0x3102004); /* MC_WORK_MODE1 */ if ((val & 0x3) == 0) /* two identical ranks? */ return size * 2; /* add sizes of both ranks */ return size + calculate_rank_size(val); } /* * The below routine reads the command status register to extract * DQ width and rank count. This follows the DQS training command in * channel_init. If error bit 22 is reset, we have two ranks and full DQ. * If there was an error, figure out whether it was half DQ, single rank, * or both. Set bit 12 and 0 in dram_para2 with the results. */ static int dqs_gate_detect(dram_config_t *config) { uint32_t dx0, dx1; if ((readl(0x3103010) & BIT(22)) == 0) { config->dram_para2 = (config->dram_para2 & ~0xf) | BIT(12); debug("dual rank and full DQ\n"); return 1; } dx0 = (readl(0x3103348) & 0x3000000) >> 24; if (dx0 == 0) { config->dram_para2 = (config->dram_para2 & ~0xf) | 0x1001; debug("dual rank and half DQ\n"); return 1; } if (dx0 == 2) { dx1 = (readl(0x31033c8) & 0x3000000) >> 24; if (dx1 == 2) { config->dram_para2 = config->dram_para2 & ~0xf00f; debug("single rank and full DQ\n"); } else { config->dram_para2 = (config->dram_para2 & ~0xf00f) | BIT(0); debug("single rank and half DQ\n"); } return 1; } if ((config->dram_tpr13 & BIT(29)) == 0) return 0; debug("DX0 state: %d\n", dx0); debug("DX1 state: %d\n", dx1); return 0; } static int dramc_simple_wr_test(unsigned int mem_mb, int len) { unsigned int offs = (mem_mb / 2) << 18; // half of memory size unsigned int patt1 = 0x01234567; unsigned int patt2 = 0xfedcba98; unsigned int *addr, v1, v2, i; addr = (unsigned int *)CFG_SYS_SDRAM_BASE; for (i = 0; i != len; i++, addr++) { writel(patt1 + i, (unsigned long)addr); writel(patt2 + i, (unsigned long)(addr + offs)); } addr = (unsigned int *)CFG_SYS_SDRAM_BASE; for (i = 0; i != len; i++) { v1 = readl((unsigned long)(addr + i)); v2 = patt1 + i; if (v1 != v2) { printf("DRAM: simple test FAIL\n"); printf("%x != %x at address %p\n", v1, v2, addr + i); return 1; } v1 = readl((unsigned long)(addr + offs + i)); v2 = patt2 + i; if (v1 != v2) { printf("DRAM: simple test FAIL\n"); printf("%x != %x at address %p\n", v1, v2, addr + offs + i); return 1; } } debug("DRAM: simple test OK\n"); return 0; } // Set the Vref mode for the controller // static void mctl_vrefzq_init(const dram_para_t *para, const dram_config_t *config) { if (config->dram_tpr13 & BIT(17)) return; clrsetbits_le32(0x3103110, 0x7f7f7f7f, para->dram_tpr5); // IOCVR1 if ((config->dram_tpr13 & BIT(16)) == 0) clrsetbits_le32(0x3103114, 0x7f, para->dram_tpr6 & 0x7f); } // Perform an init of the controller. This is actually done 3 times. The first // time to establish the number of ranks and DQ width. The second time to // establish the actual ram size. The third time is final one, with the final // settings. // static int mctl_core_init(const dram_para_t *para, const dram_config_t *config) { mctl_sys_init(para, config); mctl_vrefzq_init(para, config); mctl_com_init(para, config); mctl_phy_ac_remapping(para, config); mctl_set_timing_params(para, config); return mctl_channel_init(0, para, config); } /* * This routine sizes a DRAM device by cycling through address lines and * figuring out if they are connected to a real address line, or if the * address is a mirror. * First the column and bank bit allocations are set to low values (2 and 9 * address lines). Then a maximum allocation (16 lines) is set for rows and * this is tested. * Next the BA2 line is checked. This seems to be placed above the column, * BA0-1 and row addresses. Finally, the column address is allocated 13 lines * and these are tested. The results are placed in dram_para1 and dram_para2. */ static uint32_t get_payload(bool odd, unsigned long int ptr) { if (odd) return (uint32_t)ptr; else return ~((uint32_t)ptr); } static int auto_scan_dram_size(const dram_para_t *para, dram_config_t *config) { unsigned int rval, i, j, rank, maxrank, offs; unsigned int shft; unsigned long ptr, mc_work_mode, chk; if (mctl_core_init(para, config) == 0) { printf("DRAM initialisation error : 0\n"); return 0; } maxrank = (config->dram_para2 & 0xf000) ? 2 : 1; mc_work_mode = 0x3102000; offs = 0; /* write test pattern */ for (i = 0, ptr = CFG_SYS_SDRAM_BASE; i < 64; i++, ptr += 4) writel(get_payload(i & 0x1, ptr), ptr); for (rank = 0; rank < maxrank;) { /* set row mode */ clrsetbits_le32(mc_work_mode, 0xf0c, 0x6f0); udelay(1); // Scan per address line, until address wraps (i.e. see shadow) for (i = 11; i < 17; i++) { chk = CFG_SYS_SDRAM_BASE + (1U << (i + 11)); ptr = CFG_SYS_SDRAM_BASE; for (j = 0; j < 64; j++) { if (readl(chk) != get_payload(j & 0x1, ptr)) break; ptr += 4; chk += 4; } if (j == 64) break; } if (i > 16) i = 16; debug("rank %d row = %d\n", rank, i); /* Store rows in para 1 */ shft = offs + 4; rval = config->dram_para1; rval &= ~(0xff << shft); rval |= i << shft; config->dram_para1 = rval; if (rank == 1) /* Set bank mode for rank0 */ clrsetbits_le32(0x3102000, 0xffc, 0x6a4); /* Set bank mode for current rank */ clrsetbits_le32(mc_work_mode, 0xffc, 0x6a4); udelay(1); // Test if bit A23 is BA2 or mirror XXX A22? chk = CFG_SYS_SDRAM_BASE + (1U << 22); ptr = CFG_SYS_SDRAM_BASE; for (i = 0, j = 0; i < 64; i++) { if (readl(chk) != get_payload(i & 1, ptr)) { j = 1; break; } ptr += 4; chk += 4; } debug("rank %d bank = %d\n", rank, (j + 1) << 2); /* 4 or 8 */ /* Store banks in para 1 */ shft = 12 + offs; rval = config->dram_para1; rval &= ~(0xf << shft); rval |= j << shft; config->dram_para1 = rval; if (rank == 1) /* Set page mode for rank0 */ clrsetbits_le32(0x3102000, 0xffc, 0xaa0); /* Set page mode for current rank */ clrsetbits_le32(mc_work_mode, 0xffc, 0xaa0); udelay(1); // Scan per address line, until address wraps (i.e. see shadow) for (i = 9; i < 14; i++) { chk = CFG_SYS_SDRAM_BASE + (1U << i); ptr = CFG_SYS_SDRAM_BASE; for (j = 0; j < 64; j++) { if (readl(chk) != get_payload(j & 1, ptr)) break; ptr += 4; chk += 4; } if (j == 64) break; } if (i > 13) i = 13; unsigned int pgsize = (i == 9) ? 0 : (1 << (i - 10)); debug("rank %d page size = %d KB\n", rank, pgsize); /* Store page size */ shft = offs; rval = config->dram_para1; rval &= ~(0xf << shft); rval |= pgsize << shft; config->dram_para1 = rval; // Move to next rank rank++; if (rank != maxrank) { if (rank == 1) { /* MC_WORK_MODE */ clrsetbits_le32(0x3202000, 0xffc, 0x6f0); /* MC_WORK_MODE2 */ clrsetbits_le32(0x3202004, 0xffc, 0x6f0); } /* store rank1 config in upper half of para1 */ offs += 16; mc_work_mode += 4; /* move to MC_WORK_MODE2 */ } } if (maxrank == 2) { config->dram_para2 &= 0xfffff0ff; /* note: rval is equal to para->dram_para1 here */ if ((rval & 0xffff) == (rval >> 16)) { debug("rank1 config same as rank0\n"); } else { config->dram_para2 |= BIT(8); debug("rank1 config different from rank0\n"); } } return 1; } /* * This routine sets up parameters with dqs_gating_mode equal to 1 and two * ranks enabled. It then configures the core and tests for 1 or 2 ranks and * full or half DQ width. It then resets the parameters to the original values. * dram_para2 is updated with the rank and width findings. */ static int auto_scan_dram_rank_width(const dram_para_t *para, dram_config_t *config) { unsigned int s1 = config->dram_tpr13; unsigned int s2 = config->dram_para1; config->dram_para1 = 0x00b000b0; config->dram_para2 = (config->dram_para2 & ~0xf) | BIT(12); /* set DQS probe mode */ config->dram_tpr13 = (config->dram_tpr13 & ~0x8) | BIT(2) | BIT(0); mctl_core_init(para, config); if (readl(0x3103010) & BIT(20)) return 0; if (dqs_gate_detect(config) == 0) return 0; config->dram_tpr13 = s1; config->dram_para1 = s2; return 1; } /* * This routine determines the SDRAM topology. It first establishes the number * of ranks and the DQ width. Then it scans the SDRAM address lines to establish * the size of each rank. It then updates dram_tpr13 to reflect that the sizes * are now known: a re-init will not repeat the autoscan. */ static int auto_scan_dram_config(const dram_para_t *para, dram_config_t *config) { if (((config->dram_tpr13 & BIT(14)) == 0) && (auto_scan_dram_rank_width(para, config) == 0)) { printf("ERROR: auto scan dram rank & width failed\n"); return 0; } if (((config->dram_tpr13 & BIT(0)) == 0) && (auto_scan_dram_size(para, config) == 0)) { printf("ERROR: auto scan dram size failed\n"); return 0; } if ((config->dram_tpr13 & BIT(15)) == 0) config->dram_tpr13 |= BIT(14) | BIT(13) | BIT(1) | BIT(0); return 1; } static int init_DRAM(int type, const dram_para_t *para) { dram_config_t config = { .dram_para1 = 0x000010d2, .dram_para2 = 0, .dram_tpr13 = CONFIG_DRAM_SUNXI_TPR13, }; u32 rc, mem_size_mb; debug("DRAM BOOT DRIVE INFO: %s\n", "V0.24"); debug("DRAM CLK = %d MHz\n", para->dram_clk); debug("DRAM Type = %d (2:DDR2,3:DDR3)\n", para->dram_type); if ((para->dram_odt_en & 0x1) == 0) debug("DRAMC read ODT off\n"); else debug("DRAMC ZQ value: 0x%x\n", para->dram_zq); /* Test ZQ status */ if (config.dram_tpr13 & BIT(16)) { debug("DRAM only have internal ZQ\n"); setbits_le32(0x3000160, BIT(8)); writel(0, 0x3000168); udelay(10); } else { clrbits_le32(0x3000160, 0x3); writel(config.dram_tpr13 & BIT(16), 0x7010254); udelay(10); clrsetbits_le32(0x3000160, 0x108, BIT(1)); udelay(10); setbits_le32(0x3000160, BIT(0)); udelay(20); debug("ZQ value = 0x%x\n", readl(0x300016c)); } dram_voltage_set(para); /* Set SDRAM controller auto config */ if ((config.dram_tpr13 & BIT(0)) == 0) { if (auto_scan_dram_config(para, &config) == 0) { printf("auto_scan_dram_config() FAILED\n"); return 0; } } /* report ODT */ rc = para->dram_mr1; if ((rc & 0x44) == 0) debug("DRAM ODT off\n"); else debug("DRAM ODT value: 0x%x\n", rc); /* Init core, final run */ if (mctl_core_init(para, &config) == 0) { printf("DRAM initialisation error: 1\n"); return 0; } /* Get SDRAM size */ /* TODO: who ever puts a negative number in the top half? */ rc = config.dram_para2; if (rc & BIT(31)) { rc = (rc >> 16) & ~BIT(15); } else { rc = DRAMC_get_dram_size(); debug("DRAM: size = %dMB\n", rc); config.dram_para2 = (config.dram_para2 & 0xffffU) | rc << 16; } mem_size_mb = rc; /* Purpose ?? */ if (config.dram_tpr13 & BIT(30)) { rc = para->dram_tpr8; if (rc == 0) rc = 0x10000200; writel(rc, 0x31030a0); writel(0x40a, 0x310309c); setbits_le32(0x3103004, BIT(0)); debug("Enable Auto SR\n"); } else { clrbits_le32(0x31030a0, 0xffff); clrbits_le32(0x3103004, 0x1); } /* Purpose ?? */ if (config.dram_tpr13 & BIT(9)) { clrsetbits_le32(0x3103100, 0xf000, 0x5000); } else { if (para->dram_type != SUNXI_DRAM_TYPE_LPDDR2) clrbits_le32(0x3103100, 0xf000); } setbits_le32(0x3103140, BIT(31)); /* CHECK: is that really writing to a different register? */ if (config.dram_tpr13 & BIT(8)) writel(readl(0x3103140) | 0x300, 0x31030b8); if (config.dram_tpr13 & BIT(16)) clrbits_le32(0x3103108, BIT(13)); else setbits_le32(0x3103108, BIT(13)); /* Purpose ?? */ if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) clrsetbits_le32(0x310307c, 0xf0000, 0x1000); dram_enable_all_master(); if (config.dram_tpr13 & BIT(28)) { if ((readl(0x70005d4) & BIT(16)) || dramc_simple_wr_test(mem_size_mb, 4096)) return 0; } return mem_size_mb; } static const dram_para_t para = { .dram_clk = CONFIG_DRAM_CLK, .dram_type = CONFIG_SUNXI_DRAM_TYPE, .dram_zq = CONFIG_DRAM_ZQ, .dram_odt_en = CONFIG_DRAM_SUNXI_ODT_EN, .dram_mr0 = 0x1c70, .dram_mr1 = 0x42, .dram_mr2 = 0x18, .dram_mr3 = 0, .dram_tpr0 = 0x004a2195, .dram_tpr1 = 0x02423190, .dram_tpr2 = 0x0008b061, .dram_tpr3 = 0xb4787896, // unused .dram_tpr4 = 0, .dram_tpr5 = 0x48484848, .dram_tpr6 = 0x00000048, .dram_tpr7 = 0x1620121e, // unused .dram_tpr8 = 0, .dram_tpr9 = 0, // clock? .dram_tpr10 = 0, .dram_tpr11 = CONFIG_DRAM_SUNXI_TPR11, .dram_tpr12 = CONFIG_DRAM_SUNXI_TPR12, }; unsigned long sunxi_dram_init(void) { return init_DRAM(0, ¶) * 1024UL * 1024; }; #ifdef CONFIG_RAM /* using the driver model */ struct sunxi_ram_priv { size_t size; }; static int sunxi_ram_probe(struct udevice *dev) { struct sunxi_ram_priv *priv = dev_get_priv(dev); unsigned long dram_size; debug("%s: %s: probing\n", __func__, dev->name); dram_size = sunxi_dram_init(); if (!dram_size) { printf("DRAM init failed\n"); return -ENODEV; } priv->size = dram_size; return 0; } static int sunxi_ram_get_info(struct udevice *dev, struct ram_info *info) { struct sunxi_ram_priv *priv = dev_get_priv(dev); debug("%s: %s: getting info\n", __func__, dev->name); info->base = CFG_SYS_SDRAM_BASE; info->size = priv->size; return 0; } static struct ram_ops sunxi_ram_ops = { .get_info = sunxi_ram_get_info, }; static const struct udevice_id sunxi_ram_ids[] = { { .compatible = "allwinner,sun20i-d1-mbus" }, { } }; U_BOOT_DRIVER(sunxi_ram) = { .name = "sunxi_ram", .id = UCLASS_RAM, .of_match = sunxi_ram_ids, .ops = &sunxi_ram_ops, .probe = sunxi_ram_probe, .priv_auto = sizeof(struct sunxi_ram_priv), }; #endif /* CONFIG_RAM (using driver model) */