diff --git a/arch/arm/include/asm/arch-rockchip/sdram_rk3036.h b/arch/arm/include/asm/arch-rockchip/sdram_rk3036.h
new file mode 100644
index 0000000..4ce2ba5
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/sdram_rk3036.h
@@ -0,0 +1,341 @@
+/*
+ * (C) Copyright 2015 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+#ifndef _ASM_ARCH_SDRAM_RK3036_H
+#define _ASM_ARCH_SDRAM_RK3036_H
+
+#include <common.h>
+
+struct rk3036_ddr_pctl {
+	u32 scfg;
+	u32 sctl;
+	u32 stat;
+	u32 intrstat;
+	u32 reserved0[12];
+	u32 mcmd;
+	u32 powctl;
+	u32 powstat;
+	u32 cmdtstat;
+	u32 cmdtstaten;
+	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 reserved5[47];
+	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(rk3036_ddr_pctl, iptr, 0x03fc);
+
+struct rk3036_ddr_phy {
+	u32 ddrphy_reg1;
+	u32 ddrphy_reg3;
+	u32 ddrphy_reg2;
+	u32 reserve[11];
+	u32 ddrphy_reg4a;
+	u32 ddrphy_reg4b;
+	u32 reserve1[5];
+	u32 ddrphy_reg16;
+	u32 reserve2;
+	u32 ddrphy_reg18;
+	u32 ddrphy_reg19;
+	u32 reserve3;
+	u32 ddrphy_reg21;
+	u32 reserve4;
+	u32 ddrphy_reg22;
+	u32 reserve5[3];
+	u32 ddrphy_reg25;
+	u32 ddrphy_reg26;
+	u32 ddrphy_reg27;
+	u32 ddrphy_reg28;
+	u32 reserve6[17];
+	u32 ddrphy_reg6;
+	u32 ddrphy_reg7;
+	u32 reserve7;
+	u32 ddrphy_reg8;
+	u32 ddrphy_reg0e4;
+	u32 reserve8[11];
+	u32 ddrphy_reg9;
+	u32 ddrphy_reg10;
+	u32 reserve9;
+	u32 ddrphy_reg11;
+	u32 ddrphy_reg124;
+	u32 reserve10[38];
+	u32 ddrphy_reg29;
+	u32 reserve11[40];
+	u32 ddrphy_reg264;
+	u32 reserve12[18];
+	u32 ddrphy_reg2a;
+	u32 reserve13[4];
+	u32 ddrphy_reg30;
+	u32 ddrphy_reg31;
+	u32 ddrphy_reg32;
+	u32 ddrphy_reg33;
+	u32 ddrphy_reg34;
+	u32 ddrphy_reg35;
+	u32 ddrphy_reg36;
+	u32 ddrphy_reg37;
+	u32 ddrphy_reg38;
+	u32 ddrphy_reg39;
+	u32 ddrphy_reg40;
+	u32 ddrphy_reg41;
+	u32 ddrphy_reg42;
+	u32 ddrphy_reg43;
+	u32 ddrphy_reg44;
+	u32 ddrphy_reg45;
+	u32 ddrphy_reg46;
+	u32 ddrphy_reg47;
+	u32 ddrphy_reg48;
+	u32 ddrphy_reg49;
+	u32 ddrphy_reg50;
+	u32 ddrphy_reg51;
+	u32 ddrphy_reg52;
+	u32 ddrphy_reg53;
+	u32 reserve14;
+	u32 ddrphy_reg54;
+	u32 ddrphy_reg55;
+	u32 ddrphy_reg56;
+	u32 ddrphy_reg57;
+	u32 ddrphy_reg58;
+	u32 ddrphy_reg59;
+	u32 ddrphy_reg5a;
+	u32 ddrphy_reg5b;
+	u32 ddrphy_reg5c;
+	u32 ddrphy_reg5d;
+	u32 ddrphy_reg5e;
+	u32 reserve15[28];
+	u32 ddrphy_reg5f;
+	u32 reserve16[6];
+	u32 ddrphy_reg60;
+	u32 ddrphy_reg61;
+	u32 ddrphy_reg62;
+};
+check_member(rk3036_ddr_phy, ddrphy_reg62, 0x03e8);
+
+struct rk3036_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;
+};
+
+struct rk3036_phy_timing {
+	u32 mr[4];
+	u32 bl;
+	u32 cl_al;
+};
+
+typedef union {
+	u32 noc_timing;
+	struct {
+		u32 acttoact:6;
+		u32 rdtomiss:6;
+		u32 wrtomiss:6;
+		u32 burstlen:3;
+		u32 rdtowr:5;
+		u32 wrtord:5;
+		u32 bwratio:1;
+	};
+} rk3036_noc_timing;
+
+struct rk3036_ddr_timing {
+	u32 freq;
+	struct rk3036_pctl_timing pctl_timing;
+	struct rk3036_phy_timing phy_timing;
+	rk3036_noc_timing noc_timing;
+};
+
+struct rk3036_service_sys {
+	u32 id_coreid;
+	u32 id_revisionid;
+	u32 ddrconf;
+	u32 ddrtiming;
+	u32 ddrmode;
+	u32 readlatency;
+};
+
+struct rk3036_ddr_config {
+	/*
+	 * 000: lpddr
+	 * 001: ddr
+	 * 010: ddr2
+	 * 011: ddr3
+	 * 100: lpddr2-s2
+	 * 101: lpddr2-s4
+	 * 110: lpddr3
+	 */
+	u32 ddr_type;
+	u32 rank;
+	u32 cs0_row;
+	u32 cs1_row;
+
+	/* 2: 4bank, 3: 8bank */
+	u32 bank;
+	u32 col;
+
+	/* bw(0: 8bit, 1: 16bit, 2: 32bit) */
+	u32 bw;
+};
+
+/* rk3036 sdram initial */
+void sdram_init(void);
+
+/* get ddr die config, implement in specific board */
+void get_ddr_config(struct rk3036_ddr_config *config);
+
+/* get ddr size on board */
+size_t sdram_size(void);
+#endif
diff --git a/arch/arm/mach-rockchip/rk3036/Makefile b/arch/arm/mach-rockchip/rk3036/Makefile
index 5d14b95..6095777 100644
--- a/arch/arm/mach-rockchip/rk3036/Makefile
+++ b/arch/arm/mach-rockchip/rk3036/Makefile
@@ -8,3 +8,5 @@ ifndef CONFIG_SPL_BUILD
 obj-y += reset_rk3036.o
 obj-y += syscon_rk3036.o
 endif
+
+obj-y += sdram_rk3036.o
diff --git a/arch/arm/mach-rockchip/rk3036/sdram_rk3036.c b/arch/arm/mach-rockchip/rk3036/sdram_rk3036.c
new file mode 100644
index 0000000..6fcb03e
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3036/sdram_rk3036.c
@@ -0,0 +1,766 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <asm/arch/cru_rk3036.h>
+#include <asm/arch/grf_rk3036.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/sdram_rk3036.h>
+#include <asm/arch/timer.h>
+#include <asm/arch/uart.h>
+
+#define CRU_BASE	0x20000000
+#define GRF_BASE	0x20008000
+#define	DDR_PHY_BASE	0x2000a000
+#define	DDR_PCTL_BASE	0x20004000
+#define CPU_AXI_BUS_BASE	0x10128000
+
+struct rk3036_sdram_priv {
+	struct rk3036_cru *cru;
+	struct rk3036_grf *grf;
+	struct rk3036_ddr_phy *phy;
+	struct rk3036_ddr_pctl *pctl;
+	struct rk3036_service_sys *axi_bus;
+
+	/* ddr die config */
+	struct rk3036_ddr_config ddr_config;
+};
+
+/* use interge mode, 396M dpll setting
+ * refdiv, fbdiv, postdiv1, postdiv2
+ */
+const struct pll_div dpll_init_cfg = {1, 66, 4, 1};
+
+/* 396Mhz ddr timing */
+const struct rk3036_ddr_timing ddr_timing = {0x18c,
+	{0x18c, 0xc8, 0x1f4, 0x27, 0x4e,
+	0x4, 0x8b, 0x06, 0x03, 0x0, 0x06, 0x05, 0x0f, 0x15, 0x06, 0x04, 0x04,
+	0x06, 0x04, 0x200, 0x03, 0x0a, 0x40, 0x2710, 0x01, 0x05, 0x05, 0x03,
+	0x0c, 0x28, 0x100, 0x0, 0x04, 0x0},
+	{{0x420, 0x42, 0x0, 0x0}, 0x01, 0x60},
+	{0x24717315} };
+
+/*
+ * [7:6]  bank(n:n bit bank)
+ * [5:4]  row(13+n)
+ * [3]    cs(0:1 cs, 1:2 cs)
+ * [2:1]  bank(n:n bit bank)
+ * [0]    col(10+n)
+ */
+char ddr_cfg_2_rbc[] = {
+	((3 << 6) | (3 << 4) | (0 << 3) | (0 << 1) | 1),
+	((0 << 6) | (1 << 4) | (0 << 3) | (3 << 1) | 0),
+	((0 << 6) | (2 << 4) | (0 << 3) | (3 << 1) | 0),
+	((0 << 6) | (3 << 4) | (0 << 3) | (3 << 1) | 0),
+	((0 << 6) | (1 << 4) | (0 << 3) | (3 << 1) | 1),
+	((0 << 6) | (2 << 4) | (0 << 3) | (3 << 1) | 1),
+	((0 << 6) | (3 << 4) | (0 << 3) | (3 << 1) | 1),
+	((0 << 6) | (0 << 4) | (0 << 3) | (3 << 1) | 0),
+	((0 << 6) | (0 << 4) | (0 << 3) | (3 << 1) | 1),
+	((0 << 6) | (3 << 4) | (1 << 3) | (3 << 1) | 0),
+	((0 << 6) | (3 << 4) | (1 << 3) | (3 << 1) | 1),
+	((1 << 6) | (2 << 4) | (0 << 3) | (2 << 1) | 0),
+	((3 << 6) | (2 << 4) | (0 << 3) | (0 << 1) | 1),
+	((3 << 6) | (3 << 4) | (0 << 3) | (0 << 1) | 0),
+};
+
+/* DDRPHY REG */
+enum {
+	/* DDRPHY_REG1 */
+	SOFT_RESET_MASK				= 3,
+	SOFT_RESET_SHIFT			= 2,
+
+	/* DDRPHY_REG2 */
+	MEMORY_SELECT_DDR3			= 0 << 6,
+	DQS_SQU_CAL_NORMAL_MODE			= 0 << 1,
+	DQS_SQU_CAL_START			= 1 << 0,
+	DQS_SQU_NO_CAL				= 0 << 0,
+
+	/* DDRPHY_REG2A */
+	CMD_DLL_BYPASS				= 1 << 4,
+	CMD_DLL_BYPASS_DISABLE			= 0 << 4,
+	HIGH_8BIT_DLL_BYPASS			= 1 << 3,
+	HIGH_8BIT_DLL_BYPASS_DISABLE		= 0 << 3,
+	LOW_8BIT_DLL_BYPASS			= 1 << 2,
+	LOW_8BIT_DLL_BYPASS_DISABLE		= 0 << 2,
+
+	/* DDRPHY_REG19 */
+	CMD_FEEDBACK_ENABLE			= 1 << 5,
+	CMD_SLAVE_DLL_INVERSE_MODE		= 1 << 4,
+	CMD_SLAVE_DLL_NO_INVERSE_MODE		= 0 << 4,
+	CMD_SLAVE_DLL_ENALBE			= 1 << 3,
+	CMD_TX_SLAVE_DLL_DELAY_MASK		= 7,
+	CMD_TX_SLAVE_DLL_DELAY_SHIFT		= 0,
+
+	/* DDRPHY_REG6 */
+	LEFT_CHN_TX_DQ_PHASE_BYPASS_90		= 1 << 4,
+	LEFT_CHN_TX_DQ_PHASE_BYPASS_0		= 0 << 4,
+	LEFT_CHN_TX_DQ_DLL_ENABLE		= 1 << 3,
+	LEFT_CHN_TX_DQ_DLL_DELAY_MASK		= 7,
+	LEFT_CHN_TX_DQ_DLL_DELAY_SHIFT		= 0,
+
+	/* DDRPHY_REG8 */
+	LEFT_CHN_RX_DQS_DELAY_TAP_MASK		= 3,
+	LEFT_CHN_RX_DQS_DELAY_TAP_SHIFT		= 0,
+
+	/* DDRPHY_REG9 */
+	RIGHT_CHN_TX_DQ_PHASE_BYPASS_90		= 1 << 4,
+	RIGHT_CHN_TX_DQ_PHASE_BYPASS_0		= 0 << 4,
+	RIGHT_CHN_TX_DQ_DLL_ENABLE		= 1 << 3,
+	RIGHT_CHN_TX_DQ_DLL_DELAY_MASK		= 7,
+	RIGHT_CHN_TX_DQ_DLL_DELAY_SHIFT		= 0,
+
+	/* DDRPHY_REG11 */
+	RIGHT_CHN_RX_DQS_DELAY_TAP_MASK		= 3,
+	RIGHT_CHN_RX_DQS_DELAY_TAP_SHIFT	= 0,
+
+	/* DDRPHY_REG62 */
+	CAL_DONE_MASK				= 3,
+	HIGH_8BIT_CAL_DONE			= 1 << 1,
+	LOW_8BIT_CAL_DONE			= 1 << 0,
+};
+
+/* PTCL */
+enum {
+	/* PCTL_DFISTCFG0 */
+	DFI_INIT_START			= 1 << 0,
+	DFI_DATA_BYTE_DISABLE_EN	= 1 << 2,
+
+	/* PCTL_DFISTCFG1 */
+	DFI_DRAM_CLK_SR_EN		= 1 << 0,
+	DFI_DRAM_CLK_DPD_EN		= 1 << 1,
+
+	/* PCTL_DFISTCFG2 */
+	DFI_PARITY_INTR_EN		= 1 << 0,
+	DFI_PARITY_EN			= 1 << 1,
+
+	/* PCTL_DFILPCFG0 */
+	TLP_RESP_TIME_SHIFT		= 16,
+	LP_SR_EN			= 1 << 8,
+	LP_PD_EN			= 1 << 0,
+
+	/* PCTL_DFIODTCFG */
+	RANK0_ODT_WRITE_SEL		= 1 << 3,
+	RANK1_ODT_WRITE_SEL		= 1 << 11,
+
+	/* PCTL_DFIODTCFG1 */
+	ODT_LEN_BL8_W_SHIFT		= 16,
+
+	/* PCTL_MCFG */
+	TFAW_CFG_MASK			= 3,
+	TFAW_CFG_SHIFT			= 18,
+	PD_EXIT_SLOW_MODE		= 0 << 17,
+	PD_ACTIVE_POWER_DOWN		= 1 << 16,
+	PD_IDLE_MASK			= 0xff,
+	PD_IDLE_SHIFT			= 8,
+	MEM_BL4				= 0 << 0,
+	MEM_BL8				= 1 << 0,
+
+	/* PCTL_MCFG1 */
+	HW_EXIT_IDLE_EN_MASK		= 1,
+	HW_EXIT_IDLE_EN_SHIFT		= 31,
+	SR_IDLE_MASK			= 0x1ff,
+	SR_IDLE_SHIFT			= 0,
+
+	/* PCTL_SCFG */
+	HW_LOW_POWER_EN			= 1 << 0,
+
+	/* PCTL_POWCTL */
+	POWER_UP_START			= 1 << 0,
+
+	/* PCTL_POWSTAT */
+	POWER_UP_DONE			= 1 << 0,
+
+	/* PCTL_MCMD */
+	START_CMD			= 1 << 31,
+	BANK_ADDR_MASK			= 7,
+	BANK_ADDR_SHIFT			= 17,
+	CMD_ADDR_MASK			= 0x1fff,
+	CMD_ADDR_SHIFT			= 4,
+	DESELECT_CMD			= 0,
+	PREA_CMD,
+	REF_CMD,
+	MRS_CMD,
+	ZQCS_CMD,
+	ZQCL_CMD,
+	RSTL_CMD,
+	MRR_CMD				= 8,
+
+	/* PCTL_STAT */
+	INIT_MEM			= 0,
+	CONFIG,
+	CONFIG_REQ,
+	ACCESS,
+	ACCESS_REQ,
+	LOW_POWER,
+	LOW_POWER_ENTRY_REQ,
+	LOW_POWER_EXIT_REQ,
+	PCTL_STAT_MASK			= 7,
+
+	/* PCTL_SCTL */
+	INIT_STATE			= 0,
+	CFG_STATE			= 1,
+	GO_STATE			= 2,
+	SLEEP_STATE			= 3,
+	WAKEUP_STATE			= 4,
+};
+
+/* GRF_SOC_CON2 */
+#define	MSCH4_MAINDDR3		(1 << 7)
+#define PHY_DRV_ODT_SET(n)	((n << 4) | n)
+#define DDR3_DLL_RESET		(1 << 8)
+
+/* CK pull up/down driver strength control */
+enum {
+	PHY_RON_DISABLE		= 0,
+	PHY_RON_309OHM		= 1,
+	PHY_RON_155OHM,
+	PHY_RON_103OHM		= 3,
+	PHY_RON_63OHM		= 5,
+	PHY_RON_45OHM		= 7,
+	PHY_RON_77OHM,
+	PHY_RON_62OHM,
+	PHY_RON_52OHM,
+	PHY_RON_44OHM,
+	PHY_RON_39OHM,
+	PHY_RON_34OHM,
+	PHY_RON_31OHM,
+	PHY_RON_28OHM,
+};
+
+/* DQ pull up/down control */
+enum {
+	PHY_RTT_DISABLE		= 0,
+	PHY_RTT_861OHM		= 1,
+	PHY_RTT_431OHM,
+	PHY_RTT_287OHM,
+	PHY_RTT_216OHM,
+	PHY_RTT_172OHM,
+	PHY_RTT_145OHM,
+	PHY_RTT_124OHM,
+	PHY_RTT_215OHM,
+	PHY_RTT_144OHM		= 0xa,
+	PHY_RTT_123OHM,
+	PHY_RTT_108OHM,
+	PHY_RTT_96OHM,
+	PHY_RTT_86OHM,
+	PHY_RTT_78OHM,
+};
+
+/* DQS squelch DLL delay */
+enum {
+	DQS_DLL_NO_DELAY	= 0,
+	DQS_DLL_22P5_DELAY,
+	DQS_DLL_45_DELAY,
+	DQS_DLL_67P5_DELAY,
+	DQS_DLL_90_DELAY,
+	DQS_DLL_112P5_DELAY,
+	DQS_DLL_135_DELAY,
+	DQS_DLL_157P5_DELAY,
+};
+
+/* GRF_OS_REG1 */
+enum {
+	/*
+	 * 000: lpddr
+	 * 001: ddr
+	 * 010: ddr2
+	 * 011: ddr3
+	 * 100: lpddr2-s2
+	 * 101: lpddr2-s4
+	 * 110: lpddr3
+	 */
+	DDR_TYPE_MASK		= 7,
+	DDR_TYPE_SHIFT		= 13,
+
+	/* 0: 1 chn, 1: 2 chn */
+	DDR_CHN_CNT_SHIFT	= 12,
+
+	/* 0: 1 rank, 1: 2 rank */
+	DDR_RANK_CNT_MASK	= 1,
+	DDR_RANK_CNT_SHIFT	= 11,
+
+	/*
+	 * 00: 9col
+	 * 01: 10col
+	 * 10: 11col
+	 * 11: 12col
+	 */
+	DDR_COL_MASK		= 3,
+	DDR_COL_SHIFT		= 9,
+
+	/* 0: 8 bank, 1: 4 bank*/
+	DDR_BANK_MASK		= 1,
+	DDR_BANK_SHIFT		= 8,
+
+	/*
+	 * 00: 13 row
+	 * 01: 14 row
+	 * 10: 15 row
+	 * 11: 16 row
+	 */
+	DDR_CS0_ROW_MASK	= 3,
+	DDR_CS0_ROW_SHIFT	= 6,
+	DDR_CS1_ROW_MASK	= 3,
+	DDR_CS1_ROW_SHIFT	= 4,
+
+	/*
+	 * 00: 32 bit
+	 * 01: 16 bit
+	 * 10: 8 bit
+	 * rk3036 only support 16bit
+	 */
+	DDR_BW_MASK		= 3,
+	DDR_BW_SHIFT		= 2,
+	DDR_DIE_BW_MASK		= 3,
+	DDR_DIE_BW_SHIFT	= 0,
+};
+
+static void rkdclk_init(struct rk3036_sdram_priv priv)
+{
+	struct rk3036_pll *pll = &priv.cru->pll[1];
+
+	/* pll enter slow-mode */
+	rk_clrsetreg(&priv.cru->cru_mode_con,
+		     DPLL_MODE_MASK << DPLL_MODE_SHIFT,
+		     DPLL_MODE_SLOW << DPLL_MODE_SHIFT);
+
+	/* use interger mode */
+	rk_clrreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
+
+	rk_clrsetreg(&pll->con0,
+		     PLL_POSTDIV1_MASK << PLL_POSTDIV1_SHIFT | PLL_FBDIV_MASK,
+		     (dpll_init_cfg.postdiv1 << PLL_POSTDIV1_SHIFT) |
+			dpll_init_cfg.fbdiv);
+	rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT |
+			PLL_REFDIV_MASK << PLL_REFDIV_SHIFT,
+			(dpll_init_cfg.postdiv2 << PLL_POSTDIV2_SHIFT |
+			 dpll_init_cfg.refdiv << PLL_REFDIV_SHIFT));
+
+	/* waiting for pll lock */
+	while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT))
+		rockchip_udelay(1);
+
+	/* PLL enter normal-mode */
+	rk_clrsetreg(&priv.cru->cru_mode_con,
+		     DPLL_MODE_MASK << DPLL_MODE_SHIFT,
+		     DPLL_MODE_NORM << DPLL_MODE_SHIFT);
+}
+
+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++;
+	}
+}
+
+void phy_pctrl_reset(struct rk3036_sdram_priv priv)
+{
+	struct rk3036_ddr_phy *ddr_phy = priv.phy;
+
+	rk_clrsetreg(&priv.cru->cru_softrst_con[5], 1 << DDRCTRL_PSRST_SHIFT |
+		1 << DDRCTRL_SRST_SHIFT | 1 << DDRPHY_PSRST_SHIFT |
+		1 << DDRPHY_SRST_SHIFT,
+		1 << DDRCTRL_PSRST_SHIFT | 1 << DDRCTRL_SRST_SHIFT |
+		1 << DDRPHY_PSRST_SHIFT | 1 << DDRPHY_SRST_SHIFT);
+	rockchip_udelay(10);
+
+	rk_clrsetreg(&priv.cru->cru_softrst_con[5], 1 << DDRCTRL_PSRST_SHIFT |
+		1 << DDRCTRL_SRST_SHIFT | 1 << DDRPHY_PSRST_SHIFT |
+		1 << DDRPHY_SRST_SHIFT,
+		1 << DDRCTRL_PSRST_SHIFT | 1 << DDRCTRL_SRST_SHIFT |
+		0 << DDRPHY_PSRST_SHIFT | 0 << DDRPHY_SRST_SHIFT);
+	rockchip_udelay(10);
+
+	rk_clrsetreg(&priv.cru->cru_softrst_con[5], 1 << DDRCTRL_PSRST_SHIFT |
+		1 << DDRCTRL_SRST_SHIFT | 1 << DDRPHY_PSRST_SHIFT |
+		1 << DDRPHY_SRST_SHIFT,
+		0 << DDRCTRL_PSRST_SHIFT | 0 << DDRCTRL_SRST_SHIFT |
+		0 << DDRPHY_PSRST_SHIFT | 0 << DDRPHY_SRST_SHIFT);
+	rockchip_udelay(10);
+
+	clrsetbits_le32(&ddr_phy->ddrphy_reg1,
+			SOFT_RESET_MASK << SOFT_RESET_SHIFT,
+			0 << SOFT_RESET_SHIFT);
+	rockchip_udelay(10);
+	clrsetbits_le32(&ddr_phy->ddrphy_reg1,
+			SOFT_RESET_MASK << SOFT_RESET_SHIFT,
+			3 << SOFT_RESET_SHIFT);
+
+	rockchip_udelay(1);
+}
+
+void phy_dll_bypass_set(struct rk3036_sdram_priv priv, unsigned int freq)
+{
+	struct rk3036_ddr_phy *ddr_phy = priv.phy;
+
+	if (freq < ddr_timing.freq) {
+		writel(CMD_DLL_BYPASS | HIGH_8BIT_DLL_BYPASS |
+			LOW_8BIT_DLL_BYPASS, &ddr_phy->ddrphy_reg2a);
+
+		writel(LEFT_CHN_TX_DQ_PHASE_BYPASS_90 |
+			LEFT_CHN_TX_DQ_DLL_ENABLE |
+			(0 & LEFT_CHN_TX_DQ_DLL_DELAY_MASK) <<
+			 LEFT_CHN_TX_DQ_DLL_DELAY_SHIFT, &ddr_phy->ddrphy_reg6);
+
+		writel(RIGHT_CHN_TX_DQ_PHASE_BYPASS_90 |
+			RIGHT_CHN_TX_DQ_DLL_ENABLE |
+			(0 & RIGHT_CHN_TX_DQ_DLL_DELAY_MASK) <<
+			 RIGHT_CHN_TX_DQ_DLL_DELAY_SHIFT,
+			&ddr_phy->ddrphy_reg9);
+	} else {
+		writel(CMD_DLL_BYPASS_DISABLE | HIGH_8BIT_DLL_BYPASS_DISABLE |
+			LOW_8BIT_DLL_BYPASS_DISABLE, &ddr_phy->ddrphy_reg2a);
+
+		writel(LEFT_CHN_TX_DQ_PHASE_BYPASS_0 |
+			LEFT_CHN_TX_DQ_DLL_ENABLE |
+			(4 & LEFT_CHN_TX_DQ_DLL_DELAY_MASK) <<
+			 LEFT_CHN_TX_DQ_DLL_DELAY_SHIFT,
+			&ddr_phy->ddrphy_reg6);
+
+		writel(RIGHT_CHN_TX_DQ_PHASE_BYPASS_0 |
+			RIGHT_CHN_TX_DQ_DLL_ENABLE |
+			(4 & RIGHT_CHN_TX_DQ_DLL_DELAY_MASK) <<
+			 RIGHT_CHN_TX_DQ_DLL_DELAY_SHIFT,
+			&ddr_phy->ddrphy_reg9);
+	}
+
+	writel(CMD_SLAVE_DLL_NO_INVERSE_MODE | CMD_SLAVE_DLL_ENALBE |
+			(0 & CMD_TX_SLAVE_DLL_DELAY_MASK) <<
+			CMD_TX_SLAVE_DLL_DELAY_SHIFT, &ddr_phy->ddrphy_reg19);
+
+	/* 45 degree delay */
+	writel((DQS_DLL_45_DELAY & LEFT_CHN_RX_DQS_DELAY_TAP_MASK) <<
+		LEFT_CHN_RX_DQS_DELAY_TAP_SHIFT, &ddr_phy->ddrphy_reg8);
+	writel((DQS_DLL_45_DELAY & RIGHT_CHN_RX_DQS_DELAY_TAP_MASK) <<
+		RIGHT_CHN_RX_DQS_DELAY_TAP_SHIFT, &ddr_phy->ddrphy_reg11);
+}
+
+static void send_command(struct rk3036_ddr_pctl *pctl,
+			 u32 rank, u32 cmd, u32 arg)
+{
+	writel((START_CMD | (rank << 20) | arg | cmd), &pctl->mcmd);
+	rockchip_udelay(1);
+	while (readl(&pctl->mcmd) & START_CMD)
+		;
+}
+
+static void memory_init(struct rk3036_sdram_priv priv)
+{
+	struct rk3036_ddr_pctl *pctl = priv.pctl;
+
+	send_command(pctl, 3, DESELECT_CMD, 0);
+	rockchip_udelay(1);
+	send_command(pctl, 3, PREA_CMD, 0);
+	send_command(pctl, 3, MRS_CMD,
+		     (0x02 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT |
+		     (ddr_timing.phy_timing.mr[2] & CMD_ADDR_MASK) <<
+		     CMD_ADDR_SHIFT);
+
+	send_command(pctl, 3, MRS_CMD,
+		     (0x03 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT |
+		     (ddr_timing.phy_timing.mr[3] & CMD_ADDR_MASK) <<
+		     CMD_ADDR_SHIFT);
+
+	send_command(pctl, 3, MRS_CMD,
+		     (0x01 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT |
+		     (ddr_timing.phy_timing.mr[1] & CMD_ADDR_MASK) <<
+		     CMD_ADDR_SHIFT);
+
+	send_command(pctl, 3, MRS_CMD,
+		     (0x00 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT |
+		     (ddr_timing.phy_timing.mr[0] & CMD_ADDR_MASK) <<
+		     CMD_ADDR_SHIFT | DDR3_DLL_RESET);
+
+	send_command(pctl, 3, ZQCL_CMD, 0);
+}
+
+static void data_training(struct rk3036_sdram_priv priv)
+{
+	struct rk3036_ddr_phy *ddr_phy = priv.phy;
+	struct rk3036_ddr_pctl *pctl = priv.pctl;
+	u32 value;
+
+	/* disable auto refresh */
+	value = readl(&pctl->trefi),
+	writel(0, &pctl->trefi);
+
+	clrsetbits_le32(&ddr_phy->ddrphy_reg2, 0x03,
+			DQS_SQU_CAL_NORMAL_MODE | DQS_SQU_CAL_START);
+
+	rockchip_udelay(1);
+	while ((readl(&ddr_phy->ddrphy_reg62) & CAL_DONE_MASK) !=
+		(HIGH_8BIT_CAL_DONE | LOW_8BIT_CAL_DONE)) {
+		;
+	}
+
+	clrsetbits_le32(&ddr_phy->ddrphy_reg2, 0x03,
+			DQS_SQU_CAL_NORMAL_MODE | DQS_SQU_NO_CAL);
+
+	/*
+	 * since data training will take about 20us, so send some auto
+	 * refresh(about 7.8us) to complement the lost time
+	 */
+	send_command(pctl, 3, REF_CMD, 0);
+	send_command(pctl, 3, REF_CMD, 0);
+	send_command(pctl, 3, REF_CMD, 0);
+
+	writel(value, &pctl->trefi);
+}
+
+static void move_to_config_state(struct rk3036_sdram_priv priv)
+{
+	unsigned int state;
+	struct rk3036_ddr_pctl *pctl = priv.pctl;
+
+	while (1) {
+		state = readl(&pctl->stat) & PCTL_STAT_MASK;
+		switch (state) {
+		case LOW_POWER:
+			writel(WAKEUP_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MASK)
+				!= ACCESS)
+				;
+			/* if at low power state, need wakeup first, and then
+			 * enter the config, so fallthrough
+			 */
+		case ACCESS:
+			/* fallthrough */
+		case INIT_MEM:
+			writel(CFG_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MASK) != CONFIG)
+				;
+			break;
+		case CONFIG:
+			return;
+		default:
+			break;
+		}
+	}
+}
+
+static void move_to_access_state(struct rk3036_sdram_priv priv)
+{
+	unsigned int state;
+	struct rk3036_ddr_pctl *pctl = priv.pctl;
+
+	while (1) {
+		state = readl(&pctl->stat) & PCTL_STAT_MASK;
+		switch (state) {
+		case LOW_POWER:
+			writel(WAKEUP_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MASK) != ACCESS)
+				;
+			break;
+		case INIT_MEM:
+			writel(CFG_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MASK) != CONFIG)
+				;
+			/* fallthrough */
+		case CONFIG:
+			writel(GO_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MASK) != ACCESS)
+				;
+			break;
+		case ACCESS:
+			return;
+		default:
+			break;
+		}
+	}
+}
+
+static void pctl_cfg(struct rk3036_sdram_priv priv)
+{
+	struct rk3036_ddr_pctl *pctl = priv.pctl;
+	u32 burst_len;
+	u32 reg;
+
+	writel(DFI_INIT_START | DFI_DATA_BYTE_DISABLE_EN, &pctl->dfistcfg0);
+	writel(DFI_DRAM_CLK_SR_EN | DFI_DRAM_CLK_DPD_EN, &pctl->dfistcfg1);
+	writel(DFI_PARITY_INTR_EN | DFI_PARITY_EN, &pctl->dfistcfg2);
+	writel(7 << TLP_RESP_TIME_SHIFT | LP_SR_EN | LP_PD_EN,
+	       &pctl->dfilpcfg0);
+
+	writel(1, &pctl->dfitphyupdtype0);
+	writel(0x0d, &pctl->dfitphyrdlat);
+
+	/* cs0 and cs1 write odt enable */
+	writel((RANK0_ODT_WRITE_SEL | RANK1_ODT_WRITE_SEL),
+	       &pctl->dfiodtcfg);
+
+	/* odt write length */
+	writel(7 << ODT_LEN_BL8_W_SHIFT, &pctl->dfiodtcfg1);
+
+	/* phyupd and ctrlupd disabled */
+	writel(0, &pctl->dfiupdcfg);
+
+	if ((ddr_timing.noc_timing.burstlen << 1) == 4)
+		burst_len = MEM_BL4;
+	else
+		burst_len = MEM_BL8;
+
+	copy_to_reg(&pctl->togcnt1u, &ddr_timing.pctl_timing.togcnt1u,
+		    sizeof(struct rk3036_pctl_timing));
+	reg = readl(&pctl->tcl);
+	writel(reg - 3, &pctl->dfitrddataen);
+	reg = readl(&pctl->tcwl);
+	writel(reg - 1, &pctl->dfitphywrlat);
+
+	writel(burst_len | (1 & TFAW_CFG_MASK) << TFAW_CFG_SHIFT |
+			PD_EXIT_SLOW_MODE | PD_ACTIVE_POWER_DOWN |
+			(0 & PD_IDLE_MASK) << PD_IDLE_SHIFT,
+			&pctl->mcfg);
+
+	writel(RK_SETBITS(MSCH4_MAINDDR3), &priv.grf->soc_con2);
+	setbits_le32(&pctl->scfg, HW_LOW_POWER_EN);
+}
+
+static void phy_cfg(struct rk3036_sdram_priv priv)
+{
+	struct rk3036_ddr_phy *ddr_phy = priv.phy;
+	struct rk3036_service_sys *axi_bus = priv.axi_bus;
+
+	writel(ddr_timing.noc_timing.noc_timing, &axi_bus->ddrtiming);
+	writel(0x3f, &axi_bus->readlatency);
+
+	writel(MEMORY_SELECT_DDR3 | DQS_SQU_CAL_NORMAL_MODE,
+	       &ddr_phy->ddrphy_reg2);
+
+	clrsetbits_le32(&ddr_phy->ddrphy_reg3, 1, ddr_timing.phy_timing.bl);
+	writel(ddr_timing.phy_timing.cl_al, &ddr_phy->ddrphy_reg4a);
+	writel(PHY_DRV_ODT_SET(PHY_RON_44OHM), &ddr_phy->ddrphy_reg16);
+	writel(PHY_DRV_ODT_SET(PHY_RON_44OHM), &ddr_phy->ddrphy_reg22);
+	writel(PHY_DRV_ODT_SET(PHY_RON_44OHM), &ddr_phy->ddrphy_reg25);
+	writel(PHY_DRV_ODT_SET(PHY_RON_44OHM), &ddr_phy->ddrphy_reg26);
+	writel(PHY_DRV_ODT_SET(PHY_RTT_216OHM), &ddr_phy->ddrphy_reg27);
+	writel(PHY_DRV_ODT_SET(PHY_RTT_216OHM), &ddr_phy->ddrphy_reg28);
+}
+
+void dram_cfg_rbc(struct rk3036_sdram_priv priv)
+{
+	char noc_config;
+	int i = 0;
+	struct rk3036_ddr_config config = priv.ddr_config;
+	struct rk3036_service_sys *axi_bus = priv.axi_bus;
+
+	move_to_config_state(priv);
+
+	/* 2bit in BIT1, 2 */
+	if (config.rank == 2) {
+		noc_config = (config.cs0_row - 13) << 4 | config.bank << 1 |
+			      1 << 3 | (config.col - 10);
+		if (noc_config == ddr_cfg_2_rbc[9]) {
+			i = 9;
+			goto finish;
+		} else if (noc_config == ddr_cfg_2_rbc[10]) {
+			i = 10;
+			goto finish;
+		}
+	}
+
+	noc_config = (config.cs0_row - 13) << 4 | config.bank << 1 |
+			(config.col - 10);
+
+	for (i = 0; i < sizeof(ddr_cfg_2_rbc); i++) {
+		if (noc_config == ddr_cfg_2_rbc[i])
+			goto finish;
+	}
+
+	/* bank: 1 bit in BIT6,7, 1bit in BIT1, 2 */
+	noc_config = 1 << 6 | (config.cs0_row - 13) << 4 |
+			2 << 1 | (config.col - 10);
+	if (noc_config == ddr_cfg_2_rbc[11]) {
+		i = 11;
+		goto finish;
+	}
+
+	/* bank: 2bit in BIT6,7 */
+	noc_config = (config.bank << 6) | (config.cs0_row - 13) << 4 |
+			(config.col - 10);
+
+	if (noc_config == ddr_cfg_2_rbc[0])
+		i = 0;
+	else if (noc_config == ddr_cfg_2_rbc[12])
+		i = 12;
+	else if (noc_config == ddr_cfg_2_rbc[13])
+		i = 13;
+finish:
+	writel(i, &axi_bus->ddrconf);
+	move_to_access_state(priv);
+
+	return;
+}
+
+static void sdram_all_config(struct rk3036_sdram_priv priv)
+{
+	u32 os_reg = 0;
+	struct rk3036_ddr_config config = priv.ddr_config;
+
+	os_reg = config.ddr_type << DDR_TYPE_SHIFT |
+			0 << DDR_CHN_CNT_SHIFT |
+			(config.rank - 1) << DDR_RANK_CNT_SHIFT |
+			(config.col - 1) << DDR_COL_SHIFT |
+			(config.bank == 3 ? 0 : 1) << DDR_BANK_SHIFT |
+			(config.cs0_row - 13) << DDR_CS0_ROW_SHIFT |
+			(config.cs1_row - 13) << DDR_CS1_ROW_SHIFT |
+			1 << DDR_BW_SHIFT | config.bw << DDR_DIE_BW_SHIFT;
+	writel(os_reg, &priv.grf->os_reg[1]);
+}
+
+size_t sdram_size(void)
+{
+	u32 size, os_reg, cs0_row, cs1_row, col, bank, rank;
+	struct rk3036_grf *grf = (void *)GRF_BASE;
+
+	os_reg = readl(&grf->os_reg[1]);
+
+	cs0_row = 13 + ((os_reg >> DDR_CS0_ROW_SHIFT) & DDR_CS0_ROW_MASK);
+	cs1_row = 13 + ((os_reg >> DDR_CS1_ROW_SHIFT) & DDR_CS1_ROW_MASK);
+	col = 9 + ((os_reg >> DDR_COL_SHIFT) & DDR_COL_MASK);
+	bank = 3 - ((os_reg >> DDR_BANK_SHIFT) & DDR_BANK_MASK);
+	rank = 1 + ((os_reg >> DDR_RANK_CNT_SHIFT) & DDR_RANK_CNT_MASK);
+
+	/* row + col + bank + bw(rk3036 only support 16bit, so fix in 1) */
+	size = 1 << (cs0_row + col + bank + 1);
+
+	if (rank > 1)
+		size += size >> (cs0_row - cs1_row);
+
+	return size;
+}
+
+void sdram_init(void)
+{
+	struct rk3036_sdram_priv sdram_priv;
+
+	sdram_priv.cru = (void *)CRU_BASE;
+	sdram_priv.grf = (void *)GRF_BASE;
+	sdram_priv.phy = (void *)DDR_PHY_BASE;
+	sdram_priv.pctl = (void *)DDR_PCTL_BASE;
+	sdram_priv.axi_bus = (void *)CPU_AXI_BUS_BASE;
+
+	get_ddr_config(&sdram_priv.ddr_config);
+	sdram_all_config(sdram_priv);
+	rkdclk_init(sdram_priv);
+	phy_pctrl_reset(sdram_priv);
+	phy_dll_bypass_set(sdram_priv, ddr_timing.freq);
+	pctl_cfg(sdram_priv);
+	phy_cfg(sdram_priv);
+	writel(POWER_UP_START, &sdram_priv.pctl->powctl);
+	while (!(readl(&sdram_priv.pctl->powstat) & POWER_UP_DONE))
+		;
+	memory_init(sdram_priv);
+	move_to_config_state(sdram_priv);
+	data_training(sdram_priv);
+	move_to_access_state(sdram_priv);
+	dram_cfg_rbc(sdram_priv);
+}
