diff options
author | Jinkun Hong <jinkun.hong@rock-chips.com> | 2014-08-28 09:37:22 -0700 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-03-24 15:25:23 +0100 |
commit | c33ce3554ddc73635084e6e71b5e4f7dae021926 (patch) | |
tree | c727bcdeb697d2dde1ba983a1af08a07083c4b2f /src | |
parent | d5fb66e060954f8505cfceed371aace9c8285fe7 (diff) | |
download | coreboot-c33ce3554ddc73635084e6e71b5e4f7dae021926.tar.xz |
rk3288: add ddr driver
Supports DDR3 and LPDDR3.Supports dual channel.ddr max freq is 533mhz.
ddr timing config file in src\mainboard\google\veyron\sdram_inf
Remove dpll init in rk clk_init(), add rkclk_configure_ddr(unsigned int hz).
BUG=chrome-os-partner:29778
TEST=Build coreboot
Change-Id: I429eb0b8c365c6285fb6cfef008b41776cc9c2d9
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 52838c68fe6963285c974af5dc5837e819efc321
Original-Change-Id: I6ddfe30b8585002b45060fe998c9238cbb611c05
Original-Signed-off-by: jinkun.hong <jinkun.hong@rock-chips.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/209465
Original-Reviewed-by: Julius Werner <jwerner@chromium.org>
Original-Commit-Queue: Julius Werner <jwerner@chromium.org>
Reviewed-on: http://review.coreboot.org/8865
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Tested-by: build bot (Jenkins)
Diffstat (limited to 'src')
-rw-r--r-- | src/mainboard/google/veyron/Makefile.inc | 1 | ||||
-rw-r--r-- | src/mainboard/google/veyron/romstage.c | 2 | ||||
-rw-r--r-- | src/mainboard/google/veyron/sdram_configs.c | 75 | ||||
-rw-r--r-- | src/mainboard/google/veyron/sdram_inf/sdram-ddr3-hynix-2GB.inc | 77 | ||||
-rw-r--r-- | src/mainboard/google/veyron/sdram_inf/sdram-lpddr3-samsung-2GB.inc | 78 | ||||
-rw-r--r-- | src/mainboard/google/veyron/sdram_inf/sdram-unused.inc | 3 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/Makefile.inc | 2 | ||||
-rwxr-xr-x | src/soc/rockchip/rk3288/clock.c | 77 | ||||
-rwxr-xr-x | src/soc/rockchip/rk3288/clock.h | 5 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/cpu.h | 2 | ||||
-rwxr-xr-x | src/soc/rockchip/rk3288/grf.h | 40 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/sdram.c | 1046 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/sdram.h | 104 |
13 files changed, 1498 insertions, 14 deletions
diff --git a/src/mainboard/google/veyron/Makefile.inc b/src/mainboard/google/veyron/Makefile.inc index 50a6ba039b..f249cf95af 100644 --- a/src/mainboard/google/veyron/Makefile.inc +++ b/src/mainboard/google/veyron/Makefile.inc @@ -18,6 +18,7 @@ ## romstage-y += romstage.c +romstage-y += sdram_configs.c ramstage-y += mainboard.c ramstage-y += chromeos.c diff --git a/src/mainboard/google/veyron/romstage.c b/src/mainboard/google/veyron/romstage.c index 5b26f48e3c..5831fde095 100644 --- a/src/mainboard/google/veyron/romstage.c +++ b/src/mainboard/google/veyron/romstage.c @@ -27,6 +27,7 @@ #include <timestamp.h> #include <arch/cache.h> #include <arch/exception.h> +#include <soc/rockchip/rk3288/sdram.h> void main(void) { @@ -36,6 +37,7 @@ void main(void) u32 dram_start = (CONFIG_SYS_SDRAM_BASE >> 20); u32 dram_size = CONFIG_DRAM_SIZE_MB; u32 dram_end = dram_start + dram_size; + sdram_init(get_sdram_config()); mmu_init(); /* Device memory below DRAM is uncached. */ mmu_config_range(0, dram_start, DCACHE_OFF); diff --git a/src/mainboard/google/veyron/sdram_configs.c b/src/mainboard/google/veyron/sdram_configs.c new file mode 100644 index 0000000000..b3600fbfff --- /dev/null +++ b/src/mainboard/google/veyron/sdram_configs.c @@ -0,0 +1,75 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <arch/io.h> +#include <string.h> +#include <types.h> +#include <console/console.h> +#include <soc/rockchip/rk3288/sdram.h> +#include <soc/rockchip/rk3288/gpio.h> + +static struct rk3288_sdram_params sdram_configs[] = { +#include "sdram_inf/sdram-lpddr3-samsung-2GB.inc" /* ram_code = 0000 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 0001 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 0010 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 0011 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 0100 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 0101 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 0110 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 0111 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 1000 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 1001 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 1010 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 1011 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 1100 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 1101 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 1110 */ +#include "sdram_inf/sdram-unused.inc" /* ram_code = 1111 */ +}; + +#define GPIO_RAMCODE0 (gpio_t){.port = 8, .bank = GPIO_A, .idx = 0} +#define GPIO_RAMCODE1 (gpio_t){.port = 8, .bank = GPIO_A, .idx = 1} +#define GPIO_RAMCODE2 (gpio_t){.port = 8, .bank = GPIO_A, .idx = 2} +#define GPIO_RAMCODE3 (gpio_t){.port = 8, .bank = GPIO_A, .idx = 3} + +u32 sdram_get_ram_code(void) +{ + u32 code = 0; + + gpio_input(GPIO_RAMCODE0); + gpio_input(GPIO_RAMCODE1); + gpio_input(GPIO_RAMCODE2); + gpio_input(GPIO_RAMCODE3); + + code = gpio_get_in_value(GPIO_RAMCODE3) << 3 + | gpio_get_in_value(GPIO_RAMCODE2) << 2 + | gpio_get_in_value(GPIO_RAMCODE1) << 1 + | gpio_get_in_value(GPIO_RAMCODE0) << 0; + + return code; +} + +const struct rk3288_sdram_params *get_sdram_config() +{ + u32 ramcode = sdram_get_ram_code(); + + if (ramcode >= ARRAY_SIZE(sdram_configs) + || sdram_configs[ramcode].dramtype == UNUSED) + die("Invalid RAMCODE."); + return &sdram_configs[ramcode]; +} diff --git a/src/mainboard/google/veyron/sdram_inf/sdram-ddr3-hynix-2GB.inc b/src/mainboard/google/veyron/sdram_inf/sdram-ddr3-hynix-2GB.inc new file mode 100644 index 0000000000..409a7cad35 --- /dev/null +++ b/src/mainboard/google/veyron/sdram_inf/sdram-ddr3-hynix-2GB.inc @@ -0,0 +1,77 @@ +{ + { + { + .rank = 0x1, + .col = 0xA, + .bk = 0x3, + .bw = 0x2, + .dbw = 0x1, + .row_3_4 = 0x0, + .cs0_row = 0xF, + .cs1_row = 0xF + }, + { + .rank = 0x1, + .col = 0xA, + .bk = 0x3, + .bw = 0x2, + .dbw = 0x1, + .row_3_4 = 0x0, + .cs0_row = 0xF, + .cs1_row = 0xF + } + }, + { + .togcnt1u = 0x215, + .tinit = 0xC8, + .trsth = 0x1F4, + .togcnt100n = 0x35, + .trefi = 0x4E, + .tmrd = 0x4, + .trfc = 0xBB, + .trp = 0x8, + .trtw = 0x4, + .tal = 0x0, + .tcl = 0x8, + .tcwl = 0x6, + .tras = 0x14, + .trc = 0x1D, + .trcd = 0x8, + .trrd = 0x6, + .trtp = 0x4, + .twr = 0x8, + .twtr = 0x4, + .texsr = 0x200, + .txp = 0x4, + .txpdll = 0xD, + .tzqcs = 0x40, + .tzqcsi = 0x0, + .tdqs = 0x1, + .tcksre = 0x6, + .tcksrx = 0x6, + .tcke = 0x4, + .tmod = 0xC, + .trstl = 0x36, + .tzqcl = 0x100, + .tmrr = 0x0, + .tckesr = 0x5, + .tdpd = 0x0 + }, + { + .dtpr0 = 0x3AD48890, + .dtpr1 = 0xBB08D8, + .dtpr2 = 0x1002B600, + .mr[0] = 0x840, + .mr[1] = 0x40, + .mr[2] = 0x8, + .mr[3] = 0x0 + }, + .noc_timing = 0x2891E41D, + .noc_activate = 0x5B6, + .ddrconfig = 3, + .ddr_freq = 533000000, + .dramtype = DDR3, + .num_channels = 2, + .stride = 9, + .odt = 1 +}, diff --git a/src/mainboard/google/veyron/sdram_inf/sdram-lpddr3-samsung-2GB.inc b/src/mainboard/google/veyron/sdram_inf/sdram-lpddr3-samsung-2GB.inc new file mode 100644 index 0000000000..315e542c39 --- /dev/null +++ b/src/mainboard/google/veyron/sdram_inf/sdram-lpddr3-samsung-2GB.inc @@ -0,0 +1,78 @@ +{ + /* two Samsung K4E8E304ED-EGCE000 chips */ + { + { + .rank = 0x2, + .col = 0xA, + .bk = 0x3, + .bw = 0x2, + .dbw = 0x2, + .row_3_4 = 0x0, + .cs0_row = 0xE, + .cs1_row = 0xE + }, + { + .rank = 0x2, + .col = 0xA, + .bk = 0x3, + .bw = 0x2, + .dbw = 0x2, + .row_3_4 = 0x0, + .cs0_row = 0xE, + .cs1_row = 0xE + } + }, + { + .togcnt1u = 0x215, + .tinit = 0xC8, + .trsth = 0x0, + .togcnt100n = 0x35, + .trefi = 0x26, + .tmrd = 0x2, + .trfc = 0x70, + .trp = 0x2000D, + .trtw = 0x6, + .tal = 0x0, + .tcl = 0x8, + .tcwl = 0x4, + .tras = 0x17, + .trc = 0x24, + .trcd = 0xD, + .trrd = 0x6, + .trtp = 0x4, + .twr = 0x8, + .twtr = 0x4, + .texsr = 0x76, + .txp = 0x4, + .txpdll = 0x0, + .tzqcs = 0x30, + .tzqcsi = 0x0, + .tdqs = 0x1, + .tcksre = 0x2, + .tcksrx = 0x2, + .tcke = 0x4, + .tmod = 0x0, + .trstl = 0x0, + .tzqcl = 0xC0, + .tmrr = 0x4, + .tckesr = 0x8, + .tdpd = 0x1F4 + }, + { + .dtpr0 = 0x48D7DD93, + .dtpr1 = 0x187008D8, + .dtpr2 = 0x121076, + .mr[0] = 0x0, + .mr[1] = 0xC3, + .mr[2] = 0x6, + .mr[3] = 0x1 + }, + .noc_timing = 0x20D266A4, + .noc_activate = 0x5B6, + .ddrconfig = 2, + .ddr_freq = 533000000, + .dramtype = LPDDR3, + .num_channels = 2, + .stride = 9, + .odt = 1 +}, diff --git a/src/mainboard/google/veyron/sdram_inf/sdram-unused.inc b/src/mainboard/google/veyron/sdram_inf/sdram-unused.inc new file mode 100644 index 0000000000..06498f7f14 --- /dev/null +++ b/src/mainboard/google/veyron/sdram_inf/sdram-unused.inc @@ -0,0 +1,3 @@ +{ + .dramtype= UNUSED +},
\ No newline at end of file diff --git a/src/soc/rockchip/rk3288/Makefile.inc b/src/soc/rockchip/rk3288/Makefile.inc index 4818cafb11..446aa7842d 100644 --- a/src/soc/rockchip/rk3288/Makefile.inc +++ b/src/soc/rockchip/rk3288/Makefile.inc @@ -39,7 +39,7 @@ romstage-y += clock.c romstage-y += gpio.c romstage-y += spi.c romstage-y += media.c - +romstage-y += sdram.c ramstage-y += cbmem.c ramstage-y += timer.c diff --git a/src/soc/rockchip/rk3288/clock.c b/src/soc/rockchip/rk3288/clock.c index 757d180387..b194b4c10e 100755 --- a/src/soc/rockchip/rk3288/clock.c +++ b/src/soc/rockchip/rk3288/clock.c @@ -70,11 +70,10 @@ static struct rk3288_cru_reg * const cru_ptr = (void *)CRU_BASE; (_nr * _no) == hz,\ #hz "Hz cannot be hit with PLL divisors in " __FILE__); -/* apll = 816MHz, gpll = 594MHz, cpll = 384MHz, dpll = 300MHz */ +/* apll = 816MHz, gpll = 594MHz, cpll = 384MHz */ static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 2); static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 4); static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 2, 4); -static const struct pll_div dpll_init_cfg = PLL_DIVISORS(DPLL_HZ, 1, 4); /*******************PLL CON0 BITS***************************/ #define PLL_OD_MSK (0x0F) @@ -191,23 +190,21 @@ void rkclk_init(void) /* pll enter slow-mode */ writel(RK_CLRSETBITS(APLL_MODE_MSK, APLL_MODE_SLOW) | RK_CLRSETBITS(GPLL_MODE_MSK, GPLL_MODE_SLOW) - | RK_CLRSETBITS(CPLL_MODE_MSK, CPLL_MODE_SLOW) - | RK_CLRSETBITS(DPLL_MODE_MSK, DPLL_MODE_SLOW), + | RK_CLRSETBITS(CPLL_MODE_MSK, CPLL_MODE_SLOW), &cru_ptr->cru_mode_con); /* init pll */ rkclk_set_pll(&cru_ptr->cru_apll_con[0], &apll_init_cfg); rkclk_set_pll(&cru_ptr->cru_gpll_con[0], &gpll_init_cfg); rkclk_set_pll(&cru_ptr->cru_cpll_con[0], &cpll_init_cfg); - rkclk_set_pll(&cru_ptr->cru_dpll_con[0], &dpll_init_cfg); /* waiting for pll lock */ while (1) { if ((readl(&rk3288_grf->soc_status[1]) & (SOCSTS_APLL_LOCK | SOCSTS_CPLL_LOCK - | SOCSTS_DPLL_LOCK | SOCSTS_GPLL_LOCK)) + | SOCSTS_GPLL_LOCK)) == (SOCSTS_APLL_LOCK | SOCSTS_CPLL_LOCK - | SOCSTS_GPLL_LOCK | SOCSTS_DPLL_LOCK)) + | SOCSTS_GPLL_LOCK)) break; udelay(1); } @@ -248,12 +245,74 @@ void rkclk_init(void) /* PLL enter normal-mode */ writel(RK_CLRSETBITS(APLL_MODE_MSK, APLL_MODE_NORM) | RK_CLRSETBITS(GPLL_MODE_MSK, GPLL_MODE_NORM) - | RK_CLRSETBITS(CPLL_MODE_MSK, CPLL_MODE_NORM) - | RK_CLRSETBITS(DPLL_MODE_MSK, DPLL_MODE_NORM), + | RK_CLRSETBITS(CPLL_MODE_MSK, CPLL_MODE_NORM), &cru_ptr->cru_mode_con); } +void rkclk_configure_ddr(unsigned int hz) +{ + struct pll_div dpll_cfg; + + if (hz <= 150000000) { + dpll_cfg.nr = 3; + dpll_cfg.no = 8; + } else if (hz <= 540000000) { + dpll_cfg.nr = 6; + dpll_cfg.no = 4; + } else { + dpll_cfg.nr = 1; + dpll_cfg.no = 1; + } + + dpll_cfg.nf = (hz / 1000 * dpll_cfg.nr * dpll_cfg.no) / 24000; + assert(dpll_cfg.nf < 4096 + && hz == dpll_cfg.nf * 24000 / (dpll_cfg.nr * dpll_cfg.no) + * 1000); + /* pll enter slow-mode */ + writel(RK_CLRSETBITS(DPLL_MODE_MSK, DPLL_MODE_SLOW), + &cru_ptr->cru_mode_con); + + rkclk_set_pll(&cru_ptr->cru_dpll_con[0], &dpll_cfg); + + /* waiting for pll lock */ + while (1) { + if (readl(&rk3288_grf->soc_status[1]) & SOCSTS_DPLL_LOCK) + break; + udelay(1); + } + + /* PLL enter normal-mode */ + writel(RK_CLRSETBITS(DPLL_MODE_MSK, DPLL_MODE_NORM), + &cru_ptr->cru_mode_con); +} + +void rkclk_ddr_reset(u32 ch, u32 ctl, u32 phy) +{ + u32 phy_ctl_srstn_shift = 4 + 5 * ch; + u32 ctl_psrstn_shift = 3 + 5 * ch; + u32 ctl_srstn_shift = 2 + 5 * ch; + u32 phy_psrstn_shift = 1 + 5 * ch; + u32 phy_srstn_shift = 5 * ch; + + writel(RK_CLRSETBITS(1 << phy_ctl_srstn_shift, + phy << phy_ctl_srstn_shift) + | RK_CLRSETBITS(1 << ctl_psrstn_shift, ctl << ctl_psrstn_shift) + | RK_CLRSETBITS(1 << ctl_srstn_shift, ctl << ctl_srstn_shift) + | RK_CLRSETBITS(1 << phy_psrstn_shift, phy << phy_psrstn_shift) + | RK_CLRSETBITS(1 << phy_srstn_shift, phy << phy_srstn_shift), + &cru_ptr->cru_softrst_con[10]); +} + +void rkclk_ddr_phy_ctl_reset(u32 ch, u32 n) +{ + u32 phy_ctl_srstn_shift = 4 + 5 * ch; + + writel(RK_CLRSETBITS(1 << phy_ctl_srstn_shift, + n << phy_ctl_srstn_shift), + &cru_ptr->cru_softrst_con[10]); +} + void rkclk_configure_spi(unsigned int bus, unsigned int hz) { int src_clk_div = GPLL_HZ / hz; diff --git a/src/soc/rockchip/rk3288/clock.h b/src/soc/rockchip/rk3288/clock.h index e7732199ac..d04dfeb29c 100755 --- a/src/soc/rockchip/rk3288/clock.h +++ b/src/soc/rockchip/rk3288/clock.h @@ -25,10 +25,11 @@ #define APLL_HZ 816000000 #define GPLL_HZ 594000000 #define CPLL_HZ 384000000 -#define DPLL_HZ 300000000 void rkclk_init(void); void rkclk_configure_spi(unsigned int bus, unsigned int hz); +void rkclk_ddr_reset(u32 ch, u32 ctl, u32 phy); +void rkclk_ddr_phy_ctl_reset(u32 ch, u32 n); +void rkclk_configure_ddr(unsigned int hz); #endif /* __SOC_ROCKCHIP_RK3288_CLOCK_H__ */ - diff --git a/src/soc/rockchip/rk3288/cpu.h b/src/soc/rockchip/rk3288/cpu.h index c42bbe8752..9bcfe3e1e9 100644 --- a/src/soc/rockchip/rk3288/cpu.h +++ b/src/soc/rockchip/rk3288/cpu.h @@ -20,6 +20,8 @@ #ifndef __SOC_ROCKCHIP_RK3288_CPU_H__ #define __SOC_ROCKCHIP_RK3288_CPU_H__ +#include <arch/io.h> + #define RK_CLRSETBITS(clr, set) ((((clr) | (set)) << 16) | set) #define RK_SETBITS(set) RK_CLRSETBITS(0, set) #define RK_CLRBITS(clr) RK_CLRSETBITS(clr, 0) diff --git a/src/soc/rockchip/rk3288/grf.h b/src/soc/rockchip/rk3288/grf.h index 547a4c7ee9..b035bb9dd3 100755 --- a/src/soc/rockchip/rk3288/grf.h +++ b/src/soc/rockchip/rk3288/grf.h @@ -25,9 +25,10 @@ #include "cpu.h" struct rk3288_grf_gpio_lh { - u32 gpiol; - u32 gpioh; + u32 l; + u32 h; }; +check_member(rk3288_grf_gpio_lh, h, 0x4); struct rk3288_grf_regs { u32 reserved[3]; @@ -155,6 +156,41 @@ struct rk3288_grf_regs { }; check_member(rk3288_grf_regs, soc_con16, 0x3a8); +struct rk3288_sgrf_regs { + u32 soc_con0; + u32 soc_con1; + u32 soc_con2; + u32 soc_con3; + u32 soc_con4; + u32 soc_con5; + u32 reserved1[(0x20-0x18)/4]; + u32 busdmac_con[2]; + u32 reserved2[(0x40-0x28)/4]; + u32 cpu_con[3]; + u32 reserved3[(0x50-0x4c)/4]; + u32 soc_con6; + u32 soc_con7; + u32 soc_con8; + u32 soc_con9; + u32 soc_con10; + u32 soc_con11; + u32 soc_con12; + u32 soc_con13; + u32 soc_con14; + u32 soc_con15; + u32 soc_con16; + u32 soc_con17; + u32 soc_con18; + u32 soc_con19; + u32 soc_con20; + u32 soc_con21; + u32 reserved4[(0x100-0x90)/4]; + u32 soc_status[2]; + u32 reserved5[(0x120-0x108)/4]; + u32 fast_boot_addr; +}; +check_member(rk3288_sgrf_regs, fast_boot_addr, 0x0120); + static struct rk3288_grf_regs * const rk3288_grf = (void *)GRF_BASE; static struct rk3288_sgrf_regs * const rk3288_sgrf = (void *)GRF_SECURE_BASE; diff --git a/src/soc/rockchip/rk3288/sdram.c b/src/soc/rockchip/rk3288/sdram.c new file mode 100644 index 0000000000..4f8b268398 --- /dev/null +++ b/src/soc/rockchip/rk3288/sdram.c @@ -0,0 +1,1046 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <arch/io.h> +#include <string.h> +#include <types.h> +#include <console/console.h> +#include <delay.h> +#include "addressmap.h" +#include "clock.h" +#include "sdram.h" +#include "grf.h" +#include "cpu.h" +#include "pmu.h" + +struct rk3288_ddr_pctl_regs { + u32 scfg; + u32 sctl; + u32 stat; + u32 intrstat; + u32 reserved0[12]; + u32 mcmd; + u32 powctl; + u32 powstat; + u32 cmdtstat; + u32 tstaten; + u32 reserved1[3]; + u32 mrrcfg0; + u32 mrrstat0; + u32 mrrstat1; + u32 reserved2[4]; + u32 mcfg1; + u32 mcfg; + u32 ppcfg; + u32 mstat; + u32 lpddr2zqcfg; + u32 reserved3; + u32 dtupdes; + u32 dtuna; + u32 dtune; + u32 dtuprd0; + u32 dtuprd1; + u32 dtuprd2; + u32 dtuprd3; + u32 dtuawdt; + u32 reserved4[3]; + u32 togcnt1u; + u32 tinit; + u32 trsth; + u32 togcnt100n; + u32 trefi; + u32 tmrd; + u32 trfc; + u32 trp; + u32 trtw; + u32 tal; + u32 tcl; + u32 tcwl; + u32 tras; + u32 trc; + u32 trcd; + u32 trrd; + u32 trtp; + u32 twr; + u32 twtr; + u32 texsr; + u32 txp; + u32 txpdll; + u32 tzqcs; + u32 tzqcsi; + u32 tdqs; + u32 tcksre; + u32 tcksrx; + u32 tcke; + u32 tmod; + u32 trstl; + u32 tzqcl; + u32 tmrr; + u32 tckesr; + u32 tdpd; + u32 reserved5[14]; + u32 ecccfg; + u32 ecctst; + u32 eccclr; + u32 ecclog; + u32 reserved6[28]; + u32 dtuwactl; + u32 dturactl; + u32 dtucfg; + u32 dtuectl; + u32 dtuwd0; + u32 dtuwd1; + u32 dtuwd2; + u32 dtuwd3; + u32 dtuwdm; + u32 dturd0; + u32 dturd1; + u32 dturd2; + u32 dturd3; + u32 dtulfsrwd; + u32 dtulfsrrd; + u32 dtueaf; + u32 dfitctrldelay; + u32 dfiodtcfg; + u32 dfiodtcfg1; + u32 dfiodtrankmap; + u32 dfitphywrdata; + u32 dfitphywrlat; + u32 reserved7[2]; + u32 dfitrddataen; + u32 dfitphyrdlat; + u32 reserved8[2]; + u32 dfitphyupdtype0; + u32 dfitphyupdtype1; + u32 dfitphyupdtype2; + u32 dfitphyupdtype3; + u32 dfitctrlupdmin; + u32 dfitctrlupdmax; + u32 dfitctrlupddly; + u32 reserved9; + u32 dfiupdcfg; + u32 dfitrefmski; + u32 dfitctrlupdi; + u32 reserved10[4]; + u32 dfitrcfg0; + u32 dfitrstat0; + u32 dfitrwrlvlen; + u32 dfitrrdlvlen; + u32 dfitrrdlvlgateen; + u32 dfiststat0; + u32 dfistcfg0; + u32 dfistcfg1; + u32 reserved11; + u32 dfitdramclken; + u32 dfitdramclkdis; + u32 dfistcfg2; + u32 dfistparclr; + u32 dfistparlog; + u32 reserved12[3]; + u32 dfilpcfg0; + u32 reserved13[3]; + u32 dfitrwrlvlresp0; + u32 dfitrwrlvlresp1; + u32 dfitrwrlvlresp2; + u32 dfitrrdlvlresp0; + u32 dfitrrdlvlresp1; + u32 dfitrrdlvlresp2; + u32 dfitrwrlvldelay0; + u32 dfitrwrlvldelay1; + u32 dfitrwrlvldelay2; + u32 dfitrrdlvldelay0; + u32 dfitrrdlvldelay1; + u32 dfitrrdlvldelay2; + u32 dfitrrdlvlgatedelay0; + u32 dfitrrdlvlgatedelay1; + u32 dfitrrdlvlgatedelay2; + u32 dfitrcmd; + u32 reserved14[46]; + u32 ipvr; + u32 iptr; +}; +check_member(rk3288_ddr_pctl_regs, iptr, 0x03fc); + +struct rk3288_ddr_publ_datx { + u32 dxgcr; + u32 dxgsr[2]; + u32 dxdllcr; + u32 dxdqtr; + u32 dxdqstr; + u32 reserved[10]; +}; + +struct rk3288_ddr_publ_regs { + u32 ridr; + u32 pir; + u32 pgcr; + u32 pgsr; + u32 dllgcr; + u32 acdllcr; + u32 ptr[3]; + u32 aciocr; + u32 dxccr; + u32 dsgcr; + u32 dcr; + u32 dtpr[3]; + u32 mr[4]; + u32 odtcr; + u32 dtar; + u32 dtdr[2]; + u32 reserved1[24]; + u32 dcuar; + u32 dcudr; + u32 dcurr; + u32 dculr; + u32 dcugcr; + u32 dcutpr; + u32 dcusr[2]; + u32 reserved2[8]; + u32 bist[17]; + u32 reserved3[15]; + u32 zq0cr[2]; + u32 zq0sr[2]; + u32 zq1cr[2]; + u32 zq1sr[2]; + u32 zq2cr[2]; + u32 zq2sr[2]; + u32 zq3cr[2]; + u32 zq3sr[2]; + struct rk3288_ddr_publ_datx datx8[4]; +}; +check_member(rk3288_ddr_publ_regs, datx8[3].dxdqstr, 0x0294); + +struct rk3288_msch_regs { + u32 coreid; + u32 revisionid; + u32 ddrconf; + u32 ddrtiming; + u32 ddrmode; + u32 readlatency; + u32 reserved1[8]; + u32 activate; + u32 devtodev; +}; +check_member(rk3288_msch_regs, devtodev, 0x003c); + +static struct rk3288_ddr_pctl_regs * const rk3288_ddr_pctl[2] = { + (void *)DDR_PCTL0_BASE, (void *)DDR_PCTL1_BASE}; +static struct rk3288_ddr_publ_regs * const rk3288_ddr_publ[2] = { + (void *)DDR_PUBL0_BASE, (void *)DDR_PUBL1_BASE}; +static struct rk3288_msch_regs * const rk3288_msch[2] = { + (void *)SERVICE_BUS_BASE, (void *)SERVICE_BUS_BASE + 0x80}; + +/* PCT_DFISTCFG0 */ +#define DFI_INIT_START (1 << 0) + +/* PCT_DFISTCFG1 */ +#define DFI_DRAM_CLK_SR_EN (1 << 0) +#define DFI_DRAM_CLK_DPD_EN (1 << 1) + +/* PCT_DFISTCFG2 */ +#define DFI_PARITY_INTR_EN (1 << 0) +#define DFI_PARITY_EN (1 << 1) + +/* PCT_DFILPCFG0 */ +#define TLP_RESP_TIME(n) (n << 16) +#define LP_SR_EN (1 << 8) +#define LP_PD_EN (1 << 0) + +/* PCT_DFITCTRLDELAY */ +#define TCTRL_DELAY_TIME(n) (n << 0) + +/* PCT_DFITPHYWRDATA */ +#define TPHY_WRDATA_TIME(n) (n << 0) + +/* PCT_DFITPHYRDLAT */ +#define TPHY_RDLAT_TIME(n) (n << 0) + +/* PCT_DFITDRAMCLKDIS */ +#define TDRAM_CLK_DIS_TIME(n) (n << 0) + +/* PCT_DFITDRAMCLKEN */ +#define TDRAM_CLK_EN_TIME(n) (n << 0) + +/* PCTL_DFIODTCFG */ +#define RANK0_ODT_WRITE_SEL (1 << 3) +#define RANK1_ODT_WRITE_SEL (1 << 11) + +/* PCTL_DFIODTCFG1 */ +#define ODT_LEN_BL8_W(n) (n<<16) + +/* PUBL_ACDLLCR */ +#define ACDLLCR_DLLDIS (1 << 31) +#define ACDLLCR_DLLSRST (1 << 30) + +/* PUBL_DXDLLCR */ +#define DXDLLCR_DLLDIS (1 << 31) +#define DXDLLCR_DLLSRST (1 << 30) + +/* PUBL_DLLGCR */ +#define DLLGCR_SBIAS (1 << 30) + +/* PUBL_DXGCR */ +#define DQSRTT (1 << 9) +#define DQRTT (1 << 10) + +/* PIR */ +#define PIR_INIT (1 << 0) +#define PIR_DLLSRST (1 << 1) +#define PIR_DLLLOCK (1 << 2) +#define PIR_ZCAL (1 << 3) +#define PIR_ITMSRST (1 << 4) +#define PIR_DRAMRST (1 << 5) +#define PIR_DRAMINIT (1 << 6) +#define PIR_QSTRN (1 << 7) +#define PIR_RVTRN (1 << 8) +#define PIR_ICPC (1 << 16) +#define PIR_DLLBYP (1 << 17) +#define PIR_CTLDINIT (1 << 18) +#define PIR_CLRSR (1 << 28) +#define PIR_LOCKBYP (1 << 29) +#define PIR_ZCALBYP (1 << 30) +#define PIR_INITBYP (1u << 31) + +/* PGCR */ +#define PGCR_DFTLMT(n) ((n) << 3) +#define PGCR_DFTCMP(n) ((n) << 2) +#define PGCR_DQSCFG(n) ((n) << 1) +#define PGCR_ITMDMD(n) ((n) << 0) + +/* PGSR */ +#define PGSR_IDONE (1 << 0) +#define PGSR_DLDONE (1 << 1) +#define PGSR_ZCDONE (1 << 2) +#define PGSR_DIDONE (1 << 3) +#define PGSR_DTDONE (1 << 4) +#define PGSR_DTERR (1 << 5) +#define PGSR_DTIERR (1 << 6) +#define PGSR_DFTERR (1 << 7) +#define PGSR_RVERR (1 << 8) +#define PGSR_RVEIRR (1 << 9) + +/* PTR0 */ +#define PRT_ITMSRST(n) ((n) << 18) +#define PRT_DLLLOCK(n) ((n) << 6) +#define PRT_DLLSRST(n) ((n) << 0) + +/* PTR1 */ +#define PRT_DINIT1(n) ((n) << 19) +#define PRT_DINIT0(n) ((n) << 0) + +/* PTR2 */ +#define PRT_DINIT3(n) ((n) << 17) +#define PRT_DINIT2(n) ((n) << 0) + +/* DCR */ +#define DDRMD_LPDDR 0 +#define DDRMD_DDR 1 +#define DDRMD_DDR2 2 +#define DDRMD_DDR3 3 +#define DDRMD_LPDDR2_LPDDR3 4 +#define DDRMD_MSK (7 << 0) +#define DDRMD_CFG(n) ((n) << 0) +#define PDQ_MSK (7 << 4) +#define PDQ_CFG(n) ((n) << 4) + +/* DXCCR */ +#define DQSNRES_MSK (0x0f << 8) +#define DQSNRES_CFG(n) ((n) << 8) +#define DQSRES_MSK (0x0f << 4) +#define DQSRES_CFG(n) ((n) << 4) + +/* DTPR */ +#define TDQSCKMAX_VAL(n) (((n) >> 27) & 7) +#define TDQSCK_VAL(n) (((n) >> 24) & 7) + +/* DSGCR */ +#define DQSGX_MSK (0x07 << 5) +#define DQSGX_CFG(n) ((n) << 5) +#define DQSGE_MSK (0x07 << 8) +#define DQSGE_CFG(n) ((n) << 8) + +/* SCTL */ +#define INIT_STATE (0) +#define CFG_STATE (1) +#define GO_STATE (2) +#define SLEEP_STATE (3) +#define WAKEUP_STATE (4) + +/* STAT */ +#define LP_TRIG_VAL(n) (((n) >> 4) & 7) +#define PCTL_STAT_MSK (7) +#define INIT_MEM (0) +#define CONFIG (1) +#define CONFIG_REQ (2) +#define ACCESS (3) +#define ACCESS_REQ (4) +#define LOW_POWER (5) +#define LOW_POWER_ENTRY_REQ (6) +#define LOW_POWER_EXIT_REQ (7) + +/* ZQCR*/ +#define PD_OUTPUT(n) ((n) << 0) +#define PU_OUTPUT(n) ((n) << 5) +#define PD_ONDIE(n) ((n) << 10) +#define PU_ONDIE(n) ((n) << 15) +#define ZDEN(n) ((n) << 28) + +/* DDLGCR */ +#define SBIAS_BYPASS (1 << 23) + +/* MCFG */ +#define MDDR_LPDDR2_CLK_STOP_IDLE(n) ((n) << 24) +#define PD_IDLE(n) ((n) << 8) +#define MDDR_EN (2 << 22) +#define LPDDR2_EN (3 << 22) +#define DDR2_EN (0 << 5) +#define DDR3_EN (1 << 5) +#define LPDDR2_S2 (0 << 6) +#define LPDDR2_S4 (1 << 6) +#define MDDR_LPDDR2_BL_2 (0 << 20) +#define MDDR_LPDDR2_BL_4 (1 << 20) +#define MDDR_LPDDR2_BL_8 (2 << 20) +#define MDDR_LPDDR2_BL_16 (3 << 20) +#define DDR2_DDR3_BL_4 (0) +#define DDR2_DDR3_BL_8 (1) +#define TFAW_CFG(n) (((n)-4) << 18) +#define PD_EXIT_SLOW (0 << 17) +#define PD_EXIT_FAST (1 << 17) +#define PD_TYPE(n) ((n) << 16) +#define BURSTLENGTH_CFG(n) (((n) >> 1) << 20) + +/* POWCTL */ +#define POWER_UP_START (1 << 0) + +/* POWSTAT */ +#define POWER_UP_DONE (1 << 0) + +/* MCMD */ +#define DESELECT_CMD (0) +#define PREA_CMD (1) +#define REF_CMD (2) +#define MRS_CMD (3) +#define ZQCS_CMD (4) +#define ZQCL_CMD (5) +#define RSTL_CMD (6) +#define MRR_CMD (8) +#define DPDE_CMD (9) + +#define LPDDR2_MA(n) (((n) & 0xff) << 4) + +#define START_CMD (1u << 31) + +/* DEVTODEV */ +#define BUSWRTORD(n) ((n) << 4) +#define BUSRDTOWR(n) ((n) << 2) +#define BUSRDTORD(n) ((n) << 0) + +/* GRF_SOC_CON0 */ +#define MSCH_MAINDDR3(ch, n) (((n) << (3 + (ch))) \ + | ((1 << (3 + (ch))) << 16)) + +/* GRF_SOC_CON2 */ +#define PUBL_LPDDR3_EN(ch, n) RK_CLRSETBITS(1 << (10 + (3 * (ch))), \ + (n) << (10 + (3 * (ch)))) +#define PCTL_LPDDR3_ODT_EN(ch, n) RK_CLRSETBITS(1 << (9 + (3 * (ch))), \ + (n) << (9 + (3 * (ch)))) +#define PCTL_BST_DISABLE(ch, n) RK_CLRSETBITS(1 << (8 + (3 * (ch))), \ + (n) << (8 + (3 * (ch)))) + +/* mr1 for ddr3 */ +#define DDR3_DLL_ENABLE (0) +#define DDR3_DLL_DISABLE (1) + +/* + * sys_reg bitfield struct + * [31] row_3_4_ch1 + * [30] row_3_4_ch0 + * [29:28] chinfo + * [27] rank_ch1 + * [26:25] col_ch1 + * [24] bk_ch1 + * [23:22] cs0_row_ch1 + * [21:20] cs1_row_ch1 + * [19:18] bw_ch1 + * [17:16] dbw_ch1; + * [15:13] ddrtype + * [12] channelnum + * [11] rank_ch0 + * [10:9] col_ch0 + * [8] bk_ch0 + * [7:6] cs0_row_ch0 + * [5:4] cs1_row_ch0 + * [3:2] bw_ch0 + * [1:0] dbw_ch0 +*/ +#define SYS_REG_DDRTYPE(n) ((n) << 13) +#define SYS_REG_NUM_CH(n) (((n) - 1) << 12) +#define SYS_REG_ROW_3_4(n, ch) ((n) << (30 + (ch))) +#define SYS_REG_CHINFO(ch) (1 << (28 + (ch))) +#define SYS_REG_RANK(n, ch) (((n) - 1) << (11 + ((ch) * 16))) +#define SYS_REG_COL(n, ch) (((n) - 9) << (9 + ((ch) * 16))) +#define SYS_REG_BK(n, ch) (((n) == 3 ? 0 : 1) \ + << (8 + ((ch) * 16))) +#define SYS_REG_CS0_ROW(n, ch) (((n) - 13) << (6 + ((ch) * 16))) +#define SYS_REG_CS1_ROW(n, ch) (((n) - 13) << (4 + ((ch) * 16))) +#define SYS_REG_BW(n, ch) ((2 >> (n)) << (2 + ((ch) * 16))) +#define SYS_REG_DBW(n, ch) ((2 >> (n)) << (0 + ((ch) * 16))) + +static void copy_to_reg(u32 *dest, const u32 *src, u32 n) +{ + int i; + for (i = 0; i < n / sizeof(u32); i++) { + writel(*src, dest); + src++; + dest++; + } +} + +static void phy_pctrl_reset(struct rk3288_ddr_publ_regs *ddr_publ_regs, + u32 channel) +{ + int i; + rkclk_ddr_reset(channel, 1, 1); + udelay(1); + clrbits_le32(&ddr_publ_regs->acdllcr, ACDLLCR_DLLSRST); + for (i = 0; i < 4; i++) + clrbits_le32(&ddr_publ_regs->datx8[i].dxdllcr, DXDLLCR_DLLSRST); + + udelay(10); + setbits_le32(&ddr_publ_regs->acdllcr, ACDLLCR_DLLSRST); + for (i = 0; i < 4; i++) + setbits_le32(&ddr_publ_regs->datx8[i].dxdllcr, DXDLLCR_DLLSRST); + + udelay(10); + rkclk_ddr_reset(channel, 1, 0); + udelay(10); + rkclk_ddr_reset(channel, 0, 0); + udelay(1); +} + +static void phy_dll_bypass_set(struct rk3288_ddr_publ_regs *ddr_publ_regs, + u32 freq) +{ + int i; + if (freq <= 250000000) { + if (freq <= 150000000) + clrbits_le32(&ddr_publ_regs->dllgcr, SBIAS_BYPASS); + else + setbits_le32(&ddr_publ_regs->dllgcr, SBIAS_BYPASS); + setbits_le32(&ddr_publ_regs->acdllcr, ACDLLCR_DLLDIS); + for (i = 0; i < 4; i++) + setbits_le32(&ddr_publ_regs->datx8[i].dxdllcr, + DXDLLCR_DLLDIS); + + setbits_le32(&ddr_publ_regs->pir, PIR_DLLBYP); + } else { + clrbits_le32(&ddr_publ_regs->dllgcr, SBIAS_BYPASS); + clrbits_le32(&ddr_publ_regs->acdllcr, ACDLLCR_DLLDIS); + for (i = 0; i < 4; i++) + clrbits_le32(&ddr_publ_regs->datx8[i].dxdllcr, + DXDLLCR_DLLDIS); + + clrbits_le32(&ddr_publ_regs->pir, PIR_DLLBYP); + } +} + +static void dfi_cfg(struct rk3288_ddr_pctl_regs *ddr_pctl_regs, u32 dramtype) +{ + writel(DFI_INIT_START, &ddr_pctl_regs->dfistcfg0); + writel(DFI_DRAM_CLK_SR_EN | DFI_DRAM_CLK_DPD_EN, + &ddr_pctl_regs->dfistcfg1); + writel(DFI_PARITY_INTR_EN | DFI_PARITY_EN, &ddr_pctl_regs->dfistcfg2); + writel(TLP_RESP_TIME(7) | LP_SR_EN | LP_PD_EN, + &ddr_pctl_regs->dfilpcfg0); + + writel(TCTRL_DELAY_TIME(2), &ddr_pctl_regs->dfitctrldelay); + writel(TPHY_WRDATA_TIME(1), &ddr_pctl_regs->dfitphywrdata); + writel(TPHY_RDLAT_TIME(0xf), &ddr_pctl_regs->dfitphyrdlat); + writel(TDRAM_CLK_DIS_TIME(2), &ddr_pctl_regs->dfitdramclkdis); + writel(TDRAM_CLK_EN_TIME(2), &ddr_pctl_regs->dfitdramclken); + writel(0x1, &ddr_pctl_regs->dfitphyupdtype0); + + /* cs0 and cs1 write odt enable */ + writel((RANK0_ODT_WRITE_SEL | RANK1_ODT_WRITE_SEL), + &ddr_pctl_regs->dfiodtcfg); + /* odt write length */ + writel(ODT_LEN_BL8_W(7), &ddr_pctl_regs->dfiodtcfg1); + /* phyupd and ctrlupd disabled */ + writel(0, &ddr_pctl_regs->dfiupdcfg); +} + +static void pctl_cfg(u32 channel, + const struct rk3288_sdram_params *sdram_params) +{ + unsigned int burstlen; + struct rk3288_ddr_pctl_regs *ddr_pctl_regs = rk3288_ddr_pctl[channel]; + burstlen = (sdram_params->noc_timing >> 18) & 0x7; + copy_to_reg(&ddr_pctl_regs->togcnt1u, + &(sdram_params->pctl_timing.togcnt1u), + sizeof(sdram_params->pctl_timing)); + switch (sdram_params->dramtype) { + case LPDDR3: + writel(sdram_params->pctl_timing.tcl - 1, + &ddr_pctl_regs->dfitrddataen); + writel(sdram_params->pctl_timing.tcwl, + &ddr_pctl_regs->dfitphywrlat); + writel(LPDDR2_S4 | MDDR_LPDDR2_CLK_STOP_IDLE(0) | LPDDR2_EN + | BURSTLENGTH_CFG(burstlen) | TFAW_CFG(6) | PD_EXIT_FAST + | PD_TYPE(1) | PD_IDLE(0), &ddr_pctl_regs->mcfg); + writel(MSCH_MAINDDR3(channel, 0), &rk3288_grf->soc_con0); + + writel(PUBL_LPDDR3_EN(channel, 1) + | PCTL_BST_DISABLE(channel, 1) + | PCTL_LPDDR3_ODT_EN(channel, 1), + &rk3288_grf->soc_con2); + + break; + case DDR3: + if (sdram_params->phy_timing.mr[1] & DDR3_DLL_DISABLE) + writel(sdram_params->pctl_timing.tcl - 3, + &ddr_pctl_regs->dfitrddataen); + else + writel(sdram_params->pctl_timing.tcl - 2, + &ddr_pctl_regs->dfitrddataen); + writel(sdram_params->pctl_timing.tcwl - 1, + &ddr_pctl_regs->dfitphywrlat); + writel(MDDR_LPDDR2_CLK_STOP_IDLE(0) | DDR3_EN + | DDR2_DDR3_BL_8 | TFAW_CFG(5) | PD_EXIT_SLOW + | PD_TYPE(1) | PD_IDLE(0), &ddr_pctl_regs->mcfg); + writel(MSCH_MAINDDR3(channel, 1), &rk3288_grf->soc_con0); + + writel(PUBL_LPDDR3_EN(channel, 0) + | PCTL_BST_DISABLE(channel, 0) + | PCTL_LPDDR3_ODT_EN(channel, 0), + &rk3288_grf->soc_con2); + + break; + } + + setbits_le32(&ddr_pctl_regs->scfg, 1); +} + +static void phy_cfg(u32 channel, const struct rk3288_sdram_params *sdram_params) +{ + u32 i; + struct rk3288_ddr_publ_regs *ddr_publ_regs = rk3288_ddr_publ[channel]; + struct rk3288_msch_regs *msch_regs = rk3288_msch[channel]; + + /* DDR PHY Timing */ + copy_to_reg(&ddr_publ_regs->dtpr[0], + &(sdram_params->phy_timing.dtpr0), + sizeof(sdram_params->phy_timing)); + writel(sdram_params->noc_timing, &msch_regs->ddrtiming); + writel(0x3f, &msch_regs->readlatency); + writel(sdram_params->noc_activate, &msch_regs->activate); + writel(BUSWRTORD(2) | BUSRDTOWR(2) | BUSRDTORD(1), + &msch_regs->devtodev); + writel(PRT_ITMSRST(8) | PRT_DLLLOCK(2750) | PRT_DLLSRST(27), + &ddr_publ_regs->ptr[0]); + /* tDINIT1=400ns (533MHz), tDINIT0=500us (533MHz) */ + writel(PRT_DINIT1(213) | PRT_DINIT0(266525), &ddr_publ_regs->ptr[1]); + /* tDINIT3=1us (533MHz), tDINIT2=200us (533MHz) */ + writel(PRT_DINIT3(534) | PRT_DINIT2(106610), &ddr_publ_regs->ptr[2]); + + switch (sdram_params->dramtype) { + case LPDDR3: + clrsetbits_le32(&ddr_publ_regs->pgcr, 0x1F, PGCR_DFTLMT(0) + | PGCR_DFTCMP(0) | PGCR_DQSCFG(1) | PGCR_ITMDMD(0)); + /* DDRMODE select LPDDR3 */ + clrsetbits_le32(&ddr_publ_regs->dcr, DDRMD_MSK, + DDRMD_CFG(DDRMD_LPDDR2_LPDDR3)); + clrsetbits_le32(&ddr_publ_regs->dxccr, DQSNRES_MSK | DQSRES_MSK, + DQSRES_CFG(4) | DQSNRES_CFG(0xc)); + i = TDQSCKMAX_VAL(readl(&ddr_publ_regs->dtpr[1])) + - TDQSCK_VAL(readl(&ddr_publ_regs->dtpr[1])); + clrsetbits_le32(&ddr_publ_regs->dsgcr, DQSGE_MSK | DQSGX_MSK, + DQSGE_CFG(i) | DQSGX_CFG(i)); + break; + case DDR3: + clrbits_le32(&ddr_publ_regs->pgcr, 0x1f); + clrsetbits_le32(&ddr_publ_regs->dcr, DDRMD_MSK, + DDRMD_CFG(DDRMD_DDR3)); + break; + } + if (sdram_params->odt) { + /*dynamic RTT enable */ + for (i = 0; i < 4; i++) + setbits_le32(&ddr_publ_regs->datx8[i].dxgcr, + DQSRTT | DQRTT); + } else { + /*dynamic RTT disable */ + for (i = 0; i < 4; i++) + clrbits_le32(&ddr_publ_regs->datx8[i].dxgcr, + DQSRTT | DQRTT); + + } +} + +static void phy_init(struct rk3288_ddr_publ_regs *ddr_publ_regs) +{ + setbits_le32(&ddr_publ_regs->pir, PIR_INIT | PIR_DLLSRST + | PIR_DLLLOCK | PIR_ZCAL | PIR_ITMSRST | PIR_CLRSR); + udelay(1); + while ((readl(&ddr_publ_regs->pgsr) & + (PGSR_IDONE | PGSR_DLDONE | PGSR_ZCDONE)) != + (PGSR_IDONE | PGSR_DLDONE | PGSR_ZCDONE)) + ; +} + +static void send_command(struct rk3288_ddr_pctl_regs *ddr_pctl_regs, u32 rank, + u32 cmd, u32 arg) +{ + writel((START_CMD | (rank << 20) | arg | cmd), &ddr_pctl_regs->mcmd); + udelay(1); + while (readl(&ddr_pctl_regs->mcmd) & START_CMD) + ; +} + +static void memory_init(struct rk3288_ddr_publ_regs *ddr_publ_regs, + u32 dramtype) +{ + setbits_le32(&ddr_publ_regs->pir, + (PIR_INIT | PIR_DRAMINIT | PIR_LOCKBYP + | PIR_ZCALBYP | PIR_CLRSR | PIR_ICPC + | (dramtype == DDR3 ? PIR_DRAMRST : 0))); + udelay(1); + while ((readl(&ddr_publ_regs->pgsr) & (PGSR_IDONE | PGSR_DLDONE)) + != (PGSR_IDONE | PGSR_DLDONE)) + ; +} + +static void move_to_config_state(struct rk3288_ddr_publ_regs *ddr_publ_regs, + struct rk3288_ddr_pctl_regs *ddr_pctl_regs) +{ + unsigned int state; + + while (1) { + state = readl(&ddr_pctl_regs->stat) & PCTL_STAT_MSK; + + switch (state) { + case LOW_POWER: + writel(WAKEUP_STATE, &ddr_pctl_regs->sctl); + while ((readl(&ddr_pctl_regs->stat) & PCTL_STAT_MSK) + != ACCESS) + ; + /* wait DLL lock */ + while ((readl(&ddr_publ_regs->pgsr) & PGSR_DLDONE) + != PGSR_DLDONE) + ; + /* if at low power state,need wakeup first, + * and then enter the config + * so here no break. + */ + case ACCESS: + case INIT_MEM: + writel(CFG_STATE, &ddr_pctl_regs->sctl); + while ((readl(&ddr_pctl_regs->stat) & PCTL_STAT_MSK) + != CONFIG) + ; + break; + case CONFIG: + return; + default: + break; + } + } +} + +static void set_bandwidth_ratio(u32 channel, u32 n) +{ + struct rk3288_ddr_pctl_regs *ddr_pctl_regs = rk3288_ddr_pctl[channel]; + struct rk3288_ddr_publ_regs *ddr_publ_regs = rk3288_ddr_publ[channel]; + struct rk3288_msch_regs *msch_regs = rk3288_msch[channel]; + + if (n == 1) { + setbits_le32(&ddr_pctl_regs->ppcfg, 1); + writel(RK_SETBITS(1 << (8 + channel)), + &rk3288_grf->soc_con0); + setbits_le32(&msch_regs->ddrtiming, 1 << 31); + /* Data Byte disable*/ + clrbits_le32(&ddr_publ_regs->datx8[2].dxgcr, 1); + clrbits_le32(&ddr_publ_regs->datx8[3].dxgcr, 1); + /*disable DLL */ + setbits_le32(&ddr_publ_regs->datx8[2].dxdllcr, + DXDLLCR_DLLDIS); + setbits_le32(&ddr_publ_regs->datx8[3].dxdllcr, + DXDLLCR_DLLDIS); + } else { + clrbits_le32(&ddr_pctl_regs->ppcfg, 1); + writel(RK_CLRBITS(1 << (8 + channel)), + &rk3288_grf->soc_con0); + clrbits_le32(&msch_regs->ddrtiming, 1 << 31); + /* Data Byte enable*/ + setbits_le32(&ddr_publ_regs->datx8[2].dxgcr, 1); + setbits_le32(&ddr_publ_regs->datx8[3].dxgcr, 1); + + /*enable DLL */ + clrbits_le32(&ddr_publ_regs->datx8[2].dxdllcr, + DXDLLCR_DLLDIS); + clrbits_le32(&ddr_publ_regs->datx8[3].dxdllcr, + DXDLLCR_DLLDIS); + /* reset DLL */ + clrbits_le32(&ddr_publ_regs->datx8[2].dxdllcr, + DXDLLCR_DLLSRST); + clrbits_le32(&ddr_publ_regs->datx8[3].dxdllcr, + DXDLLCR_DLLSRST); + udelay(10); + setbits_le32(&ddr_publ_regs->datx8[2].dxdllcr, + DXDLLCR_DLLSRST); + setbits_le32(&ddr_publ_regs->datx8[3].dxdllcr, + DXDLLCR_DLLSRST); + } + setbits_le32(&ddr_pctl_regs->dfistcfg0, 1 << 2); + +} + +static int data_training(u32 channel, + const struct rk3288_sdram_params *sdram_params) +{ + unsigned int j; + int ret = 0; + u32 rank; + int i; + u32 step[2] = { PIR_QSTRN, PIR_RVTRN }; + struct rk3288_ddr_publ_regs *ddr_publ_regs = rk3288_ddr_publ[channel]; + struct rk3288_ddr_pctl_regs *ddr_pctl_regs = rk3288_ddr_pctl[channel]; + + /* disable auto refresh */ + writel(0, &ddr_pctl_regs->trefi); + + if (sdram_params->dramtype != LPDDR3) + setbits_le32(&ddr_publ_regs->pgcr, PGCR_DQSCFG(1)); + rank = sdram_params->ch[channel].rank | 1; + for (j = 0; j < ARRAY_SIZE(step); j++) { + /* + * trigger QSTRN and RVTRN + * clear DTDONE status + */ + setbits_le32(&ddr_publ_regs->pir, PIR_CLRSR); + + /* trigger DTT */ + setbits_le32(&ddr_publ_regs->pir, + PIR_INIT | step[j] | PIR_LOCKBYP | PIR_ZCALBYP | + PIR_CLRSR); + udelay(1); + /* wait echo byte DTDONE */ + while ((readl(&ddr_publ_regs->datx8[0].dxgsr[0]) & rank) + != rank) + ; + while ((readl(&ddr_publ_regs->datx8[1].dxgsr[0]) & rank) + != rank) + ; + if (!(readl(&ddr_pctl_regs->ppcfg) & 1)) { + while ((readl(&ddr_publ_regs->datx8[2].dxgsr[0]) + & rank) != rank) + ; + while ((readl(&ddr_publ_regs->datx8[3].dxgsr[0]) + & rank) != rank) + ; + } + if (readl(&ddr_publ_regs->pgsr) & + (PGSR_DTERR | PGSR_RVERR | PGSR_RVEIRR)) { + ret = -1; + break; + } + } + /* send some auto refresh to complement the lost while DTT */ + for (i = 0; i < (rank > 1 ? 4 : 2); i++) + send_command(ddr_pctl_regs, rank, REF_CMD, 0); + + if (sdram_params->dramtype != LPDDR3) + clrbits_le32(&ddr_publ_regs->pgcr, PGCR_DQSCFG(1)); + + /* resume auto refresh */ + writel(sdram_params->pctl_timing.trefi, &ddr_pctl_regs->trefi); + + return ret; +} + +static void move_to_access_state(u32 chnum) +{ + struct rk3288_ddr_publ_regs *ddr_publ_regs = rk3288_ddr_publ[chnum]; + struct rk3288_ddr_pctl_regs *ddr_pctl_regs = rk3288_ddr_pctl[chnum]; + + unsigned int state; + + while (1) { + state = readl(&ddr_pctl_regs->stat) & PCTL_STAT_MSK; + + switch (state) { + case LOW_POWER: + if (LP_TRIG_VAL(readl(&ddr_pctl_regs->stat)) == 1) + return; + + writel(WAKEUP_STATE, &ddr_pctl_regs->sctl); + while ((readl(&ddr_pctl_regs->stat) & PCTL_STAT_MSK) + != ACCESS) + ; + /* wait DLL lock */ + while ((readl(&ddr_publ_regs->pgsr) & PGSR_DLDONE) + != PGSR_DLDONE) + ; + break; + case INIT_MEM: + writel(CFG_STATE, &ddr_pctl_regs->sctl); + while ((readl(&ddr_pctl_regs->stat) & PCTL_STAT_MSK) + != CONFIG) + ; + case CONFIG: + writel(GO_STATE, &ddr_pctl_regs->sctl); + while ((readl(&ddr_pctl_regs->stat) & PCTL_STAT_MSK) + == CONFIG) + ; + break; + case ACCESS: + return; + default: + break; + } + } +} + +static void dram_cfg_rbc(u32 chnum, + const struct rk3288_sdram_params *sdram_params) +{ + struct rk3288_ddr_publ_regs *ddr_publ_regs = rk3288_ddr_publ[chnum]; + struct rk3288_msch_regs *msch_regs = rk3288_msch[chnum]; + + if (sdram_params->ch[chnum].bk == 3) + clrsetbits_le32(&ddr_publ_regs->dcr, PDQ_MSK, PDQ_CFG(1)); + else + clrbits_le32(&ddr_publ_regs->dcr, PDQ_MSK); + + writel(sdram_params->ddrconfig, &msch_regs->ddrconf); +} + +static void dram_all_config(const struct rk3288_sdram_params *sdram_params) +{ + u32 sys_reg = 0; + unsigned int channel; + + sys_reg |= SYS_REG_DDRTYPE(sdram_params->dramtype); + sys_reg |= SYS_REG_NUM_CH(sdram_params->num_channels); + for (channel = 0; channel < sdram_params->num_channels; channel++) { + const struct rk3288_sdram_channel *info = + &(sdram_params->ch[channel]); + sys_reg |= SYS_REG_ROW_3_4(info->row_3_4, channel); + sys_reg |= SYS_REG_CHINFO(channel); + sys_reg |= SYS_REG_RANK(info->rank, channel); + sys_reg |= SYS_REG_COL(info->col, channel); + sys_reg |= SYS_REG_BK(info->bk, channel); + sys_reg |= SYS_REG_CS0_ROW(info->cs0_row, channel); + sys_reg |= SYS_REG_CS1_ROW(info->cs1_row, channel); + sys_reg |= SYS_REG_BW(info->bw, channel); + sys_reg |= SYS_REG_DBW(info->dbw, channel); + + dram_cfg_rbc(channel, sdram_params); + } + writel(sys_reg, &rk3288_pmu->sys_reg[2]); + writel(RK_CLRSETBITS(0x1F, sdram_params->stride), + &rk3288_sgrf->soc_con2); +} + +void sdram_init(const struct rk3288_sdram_params *sdram_params) +{ + int channel; + int zqcr; + printk(BIOS_INFO, "Starting SDRAM initialization...\n"); + + if (sdram_params->ddr_freq > 533000000) + die("SDRAM frequency is to high!"); + + rkclk_configure_ddr(sdram_params->ddr_freq); + + for (channel = 0; channel < sdram_params->num_channels; channel++) { + struct rk3288_ddr_pctl_regs *ddr_pctl_regs = + rk3288_ddr_pctl[channel]; + struct rk3288_ddr_publ_regs *ddr_publ_regs = + rk3288_ddr_publ[channel]; + + phy_pctrl_reset(ddr_publ_regs, channel); + phy_dll_bypass_set(ddr_publ_regs, sdram_params->ddr_freq); + + dfi_cfg(ddr_pctl_regs, sdram_params->dramtype); + + pctl_cfg(channel, sdram_params); + + phy_cfg(channel, sdram_params); + + phy_init(ddr_publ_regs); + + writel(POWER_UP_START, &ddr_pctl_regs->powctl); + while (!(readl(&ddr_pctl_regs->powstat) & POWER_UP_DONE)) + ; + send_command(ddr_pctl_regs, 3, DESELECT_CMD, 0); + udelay(1); + send_command(ddr_pctl_regs, 3, PREA_CMD, 0); + + memory_init(ddr_publ_regs, sdram_params->dramtype); + move_to_config_state(ddr_publ_regs, ddr_pctl_regs); + set_bandwidth_ratio(channel, sdram_params->ch[channel].bw); + /* + * set cs + * CS0, n=1 + * CS1, n=2 + * CS0 & CS1, n = 3 + */ + clrsetbits_le32(&ddr_publ_regs->pgcr, 0xF << 18, + (sdram_params->ch[channel].rank | 1) << 18); + /* DS=40ohm,ODT=155ohm */ + zqcr = ZDEN(1) | PU_ONDIE(0x2) | PD_ONDIE(0x2) + | PU_OUTPUT(0x19) | PD_OUTPUT(0x19); + writel(zqcr, &ddr_publ_regs->zq1cr[0]); + writel(zqcr, &ddr_publ_regs->zq0cr[0]); + + if (sdram_params->dramtype == LPDDR3) { + /* LPDDR2/LPDDR3 need to wait DAI complete, max 10us */ + udelay(10); + if (channel == 0) { + writel(0, &ddr_pctl_regs->mrrcfg0); + send_command(ddr_pctl_regs, 1, MRR_CMD, + LPDDR2_MA(0x8)); + /* S8 */ + if ((readl(&ddr_pctl_regs->mrrstat0) & 0x3) + != 3) + die("SDRAM initialization failed!"); + } + } + + if (-1 == data_training(channel, sdram_params)) { + if (sdram_params->dramtype == LPDDR3) { + rkclk_ddr_phy_ctl_reset(channel, 1); + udelay(10); + rkclk_ddr_phy_ctl_reset(channel, 0); + udelay(10); + } + die("SDRAM initialization failed!"); + } + + if (sdram_params->dramtype == LPDDR3) { + u32 i; + writel(0, &ddr_pctl_regs->mrrcfg0); + for (i = 0; i < 17; i++) + send_command(ddr_pctl_regs, 1, MRR_CMD, + LPDDR2_MA(i)); + } + move_to_access_state(channel); + } + dram_all_config(sdram_params); + printk(BIOS_INFO, "Finish SDRAM initialization...\n"); +} diff --git a/src/soc/rockchip/rk3288/sdram.h b/src/soc/rockchip/rk3288/sdram.h new file mode 100644 index 0000000000..1da14b7ed5 --- /dev/null +++ b/src/soc/rockchip/rk3288/sdram.h @@ -0,0 +1,104 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SOC_ROCKCHIP_RK3288_SDRAM_H__ +#define __SOC_ROCKCHIP_RK3288_SDRAM_H__ + +#include <arch/io.h> + +enum { + DDR3 = 3, + LPDDR3 = 6, + UNUSED = 0xFF, +}; + +struct rk3288_sdram_channel { + u8 rank; + u8 col; + u8 bk; + u8 bw; + u8 dbw; + u8 row_3_4; + u8 cs0_row; + u8 cs1_row; +}; + +struct rk3288_sdram_pctl_timing { + u32 togcnt1u; + u32 tinit; + u32 trsth; + u32 togcnt100n; + u32 trefi; + u32 tmrd; + u32 trfc; + u32 trp; + u32 trtw; + u32 tal; + u32 tcl; + u32 tcwl; + u32 tras; + u32 trc; + u32 trcd; + u32 trrd; + u32 trtp; + u32 twr; + u32 twtr; + u32 texsr; + u32 txp; + u32 txpdll; + u32 tzqcs; + u32 tzqcsi; + u32 tdqs; + u32 tcksre; + u32 tcksrx; + u32 tcke; + u32 tmod; + u32 trstl; + u32 tzqcl; + u32 tmrr; + u32 tckesr; + u32 tdpd; +}; +check_member(rk3288_sdram_pctl_timing, tdpd, 0x144 - 0xc0); + +struct rk3288_sdram_phy_timing { + u32 dtpr0; + u32 dtpr1; + u32 dtpr2; + u32 mr[4]; +}; + +struct rk3288_sdram_params { + struct rk3288_sdram_channel ch[2]; + struct rk3288_sdram_pctl_timing pctl_timing; + struct rk3288_sdram_phy_timing phy_timing; + u32 noc_timing; + u32 noc_activate; + u32 ddrconfig; + u32 ddr_freq; + u8 dramtype; + u8 num_channels; + u8 stride; + u8 odt; +}; + +void sdram_init(const struct rk3288_sdram_params *sdram_params); +u32 sdram_get_ram_code(void); +const struct rk3288_sdram_params *get_sdram_config(void); +#endif |