diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index fca23b4e64..d6d9558c69 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1115,6 +1115,9 @@ config ARCH_ROCKCHIP
 	imply FAT_WRITE
 	imply USB_FUNCTION_FASTBOOT
 	imply SPL_SYSRESET
+	imply TPL_SYSRESET
+	imply ADC
+	imply SARADC_ROCKCHIP
 
 config TARGET_THUNDERX_88XX
 	bool "Support ThunderX 88xx"
diff --git a/arch/arm/dts/rk3229-evb.dts b/arch/arm/dts/rk3229-evb.dts
index 64f1c2d7da..ae0b0a4b8e 100644
--- a/arch/arm/dts/rk3229-evb.dts
+++ b/arch/arm/dts/rk3229-evb.dts
@@ -40,7 +40,6 @@
 };
 
 &dmc {
-	rockchip,sdram-channel = /bits/ 8 <1 10 3 2 1 0 15 15>;
 	rockchip,pctl-timing = <0x96 0xC8 0x1F3 0xF 0x8000004D 0x4 0x4E 0x6 0x3
 		0x0 0x6 0x5 0xC 0x10 0x6 0x4 0x4
 		0x5 0x4 0x200 0x3 0xA 0x40 0x0 0x1
diff --git a/arch/arm/dts/rk3288-popmetal.dtsi b/arch/arm/dts/rk3288-popmetal.dtsi
index dd6ce8b69e..63785eb55e 100644
--- a/arch/arm/dts/rk3288-popmetal.dtsi
+++ b/arch/arm/dts/rk3288-popmetal.dtsi
@@ -491,6 +491,10 @@
 	};
 };
 
+&saradc {
+	status = "okay";
+};
+
 &tsadc {
 	rockchip,hw-tshut-mode = <0>;
 	rockchip,hw-tshut-polarity = <0>;
diff --git a/arch/arm/dts/rk3328-evb.dts b/arch/arm/dts/rk3328-evb.dts
index 8a14c653e4..3dd9d81961 100644
--- a/arch/arm/dts/rk3328-evb.dts
+++ b/arch/arm/dts/rk3328-evb.dts
@@ -42,6 +42,10 @@
 	};
 };
 
+&saradc {
+	status = "okay";
+};
+
 &uart2 {
 	status = "okay";
 };
@@ -87,3 +91,121 @@
 	vbus-supply = <&vcc5v0_host_xhci>;
 	status = "okay";
 };
+
+&i2c1 {
+	clock-frequency = <400000>;
+	i2c-scl-rising-time-ns = <168>;
+	i2c-scl-falling-time-ns = <4>;
+	status = "okay";
+
+	rk805: pmic@18 {
+		compatible = "rockchip,rk805";
+		status = "okay";
+		reg = <0x18>;
+		interrupt-parent = <&gpio2>;
+		interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pmic_int_l>;
+		rockchip,system-power-controller;
+		wakeup-source;
+		gpio-controller;
+		#gpio-cells = <2>;
+		#clock-cells = <1>;
+		clock-output-names = "xin32k", "rk805-clkout2";
+
+		regulators {
+			vdd_logic: DCDC_REG1 {
+				regulator-name = "vdd_logic";
+				regulator-min-microvolt = <712500>;
+				regulator-max-microvolt = <1450000>;
+				regulator-ramp-delay = <6001>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1000000>;
+				};
+			};
+
+			vdd_arm: DCDC_REG2 {
+				regulator-name = "vdd_arm";
+				regulator-min-microvolt = <712500>;
+				regulator-max-microvolt = <1450000>;
+				regulator-ramp-delay = <6001>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1000000>;
+				};
+			};
+
+			vcc_ddr: DCDC_REG3 {
+				regulator-name = "vcc_ddr";
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-state-mem {
+					regulator-on-in-suspend;
+				};
+			};
+
+			vcc_io: DCDC_REG4 {
+				regulator-name = "vcc_io";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <3300000>;
+				};
+			};
+
+			vdd_18: LDO_REG1 {
+				regulator-name = "vdd_18";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1800000>;
+				};
+			};
+
+			vcc_18emmc: LDO_REG2 {
+				regulator-name = "vcc_18emmc";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1800000>;
+				};
+			};
+
+			vdd_10: LDO_REG3 {
+				regulator-name = "vdd_10";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1000000>;
+				};
+			};
+		};
+	};
+};
+
+&pinctrl {
+	pmic {
+		pmic_int_l: pmic-int-l {
+		rockchip,pins =
+			<2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>;	/* gpio2_a6 */
+		};
+	};
+};
+
diff --git a/arch/arm/dts/rk3368-px5-evb.dts b/arch/arm/dts/rk3368-px5-evb.dts
index c7478f7ddb..e9c5ebad47 100644
--- a/arch/arm/dts/rk3368-px5-evb.dts
+++ b/arch/arm/dts/rk3368-px5-evb.dts
@@ -296,6 +296,10 @@
 	};
 };
 
+&saradc {
+	status = "okay";
+};
+
 &tsadc {
 	status = "okay";
 	rockchip,hw-tshut-mode = <0>; /* CRU */
diff --git a/arch/arm/dts/rk3368-sheep.dts b/arch/arm/dts/rk3368-sheep.dts
index 7c190f7456..27befadd67 100644
--- a/arch/arm/dts/rk3368-sheep.dts
+++ b/arch/arm/dts/rk3368-sheep.dts
@@ -260,6 +260,10 @@
 	};
 };
 
+&saradc {
+	status = "okay";
+};
+
 &tsadc {
 	status = "okay";
 	rockchip,hw-tshut-mode = <0>; /* CRU */
diff --git a/arch/arm/dts/rk3399-evb.dts b/arch/arm/dts/rk3399-evb.dts
index be0c6d98bd..0e5d8d79a1 100644
--- a/arch/arm/dts/rk3399-evb.dts
+++ b/arch/arm/dts/rk3399-evb.dts
@@ -149,6 +149,10 @@
 	status = "okay";
 };
 
+&saradc {
+	status = "okay";
+};
+
 &sdmmc {
 	bus-width = <4>;
 	status = "okay";
diff --git a/arch/arm/dts/rk3399-puma.dtsi b/arch/arm/dts/rk3399-puma.dtsi
index a04878e223..65ab380139 100644
--- a/arch/arm/dts/rk3399-puma.dtsi
+++ b/arch/arm/dts/rk3399-puma.dtsi
@@ -20,7 +20,8 @@
 
 	chosen {
 		stdout-path = "serial0:115200n8";
-		u-boot,spl-boot-order = &spiflash, &sdhci, &sdmmc;
+		u-boot,spl-boot-order = \
+			"same-as-spl", &spiflash, &sdhci, &sdmmc;
 	};
 
 	aliases {
@@ -100,6 +101,24 @@
 		regulator-max-microvolt = <3300000>;
 	};
 
+	/*
+	 * The Qseven BIOS_DISABLE signal on the RK3399-Q7 keeps the on-module
+	 * eMMC and SPI flash powered-down initially (in fact it keeps the
+	 * reset signal asserted).  Even though it is an enable signal, we
+	 * model this as a regulator.
+	 */
+	bios_enable: bios_enable {
+		compatible = "regulator-fixed";
+		u-boot,dm-pre-reloc;
+		regulator-name = "bios_enable";
+		enable-active-low;
+		gpio = <&gpio3 29 GPIO_ACTIVE_HIGH>;
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
 	vccadc_ref: vccadc-ref {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc1v8_sys";
@@ -458,7 +477,7 @@
 };
 
 &pcie_phy {
-	        status = "okay";
+		status = "okay";
 };
 
 &pmu_io_domains {
@@ -485,7 +504,7 @@
 };
 
 &sdmmc {
-        u-boot,dm-pre-reloc;
+	u-boot,dm-pre-reloc;
 	clock-frequency = <150000000>;
 	clock-freq-min-max = <100000 150000000>;
 	supports-sd;
@@ -532,10 +551,15 @@
 	status = "okay";
 };
 
+&gpio3 {
+	u-boot,dm-pre-reloc;
+};
+
 &pinctrl {
 	/* Pins that are not explicitely used by any devices */
 	pinctrl-names = "default";
 	pinctrl-0 = <&puma_pin_hog>;
+
 	hog {
 		puma_pin_hog: puma_pin_hog {
 			rockchip,pins =
@@ -575,7 +599,7 @@
 	i2c8 {
 		i2c8_xfer_a: i2c8-xfer {
 			rockchip,pins = <1 21 RK_FUNC_1 &pcfg_pull_up>,
-			                <1 20 RK_FUNC_1 &pcfg_pull_up>;
+					<1 20 RK_FUNC_1 &pcfg_pull_up>;
 		};
 	};
 };
@@ -651,4 +675,3 @@
 &spi5 {
 	status = "okay";
 };
-
diff --git a/arch/arm/dts/rv1108-evb.dts b/arch/arm/dts/rv1108-evb.dts
index 2b221b6d61..8e857b2c89 100644
--- a/arch/arm/dts/rv1108-evb.dts
+++ b/arch/arm/dts/rv1108-evb.dts
@@ -39,6 +39,10 @@
 	snps,reset-gpio = <&gpio1 RK_PC1 GPIO_ACTIVE_LOW>;
 };
 
+&saradc {
+	status = "okay";
+};
+
 &sfc {
 	status = "okay";
 	flash@0 {
diff --git a/arch/arm/dts/rv1108.dtsi b/arch/arm/dts/rv1108.dtsi
index 3153dfe658..31b4d93b07 100644
--- a/arch/arm/dts/rv1108.dtsi
+++ b/arch/arm/dts/rv1108.dtsi
@@ -126,6 +126,17 @@
 		reg = <0x10300000 0x1000>;
 	};
 
+	saradc: saradc@1038c000 {
+		compatible = "rockchip,rv1108-saradc", "rockchip,rk3399-saradc";
+		reg = <0x1038c000 0x100>;
+		interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
+		#io-channel-cells = <1>;
+		clock-frequency = <1000000>;
+		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
+		clock-names = "saradc", "apb_pclk";
+		status = "disabled";
+	};
+
 	pmugrf: syscon@20060000 {
 		compatible = "rockchip,rv1108-pmugrf", "syscon";
 		reg = <0x20060000 0x1000>;
diff --git a/arch/arm/include/asm/arch-rockchip/bootrom.h b/arch/arm/include/asm/arch-rockchip/bootrom.h
index 92eb8783a3..169cc5e50b 100644
--- a/arch/arm/include/asm/arch-rockchip/bootrom.h
+++ b/arch/arm/include/asm/arch-rockchip/bootrom.h
@@ -24,4 +24,22 @@ void back_to_bootrom(void);
  */
 void _back_to_bootrom_s(void);
 
+/**
+ * Boot-device identifiers as used by the BROM
+ */
+enum {
+	BROM_BOOTSOURCE_NAND = 1,
+	BROM_BOOTSOURCE_EMMC = 2,
+	BROM_BOOTSOURCE_SPINOR = 3,
+	BROM_BOOTSOURCE_SPINAND = 4,
+	BROM_BOOTSOURCE_SD = 5,
+	BROM_BOOTSOURCE_USB = 10,
+	BROM_LAST_BOOTSOURCE = BROM_BOOTSOURCE_USB
+};
+
+/**
+ * Locations of the boot-device identifier in SRAM
+ */
+#define RK3399_BROM_BOOTSOURCE_ID_ADDR   0xff8c0010
+
 #endif
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3368.h b/arch/arm/include/asm/arch-rockchip/cru_rk3368.h
index 2b1197fd46..5f6a5fbe4c 100644
--- a/arch/arm/include/asm/arch-rockchip/cru_rk3368.h
+++ b/arch/arm/include/asm/arch-rockchip/cru_rk3368.h
@@ -89,6 +89,11 @@ enum {
 	MCU_CLK_DIV_SHIFT		= 0,
 	MCU_CLK_DIV_MASK		= GENMASK(4, 0),
 
+	/* CLKSEL_CON25 */
+	CLK_SARADC_DIV_CON_SHIFT	= 8,
+	CLK_SARADC_DIV_CON_MASK		= GENMASK(15, 8),
+	CLK_SARADC_DIV_CON_WIDTH	= 8,
+
 	/* CLKSEL43_CON */
 	GMAC_MUX_SEL_EXTCLK             = BIT(8),
 
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rv1108.h b/arch/arm/include/asm/arch-rockchip/cru_rv1108.h
index 2a1ae692be..ad2dc96467 100644
--- a/arch/arm/include/asm/arch-rockchip/cru_rv1108.h
+++ b/arch/arm/include/asm/arch-rockchip/cru_rv1108.h
@@ -90,6 +90,11 @@ enum {
 	CORE_CLK_DIV_SHIFT	= 0,
 	CORE_CLK_DIV_MASK	= 0x1f << CORE_CLK_DIV_SHIFT,
 
+	/* CLKSEL_CON22 */
+	CLK_SARADC_DIV_CON_SHIFT= 0,
+	CLK_SARADC_DIV_CON_MASK	= GENMASK(9, 0),
+	CLK_SARADC_DIV_CON_WIDTH= 10,
+
 	/* CLKSEL24_CON */
 	MAC_PLL_SEL_SHIFT	= 12,
 	MAC_PLL_SEL_MASK	= 1 << MAC_PLL_SEL_SHIFT,
diff --git a/arch/arm/include/asm/arch-rockchip/grf_rk3036.h b/arch/arm/include/asm/arch-rockchip/grf_rk3036.h
index 7625f249bd..d995b7db14 100644
--- a/arch/arm/include/asm/arch-rockchip/grf_rk3036.h
+++ b/arch/arm/include/asm/arch-rockchip/grf_rk3036.h
@@ -209,10 +209,10 @@ enum {
 	GPIO1A3_I2S_LRCKTX,
 
 	GPIO1A2_SHIFT		= 4,
-	GPIO1A2_MASK		= 6 << GPIO1A2_SHIFT,
+	GPIO1A2_MASK		= 3 << GPIO1A2_SHIFT,
 	GPIO1A2_GPIO		= 0,
 	GPIO1A2_I2S_LRCKRX,
-	GPIO1A2_I2S_PWM1_0,
+	GPIO1A2_PWM1_0,
 
 	GPIO1A1_SHIFT		= 2,
 	GPIO1A1_MASK		= 1 << GPIO1A1_SHIFT,
diff --git a/arch/arm/include/asm/arch-rockchip/sdram_rk322x.h b/arch/arm/include/asm/arch-rockchip/sdram_rk322x.h
new file mode 100644
index 0000000000..b40da409d4
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/sdram_rk322x.h
@@ -0,0 +1,581 @@
+/*
+ * (C) Copyright 2017 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+#ifndef _ASM_ARCH_SDRAM_RK322X_H
+#define _ASM_ARCH_SDRAM_RK322X_H
+
+#include <common.h>
+
+enum {
+	DDR3		= 3,
+	LPDDR2		= 5,
+	LPDDR3		= 6,
+	UNUSED		= 0xFF,
+};
+
+struct rk322x_sdram_channel {
+	/*
+	 * bit width in address, eg:
+	 * 8 banks using 3 bit to address,
+	 * 2 cs using 1 bit to address.
+	 */
+	u8 rank;
+	u8 col;
+	u8 bk;
+	u8 bw;
+	u8 dbw;
+	u8 row_3_4;
+	u8 cs0_row;
+	u8 cs1_row;
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	/*
+	 * For of-platdata, which would otherwise convert this into two
+	 * byte-swapped integers. With a size of 9 bytes, this struct will
+	 * appear in of-platdata as a byte array.
+	 *
+	 * If OF_PLATDATA enabled, need to add a dummy byte in dts.(i.e 0xff)
+	 */
+	u8 dummy;
+#endif
+};
+
+struct rk322x_ddr_pctl {
+	u32 scfg;
+	u32 sctl;
+	u32 stat;
+	u32 intrstat;
+	u32 reserved0[(0x40 - 0x10) / 4];
+	u32 mcmd;
+	u32 powctl;
+	u32 powstat;
+	u32 cmdtstat;
+	u32 cmdtstaten;
+	u32 reserved1[(0x60 - 0x54) / 4];
+	u32 mrrcfg0;
+	u32 mrrstat0;
+	u32 mrrstat1;
+	u32 reserved2[(0x7c - 0x6c) / 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[(0xc0 - 0xb4) / 4];
+
+	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 tref_mem_ddr3;
+	u32 reserved5[(0x180 - 0x14c) / 4];
+	u32 ecccfg;
+	u32 ecctst;
+	u32 eccclr;
+	u32 ecclog;
+	u32 reserved6[(0x200 - 0x190) / 4];
+	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;
+	/* dfi control registers */
+	u32 dfitctrldelay;
+	u32 dfiodtcfg;
+	u32 dfiodtcfg1;
+	u32 dfiodtrankmap;
+	/* dfi write data registers */
+	u32 dfitphywrdata;
+	u32 dfitphywrlat;
+	u32 reserved7[(0x260 - 0x258) / 4];
+	u32 dfitrddataen;
+	u32 dfitphyrdlat;
+	u32 reserved8[(0x270 - 0x268) / 4];
+	u32 dfitphyupdtype0;
+	u32 dfitphyupdtype1;
+	u32 dfitphyupdtype2;
+	u32 dfitphyupdtype3;
+	u32 dfitctrlupdmin;
+	u32 dfitctrlupdmax;
+	u32 dfitctrlupddly;
+	u32 reserved9;
+	u32 dfiupdcfg;
+	u32 dfitrefmski;
+	u32 dfitctrlupdi;
+	u32 reserved10[(0x2ac - 0x29c) / 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[(0x2f0 - 0x2e4) / 4];
+
+	u32 dfilpcfg0;
+	u32 reserved13[(0x300 - 0x2f4) / 4];
+	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[(0x3f8 - 0x340) / 4];
+	u32 ipvr;
+	u32 iptr;
+};
+check_member(rk322x_ddr_pctl, iptr, 0x03fc);
+
+struct rk322x_ddr_phy {
+	u32 ddrphy_reg[0x100];
+};
+
+struct rk322x_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;
+	u32 trefi_mem_ddr3;
+};
+
+struct rk322x_phy_timing {
+	u32 mr[4];
+	u32 mr11;
+	u32 bl;
+	u32 cl_al;
+};
+
+struct rk322x_msch_timings {
+	u32 ddrtiming;
+	u32 ddrmode;
+	u32 readlatency;
+	u32 activate;
+	u32 devtodev;
+};
+
+struct rk322x_service_sys {
+	u32 id_coreid;
+	u32 id_revisionid;
+	u32 ddrconf;
+	u32 ddrtiming;
+	u32 ddrmode;
+	u32 readlatency;
+	u32 activate;
+	u32 devtodev;
+};
+
+struct rk322x_base_params {
+	struct rk322x_msch_timings noc_timing;
+	u32 ddrconfig;
+	u32 ddr_freq;
+	u32 dramtype;
+	/*
+	 * unused for rk322x
+	 */
+	u32 stride;
+	u32 odt;
+};
+
+/* PCT_DFISTCFG0 */
+#define DFI_INIT_START			BIT(0)
+#define DFI_DATA_BYTE_DISABLE_EN	BIT(2)
+
+/* PCT_DFISTCFG1 */
+#define DFI_DRAM_CLK_SR_EN		BIT(0)
+#define DFI_DRAM_CLK_DPD_EN		BIT(1)
+
+/* PCT_DFISTCFG2 */
+#define DFI_PARITY_INTR_EN		BIT(0)
+#define DFI_PARITY_EN			BIT(1)
+
+/* PCT_DFILPCFG0 */
+#define TLP_RESP_TIME_SHIFT		16
+#define LP_SR_EN			BIT(8)
+#define LP_PD_EN			BIT(0)
+
+/* PCT_DFITCTRLDELAY */
+#define TCTRL_DELAY_TIME_SHIFT		0
+
+/* PCT_DFITPHYWRDATA */
+#define TPHY_WRDATA_TIME_SHIFT		0
+
+/* PCT_DFITPHYRDLAT */
+#define TPHY_RDLAT_TIME_SHIFT		0
+
+/* PCT_DFITDRAMCLKDIS */
+#define TDRAM_CLK_DIS_TIME_SHIFT	0
+
+/* PCT_DFITDRAMCLKEN */
+#define TDRAM_CLK_EN_TIME_SHIFT		0
+
+/* PCTL_DFIODTCFG */
+#define RANK0_ODT_WRITE_SEL		BIT(3)
+#define RANK1_ODT_WRITE_SEL		BIT(11)
+
+/* PCTL_DFIODTCFG1 */
+#define ODT_LEN_BL8_W_SHIFT		16
+
+/* PUBL_ACDLLCR */
+#define ACDLLCR_DLLDIS			BIT(31)
+#define ACDLLCR_DLLSRST			BIT(30)
+
+/* PUBL_DXDLLCR */
+#define DXDLLCR_DLLDIS			BIT(31)
+#define DXDLLCR_DLLSRST			BIT(30)
+
+/* PUBL_DLLGCR */
+#define DLLGCR_SBIAS			BIT(30)
+
+/* PUBL_DXGCR */
+#define DQSRTT				BIT(9)
+#define DQRTT				BIT(10)
+
+/* PIR */
+#define PIR_INIT			BIT(0)
+#define PIR_DLLSRST			BIT(1)
+#define PIR_DLLLOCK			BIT(2)
+#define PIR_ZCAL			BIT(3)
+#define PIR_ITMSRST			BIT(4)
+#define PIR_DRAMRST			BIT(5)
+#define PIR_DRAMINIT			BIT(6)
+#define PIR_QSTRN			BIT(7)
+#define PIR_RVTRN			BIT(8)
+#define PIR_ICPC			BIT(16)
+#define PIR_DLLBYP			BIT(17)
+#define PIR_CTLDINIT			BIT(18)
+#define PIR_CLRSR			BIT(28)
+#define PIR_LOCKBYP			BIT(29)
+#define PIR_ZCALBYP			BIT(30)
+#define PIR_INITBYP			BIT(31)
+
+/* PGCR */
+#define PGCR_DFTLMT_SHIFT		3
+#define PGCR_DFTCMP_SHIFT		2
+#define PGCR_DQSCFG_SHIFT		1
+#define PGCR_ITMDMD_SHIFT		0
+
+/* PGSR */
+#define PGSR_IDONE			BIT(0)
+#define PGSR_DLDONE			BIT(1)
+#define PGSR_ZCDONE			BIT(2)
+#define PGSR_DIDONE			BIT(3)
+#define PGSR_DTDONE			BIT(4)
+#define PGSR_DTERR			BIT(5)
+#define PGSR_DTIERR			BIT(6)
+#define PGSR_DFTERR			BIT(7)
+#define PGSR_RVERR			BIT(8)
+#define PGSR_RVEIRR			BIT(9)
+
+/* PTR0 */
+#define PRT_ITMSRST_SHIFT		18
+#define PRT_DLLLOCK_SHIFT		6
+#define PRT_DLLSRST_SHIFT		0
+
+/* PTR1 */
+#define PRT_DINIT0_SHIFT		0
+#define PRT_DINIT1_SHIFT		19
+
+/* PTR2 */
+#define PRT_DINIT2_SHIFT		0
+#define PRT_DINIT3_SHIFT		17
+
+/* DCR */
+#define DDRMD_LPDDR			0
+#define DDRMD_DDR			1
+#define DDRMD_DDR2			2
+#define DDRMD_DDR3			3
+#define DDRMD_LPDDR2_LPDDR3		4
+#define DDRMD_MASK			7
+#define DDRMD_SHIFT			0
+#define PDQ_MASK			7
+#define PDQ_SHIFT			4
+
+/* DXCCR */
+#define DQSNRES_MASK			0xf
+#define DQSNRES_SHIFT			8
+#define DQSRES_MASK			0xf
+#define DQSRES_SHIFT			4
+
+/* DTPR */
+#define TDQSCKMAX_SHIFT			27
+#define TDQSCKMAX_MASK			7
+#define TDQSCK_SHIFT			24
+#define TDQSCK_MASK			7
+
+/* DSGCR */
+#define DQSGX_SHIFT			5
+#define DQSGX_MASK			7
+#define DQSGE_SHIFT			8
+#define DQSGE_MASK			7
+
+/* 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_SHIFT			4
+#define LP_TRIG_MASK			7
+#define PCTL_STAT_MASK			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_SHIFT			0
+#define PU_OUTPUT_SHIFT			5
+#define PD_ONDIE_SHIFT			10
+#define PU_ONDIE_SHIFT			15
+#define ZDEN_SHIFT			28
+
+/* DDLGCR */
+#define SBIAS_BYPASS			BIT(23)
+
+/* MCFG */
+#define MDDR_LPDDR2_CLK_STOP_IDLE_SHIFT	24
+#define PD_IDLE_SHIFT			8
+#define MDDR_EN				(2 << 22)
+#define LPDDR2_EN			(3 << 22)
+#define LPDDR3_EN			(1 << 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_SHIFT			18
+#define PD_EXIT_SLOW			(0 << 17)
+#define PD_EXIT_FAST			(1 << 17)
+#define PD_TYPE_SHIFT			16
+#define BURSTLENGTH_SHIFT		20
+
+/* POWCTL */
+#define POWER_UP_START			BIT(0)
+
+/* POWSTAT */
+#define POWER_UP_DONE			BIT(0)
+
+/* MCMD */
+enum {
+	DESELECT_CMD			= 0,
+	PREA_CMD,
+	REF_CMD,
+	MRS_CMD,
+	ZQCS_CMD,
+	ZQCL_CMD,
+	RSTL_CMD,
+	MRR_CMD				= 8,
+	DPDE_CMD,
+};
+
+#define BANK_ADDR_MASK			7
+#define BANK_ADDR_SHIFT			17
+#define CMD_ADDR_MASK			0x1fff
+#define CMD_ADDR_SHIFT			4
+
+#define LPDDR23_MA_SHIFT		4
+#define LPDDR23_MA_MASK			0xff
+#define LPDDR23_OP_SHIFT		12
+#define LPDDR23_OP_MASK			0xff
+
+#define START_CMD			(1u << 31)
+
+/* DDRPHY REG */
+enum {
+	/* DDRPHY_REG0 */
+	SOFT_RESET_MASK				= 3,
+	SOFT_DERESET_ANALOG			= 1 << 2,
+	SOFT_DERESET_DIGITAL			= 1 << 3,
+	SOFT_RESET_SHIFT			= 2,
+
+	/* DDRPHY REG1 */
+	PHY_DDR3				= 0,
+	PHY_DDR2				= 1,
+	PHY_LPDDR3				= 2,
+	PHY_LPDDR2				= 3,
+
+	PHT_BL_8				= 1 << 2,
+	PHY_BL_4				= 0 << 2,
+
+	/* DDRPHY_REG2 */
+	MEMORY_SELECT_DDR3			= 0 << 0,
+	MEMORY_SELECT_LPDDR3			= 2 << 0,
+	MEMORY_SELECT_LPDDR2			= 3 << 0,
+	DQS_SQU_CAL_SEL_CS0_CS1			= 0 << 4,
+	DQS_SQU_CAL_SEL_CS1			= 1 << 4,
+	DQS_SQU_CAL_SEL_CS0			= 2 << 4,
+	DQS_SQU_CAL_NORMAL_MODE			= 0 << 1,
+	DQS_SQU_CAL_BYPASS_MODE			= 1 << 1,
+	DQS_SQU_CAL_START			= 1 << 0,
+	DQS_SQU_NO_CAL				= 0 << 0,
+};
+
+/* CK pull up/down driver strength control */
+enum {
+	PHY_RON_RTT_DISABLE = 0,
+	PHY_RON_RTT_451OHM = 1,
+	PHY_RON_RTT_225OHM,
+	PHY_RON_RTT_150OHM,
+	PHY_RON_RTT_112OHM,
+	PHY_RON_RTT_90OHM,
+	PHY_RON_RTT_75OHM,
+	PHY_RON_RTT_64OHM = 7,
+
+	PHY_RON_RTT_56OHM = 16,
+	PHY_RON_RTT_50OHM,
+	PHY_RON_RTT_45OHM,
+	PHY_RON_RTT_41OHM,
+	PHY_RON_RTT_37OHM,
+	PHY_RON_RTT_34OHM,
+	PHY_RON_RTT_33OHM,
+	PHY_RON_RTT_30OHM = 23,
+
+	PHY_RON_RTT_28OHM = 24,
+	PHY_RON_RTT_26OHM,
+	PHY_RON_RTT_25OHM,
+	PHY_RON_RTT_23OHM,
+	PHY_RON_RTT_22OHM,
+	PHY_RON_RTT_21OHM,
+	PHY_RON_RTT_20OHM,
+	PHY_RON_RTT_19OHM = 31,
+};
+
+/* 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_SOC_CON0 */
+#define GRF_DDR_16BIT_EN		(((0x1 << 0) << 16) | (0x1 << 0))
+#define GRF_DDR_32BIT_EN		(((0x1 << 0) << 16) | (0x0 << 0))
+#define GRF_MSCH_NOC_16BIT_EN		(((0x1 << 7) << 16) | (0x1 << 7))
+#define GRF_MSCH_NOC_32BIT_EN		(((0x1 << 7) << 16) | (0x0 << 7))
+
+#define GRF_DDRPHY_BUFFEREN_CORE_EN	(((0x1 << 8) << 16) | (0x0 << 8))
+#define GRF_DDRPHY_BUFFEREN_CORE_DIS	(((0x1 << 8) << 16) | (0x1 << 8))
+
+#define GRF_DDR3_EN			(((0x1 << 6) << 16) | (0x1 << 6))
+#define GRF_LPDDR2_3_EN			(((0x1 << 6) << 16) | (0x0 << 6))
+
+#define PHY_DRV_ODT_SET(n)		(((n) << 4) | (n))
+#define DDR3_DLL_RESET			(1 << 8)
+
+#endif /* _ASM_ARCH_SDRAM_RK322X_H */
diff --git a/arch/arm/include/asm/arch-rockchip/sys_proto.h b/arch/arm/include/asm/arch-rockchip/sys_proto.h
index 35423e1ba0..e428d59336 100644
--- a/arch/arm/include/asm/arch-rockchip/sys_proto.h
+++ b/arch/arm/include/asm/arch-rockchip/sys_proto.h
@@ -7,4 +7,27 @@
 #ifndef _ASM_ARCH_SYS_PROTO_H
 #define _ASM_ARCH_SYS_PROTO_H
 
+#ifdef CONFIG_ROCKCHIP_RK3288
+#include <asm/armv7.h>
+
+static void configure_l2ctlr(void)
+{
+	uint32_t l2ctlr;
+
+	l2ctlr = read_l2ctlr();
+	l2ctlr &= 0xfffc0000; /* clear bit0~bit17 */
+
+	/*
+	* Data RAM write latency: 2 cycles
+	* Data RAM read latency: 2 cycles
+	* Data RAM setup latency: 1 cycle
+	* Tag RAM write latency: 1 cycle
+	* Tag RAM read latency: 1 cycle
+	* Tag RAM setup latency: 1 cycle
+	*/
+	l2ctlr |= (1 << 3 | 1 << 0);
+	write_l2ctlr(l2ctlr);
+}
+#endif /* CONFIG_ROCKCHIP_RK3288 */
+
 #endif /* _ASM_ARCH_SYS_PROTO_H */
diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h
index a20702e612..efc515eb33 100644
--- a/arch/arm/include/asm/armv7.h
+++ b/arch/arm/include/asm/armv7.h
@@ -61,6 +61,27 @@
 #include <asm/io.h>
 #include <asm/barriers.h>
 
+/* read L2 control register (L2CTLR) */
+static inline uint32_t read_l2ctlr(void)
+{
+	uint32_t val = 0;
+
+	asm volatile ("mrc p15, 1, %0, c9, c0, 2" : "=r" (val));
+
+	return val;
+}
+
+/* write L2 control register (L2CTLR) */
+static inline void write_l2ctlr(uint32_t val)
+{
+	/*
+	 * Note: L2CTLR can only be written when the L2 memory system
+	 * is idle, ie before the MMU is enabled.
+	 */
+	asm volatile("mcr p15, 1, %0, c9, c0, 2" : : "r" (val) : "memory");
+	isb();
+}
+
 /*
  * Workaround for ARM errata # 798870
  * Set L2ACTLR[7] to reissue any memory transaction in the L2 that has been
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index 79e9704a2c..daafc8de6a 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -12,6 +12,7 @@ obj-spl-$(CONFIG_ROCKCHIP_BROM_HELPER) += bootrom.o save_boot_param.o
 obj-tpl-$(CONFIG_ROCKCHIP_BROM_HELPER) += bootrom.o save_boot_param.o
 
 obj-tpl-$(CONFIG_ROCKCHIP_RK3188) += rk3188-board-tpl.o
+obj-tpl-$(CONFIG_ROCKCHIP_RK3288) += rk3288-board-tpl.o
 obj-tpl-$(CONFIG_ROCKCHIP_RK3368) += rk3368-board-tpl.o
 
 obj-spl-$(CONFIG_ROCKCHIP_RK3036) += rk3036-board-spl.o
diff --git a/arch/arm/mach-rockchip/rk3188/Makefile b/arch/arm/mach-rockchip/rk3188/Makefile
index 2dc9511de7..7fa010405b 100644
--- a/arch/arm/mach-rockchip/rk3188/Makefile
+++ b/arch/arm/mach-rockchip/rk3188/Makefile
@@ -6,6 +6,5 @@
 
 ifndef CONFIG_TPL_BUILD
 obj-y += clk_rk3188.o
-obj-y += sdram_rk3188.o
 obj-y += syscon_rk3188.o
 endif
diff --git a/arch/arm/mach-rockchip/rk322x-board.c b/arch/arm/mach-rockchip/rk322x-board.c
index dcd8cf805f..d44311457a 100644
--- a/arch/arm/mach-rockchip/rk322x-board.c
+++ b/arch/arm/mach-rockchip/rk322x-board.c
@@ -21,12 +21,12 @@ DECLARE_GLOBAL_DATA_PTR;
 static void setup_boot_mode(void)
 {
 	struct rk322x_grf *const grf = (void *)GRF_BASE;
-	int boot_mode = readl(&grf->os_reg[4]);
+	int boot_mode = readl(&grf->os_reg[0]);
 
 	debug("boot mode %x.\n", boot_mode);
 
 	/* Clear boot mode */
-	writel(BOOT_NORMAL, &grf->os_reg[4]);
+	writel(BOOT_NORMAL, &grf->os_reg[0]);
 
 	switch (boot_mode) {
 	case BOOT_FASTBOOT:
diff --git a/arch/arm/mach-rockchip/rk3288-board-spl.c b/arch/arm/mach-rockchip/rk3288-board-spl.c
index 6b7bf85d8d..7b7fd5a6f1 100644
--- a/arch/arm/mach-rockchip/rk3288-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3288-board-spl.c
@@ -19,7 +19,10 @@
 #include <asm/arch/clock.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/periph.h>
+#include <asm/arch/pmu_rk3288.h>
 #include <asm/arch/sdram.h>
+#include <asm/arch/sdram_common.h>
+#include <asm/arch/sys_proto.h>
 #include <asm/arch/timer.h>
 #include <dm/pinctrl.h>
 #include <dm/root.h>
@@ -80,46 +83,6 @@ u32 spl_boot_mode(const u32 boot_device)
 	return MMCSD_MODE_RAW;
 }
 
-/* read L2 control register (L2CTLR) */
-static inline uint32_t read_l2ctlr(void)
-{
-	uint32_t val = 0;
-
-	asm volatile ("mrc p15, 1, %0, c9, c0, 2" : "=r" (val));
-
-	return val;
-}
-
-/* write L2 control register (L2CTLR) */
-static inline void write_l2ctlr(uint32_t val)
-{
-	/*
-	 * Note: L2CTLR can only be written when the L2 memory system
-	 * is idle, ie before the MMU is enabled.
-	 */
-	asm volatile("mcr p15, 1, %0, c9, c0, 2" : : "r" (val) : "memory");
-	isb();
-}
-
-static void configure_l2ctlr(void)
-{
-	uint32_t l2ctlr;
-
-	l2ctlr = read_l2ctlr();
-	l2ctlr &= 0xfffc0000; /* clear bit0~bit17 */
-
-	/*
-	* Data RAM write latency: 2 cycles
-	* Data RAM read latency: 2 cycles
-	* Data RAM setup latency: 1 cycle
-	* Tag RAM write latency: 1 cycle
-	* Tag RAM read latency: 1 cycle
-	* Tag RAM setup latency: 1 cycle
-	*/
-	l2ctlr |= (1 << 3 | 1 << 0);
-	write_l2ctlr(l2ctlr);
-}
-
 #ifdef CONFIG_SPL_MMC_SUPPORT
 static int configure_emmc(struct udevice *pinctrl)
 {
@@ -243,12 +206,15 @@ void board_init_f(ulong dummy)
 	}
 #endif
 
+#if !defined(CONFIG_SUPPORT_TPL)
 	debug("\nspl:init dram\n");
 	ret = uclass_get_device(UCLASS_RAM, 0, &dev);
 	if (ret) {
 		debug("DRAM init failed: %d\n", ret);
 		return;
 	}
+#endif
+
 #if CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM) && !defined(CONFIG_SPL_BOARD_INIT)
 	back_to_bootrom();
 #endif
@@ -326,3 +292,18 @@ err:
 	/* No way to report error here */
 	hang();
 }
+
+#ifdef CONFIG_SPL_OS_BOOT
+
+#define PMU_BASE		0xff730000
+int dram_init_banksize(void)
+{
+	struct rk3288_pmu *const pmu = (void *)PMU_BASE;
+	size_t size = rockchip_sdram_size((phys_addr_t)&pmu->sys_reg[2]);
+
+	gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
+	gd->bd->bi_dram[0].size = size;
+
+	return 0;
+}
+#endif
diff --git a/arch/arm/mach-rockchip/rk3288-board-tpl.c b/arch/arm/mach-rockchip/rk3288-board-tpl.c
new file mode 100644
index 0000000000..3d08b5b6d8
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3288-board-tpl.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 Amarula Solutions
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <debug_uart.h>
+#include <dm.h>
+#include <ram.h>
+#include <spl.h>
+#include <version.h>
+#include <asm/io.h>
+#include <asm/arch/bootrom.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/grf_rk3288.h>
+#include <asm/arch/periph.h>
+#include <asm/arch/pmu_rk3288.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/timer.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define GRF_BASE		0xff770000
+void board_init_f(ulong dummy)
+{
+	struct udevice *dev;
+	int ret;
+
+	/* Example code showing how to enable the debug UART on RK3288 */
+	/* Enable early UART on the RK3288 */
+	struct rk3288_grf * const grf = (void *)GRF_BASE;
+
+	rk_clrsetreg(&grf->gpio7ch_iomux, GPIO7C7_MASK << GPIO7C7_SHIFT |
+		     GPIO7C6_MASK << GPIO7C6_SHIFT,
+		     GPIO7C7_UART2DBG_SOUT << GPIO7C7_SHIFT |
+		     GPIO7C6_UART2DBG_SIN << GPIO7C6_SHIFT);
+	/*
+	 * Debug UART can be used from here if required:
+	 *
+	 * debug_uart_init();
+	 * printch('a');
+	 * printhex8(0x1234);
+	 * printascii("string");
+	 */
+	debug_uart_init();
+
+	ret = spl_early_init();
+	if (ret) {
+		debug("spl_early_init() failed: %d\n", ret);
+		hang();
+	}
+
+	rockchip_timer_init();
+	configure_l2ctlr();
+
+	ret = rockchip_get_clk(&dev);
+	if (ret) {
+		debug("CLK init failed: %d\n", ret);
+		return;
+	}
+
+	ret = uclass_get_device(UCLASS_RAM, 0, &dev);
+	if (ret) {
+		debug("DRAM init failed: %d\n", ret);
+		return;
+	}
+}
+
+void board_return_to_bootrom(void)
+{
+	back_to_bootrom();
+}
+
+u32 spl_boot_device(void)
+{
+	return BOOT_DEVICE_BOOTROM;
+}
+
+void spl_board_init(void)
+{
+	puts("\nU-Boot TPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \
+				U_BOOT_TIME ")\n");
+}
diff --git a/arch/arm/mach-rockchip/rk3288/Kconfig b/arch/arm/mach-rockchip/rk3288/Kconfig
index 4ad2940069..6beb26fd7a 100644
--- a/arch/arm/mach-rockchip/rk3288/Kconfig
+++ b/arch/arm/mach-rockchip/rk3288/Kconfig
@@ -87,6 +87,22 @@ config TARGET_POPMETAL_RK3288
 config TARGET_VYASA_RK3288
 	bool "Vyasa-RK3288"
 	select BOARD_LATE_INIT
+	select TPL
+	select SUPPORT_TPL
+	select TPL_DM
+	select TPL_REGMAP
+	select TPL_SYSCON
+	select TPL_CLK
+	select TPL_RAM
+	select TPL_OF_PLATDATA
+	select TPL_OF_CONTROL
+	select TPL_BOOTROM_SUPPORT
+	select TPL_NEEDS_SEPARATE_TEXT_BASE if SPL
+	select ROCKCHIP_BROM_HELPER
+	select TPL_DRIVERS_MISC_SUPPORT
+	select TPL_LIBCOMMON_SUPPORT
+	select TPL_LIBGENERIC_SUPPORT
+	select TPL_SERIAL_SUPPORT
 	help
 	  Vyasa is a RK3288-based development board with 2 USB ports,
 	  HDMI, VGA, micro-SD card, audio, WiFi  and Gigabit Ethernet, It
diff --git a/arch/arm/mach-rockchip/rk3288/Makefile b/arch/arm/mach-rockchip/rk3288/Makefile
index b5b28efbe8..a0033a0d84 100644
--- a/arch/arm/mach-rockchip/rk3288/Makefile
+++ b/arch/arm/mach-rockchip/rk3288/Makefile
@@ -6,5 +6,4 @@
 
 obj-y += clk_rk3288.o
 obj-y += rk3288.o
-obj-y += sdram_rk3288.o
 obj-y += syscon_rk3288.o
diff --git a/arch/arm/mach-rockchip/rk3328/Makefile b/arch/arm/mach-rockchip/rk3328/Makefile
index 72873e29e6..bbab036a12 100644
--- a/arch/arm/mach-rockchip/rk3328/Makefile
+++ b/arch/arm/mach-rockchip/rk3328/Makefile
@@ -6,5 +6,4 @@
 
 obj-y += clk_rk3328.o
 obj-y += rk3328.o
-obj-y += sdram_rk3328.o
 obj-y += syscon_rk3328.o
diff --git a/arch/arm/mach-rockchip/rk3399-board-spl.c b/arch/arm/mach-rockchip/rk3399-board-spl.c
index 3406156447..9c20f56dc9 100644
--- a/arch/arm/mach-rockchip/rk3399-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3399-board-spl.c
@@ -1,10 +1,12 @@
 /*
  * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
  *
  * SPDX-License-Identifier:     GPL-2.0+
  */
 
 #include <common.h>
+#include <asm/arch/bootrom.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/grf_rk3399.h>
 #include <asm/arch/hardware.h>
@@ -19,9 +21,43 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+void board_return_to_bootrom(void)
+{
+	back_to_bootrom();
+}
+
+static const char * const boot_devices[BROM_LAST_BOOTSOURCE + 1] = {
+	[BROM_BOOTSOURCE_EMMC] = "/sdhci@fe330000",
+	[BROM_BOOTSOURCE_SPINOR] = "/spi@ff1d0000",
+	[BROM_BOOTSOURCE_SD] = "/dwmmc@fe320000",
+};
+
+const char *board_spl_was_booted_from(void)
+{
+	u32  bootdevice_brom_id = readl(RK3399_BROM_BOOTSOURCE_ID_ADDR);
+	const char *bootdevice_ofpath = NULL;
+
+	if (bootdevice_brom_id < ARRAY_SIZE(boot_devices))
+		bootdevice_ofpath = boot_devices[bootdevice_brom_id];
+
+	if (bootdevice_ofpath)
+		debug("%s: brom_bootdevice_id %x maps to '%s'\n",
+		      __func__, bootdevice_brom_id, bootdevice_ofpath);
+	else
+		debug("%s: failed to resolve brom_bootdevice_id %x\n",
+		      __func__, bootdevice_brom_id);
+
+	return bootdevice_ofpath;
+}
+
 u32 spl_boot_device(void)
 {
-	return BOOT_DEVICE_MMC1;
+	u32 boot_device = BOOT_DEVICE_MMC1;
+
+	if (CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM))
+		return BOOT_DEVICE_BOOTROM;
+
+	return boot_device;
 }
 
 u32 spl_boot_mode(const u32 boot_device)
@@ -137,37 +173,6 @@ void board_init_f(ulong dummy)
 	}
 }
 
-void spl_board_init(void)
-{
-	struct udevice *pinctrl;
-	int ret;
-
-	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
-	if (ret) {
-		debug("%s: Cannot find pinctrl device\n", __func__);
-		goto err;
-	}
-
-	/* Enable debug UART */
-	ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_UART_DBG);
-	if (ret) {
-		debug("%s: Failed to set up console UART\n", __func__);
-		goto err;
-	}
-
-	preloader_console_init();
-#if CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM)
-	back_to_bootrom();
-#endif
-
-	return;
-err:
-	printf("spl_board_init: Error %d\n", ret);
-
-	/* No way to report error here */
-	hang();
-}
-
 #ifdef CONFIG_SPL_LOAD_FIT
 int board_fit_config_name_match(const char *name)
 {
diff --git a/arch/arm/mach-rockchip/rk3399/Makefile b/arch/arm/mach-rockchip/rk3399/Makefile
index 793ce31c12..98ebeac340 100644
--- a/arch/arm/mach-rockchip/rk3399/Makefile
+++ b/arch/arm/mach-rockchip/rk3399/Makefile
@@ -6,5 +6,4 @@
 
 obj-y += clk_rk3399.o
 obj-y += rk3399.o
-obj-y += sdram_rk3399.o
 obj-y += syscon_rk3399.o
diff --git a/arch/arm/mach-rockchip/spl-boot-order.c b/arch/arm/mach-rockchip/spl-boot-order.c
index 4f78c72720..843998dfdc 100644
--- a/arch/arm/mach-rockchip/spl-boot-order.c
+++ b/arch/arm/mach-rockchip/spl-boot-order.c
@@ -10,6 +10,25 @@
 #include <spl.h>
 
 #if CONFIG_IS_ENABLED(OF_CONTROL)
+/**
+ * spl_node_to_boot_device() - maps from a DT-node to a SPL boot device
+ * @node:	of_offset of the node
+ *
+ * The SPL framework uses BOOT_DEVICE_... constants to identify its boot
+ * sources.  These may take on a device-specific meaning, depending on
+ * what nodes are enabled in a DTS (e.g. BOOT_DEVICE_MMC1 may refer to
+ * different controllers/block-devices, depending on which SD/MMC controllers
+ * are enabled in any given DTS).  This function maps from a DT-node back
+ * onto a BOOT_DEVICE_... constant, considering the currently active devices.
+ *
+ * Returns
+ *   -ENOENT, if no device matching the node could be found
+ *   -ENOSYS, if the device matching the node can not be mapped onto a
+ *            SPL boot device (e.g. the third MMC device)
+ *   -1, for unspecified failures
+ *   a positive integer (from the BOOT_DEVICE_... family) on succes.
+ */
+
 static int spl_node_to_boot_device(int node)
 {
 	struct udevice *parent;
@@ -57,6 +76,24 @@ static int spl_node_to_boot_device(int node)
 	return -1;
 }
 
+/**
+ * board_spl_was_booted_from() - retrieves the of-path the SPL was loaded from
+ *
+ * To support a 'same-as-spl' specification in the search-order for the next
+ * stage, we need a SoC- or board-specific way to handshake with what 'came
+ * before us' (either a BROM or TPL stage) and map the info retrieved onto
+ * a OF path.
+ *
+ * Returns
+ *   NULL, on failure or if the device could not be identified
+ *   a of_path (a string), on success
+ */
+__weak const char *board_spl_was_booted_from(void)
+{
+	debug("%s: no support for 'same-as-spl' for this board\n", __func__);
+	return NULL;
+}
+
 void board_boot_order(u32 *spl_boot_list)
 {
 	const void *blob = gd->fdt_blob;
@@ -78,8 +115,17 @@ void board_boot_order(u32 *spl_boot_list)
 	     (conf = fdt_stringlist_get(blob, chosen_node,
 					"u-boot,spl-boot-order", elem, NULL));
 	     elem++) {
+		const char *alias;
+
+		/* Handle the case of 'same device the SPL was loaded from' */
+		if (strncmp(conf, "same-as-spl", 11) == 0) {
+			conf = board_spl_was_booted_from();
+			if (!conf)
+				continue;
+		}
+
 		/* First check if the list element is an alias */
-		const char *alias = fdt_get_alias(blob, conf);
+		alias = fdt_get_alias(blob, conf);
 		if (alias)
 			conf = alias;
 
diff --git a/board/amarula/vyasa-rk3288/vyasa-rk3288.c b/board/amarula/vyasa-rk3288/vyasa-rk3288.c
index ceee42c16e..7985671fbd 100644
--- a/board/amarula/vyasa-rk3288/vyasa-rk3288.c
+++ b/board/amarula/vyasa-rk3288/vyasa-rk3288.c
@@ -5,3 +5,16 @@
  */
 
 #include <common.h>
+
+#ifndef CONFIG_TPL_BUILD
+#include <spl.h>
+
+int spl_start_uboot(void)
+{
+        /* break into full u-boot on 'c' */
+        if (serial_tstc() && serial_getc() == 'c')
+                return 1;
+
+        return 0;
+}
+#endif
diff --git a/board/rockchip/evb_rk3399/evb-rk3399.c b/board/rockchip/evb_rk3399/evb-rk3399.c
index d50c59db8d..502dec325f 100644
--- a/board/rockchip/evb_rk3399/evb-rk3399.c
+++ b/board/rockchip/evb_rk3399/evb-rk3399.c
@@ -3,13 +3,14 @@
  *
  * SPDX-License-Identifier:     GPL-2.0+
  */
+
 #include <common.h>
 #include <dm.h>
-#include <ram.h>
 #include <dm/pinctrl.h>
 #include <dm/uclass-internal.h>
 #include <asm/arch/periph.h>
 #include <power/regulator.h>
+#include <spl.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -67,3 +68,30 @@ int board_init(void)
 out:
 	return 0;
 }
+
+void spl_board_init(void)
+{
+	struct udevice *pinctrl;
+	int ret;
+
+	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
+	if (ret) {
+		debug("%s: Cannot find pinctrl device\n", __func__);
+		goto err;
+	}
+
+	/* Enable debug UART */
+	ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_UART_DBG);
+	if (ret) {
+		debug("%s: Failed to set up console UART\n", __func__);
+		goto err;
+	}
+
+	preloader_console_init();
+	return;
+err:
+	printf("%s: Error %d\n", __func__, ret);
+
+	/* No way to report error here */
+	hang();
+}
diff --git a/board/theobroma-systems/lion_rk3368/fit_spl_atf.its b/board/theobroma-systems/lion_rk3368/fit_spl_atf.its
index 405750f351..60daddcc44 100644
--- a/board/theobroma-systems/lion_rk3368/fit_spl_atf.its
+++ b/board/theobroma-systems/lion_rk3368/fit_spl_atf.its
@@ -27,8 +27,8 @@
 			type = "firmware";
 			arch = "arm64";
 			compression = "none";
-			load = <0x00010000>;
-			entry = <0x00010000>;
+			load = <0x00100000>;
+			entry = <0x00100000>;
 		};
 
 		fdt {
diff --git a/board/theobroma-systems/puma_rk3399/puma-rk3399.c b/board/theobroma-systems/puma_rk3399/puma-rk3399.c
index 45d56cd99e..2b4988e2d2 100644
--- a/board/theobroma-systems/puma_rk3399/puma-rk3399.c
+++ b/board/theobroma-systems/puma_rk3399/puma-rk3399.c
@@ -3,62 +3,50 @@
  *
  * SPDX-License-Identifier:     GPL-2.0+
  */
+
 #include <common.h>
 #include <dm.h>
 #include <misc.h>
-#include <ram.h>
 #include <dm/pinctrl.h>
 #include <dm/uclass-internal.h>
 #include <asm/setup.h>
 #include <asm/arch/periph.h>
 #include <power/regulator.h>
+#include <spl.h>
 #include <u-boot/sha256.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
 int board_init(void)
 {
-	struct udevice *pinctrl, *regulator;
 	int ret;
 
 	/*
-	 * The PWM does not have decicated interrupt number in dts and can
-	 * not get periph_id by pinctrl framework, so let's init them here.
-	 * The PWM2 and PWM3 are for pwm regulators.
+	 * We need to call into regulators_enable_boot_on() again, as the call
+	 * during SPL may have not included all regulators.
 	 */
-	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
-	if (ret) {
-		debug("%s: Cannot find pinctrl device\n", __func__);
-		goto out;
-	}
-
-	ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_PWM2);
-	if (ret) {
-		debug("%s PWM2 pinctrl init fail!\n", __func__);
-		goto out;
-	}
-
-	/* rk3399 need to init vdd_center to get the correct output voltage */
-	ret = regulator_get_by_platname("vdd_center", &regulator);
+	ret = regulators_enable_boot_on(false);
 	if (ret)
-		debug("%s: Cannot get vdd_center regulator\n", __func__);
+		debug("%s: Cannot enable boot on regulator\n", __func__);
 
-	ret = regulator_get_by_platname("vcc5v0_host", &regulator);
-	if (ret) {
-		debug("%s vcc5v0_host init fail! ret %d\n", __func__, ret);
-		goto out;
-	}
-
-	ret = regulator_set_enable(regulator, true);
-	if (ret) {
-		debug("%s vcc5v0-host-en set fail!\n", __func__);
-		goto out;
-	}
-
-out:
 	return 0;
 }
 
+void spl_board_init(void)
+{
+	int  ret;
+
+	/*
+	 * Turning the eMMC and SPI back on (if disabled via the Qseven
+	 * BIOS_ENABLE) signal is done through a always-on regulator).
+	 */
+	ret = regulators_enable_boot_on(false);
+	if (ret)
+		debug("%s: Cannot enable boot on regulator\n", __func__);
+
+	preloader_console_init();
+}
+
 static void setup_macaddr(void)
 {
 #if CONFIG_IS_ENABLED(CMD_NET)
@@ -91,8 +79,6 @@ static void setup_macaddr(void)
 	mac_addr[0] |= 0x02;  /* set local assignment bit (IEEE802) */
 	eth_env_set_enetaddr("ethaddr", mac_addr);
 #endif
-
-	return;
 }
 
 static void setup_serial(void)
@@ -147,8 +133,6 @@ static void setup_serial(void)
 	env_set("cpuid#", cpuid_str);
 	env_set("serial#", serialno_str);
 #endif
-
-	return;
 }
 
 int misc_init_r(void)
diff --git a/configs/evb-rk3328_defconfig b/configs/evb-rk3328_defconfig
index 7bec001786..6d0f3af7a3 100644
--- a/configs/evb-rk3328_defconfig
+++ b/configs/evb-rk3328_defconfig
@@ -22,12 +22,18 @@ CONFIG_REGMAP=y
 CONFIG_SYSCON=y
 CONFIG_CLK=y
 CONFIG_ROCKCHIP_GPIO=y
+CONFIG_SYS_I2C_ROCKCHIP=y
 CONFIG_MMC_DW=y
 CONFIG_MMC_DW_ROCKCHIP=y
 CONFIG_PINCTRL=y
 CONFIG_PINCTRL_ROCKCHIP_RK3328=y
+CONFIG_DM_PMIC=y
+CONFIG_PMIC_RK8XX=y
+CONFIG_DM_REGULATOR=y
 CONFIG_REGULATOR_PWM=y
 CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_REGULATOR_RK8XX=y
+CONFIG_PWM_ROCKCHIP=y
 CONFIG_RAM=y
 CONFIG_BAUDRATE=1500000
 CONFIG_DEBUG_UART_BASE=0xFF130000
diff --git a/configs/lion-rk3368_defconfig b/configs/lion-rk3368_defconfig
index c7ee7b3f97..985fb1ce98 100644
--- a/configs/lion-rk3368_defconfig
+++ b/configs/lion-rk3368_defconfig
@@ -23,7 +23,6 @@ CONFIG_BOOTSTAGE=y
 CONFIG_SPL_BOOTSTAGE=y
 CONFIG_BOOTSTAGE_REPORT=y
 CONFIG_BOOTSTAGE_FDT=y
-CONFIG_ENV_IS_IN_MMC=y
 # CONFIG_DISPLAY_CPUINFO is not set
 CONFIG_ARCH_EARLY_INIT_R=y
 CONFIG_SPL=y
diff --git a/configs/puma-rk3399_defconfig b/configs/puma-rk3399_defconfig
index 2ab2516c01..62a8d7c859 100644
--- a/configs/puma-rk3399_defconfig
+++ b/configs/puma-rk3399_defconfig
@@ -1,5 +1,6 @@
 CONFIG_ARM=y
 CONFIG_ARCH_ROCKCHIP=y
+CONFIG_SPL_GPIO_SUPPORT=y
 CONFIG_SPL_LIBCOMMON_SUPPORT=y
 CONFIG_SPL_LIBGENERIC_SUPPORT=y
 CONFIG_SYS_MALLOC_F_LEN=0x4000
@@ -20,6 +21,8 @@ CONFIG_SPL_BOARD_INIT=y
 CONFIG_SPL_STACK_R=y
 CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x4000
 CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x200
+CONFIG_SPL_I2C_SUPPORT=y
+CONFIG_SPL_POWER_SUPPORT=y
 CONFIG_CMD_BOOTZ=y
 # CONFIG_CMD_IMLS is not set
 CONFIG_CMD_GPT=y
@@ -63,8 +66,10 @@ CONFIG_SPL_PINCTRL=y
 CONFIG_PINCTRL_ROCKCHIP_RK3399=y
 CONFIG_DM_PMIC=y
 CONFIG_PMIC_RK8XX=y
-CONFIG_REGULATOR_PWM=y
+CONFIG_SPL_DM_REGULATOR=y
 CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_SPL_DM_REGULATOR_FIXED=y
+CONFIG_DM_REGULATOR_GPIO=y
 CONFIG_REGULATOR_RK8XX=y
 CONFIG_PWM_ROCKCHIP=y
 CONFIG_RAM=y
diff --git a/configs/vyasa-rk3288_defconfig b/configs/vyasa-rk3288_defconfig
index 7db7b0b6f9..7bd6068e0c 100644
--- a/configs/vyasa-rk3288_defconfig
+++ b/configs/vyasa-rk3288_defconfig
@@ -1,8 +1,11 @@
 CONFIG_ARM=y
+# CONFIG_SPL_USE_ARCH_MEMCPY is not set
+# CONFIG_SPL_USE_ARCH_MEMSET is not set
 CONFIG_ARCH_ROCKCHIP=y
 CONFIG_SYS_MALLOC_F_LEN=0x2000
 CONFIG_ROCKCHIP_RK3288=y
 CONFIG_TARGET_VYASA_RK3288=y
+CONFIG_TPL_TEXT_BASE=0xff704004
 CONFIG_SPL_STACK_R_ADDR=0x80000
 CONFIG_DEFAULT_DEVICE_TREE="rk3288-vyasa"
 CONFIG_DEBUG_UART=y
diff --git a/doc/README.rockchip b/doc/README.rockchip
index 12fec38139..4b7be0b715 100644
--- a/doc/README.rockchip
+++ b/doc/README.rockchip
@@ -150,6 +150,24 @@ Note: rk3036 SDMMC and debug uart use the same iomux, so if you boot from SD, th
       debug uart must be disabled
 
 
+Booting from an SD card on RK3288 with TPL
+==========================================
+
+Since the size of SPL can't be exceeded 0x8000 bytes in RK3288, it is not possible add
+new SPL features like Falcon mode or etc.
+
+So introduce TPL so-that adding new features to SPL is possible because now TPL should
+run minimal with code like DDR, clock etc and rest of new features in SPL.
+
+As of now TPL is added on Vyasa-RK3288 board.
+
+To write an image that boots from an SD card (assumed to be /dev/mmcblk0):
+
+   ./tools/mkimage -n rk3288 -T rksd -d ./tpl/u-boot-tpl.bin out &&
+    cat ./spl/u-boot-spl-dtb.bin >> out &&
+    sudo dd if=out of=/dev/mmcblk0 seek=64 &&
+    sudo dd if=u-boot-dtb.img of=/dev/mmcblk0 seek=256
+
 Booting from an SD card on RK3188
 =================================
 
diff --git a/doc/device-tree-bindings/chosen.txt b/doc/device-tree-bindings/chosen.txt
index 5625d210e8..c96b8f7109 100644
--- a/doc/device-tree-bindings/chosen.txt
+++ b/doc/device-tree-bindings/chosen.txt
@@ -56,10 +56,20 @@ Each list element of the property should specify a device to be probed
 in the order they are listed: references (i.e. implicit paths), a full
 path or an alias is expected for each entry.
 
+A special specifier "same-as-spl" can be used at any position in the
+boot-order to direct U-Boot to insert the device the SPL was booted
+from there.  Whether this is indeed inserted or silently ignored (if
+it is not supported on any given SoC/board or if the boot-device is
+not available to continue booting from) is implementation-defined.
+Note that if "same-as-spl" expands to an actual node for a given
+board, the corresponding node may appear multiple times in the
+boot-order (as there currently exists no mechanism to suppress
+duplicates from the list).
+
 Example
 -------
 / {
 	chosen {
-		u-boot,spl-boot-order = &sdmmc, "/sdhci@fe330000";
+		u-boot,spl-boot-order = "same-as-spl", &sdmmc, "/sdhci@fe330000";
 	};
 };
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index e5335f7234..8094420548 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -28,3 +28,12 @@ config ADC_SANDBOX
 	  - 4 analog input channels
 	  - 16-bit resolution
 	  - single and multi-channel conversion mode
+
+config SARADC_ROCKCHIP
+	bool "Enable Rockchip SARADC driver"
+	help
+	  This enables driver for Rockchip SARADC.
+	  It provides:
+	  - 2~6 analog input channels
+	  - 1O or 12 bits resolution
+	  - Up to 1MSPS of sample rate
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
index cebf26de07..4b5aa693ec 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -8,3 +8,4 @@
 obj-$(CONFIG_ADC) += adc-uclass.o
 obj-$(CONFIG_ADC_EXYNOS) += exynos-adc.o
 obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
+obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c
new file mode 100644
index 0000000000..0e6271d097
--- /dev/null
+++ b/drivers/adc/rockchip-saradc.c
@@ -0,0 +1,183 @@
+/*
+ * (C) Copyright 2017, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ * Rockchip SARADC driver for U-Boot
+ */
+
+#include <common.h>
+#include <adc.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+
+#define SARADC_CTRL_CHN_MASK		GENMASK(2, 0)
+#define SARADC_CTRL_POWER_CTRL		BIT(3)
+#define SARADC_CTRL_IRQ_ENABLE		BIT(5)
+#define SARADC_CTRL_IRQ_STATUS		BIT(6)
+
+#define SARADC_TIMEOUT			(100 * 1000)
+
+struct rockchip_saradc_regs {
+	unsigned int data;
+	unsigned int stas;
+	unsigned int ctrl;
+	unsigned int dly_pu_soc;
+};
+
+struct rockchip_saradc_data {
+	int				num_bits;
+	int				num_channels;
+	unsigned long			clk_rate;
+};
+
+struct rockchip_saradc_priv {
+	struct rockchip_saradc_regs		*regs;
+	int					active_channel;
+	const struct rockchip_saradc_data	*data;
+};
+
+int rockchip_saradc_channel_data(struct udevice *dev, int channel,
+				 unsigned int *data)
+{
+	struct rockchip_saradc_priv *priv = dev_get_priv(dev);
+	struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+
+	if (channel != priv->active_channel) {
+		error("Requested channel is not active!");
+		return -EINVAL;
+	}
+
+	if ((readl(&priv->regs->ctrl) & SARADC_CTRL_IRQ_STATUS) !=
+	    SARADC_CTRL_IRQ_STATUS)
+		return -EBUSY;
+
+	/* Read value */
+	*data = readl(&priv->regs->data);
+	*data &= uc_pdata->data_mask;
+
+	/* Power down adc */
+	writel(0, &priv->regs->ctrl);
+
+	return 0;
+}
+
+int rockchip_saradc_start_channel(struct udevice *dev, int channel)
+{
+	struct rockchip_saradc_priv *priv = dev_get_priv(dev);
+
+	if (channel < 0 || channel >= priv->data->num_channels) {
+		error("Requested channel is invalid!");
+		return -EINVAL;
+	}
+
+	/* 8 clock periods as delay between power up and start cmd */
+	writel(8, &priv->regs->dly_pu_soc);
+
+	/* Select the channel to be used and trigger conversion */
+	writel(SARADC_CTRL_POWER_CTRL | (channel & SARADC_CTRL_CHN_MASK) |
+	       SARADC_CTRL_IRQ_ENABLE, &priv->regs->ctrl);
+
+	priv->active_channel = channel;
+
+	return 0;
+}
+
+int rockchip_saradc_stop(struct udevice *dev)
+{
+	struct rockchip_saradc_priv *priv = dev_get_priv(dev);
+
+	/* Power down adc */
+	writel(0, &priv->regs->ctrl);
+
+	priv->active_channel = -1;
+
+	return 0;
+}
+
+int rockchip_saradc_probe(struct udevice *dev)
+{
+	struct rockchip_saradc_priv *priv = dev_get_priv(dev);
+	struct clk clk;
+	int ret;
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret)
+		return ret;
+
+	ret = clk_set_rate(&clk, priv->data->clk_rate);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	priv->active_channel = -1;
+
+	return 0;
+}
+
+int rockchip_saradc_ofdata_to_platdata(struct udevice *dev)
+{
+	struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+	struct rockchip_saradc_priv *priv = dev_get_priv(dev);
+	struct rockchip_saradc_data *data;
+
+	data = (struct rockchip_saradc_data *)dev_get_driver_data(dev);
+	priv->regs = (struct rockchip_saradc_regs *)dev_read_addr(dev);
+	if (priv->regs == (struct rockchip_saradc_regs *)FDT_ADDR_T_NONE) {
+		error("Dev: %s - can't get address!", dev->name);
+		return -ENODATA;
+	}
+
+	priv->data = data;
+	uc_pdata->data_mask = (1 << priv->data->num_bits) - 1;;
+	uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+	uc_pdata->data_timeout_us = SARADC_TIMEOUT / 5;
+	uc_pdata->channel_mask = (1 << priv->data->num_channels) - 1;
+
+	return 0;
+}
+
+static const struct adc_ops rockchip_saradc_ops = {
+	.start_channel = rockchip_saradc_start_channel,
+	.channel_data = rockchip_saradc_channel_data,
+	.stop = rockchip_saradc_stop,
+};
+
+static const struct rockchip_saradc_data saradc_data = {
+	.num_bits = 10,
+	.num_channels = 3,
+	.clk_rate = 1000000,
+};
+
+static const struct rockchip_saradc_data rk3066_tsadc_data = {
+	.num_bits = 12,
+	.num_channels = 2,
+	.clk_rate = 50000,
+};
+
+static const struct rockchip_saradc_data rk3399_saradc_data = {
+	.num_bits = 10,
+	.num_channels = 6,
+	.clk_rate = 1000000,
+};
+
+static const struct udevice_id rockchip_saradc_ids[] = {
+	{ .compatible = "rockchip,saradc",
+	  .data = (ulong)&saradc_data },
+	{ .compatible = "rockchip,rk3066-tsadc",
+	  .data = (ulong)&rk3066_tsadc_data },
+	{ .compatible = "rockchip,rk3399-saradc",
+	  .data = (ulong)&rk3399_saradc_data },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_saradc) = {
+	.name		= "rockchip_saradc",
+	.id		= UCLASS_ADC,
+	.of_match	= rockchip_saradc_ids,
+	.ops		= &rockchip_saradc_ops,
+	.probe		= rockchip_saradc_probe,
+	.ofdata_to_platdata = rockchip_saradc_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct rockchip_saradc_priv),
+};
diff --git a/drivers/clk/rockchip/clk_rk322x.c b/drivers/clk/rockchip/clk_rk322x.c
index d7f6a3c313..e87267d239 100644
--- a/drivers/clk/rockchip/clk_rk322x.c
+++ b/drivers/clk/rockchip/clk_rk322x.c
@@ -117,16 +117,16 @@ static void rkclk_init(struct rk322x_cru *cru)
 		     pclk_div << CORE_PERI_DIV_SHIFT);
 
 	/*
-	 * select apll as pd_bus bus clock source and
+	 * select gpll as pd_bus bus clock source and
 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
 	 */
 	aclk_div = GPLL_HZ / BUS_ACLK_HZ - 1;
 	assert((aclk_div + 1) * BUS_ACLK_HZ == GPLL_HZ && aclk_div <= 0x1f);
 
-	pclk_div = GPLL_HZ / BUS_PCLK_HZ - 1;
+	pclk_div = BUS_ACLK_HZ / BUS_PCLK_HZ - 1;
 	assert((pclk_div + 1) * BUS_PCLK_HZ == GPLL_HZ && pclk_div <= 0x7);
 
-	hclk_div = GPLL_HZ / BUS_HCLK_HZ - 1;
+	hclk_div = BUS_ACLK_HZ / BUS_HCLK_HZ - 1;
 	assert((hclk_div + 1) * BUS_HCLK_HZ == GPLL_HZ && hclk_div <= 0x3);
 
 	rk_clrsetreg(&cru->cru_clksel_con[0],
@@ -389,7 +389,7 @@ static int rk322x_clk_bind(struct udevice *dev)
 	/* The reset driver does not have a device node, so bind it here */
 	ret = device_bind_driver(gd->dm_root, "rk322x_sysreset", "reset", &dev);
 	if (ret)
-		debug("Warning: No RK3036 reset driver: ret=%d\n", ret);
+		debug("Warning: No RK322x reset driver: ret=%d\n", ret);
 
 	return 0;
 }
diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c
index 478195b10b..a133810bf6 100644
--- a/drivers/clk/rockchip/clk_rk3288.c
+++ b/drivers/clk/rockchip/clk_rk3288.c
@@ -5,6 +5,7 @@
  */
 
 #include <common.h>
+#include <bitfield.h>
 #include <clk-uclass.h>
 #include <dm.h>
 #include <dt-structs.h>
@@ -111,6 +112,15 @@ enum {
 	PERI_ACLK_DIV_SHIFT	= 0,
 	PERI_ACLK_DIV_MASK	= 0x1f << PERI_ACLK_DIV_SHIFT,
 
+	/*
+	 * CLKSEL24
+	 * saradc_div_con:
+	 * clk_saradc=24MHz/(saradc_div_con+1)
+	 */
+	CLK_SARADC_DIV_CON_SHIFT	= 8,
+	CLK_SARADC_DIV_CON_MASK		= GENMASK(15, 8),
+	CLK_SARADC_DIV_CON_WIDTH	= 8,
+
 	SOCSTS_DPLL_LOCK	= 1 << 5,
 	SOCSTS_APLL_LOCK	= 1 << 6,
 	SOCSTS_CPLL_LOCK	= 1 << 7,
@@ -634,6 +644,31 @@ static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint gclk_rate,
 	return rockchip_spi_get_clk(cru, gclk_rate, periph);
 }
 
+static ulong rockchip_saradc_get_clk(struct rk3288_cru *cru)
+{
+	u32 div, val;
+
+	val = readl(&cru->cru_clksel_con[24]);
+	div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT,
+			       CLK_SARADC_DIV_CON_WIDTH);
+
+	return DIV_TO_RATE(OSC_HZ, div);
+}
+
+static ulong rockchip_saradc_set_clk(struct rk3288_cru *cru, uint hz)
+{
+	int src_clk_div;
+
+	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
+	assert(src_clk_div < 128);
+
+	rk_clrsetreg(&cru->cru_clksel_con[24],
+		     CLK_SARADC_DIV_CON_MASK,
+		     src_clk_div << CLK_SARADC_DIV_CON_SHIFT);
+
+	return rockchip_saradc_get_clk(cru);
+}
+
 static ulong rk3288_clk_get_rate(struct clk *clk)
 {
 	struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
@@ -666,6 +701,9 @@ static ulong rk3288_clk_get_rate(struct clk *clk)
 		return gclk_rate;
 	case PCLK_PWM:
 		return PD_BUS_PCLK_HZ;
+	case SCLK_SARADC:
+		new_rate = rockchip_saradc_get_clk(priv->cru);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -756,6 +794,9 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate)
 		new_rate = rate;
 		break;
 #endif
+	case SCLK_SARADC:
+		new_rate = rockchip_saradc_set_clk(priv->cru, rate);
+		break;
 	default:
 		return -ENOENT;
 	}
diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c
index c3a6650de0..540d9104c3 100644
--- a/drivers/clk/rockchip/clk_rk3328.c
+++ b/drivers/clk/rockchip/clk_rk3328.c
@@ -5,6 +5,7 @@
  */
 
 #include <common.h>
+#include <bitfield.h>
 #include <clk-uclass.h>
 #include <dm.h>
 #include <errno.h>
@@ -114,7 +115,8 @@ enum {
 
 	/* CLKSEL_CON23 */
 	CLK_SARADC_DIV_CON_SHIFT	= 0,
-	CLK_SARADC_DIV_CON_MASK		= 0x3ff << CLK_SARADC_DIV_CON_SHIFT,
+	CLK_SARADC_DIV_CON_MASK		= GENMASK(9, 0),
+	CLK_SARADC_DIV_CON_WIDTH	= 10,
 
 	/* CLKSEL_CON24 */
 	CLK_PWM_PLL_SEL_CPLL		= 0,
@@ -478,6 +480,31 @@ static ulong rk3328_pwm_set_clk(struct rk3328_cru *cru, uint hz)
 	return DIV_TO_RATE(GPLL_HZ, div);
 }
 
+static ulong rk3328_saradc_get_clk(struct rk3328_cru *cru)
+{
+	u32 div, val;
+
+	val = readl(&cru->clksel_con[23]);
+	div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT,
+			       CLK_SARADC_DIV_CON_WIDTH);
+
+	return DIV_TO_RATE(OSC_HZ, div);
+}
+
+static ulong rk3328_saradc_set_clk(struct rk3328_cru *cru, uint hz)
+{
+	int src_clk_div;
+
+	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
+	assert(src_clk_div < 128);
+
+	rk_clrsetreg(&cru->clksel_con[23],
+		     CLK_SARADC_DIV_CON_MASK,
+		     src_clk_div << CLK_SARADC_DIV_CON_SHIFT);
+
+	return rk3328_saradc_get_clk(cru);
+}
+
 static ulong rk3328_clk_get_rate(struct clk *clk)
 {
 	struct rk3328_clk_priv *priv = dev_get_priv(clk->dev);
@@ -501,6 +528,9 @@ static ulong rk3328_clk_get_rate(struct clk *clk)
 	case SCLK_PWM:
 		rate = rk3328_pwm_get_clk(priv->cru);
 		break;
+	case SCLK_SARADC:
+		rate = rk3328_saradc_get_clk(priv->cru);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -531,6 +561,9 @@ static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate)
 	case SCLK_PWM:
 		ret = rk3328_pwm_set_clk(priv->cru, rate);
 		break;
+	case SCLK_SARADC:
+		ret = rk3328_saradc_set_clk(priv->cru, rate);
+		break;
 	default:
 		return -ENOENT;
 	}
diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c
index e2747816b9..3a12be7728 100644
--- a/drivers/clk/rockchip/clk_rk3368.c
+++ b/drivers/clk/rockchip/clk_rk3368.c
@@ -12,6 +12,7 @@
 #include <errno.h>
 #include <mapmem.h>
 #include <syscon.h>
+#include <bitfield.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/cru_rk3368.h>
 #include <asm/arch/hardware.h>
@@ -397,6 +398,31 @@ static ulong rk3368_spi_set_clk(struct rk3368_cru *cru, ulong clk_id, uint hz)
 	return rk3368_spi_get_clk(cru, clk_id);
 }
 
+static ulong rk3368_saradc_get_clk(struct rk3368_cru *cru)
+{
+	u32 div, val;
+
+	val = readl(&cru->clksel_con[25]);
+	div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT,
+			       CLK_SARADC_DIV_CON_WIDTH);
+
+	return DIV_TO_RATE(OSC_HZ, div);
+}
+
+static ulong rk3368_saradc_set_clk(struct rk3368_cru *cru, uint hz)
+{
+	int src_clk_div;
+
+	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
+	assert(src_clk_div < 128);
+
+	rk_clrsetreg(&cru->clksel_con[25],
+		     CLK_SARADC_DIV_CON_MASK,
+		     src_clk_div << CLK_SARADC_DIV_CON_SHIFT);
+
+	return rk3368_saradc_get_clk(cru);
+}
+
 static ulong rk3368_clk_get_rate(struct clk *clk)
 {
 	struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
@@ -419,6 +445,9 @@ static ulong rk3368_clk_get_rate(struct clk *clk)
 		rate = rk3368_mmc_get_clk(priv->cru, clk->id);
 		break;
 #endif
+	case SCLK_SARADC:
+		rate = rk3368_saradc_get_clk(priv->cru);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -453,6 +482,9 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate)
 		ret = rk3368_gmac_set_clk(priv->cru, clk->id, rate);
 		break;
 #endif
+	case SCLK_SARADC:
+		ret =  rk3368_saradc_set_clk(priv->cru, rate);
+		break;
 	default:
 		return -ENOENT;
 	}
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index 9d963be552..50faf5d193 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -12,6 +12,7 @@
 #include <errno.h>
 #include <mapmem.h>
 #include <syscon.h>
+#include <bitfield.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/cru_rk3399.h>
@@ -181,7 +182,8 @@ enum {
 
 	/* CLKSEL_CON26 */
 	CLK_SARADC_DIV_CON_SHIFT	= 8,
-	CLK_SARADC_DIV_CON_MASK		= 0xff << CLK_SARADC_DIV_CON_SHIFT,
+	CLK_SARADC_DIV_CON_MASK		= GENMASK(15, 8),
+	CLK_SARADC_DIV_CON_WIDTH	= 8,
 
 	/* CLKSEL_CON27 */
 	CLK_TSADC_SEL_X24M		= 0x0,
@@ -860,6 +862,32 @@ static ulong rk3399_ddr_set_clk(struct rk3399_cru *cru,
 
 	return set_rate;
 }
+
+static ulong rk3399_saradc_get_clk(struct rk3399_cru *cru)
+{
+	u32 div, val;
+
+	val = readl(&cru->clksel_con[26]);
+	div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT,
+			       CLK_SARADC_DIV_CON_WIDTH);
+
+	return DIV_TO_RATE(OSC_HZ, div);
+}
+
+static ulong rk3399_saradc_set_clk(struct rk3399_cru *cru, uint hz)
+{
+	int src_clk_div;
+
+	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
+	assert(src_clk_div < 128);
+
+	rk_clrsetreg(&cru->clksel_con[26],
+		     CLK_SARADC_DIV_CON_MASK,
+		     src_clk_div << CLK_SARADC_DIV_CON_SHIFT);
+
+	return rk3399_saradc_get_clk(cru);
+}
+
 static ulong rk3399_clk_get_rate(struct clk *clk)
 {
 	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
@@ -895,6 +923,9 @@ static ulong rk3399_clk_get_rate(struct clk *clk)
 		break;
 	case PCLK_EFUSE1024NS:
 		break;
+	case SCLK_SARADC:
+		rate = rk3399_saradc_get_clk(priv->cru);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -943,6 +974,9 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate)
 		break;
 	case PCLK_EFUSE1024NS:
 		break;
+	case SCLK_SARADC:
+		ret = rk3399_saradc_set_clk(priv->cru, rate);
+		break;
 	default:
 		return -ENOENT;
 	}
diff --git a/drivers/clk/rockchip/clk_rv1108.c b/drivers/clk/rockchip/clk_rv1108.c
index cf966bbdc3..86e73e414c 100644
--- a/drivers/clk/rockchip/clk_rv1108.c
+++ b/drivers/clk/rockchip/clk_rv1108.c
@@ -5,6 +5,7 @@
  */
 
 #include <common.h>
+#include <bitfield.h>
 #include <clk-uclass.h>
 #include <dm.h>
 #include <errno.h>
@@ -36,7 +37,7 @@ enum {
 			 #hz "Hz cannot be hit with PLL "\
 			 "divisors on line " __stringify(__LINE__));
 
-/* use interge mode*/
+/* use integer mode */
 static inline int rv1108_pll_id(enum rk_clk_id clk_id)
 {
 	int id = 0;
@@ -130,6 +131,31 @@ static int rv1108_sfc_set_clk(struct rv1108_cru *cru, uint rate)
 	return DIV_TO_RATE(pll_rate, div);
 }
 
+static ulong rv1108_saradc_get_clk(struct rv1108_cru *cru)
+{
+	u32 div, val;
+
+	val = readl(&cru->clksel_con[22]);
+	div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT,
+			       CLK_SARADC_DIV_CON_WIDTH);
+
+	return DIV_TO_RATE(OSC_HZ, div);
+}
+
+static ulong rv1108_saradc_set_clk(struct rv1108_cru *cru, uint hz)
+{
+	int src_clk_div;
+
+	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
+	assert(src_clk_div < 128);
+
+	rk_clrsetreg(&cru->clksel_con[22],
+		     CLK_SARADC_DIV_CON_MASK,
+		     src_clk_div << CLK_SARADC_DIV_CON_SHIFT);
+
+	return rv1108_saradc_get_clk(cru);
+}
+
 static ulong rv1108_clk_get_rate(struct clk *clk)
 {
 	struct rv1108_clk_priv *priv = dev_get_priv(clk->dev);
@@ -137,6 +163,8 @@ static ulong rv1108_clk_get_rate(struct clk *clk)
 	switch (clk->id) {
 	case 0 ... 63:
 		return rkclk_pll_get_rate(priv->cru, clk->id);
+	case SCLK_SARADC:
+		return rv1108_saradc_get_clk(priv->cru);
 	default:
 		return -ENOENT;
 	}
@@ -154,6 +182,9 @@ static ulong rv1108_clk_set_rate(struct clk *clk, ulong rate)
 	case SCLK_SFC:
 		new_rate = rv1108_sfc_set_clk(priv->cru, rate);
 		break;
+	case SCLK_SARADC:
+		new_rate = rv1108_saradc_set_clk(priv->cru, rate);
+		break;
 	default:
 		return -ENOENT;
 	}
diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c
index 840b3f6046..332280c220 100644
--- a/drivers/i2c/rk_i2c.c
+++ b/drivers/i2c/rk_i2c.c
@@ -396,6 +396,7 @@ static const struct udevice_id rockchip_i2c_ids[] = {
 	{ .compatible = "rockchip,rk3066-i2c" },
 	{ .compatible = "rockchip,rk3188-i2c" },
 	{ .compatible = "rockchip,rk3288-i2c" },
+	{ .compatible = "rockchip,rk3328-i2c" },
 	{ .compatible = "rockchip,rk3399-i2c" },
 	{ }
 };
diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3368.c b/drivers/pinctrl/rockchip/pinctrl_rk3368.c
index b1f5704164..25249e377a 100644
--- a/drivers/pinctrl/rockchip/pinctrl_rk3368.c
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3368.c
@@ -208,6 +208,29 @@ enum {
 	GPIO2A0_FLASH_CSN0      = (1 << GPIO2A0_SHIFT),
 };
 
+/*GRF_GPIO2B_IOMUX*/
+enum {
+	GPIO2B3_SHIFT           = 6,
+	GPIO2B3_MASK            = GENMASK(GPIO2B3_SHIFT + 1, GPIO2B3_SHIFT),
+	GPIO2B3_GPIO            = 0,
+	GPIO2B3_SDMMC0_DTECTN   = (1 << GPIO2B3_SHIFT),
+
+	GPIO2B2_SHIFT           = 4,
+	GPIO2B2_MASK            = GENMASK(GPIO2B2_SHIFT + 1, GPIO2B2_SHIFT),
+	GPIO2B2_GPIO            = 0,
+	GPIO2B2_SDMMC0_CMD      = (1 << GPIO2B2_SHIFT),
+
+	GPIO2B1_SHIFT           = 2,
+	GPIO2B1_MASK            = GENMASK(GPIO2B1_SHIFT + 1, GPIO2B1_SHIFT),
+	GPIO2B1_GPIO            = 0,
+	GPIO2B1_SDMMC0_CLKOUT   = (1 << GPIO2B1_SHIFT),
+
+	GPIO2B0_SHIFT           = 0,
+	GPIO2B0_MASK            = GENMASK(GPIO2B0_SHIFT + 1, GPIO2B0_SHIFT),
+	GPIO2B0_GPIO            = 0,
+	GPIO2B0_SDMMC0_D3       = (1 << GPIO2B0_SHIFT),
+};
+
 /*GRF_GPIO2D_IOMUX*/
 enum {
 	GPIO2D7_SHIFT           = 14,
@@ -580,11 +603,17 @@ static void pinctrl_rk3368_sdmmc_config(struct rk3368_grf *grf, int mmc_id)
 			     GPIO2A4_EMMC_CLKOUT);
 		break;
 	case PERIPH_ID_SDCARD:
-		/*
-		 * We assume that the BROM has already set this up
-		 * correctly for us and that there's nothing to do
-		 * here.
-		 */
+		debug("mmc id = %d setting registers!\n", mmc_id);
+		rk_clrsetreg(&grf->gpio2a_iomux,
+			     GPIO2A5_MASK | GPIO2A7_MASK |
+			     GPIO2A7_MASK,
+			     GPIO2A5_SDMMC0_D0 | GPIO2A6_SDMMC0_D1 |
+			     GPIO2A7_SDMMC0_D2);
+		rk_clrsetreg(&grf->gpio2b_iomux,
+			     GPIO2B0_MASK | GPIO2B1_MASK |
+			     GPIO2B2_MASK | GPIO2B3_MASK,
+			     GPIO2B0_SDMMC0_D3 | GPIO2B1_SDMMC0_CLKOUT |
+			     GPIO2B2_SDMMC0_CMD | GPIO2B3_SDMMC0_DTECTN);
 		break;
 	default:
 		debug("mmc id = %d iomux error!\n", mmc_id);
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index 2cfade1cb2..8892fa14e0 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -77,6 +77,13 @@ config DM_REGULATOR_FIXED
 	features for fixed value regulators. The driver implements get/set api
 	for enable and get only for voltage value.
 
+config SPL_DM_REGULATOR_FIXED
+	bool "Enable Driver Model for REGULATOR Fixed value in SPL"
+	depends on DM_REGULATOR_FIXED
+	---help---
+	This config enables implementation of driver-model regulator uclass
+	features for fixed value regulators in SPL.
+
 config DM_REGULATOR_GPIO
 	bool "Enable Driver Model for GPIO REGULATOR"
 	depends on DM_REGULATOR
diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile
index b09d03c2c9..45b5fe7247 100644
--- a/drivers/ram/rockchip/Makefile
+++ b/drivers/ram/rockchip/Makefile
@@ -5,3 +5,8 @@
 #
 
 obj-$(CONFIG_ROCKCHIP_RK3368) = dmc-rk3368.o
+obj-$(CONFIG_ROCKCHIP_RK3188) = sdram_rk3188.o
+obj-$(CONFIG_ROCKCHIP_RK322X) = sdram_rk322x.o
+obj-$(CONFIG_ROCKCHIP_RK3288) = sdram_rk3288.o
+obj-$(CONFIG_ROCKCHIP_RK3328) = sdram_rk3328.o
+obj-$(CONFIG_ROCKCHIP_RK3399) = sdram_rk3399.o
diff --git a/arch/arm/mach-rockchip/rk3188/sdram_rk3188.c b/drivers/ram/rockchip/sdram_rk3188.c
similarity index 99%
rename from arch/arm/mach-rockchip/rk3188/sdram_rk3188.c
rename to drivers/ram/rockchip/sdram_rk3188.c
index 9d8b225dfa..365d00ef54 100644
--- a/arch/arm/mach-rockchip/rk3188/sdram_rk3188.c
+++ b/drivers/ram/rockchip/sdram_rk3188.c
@@ -682,11 +682,18 @@ out:
 
 static int sdram_get_niu_config(struct rk3188_sdram_params *sdram_params)
 {
-	int i, tmp, size, ret = 0;
+	int i, tmp, size, row, ret = 0;
 
+	row = sdram_params->ch[0].cs0_row;
+	/*
+	 * RK3188 share the rank and row bit15, we use same ddr config for 15bit
+	 * and 16bit row
+	 */
+	if (row == 16)
+		row = 15;
 	tmp = sdram_params->ch[0].col - 9;
 	tmp -= (sdram_params->ch[0].bw == 2) ? 0 : 1;
-	tmp |= ((sdram_params->ch[0].cs0_row - 13) << 4);
+	tmp |= ((row - 13) << 4);
 	size = sizeof(ddrconf_table)/sizeof(ddrconf_table[0]);
 	for (i = 0; i < size; i++)
 		if (tmp == ddrconf_table[i])
diff --git a/drivers/ram/rockchip/sdram_rk322x.c b/drivers/ram/rockchip/sdram_rk322x.c
new file mode 100644
index 0000000000..cc3138b21d
--- /dev/null
+++ b/drivers/ram/rockchip/sdram_rk322x.c
@@ -0,0 +1,855 @@
+/*
+ * (C) Copyright 2017 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <errno.h>
+#include <ram.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk322x.h>
+#include <asm/arch/grf_rk322x.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/sdram_rk322x.h>
+#include <asm/arch/timer.h>
+#include <asm/arch/uart.h>
+#include <asm/arch/sdram_common.h>
+#include <asm/types.h>
+#include <linux/err.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+struct chan_info {
+	struct rk322x_ddr_pctl *pctl;
+	struct rk322x_ddr_phy *phy;
+	struct rk322x_service_sys *msch;
+};
+
+struct dram_info {
+	struct chan_info chan[1];
+	struct ram_info info;
+	struct clk ddr_clk;
+	struct rk322x_cru *cru;
+	struct rk322x_grf *grf;
+};
+
+struct rk322x_sdram_params {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+		struct dtd_rockchip_rk3228_dmc of_plat;
+#endif
+		struct rk322x_sdram_channel ch[1];
+		struct rk322x_pctl_timing pctl_timing;
+		struct rk322x_phy_timing phy_timing;
+		struct rk322x_base_params base;
+		int num_channels;
+		struct regmap *map;
+};
+
+#ifdef CONFIG_TPL_BUILD
+/*
+ * [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)
+ */
+const char ddr_cfg_2_rbc[] = {
+	((0 << 6) | (0 << 4) | (0 << 3) | (1 << 2) | 1),
+	((0 << 6) | (1 << 4) | (0 << 3) | (1 << 2) | 1),
+	((0 << 6) | (2 << 4) | (0 << 3) | (1 << 2) | 1),
+	((0 << 6) | (3 << 4) | (0 << 3) | (1 << 2) | 1),
+	((0 << 6) | (1 << 4) | (0 << 3) | (1 << 2) | 2),
+	((0 << 6) | (2 << 4) | (0 << 3) | (1 << 2) | 2),
+	((0 << 6) | (3 << 4) | (0 << 3) | (1 << 2) | 2),
+	((0 << 6) | (0 << 4) | (0 << 3) | (1 << 2) | 0),
+	((0 << 6) | (1 << 4) | (0 << 3) | (1 << 2) | 0),
+	((0 << 6) | (2 << 4) | (0 << 3) | (1 << 2) | 0),
+	((0 << 6) | (3 << 4) | (0 << 3) | (1 << 2) | 0),
+	((0 << 6) | (2 << 4) | (0 << 3) | (0 << 2) | 1),
+	((1 << 6) | (1 << 4) | (0 << 3) | (0 << 2) | 2),
+	((1 << 6) | (1 << 4) | (0 << 3) | (0 << 2) | 1),
+	((0 << 6) | (3 << 4) | (1 << 3) | (1 << 2) | 1),
+	((0 << 6) | (3 << 4) | (1 << 3) | (1 << 2) | 0),
+};
+
+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 rk322x_cru *cru,
+		     struct rk322x_ddr_phy *ddr_phy)
+{
+	rk_clrsetreg(&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_clrreg(&cru->cru_softrst_con[5], 1 << DDRPHY_PSRST_SHIFT |
+						  1 << DDRPHY_SRST_SHIFT);
+	rockchip_udelay(10);
+
+	rk_clrreg(&cru->cru_softrst_con[5], 1 << DDRCTRL_PSRST_SHIFT |
+						  1 << DDRCTRL_SRST_SHIFT);
+	rockchip_udelay(10);
+
+	clrbits_le32(&ddr_phy->ddrphy_reg[0],
+		     SOFT_RESET_MASK << SOFT_RESET_SHIFT);
+	rockchip_udelay(10);
+	setbits_le32(&ddr_phy->ddrphy_reg[0],
+		     SOFT_DERESET_ANALOG);
+	rockchip_udelay(5);
+	setbits_le32(&ddr_phy->ddrphy_reg[0],
+		     SOFT_DERESET_DIGITAL);
+
+	rockchip_udelay(1);
+}
+
+void phy_dll_bypass_set(struct rk322x_ddr_phy *ddr_phy, u32 freq)
+{
+	u32 tmp;
+
+	setbits_le32(&ddr_phy->ddrphy_reg[0x13], 0x10);
+	setbits_le32(&ddr_phy->ddrphy_reg[0x26], 0x10);
+	setbits_le32(&ddr_phy->ddrphy_reg[0x36], 0x10);
+	setbits_le32(&ddr_phy->ddrphy_reg[0x46], 0x10);
+	setbits_le32(&ddr_phy->ddrphy_reg[0x56], 0x10);
+
+	clrbits_le32(&ddr_phy->ddrphy_reg[0x14], 0x8);
+	clrbits_le32(&ddr_phy->ddrphy_reg[0x27], 0x8);
+	clrbits_le32(&ddr_phy->ddrphy_reg[0x37], 0x8);
+	clrbits_le32(&ddr_phy->ddrphy_reg[0x47], 0x8);
+	clrbits_le32(&ddr_phy->ddrphy_reg[0x57], 0x8);
+
+	if (freq <= 400)
+		setbits_le32(&ddr_phy->ddrphy_reg[0xa4], 0x1f);
+	else
+		clrbits_le32(&ddr_phy->ddrphy_reg[0xa4], 0x1f);
+
+	if (freq <= 680)
+		tmp = 3;
+	else
+		tmp = 2;
+
+	writel(tmp, &ddr_phy->ddrphy_reg[0x28]);
+	writel(tmp, &ddr_phy->ddrphy_reg[0x38]);
+	writel(tmp, &ddr_phy->ddrphy_reg[0x48]);
+	writel(tmp, &ddr_phy->ddrphy_reg[0x58]);
+}
+
+static void send_command(struct rk322x_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 chan_info *chan,
+			struct rk322x_sdram_params *sdram_params)
+{
+	struct rk322x_ddr_pctl *pctl = chan->pctl;
+	u32 dramtype = sdram_params->base.dramtype;
+
+	if (dramtype == DDR3) {
+		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 |
+			     (sdram_params->phy_timing.mr[2] & CMD_ADDR_MASK) <<
+			     CMD_ADDR_SHIFT);
+
+		send_command(pctl, 3, MRS_CMD,
+			     (0x03 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT |
+			     (sdram_params->phy_timing.mr[3] & CMD_ADDR_MASK) <<
+			     CMD_ADDR_SHIFT);
+
+		send_command(pctl, 3, MRS_CMD,
+			     (0x01 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT |
+			     (sdram_params->phy_timing.mr[1] & CMD_ADDR_MASK) <<
+			     CMD_ADDR_SHIFT);
+
+		send_command(pctl, 3, MRS_CMD,
+			     (0x00 & BANK_ADDR_MASK) << BANK_ADDR_SHIFT |
+			     ((sdram_params->phy_timing.mr[0] |
+			       DDR3_DLL_RESET) &
+			     CMD_ADDR_MASK) << CMD_ADDR_SHIFT);
+
+		send_command(pctl, 3, ZQCL_CMD, 0);
+	} else {
+		send_command(pctl, 3, MRS_CMD,
+			     (0x63 & LPDDR23_MA_MASK) << LPDDR23_MA_SHIFT |
+			     (0 & LPDDR23_OP_MASK) <<
+			     LPDDR23_OP_SHIFT);
+		rockchip_udelay(10);
+		send_command(pctl, 3, MRS_CMD,
+			     (0x10 & LPDDR23_MA_MASK) << LPDDR23_MA_SHIFT |
+			     (0xff & LPDDR23_OP_MASK) <<
+			     LPDDR23_OP_SHIFT);
+		rockchip_udelay(1);
+		send_command(pctl, 3, MRS_CMD,
+			     (0x10 & LPDDR23_MA_MASK) << LPDDR23_MA_SHIFT |
+			     (0xff & LPDDR23_OP_MASK) <<
+			     LPDDR23_OP_SHIFT);
+		rockchip_udelay(1);
+		send_command(pctl, 3, MRS_CMD,
+			     (1 & LPDDR23_MA_MASK) << LPDDR23_MA_SHIFT |
+			     (sdram_params->phy_timing.mr[1] &
+			      LPDDR23_OP_MASK) << LPDDR23_OP_SHIFT);
+		send_command(pctl, 3, MRS_CMD,
+			     (2 & LPDDR23_MA_MASK) << LPDDR23_MA_SHIFT |
+			     (sdram_params->phy_timing.mr[2] &
+			      LPDDR23_OP_MASK) << LPDDR23_OP_SHIFT);
+		send_command(pctl, 3, MRS_CMD,
+			     (3 & LPDDR23_MA_MASK) << LPDDR23_MA_SHIFT |
+			     (sdram_params->phy_timing.mr[3] &
+			      LPDDR23_OP_MASK) << LPDDR23_OP_SHIFT);
+		if (dramtype == LPDDR3)
+			send_command(pctl, 3, MRS_CMD, (11 & LPDDR23_MA_MASK) <<
+				     LPDDR23_MA_SHIFT |
+				     (sdram_params->phy_timing.mr11 &
+				      LPDDR23_OP_MASK) << LPDDR23_OP_SHIFT);
+	}
+}
+
+static u32 data_training(struct chan_info *chan)
+{
+	struct rk322x_ddr_phy *ddr_phy = chan->phy;
+	struct rk322x_ddr_pctl *pctl = chan->pctl;
+	u32 value;
+	u32 bw = (readl(&ddr_phy->ddrphy_reg[0]) >> 4) & 0xf;
+	u32 ret;
+
+	/* disable auto refresh */
+	value = readl(&pctl->trefi) | (1 << 31);
+	writel(1 << 31, &pctl->trefi);
+
+	clrsetbits_le32(&ddr_phy->ddrphy_reg[2], 0x30,
+			DQS_SQU_CAL_SEL_CS0);
+	setbits_le32(&ddr_phy->ddrphy_reg[2], DQS_SQU_CAL_START);
+
+	rockchip_udelay(30);
+	ret = readl(&ddr_phy->ddrphy_reg[0xff]);
+
+	clrbits_le32(&ddr_phy->ddrphy_reg[2],
+		     DQS_SQU_CAL_START);
+
+	/*
+	 * since data training will take about 20us, so send some auto
+	 * refresh(about 7.8us) to complement the lost time
+	 */
+	send_command(pctl, 3, PREA_CMD, 0);
+	send_command(pctl, 3, REF_CMD, 0);
+
+	writel(value, &pctl->trefi);
+
+	if (ret & 0x10) {
+		ret = -1;
+	} else {
+		ret = (ret & 0xf) ^ bw;
+		ret = (ret == 0) ? 0 : -1;
+	}
+	return ret;
+}
+
+static void move_to_config_state(struct rk322x_ddr_pctl *pctl)
+{
+	unsigned int state;
+
+	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 rk322x_ddr_pctl *pctl)
+{
+	unsigned int state;
+
+	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 move_to_lowpower_state(struct rk322x_ddr_pctl *pctl)
+{
+	unsigned int state;
+
+	while (1) {
+		state = readl(&pctl->stat) & PCTL_STAT_MASK;
+		switch (state) {
+		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:
+			writel(SLEEP_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MASK) !=
+			       LOW_POWER)
+				;
+			break;
+		case LOW_POWER:
+			return;
+		default:
+			break;
+		}
+	}
+}
+
+/* pctl should in low power mode when call this function */
+static void phy_softreset(struct dram_info *dram)
+{
+	struct rk322x_ddr_phy *ddr_phy = dram->chan[0].phy;
+	struct rk322x_grf *grf = dram->grf;
+
+	writel(GRF_DDRPHY_BUFFEREN_CORE_EN, &grf->soc_con[0]);
+	clrbits_le32(&ddr_phy->ddrphy_reg[0], 0x3 << 2);
+	rockchip_udelay(1);
+	setbits_le32(&ddr_phy->ddrphy_reg[0], 1 << 2);
+	rockchip_udelay(5);
+	setbits_le32(&ddr_phy->ddrphy_reg[0], 1 << 3);
+	writel(GRF_DDRPHY_BUFFEREN_CORE_DIS, &grf->soc_con[0]);
+}
+
+/* bw: 2: 32bit, 1:16bit */
+static void set_bw(struct dram_info *dram, u32 bw)
+{
+	struct rk322x_ddr_pctl *pctl = dram->chan[0].pctl;
+	struct rk322x_ddr_phy *ddr_phy = dram->chan[0].phy;
+	struct rk322x_grf *grf = dram->grf;
+
+	if (bw == 1) {
+		setbits_le32(&pctl->ppcfg, 1);
+		clrbits_le32(&ddr_phy->ddrphy_reg[0], 0xc << 4);
+		writel(GRF_MSCH_NOC_16BIT_EN, &grf->soc_con[0]);
+		clrbits_le32(&ddr_phy->ddrphy_reg[0x46], 0x8);
+		clrbits_le32(&ddr_phy->ddrphy_reg[0x56], 0x8);
+	} else {
+		clrbits_le32(&pctl->ppcfg, 1);
+		setbits_le32(&ddr_phy->ddrphy_reg[0], 0xf << 4);
+		writel(GRF_DDR_32BIT_EN | GRF_MSCH_NOC_32BIT_EN,
+		       &grf->soc_con[0]);
+		setbits_le32(&ddr_phy->ddrphy_reg[0x46], 0x8);
+		setbits_le32(&ddr_phy->ddrphy_reg[0x56], 0x8);
+	}
+}
+
+static void pctl_cfg(struct rk322x_ddr_pctl *pctl,
+		     struct rk322x_sdram_params *sdram_params,
+		     struct rk322x_grf *grf)
+{
+	u32 burst_len;
+	u32 bw;
+	u32 dramtype = sdram_params->base.dramtype;
+
+	if (sdram_params->ch[0].bw == 2)
+		bw = GRF_DDR_32BIT_EN | GRF_MSCH_NOC_32BIT_EN;
+	else
+		bw = GRF_MSCH_NOC_16BIT_EN;
+
+	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(0x51010, &pctl->dfilpcfg0);
+
+	writel(1, &pctl->dfitphyupdtype0);
+	writel(0x0d, &pctl->dfitphyrdlat);
+	writel(0, &pctl->dfitphywrdata);
+
+	writel(0, &pctl->dfiupdcfg);
+	copy_to_reg(&pctl->togcnt1u, &sdram_params->pctl_timing.togcnt1u,
+		    sizeof(struct rk322x_pctl_timing));
+	if (dramtype == DDR3) {
+		writel((1 << 3) | (1 << 11),
+		       &pctl->dfiodtcfg);
+		writel(7 << 16, &pctl->dfiodtcfg1);
+		writel((readl(&pctl->tcl) - 1) / 2 - 1, &pctl->dfitrddataen);
+		writel((readl(&pctl->tcwl) - 1) / 2 - 1, &pctl->dfitphywrlat);
+		writel(500, &pctl->trsth);
+		writel(0 << MDDR_LPDDR2_CLK_STOP_IDLE_SHIFT | DDR3_EN |
+		       DDR2_DDR3_BL_8 | (6 - 4) << TFAW_SHIFT | PD_EXIT_SLOW |
+		       1 << PD_TYPE_SHIFT | 0 << PD_IDLE_SHIFT,
+		       &pctl->mcfg);
+		writel(bw | GRF_DDR3_EN, &grf->soc_con[0]);
+	} else {
+		if (sdram_params->phy_timing.bl & PHT_BL_8)
+			burst_len = MDDR_LPDDR2_BL_8;
+		else
+			burst_len = MDDR_LPDDR2_BL_4;
+
+		writel(readl(&pctl->tcl) / 2 - 1, &pctl->dfitrddataen);
+		writel(readl(&pctl->tcwl) / 2 - 1, &pctl->dfitphywrlat);
+		writel(0, &pctl->trsth);
+		if (dramtype == LPDDR2) {
+			writel(0 << MDDR_LPDDR2_CLK_STOP_IDLE_SHIFT |
+			       LPDDR2_S4 | LPDDR2_EN | burst_len |
+			       (6 - 4) << TFAW_SHIFT | PD_EXIT_FAST |
+			       1 << PD_TYPE_SHIFT | 0 << PD_IDLE_SHIFT,
+			       &pctl->mcfg);
+			writel(0, &pctl->dfiodtcfg);
+			writel(0, &pctl->dfiodtcfg1);
+		} else {
+			writel(0 << MDDR_LPDDR2_CLK_STOP_IDLE_SHIFT |
+			       LPDDR2_S4 | LPDDR3_EN | burst_len |
+			       (6 - 4) << TFAW_SHIFT | PD_EXIT_FAST |
+			       1 << PD_TYPE_SHIFT | 0 << PD_IDLE_SHIFT,
+			       &pctl->mcfg);
+			writel((1 << 3) | (1 << 2), &pctl->dfiodtcfg);
+			writel((7 << 16) | 4, &pctl->dfiodtcfg1);
+		}
+		writel(bw | GRF_LPDDR2_3_EN, &grf->soc_con[0]);
+	}
+	setbits_le32(&pctl->scfg, 1);
+}
+
+static void phy_cfg(struct chan_info *chan,
+		    struct rk322x_sdram_params *sdram_params)
+{
+	struct rk322x_ddr_phy *ddr_phy = chan->phy;
+	struct rk322x_service_sys *axi_bus = chan->msch;
+	struct rk322x_msch_timings *noc_timing = &sdram_params->base.noc_timing;
+	struct rk322x_phy_timing *phy_timing = &sdram_params->phy_timing;
+	struct rk322x_pctl_timing *pctl_timing = &sdram_params->pctl_timing;
+	u32 cmd_drv, clk_drv, dqs_drv, dqs_odt;
+
+	writel(noc_timing->ddrtiming, &axi_bus->ddrtiming);
+	writel(noc_timing->ddrmode, &axi_bus->ddrmode);
+	writel(noc_timing->readlatency, &axi_bus->readlatency);
+	writel(noc_timing->activate, &axi_bus->activate);
+	writel(noc_timing->devtodev, &axi_bus->devtodev);
+
+	switch (sdram_params->base.dramtype) {
+	case DDR3:
+		writel(PHY_DDR3 | phy_timing->bl, &ddr_phy->ddrphy_reg[1]);
+		break;
+	case LPDDR2:
+		writel(PHY_LPDDR2 | phy_timing->bl, &ddr_phy->ddrphy_reg[1]);
+		break;
+	default:
+		writel(PHY_LPDDR2 | phy_timing->bl, &ddr_phy->ddrphy_reg[1]);
+		break;
+	}
+
+	writel(phy_timing->cl_al, &ddr_phy->ddrphy_reg[0xb]);
+	writel(pctl_timing->tcwl, &ddr_phy->ddrphy_reg[0xc]);
+
+	cmd_drv = PHY_RON_RTT_34OHM;
+	clk_drv = PHY_RON_RTT_45OHM;
+	dqs_drv = PHY_RON_RTT_34OHM;
+	if (sdram_params->base.dramtype == LPDDR2)
+		dqs_odt = PHY_RON_RTT_DISABLE;
+	else
+		dqs_odt = PHY_RON_RTT_225OHM;
+
+	writel(cmd_drv, &ddr_phy->ddrphy_reg[0x11]);
+	clrsetbits_le32(&ddr_phy->ddrphy_reg[0x12], (0x1f << 3), cmd_drv << 3);
+	writel(clk_drv, &ddr_phy->ddrphy_reg[0x16]);
+	writel(clk_drv, &ddr_phy->ddrphy_reg[0x18]);
+
+	writel(dqs_drv, &ddr_phy->ddrphy_reg[0x20]);
+	writel(dqs_drv, &ddr_phy->ddrphy_reg[0x2f]);
+	writel(dqs_drv, &ddr_phy->ddrphy_reg[0x30]);
+	writel(dqs_drv, &ddr_phy->ddrphy_reg[0x3f]);
+	writel(dqs_drv, &ddr_phy->ddrphy_reg[0x40]);
+	writel(dqs_drv, &ddr_phy->ddrphy_reg[0x4f]);
+	writel(dqs_drv, &ddr_phy->ddrphy_reg[0x50]);
+	writel(dqs_drv, &ddr_phy->ddrphy_reg[0x5f]);
+
+	writel(dqs_odt, &ddr_phy->ddrphy_reg[0x21]);
+	writel(dqs_odt, &ddr_phy->ddrphy_reg[0x2e]);
+	writel(dqs_odt, &ddr_phy->ddrphy_reg[0x31]);
+	writel(dqs_odt, &ddr_phy->ddrphy_reg[0x3e]);
+	writel(dqs_odt, &ddr_phy->ddrphy_reg[0x41]);
+	writel(dqs_odt, &ddr_phy->ddrphy_reg[0x4e]);
+	writel(dqs_odt, &ddr_phy->ddrphy_reg[0x51]);
+	writel(dqs_odt, &ddr_phy->ddrphy_reg[0x5e]);
+}
+
+void dram_cfg_rbc(struct chan_info *chan,
+		  struct rk322x_sdram_params *sdram_params)
+{
+	char noc_config;
+	int i = 0;
+	struct rk322x_sdram_channel *config = &sdram_params->ch[0];
+	struct rk322x_service_sys *axi_bus = chan->msch;
+
+	move_to_config_state(chan->pctl);
+
+	if ((config->rank == 2) && (config->cs1_row == config->cs0_row)) {
+		if ((config->col + config->bw) == 12) {
+			i = 14;
+			goto finish;
+		} else if ((config->col + config->bw) == 11) {
+			i = 15;
+			goto finish;
+		}
+	}
+	noc_config = ((config->cs0_row - 13) << 4) | ((config->bk - 2) << 2) |
+				(config->col + config->bw - 11);
+	for (i = 0; i < 11; i++) {
+		if (noc_config == ddr_cfg_2_rbc[i])
+			break;
+	}
+
+	if (i < 11)
+		goto finish;
+
+	noc_config = ((config->bk - 2) << 6) | ((config->cs0_row - 13) << 4) |
+				(config->col + config->bw - 11);
+
+	for (i = 11; i < 14; i++) {
+		if (noc_config == ddr_cfg_2_rbc[i])
+			break;
+	}
+	if (i < 14)
+		goto finish;
+	else
+		i = 0;
+
+finish:
+	writel(i, &axi_bus->ddrconf);
+	move_to_access_state(chan->pctl);
+}
+
+static void dram_all_config(const struct dram_info *dram,
+			    struct rk322x_sdram_params *sdram_params)
+{
+	struct rk322x_sdram_channel *info = &sdram_params->ch[0];
+	u32 sys_reg = 0;
+
+	sys_reg |= sdram_params->base.dramtype << SYS_REG_DDRTYPE_SHIFT;
+	sys_reg |= (1 - 1) << SYS_REG_NUM_CH_SHIFT;
+	sys_reg |= info->row_3_4 << SYS_REG_ROW_3_4_SHIFT(0);
+	sys_reg |= 1 << SYS_REG_CHINFO_SHIFT(0);
+	sys_reg |= (info->rank - 1) << SYS_REG_RANK_SHIFT(0);
+	sys_reg |= (info->col - 9) << SYS_REG_COL_SHIFT(0);
+	sys_reg |= info->bk == 3 ? 0 : 1 << SYS_REG_BK_SHIFT(0);
+	sys_reg |= (info->cs0_row - 13) << SYS_REG_CS0_ROW_SHIFT(0);
+	sys_reg |= (info->cs1_row - 13) << SYS_REG_CS1_ROW_SHIFT(0);
+	sys_reg |= (2 >> info->bw) << SYS_REG_BW_SHIFT(0);
+	sys_reg |= (2 >> info->dbw) << SYS_REG_DBW_SHIFT(0);
+
+	writel(sys_reg, &dram->grf->os_reg[2]);
+}
+
+#define TEST_PATTEN	0x5aa5f00f
+
+static int dram_cap_detect(struct dram_info *dram,
+			   struct rk322x_sdram_params *sdram_params)
+{
+	u32 bw, row, col, addr;
+	u32 ret = 0;
+	struct rk322x_service_sys *axi_bus = dram->chan[0].msch;
+
+	if (sdram_params->base.dramtype == DDR3)
+		sdram_params->ch[0].dbw = 1;
+	else
+		sdram_params->ch[0].dbw = 2;
+
+	move_to_config_state(dram->chan[0].pctl);
+	/* bw detect */
+	set_bw(dram, 2);
+	if (data_training(&dram->chan[0]) == 0) {
+		bw = 2;
+	} else {
+		bw = 1;
+		set_bw(dram, 1);
+		move_to_lowpower_state(dram->chan[0].pctl);
+		phy_softreset(dram);
+		move_to_config_state(dram->chan[0].pctl);
+		if (data_training(&dram->chan[0])) {
+			printf("BW detect error\n");
+			ret = -EINVAL;
+		}
+	}
+	sdram_params->ch[0].bw = bw;
+	sdram_params->ch[0].bk = 3;
+
+	if (bw == 2)
+		writel(6, &axi_bus->ddrconf);
+	else
+		writel(3, &axi_bus->ddrconf);
+	move_to_access_state(dram->chan[0].pctl);
+	for (col = 11; col >= 9; col--) {
+		writel(0, CONFIG_SYS_SDRAM_BASE);
+		addr = CONFIG_SYS_SDRAM_BASE +
+			(1 << (col + bw - 1));
+		writel(TEST_PATTEN, addr);
+		if ((readl(addr) == TEST_PATTEN) &&
+		    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
+			break;
+	}
+	if (col == 8) {
+		printf("Col detect error\n");
+		ret = -EINVAL;
+		goto out;
+	} else {
+		sdram_params->ch[0].col = col;
+	}
+
+	writel(10, &axi_bus->ddrconf);
+
+	/* Detect row*/
+	for (row = 16; row >= 12; row--) {
+		writel(0, CONFIG_SYS_SDRAM_BASE);
+		addr = CONFIG_SYS_SDRAM_BASE + (1u << (row + 11 + 3 - 1));
+		writel(TEST_PATTEN, addr);
+		if ((readl(addr) == TEST_PATTEN) &&
+		    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
+			break;
+	}
+	if (row == 11) {
+		printf("Row detect error\n");
+		ret = -EINVAL;
+	} else {
+		sdram_params->ch[0].cs1_row = row;
+		sdram_params->ch[0].row_3_4 = 0;
+		sdram_params->ch[0].cs0_row = row;
+	}
+	/* cs detect */
+	writel(0, CONFIG_SYS_SDRAM_BASE);
+	writel(TEST_PATTEN, CONFIG_SYS_SDRAM_BASE + (1u << 30));
+	writel(~TEST_PATTEN, CONFIG_SYS_SDRAM_BASE + (1u << 30) + 4);
+	if ((readl(CONFIG_SYS_SDRAM_BASE + (1u << 30)) == TEST_PATTEN) &&
+	    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
+		sdram_params->ch[0].rank = 2;
+	else
+		sdram_params->ch[0].rank = 1;
+out:
+	return ret;
+}
+
+static int sdram_init(struct dram_info *dram,
+		      struct rk322x_sdram_params *sdram_params)
+{
+	int ret;
+
+	ret = clk_set_rate(&dram->ddr_clk,
+			   sdram_params->base.ddr_freq * MHz * 2);
+	if (ret < 0) {
+		printf("Could not set DDR clock\n");
+		return ret;
+	}
+
+	phy_pctrl_reset(dram->cru, dram->chan[0].phy);
+	phy_dll_bypass_set(dram->chan[0].phy, sdram_params->base.ddr_freq);
+	pctl_cfg(dram->chan[0].pctl, sdram_params, dram->grf);
+	phy_cfg(&dram->chan[0], sdram_params);
+	writel(POWER_UP_START, &dram->chan[0].pctl->powctl);
+	while (!(readl(&dram->chan[0].pctl->powstat) & POWER_UP_DONE))
+		;
+	memory_init(&dram->chan[0], sdram_params);
+	move_to_access_state(dram->chan[0].pctl);
+	ret = dram_cap_detect(dram, sdram_params);
+	if (ret)
+		goto out;
+	dram_cfg_rbc(&dram->chan[0], sdram_params);
+	dram_all_config(dram, sdram_params);
+out:
+	return ret;
+}
+
+static int rk322x_dmc_ofdata_to_platdata(struct udevice *dev)
+{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rk322x_sdram_params *params = dev_get_platdata(dev);
+	const void *blob = gd->fdt_blob;
+	int node = dev_of_offset(dev);
+	int ret;
+
+	params->num_channels = 1;
+
+	ret = fdtdec_get_int_array(blob, node, "rockchip,pctl-timing",
+				   (u32 *)&params->pctl_timing,
+				   sizeof(params->pctl_timing) / sizeof(u32));
+	if (ret) {
+		printf("%s: Cannot read rockchip,pctl-timing\n", __func__);
+		return -EINVAL;
+	}
+	ret = fdtdec_get_int_array(blob, node, "rockchip,phy-timing",
+				   (u32 *)&params->phy_timing,
+				   sizeof(params->phy_timing) / sizeof(u32));
+	if (ret) {
+		printf("%s: Cannot read rockchip,phy-timing\n", __func__);
+		return -EINVAL;
+	}
+	ret = fdtdec_get_int_array(blob, node, "rockchip,sdram-params",
+				   (u32 *)&params->base,
+				   sizeof(params->base) / sizeof(u32));
+	if (ret) {
+		printf("%s: Cannot read rockchip,sdram-params\n", __func__);
+		return -EINVAL;
+	}
+	ret = regmap_init_mem(dev, &params->map);
+	if (ret)
+		return ret;
+#endif
+
+	return 0;
+}
+#endif /* CONFIG_TPL_BUILD */
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+static int conv_of_platdata(struct udevice *dev)
+{
+	struct rk322x_sdram_params *plat = dev_get_platdata(dev);
+	struct dtd_rockchip_rk322x_dmc *of_plat = &plat->of_plat;
+	int ret;
+
+	memcpy(&plat->pctl_timing, of_plat->rockchip_pctl_timing,
+	       sizeof(plat->pctl_timing));
+	memcpy(&plat->phy_timing, of_plat->rockchip_phy_timing,
+	       sizeof(plat->phy_timing));
+	memcpy(&plat->base, of_plat->rockchip_sdram_params, sizeof(plat->base));
+
+	plat->num_channels = 1;
+	ret = regmap_init_mem_platdata(dev, of_plat->reg,
+				       ARRAY_SIZE(of_plat->reg) / 2,
+				       &plat->map);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+#endif
+
+static int rk322x_dmc_probe(struct udevice *dev)
+{
+#ifdef CONFIG_TPL_BUILD
+	struct rk322x_sdram_params *plat = dev_get_platdata(dev);
+	int ret;
+	struct udevice *dev_clk;
+#endif
+	struct dram_info *priv = dev_get_priv(dev);
+
+	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+#ifdef CONFIG_TPL_BUILD
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	ret = conv_of_platdata(dev);
+	if (ret)
+		return ret;
+#endif
+
+	priv->chan[0].msch = syscon_get_first_range(ROCKCHIP_SYSCON_MSCH);
+	priv->chan[0].pctl = regmap_get_range(plat->map, 0);
+	priv->chan[0].phy = regmap_get_range(plat->map, 1);
+	ret = rockchip_get_clk(&dev_clk);
+	if (ret)
+		return ret;
+	priv->ddr_clk.id = CLK_DDR;
+	ret = clk_request(dev_clk, &priv->ddr_clk);
+	if (ret)
+		return ret;
+
+	priv->cru = rockchip_get_cru();
+	if (IS_ERR(priv->cru))
+		return PTR_ERR(priv->cru);
+	ret = sdram_init(priv, plat);
+	if (ret)
+		return ret;
+#else
+	priv->info.base = CONFIG_SYS_SDRAM_BASE;
+	priv->info.size = rockchip_sdram_size(
+			(phys_addr_t)&priv->grf->os_reg[2]);
+#endif
+
+	return 0;
+}
+
+static int rk322x_dmc_get_info(struct udevice *dev, struct ram_info *info)
+{
+	struct dram_info *priv = dev_get_priv(dev);
+
+	*info = priv->info;
+
+	return 0;
+}
+
+static struct ram_ops rk322x_dmc_ops = {
+	.get_info = rk322x_dmc_get_info,
+};
+
+static const struct udevice_id rk322x_dmc_ids[] = {
+	{ .compatible = "rockchip,rk3228-dmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(dmc_rk322x) = {
+	.name = "rockchip_rk322x_dmc",
+	.id = UCLASS_RAM,
+	.of_match = rk322x_dmc_ids,
+	.ops = &rk322x_dmc_ops,
+#ifdef CONFIG_TPL_BUILD
+	.ofdata_to_platdata = rk322x_dmc_ofdata_to_platdata,
+#endif
+	.probe = rk322x_dmc_probe,
+	.priv_auto_alloc_size = sizeof(struct dram_info),
+#ifdef CONFIG_TPL_BUILD
+	.platdata_auto_alloc_size = sizeof(struct rk322x_sdram_params),
+#endif
+};
+
diff --git a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c b/drivers/ram/rockchip/sdram_rk3288.c
similarity index 100%
rename from arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
rename to drivers/ram/rockchip/sdram_rk3288.c
diff --git a/arch/arm/mach-rockchip/rk3328/sdram_rk3328.c b/drivers/ram/rockchip/sdram_rk3328.c
similarity index 100%
rename from arch/arm/mach-rockchip/rk3328/sdram_rk3328.c
rename to drivers/ram/rockchip/sdram_rk3328.c
diff --git a/arch/arm/mach-rockchip/rk3399/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c
similarity index 100%
rename from arch/arm/mach-rockchip/rk3399/sdram_rk3399.c
rename to drivers/ram/rockchip/sdram_rk3399.c
diff --git a/include/configs/evb_rk3399.h b/include/configs/evb_rk3399.h
index 015f25a774..66ead6c117 100644
--- a/include/configs/evb_rk3399.h
+++ b/include/configs/evb_rk3399.h
@@ -10,12 +10,6 @@
 #include <configs/rk3399_common.h>
 
 #define CONFIG_SYS_MMC_ENV_DEV 1
-/*
- * SPL @ 32k for ~36k
- * ENV @ 96k
- * u-boot @ 128K
- */
-#define CONFIG_ENV_OFFSET (96 * 1024)
 
 #define SDRAM_BANK_SIZE			(2UL << 30)
 
diff --git a/include/configs/rk3288_common.h b/include/configs/rk3288_common.h
index e9e3c4013c..34f255847a 100644
--- a/include/configs/rk3288_common.h
+++ b/include/configs/rk3288_common.h
@@ -32,7 +32,11 @@
 #define CONFIG_SYS_INIT_SP_ADDR		0x00100000
 #define CONFIG_SYS_LOAD_ADDR		0x00800800
 #define CONFIG_SPL_STACK		0xff718000
-#define CONFIG_SPL_TEXT_BASE		0xff704004
+#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_TPL_BOOTROM_SUPPORT)
+# define CONFIG_SPL_TEXT_BASE		0x0
+#else
+# define CONFIG_SPL_TEXT_BASE		0xff704004
+#endif
 
 /* MMC/SD IP block */
 #define CONFIG_BOUNCE_BUFFER
diff --git a/include/configs/vyasa-rk3288.h b/include/configs/vyasa-rk3288.h
index 9d6c80f549..8774e42430 100644
--- a/include/configs/vyasa-rk3288.h
+++ b/include/configs/vyasa-rk3288.h
@@ -20,4 +20,21 @@
 #define CONFIG_SYS_MMC_ENV_DEV 1
 #undef CONFIG_CMD_USB_MASS_STORAGE
 
+#ifndef CONFIG_TPL_BUILD
+
+#define CONFIG_SPL_OS_BOOT
+
+/* Falcon Mode */
+#define CONFIG_SPL_FS_LOAD_ARGS_NAME	"args"
+#define CONFIG_SPL_FS_LOAD_KERNEL_NAME	"uImage"
+#define CONFIG_CMD_SPL
+#define CONFIG_SYS_SPL_ARGS_ADDR	0x0ffe5000
+#define CONFIG_CMD_SPL_WRITE_SIZE      (128 * SZ_1K)
+
+/* Falcon Mode - MMC support: args@1MB kernel@2MB */
+#define CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTOR  0x800   /* 1MB */
+#define CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTORS (CONFIG_CMD_SPL_WRITE_SIZE / 512)
+#define CONFIG_SYS_MMCSD_RAW_MODE_KERNEL_SECTOR        0x1000  /* 2MB */
+#endif
+
 #endif
diff --git a/include/dt-bindings/clock/rv1108-cru.h b/include/dt-bindings/clock/rv1108-cru.h
index d2ad3bb52d..7defc6b282 100644
--- a/include/dt-bindings/clock/rv1108-cru.h
+++ b/include/dt-bindings/clock/rv1108-cru.h
@@ -39,6 +39,7 @@
 #define SCLK_MAC_TX			88
 #define SCLK_MACREF			89
 #define SCLK_MACREF_OUT			90
+#define SCLK_SARADC			91
 
 
 /* aclk gates */
@@ -67,6 +68,7 @@
 #define PCLK_TIMER			270
 #define PCLK_PERI			271
 #define PCLK_GMAC			272
+#define PCLK_SARADC			273
 
 /* hclk gates */
 #define HCLK_I2S0_8CH			320
diff --git a/tools/rkcommon.c b/tools/rkcommon.c
index 04e8272fd5..1a24e16589 100644
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -73,6 +73,7 @@ struct spl_info {
 
 static struct spl_info spl_infos[] = {
 	{ "rk3036", "RK30", 0x1000, false, false },
+	{ "rk3128", "RK31", 0x1800, false, false },
 	{ "rk3188", "RK31", 0x8000 - 0x800, true, false },
 	{ "rk322x", "RK32", 0x8000 - 0x1000, false, false },
 	{ "rk3288", "RK32", 0x8000, false, false },