[coreboot-gerrit] New patch to review for coreboot: 5630853 rk3288: add ddr driver

Patrick Georgi (pgeorgi@google.com) gerrit at coreboot.org
Mon Mar 23 17:32:45 CET 2015


Patrick Georgi (pgeorgi at google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/8865

-gerrit

commit 56308536f1be62671a5161ddc8c3ad3e17b2a05d
Author: Jinkun Hong <jinkun.hong at rock-chips.com>
Date:   Thu Aug 28 09:37:22 2014 -0700

    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 at chromium.org>
    Original-Commit-Id: 52838c68fe6963285c974af5dc5837e819efc321
    Original-Change-Id: I6ddfe30b8585002b45060fe998c9238cbb611c05
    Original-Signed-off-by: jinkun.hong <jinkun.hong at rock-chips.com>
    Original-Reviewed-on: https://chromium-review.googlesource.com/209465
    Original-Reviewed-by: Julius Werner <jwerner at chromium.org>
    Original-Commit-Queue: Julius Werner <jwerner at chromium.org>
---
 src/mainboard/google/veyron/Makefile.inc           |    1 +
 src/mainboard/google/veyron/romstage.c             |    2 +
 src/mainboard/google/veyron/sdram_configs.c        |   75 ++
 .../veyron/sdram_inf/sdram-ddr3-hynix-2GB.inc      |   77 ++
 .../veyron/sdram_inf/sdram-lpddr3-samsung-2GB.inc  |   78 ++
 .../google/veyron/sdram_inf/sdram-unused.inc       |    3 +
 src/soc/rockchip/rk3288/Makefile.inc               |    2 +-
 src/soc/rockchip/rk3288/clock.c                    |   77 +-
 src/soc/rockchip/rk3288/clock.h                    |    5 +-
 src/soc/rockchip/rk3288/cpu.h                      |    2 +
 src/soc/rockchip/rk3288/grf.h                      |   40 +-
 src/soc/rockchip/rk3288/sdram.c                    | 1046 ++++++++++++++++++++
 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 50a6ba0..f249cf9 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 5b26f48..5831fde 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 0000000..b3600fb
--- /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 0000000..409a7ca
--- /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 0000000..315e542
--- /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 0000000..06498f7
--- /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 2295263..fda0169 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 757d180..b194b4c 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 e773219..d04dfeb 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 c42bbe8..9bcfe3e 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 547a4c7..b035bb9 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 0000000..4f8b268
--- /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 0000000..1da14b7
--- /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



More information about the coreboot-gerrit mailing list