// SPDX-License-Identifier: GPL-2.0 /* * (C) Copyright 2018 Rockchip Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct dram_info { #ifdef CONFIG_TPL_BUILD struct ddr_pctl_regs *pctl; struct ddr_phy_regs *phy; struct px30_cru *cru; struct msch_regs *msch; struct px30_ddr_grf_regs *ddr_grf; struct px30_grf *grf; #endif struct ram_info info; struct px30_pmugrf *pmugrf; }; #ifdef CONFIG_TPL_BUILD u8 ddr_cfg_2_rbc[] = { /* * [6:4] max row: 13+n * [3] bank(0:4bank,1:8bank) * [2:0] col(10+n) */ ((5 << 4) | (1 << 3) | 0), /* 0 */ ((5 << 4) | (1 << 3) | 1), /* 1 */ ((4 << 4) | (1 << 3) | 2), /* 2 */ ((3 << 4) | (1 << 3) | 3), /* 3 */ ((2 << 4) | (1 << 3) | 4), /* 4 */ ((5 << 4) | (0 << 3) | 2), /* 5 */ ((4 << 4) | (1 << 3) | 2), /* 6 */ /*((0<<3)|3),*/ /* 12 for ddr4 */ /*((1<<3)|1),*/ /* 13 B,C exchange for rkvdec */ }; /* * for ddr4 if ddrconfig=7, upctl should set 7 and noc should * set to 1 for more efficient. * noc ddrconf, upctl addrmap * 1 7 * 2 8 * 3 9 * 12 10 * 5 11 */ u8 d4_rbc_2_d3_rbc[] = { 1, /* 7 */ 2, /* 8 */ 3, /* 9 */ 12, /* 10 */ 5, /* 11 */ }; /* * row higher than cs should be disabled by set to 0xf * rank addrmap calculate by real cap. */ u32 addrmap[][8] = { /* map0 map1, map2, map3, map4, map5 * map6, map7, map8 * ------------------------------------------------------- * bk2-0 col 5-2 col 9-6 col 11-10 row 11-0 * row 15-12 row 17-16 bg1,0 * ------------------------------------------------------- * 4,3,2 5-2 9-6 6 * 3,2 */ {0x00060606, 0x00000000, 0x1f1f0000, 0x00001f1f, 0x05050505, 0x05050505, 0x00000505, 0x3f3f}, /* 0 */ {0x00070707, 0x00000000, 0x1f000000, 0x00001f1f, 0x06060606, 0x06060606, 0x06060606, 0x3f3f}, /* 1 */ {0x00080808, 0x00000000, 0x00000000, 0x00001f1f, 0x07070707, 0x07070707, 0x00000f07, 0x3f3f}, /* 2 */ {0x00090909, 0x00000000, 0x00000000, 0x00001f00, 0x08080808, 0x08080808, 0x00000f0f, 0x3f3f}, /* 3 */ {0x000a0a0a, 0x00000000, 0x00000000, 0x00000000, 0x09090909, 0x0f090909, 0x00000f0f, 0x3f3f}, /* 4 */ {0x00080808, 0x00000000, 0x00000000, 0x00001f1f, 0x06060606, 0x06060606, 0x00000606, 0x3f3f}, /* 5 */ {0x00080808, 0x00000000, 0x00000000, 0x00001f1f, 0x07070707, 0x07070707, 0x00000f0f, 0x3f3f}, /* 6 */ {0x003f0808, 0x00000006, 0x1f1f0000, 0x00001f1f, 0x06060606, 0x06060606, 0x00000606, 0x0600}, /* 7 */ {0x003f0909, 0x00000007, 0x1f000000, 0x00001f1f, 0x07070707, 0x07070707, 0x00000f07, 0x0700}, /* 8 */ {0x003f0a0a, 0x01010100, 0x01010101, 0x00001f1f, 0x08080808, 0x08080808, 0x00000f0f, 0x0801}, /* 9 */ {0x003f0909, 0x01010100, 0x01010101, 0x00001f1f, 0x07070707, 0x07070707, 0x00000f07, 0x3f01}, /* 10 */ {0x003f0808, 0x00000007, 0x1f000000, 0x00001f1f, 0x06060606, 0x06060606, 0x00000606, 0x3f00}, /* 11 */ /* when ddr4 12 map to 10, when ddr3 12 unused */ {0x003f0909, 0x01010100, 0x01010101, 0x00001f1f, 0x07070707, 0x07070707, 0x00000f07, 0x3f01}, /* 10 */ {0x00070706, 0x00000000, 0x1f010000, 0x00001f1f, 0x06060606, 0x06060606, 0x00000606, 0x3f3f}, /* 13 */ }; #define PMUGRF_BASE_ADDR 0xFF010000 #define CRU_BASE_ADDR 0xFF2B0000 #define GRF_BASE_ADDR 0xFF140000 #define DDRC_BASE_ADDR 0xFF600000 #define DDR_PHY_BASE_ADDR 0xFF2A0000 #define SERVER_MSCH0_BASE_ADDR 0xFF530000 #define DDR_GRF_BASE_ADDR 0xff630000 struct dram_info dram_info; struct px30_sdram_params sdram_configs[] = { #if defined(CONFIG_RAM_ROCKCHIP_DDR4) #include "sdram-px30-ddr4-detect-333.inc" #elif defined(CONFIG_RAM_ROCKCHIP_LPDDR2) #include "sdram-px30-lpddr2-detect-333.inc" #elif defined(CONFIG_RAM_ROCKCHIP_LPDDR3) #include "sdram-px30-lpddr3-detect-333.inc" #else #include "sdram-px30-ddr3-detect-333.inc" #endif }; struct ddr_phy_skew skew = { #include "sdram-px30-ddr_skew.inc" }; static void rkclk_ddr_reset(struct dram_info *dram, u32 ctl_srstn, u32 ctl_psrstn, u32 phy_srstn, u32 phy_psrstn) { writel(upctl2_srstn_req(ctl_srstn) | upctl2_psrstn_req(ctl_psrstn) | upctl2_asrstn_req(ctl_srstn), &dram->cru->softrst_con[1]); writel(ddrphy_srstn_req(phy_srstn) | ddrphy_psrstn_req(phy_psrstn), &dram->cru->softrst_con[2]); } static void rkclk_set_dpll(struct dram_info *dram, unsigned int hz) { unsigned int refdiv, postdiv1, postdiv2, fbdiv; int delay = 1000; u32 mhz = hz / MHz; refdiv = 1; if (mhz <= 300) { postdiv1 = 4; postdiv2 = 2; } else if (mhz <= 400) { postdiv1 = 6; postdiv2 = 1; } else if (mhz <= 600) { postdiv1 = 4; postdiv2 = 1; } else if (mhz <= 800) { postdiv1 = 3; postdiv2 = 1; } else if (mhz <= 1600) { postdiv1 = 2; postdiv2 = 1; } else { postdiv1 = 1; postdiv2 = 1; } fbdiv = (mhz * refdiv * postdiv1 * postdiv2) / 24; writel(DPLL_MODE(CLOCK_FROM_XIN_OSC), &dram->cru->mode); writel(POSTDIV1(postdiv1) | FBDIV(fbdiv), &dram->cru->pll[1].con0); writel(DSMPD(1) | POSTDIV2(postdiv2) | REFDIV(refdiv), &dram->cru->pll[1].con1); while (delay > 0) { udelay(1); if (LOCK(readl(&dram->cru->pll[1].con1))) break; delay--; } writel(DPLL_MODE(CLOCK_FROM_PLL), &dram->cru->mode); } static void rkclk_configure_ddr(struct dram_info *dram, struct px30_sdram_params *sdram_params) { /* for inno ddr phy need 2*freq */ rkclk_set_dpll(dram, sdram_params->base.ddr_freq * MHz * 2); } /* return ddrconfig value * (-1), find ddrconfig fail * other, the ddrconfig value * only support cs0_row >= cs1_row */ static unsigned int calculate_ddrconfig(struct px30_sdram_params *sdram_params) { struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; u32 bw, die_bw, col, bank; u32 i, tmp; u32 ddrconf = -1; bw = cap_info->bw; die_bw = cap_info->dbw; col = cap_info->col; bank = cap_info->bk; if (sdram_params->base.dramtype == DDR4) { if (die_bw == 0) ddrconf = 7 + bw; else ddrconf = 12 - bw; ddrconf = d4_rbc_2_d3_rbc[ddrconf - 7]; } else { tmp = ((bank - 2) << 3) | (col + bw - 10); for (i = 0; i < 7; i++) if ((ddr_cfg_2_rbc[i] & 0xf) == tmp) { ddrconf = i; break; } if (i > 6) printascii("calculate ddrconfig error\n"); } return ddrconf; } /* * calculate controller dram address map, and setting to register. * argument sdram_params->ch.ddrconf must be right value before * call this function. */ static void set_ctl_address_map(struct dram_info *dram, struct px30_sdram_params *sdram_params) { struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; void __iomem *pctl_base = dram->pctl; u32 cs_pst, bg, max_row, ddrconf; u32 i; if (sdram_params->base.dramtype == DDR4) /* * DDR4 8bit dram BG = 2(4bank groups), * 16bit dram BG = 1 (2 bank groups) */ bg = (cap_info->dbw == 0) ? 2 : 1; else bg = 0; cs_pst = cap_info->bw + cap_info->col + bg + cap_info->bk + cap_info->cs0_row; if (cs_pst >= 32 || cap_info->rank == 1) writel(0x1f, pctl_base + DDR_PCTL2_ADDRMAP0); else writel(cs_pst - 8, pctl_base + DDR_PCTL2_ADDRMAP0); ddrconf = cap_info->ddrconfig; if (sdram_params->base.dramtype == DDR4) { for (i = 0; i < ARRAY_SIZE(d4_rbc_2_d3_rbc); i++) { if (d4_rbc_2_d3_rbc[i] == ddrconf) { ddrconf = 7 + i; break; } } } sdram_copy_to_reg((u32 *)(pctl_base + DDR_PCTL2_ADDRMAP1), &addrmap[ddrconf][0], 8 * 4); max_row = cs_pst - 1 - 8 - (addrmap[ddrconf][5] & 0xf); if (max_row < 12) printascii("set addrmap fail\n"); /* need to disable row ahead of rank by set to 0xf */ for (i = 17; i > max_row; i--) clrsetbits_le32(pctl_base + DDR_PCTL2_ADDRMAP6 + ((i - 12) * 8 / 32) * 4, 0xf << ((i - 12) * 8 % 32), 0xf << ((i - 12) * 8 % 32)); if ((sdram_params->base.dramtype == LPDDR3 || sdram_params->base.dramtype == LPDDR2) && cap_info->row_3_4) setbits_le32(pctl_base + DDR_PCTL2_ADDRMAP6, 1 << 31); if (sdram_params->base.dramtype == DDR4 && cap_info->bw != 0x2) setbits_le32(pctl_base + DDR_PCTL2_PCCFG, 1 << 8); } /* * rank = 1: cs0 * rank = 2: cs1 */ int read_mr(struct dram_info *dram, u32 rank, u32 mr_num) { void __iomem *ddr_grf_base = dram->ddr_grf; pctl_read_mr(dram->pctl, rank, mr_num); return (readl(ddr_grf_base + DDR_GRF_STATUS(0)) & 0xff); } #define MIN(a, b) (((a) > (b)) ? (b) : (a)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) static u32 check_rd_gate(struct dram_info *dram) { void __iomem *phy_base = dram->phy; u32 max_val = 0; u32 min_val = 0xff; u32 gate[4]; u32 i, bw; bw = (readl(PHY_REG(phy_base, 0x0)) >> 4) & 0xf; switch (bw) { case 0x1: bw = 1; break; case 0x3: bw = 2; break; case 0xf: default: bw = 4; break; } for (i = 0; i < bw; i++) { gate[i] = readl(PHY_REG(phy_base, 0xfb + i)); max_val = MAX(max_val, gate[i]); min_val = MIN(min_val, gate[i]); } if (max_val > 0x80 || min_val < 0x20) return -1; else return 0; } static int data_training(struct dram_info *dram, u32 cs, u32 dramtype) { void __iomem *pctl_base = dram->pctl; u32 dis_auto_zq = 0; u32 pwrctl; u32 ret; /* disable auto low-power */ pwrctl = readl(pctl_base + DDR_PCTL2_PWRCTL); writel(0, pctl_base + DDR_PCTL2_PWRCTL); dis_auto_zq = pctl_dis_zqcs_aref(dram->pctl); ret = phy_data_training(dram->phy, cs, dramtype); pctl_rest_zqcs_aref(dram->pctl, dis_auto_zq); /* restore auto low-power */ writel(pwrctl, pctl_base + DDR_PCTL2_PWRCTL); return ret; } static void dram_set_bw(struct dram_info *dram, u32 bw) { phy_dram_set_bw(dram->phy, bw); } static void set_ddrconfig(struct dram_info *dram, u32 ddrconfig) { writel(ddrconfig | (ddrconfig << 8), &dram->msch->deviceconf); rk_clrsetreg(&dram->grf->soc_noc_con[1], 0x3 << 14, 0 << 14); } static void sdram_msch_config(struct msch_regs *msch, struct sdram_msch_timings *noc_timings, struct sdram_cap_info *cap_info, struct sdram_base_params *base) { u64 cs_cap[2]; cs_cap[0] = sdram_get_cs_cap(cap_info, 0, base->dramtype); cs_cap[1] = sdram_get_cs_cap(cap_info, 1, base->dramtype); writel(((((cs_cap[1] >> 20) / 64) & 0xff) << 8) | (((cs_cap[0] >> 20) / 64) & 0xff), &msch->devicesize); writel(noc_timings->ddrtiminga0.d32, &msch->ddrtiminga0); writel(noc_timings->ddrtimingb0.d32, &msch->ddrtimingb0); writel(noc_timings->ddrtimingc0.d32, &msch->ddrtimingc0); writel(noc_timings->devtodev0.d32, &msch->devtodev0); writel(noc_timings->ddrmode.d32, &msch->ddrmode); writel(noc_timings->ddr4timing.d32, &msch->ddr4timing); writel(noc_timings->agingx0, &msch->agingx0); writel(noc_timings->agingx0, &msch->aging0); writel(noc_timings->agingx0, &msch->aging1); writel(noc_timings->agingx0, &msch->aging2); writel(noc_timings->agingx0, &msch->aging3); } static void dram_all_config(struct dram_info *dram, struct px30_sdram_params *sdram_params) { struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; u32 sys_reg2 = 0; u32 sys_reg3 = 0; set_ddrconfig(dram, cap_info->ddrconfig); sdram_org_config(cap_info, &sdram_params->base, &sys_reg2, &sys_reg3, 0); writel(sys_reg2, &dram->pmugrf->os_reg[2]); writel(sys_reg3, &dram->pmugrf->os_reg[3]); sdram_msch_config(dram->msch, &sdram_params->ch.noc_timings, cap_info, &sdram_params->base); } static void enable_low_power(struct dram_info *dram, struct px30_sdram_params *sdram_params) { void __iomem *pctl_base = dram->pctl; void __iomem *phy_base = dram->phy; void __iomem *ddr_grf_base = dram->ddr_grf; u32 grf_lp_con; /* * bit0: grf_upctl_axi_cg_en = 1 enable upctl2 axi clk auto gating * bit1: grf_upctl_apb_cg_en = 1 ungated axi,core clk for apb access * bit2: grf_upctl_core_cg_en = 1 enable upctl2 core clk auto gating * bit3: grf_selfref_type2_en = 0 disable core clk gating when type2 sr * bit4: grf_upctl_syscreq_cg_en = 1 * ungating coreclk when c_sysreq assert * bit8-11: grf_auto_sr_dly = 6 */ writel(0x1f1f0617, &dram->ddr_grf->ddr_grf_con[1]); if (sdram_params->base.dramtype == DDR4) grf_lp_con = (0x7 << 16) | (1 << 1); else if (sdram_params->base.dramtype == DDR3) grf_lp_con = (0x7 << 16) | (1 << 0); else grf_lp_con = (0x7 << 16) | (1 << 2); /* en lpckdis_en */ grf_lp_con = grf_lp_con | (0x1 << (9 + 16)) | (0x1 << 9); writel(grf_lp_con, ddr_grf_base + DDR_GRF_LP_CON); /* off digit module clock when enter power down */ setbits_le32(PHY_REG(phy_base, 7), 1 << 7); /* enable sr, pd */ if (PD_IDLE == 0) clrbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 1)); else setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 1)); if (SR_IDLE == 0) clrbits_le32(pctl_base + DDR_PCTL2_PWRCTL, 1); else setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, 1); setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 3)); } /* * pre_init: 0: pre init for dram cap detect * 1: detect correct cap(except cs1 row)info, than reinit * 2: after reinit, we detect cs1_row, if cs1_row not equal * to cs0_row and cs is in middle on ddrconf map, we need * to reinit dram, than set the correct ddrconf. */ static int sdram_init_(struct dram_info *dram, struct px30_sdram_params *sdram_params, u32 pre_init) { struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; void __iomem *pctl_base = dram->pctl; rkclk_ddr_reset(dram, 1, 1, 1, 1); udelay(10); /* * dereset ddr phy psrstn to config pll, * if using phy pll psrstn must be dereset * before config pll */ rkclk_ddr_reset(dram, 1, 1, 1, 0); rkclk_configure_ddr(dram, sdram_params); /* release phy srst to provide clk to ctrl */ rkclk_ddr_reset(dram, 1, 1, 0, 0); udelay(10); phy_soft_reset(dram->phy); /* release ctrl presetn, and config ctl registers */ rkclk_ddr_reset(dram, 1, 0, 0, 0); pctl_cfg(dram->pctl, &sdram_params->pctl_regs, SR_IDLE, PD_IDLE); cap_info->ddrconfig = calculate_ddrconfig(sdram_params); set_ctl_address_map(dram, sdram_params); phy_cfg(dram->phy, &sdram_params->phy_regs, sdram_params->skew, &sdram_params->base, cap_info->bw); /* enable dfi_init_start to init phy after ctl srstn deassert */ setbits_le32(pctl_base + DDR_PCTL2_DFIMISC, (1 << 5) | (1 << 4)); rkclk_ddr_reset(dram, 0, 0, 0, 0); /* wait for dfi_init_done and dram init complete */ while ((readl(pctl_base + DDR_PCTL2_STAT) & 0x7) == 0) continue; if (sdram_params->base.dramtype == LPDDR3) pctl_write_mr(dram->pctl, 3, 11, 3, LPDDR3); /* do ddr gate training */ redo_cs0_training: if (data_training(dram, 0, sdram_params->base.dramtype) != 0) { if (pre_init != 0) printascii("DTT cs0 error\n"); return -1; } if (check_rd_gate(dram)) { printascii("re training cs0"); goto redo_cs0_training; } if (sdram_params->base.dramtype == LPDDR3) { if ((read_mr(dram, 1, 8) & 0x3) != 0x3) return -1; } else if (sdram_params->base.dramtype == LPDDR2) { if ((read_mr(dram, 1, 8) & 0x3) != 0x0) return -1; } /* for px30: when 2cs, both 2 cs should be training */ if (pre_init != 0 && cap_info->rank == 2) { redo_cs1_training: if (data_training(dram, 1, sdram_params->base.dramtype) != 0) { printascii("DTT cs1 error\n"); return -1; } if (check_rd_gate(dram)) { printascii("re training cs1"); goto redo_cs1_training; } } if (sdram_params->base.dramtype == DDR4) pctl_write_vrefdq(dram->pctl, 0x3, 5670, sdram_params->base.dramtype); dram_all_config(dram, sdram_params); enable_low_power(dram, sdram_params); return 0; } static int dram_detect_cap(struct dram_info *dram, struct px30_sdram_params *sdram_params, unsigned char channel) { struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; /* * for ddr3: ddrconf = 3 * for ddr4: ddrconf = 12 * for lpddr3: ddrconf = 3 * default bw = 1 */ u32 bk, bktmp; u32 col, coltmp; u32 rowtmp; u32 cs; u32 bw = 1; u32 dram_type = sdram_params->base.dramtype; if (dram_type != DDR4) { /* detect col and bk for ddr3/lpddr3 */ coltmp = 12; bktmp = 3; if (dram_type == LPDDR2) rowtmp = 15; else rowtmp = 16; if (sdram_detect_col(cap_info, coltmp) != 0) goto cap_err; sdram_detect_bank(cap_info, coltmp, bktmp); sdram_detect_dbw(cap_info, dram_type); } else { /* detect bg for ddr4 */ coltmp = 10; bktmp = 4; rowtmp = 17; col = 10; bk = 2; cap_info->col = col; cap_info->bk = bk; sdram_detect_bg(cap_info, coltmp); } /* detect row */ if (sdram_detect_row(cap_info, coltmp, bktmp, rowtmp) != 0) goto cap_err; /* detect row_3_4 */ sdram_detect_row_3_4(cap_info, coltmp, bktmp); /* bw and cs detect using data training */ if (data_training(dram, 1, dram_type) == 0) cs = 1; else cs = 0; cap_info->rank = cs + 1; dram_set_bw(dram, 2); if (data_training(dram, 0, dram_type) == 0) bw = 2; else bw = 1; cap_info->bw = bw; cap_info->cs0_high16bit_row = cap_info->cs0_row; if (cs) { cap_info->cs1_row = cap_info->cs0_row; cap_info->cs1_high16bit_row = cap_info->cs0_row; } else { cap_info->cs1_row = 0; cap_info->cs1_high16bit_row = 0; } return 0; cap_err: return -1; } /* return: 0 = success, other = fail */ static int sdram_init_detect(struct dram_info *dram, struct px30_sdram_params *sdram_params) { struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; u32 ret; u32 sys_reg = 0; u32 sys_reg3 = 0; if (sdram_init_(dram, sdram_params, 0) != 0) return -1; if (dram_detect_cap(dram, sdram_params, 0) != 0) return -1; /* modify bw, cs related timing */ pctl_remodify_sdram_params(&sdram_params->pctl_regs, cap_info, sdram_params->base.dramtype); /* reinit sdram by real dram cap */ ret = sdram_init_(dram, sdram_params, 1); if (ret != 0) goto out; /* redetect cs1 row */ sdram_detect_cs1_row(cap_info, sdram_params->base.dramtype); if (cap_info->cs1_row) { sys_reg = readl(&dram->pmugrf->os_reg[2]); sys_reg3 = readl(&dram->pmugrf->os_reg[3]); SYS_REG_ENC_CS1_ROW(cap_info->cs1_row, sys_reg, sys_reg3, 0); writel(sys_reg, &dram->pmugrf->os_reg[2]); writel(sys_reg3, &dram->pmugrf->os_reg[3]); } ret = sdram_detect_high_row(cap_info); out: return ret; } struct px30_sdram_params *get_default_sdram_config(void) { sdram_configs[0].skew = &skew; return &sdram_configs[0]; } /* return: 0 = success, other = fail */ int sdram_init(void) { struct px30_sdram_params *sdram_params; int ret = 0; dram_info.phy = (void *)DDR_PHY_BASE_ADDR; dram_info.pctl = (void *)DDRC_BASE_ADDR; dram_info.grf = (void *)GRF_BASE_ADDR; dram_info.cru = (void *)CRU_BASE_ADDR; dram_info.msch = (void *)SERVER_MSCH0_BASE_ADDR; dram_info.ddr_grf = (void *)DDR_GRF_BASE_ADDR; dram_info.pmugrf = (void *)PMUGRF_BASE_ADDR; sdram_params = get_default_sdram_config(); ret = sdram_init_detect(&dram_info, sdram_params); if (ret) goto error; sdram_print_ddr_info(&sdram_params->ch.cap_info, &sdram_params->base, 0); printascii("out\n"); return ret; error: return (-1); } #else static int px30_dmc_probe(struct udevice *dev) { struct dram_info *priv = dev_get_priv(dev); priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); debug("%s: grf=%p\n", __func__, priv->pmugrf); priv->info.base = CFG_SYS_SDRAM_BASE; priv->info.size = rockchip_sdram_size((phys_addr_t)&priv->pmugrf->os_reg[2]); return 0; } static int px30_dmc_get_info(struct udevice *dev, struct ram_info *info) { struct dram_info *priv = dev_get_priv(dev); *info = priv->info; return 0; } static struct ram_ops px30_dmc_ops = { .get_info = px30_dmc_get_info, }; static const struct udevice_id px30_dmc_ids[] = { { .compatible = "rockchip,px30-dmc" }, { } }; U_BOOT_DRIVER(dmc_px30) = { .name = "rockchip_px30_dmc", .id = UCLASS_RAM, .of_match = px30_dmc_ids, .ops = &px30_dmc_ops, .probe = px30_dmc_probe, .priv_auto = sizeof(struct dram_info), }; #endif /* CONFIG_TPL_BUILD */