| // SPDX-License-Identifier: (GPL-2.0+) |
| /* |
| * suniv DRAM initialization |
| * |
| * Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io> |
| * |
| * Based on xboot's arch/arm32/mach-f1c100s/sys-dram.c, which is: |
| * |
| * Copyright(c) 2007-2018 Jianjun Jiang <8192542@qq.com> |
| */ |
| |
| #include <common.h> |
| #include <asm/io.h> |
| #include <asm/arch/clock.h> |
| #include <asm/arch/dram.h> |
| #include <asm/arch/gpio.h> |
| #include <linux/bitops.h> |
| #include <linux/delay.h> |
| #include <hang.h> |
| |
| #define SDR_T_CAS (0x2) |
| #define SDR_T_RAS (0x8) |
| #define SDR_T_RCD (0x3) |
| #define SDR_T_RP (0x3) |
| #define SDR_T_WR (0x3) |
| #define SDR_T_RFC (0xd) |
| #define SDR_T_XSR (0xf9) |
| #define SDR_T_RC (0xb) |
| #define SDR_T_INIT (0x8) |
| #define SDR_T_INIT_REF (0x7) |
| #define SDR_T_WTR (0x2) |
| #define SDR_T_RRD (0x2) |
| #define SDR_T_XP (0x0) |
| |
| enum dram_type { |
| DRAM_TYPE_SDR = 0, |
| DRAM_TYPE_DDR = 1, |
| /* Not supported yet. */ |
| DRAM_TYPE_MDDR = 2, |
| }; |
| |
| struct dram_para { |
| u32 size; /* dram size (unit: MByte) */ |
| u32 clk; /* dram work clock (unit: MHz) */ |
| u32 access_mode; /* 0: interleave mode 1: sequence mode */ |
| u32 cs_num; /* dram chip count 1: one chip 2: two chip */ |
| u32 ddr8_remap; /* for 8bits data width DDR 0: normal 1: 8bits */ |
| enum dram_type sdr_ddr; |
| u32 bwidth; /* dram bus width */ |
| u32 col_width; /* column address width */ |
| u32 row_width; /* row address width */ |
| u32 bank_size; /* dram bank count */ |
| u32 cas; /* dram cas */ |
| }; |
| |
| struct dram_para suniv_dram_para = { |
| .size = 32, |
| .clk = 156, |
| .access_mode = 1, |
| .cs_num = 1, |
| .ddr8_remap = 0, |
| .sdr_ddr = DRAM_TYPE_DDR, |
| .bwidth = 16, |
| .col_width = 10, |
| .row_width = 13, |
| .bank_size = 4, |
| .cas = 0x3, |
| }; |
| |
| static int dram_initial(void) |
| { |
| unsigned int time = 0xffffff; |
| |
| setbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, 0x1); |
| while ((readl(SUNXI_DRAMC_BASE + DRAM_SCTLR) & 0x1) && time--) { |
| if (time == 0) |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int dram_delay_scan(void) |
| { |
| unsigned int time = 0xffffff; |
| |
| setbits_le32(SUNXI_DRAMC_BASE + DRAM_DDLYR, 0x1); |
| while ((readl(SUNXI_DRAMC_BASE + DRAM_DDLYR) & 0x1) && time--) { |
| if (time == 0) |
| return 0; |
| } |
| return 1; |
| } |
| |
| static void dram_set_autofresh_cycle(u32 clk) |
| { |
| u32 val = 0; |
| u32 row = 0; |
| u32 temp = 0; |
| |
| row = readl(SUNXI_DRAMC_BASE + DRAM_SCONR); |
| row &= 0x1e0; |
| row >>= 0x5; |
| |
| if (row == 0xc) { |
| if (clk >= 1000000) { |
| temp = clk + (clk >> 3) + (clk >> 4) + (clk >> 5); |
| while (temp >= (10000000 >> 6)) { |
| temp -= (10000000 >> 6); |
| val++; |
| } |
| } else { |
| val = (clk * 499) >> 6; |
| } |
| } else if (row == 0xb) { |
| if (clk >= 1000000) { |
| temp = clk + (clk >> 3) + (clk >> 4) + (clk >> 5); |
| while (temp >= (10000000 >> 7)) { |
| temp -= (10000000 >> 7); |
| val++; |
| } |
| } else { |
| val = (clk * 499) >> 5; |
| } |
| } |
| writel(val, SUNXI_DRAMC_BASE + DRAM_SREFR); |
| } |
| |
| static int dram_para_setup(struct dram_para *para) |
| { |
| u32 val = 0; |
| |
| val = (para->ddr8_remap) | (0x1 << 1) | |
| ((para->bank_size >> 2) << 3) | |
| ((para->cs_num >> 1) << 4) | |
| ((para->row_width - 1) << 5) | |
| ((para->col_width - 1) << 9) | |
| ((para->sdr_ddr ? (para->bwidth >> 4) : (para->bwidth >> 5)) << 13) | |
| (para->access_mode << 15) | |
| (para->sdr_ddr << 16); |
| |
| writel(val, SUNXI_DRAMC_BASE + DRAM_SCONR); |
| setbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, 0x1 << 19); |
| return dram_initial(); |
| } |
| |
| static u32 dram_check_delay(u32 bwidth) |
| { |
| u32 dsize; |
| int i, j; |
| u32 num = 0; |
| u32 dflag = 0; |
| |
| dsize = ((bwidth == 16) ? 4 : 2); |
| for (i = 0; i < dsize; i++) { |
| if (i == 0) |
| dflag = readl(SUNXI_DRAMC_BASE + DRAM_DRPTR0); |
| else if (i == 1) |
| dflag = readl(SUNXI_DRAMC_BASE + DRAM_DRPTR1); |
| else if (i == 2) |
| dflag = readl(SUNXI_DRAMC_BASE + DRAM_DRPTR2); |
| else if (i == 3) |
| dflag = readl(SUNXI_DRAMC_BASE + DRAM_DRPTR3); |
| |
| for (j = 0; j < 32; j++) { |
| if (dflag & 0x1) |
| num++; |
| dflag >>= 1; |
| } |
| } |
| return num; |
| } |
| |
| static int sdr_readpipe_scan(void) |
| { |
| u32 k = 0; |
| |
| for (k = 0; k < 32; k++) |
| writel(k, CONFIG_SYS_SDRAM_BASE + 4 * k); |
| for (k = 0; k < 32; k++) { |
| if (readl(CONFIG_SYS_SDRAM_BASE + 4 * k) != k) |
| return 0; |
| } |
| return 1; |
| } |
| |
| static u32 sdr_readpipe_select(void) |
| { |
| u32 value = 0; |
| u32 i = 0; |
| |
| for (i = 0; i < 8; i++) { |
| clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, |
| 0x7 << 6, i << 6); |
| if (sdr_readpipe_scan()) { |
| value = i; |
| return value; |
| } |
| } |
| return value; |
| } |
| |
| static u32 dram_check_type(struct dram_para *para) |
| { |
| u32 times = 0; |
| int i; |
| |
| for (i = 0; i < 8; i++) { |
| clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, |
| 0x7 << 6, i << 6); |
| dram_delay_scan(); |
| if (readl(SUNXI_DRAMC_BASE + DRAM_DDLYR) & 0x30) |
| times++; |
| } |
| |
| if (times == 8) { |
| para->sdr_ddr = DRAM_TYPE_SDR; |
| return 0; |
| } |
| para->sdr_ddr = DRAM_TYPE_DDR; |
| return 1; |
| } |
| |
| static u32 dram_scan_readpipe(struct dram_para *para) |
| { |
| u32 rp_best = 0, rp_val = 0; |
| u32 readpipe[8]; |
| int i; |
| |
| if (para->sdr_ddr == DRAM_TYPE_DDR) { |
| for (i = 0; i < 8; i++) { |
| clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, |
| 0x7 << 6, i << 6); |
| dram_delay_scan(); |
| readpipe[i] = 0; |
| if ((((readl(SUNXI_DRAMC_BASE + DRAM_DDLYR) >> 4) & 0x3) == 0x0) && |
| (((readl(SUNXI_DRAMC_BASE + DRAM_DDLYR) >> 4) & 0x1) == 0x0)) |
| readpipe[i] = dram_check_delay(para->bwidth); |
| if (rp_val < readpipe[i]) { |
| rp_val = readpipe[i]; |
| rp_best = i; |
| } |
| } |
| clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, |
| 0x7 << 6, rp_best << 6); |
| dram_delay_scan(); |
| } else { |
| clrbits_le32(SUNXI_DRAMC_BASE + DRAM_SCONR, |
| (0x1 << 16) | (0x3 << 13)); |
| rp_best = sdr_readpipe_select(); |
| clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, |
| 0x7 << 6, rp_best << 6); |
| } |
| return 0; |
| } |
| |
| static u32 dram_get_dram_size(struct dram_para *para) |
| { |
| u32 colflag = 10, rowflag = 13; |
| u32 val1 = 0; |
| u32 count = 0; |
| u32 addr1, addr2; |
| int i; |
| |
| para->col_width = colflag; |
| para->row_width = rowflag; |
| dram_para_setup(para); |
| dram_scan_readpipe(para); |
| for (i = 0; i < 32; i++) { |
| *((u8 *)(CONFIG_SYS_SDRAM_BASE + 0x200 + i)) = 0x11; |
| *((u8 *)(CONFIG_SYS_SDRAM_BASE + 0x600 + i)) = 0x22; |
| } |
| for (i = 0; i < 32; i++) { |
| val1 = *((u8 *)(CONFIG_SYS_SDRAM_BASE + 0x200 + i)); |
| if (val1 == 0x22) |
| count++; |
| } |
| if (count == 32) |
| colflag = 9; |
| else |
| colflag = 10; |
| count = 0; |
| para->col_width = colflag; |
| para->row_width = rowflag; |
| dram_para_setup(para); |
| if (colflag == 10) { |
| addr1 = CONFIG_SYS_SDRAM_BASE + 0x400000; |
| addr2 = CONFIG_SYS_SDRAM_BASE + 0xc00000; |
| } else { |
| addr1 = CONFIG_SYS_SDRAM_BASE + 0x200000; |
| addr2 = CONFIG_SYS_SDRAM_BASE + 0x600000; |
| } |
| for (i = 0; i < 32; i++) { |
| *((u8 *)(addr1 + i)) = 0x33; |
| *((u8 *)(addr2 + i)) = 0x44; |
| } |
| for (i = 0; i < 32; i++) { |
| val1 = *((u8 *)(addr1 + i)); |
| if (val1 == 0x44) |
| count++; |
| } |
| if (count == 32) |
| rowflag = 12; |
| else |
| rowflag = 13; |
| para->col_width = colflag; |
| para->row_width = rowflag; |
| if (para->row_width != 13) |
| para->size = 16; |
| else if (para->col_width == 10) |
| para->size = 64; |
| else |
| para->size = 32; |
| dram_set_autofresh_cycle(para->clk); |
| para->access_mode = 0; |
| dram_para_setup(para); |
| |
| return 0; |
| } |
| |
| static void simple_dram_check(void) |
| { |
| volatile u32 *dram = (u32 *)CONFIG_SYS_SDRAM_BASE; |
| int i; |
| |
| for (i = 0; i < 0x40; i++) |
| dram[i] = i; |
| |
| for (i = 0; i < 0x40; i++) { |
| if (dram[i] != i) { |
| printf("DRAM initialization failed: dram[0x%x] != 0x%x.", i, dram[i]); |
| hang(); |
| } |
| } |
| |
| for (i = 0; i < 0x10000; i += 0x40) |
| dram[i] = i; |
| |
| for (i = 0; i < 0x10000; i += 0x40) { |
| if (dram[i] != i) { |
| printf("DRAM initialization failed: dram[0x%x] != 0x%x.", i, dram[i]); |
| hang(); |
| } |
| } |
| } |
| |
| static void do_dram_init(struct dram_para *para) |
| { |
| struct sunxi_ccm_reg * const ccm = |
| (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
| u32 val; |
| u8 m; /* PLL_DDR clock factor */ |
| |
| sunxi_gpio_set_cfgpin(SUNXI_GPB(3), 0x7); |
| mdelay(5); |
| /* TODO: dig out what's them... some analog register? */ |
| if ((para->cas >> 3) & 0x1) |
| setbits_le32(SUNXI_PIO_BASE + 0x2c4, (0x1 << 23) | (0x20 << 17)); |
| |
| if (para->clk >= 144 && para->clk <= 180) |
| writel(0xaaa, SUNXI_PIO_BASE + 0x2c0); |
| if (para->clk >= 180) |
| writel(0xfff, SUNXI_PIO_BASE + 0x2c0); |
| |
| if (para->cas & BIT(4)) |
| writel(0xd1303333, &ccm->pll5_pattern_cfg); |
| else if (para->cas & BIT(5)) |
| writel(0xcce06666, &ccm->pll5_pattern_cfg); |
| else if (para->cas & BIT(6)) |
| writel(0xc8909999, &ccm->pll5_pattern_cfg); |
| else if (para->cas & BIT(7)) |
| writel(0xc440cccc, &ccm->pll5_pattern_cfg); |
| |
| if (para->clk <= 96) |
| m = 2; |
| else |
| m = 1; |
| |
| val = CCM_PLL5_CTRL_EN | CCM_PLL5_CTRL_UPD | |
| CCM_PLL5_CTRL_N((para->clk * 2) / (24 / m)) | |
| CCM_PLL5_CTRL_K(1) | CCM_PLL5_CTRL_M(m); |
| if (para->cas & GENMASK(7, 4)) |
| val |= CCM_PLL5_CTRL_SIGMA_DELTA_EN; |
| writel(val, &ccm->pll5_cfg); |
| setbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_UPD); |
| mctl_await_completion(&ccm->pll5_cfg, BIT(28), BIT(28)); |
| mdelay(5); |
| |
| setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_MCTL)); |
| clrbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_RESET_OFFSET_MCTL)); |
| udelay(50); |
| setbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_RESET_OFFSET_MCTL)); |
| |
| clrsetbits_le32(SUNXI_PIO_BASE + 0x2c4, (1 << 16), |
| ((para->sdr_ddr == DRAM_TYPE_DDR) << 16)); |
| |
| val = (SDR_T_CAS << 0) | (SDR_T_RAS << 3) | (SDR_T_RCD << 7) | |
| (SDR_T_RP << 10) | (SDR_T_WR << 13) | (SDR_T_RFC << 15) | |
| (SDR_T_XSR << 19) | (SDR_T_RC << 28); |
| writel(val, SUNXI_DRAMC_BASE + DRAM_STMG0R); |
| val = (SDR_T_INIT << 0) | (SDR_T_INIT_REF << 16) | (SDR_T_WTR << 20) | |
| (SDR_T_RRD << 22) | (SDR_T_XP << 25); |
| writel(val, SUNXI_DRAMC_BASE + DRAM_STMG1R); |
| dram_para_setup(para); |
| dram_check_type(para); |
| |
| clrsetbits_le32(SUNXI_PIO_BASE + 0x2c4, (1 << 16), |
| ((para->sdr_ddr == DRAM_TYPE_DDR) << 16)); |
| |
| dram_set_autofresh_cycle(para->clk); |
| dram_scan_readpipe(para); |
| dram_get_dram_size(para); |
| simple_dram_check(); |
| } |
| |
| unsigned long sunxi_dram_init(void) |
| { |
| do_dram_init(&suniv_dram_para); |
| |
| return suniv_dram_para.size * 1024 * 1024; |
| } |