11881cdb1SYouMin Chen // SPDX-License-Identifier: GPL-2.0 25eeb396bSKever Yang /* 31881cdb1SYouMin Chen * (C) Copyright 2018 Rockchip Electronics Co., Ltd. 45eeb396bSKever Yang */ 55eeb396bSKever Yang 65eeb396bSKever Yang #include <common.h> 71881cdb1SYouMin Chen #include <debug_uart.h> 85eeb396bSKever Yang #include <dm.h> 95eeb396bSKever Yang #include <ram.h> 105eeb396bSKever Yang #include <syscon.h> 111881cdb1SYouMin Chen #include <asm/io.h> 125eeb396bSKever Yang #include <asm/arch/clock.h> 131881cdb1SYouMin Chen #include <asm/arch/cru_px30.h> 145eeb396bSKever Yang #include <asm/arch/grf_px30.h> 151881cdb1SYouMin Chen #include <asm/arch/hardware.h> 16e1f97ec3SYouMin Chen #include <asm/arch/sdram.h> 171881cdb1SYouMin Chen #include <asm/arch/sdram_px30.h> 181881cdb1SYouMin Chen 191881cdb1SYouMin Chen /* 201881cdb1SYouMin Chen * Because px30 sram size is small, so need define CONFIG_TPL_TINY_FRAMEWORK 211881cdb1SYouMin Chen * to reduce TPL size when build TPL firmware. 221881cdb1SYouMin Chen */ 231881cdb1SYouMin Chen #ifdef CONFIG_TPL_BUILD 241881cdb1SYouMin Chen #ifndef CONFIG_TPL_TINY_FRAMEWORK 251881cdb1SYouMin Chen #error please defined CONFIG_TPL_TINY_FRAMEWORK for px30 !!! 261881cdb1SYouMin Chen #endif 271881cdb1SYouMin Chen #endif 285eeb396bSKever Yang 29cf41a383STang Yun ping #ifdef CONFIG_TPL_BUILD 30cf41a383STang Yun ping 315eeb396bSKever Yang DECLARE_GLOBAL_DATA_PTR; 325eeb396bSKever Yang struct dram_info { 3355c5751eSYouMin Chen struct ddr_pctl_regs *pctl; 3455c5751eSYouMin Chen struct ddr_phy_regs *phy; 351881cdb1SYouMin Chen struct px30_cru *cru; 3655c5751eSYouMin Chen struct msch_regs *msch; 371881cdb1SYouMin Chen struct px30_ddr_grf_regs *ddr_grf; 381881cdb1SYouMin Chen struct px30_grf *grf; 395eeb396bSKever Yang struct ram_info info; 405eeb396bSKever Yang struct px30_pmugrf *pmugrf; 415eeb396bSKever Yang }; 425eeb396bSKever Yang 431881cdb1SYouMin Chen #define PMUGRF_BASE_ADDR 0xFF010000 441881cdb1SYouMin Chen #define CRU_BASE_ADDR 0xFF2B0000 451881cdb1SYouMin Chen #define GRF_BASE_ADDR 0xFF140000 461881cdb1SYouMin Chen #define DDRC_BASE_ADDR 0xFF600000 471881cdb1SYouMin Chen #define DDR_PHY_BASE_ADDR 0xFF2A0000 481881cdb1SYouMin Chen #define SERVER_MSCH0_BASE_ADDR 0xFF530000 491881cdb1SYouMin Chen #define DDR_GRF_BASE_ADDR 0xff630000 501881cdb1SYouMin Chen 511881cdb1SYouMin Chen struct dram_info dram_info; 521881cdb1SYouMin Chen 531881cdb1SYouMin Chen struct px30_sdram_params sdram_configs[] = { 54c90232abSKever Yang #ifdef CONFIG_ROCKCHIP_RK3326 55c90232abSKever Yang #include "sdram-px30-lpddr3-detect-333.inc" 56c90232abSKever Yang #else 5793db2afcSKever Yang #include "sdram-px30-ddr3-detect-333.inc" 58c90232abSKever Yang #endif 591881cdb1SYouMin Chen }; 601881cdb1SYouMin Chen 6155c5751eSYouMin Chen struct ddr_phy_skew skew = { 621881cdb1SYouMin Chen #include "sdram-px30-ddr_skew.inc" 631881cdb1SYouMin Chen }; 641881cdb1SYouMin Chen 651881cdb1SYouMin Chen static void rkclk_ddr_reset(struct dram_info *dram, 661881cdb1SYouMin Chen u32 ctl_srstn, u32 ctl_psrstn, 671881cdb1SYouMin Chen u32 phy_srstn, u32 phy_psrstn) 681881cdb1SYouMin Chen { 691881cdb1SYouMin Chen writel(upctl2_srstn_req(ctl_srstn) | upctl2_psrstn_req(ctl_psrstn) | 701881cdb1SYouMin Chen upctl2_asrstn_req(ctl_srstn), 711881cdb1SYouMin Chen &dram->cru->softrst_con[1]); 721881cdb1SYouMin Chen writel(ddrphy_srstn_req(phy_srstn) | ddrphy_psrstn_req(phy_psrstn), 731881cdb1SYouMin Chen &dram->cru->softrst_con[2]); 741881cdb1SYouMin Chen } 751881cdb1SYouMin Chen 7655c5751eSYouMin Chen static void rkclk_set_dpll(struct dram_info *dram, unsigned int hz) 771881cdb1SYouMin Chen { 781881cdb1SYouMin Chen unsigned int refdiv, postdiv1, postdiv2, fbdiv; 791881cdb1SYouMin Chen int delay = 1000; 8055c5751eSYouMin Chen u32 mhz = hz / MHz; 811881cdb1SYouMin Chen 821881cdb1SYouMin Chen refdiv = 1; 831881cdb1SYouMin Chen if (mhz <= 300) { 841881cdb1SYouMin Chen postdiv1 = 4; 851881cdb1SYouMin Chen postdiv2 = 2; 861881cdb1SYouMin Chen } else if (mhz <= 400) { 871881cdb1SYouMin Chen postdiv1 = 6; 881881cdb1SYouMin Chen postdiv2 = 1; 891881cdb1SYouMin Chen } else if (mhz <= 600) { 901881cdb1SYouMin Chen postdiv1 = 4; 911881cdb1SYouMin Chen postdiv2 = 1; 921881cdb1SYouMin Chen } else if (mhz <= 800) { 931881cdb1SYouMin Chen postdiv1 = 3; 941881cdb1SYouMin Chen postdiv2 = 1; 951881cdb1SYouMin Chen } else if (mhz <= 1600) { 961881cdb1SYouMin Chen postdiv1 = 2; 971881cdb1SYouMin Chen postdiv2 = 1; 981881cdb1SYouMin Chen } else { 991881cdb1SYouMin Chen postdiv1 = 1; 1001881cdb1SYouMin Chen postdiv2 = 1; 1011881cdb1SYouMin Chen } 1021881cdb1SYouMin Chen fbdiv = (mhz * refdiv * postdiv1 * postdiv2) / 24; 1031881cdb1SYouMin Chen 1041881cdb1SYouMin Chen writel(DPLL_MODE(CLOCK_FROM_XIN_OSC), &dram->cru->mode); 1051881cdb1SYouMin Chen 1061881cdb1SYouMin Chen writel(POSTDIV1(postdiv1) | FBDIV(fbdiv), &dram->cru->pll[1].con0); 1071881cdb1SYouMin Chen writel(DSMPD(1) | POSTDIV2(postdiv2) | REFDIV(refdiv), 1081881cdb1SYouMin Chen &dram->cru->pll[1].con1); 1091881cdb1SYouMin Chen 1101881cdb1SYouMin Chen while (delay > 0) { 1111881cdb1SYouMin Chen udelay(1); 1121881cdb1SYouMin Chen if (LOCK(readl(&dram->cru->pll[1].con1))) 1131881cdb1SYouMin Chen break; 1141881cdb1SYouMin Chen delay--; 1151881cdb1SYouMin Chen } 1161881cdb1SYouMin Chen 1171881cdb1SYouMin Chen writel(DPLL_MODE(CLOCK_FROM_PLL), &dram->cru->mode); 1181881cdb1SYouMin Chen } 1191881cdb1SYouMin Chen 1201881cdb1SYouMin Chen static void rkclk_configure_ddr(struct dram_info *dram, 1211881cdb1SYouMin Chen struct px30_sdram_params *sdram_params) 1221881cdb1SYouMin Chen { 1231881cdb1SYouMin Chen /* for inno ddr phy need 2*freq */ 12455c5751eSYouMin Chen rkclk_set_dpll(dram, sdram_params->base.ddr_freq * MHz * 2); 1251881cdb1SYouMin Chen } 1261881cdb1SYouMin Chen 1271881cdb1SYouMin Chen /* return ddrconfig value 1281881cdb1SYouMin Chen * (-1), find ddrconfig fail 1291881cdb1SYouMin Chen * other, the ddrconfig value 1301881cdb1SYouMin Chen * only support cs0_row >= cs1_row 1311881cdb1SYouMin Chen */ 1321881cdb1SYouMin Chen static unsigned int calculate_ddrconfig(struct px30_sdram_params *sdram_params) 1331881cdb1SYouMin Chen { 13455c5751eSYouMin Chen struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; 1351881cdb1SYouMin Chen u32 bw, die_bw, col, bank; 1361881cdb1SYouMin Chen u32 i, tmp; 1371881cdb1SYouMin Chen u32 ddrconf = -1; 1381881cdb1SYouMin Chen 13955c5751eSYouMin Chen bw = cap_info->bw; 14055c5751eSYouMin Chen die_bw = cap_info->dbw; 14155c5751eSYouMin Chen col = cap_info->col; 14255c5751eSYouMin Chen bank = cap_info->bk; 1431881cdb1SYouMin Chen 14455c5751eSYouMin Chen if (sdram_params->base.dramtype == DDR4) { 1451881cdb1SYouMin Chen if (die_bw == 0) 1461881cdb1SYouMin Chen ddrconf = 7 + bw; 1471881cdb1SYouMin Chen else 1481881cdb1SYouMin Chen ddrconf = 12 - bw; 1491881cdb1SYouMin Chen ddrconf = d4_rbc_2_d3_rbc[ddrconf - 7]; 1501881cdb1SYouMin Chen } else { 1511881cdb1SYouMin Chen tmp = ((bank - 2) << 3) | (col + bw - 10); 1521881cdb1SYouMin Chen for (i = 0; i < 7; i++) 1531881cdb1SYouMin Chen if ((ddr_cfg_2_rbc[i] & 0xf) == tmp) { 1541881cdb1SYouMin Chen ddrconf = i; 1551881cdb1SYouMin Chen break; 1561881cdb1SYouMin Chen } 1571881cdb1SYouMin Chen if (i > 6) 1581881cdb1SYouMin Chen printascii("calculate ddrconfig error\n"); 1591881cdb1SYouMin Chen } 1601881cdb1SYouMin Chen 1611881cdb1SYouMin Chen return ddrconf; 1621881cdb1SYouMin Chen } 1631881cdb1SYouMin Chen 1641881cdb1SYouMin Chen /* 1651881cdb1SYouMin Chen * calculate controller dram address map, and setting to register. 1661881cdb1SYouMin Chen * argument sdram_params->ch.ddrconf must be right value before 1671881cdb1SYouMin Chen * call this function. 1681881cdb1SYouMin Chen */ 1691881cdb1SYouMin Chen static void set_ctl_address_map(struct dram_info *dram, 1701881cdb1SYouMin Chen struct px30_sdram_params *sdram_params) 1711881cdb1SYouMin Chen { 17255c5751eSYouMin Chen struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; 1731881cdb1SYouMin Chen void __iomem *pctl_base = dram->pctl; 1741881cdb1SYouMin Chen u32 cs_pst, bg, max_row, ddrconf; 1751881cdb1SYouMin Chen u32 i; 1761881cdb1SYouMin Chen 17755c5751eSYouMin Chen if (sdram_params->base.dramtype == DDR4) 1781881cdb1SYouMin Chen /* 1791881cdb1SYouMin Chen * DDR4 8bit dram BG = 2(4bank groups), 1801881cdb1SYouMin Chen * 16bit dram BG = 1 (2 bank groups) 1811881cdb1SYouMin Chen */ 18255c5751eSYouMin Chen bg = (cap_info->dbw == 0) ? 2 : 1; 1831881cdb1SYouMin Chen else 1841881cdb1SYouMin Chen bg = 0; 1851881cdb1SYouMin Chen 18655c5751eSYouMin Chen cs_pst = cap_info->bw + cap_info->col + 18755c5751eSYouMin Chen bg + cap_info->bk + cap_info->cs0_row; 18855c5751eSYouMin Chen if (cs_pst >= 32 || cap_info->rank == 1) 1891881cdb1SYouMin Chen writel(0x1f, pctl_base + DDR_PCTL2_ADDRMAP0); 1901881cdb1SYouMin Chen else 1911881cdb1SYouMin Chen writel(cs_pst - 8, pctl_base + DDR_PCTL2_ADDRMAP0); 1921881cdb1SYouMin Chen 19355c5751eSYouMin Chen ddrconf = cap_info->ddrconfig; 19455c5751eSYouMin Chen if (sdram_params->base.dramtype == DDR4) { 1951881cdb1SYouMin Chen for (i = 0; i < ARRAY_SIZE(d4_rbc_2_d3_rbc); i++) { 1961881cdb1SYouMin Chen if (d4_rbc_2_d3_rbc[i] == ddrconf) { 1971881cdb1SYouMin Chen ddrconf = 7 + i; 1981881cdb1SYouMin Chen break; 1991881cdb1SYouMin Chen } 2001881cdb1SYouMin Chen } 2011881cdb1SYouMin Chen } 2021881cdb1SYouMin Chen 20355c5751eSYouMin Chen sdram_copy_to_reg((u32 *)(pctl_base + DDR_PCTL2_ADDRMAP1), 2041881cdb1SYouMin Chen &addrmap[ddrconf][0], 8 * 4); 2051881cdb1SYouMin Chen max_row = cs_pst - 1 - 8 - (addrmap[ddrconf][5] & 0xf); 2061881cdb1SYouMin Chen 2071881cdb1SYouMin Chen if (max_row < 12) 2081881cdb1SYouMin Chen printascii("set addrmap fail\n"); 2091881cdb1SYouMin Chen /* need to disable row ahead of rank by set to 0xf */ 2101881cdb1SYouMin Chen for (i = 17; i > max_row; i--) 2111881cdb1SYouMin Chen clrsetbits_le32(pctl_base + DDR_PCTL2_ADDRMAP6 + 2121881cdb1SYouMin Chen ((i - 12) * 8 / 32) * 4, 2131881cdb1SYouMin Chen 0xf << ((i - 12) * 8 % 32), 2141881cdb1SYouMin Chen 0xf << ((i - 12) * 8 % 32)); 2151881cdb1SYouMin Chen 21655c5751eSYouMin Chen if ((sdram_params->base.dramtype == LPDDR3 || 21755c5751eSYouMin Chen sdram_params->base.dramtype == LPDDR2) && 21855c5751eSYouMin Chen cap_info->row_3_4) 2191881cdb1SYouMin Chen setbits_le32(pctl_base + DDR_PCTL2_ADDRMAP6, 1 << 31); 22055c5751eSYouMin Chen if (sdram_params->base.dramtype == DDR4 && cap_info->bw != 0x2) 2211881cdb1SYouMin Chen setbits_le32(pctl_base + DDR_PCTL2_PCCFG, 1 << 8); 2221881cdb1SYouMin Chen } 2231881cdb1SYouMin Chen 2241881cdb1SYouMin Chen /* 2251881cdb1SYouMin Chen * rank = 1: cs0 2261881cdb1SYouMin Chen * rank = 2: cs1 2271881cdb1SYouMin Chen */ 2281881cdb1SYouMin Chen int read_mr(struct dram_info *dram, u32 rank, u32 mr_num) 2291881cdb1SYouMin Chen { 2301881cdb1SYouMin Chen void __iomem *ddr_grf_base = dram->ddr_grf; 2311881cdb1SYouMin Chen 23255c5751eSYouMin Chen pctl_read_mr(dram->pctl, rank, mr_num); 2331881cdb1SYouMin Chen 2341881cdb1SYouMin Chen return (readl(ddr_grf_base + DDR_GRF_STATUS(0)) & 0xff); 2351881cdb1SYouMin Chen } 2361881cdb1SYouMin Chen 2371881cdb1SYouMin Chen #define MIN(a, b) (((a) > (b)) ? (b) : (a)) 2381881cdb1SYouMin Chen #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 2391881cdb1SYouMin Chen static u32 check_rd_gate(struct dram_info *dram) 2401881cdb1SYouMin Chen { 2411881cdb1SYouMin Chen void __iomem *phy_base = dram->phy; 2421881cdb1SYouMin Chen 2431881cdb1SYouMin Chen u32 max_val = 0; 2441881cdb1SYouMin Chen u32 min_val = 0xff; 2451881cdb1SYouMin Chen u32 gate[4]; 2461881cdb1SYouMin Chen u32 i, bw; 2471881cdb1SYouMin Chen 2481881cdb1SYouMin Chen bw = (readl(PHY_REG(phy_base, 0x0)) >> 4) & 0xf; 2491881cdb1SYouMin Chen switch (bw) { 2501881cdb1SYouMin Chen case 0x1: 2511881cdb1SYouMin Chen bw = 1; 2521881cdb1SYouMin Chen break; 2531881cdb1SYouMin Chen case 0x3: 2541881cdb1SYouMin Chen bw = 2; 2551881cdb1SYouMin Chen break; 2561881cdb1SYouMin Chen case 0xf: 2571881cdb1SYouMin Chen default: 2581881cdb1SYouMin Chen bw = 4; 2591881cdb1SYouMin Chen break; 2601881cdb1SYouMin Chen } 2611881cdb1SYouMin Chen 2621881cdb1SYouMin Chen for (i = 0; i < bw; i++) { 2631881cdb1SYouMin Chen gate[i] = readl(PHY_REG(phy_base, 0xfb + i)); 2641881cdb1SYouMin Chen max_val = MAX(max_val, gate[i]); 2651881cdb1SYouMin Chen min_val = MIN(min_val, gate[i]); 2661881cdb1SYouMin Chen } 2671881cdb1SYouMin Chen 2681881cdb1SYouMin Chen if (max_val > 0x80 || min_val < 0x20) 2691881cdb1SYouMin Chen return -1; 2701881cdb1SYouMin Chen else 2711881cdb1SYouMin Chen return 0; 2721881cdb1SYouMin Chen } 2731881cdb1SYouMin Chen 2741881cdb1SYouMin Chen static int data_training(struct dram_info *dram, u32 cs, u32 dramtype) 2751881cdb1SYouMin Chen { 27655c5751eSYouMin Chen void __iomem *pctl_base = dram->pctl; 2771881cdb1SYouMin Chen u32 dis_auto_zq = 0; 27855c5751eSYouMin Chen u32 pwrctl; 27955c5751eSYouMin Chen u32 ret; 2801881cdb1SYouMin Chen 28155c5751eSYouMin Chen /* disable auto low-power */ 28255c5751eSYouMin Chen pwrctl = readl(pctl_base + DDR_PCTL2_PWRCTL); 28355c5751eSYouMin Chen writel(0, pctl_base + DDR_PCTL2_PWRCTL); 2841881cdb1SYouMin Chen 28555c5751eSYouMin Chen dis_auto_zq = pctl_dis_zqcs_aref(dram->pctl); 2861881cdb1SYouMin Chen 28755c5751eSYouMin Chen ret = phy_data_training(dram->phy, cs, dramtype); 2881881cdb1SYouMin Chen 28955c5751eSYouMin Chen pctl_rest_zqcs_aref(dram->pctl, dis_auto_zq); 2901881cdb1SYouMin Chen 29155c5751eSYouMin Chen /* restore auto low-power */ 29255c5751eSYouMin Chen writel(pwrctl, pctl_base + DDR_PCTL2_PWRCTL); 2931881cdb1SYouMin Chen 2941881cdb1SYouMin Chen return ret; 2951881cdb1SYouMin Chen } 2961881cdb1SYouMin Chen 29755c5751eSYouMin Chen static void dram_set_bw(struct dram_info *dram, u32 bw) 2981881cdb1SYouMin Chen { 29955c5751eSYouMin Chen phy_dram_set_bw(dram->phy, bw); 3001881cdb1SYouMin Chen } 3011881cdb1SYouMin Chen 3021881cdb1SYouMin Chen static void set_ddrconfig(struct dram_info *dram, u32 ddrconfig) 3031881cdb1SYouMin Chen { 3041881cdb1SYouMin Chen writel(ddrconfig | (ddrconfig << 8), &dram->msch->deviceconf); 3051881cdb1SYouMin Chen rk_clrsetreg(&dram->grf->soc_noc_con[1], 0x3 << 14, 0 << 14); 3061881cdb1SYouMin Chen } 3071881cdb1SYouMin Chen 3085e6e8f2dSYouMin Chen static void sdram_msch_config(struct msch_regs *msch, 3095e6e8f2dSYouMin Chen struct sdram_msch_timings *noc_timings, 3105e6e8f2dSYouMin Chen struct sdram_cap_info *cap_info, 3115e6e8f2dSYouMin Chen struct sdram_base_params *base) 3125e6e8f2dSYouMin Chen { 3135e6e8f2dSYouMin Chen u64 cs_cap[2]; 3145e6e8f2dSYouMin Chen 3155e6e8f2dSYouMin Chen cs_cap[0] = sdram_get_cs_cap(cap_info, 0, base->dramtype); 3165e6e8f2dSYouMin Chen cs_cap[1] = sdram_get_cs_cap(cap_info, 1, base->dramtype); 3175e6e8f2dSYouMin Chen writel(((((cs_cap[1] >> 20) / 64) & 0xff) << 8) | 3185e6e8f2dSYouMin Chen (((cs_cap[0] >> 20) / 64) & 0xff), 3195e6e8f2dSYouMin Chen &msch->devicesize); 3205e6e8f2dSYouMin Chen 3215e6e8f2dSYouMin Chen writel(noc_timings->ddrtiminga0.d32, 3225e6e8f2dSYouMin Chen &msch->ddrtiminga0); 3235e6e8f2dSYouMin Chen writel(noc_timings->ddrtimingb0.d32, 3245e6e8f2dSYouMin Chen &msch->ddrtimingb0); 3255e6e8f2dSYouMin Chen writel(noc_timings->ddrtimingc0.d32, 3265e6e8f2dSYouMin Chen &msch->ddrtimingc0); 3275e6e8f2dSYouMin Chen writel(noc_timings->devtodev0.d32, 3285e6e8f2dSYouMin Chen &msch->devtodev0); 3295e6e8f2dSYouMin Chen writel(noc_timings->ddrmode.d32, &msch->ddrmode); 3305e6e8f2dSYouMin Chen writel(noc_timings->ddr4timing.d32, 3315e6e8f2dSYouMin Chen &msch->ddr4timing); 3325e6e8f2dSYouMin Chen writel(noc_timings->agingx0, &msch->agingx0); 3335e6e8f2dSYouMin Chen writel(noc_timings->agingx0, &msch->aging0); 3345e6e8f2dSYouMin Chen writel(noc_timings->agingx0, &msch->aging1); 3355e6e8f2dSYouMin Chen writel(noc_timings->agingx0, &msch->aging2); 3365e6e8f2dSYouMin Chen writel(noc_timings->agingx0, &msch->aging3); 3375e6e8f2dSYouMin Chen } 3385e6e8f2dSYouMin Chen 3391881cdb1SYouMin Chen static void dram_all_config(struct dram_info *dram, 3401881cdb1SYouMin Chen struct px30_sdram_params *sdram_params) 3411881cdb1SYouMin Chen { 34255c5751eSYouMin Chen struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; 34355c5751eSYouMin Chen u32 sys_reg2 = 0; 3441881cdb1SYouMin Chen u32 sys_reg3 = 0; 3451881cdb1SYouMin Chen 34655c5751eSYouMin Chen set_ddrconfig(dram, cap_info->ddrconfig); 34755c5751eSYouMin Chen sdram_org_config(cap_info, &sdram_params->base, &sys_reg2, 34855c5751eSYouMin Chen &sys_reg3, 0); 34955c5751eSYouMin Chen writel(sys_reg2, &dram->pmugrf->os_reg[2]); 3501881cdb1SYouMin Chen writel(sys_reg3, &dram->pmugrf->os_reg[3]); 35155c5751eSYouMin Chen sdram_msch_config(dram->msch, &sdram_params->ch.noc_timings, cap_info, 35255c5751eSYouMin Chen &sdram_params->base); 3531881cdb1SYouMin Chen } 3541881cdb1SYouMin Chen 3551881cdb1SYouMin Chen static void enable_low_power(struct dram_info *dram, 3561881cdb1SYouMin Chen struct px30_sdram_params *sdram_params) 3571881cdb1SYouMin Chen { 3581881cdb1SYouMin Chen void __iomem *pctl_base = dram->pctl; 359a82a427dSYouMin Chen void __iomem *phy_base = dram->phy; 3601881cdb1SYouMin Chen void __iomem *ddr_grf_base = dram->ddr_grf; 3611881cdb1SYouMin Chen u32 grf_lp_con; 3621881cdb1SYouMin Chen 3631881cdb1SYouMin Chen /* 3641881cdb1SYouMin Chen * bit0: grf_upctl_axi_cg_en = 1 enable upctl2 axi clk auto gating 3651881cdb1SYouMin Chen * bit1: grf_upctl_apb_cg_en = 1 ungated axi,core clk for apb access 3661881cdb1SYouMin Chen * bit2: grf_upctl_core_cg_en = 1 enable upctl2 core clk auto gating 3671881cdb1SYouMin Chen * bit3: grf_selfref_type2_en = 0 disable core clk gating when type2 sr 3681881cdb1SYouMin Chen * bit4: grf_upctl_syscreq_cg_en = 1 3691881cdb1SYouMin Chen * ungating coreclk when c_sysreq assert 3701881cdb1SYouMin Chen * bit8-11: grf_auto_sr_dly = 6 3711881cdb1SYouMin Chen */ 3721881cdb1SYouMin Chen writel(0x1f1f0617, &dram->ddr_grf->ddr_grf_con[1]); 3731881cdb1SYouMin Chen 37455c5751eSYouMin Chen if (sdram_params->base.dramtype == DDR4) 3751881cdb1SYouMin Chen grf_lp_con = (0x7 << 16) | (1 << 1); 37655c5751eSYouMin Chen else if (sdram_params->base.dramtype == DDR3) 3771881cdb1SYouMin Chen grf_lp_con = (0x7 << 16) | (1 << 0); 3781881cdb1SYouMin Chen else 3791881cdb1SYouMin Chen grf_lp_con = (0x7 << 16) | (1 << 2); 3801881cdb1SYouMin Chen 3811881cdb1SYouMin Chen /* en lpckdis_en */ 3821881cdb1SYouMin Chen grf_lp_con = grf_lp_con | (0x1 << (9 + 16)) | (0x1 << 9); 3831881cdb1SYouMin Chen writel(grf_lp_con, ddr_grf_base + DDR_GRF_LP_CON); 3841881cdb1SYouMin Chen 385a82a427dSYouMin Chen /* off digit module clock when enter power down */ 386a82a427dSYouMin Chen setbits_le32(PHY_REG(phy_base, 7), 1 << 7); 387a82a427dSYouMin Chen 3881881cdb1SYouMin Chen /* enable sr, pd */ 3891881cdb1SYouMin Chen if (PD_IDLE == 0) 3901881cdb1SYouMin Chen clrbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 1)); 3911881cdb1SYouMin Chen else 3921881cdb1SYouMin Chen setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 1)); 3931881cdb1SYouMin Chen if (SR_IDLE == 0) 3941881cdb1SYouMin Chen clrbits_le32(pctl_base + DDR_PCTL2_PWRCTL, 1); 3951881cdb1SYouMin Chen else 3961881cdb1SYouMin Chen setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, 1); 3971881cdb1SYouMin Chen setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 3)); 3981881cdb1SYouMin Chen } 3991881cdb1SYouMin Chen 4001881cdb1SYouMin Chen /* 4011881cdb1SYouMin Chen * pre_init: 0: pre init for dram cap detect 4021881cdb1SYouMin Chen * 1: detect correct cap(except cs1 row)info, than reinit 4031881cdb1SYouMin Chen * 2: after reinit, we detect cs1_row, if cs1_row not equal 4041881cdb1SYouMin Chen * to cs0_row and cs is in middle on ddrconf map, we need 4051881cdb1SYouMin Chen * to reinit dram, than set the correct ddrconf. 4061881cdb1SYouMin Chen */ 4071881cdb1SYouMin Chen static int sdram_init_(struct dram_info *dram, 4081881cdb1SYouMin Chen struct px30_sdram_params *sdram_params, u32 pre_init) 4091881cdb1SYouMin Chen { 41055c5751eSYouMin Chen struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; 4111881cdb1SYouMin Chen void __iomem *pctl_base = dram->pctl; 4121881cdb1SYouMin Chen 4131881cdb1SYouMin Chen rkclk_ddr_reset(dram, 1, 1, 1, 1); 4141881cdb1SYouMin Chen udelay(10); 4151881cdb1SYouMin Chen /* 4161881cdb1SYouMin Chen * dereset ddr phy psrstn to config pll, 4171881cdb1SYouMin Chen * if using phy pll psrstn must be dereset 4181881cdb1SYouMin Chen * before config pll 4191881cdb1SYouMin Chen */ 4201881cdb1SYouMin Chen rkclk_ddr_reset(dram, 1, 1, 1, 0); 4211881cdb1SYouMin Chen rkclk_configure_ddr(dram, sdram_params); 4221881cdb1SYouMin Chen 4231881cdb1SYouMin Chen /* release phy srst to provide clk to ctrl */ 4241881cdb1SYouMin Chen rkclk_ddr_reset(dram, 1, 1, 0, 0); 4251881cdb1SYouMin Chen udelay(10); 42655c5751eSYouMin Chen phy_soft_reset(dram->phy); 4271881cdb1SYouMin Chen /* release ctrl presetn, and config ctl registers */ 4281881cdb1SYouMin Chen rkclk_ddr_reset(dram, 1, 0, 0, 0); 42955c5751eSYouMin Chen pctl_cfg(dram->pctl, &sdram_params->pctl_regs, SR_IDLE, PD_IDLE); 43055c5751eSYouMin Chen cap_info->ddrconfig = calculate_ddrconfig(sdram_params); 4311881cdb1SYouMin Chen set_ctl_address_map(dram, sdram_params); 43255c5751eSYouMin Chen phy_cfg(dram->phy, &sdram_params->phy_regs, sdram_params->skew, 43355c5751eSYouMin Chen &sdram_params->base, cap_info->bw); 4341881cdb1SYouMin Chen 4351881cdb1SYouMin Chen /* enable dfi_init_start to init phy after ctl srstn deassert */ 4361881cdb1SYouMin Chen setbits_le32(pctl_base + DDR_PCTL2_DFIMISC, (1 << 5) | (1 << 4)); 4371881cdb1SYouMin Chen 4381881cdb1SYouMin Chen rkclk_ddr_reset(dram, 0, 0, 0, 0); 4391881cdb1SYouMin Chen /* wait for dfi_init_done and dram init complete */ 4401881cdb1SYouMin Chen while ((readl(pctl_base + DDR_PCTL2_STAT) & 0x7) == 0) 4411881cdb1SYouMin Chen continue; 4421881cdb1SYouMin Chen 44355c5751eSYouMin Chen if (sdram_params->base.dramtype == LPDDR3) 44455c5751eSYouMin Chen pctl_write_mr(dram->pctl, 3, 11, 3, LPDDR3); 4451881cdb1SYouMin Chen 4461881cdb1SYouMin Chen /* do ddr gate training */ 4471881cdb1SYouMin Chen redo_cs0_training: 44855c5751eSYouMin Chen if (data_training(dram, 0, sdram_params->base.dramtype) != 0) { 4491881cdb1SYouMin Chen if (pre_init != 0) 4501881cdb1SYouMin Chen printascii("DTT cs0 error\n"); 4511881cdb1SYouMin Chen return -1; 4521881cdb1SYouMin Chen } 4531881cdb1SYouMin Chen if (check_rd_gate(dram)) { 4541881cdb1SYouMin Chen printascii("re training cs0"); 4551881cdb1SYouMin Chen goto redo_cs0_training; 4561881cdb1SYouMin Chen } 4571881cdb1SYouMin Chen 45855c5751eSYouMin Chen if (sdram_params->base.dramtype == LPDDR3) { 4591881cdb1SYouMin Chen if ((read_mr(dram, 1, 8) & 0x3) != 0x3) 4601881cdb1SYouMin Chen return -1; 46155c5751eSYouMin Chen } else if (sdram_params->base.dramtype == LPDDR2) { 4621881cdb1SYouMin Chen if ((read_mr(dram, 1, 8) & 0x3) != 0x0) 4631881cdb1SYouMin Chen return -1; 4641881cdb1SYouMin Chen } 4651881cdb1SYouMin Chen /* for px30: when 2cs, both 2 cs should be training */ 46655c5751eSYouMin Chen if (pre_init != 0 && cap_info->rank == 2) { 4671881cdb1SYouMin Chen redo_cs1_training: 46855c5751eSYouMin Chen if (data_training(dram, 1, sdram_params->base.dramtype) != 0) { 4691881cdb1SYouMin Chen printascii("DTT cs1 error\n"); 4701881cdb1SYouMin Chen return -1; 4711881cdb1SYouMin Chen } 4721881cdb1SYouMin Chen if (check_rd_gate(dram)) { 4731881cdb1SYouMin Chen printascii("re training cs1"); 4741881cdb1SYouMin Chen goto redo_cs1_training; 4751881cdb1SYouMin Chen } 4761881cdb1SYouMin Chen } 4771881cdb1SYouMin Chen 47855c5751eSYouMin Chen if (sdram_params->base.dramtype == DDR4) 47955c5751eSYouMin Chen pctl_write_vrefdq(dram->pctl, 0x3, 5670, 48055c5751eSYouMin Chen sdram_params->base.dramtype); 4811881cdb1SYouMin Chen 4821881cdb1SYouMin Chen dram_all_config(dram, sdram_params); 4831881cdb1SYouMin Chen enable_low_power(dram, sdram_params); 4841881cdb1SYouMin Chen 4851881cdb1SYouMin Chen return 0; 4861881cdb1SYouMin Chen } 4871881cdb1SYouMin Chen 48855c5751eSYouMin Chen static int dram_detect_cap(struct dram_info *dram, 4891881cdb1SYouMin Chen struct px30_sdram_params *sdram_params, 4901881cdb1SYouMin Chen unsigned char channel) 4911881cdb1SYouMin Chen { 49255c5751eSYouMin Chen struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; 4931881cdb1SYouMin Chen 4941881cdb1SYouMin Chen /* 4951881cdb1SYouMin Chen * for ddr3: ddrconf = 3 4961881cdb1SYouMin Chen * for ddr4: ddrconf = 12 4971881cdb1SYouMin Chen * for lpddr3: ddrconf = 3 4981881cdb1SYouMin Chen * default bw = 1 4991881cdb1SYouMin Chen */ 5001881cdb1SYouMin Chen u32 bk, bktmp; 5011881cdb1SYouMin Chen u32 col, coltmp; 50255c5751eSYouMin Chen u32 rowtmp; 5031881cdb1SYouMin Chen u32 cs; 5041881cdb1SYouMin Chen u32 bw = 1; 50555c5751eSYouMin Chen u32 dram_type = sdram_params->base.dramtype; 5061881cdb1SYouMin Chen 5071881cdb1SYouMin Chen if (dram_type != DDR4) { 5081881cdb1SYouMin Chen /* detect col and bk for ddr3/lpddr3 */ 5091881cdb1SYouMin Chen coltmp = 12; 5101881cdb1SYouMin Chen bktmp = 3; 511f588f59eSYouMin Chen if (dram_type == LPDDR2) 512f588f59eSYouMin Chen rowtmp = 15; 513f588f59eSYouMin Chen else 5141881cdb1SYouMin Chen rowtmp = 16; 5151881cdb1SYouMin Chen 51655c5751eSYouMin Chen if (sdram_detect_col(cap_info, coltmp) != 0) 5171881cdb1SYouMin Chen goto cap_err; 51855c5751eSYouMin Chen sdram_detect_bank(cap_info, coltmp, bktmp); 51955c5751eSYouMin Chen sdram_detect_dbw(cap_info, dram_type); 5201881cdb1SYouMin Chen } else { 5211881cdb1SYouMin Chen /* detect bg for ddr4 */ 5221881cdb1SYouMin Chen coltmp = 10; 5231881cdb1SYouMin Chen bktmp = 4; 5241881cdb1SYouMin Chen rowtmp = 17; 5251881cdb1SYouMin Chen 5261881cdb1SYouMin Chen col = 10; 5271881cdb1SYouMin Chen bk = 2; 52855c5751eSYouMin Chen cap_info->col = col; 52955c5751eSYouMin Chen cap_info->bk = bk; 53055c5751eSYouMin Chen sdram_detect_bg(cap_info, coltmp); 5311881cdb1SYouMin Chen } 53255c5751eSYouMin Chen 5331881cdb1SYouMin Chen /* detect row */ 53455c5751eSYouMin Chen if (sdram_detect_row(cap_info, coltmp, bktmp, rowtmp) != 0) 5351881cdb1SYouMin Chen goto cap_err; 53655c5751eSYouMin Chen 5371881cdb1SYouMin Chen /* detect row_3_4 */ 53855c5751eSYouMin Chen sdram_detect_row_3_4(cap_info, coltmp, bktmp); 5391881cdb1SYouMin Chen 54055c5751eSYouMin Chen /* bw and cs detect using data training */ 5411881cdb1SYouMin Chen if (data_training(dram, 1, dram_type) == 0) 5421881cdb1SYouMin Chen cs = 1; 5431881cdb1SYouMin Chen else 5441881cdb1SYouMin Chen cs = 0; 54555c5751eSYouMin Chen cap_info->rank = cs + 1; 5461881cdb1SYouMin Chen 54755c5751eSYouMin Chen dram_set_bw(dram, 2); 5481881cdb1SYouMin Chen if (data_training(dram, 0, dram_type) == 0) 5491881cdb1SYouMin Chen bw = 2; 5501881cdb1SYouMin Chen else 5511881cdb1SYouMin Chen bw = 1; 55255c5751eSYouMin Chen cap_info->bw = bw; 5531881cdb1SYouMin Chen 55455c5751eSYouMin Chen cap_info->cs0_high16bit_row = cap_info->cs0_row; 5551881cdb1SYouMin Chen if (cs) { 55655c5751eSYouMin Chen cap_info->cs1_row = cap_info->cs0_row; 55755c5751eSYouMin Chen cap_info->cs1_high16bit_row = cap_info->cs0_row; 5581881cdb1SYouMin Chen } else { 55955c5751eSYouMin Chen cap_info->cs1_row = 0; 56055c5751eSYouMin Chen cap_info->cs1_high16bit_row = 0; 5611881cdb1SYouMin Chen } 5621881cdb1SYouMin Chen 56355c5751eSYouMin Chen return 0; 5641881cdb1SYouMin Chen cap_err: 56555c5751eSYouMin Chen return -1; 5661881cdb1SYouMin Chen } 5671881cdb1SYouMin Chen 568cfadd6bbSYouMin Chen void get_ddr_param(struct px30_sdram_params *sdram_params, 569cfadd6bbSYouMin Chen struct ddr_param *ddr_param) 570cfadd6bbSYouMin Chen { 57155c5751eSYouMin Chen struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; 57255c5751eSYouMin Chen u32 dram_type = sdram_params->base.dramtype; 573cfadd6bbSYouMin Chen u64 cs_cap[2]; 574cfadd6bbSYouMin Chen 57555c5751eSYouMin Chen cs_cap[0] = sdram_get_cs_cap(cap_info, 0, dram_type); 57655c5751eSYouMin Chen cs_cap[1] = sdram_get_cs_cap(cap_info, 1, dram_type); 577cfadd6bbSYouMin Chen 57855c5751eSYouMin Chen if (cap_info->row_3_4) { 579cfadd6bbSYouMin Chen cs_cap[0] = cs_cap[0] * 3 / 4; 580cfadd6bbSYouMin Chen cs_cap[1] = cs_cap[1] * 3 / 4; 581cfadd6bbSYouMin Chen } 582cfadd6bbSYouMin Chen 58355c5751eSYouMin Chen if (cap_info->row_3_4 && cap_info->rank == 2) { 584cfadd6bbSYouMin Chen ddr_param->count = 2; 585cfadd6bbSYouMin Chen ddr_param->para[0] = 0; 586cfadd6bbSYouMin Chen ddr_param->para[1] = cs_cap[0] * 4 / 3; 587cfadd6bbSYouMin Chen ddr_param->para[2] = cs_cap[0]; 588cfadd6bbSYouMin Chen ddr_param->para[3] = cs_cap[1]; 589cfadd6bbSYouMin Chen } else { 590cfadd6bbSYouMin Chen ddr_param->count = 1; 591cfadd6bbSYouMin Chen ddr_param->para[0] = 0; 592cfadd6bbSYouMin Chen ddr_param->para[1] = (u64)cs_cap[0] + (u64)cs_cap[1]; 593cfadd6bbSYouMin Chen } 594cfadd6bbSYouMin Chen } 595cfadd6bbSYouMin Chen 5961881cdb1SYouMin Chen /* return: 0 = success, other = fail */ 5971881cdb1SYouMin Chen static int sdram_init_detect(struct dram_info *dram, 5981881cdb1SYouMin Chen struct px30_sdram_params *sdram_params) 5991881cdb1SYouMin Chen { 60055c5751eSYouMin Chen struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; 6011881cdb1SYouMin Chen u32 ret; 6021881cdb1SYouMin Chen u32 sys_reg = 0; 6031881cdb1SYouMin Chen u32 sys_reg3 = 0; 6041881cdb1SYouMin Chen 6051881cdb1SYouMin Chen if (sdram_init_(dram, sdram_params, 0) != 0) 6061881cdb1SYouMin Chen return -1; 6071881cdb1SYouMin Chen 60855c5751eSYouMin Chen if (dram_detect_cap(dram, sdram_params, 0) != 0) 6091881cdb1SYouMin Chen return -1; 6101881cdb1SYouMin Chen 6111881cdb1SYouMin Chen /* modify bw, cs related timing */ 61255c5751eSYouMin Chen pctl_remodify_sdram_params(&sdram_params->pctl_regs, cap_info, 61355c5751eSYouMin Chen sdram_params->base.dramtype); 6141881cdb1SYouMin Chen /* reinit sdram by real dram cap */ 6151881cdb1SYouMin Chen ret = sdram_init_(dram, sdram_params, 1); 6161881cdb1SYouMin Chen if (ret != 0) 6171881cdb1SYouMin Chen goto out; 6181881cdb1SYouMin Chen 6191881cdb1SYouMin Chen /* redetect cs1 row */ 62055c5751eSYouMin Chen sdram_detect_cs1_row(cap_info, sdram_params->base.dramtype); 62155c5751eSYouMin Chen if (cap_info->cs1_row) { 6221881cdb1SYouMin Chen sys_reg = readl(&dram->pmugrf->os_reg[2]); 6231881cdb1SYouMin Chen sys_reg3 = readl(&dram->pmugrf->os_reg[3]); 62455c5751eSYouMin Chen SYS_REG_ENC_CS1_ROW(cap_info->cs1_row, 62555c5751eSYouMin Chen sys_reg, sys_reg3, 0); 6261881cdb1SYouMin Chen writel(sys_reg, &dram->pmugrf->os_reg[2]); 6271881cdb1SYouMin Chen writel(sys_reg3, &dram->pmugrf->os_reg[3]); 6281881cdb1SYouMin Chen } 6291881cdb1SYouMin Chen 630*f627cf25SZhihuan He ret = sdram_detect_high_row(cap_info, sdram_params->base.dramtype); 6311881cdb1SYouMin Chen 6321881cdb1SYouMin Chen out: 6331881cdb1SYouMin Chen return ret; 6341881cdb1SYouMin Chen } 6351881cdb1SYouMin Chen 6361881cdb1SYouMin Chen struct px30_sdram_params 6371881cdb1SYouMin Chen *get_default_sdram_config(void) 6381881cdb1SYouMin Chen { 6391881cdb1SYouMin Chen sdram_configs[0].skew = &skew; 6401881cdb1SYouMin Chen 6411881cdb1SYouMin Chen return &sdram_configs[0]; 6421881cdb1SYouMin Chen } 6431881cdb1SYouMin Chen 6441881cdb1SYouMin Chen /* return: 0 = success, other = fail */ 6451881cdb1SYouMin Chen int sdram_init(void) 6461881cdb1SYouMin Chen { 6471881cdb1SYouMin Chen struct px30_sdram_params *sdram_params; 6481881cdb1SYouMin Chen int ret = 0; 649cfadd6bbSYouMin Chen struct ddr_param ddr_param; 6501881cdb1SYouMin Chen 6511881cdb1SYouMin Chen dram_info.phy = (void *)DDR_PHY_BASE_ADDR; 6521881cdb1SYouMin Chen dram_info.pctl = (void *)DDRC_BASE_ADDR; 6531881cdb1SYouMin Chen dram_info.grf = (void *)GRF_BASE_ADDR; 6541881cdb1SYouMin Chen dram_info.cru = (void *)CRU_BASE_ADDR; 6551881cdb1SYouMin Chen dram_info.msch = (void *)SERVER_MSCH0_BASE_ADDR; 6561881cdb1SYouMin Chen dram_info.ddr_grf = (void *)DDR_GRF_BASE_ADDR; 6571881cdb1SYouMin Chen dram_info.pmugrf = (void *)PMUGRF_BASE_ADDR; 6581881cdb1SYouMin Chen 6591881cdb1SYouMin Chen sdram_params = get_default_sdram_config(); 6601881cdb1SYouMin Chen ret = sdram_init_detect(&dram_info, sdram_params); 6611881cdb1SYouMin Chen 6621881cdb1SYouMin Chen if (ret) 6631881cdb1SYouMin Chen goto error; 6641881cdb1SYouMin Chen 665cfadd6bbSYouMin Chen get_ddr_param(sdram_params, &ddr_param); 666cfadd6bbSYouMin Chen rockchip_setup_ddr_param(&ddr_param); 66755c5751eSYouMin Chen sdram_print_ddr_info(&sdram_params->ch.cap_info, 66855c5751eSYouMin Chen &sdram_params->base, 0); 669504e252fSYouMin Chen 6701881cdb1SYouMin Chen printascii("out\n"); 6711881cdb1SYouMin Chen return ret; 6721881cdb1SYouMin Chen error: 6731881cdb1SYouMin Chen return (-1); 6741881cdb1SYouMin Chen } 6751881cdb1SYouMin Chen #endif /* CONFIG_TPL_BUILD */ 676