Merge git://git.denx.de/u-boot-rockchip

This includes support for rk3188 from Heiko Stübner and and rk3328 from
Kever Yang.  Also included is SPL support for rk3399 and a fix for
rk3288 to get it booting again (spl_early_init()).
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 231ebfa..d00651c 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -38,6 +38,7 @@
 	rk3288-fennec.dtb \
 	rk3288-tinker.dtb \
 	rk3288-popmetal.dtb \
+	rk3328-evb.dtb \
 	rk3399-evb.dtb
 dtb-$(CONFIG_ARCH_MESON) += \
 	meson-gxbb-odroidc2.dtb
diff --git a/arch/arm/dts/rk3036-sdk.dts b/arch/arm/dts/rk3036-sdk.dts
index bdc7b98..6754625 100644
--- a/arch/arm/dts/rk3036-sdk.dts
+++ b/arch/arm/dts/rk3036-sdk.dts
@@ -51,10 +51,12 @@
 };
 
 &usb_host {
+	vbus-supply = <&vcc5v0_host>;
 	status = "okay";
 };
 
 &usb_otg {
+	vbus-supply = <&vcc5v0_otg>;
 	status = "okay";
 };
 
diff --git a/arch/arm/dts/rk3036.dtsi b/arch/arm/dts/rk3036.dtsi
index ecf5416..4f44217 100644
--- a/arch/arm/dts/rk3036.dtsi
+++ b/arch/arm/dts/rk3036.dtsi
@@ -23,6 +23,7 @@
 		serial1 = &uart1;
 		serial2 = &uart2;
 		mmc0 = &emmc;
+		mmc1 = &sdmmc;
 	};
 
 	memory {
@@ -265,6 +266,18 @@
 		pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
 	};
 
+	sdmmc: dwmmc@10214000 {
+		compatible = "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc";
+		reg = <0x10214000 0x4000>;
+		clock-frequency = <37500000>;
+		max-frequency = <37500000>;
+		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>;
+		clock-names = "biu", "ciu";
+		fifo-depth = <0x100>;
+		interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
 	pinctrl: pinctrl {
 		compatible = "rockchip,rk3036-pinctrl";
 		rockchip,grf = <&grf>;
diff --git a/arch/arm/dts/rk3188.dtsi b/arch/arm/dts/rk3188.dtsi
new file mode 100644
index 0000000..f4d438e
--- /dev/null
+++ b/arch/arm/dts/rk3188.dtsi
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+ or X11
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/clock/rk3188-cru.h>
+#include "rk3xxx.dtsi"
+
+/ {
+	compatible = "rockchip,rk3188";
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		enable-method = "rockchip,rk3066-smp";
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a9";
+			next-level-cache = <&L2>;
+			reg = <0x0>;
+			operating-points = <
+				/* kHz    uV */
+				1608000 1350000
+				1416000 1250000
+				1200000 1150000
+				1008000 1075000
+				 816000  975000
+				 600000  950000
+				 504000  925000
+				 312000  875000
+			>;
+			clock-latency = <40000>;
+			clocks = <&cru ARMCLK>;
+		};
+		cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a9";
+			next-level-cache = <&L2>;
+			reg = <0x1>;
+		};
+		cpu@2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a9";
+			next-level-cache = <&L2>;
+			reg = <0x2>;
+		};
+		cpu@3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a9";
+			next-level-cache = <&L2>;
+			reg = <0x3>;
+		};
+	};
+
+	sram: sram@10080000 {
+		compatible = "mmio-sram";
+		reg = <0x10080000 0x8000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0x10080000 0x8000>;
+
+		smp-sram@0 {
+			compatible = "rockchip,rk3066-smp-sram";
+			reg = <0x0 0x50>;
+		};
+	};
+
+	i2s0: i2s@1011a000 {
+		compatible = "rockchip,rk3188-i2s", "rockchip,rk3066-i2s";
+		reg = <0x1011a000 0x2000>;
+		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2s0_bus>;
+		dmas = <&dmac1_s 6>, <&dmac1_s 7>;
+		dma-names = "tx", "rx";
+		clock-names = "i2s_hclk", "i2s_clk";
+		clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>;
+		rockchip,playback-channels = <2>;
+		rockchip,capture-channels = <2>;
+		status = "disabled";
+	};
+
+	spdif: sound@1011e000 {
+		compatible = "rockchip,rk3188-spdif", "rockchip,rk3066-spdif";
+		reg = <0x1011e000 0x2000>;
+		#sound-dai-cells = <0>;
+		clock-names = "hclk", "mclk";
+		clocks = <&cru HCLK_SPDIF>, <&cru SCLK_SPDIF>;
+		dmas = <&dmac1_s 8>;
+		dma-names = "tx";
+		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spdif_tx>;
+		status = "disabled";
+	};
+
+	cru: clock-controller@20000000 {
+		compatible = "rockchip,rk3188-cru";
+		reg = <0x20000000 0x1000>;
+		rockchip,grf = <&grf>;
+		u-boot,dm-spl;
+
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+	};
+
+	efuse: efuse@20010000 {
+		compatible = "rockchip,rockchip-efuse";
+		reg = <0x20010000 0x4000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		clocks = <&cru PCLK_EFUSE>;
+		clock-names = "pclk_efuse";
+
+		cpu_leakage: cpu_leakage@17 {
+			reg = <0x17 0x1>;
+		};
+	};
+
+	usbphy: phy {
+		compatible = "rockchip,rk3188-usb-phy", "rockchip,rk3288-usb-phy";
+		rockchip,grf = <&grf>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		usbphy0: usb-phy@10c {
+			#phy-cells = <0>;
+			reg = <0x10c>;
+			clocks = <&cru SCLK_OTGPHY0>;
+			clock-names = "phyclk";
+			#clock-cells = <0>;
+		};
+
+		usbphy1: usb-phy@11c {
+			#phy-cells = <0>;
+			reg = <0x11c>;
+			clocks = <&cru SCLK_OTGPHY1>;
+			clock-names = "phyclk";
+			#clock-cells = <0>;
+		};
+	};
+
+	pinctrl: pinctrl {
+		compatible = "rockchip,rk3188-pinctrl";
+		rockchip,grf = <&grf>;
+		rockchip,pmu = <&pmu>;
+
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		u-boot,dm-spl;
+
+		gpio0: gpio0@2000a000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x2000a000 0x100>;
+			interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru PCLK_GPIO0>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio1: gpio1@2003c000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x2003c000 0x100>;
+			interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru PCLK_GPIO1>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio2: gpio2@2003e000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x2003e000 0x100>;
+			interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru PCLK_GPIO2>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio3: gpio3@20080000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x20080000 0x100>;
+			interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru PCLK_GPIO3>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		pcfg_pull_up: pcfg_pull_up {
+			bias-pull-up;
+		};
+
+		pcfg_pull_down: pcfg_pull_down {
+			bias-pull-down;
+		};
+
+		pcfg_pull_none: pcfg_pull_none {
+			bias-disable;
+		};
+
+		emmc {
+			emmc_clk: emmc-clk {
+				rockchip,pins = <RK_GPIO0 24 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			emmc_cmd: emmc-cmd {
+				rockchip,pins = <RK_GPIO0 26 RK_FUNC_2 &pcfg_pull_up>;
+			};
+
+			emmc_rst: emmc-rst {
+				rockchip,pins = <RK_GPIO0 27 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			/*
+			 * The data pins are shared between nandc and emmc and
+			 * not accessible through pinctrl. Also they should've
+			 * been already set correctly by firmware, as
+			 * flash/emmc is the boot-device.
+			 */
+		};
+
+		emac {
+			emac_xfer: emac-xfer {
+				rockchip,pins = <RK_GPIO3 16 RK_FUNC_2 &pcfg_pull_none>, /* tx_en */
+						<RK_GPIO3 17 RK_FUNC_2 &pcfg_pull_none>, /* txd1 */
+						<RK_GPIO3 18 RK_FUNC_2 &pcfg_pull_none>, /* txd0 */
+						<RK_GPIO3 19 RK_FUNC_2 &pcfg_pull_none>, /* rxd0 */
+						<RK_GPIO3 20 RK_FUNC_2 &pcfg_pull_none>, /* rxd1 */
+						<RK_GPIO3 21 RK_FUNC_2 &pcfg_pull_none>, /* mac_clk */
+						<RK_GPIO3 22 RK_FUNC_2 &pcfg_pull_none>, /* rx_err */
+						<RK_GPIO3 23 RK_FUNC_2 &pcfg_pull_none>; /* crs_dvalid */
+			};
+
+			emac_mdio: emac-mdio {
+				rockchip,pins = <RK_GPIO3 24 RK_FUNC_2 &pcfg_pull_none>,
+						<RK_GPIO3 25 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		i2c0 {
+			i2c0_xfer: i2c0-xfer {
+				rockchip,pins = <RK_GPIO1 24 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 25 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2c1 {
+			i2c1_xfer: i2c1-xfer {
+				rockchip,pins = <RK_GPIO1 26 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 27 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2c2 {
+			i2c2_xfer: i2c2-xfer {
+				rockchip,pins = <RK_GPIO1 28 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 29 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2c3 {
+			i2c3_xfer: i2c3-xfer {
+				rockchip,pins = <RK_GPIO3 14 RK_FUNC_2 &pcfg_pull_none>,
+						<RK_GPIO3 15 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		i2c4 {
+			i2c4_xfer: i2c4-xfer {
+				rockchip,pins = <RK_GPIO1 30 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 31 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwm0 {
+			pwm0_out: pwm0-out {
+				rockchip,pins = <RK_GPIO3 27 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwm1 {
+			pwm1_out: pwm1-out {
+				rockchip,pins = <RK_GPIO3 28 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwm2 {
+			pwm2_out: pwm2-out {
+				rockchip,pins = <RK_GPIO3 29 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwm3 {
+			pwm3_out: pwm3-out {
+				rockchip,pins = <RK_GPIO3 30 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		spi0 {
+			spi0_clk: spi0-clk {
+				rockchip,pins = <RK_GPIO1 6 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi0_cs0: spi0-cs0 {
+				rockchip,pins = <RK_GPIO1 7 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi0_tx: spi0-tx {
+				rockchip,pins = <RK_GPIO1 5 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi0_rx: spi0-rx {
+				rockchip,pins = <RK_GPIO1 4 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi0_cs1: spi0-cs1 {
+				rockchip,pins = <RK_GPIO1 15 RK_FUNC_1 &pcfg_pull_up>;
+			};
+		};
+
+		spi1 {
+			spi1_clk: spi1-clk {
+				rockchip,pins = <RK_GPIO0 30 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi1_cs0: spi1-cs0 {
+				rockchip,pins = <RK_GPIO0 31 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi1_rx: spi1-rx {
+				rockchip,pins = <RK_GPIO0 28 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi1_tx: spi1-tx {
+				rockchip,pins = <RK_GPIO0 29 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi1_cs1: spi1-cs1 {
+				rockchip,pins = <RK_GPIO1 14 RK_FUNC_2 &pcfg_pull_up>;
+			};
+		};
+
+		uart0 {
+			uart0_xfer: uart0-xfer {
+				rockchip,pins = <RK_GPIO1 0 RK_FUNC_1 &pcfg_pull_up>,
+						<RK_GPIO1 1 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart0_cts: uart0-cts {
+				rockchip,pins = <RK_GPIO1 2 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart0_rts: uart0-rts {
+				rockchip,pins = <RK_GPIO1 3 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		uart1 {
+			uart1_xfer: uart1-xfer {
+				rockchip,pins = <RK_GPIO1 4 RK_FUNC_1 &pcfg_pull_up>,
+						<RK_GPIO1 5 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart1_cts: uart1-cts {
+				rockchip,pins = <RK_GPIO1 6 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart1_rts: uart1-rts {
+				rockchip,pins = <RK_GPIO1 7 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		uart2 {
+			uart2_xfer: uart2-xfer {
+				rockchip,pins = <RK_GPIO1 8 RK_FUNC_1 &pcfg_pull_up>,
+						<RK_GPIO1 9 RK_FUNC_1 &pcfg_pull_none>;
+			};
+			/* no rts / cts for uart2 */
+		};
+
+		uart3 {
+			uart3_xfer: uart3-xfer {
+				rockchip,pins = <RK_GPIO1 10 RK_FUNC_1 &pcfg_pull_up>,
+						<RK_GPIO1 11 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart3_cts: uart3-cts {
+				rockchip,pins = <RK_GPIO1 12 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart3_rts: uart3-rts {
+				rockchip,pins = <RK_GPIO1 13 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		sd0 {
+			sd0_clk: sd0-clk {
+				rockchip,pins = <RK_GPIO3 2 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd0_cmd: sd0-cmd {
+				rockchip,pins = <RK_GPIO3 3 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd0_cd: sd0-cd {
+				rockchip,pins = <RK_GPIO3 8 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd0_wp: sd0-wp {
+				rockchip,pins = <RK_GPIO3 9 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd0_pwr: sd0-pwr {
+				rockchip,pins = <RK_GPIO3 1 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd0_bus1: sd0-bus-width1 {
+				rockchip,pins = <RK_GPIO3 4 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd0_bus4: sd0-bus-width4 {
+				rockchip,pins = <RK_GPIO3 4 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO3 5 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO3 6 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO3 7 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		sd1 {
+			sd1_clk: sd1-clk {
+				rockchip,pins = <RK_GPIO3 21 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd1_cmd: sd1-cmd {
+				rockchip,pins = <RK_GPIO3 16 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd1_cd: sd1-cd {
+				rockchip,pins = <RK_GPIO3 22 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd1_wp: sd1-wp {
+				rockchip,pins = <RK_GPIO3 23 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd1_bus1: sd1-bus-width1 {
+				rockchip,pins = <RK_GPIO3 17 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			sd1_bus4: sd1-bus-width4 {
+				rockchip,pins = <RK_GPIO3 17 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO3 18 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO3 19 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO3 20 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2s0 {
+			i2s0_bus: i2s0-bus {
+				rockchip,pins = <RK_GPIO1 16 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 17 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 18 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 19 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 20 RK_FUNC_1 &pcfg_pull_none>,
+						<RK_GPIO1 21 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		spdif {
+			spdif_tx: spdif-tx {
+				rockchip,pins = <RK_GPIO1 14 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+	};
+};
+
+&emac {
+	compatible = "rockchip,rk3188-emac";
+};
+
+&global_timer {
+	interrupts = <GIC_PPI 11 0xf04>;
+};
+
+&grf {
+	compatible = "rockchip,rk3188-grf", "syscon";
+};
+
+&local_timer {
+	interrupts = <GIC_PPI 13 0xf04>;
+};
+
+&i2c0 {
+	compatible = "rockchip,rk3188-i2c";
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c0_xfer>;
+};
+
+&i2c1 {
+	compatible = "rockchip,rk3188-i2c";
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_xfer>;
+};
+
+&i2c2 {
+	compatible = "rockchip,rk3188-i2c";
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c2_xfer>;
+};
+
+&i2c3 {
+	compatible = "rockchip,rk3188-i2c";
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c3_xfer>;
+};
+
+&i2c4 {
+	compatible = "rockchip,rk3188-i2c";
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c4_xfer>;
+};
+
+&pmu {
+	compatible = "rockchip,rk3188-pmu", "syscon";
+};
+
+&pwm0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm0_out>;
+};
+
+&pwm1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm1_out>;
+};
+
+&pwm2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm2_out>;
+};
+
+&pwm3 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm3_out>;
+};
+
+&spi0 {
+	compatible = "rockchip,rk3188-spi", "rockchip,rk3066-spi";
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;
+};
+
+&spi1 {
+	compatible = "rockchip,rk3188-spi", "rockchip,rk3066-spi";
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi1_clk &spi1_tx &spi1_rx &spi1_cs0>;
+};
+
+&uart0 {
+	compatible = "rockchip,rk3188-uart", "snps,dw-apb-uart";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_xfer>;
+};
+
+&uart1 {
+	compatible = "rockchip,rk3188-uart", "snps,dw-apb-uart";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_xfer>;
+};
+
+&uart2 {
+	compatible = "rockchip,rk3188-uart", "snps,dw-apb-uart";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart2_xfer>;
+};
+
+&uart3 {
+	compatible = "rockchip,rk3188-uart", "snps,dw-apb-uart";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart3_xfer>;
+};
+
+&wdt {
+	compatible = "rockchip,rk3188-wdt", "snps,dw-wdt";
+};
diff --git a/arch/arm/dts/rk3288-evb.dtsi b/arch/arm/dts/rk3288-evb.dtsi
index cb7d03e..4960f33 100644
--- a/arch/arm/dts/rk3288-evb.dtsi
+++ b/arch/arm/dts/rk3288-evb.dtsi
@@ -11,6 +11,13 @@
 		reg = <0 0x80000000>;
 	};
 
+	ext_gmac: external-gmac-clock {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <125000000>;
+		clock-output-names = "ext_gmac";
+	};
+
 	keys: gpio-keys {
 		compatible = "gpio-keys";
 		#address-cells = <1>;
@@ -98,6 +105,21 @@
 	status = "okay";
 };
 
+&gmac {
+	phy-mode = "rgmii";
+	clock_in_out = "input";
+	snps,reset-gpio = <&gpio4 7 0>;
+	snps,reset-active-low;
+	snps,reset-delays-us = <0 10000 1000000>;
+	assigned-clocks = <&cru SCLK_MAC>;
+	assigned-clock-parents = <&ext_gmac>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_pins>;
+	tx_delay = <0x30>;
+	rx_delay = <0x10>;
+	status = "okay";
+};
+
 &hdmi {
 	ddc-i2c-bus = <&i2c5>;
 	status = "okay";
diff --git a/arch/arm/dts/rk3288-popmetal.dtsi b/arch/arm/dts/rk3288-popmetal.dtsi
index e5be4cb..dd6ce8b 100644
--- a/arch/arm/dts/rk3288-popmetal.dtsi
+++ b/arch/arm/dts/rk3288-popmetal.dtsi
@@ -203,7 +203,7 @@
 	pinctrl-0 = <&rgmii_pins>;
 	tx_delay = <0x30>;
 	rx_delay = <0x10>;
-	status = "ok";
+	status = "okay";
 };
 
 &hdmi {
diff --git a/arch/arm/dts/rk3288-tinker.dts b/arch/arm/dts/rk3288-tinker.dts
index c0550970..22881cb 100644
--- a/arch/arm/dts/rk3288-tinker.dts
+++ b/arch/arm/dts/rk3288-tinker.dts
@@ -30,6 +30,12 @@
 
 &pinctrl {
 	u-boot,dm-pre-reloc;
+
+	usb {
+		host_vbus_drv: host-vbus-drv {
+			rockchip,pins = <0 14 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
 };
 
 &pwm1 {
@@ -41,6 +47,11 @@
 	reg-shift = <2>;
 };
 
+&usb_host1 {
+	vbus-supply = <&vcc5v0_host>;
+	status = "okay";
+};
+
 &sdmmc {
 	u-boot,dm-pre-reloc;
 };
diff --git a/arch/arm/dts/rk3288-tinker.dtsi b/arch/arm/dts/rk3288-tinker.dtsi
index ceb4e2b..a752458 100644
--- a/arch/arm/dts/rk3288-tinker.dtsi
+++ b/arch/arm/dts/rk3288-tinker.dtsi
@@ -110,6 +110,18 @@
 		startup-delay-us = <100000>;
 		vin-supply = <&vcc_io>;
 	};
+
+	vcc5v0_host: usb-host-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 14 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&host_vbus_drv>;
+		regulator-name = "vcc5v0_host";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+	};
 };
 
 &cpu0 {
@@ -149,7 +161,7 @@
 	pinctrl-0 = <&rgmii_pins>;
 	tx_delay = <0x30>;
 	rx_delay = <0x10>;
-	status = "ok";
+	status = "okay";
 };
 
 &hdmi {
diff --git a/arch/arm/dts/rk3328-evb.dts b/arch/arm/dts/rk3328-evb.dts
new file mode 100644
index 0000000..01794ed
--- /dev/null
+++ b/arch/arm/dts/rk3328-evb.dts
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+/dts-v1/;
+#include "rk3328.dtsi"
+
+/ {
+	model = "Rockchip RK3328 EVB";
+	compatible = "rockchip,rk3328-evb", "rockchip,rk3328";
+
+	chosen {
+		stdout-path = &uart2;
+	};
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&sdmmc {
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	card-detect-delay = <200>;
+	disable-wp;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc0_clk>, <&sdmmc0_cmd>, <&sdmmc0_dectn>, <&sdmmc0_bus4>;
+	status = "okay";
+};
+
+&emmc {
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	supports-emmc;
+	disable-wp;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
+	status = "okay";
+};
diff --git a/arch/arm/dts/rk3328.dtsi b/arch/arm/dts/rk3328.dtsi
new file mode 100644
index 0000000..8a98ee3
--- /dev/null
+++ b/arch/arm/dts/rk3328.dtsi
@@ -0,0 +1,1477 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <dt-bindings/clock/rk3328-cru.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+
+/ {
+	compatible = "rockchip,rk3328";
+
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+		i2c0 = &i2c0;
+		i2c1 = &i2c1;
+		i2c2 = &i2c2;
+		i2c3 = &i2c3;
+	};
+
+	cpus {
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53", "arm,armv8";
+			reg = <0x0 0x0>;
+			enable-method = "psci";
+//			clocks = <&cru ARMCLK>;
+			operating-points-v2 = <&cpu0_opp_table>;
+		};
+		cpu1: cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53", "arm,armv8";
+			reg = <0x0 0x1>;
+			enable-method = "psci";
+		};
+		cpu2: cpu@2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53", "arm,armv8";
+			reg = <0x0 0x2>;
+			enable-method = "psci";
+		};
+		cpu3: cpu@3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53", "arm,armv8";
+			reg = <0x0 0x3>;
+			enable-method = "psci";
+		};
+	};
+
+	cpu0_opp_table: opp_table0 {
+		compatible = "operating-points-v2";
+		opp-shared;
+
+		opp@408000000 {
+			opp-hz = /bits/ 64 <408000000>;
+			opp-microvolt = <950000>;
+			clock-latency-ns = <40000>;
+			opp-suspend;
+		};
+		opp@600000000 {
+			opp-hz = /bits/ 64 <600000000>;
+			opp-microvolt = <950000>;
+			clock-latency-ns = <40000>;
+		};
+		opp@816000000 {
+			opp-hz = /bits/ 64 <816000000>;
+			opp-microvolt = <1000000>;
+			clock-latency-ns = <40000>;
+		};
+		opp@1008000000 {
+			opp-hz = /bits/ 64 <1008000000>;
+			opp-microvolt = <1100000>;
+			clock-latency-ns = <40000>;
+		};
+		opp@1200000000 {
+			opp-hz = /bits/ 64 <1200000000>;
+			opp-microvolt = <1225000>;
+			clock-latency-ns = <40000>;
+		};
+		opp@1296000000 {
+			opp-hz = /bits/ 64 <1296000000>;
+			opp-microvolt = <1300000>;
+			clock-latency-ns = <40000>;
+		};
+	};
+
+	arm-pmu {
+		compatible = "arm,cortex-a53-pmu";
+		interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>;
+	};
+
+	psci {
+		compatible = "arm,psci-1.0";
+		method = "smc";
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
+	};
+
+	xin24m: xin24m {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <24000000>;
+		clock-output-names = "xin24m";
+	};
+
+	i2s0: i2s@ff000000 {
+		compatible = "rockchip,rk3328-i2s", "rockchip,rk3066-i2s";
+		reg = <0x0 0xff000000 0x0 0x1000>;
+		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_I2S0>, <&cru HCLK_I2S0_8CH>;
+		clock-names = "i2s_clk", "i2s_hclk";
+		dmas = <&dmac 11>, <&dmac 12>;
+		#dma-cells = <2>;
+		dma-names = "tx", "rx";
+		status = "disabled";
+	};
+
+	i2s1: i2s@ff010000 {
+		compatible = "rockchip,rk3328-i2s", "rockchip,rk3066-i2s";
+		reg = <0x0 0xff010000 0x0 0x1000>;
+		interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_I2S1>, <&cru HCLK_I2S1_8CH>;
+		clock-names = "i2s_clk", "i2s_hclk";
+		dmas = <&dmac 14>, <&dmac 15>;
+		#dma-cells = <2>;
+		dma-names = "tx", "rx";
+		status = "disabled";
+	};
+
+	i2s2: i2s@ff020000 {
+		compatible = "rockchip,rk3328-i2s", "rockchip,rk3066-i2s";
+		reg = <0x0 0xff020000 0x0 0x1000>;
+		interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_I2S2>, <&cru HCLK_I2S2_2CH>;
+		clock-names = "i2s_clk", "i2s_hclk";
+		dmas = <&dmac 0>, <&dmac 1>;
+		#dma-cells = <2>;
+		dma-names = "tx", "rx";
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&i2s2m0_mclk
+			     &i2s2m0_sclk
+			     &i2s2m0_lrcktx
+			     &i2s2m0_lrckrx
+			     &i2s2m0_sdo
+			     &i2s2m0_sdi>;
+		pinctrl-1 = <&i2s2m0_sleep>;
+		status = "disabled";
+	};
+
+	spdif: spdif@ff030000 {
+		compatible = "rockchip,rk3328-spdif";
+		reg = <0x0 0xff030000 0x0 0x1000>;
+		interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_SPDIF>, <&cru HCLK_SPDIF_8CH>;
+		clock-names = "mclk", "hclk";
+		dmas = <&dmac 10>;
+		#dma-cells = <1>;
+		dma-names = "tx";
+		pinctrl-names = "default";
+		pinctrl-0 = <&spdifm2_tx>;
+		status = "disabled";
+	};
+
+	grf: syscon@ff100000 {
+		compatible = "rockchip,rk3328-grf", "syscon", "simple-mfd";
+		reg = <0x0 0xff100000 0x0 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		io_domains: io-domains {
+			compatible = "rockchip,rk3328-io-voltage-domain";
+			status = "disabled";
+		};
+	};
+
+	uart0: serial@ff110000 {
+		compatible = "rockchip,rk3328-uart", "snps,dw-apb-uart";
+		reg = <0x0 0xff110000 0x0 0x100>;
+		interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>;
+		clock-names = "baudclk", "apb_pclk";
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		dmas = <&dmac 2>, <&dmac 3>;
+		#dma-cells = <2>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>;
+		status = "disabled";
+	};
+
+	uart1: serial@ff120000 {
+		compatible = "rockchip,rk3328-uart", "snps,dw-apb-uart";
+		reg = <0x0 0xff120000 0x0 0x100>;
+		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>;
+		clock-names = "sclk_uart", "pclk_uart";
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		dmas = <&dmac 4>, <&dmac 5>;
+		#dma-cells = <2>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart1_xfer &uart1_cts &uart1_rts>;
+		status = "disabled";
+	};
+
+	uart2: serial@ff130000 {
+		compatible = "rockchip,rk3328-uart", "snps,dw-apb-uart";
+		reg = <0x0 0xff130000 0x0 0x100>;
+		interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>;
+		clock-names = "baudclk", "apb_pclk";
+		clock-frequency = <24000000>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		dmas = <&dmac 6>, <&dmac 7>;
+		#dma-cells = <2>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart2m1_xfer>;
+		status = "disabled";
+	};
+
+	pmu: power-management@ff140000 {
+		compatible = "rockchip,rk3328-pmu", "syscon", "simple-mfd";
+		reg = <0x0 0xff140000 0x0 0x1000>;
+	};
+
+	i2c0: i2c@ff150000 {
+		compatible = "rockchip,rk3328-i2c";
+		reg = <0x0 0xff150000 0x0 0x1000>;
+		interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&cru SCLK_I2C0>, <&cru PCLK_I2C0>;
+		clock-names = "i2c", "pclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2c0_xfer>;
+		status = "disabled";
+	};
+
+	i2c1: i2c@ff160000 {
+		compatible = "rockchip,rk3328-i2c";
+		reg = <0x0 0xff160000 0x0 0x1000>;
+		interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&cru SCLK_I2C1>, <&cru PCLK_I2C1>;
+		clock-names = "i2c", "pclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2c1_xfer>;
+		status = "disabled";
+	};
+
+	i2c2: i2c@ff170000 {
+		compatible = "rockchip,rk3328-i2c";
+		reg = <0x0 0xff170000 0x0 0x1000>;
+		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&cru SCLK_I2C2>, <&cru PCLK_I2C2>;
+		clock-names = "i2c", "pclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2c2_xfer>;
+		status = "disabled";
+	};
+
+	i2c3: i2c@ff180000 {
+		compatible = "rockchip,rk3328-i2c";
+		reg = <0x0 0xff180000 0x0 0x1000>;
+		interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&cru SCLK_I2C3>, <&cru PCLK_I2C3>;
+		clock-names = "i2c", "pclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2c3_xfer>;
+		status = "disabled";
+	};
+
+	spi0: spi@ff190000 {
+		compatible = "rockchip,rk3328-spi", "rockchip,rk3066-spi";
+		reg = <0x0 0xff190000 0x0 0x1000>;
+		interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&cru SCLK_SPI>, <&cru PCLK_SPI>;
+		clock-names = "spiclk", "apb_pclk";
+		dmas = <&dmac 8>, <&dmac 9>;
+		#dma-cells = <2>;
+		dma-names = "tx", "rx";
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi0m2_clk &spi0m2_tx &spi0m2_rx &spi0m2_cs0>;
+		status = "disabled";
+	};
+
+	wdt: watchdog@ff1a0000 {
+		compatible = "snps,dw-wdt";
+		reg = <0x0 0xff1a0000 0x0 0x100>;
+		interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
+	amba {
+		compatible = "simple-bus";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		dmac: dmac@ff1f0000 {
+			compatible = "arm,pl330", "arm,primecell";
+			reg = <0x0 0xff1f0000 0x0 0x4000>;
+			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru ACLK_DMAC>;
+			clock-names = "apb_pclk";
+			#dma-cells = <1>;
+		};
+	};
+
+	saradc: saradc@ff280000 {
+		compatible = "rockchip,rk3328-saradc", "rockchip,saradc";
+		reg = <0x0 0xff280000 0x0 0x100>;
+		interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
+		#io-channel-cells = <1>;
+		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
+		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC_P>;
+		reset-names = "saradc-apb";
+		status = "disabled";
+	};
+
+	cru: clock-controller@ff440000 {
+		compatible = "rockchip,rk3328-cru", "rockchip,cru", "syscon";
+		reg = <0x0 0xff440000 0x0 0x1000>;
+		rockchip,grf = <&grf>;
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+		assigned-clocks =
+			<&cru DCLK_LCDC>, <&cru SCLK_PDM>,
+			<&cru SCLK_RTC32K>, <&cru SCLK_UART0>,
+			<&cru SCLK_UART1>, <&cru SCLK_UART2>,
+			<&cru ACLK_BUS_PRE>, <&cru ACLK_PERI_PRE>,
+			<&cru ACLK_VIO_PRE>, <&cru ACLK_RGA_PRE>,
+			<&cru ACLK_VOP_PRE>, <&cru ACLK_RKVDEC_PRE>,
+			<&cru ACLK_RKVENC>, <&cru ACLK_VPU_PRE>,
+			<&cru SCLK_VDEC_CABAC>, <&cru SCLK_VDEC_CORE>,
+			<&cru SCLK_VENC_CORE>, <&cru SCLK_VENC_DSP>,
+			<&cru SCLK_SDIO>, <&cru SCLK_TSP>,
+			<&cru SCLK_WIFI>, <&cru ARMCLK>,
+			<&cru PLL_GPLL>, <&cru PLL_CPLL>,
+			<&cru ACLK_BUS_PRE>, <&cru HCLK_BUS_PRE>,
+			<&cru PCLK_BUS_PRE>, <&cru ACLK_PERI_PRE>,
+			<&cru HCLK_PERI>, <&cru PCLK_PERI>,
+			<&cru ACLK_VIO_PRE>, <&cru HCLK_VIO_PRE>,
+			<&cru ACLK_RGA_PRE>, <&cru SCLK_RGA>,
+			<&cru ACLK_VOP_PRE>, <&cru ACLK_RKVDEC_PRE>,
+			<&cru ACLK_RKVENC>, <&cru ACLK_VPU_PRE>,
+			<&cru SCLK_VDEC_CABAC>, <&cru SCLK_VDEC_CORE>,
+			<&cru SCLK_VENC_CORE>, <&cru SCLK_VENC_DSP>,
+			<&cru SCLK_EFUSE>, <&cru PCLK_DDR>,
+			<&cru ACLK_GMAC>, <&cru PCLK_GMAC>,
+			<&cru SCLK_RTC32K>, <&cru SCLK_USB3OTG_SUSPEND>;
+		assigned-clock-parents =
+			<&cru HDMIPHY>, <&cru PLL_APLL>,
+			<&cru PLL_GPLL>, <&xin24m>,
+			<&xin24m>, <&xin24m>;
+		assigned-clock-rates =
+			<0>, <61440000>,
+			<0>, <24000000>,
+			<24000000>, <24000000>,
+			<15000000>, <15000000>,
+			<100000000>, <100000000>,
+			<100000000>, <100000000>,
+			<50000000>, <100000000>,
+			<100000000>, <100000000>,
+			<50000000>, <50000000>,
+			<50000000>, <50000000>,
+			<24000000>, <600000000>,
+			<491520000>, <1200000000>,
+			<150000000>, <75000000>,
+			<75000000>, <150000000>,
+			<75000000>, <75000000>,
+			<300000000>, <100000000>,
+			<300000000>, <200000000>,
+			<400000000>, <500000000>,
+			<200000000>, <300000000>,
+			<300000000>, <250000000>,
+			<200000000>, <100000000>,
+			<24000000>, <100000000>,
+			<150000000>, <50000000>,
+			<32768>, <32768>;
+	};
+
+	sdmmc: rksdmmc@ff500000 {
+		compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
+		reg = <0x0 0xff500000 0x0 0x4000>;
+		clock-freq-min-max = <400000 150000000>;
+		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>;
+		clock-names = "biu", "ciu";
+		fifo-depth = <0x100>;
+		interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
+	sdio: dwmmc@ff510000 {
+		compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
+		reg = <0x0 0xff510000 0x0 0x4000>;
+		clock-freq-min-max = <400000 150000000>;
+		clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>,
+			 <&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>;
+		clock-names = "biu", "ciu", "ciu_drv", "ciu_sample";
+		fifo-depth = <0x100>;
+		interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
+	emmc: rksdmmc@ff520000 {
+		compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
+		reg = <0x0 0xff520000 0x0 0x4000>;
+		clock-freq-min-max = <400000 150000000>;
+		clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>;
+		clock-names = "biu", "ciu";
+		fifo-depth = <0x100>;
+		interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
+	sdmmc_ext: rksdmmc@ff5f0000 {
+		compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
+		reg = <0x0 0xff5f0000 0x0 0x4000>;
+		clock-freq-min-max = <400000 150000000>;
+		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>;
+		clock-names = "biu", "ciu";
+		fifo-depth = <0x100>;
+		interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
+	gic: interrupt-controller@ffb70000 {
+		compatible = "arm,gic-400";
+		#interrupt-cells = <3>;
+		#address-cells = <0>;
+		interrupt-controller;
+		reg = <0x0 0xff811000 0 0x1000>,
+		      <0x0 0xff812000 0 0x2000>,
+		      <0x0 0xff814000 0 0x2000>,
+		      <0x0 0xff816000 0 0x2000>;
+		interrupts = <GIC_PPI 9
+		      (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
+	};
+
+	pinctrl: pinctrl {
+		compatible = "rockchip,rk3328-pinctrl";
+		rockchip,grf = <&grf>;
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		gpio0: gpio0@ff210000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff210000 0x0 0x100>;
+			interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru PCLK_GPIO0>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio1: gpio1@ff220000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff220000 0x0 0x100>;
+			interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru PCLK_GPIO1>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio2: gpio2@ff230000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff230000 0x0 0x100>;
+			interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru PCLK_GPIO2>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio3: gpio3@ff240000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff240000 0x0 0x100>;
+			interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cru PCLK_GPIO3>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		pcfg_pull_up: pcfg-pull-up {
+			bias-pull-up;
+		};
+
+		pcfg_pull_down: pcfg-pull-down {
+			bias-pull-down;
+		};
+
+		pcfg_pull_none: pcfg-pull-none {
+			bias-disable;
+		};
+
+		pcfg_pull_none_2ma: pcfg-pull-none-2ma {
+			bias-disable;
+			drive-strength = <2>;
+		};
+
+		pcfg_pull_up_2ma: pcfg-pull-up-2ma {
+			bias-pull-up;
+			drive-strength = <2>;
+		};
+
+		pcfg_pull_up_4ma: pcfg-pull-up-4ma {
+			bias-pull-up;
+			drive-strength = <4>;
+		};
+
+		pcfg_pull_none_4ma: pcfg-pull-none-4ma {
+			bias-disable;
+			drive-strength = <4>;
+		};
+
+		pcfg_pull_down_4ma: pcfg-pull-down-4ma {
+			bias-pull-down;
+			drive-strength = <4>;
+		};
+
+		pcfg_pull_none_8ma: pcfg-pull-none-8ma {
+			bias-disable;
+			drive-strength = <8>;
+		};
+
+		pcfg_pull_up_8ma: pcfg-pull-up-8ma {
+			bias-pull-up;
+			drive-strength = <8>;
+		};
+
+		pcfg_pull_none_12ma: pcfg-pull-none-12ma {
+			bias-disable;
+			drive-strength = <12>;
+		};
+
+		pcfg_pull_up_12ma: pcfg-pull-up-12ma {
+			bias-pull-up;
+			drive-strength = <12>;
+		};
+
+		pcfg_output_high: pcfg-output-high {
+			output-high;
+		};
+
+		pcfg_output_low: pcfg-output-low {
+			output-low;
+		};
+
+		pcfg_input_high: pcfg-input-high {
+			bias-pull-up;
+			input-enable;
+		};
+
+		pcfg_input: pcfg-input {
+			input-enable;
+		};
+
+		i2c0 {
+			i2c0_xfer: i2c0-xfer {
+				rockchip,pins =
+					<2 24 RK_FUNC_1 &pcfg_pull_none>,
+					<2 25 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2c1 {
+			i2c1_xfer: i2c1-xfer {
+				rockchip,pins =
+					<2 4 RK_FUNC_2 &pcfg_pull_none>,
+					<2 5 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		i2c2 {
+			i2c2_xfer: i2c2-xfer {
+				rockchip,pins =
+					<2 13 RK_FUNC_1 &pcfg_pull_none>,
+					<2 14 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2c3 {
+			i2c3_xfer: i2c3-xfer {
+				rockchip,pins =
+					<0 5 RK_FUNC_2 &pcfg_pull_none>,
+					<0 6 RK_FUNC_2 &pcfg_pull_none>;
+			};
+			i2c3_gpio: i2c3-gpio {
+				rockchip,pins =
+					<0 5 RK_FUNC_GPIO &pcfg_pull_none>,
+					<0 6 RK_FUNC_GPIO &pcfg_pull_none>;
+			};
+		};
+
+		hdmi_i2c {
+			hdmii2c_xfer: hdmii2c-xfer {
+				rockchip,pins =
+					<0 5 RK_FUNC_1 &pcfg_pull_none>,
+					<0 6 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		uart0 {
+			uart0_xfer: uart0-xfer {
+				rockchip,pins =
+					<1 9 RK_FUNC_1 &pcfg_pull_up>,
+					<1 8 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart0_cts: uart0-cts {
+				rockchip,pins =
+					<1 11 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart0_rts: uart0-rts {
+				rockchip,pins =
+					<1 10 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart0_rts_gpio: uart0-rts-gpio {
+				rockchip,pins =
+					<1 10 RK_FUNC_GPIO &pcfg_pull_none>;
+			};
+		};
+
+		uart1 {
+			uart1_xfer: uart1-xfer {
+				rockchip,pins =
+					<3 4 RK_FUNC_4 &pcfg_pull_up>,
+					<3 6 RK_FUNC_4 &pcfg_pull_none>;
+			};
+
+			uart1_cts: uart1-cts {
+				rockchip,pins =
+					<3 7 RK_FUNC_4 &pcfg_pull_none>;
+			};
+
+			uart1_rts: uart1-rts {
+				rockchip,pins =
+					<3 5 RK_FUNC_4 &pcfg_pull_none>;
+			};
+
+			uart1_rts_gpio: uart1-rts-gpio {
+				rockchip,pins =
+					<3 5 RK_FUNC_GPIO &pcfg_pull_none>;
+			};
+		};
+
+		uart2-0 {
+			uart2m0_xfer: uart2m0-xfer {
+				rockchip,pins =
+					<1 0 RK_FUNC_2 &pcfg_pull_up>,
+					<1 1 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		uart2-1 {
+			uart2m1_xfer: uart2m1-xfer {
+				rockchip,pins =
+					<2 0 RK_FUNC_1 &pcfg_pull_up>,
+					<2 1 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		spi0-0 {
+			spi0m0_clk: spi0m0-clk {
+				rockchip,pins =
+					<2 8 RK_FUNC_1 &pcfg_pull_up>;
+			};
+
+			spi0m0_cs0: spi0m0-cs0 {
+				rockchip,pins =
+					<2 11 RK_FUNC_1 &pcfg_pull_up>;
+			};
+
+			spi0m0_tx: spi0m0-tx {
+				rockchip,pins =
+					<2 9 RK_FUNC_1 &pcfg_pull_up>;
+			};
+
+			spi0m0_rx: spi0m0-rx {
+				rockchip,pins =
+					<2 10 RK_FUNC_1 &pcfg_pull_up>;
+			};
+
+			spi0m0_cs1: spi0m0-cs1 {
+				rockchip,pins =
+					<2 12 RK_FUNC_1 &pcfg_pull_up>;
+			};
+		};
+
+		spi0-1 {
+			spi0m1_clk: spi0m1-clk {
+				rockchip,pins =
+					<3 23 RK_FUNC_2 &pcfg_pull_up>;
+			};
+
+			spi0m1_cs0: spi0m1-cs0 {
+				rockchip,pins =
+					<3 26 RK_FUNC_2 &pcfg_pull_up>;
+			};
+
+			spi0m1_tx: spi0m1-tx {
+				rockchip,pins =
+					<3 25 RK_FUNC_2 &pcfg_pull_up>;
+			};
+
+			spi0m1_rx: spi0m1-rx {
+				rockchip,pins =
+					<3 24 RK_FUNC_2 &pcfg_pull_up>;
+			};
+
+			spi0m1_cs1: spi0m1-cs1 {
+				rockchip,pins =
+					<3 27 RK_FUNC_2 &pcfg_pull_up>;
+			};
+		};
+
+		spi0-2 {
+			spi0m2_clk: spi0m2-clk {
+				rockchip,pins =
+					<3 0 RK_FUNC_4 &pcfg_pull_up>;
+			};
+
+			spi0m2_cs0: spi0m2-cs0 {
+				rockchip,pins =
+					<3 8 RK_FUNC_3 &pcfg_pull_up>;
+			};
+
+			spi0m2_tx: spi0m2-tx {
+				rockchip,pins =
+					<3 1 RK_FUNC_4 &pcfg_pull_up>;
+			};
+
+			spi0m2_rx: spi0m2-rx {
+				rockchip,pins =
+					<3 2 RK_FUNC_4 &pcfg_pull_up>;
+			};
+		};
+
+		i2s1 {
+			i2s1_mclk: i2s1-mclk {
+				rockchip,pins =
+					<2 15 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_sclk: i2s1-sclk {
+				rockchip,pins =
+					<2 18 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_lrckrx: i2s1-lrckrx {
+				rockchip,pins =
+					<2 16 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_lrcktx: i2s1-lrcktx {
+				rockchip,pins =
+					<2 17 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_sdi: i2s1-sdi {
+				rockchip,pins =
+					<2 19 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_sdo: i2s1-sdo {
+				rockchip,pins =
+					<2 23 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_sdio1: i2s1-sdio1 {
+				rockchip,pins =
+					<2 20 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_sdio2: i2s1-sdio2 {
+				rockchip,pins =
+					<2 21 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_sdio3: i2s1-sdio3 {
+				rockchip,pins =
+					<2 22 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s1_sleep: i2s1-sleep {
+				rockchip,pins =
+					<2 15 RK_FUNC_GPIO &pcfg_input_high>,
+					<2 16 RK_FUNC_GPIO &pcfg_input_high>,
+					<2 17 RK_FUNC_GPIO &pcfg_input_high>,
+					<2 18 RK_FUNC_GPIO &pcfg_input_high>,
+					<2 19 RK_FUNC_GPIO &pcfg_input_high>,
+					<2 20 RK_FUNC_GPIO &pcfg_input_high>,
+					<2 21 RK_FUNC_GPIO &pcfg_input_high>,
+					<2 22 RK_FUNC_GPIO &pcfg_input_high>,
+					<2 23 RK_FUNC_GPIO &pcfg_input_high>;
+			};
+		};
+
+		i2s2-0 {
+			i2s2m0_mclk: i2s2m0-mclk {
+				rockchip,pins =
+					<1 21 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s2m0_sclk: i2s2m0-sclk {
+				rockchip,pins =
+					<1 22 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s2m0_lrckrx: i2s2m0-lrckrx {
+				rockchip,pins =
+					<1 26 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s2m0_lrcktx: i2s2m0-lrcktx {
+				rockchip,pins =
+					<1 23 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s2m0_sdi: i2s2m0-sdi {
+				rockchip,pins =
+					<1 24 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s2m0_sdo: i2s2m0-sdo {
+				rockchip,pins =
+					<1 25 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s2m0_sleep: i2s2m0-sleep {
+				rockchip,pins =
+					<1 21 RK_FUNC_GPIO &pcfg_input_high>,
+					<1 22 RK_FUNC_GPIO &pcfg_input_high>,
+					<1 26 RK_FUNC_GPIO &pcfg_input_high>,
+					<1 23 RK_FUNC_GPIO &pcfg_input_high>,
+					<1 24 RK_FUNC_GPIO &pcfg_input_high>,
+					<1 25 RK_FUNC_GPIO &pcfg_input_high>;
+			};
+		};
+
+		i2s2-1 {
+			i2s2m1_mclk: i2s2m1-mclk {
+				rockchip,pins =
+					<1 21 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			i2s2m1_sclk: i2s2m1-sclk {
+				rockchip,pins =
+					<3 0 RK_FUNC_6 &pcfg_pull_none>;
+			};
+
+			i2s2m1_lrckrx: i2sm1-lrckrx {
+				rockchip,pins =
+					<3 8 RK_FUNC_6 &pcfg_pull_none>;
+			};
+
+			i2s2m1_lrcktx: i2s2m1-lrcktx {
+				rockchip,pins =
+					<3 8 RK_FUNC_4 &pcfg_pull_none>;
+			};
+
+			i2s2m1_sdi: i2s2m1-sdi {
+				rockchip,pins =
+					<3 2 RK_FUNC_6 &pcfg_pull_none>;
+			};
+
+			i2s2m1_sdo: i2s2m1-sdo {
+				rockchip,pins =
+					<3 1 RK_FUNC_6 &pcfg_pull_none>;
+			};
+
+			i2s2m1_sleep: i2s2m1-sleep {
+				rockchip,pins =
+					<1 21 RK_FUNC_GPIO &pcfg_input_high>,
+					<3 0 RK_FUNC_GPIO &pcfg_input_high>,
+					<3 8 RK_FUNC_GPIO &pcfg_input_high>,
+					<3 2 RK_FUNC_GPIO &pcfg_input_high>,
+					<3 1 RK_FUNC_GPIO &pcfg_input_high>;
+			};
+		};
+
+		spdif-0 {
+			spdifm0_tx: spdifm0-tx {
+				rockchip,pins =
+					<0 27 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		spdif-1 {
+			spdifm1_tx: spdifm1-tx {
+				rockchip,pins =
+					<2 17 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		spdif-2 {
+			spdifm2_tx: spdifm2-tx {
+				rockchip,pins =
+					<0 2 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		sdmmc0-0 {
+			sdmmc0m0_pwren: sdmmc0m0-pwren {
+				rockchip,pins =
+					<2 7 RK_FUNC_1 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0m0_gpio: sdmmc0m0-gpio {
+				rockchip,pins =
+					<2 7 RK_FUNC_GPIO &pcfg_pull_up_4ma>;
+			};
+		};
+
+		sdmmc0-1 {
+			sdmmc0m1_pwren: sdmmc0m1-pwren {
+				rockchip,pins =
+					<0 30 RK_FUNC_3 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0m1_gpio: sdmmc0m1-gpio {
+				rockchip,pins =
+					<0 30 RK_FUNC_GPIO &pcfg_pull_up_4ma>;
+			};
+		};
+
+		sdmmc0 {
+			sdmmc0_clk: sdmmc0-clk {
+				rockchip,pins =
+					<1 6 RK_FUNC_1 &pcfg_pull_none_4ma>;
+			};
+
+			sdmmc0_cmd: sdmmc0-cmd {
+				rockchip,pins =
+					<1 4 RK_FUNC_1 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0_dectn: sdmmc0-dectn {
+				rockchip,pins =
+					<1 5 RK_FUNC_1 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0_wrprt: sdmmc0-wrprt {
+				rockchip,pins =
+					<1 7 RK_FUNC_1 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0_bus1: sdmmc0-bus1 {
+				rockchip,pins =
+					<1 0 RK_FUNC_1 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0_bus4: sdmmc0-bus4 {
+				rockchip,pins =
+					<1 0 RK_FUNC_1 &pcfg_pull_up_4ma>,
+					<1 1 RK_FUNC_1 &pcfg_pull_up_4ma>,
+					<1 2 RK_FUNC_1 &pcfg_pull_up_4ma>,
+					<1 3 RK_FUNC_1 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0_gpio: sdmmc0-gpio {
+				rockchip,pins =
+					<1 6 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 4 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 5 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 7 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 3 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 2 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 1 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 0 RK_FUNC_GPIO &pcfg_pull_up_4ma>;
+			};
+		};
+
+		sdmmc0ext {
+			sdmmc0ext_clk: sdmmc0ext-clk {
+				rockchip,pins =
+					<3 2 RK_FUNC_3 &pcfg_pull_none_4ma>;
+			};
+
+			sdmmc0ext_cmd: sdmmc0ext-cmd {
+				rockchip,pins =
+					<3 0 RK_FUNC_3 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0ext_wrprt: sdmmc0ext-wrprt {
+				rockchip,pins =
+					<3 3 RK_FUNC_3 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0ext_dectn: sdmmc0ext-dectn {
+				rockchip,pins =
+					<3 1 RK_FUNC_3 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0ext_bus1: sdmmc0ext-bus1 {
+				rockchip,pins =
+					<3 4 RK_FUNC_3 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0ext_bus4: sdmmc0ext-bus4 {
+				rockchip,pins =
+					<3 4 RK_FUNC_3 &pcfg_pull_up_4ma>,
+					<3 5 RK_FUNC_3 &pcfg_pull_up_4ma>,
+					<3 6 RK_FUNC_3 &pcfg_pull_up_4ma>,
+					<3 7 RK_FUNC_3 &pcfg_pull_up_4ma>;
+			};
+
+			sdmmc0ext_gpio: sdmmc0ext-gpio {
+				rockchip,pins =
+					<3 0 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<3 1 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<3 2 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<3 3 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<3 4 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<3 5 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<3 6 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<3 7 RK_FUNC_GPIO &pcfg_pull_up_4ma>;
+			};
+		};
+
+		sdmmc1 {
+			sdmmc1_clk: sdmmc1-clk {
+				rockchip,pins =
+					<1 12 RK_FUNC_1 &pcfg_pull_none_8ma>;
+			};
+
+			sdmmc1_cmd: sdmmc1-cmd {
+				rockchip,pins =
+					<1 13 RK_FUNC_1 &pcfg_pull_up_8ma>;
+			};
+
+			sdmmc1_pwren: sdmmc1-pwren {
+				rockchip,pins =
+					<1 18 RK_FUNC_1 &pcfg_pull_up_8ma>;
+			};
+
+			sdmmc1_wrprt: sdmmc1-wrprt {
+				rockchip,pins =
+					<1 20 RK_FUNC_1 &pcfg_pull_up_8ma>;
+			};
+
+			sdmmc1_dectn: sdmmc1-dectn {
+				rockchip,pins =
+					<1 19 RK_FUNC_1 &pcfg_pull_up_8ma>;
+			};
+
+			sdmmc1_bus1: sdmmc1-bus1 {
+				rockchip,pins =
+					<1 14 RK_FUNC_1 &pcfg_pull_up_8ma>;
+			};
+
+			sdmmc1_bus4: sdmmc1-bus4 {
+				rockchip,pins =
+					<1 12 RK_FUNC_1 &pcfg_pull_up_8ma>,
+					<1 13 RK_FUNC_1 &pcfg_pull_up_8ma>,
+					<1 16 RK_FUNC_1 &pcfg_pull_up_8ma>,
+					<1 17 RK_FUNC_1 &pcfg_pull_up_8ma>;
+			};
+
+			sdmmc1_gpio: sdmmc1-gpio {
+				rockchip,pins =
+					<1 12 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 13 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 14 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 15 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 16 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 17 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 18 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 19 RK_FUNC_GPIO &pcfg_pull_up_4ma>,
+					<1 20 RK_FUNC_GPIO &pcfg_pull_up_4ma>;
+			};
+		};
+
+		emmc {
+			emmc_clk: emmc-clk {
+				rockchip,pins =
+					<3 21 RK_FUNC_2 &pcfg_pull_none_12ma>;
+			};
+
+			emmc_cmd: emmc-cmd {
+				rockchip,pins =
+					<3 19 RK_FUNC_2 &pcfg_pull_up_12ma>;
+			};
+
+			emmc_pwren: emmc-pwren {
+				rockchip,pins =
+					<3 22 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			emmc_rstnout: emmc-rstnout {
+				rockchip,pins =
+					<3 20 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			emmc_bus1: emmc-bus1 {
+				rockchip,pins =
+					<0 7 RK_FUNC_2 &pcfg_pull_up_12ma>;
+			};
+
+			emmc_bus4: emmc-bus4 {
+				rockchip,pins =
+					<0 7 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<2 28 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<2 29 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<2 30 RK_FUNC_2 &pcfg_pull_up_12ma>;
+			};
+
+			emmc_bus8: emmc-bus8 {
+				rockchip,pins =
+					<0 7 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<2 28 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<2 29 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<2 30 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<2 31 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<3 16 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<3 17 RK_FUNC_2 &pcfg_pull_up_12ma>,
+					<3 18 RK_FUNC_2 &pcfg_pull_up_12ma>;
+			};
+		};
+
+		pwm0 {
+			pwm0_pin: pwm0-pin {
+				rockchip,pins =
+					<2 4 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwm1 {
+			pwm1_pin: pwm1-pin {
+				rockchip,pins =
+					<2 5 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwm2 {
+			pwm2_pin: pwm2-pin {
+				rockchip,pins =
+					<2 6 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwmir {
+			pwmir_pin: pwmir-pin {
+				rockchip,pins =
+					<2 2 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		gmac-0 {
+			rgmiim0_pins: rgmiim0-pins {
+				rockchip,pins =
+					/* mac_txclk */
+					<0 8 RK_FUNC_1 &pcfg_pull_none_12ma>,
+					/* mac_rxclk */
+					<0 10 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_mdio */
+					<0 11 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txen */
+					<0 12 RK_FUNC_1 &pcfg_pull_none_12ma>,
+					/* mac_clk */
+					<0 24 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxdv */
+					<0 25 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_mdc */
+					<0 19 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd1 */
+					<0 14 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd0 */
+					<0 15 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd1 */
+					<0 16 RK_FUNC_1 &pcfg_pull_none_12ma>,
+					/* mac_txd0 */
+					<0 17 RK_FUNC_1 &pcfg_pull_none_12ma>,
+					/* mac_rxd3 */
+					<0 20 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd2 */
+					<0 21 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd3 */
+					<0 23 RK_FUNC_1 &pcfg_pull_none_12ma>,
+					/* mac_txd2 */
+					<0 22 RK_FUNC_1 &pcfg_pull_none_12ma>;
+			};
+
+			rmiim0_pins: rmiim0-pins {
+				rockchip,pins =
+					/* mac_mdio */
+					<0 11 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txen */
+					<0 12 RK_FUNC_1 &pcfg_pull_none_12ma>,
+					/* mac_clk */
+					<0 24 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxer */
+					<0 13 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxdv */
+					<0 25 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_mdc */
+					<0 19 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd1 */
+					<0 14 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd0 */
+					<0 15 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd1 */
+					<0 16 RK_FUNC_1 &pcfg_pull_none_12ma>,
+					/* mac_txd0 */
+					<0 17 RK_FUNC_1 &pcfg_pull_none_12ma>;
+			};
+		};
+
+		gmac-1 {
+			rgmiim1_pins: rgmiim1-pins {
+				rockchip,pins =
+					/* mac_txclk */
+					<1 12 RK_FUNC_2 &pcfg_pull_none_12ma>,
+					/* mac_rxclk */
+					<1 13 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_mdio */
+					<1 19 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_txen */
+					<1 25 RK_FUNC_2 &pcfg_pull_none_12ma>,
+					/* mac_clk */
+					<1 21 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_rxdv */
+					<1 22 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_mdc */
+					<1 23 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_rxd1 */
+					<1 10 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_rxd0 */
+					<1 11 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_txd1 */
+					<1 8 RK_FUNC_2 &pcfg_pull_none_12ma>,
+					/* mac_txd0 */
+					<1 9 RK_FUNC_2 &pcfg_pull_none_12ma>,
+					/* mac_rxd3 */
+					<1 14 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_rxd2 */
+					<1 15 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_txd3 */
+					<1 16 RK_FUNC_2 &pcfg_pull_none_12ma>,
+					/* mac_txd2 */
+					<1 17 RK_FUNC_2 &pcfg_pull_none_12ma>,
+
+					/* mac_txclk */
+					<0 8 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txen */
+					<0 12 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_clk */
+					<0 24 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd1 */
+					<0 16 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd0 */
+					<0 17 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd3 */
+					<0 23 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd2 */
+					<0 22 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			rmiim1_pins: rmiim1-pins {
+				rockchip,pins =
+					/* mac_mdio */
+					<1 19 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_txen */
+					<1 25 RK_FUNC_2 &pcfg_pull_none_12ma>,
+					/* mac_clk */
+					<1 21 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_rxer */
+					<1 24 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_rxdv */
+					<1 22 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_mdc */
+					<1 23 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_rxd1 */
+					<1 10 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_rxd0 */
+					<1 11 RK_FUNC_2 &pcfg_pull_none_2ma>,
+					/* mac_txd1 */
+					<1 8 RK_FUNC_2 &pcfg_pull_none_12ma>,
+					/* mac_txd0 */
+					<1 9 RK_FUNC_2 &pcfg_pull_none_12ma>,
+
+					/* mac_mdio */
+					<0 11 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txen */
+					<0 12 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_clk */
+					<0 24 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_mdc */
+					<0 19 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd1 */
+					<0 16 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd0 */
+					<0 17 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		gmac2phy {
+			fephyled_speed100: fephyled-speed100 {
+				rockchip,pins =
+					<0 31 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			fephyled_speed10: fephyled-speed10 {
+				rockchip,pins =
+					<0 30 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			fephyled_duplex: fephyled-duplex {
+				rockchip,pins =
+					<0 30 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			fephyled_rxm0: fephyled-rxm0 {
+				rockchip,pins =
+					<0 29 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			fephyled_txm0: fephyled-txm0 {
+				rockchip,pins =
+					<0 29 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			fephyled_linkm0: fephyled-linkm0 {
+				rockchip,pins =
+					<0 28 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			fephyled_rxm1: fephyled-rxm1 {
+				rockchip,pins =
+					<2 25 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			fephyled_txm1: fephyled-txm1 {
+				rockchip,pins =
+					<2 25 RK_FUNC_3 &pcfg_pull_none>;
+			};
+
+			fephyled_linkm1: fephyled-linkm1 {
+				rockchip,pins =
+					<2 24 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		tsadc_pin {
+			tsadc_int: tsadc-int {
+				rockchip,pins =
+					<2 13 RK_FUNC_2 &pcfg_pull_none>;
+			};
+			tsadc_gpio: tsadc-gpio {
+				rockchip,pins =
+					<2 13 RK_FUNC_GPIO &pcfg_pull_none>;
+			};
+		};
+
+		hdmi_pin {
+			hdmi_cec: hdmi-cec {
+				rockchip,pins =
+					<0 3 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			hdmi_hpd: hdmi-hpd {
+				rockchip,pins =
+					<0 4 RK_FUNC_1 &pcfg_pull_down>;
+			};
+		};
+
+		cif-0 {
+			dvp_d2d9_m0:dvp-d2d9-m0 {
+				rockchip,pins =
+					/* cif_d0 */
+					<3 4 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d1 */
+					<3 5 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d2 */
+					<3 6 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d3 */
+					<3 7 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d4 */
+					<3 8 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d5m0 */
+					<3 9 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d6m0 */
+					<3 10 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d7m0 */
+					<3 11 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_href */
+					<3 1 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_vsync */
+					<3 0 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_clkoutm0 */
+					<3 3 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_clkin */
+					<3 2 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		cif-1 {
+			dvp_d2d9_m1:dvp-d2d9-m1 {
+				rockchip,pins =
+					/* cif_d0 */
+					<3 4 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d1 */
+					<3 5 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d2 */
+					<3 6 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d3 */
+					<3 7 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d4 */
+					<3 8 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_d5m1 */
+					<2 16 RK_FUNC_4 &pcfg_pull_none>,
+					/* cif_d6m1 */
+					<2 17 RK_FUNC_4 &pcfg_pull_none>,
+					/* cif_d7m1 */
+					<2 18 RK_FUNC_4 &pcfg_pull_none>,
+					/* cif_href */
+					<3 1 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_vsync */
+					<3 0 RK_FUNC_2 &pcfg_pull_none>,
+					/* cif_clkoutm1 */
+					<2 15 RK_FUNC_4 &pcfg_pull_none>,
+					/* cif_clkin */
+					<3 2 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+	};
+};
diff --git a/arch/arm/dts/rk3399-evb.dts b/arch/arm/dts/rk3399-evb.dts
index fa60e19..a959989 100644
--- a/arch/arm/dts/rk3399-evb.dts
+++ b/arch/arm/dts/rk3399-evb.dts
@@ -7,6 +7,7 @@
 /dts-v1/;
 #include <dt-bindings/pwm/pwm.h>
 #include "rk3399.dtsi"
+#include "rk3399-sdram-lpddr3-4GB-1600.dtsi"
 
 / {
 	model = "Rockchip RK3399 Evaluation Board";
@@ -69,6 +70,7 @@
 };
 
 &sdmmc {
+	bus-width = <4>;
 	status = "okay";
 };
 
diff --git a/arch/arm/dts/rk3399-sdram-lpddr3-4GB-1600.dtsi b/arch/arm/dts/rk3399-sdram-lpddr3-4GB-1600.dtsi
new file mode 100644
index 0000000..65dfc38
--- /dev/null
+++ b/arch/arm/dts/rk3399-sdram-lpddr3-4GB-1600.dtsi
@@ -0,0 +1,1536 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+&dmc {
+	rockchip,sdram-params = <
+		0x2
+		0xa
+		0x3
+		0x2
+		0x2
+		0x0
+		0xf
+		0xf
+		1
+		0x1d191519
+		0x14040808
+		0x00000002
+		0x00006226
+		0x00000054
+		0x00000000
+		0x2
+		0xa
+		0x3
+		0x2
+		0x2
+		0x0
+		0xf
+		0xf
+		1
+		0x1d191519
+		0x14040808
+		0x00000002
+		0x00006226
+		0x00000054
+		0x00000000
+		800
+		6
+		2
+		13
+		1
+		0x00000700
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000050
+		0x00027100
+		0x00000320
+		0x00001f40
+		0x00000050
+		0x00027100
+		0x00000320
+		0x00001f40
+		0x00000050
+		0x00027100
+		0x00000320
+		0x01001f40
+		0x00000000
+		0x00000101
+		0x00020100
+		0x000000a0
+		0x00000190
+		0x00000000
+		0x06180000
+		0x00061800
+		0x04000618
+		0x33080004
+		0x280f0622
+		0x22330800
+		0x00280f06
+		0x06223308
+		0x0600280f
+		0x00000a0a
+		0x0600dac0
+		0x0a0a060c
+		0x0600dac0
+		0x0a0a060c
+		0x0600dac0
+		0x0203000c
+		0x0f0c0f00
+		0x040c0f0c
+		0x14000a0a
+		0x03030a0a
+		0x00010003
+		0x031b1b1b
+		0x00111111
+		0x00000000
+		0x03010000
+		0x0c2800a8
+		0x0c2800a8
+		0x0c2800a8
+		0x00000000
+		0x00060006
+		0x00140006
+		0x00140014
+		0x000f0f0f
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00b00000
+		0x00b000b0
+		0x00b000b0
+		0x000000b0
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000301
+		0x00000001
+		0x00000000
+		0x00000000
+		0x01000000
+		0x80104002
+		0x00040003
+		0x00040005
+		0x00030000
+		0x00050004
+		0x00000004
+		0x00040003
+		0x00040005
+		0x30a00000
+		0x00001850
+		0x185030a0
+		0x30a00000
+		0x00001850
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x02020200
+		0x00020202
+		0x00030200
+		0x00040700
+		0x00000302
+		0x02000407
+		0x00000003
+		0x00030f04
+		0x00070004
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00010000
+		0x20040020
+		0x00200400
+		0x01000400
+		0x00000b80
+		0x00000000
+		0x00000001
+		0x00000002
+		0x0000000e
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00a00000
+		0x00c80050
+		0x00c80000
+		0x005000a0
+		0x000000c8
+		0x00a000c8
+		0x00c80050
+		0x00c80000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00430000
+		0x0000001a
+		0x001a0043
+		0x00430000
+		0x0000001a
+		0x00010001
+		0x07000001
+		0x00000707
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00430000
+		0x0000001a
+		0x001a0043
+		0x00430000
+		0x0000001a
+		0x00010001
+		0x07000001
+		0x00000707
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x01000000
+		0x00000000
+		0x00000000
+		0x18151100
+		0x0000000c
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00032003
+		0x00480120
+		0x00000000
+		0x01200320
+		0x00000048
+		0x00032000
+		0x00480120
+		0x00000000
+		0x00280000
+		0x00280028
+		0x01010100
+		0x01000202
+		0x0a000002
+		0x01000f0f
+		0x00000000
+		0x00000000
+		0x00010003
+		0x00000c03
+		0x00000100
+		0x00010000
+		0x01000000
+		0x00010000
+		0x00000001
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00010000
+		0x03030301
+		0x01010808
+		0x03030001
+		0x0a0a0a03
+		0x02080808
+		0x02050103
+		0x02050103
+		0x00050103
+		0x00020202
+		0x05020500
+		0x00020502
+		0x00000000
+		0x00000000
+		0x0d000001
+		0x00010028
+		0x00010000
+		0x00000003
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00010100
+		0x01000000
+		0x00000001
+		0x00000303
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x000556aa
+		0x000aaaaa
+		0x000aa955
+		0x00055555
+		0x000b3133
+		0x0004cd33
+		0x0004cecc
+		0x000b32cc
+		0x00010300
+		0x03000100
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00ffff00
+		0x1e1e0000
+		0x0800001e
+		0x00001850
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00001850
+		0x0000f320
+		0x1850050a
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00001850
+		0x0000f320
+		0x1850050a
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00001850
+		0x0000f320
+		0x0202050a
+		0x03030202
+		0x00000018
+		0x00000000
+		0x00000000
+		0x00001403
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00030000
+		0x000e0020
+		0x000e0020
+		0x000e0020
+		0x00000000
+		0x00000000
+		0x01000000
+		0x00070007
+		0x00050007
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x01000101
+		0x01010101
+		0x01000101
+		0x01000100
+		0x00010001
+		0x00010002
+		0x00020100
+		0x00000002
+		0x00000700
+		0x00000000
+		0x000030a0
+		0x00001850
+		0x000030a0
+		0x00001850
+		0x000030a0
+		0x18501850
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00001850
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00001850
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00000200
+		0x00010000
+		0x00000007
+		0x81000001
+		0x0f0003f0
+		0x3fffffff
+		0x0f0000a0
+		0x377ff000
+		0x0f000020
+		0x377ff000
+		0x0f000030
+		0x377ff000
+		0x0f0000b0
+		0x377ff000
+		0x0f000100
+		0x377ff000
+		0x0f000110
+		0x377ff000
+		0x0f000010
+		0x377ff000
+		0x03000101
+		0x042e2e2e
+		0x06180006
+		0x00061800
+		0x00000018
+		0x0c2800a8
+		0x0c2800a8
+		0x0c2800a8
+		0x00000500
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x04040000
+		0x0d000004
+		0x00000128
+		0x00000000
+		0x00030003
+		0x00000018
+		0x00000000
+		0x00000000
+		0x03060002
+		0x03010301
+		0x01080801
+		0x04020201
+		0x01080804
+		0x00000000
+		0x03030000
+		0x0a0a0a03
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00030300
+		0x00000014
+		0x00000000
+		0x01010300
+		0x00000000
+		0x00000000
+		0x01000000
+		0x00000101
+		0x55555a5a
+		0x55555a5a
+		0x55555a5a
+		0x55555a5a
+		0x0a0a0001
+		0x0505000a
+		0x00000005
+		0x00000100
+		0x00030000
+		0x17030000
+		0x000e0020
+		0x000e0020
+		0x000e0020
+		0x00000000
+		0x00000000
+		0x00000100
+		0x140a0000
+		0x000a030a
+		0x03000a03
+		0x010a000a
+		0x00000100
+		0x01000000
+		0x00000000
+		0x00000100
+		0x1e1a0000
+		0x10010204
+		0x07070705
+		0x20000202
+		0x00201000
+		0x00201000
+		0x04041000
+		0x10100100
+		0x00010110
+		0x004b004a
+		0x1a030000
+		0x0102041e
+		0x34000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00004300
+		0x0001001a
+		0x004d4d07
+		0x001a0043
+		0x4d070001
+		0x0000434d
+		0x0001001a
+		0x004d4d07
+		0x001a0043
+		0x4d070001
+		0x0000434d
+		0x0001001a
+		0x004d4d07
+		0x001a0043
+		0x4d070001
+		0x0043004d
+		0x0001001a
+		0x004d4d07
+		0x001a0043
+		0x4d070001
+		0x0000434d
+		0x0001001a
+		0x004d4d07
+		0x001a0043
+		0x4d070001
+		0x0000434d
+		0x0001001a
+		0x004d4d07
+		0x001a0043
+		0x4d070001
+		0x0100004d
+		0x00c800c8
+		0x060400c8
+		0x0c060f11
+		0x2200d890
+		0x0a0c2005
+		0x0f11060a
+		0x00000c06
+		0x2200d890
+		0x0a0c2005
+		0x0f11060a
+		0x00000c06
+		0x2200d890
+		0x0a0c2005
+		0x0200020a
+		0x02000200
+		0x02000200
+		0x02000200
+		0x02000200
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x01000300
+		0x00185000
+		0x0000f320
+		0x00001850
+		0x0000f320
+		0x00001850
+		0x0000f320
+		0x08000000
+		0x00000100
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000002
+		0x76543210
+		0x0004c008
+		0x000000b3
+		0x00000000
+		0x00000000
+		0x00010000
+		0x01665555
+		0x00665555
+		0x00010f00
+		0x05010200
+		0x00000003
+		0x001700c0
+		0x00cc0101
+		0x00030066
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x04080000
+		0x04080400
+		0x08000000
+		0x0c00c007
+		0x00000100
+		0x00000100
+		0x55555555
+		0xaaaaaaaa
+		0x55555555
+		0xaaaaaaaa
+		0x00005555
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00200000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x02700270
+		0x02700270
+		0x02700270
+		0x02700270
+		0x00000270
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00800000
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00b30080
+		0x00000003
+		0x00000000
+		0x00020000
+		0x00000200
+		0x00000000
+		0x51315152
+		0xc0013150
+		0x020000c0
+		0x00100001
+		0x07054208
+		0x000f0c18
+		0x01000140
+		0x00000c20
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x76543210
+		0x0004c008
+		0x000000b3
+		0x00000000
+		0x00000000
+		0x00010000
+		0x01665555
+		0x00665555
+		0x00010f00
+		0x05010200
+		0x00000003
+		0x001700c0
+		0x00cc0101
+		0x00030066
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x04080000
+		0x04080400
+		0x08000000
+		0x0c00c007
+		0x00000100
+		0x00000100
+		0x55555555
+		0xaaaaaaaa
+		0x55555555
+		0xaaaaaaaa
+		0x00005555
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00200000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x02700270
+		0x02700270
+		0x02700270
+		0x02700270
+		0x00000270
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00800000
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00b30080
+		0x00000003
+		0x00000000
+		0x00020000
+		0x00000200
+		0x00000000
+		0x51315152
+		0xc0013150
+		0x020000c0
+		0x00100001
+		0x07054208
+		0x000f0c18
+		0x01000140
+		0x00000c20
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x76543210
+		0x0004c008
+		0x000000b3
+		0x00000000
+		0x00000000
+		0x00010000
+		0x01665555
+		0x00665555
+		0x00010f00
+		0x05010200
+		0x00000003
+		0x001700c0
+		0x00cc0101
+		0x00030066
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x04080000
+		0x04080400
+		0x08000000
+		0x0c00c007
+		0x00000100
+		0x00000100
+		0x55555555
+		0xaaaaaaaa
+		0x55555555
+		0xaaaaaaaa
+		0x00005555
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00200000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x02700270
+		0x02700270
+		0x02700270
+		0x02700270
+		0x00000270
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00800000
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00b30080
+		0x00000003
+		0x00000000
+		0x00020000
+		0x00000200
+		0x00000000
+		0x51315152
+		0xc0013150
+		0x020000c0
+		0x00100001
+		0x07054208
+		0x000f0c18
+		0x01000140
+		0x00000c20
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x76543210
+		0x0004c008
+		0x000000b3
+		0x00000000
+		0x00000000
+		0x00010000
+		0x01665555
+		0x00665555
+		0x00010f00
+		0x05010200
+		0x00000003
+		0x001700c0
+		0x00cc0101
+		0x00030066
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x04080000
+		0x04080400
+		0x08000000
+		0x0c00c007
+		0x00000100
+		0x00000100
+		0x55555555
+		0xaaaaaaaa
+		0x55555555
+		0xaaaaaaaa
+		0x00005555
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00200000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x02700270
+		0x02700270
+		0x02700270
+		0x02700270
+		0x00000270
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00800000
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00800080
+		0x00b30080
+		0x00000003
+		0x00000000
+		0x00020000
+		0x00000200
+		0x00000000
+		0x51315152
+		0xc0013150
+		0x020000c0
+		0x00100001
+		0x07054208
+		0x000f0c18
+		0x01000140
+		0x00000c20
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00800000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000001
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00400320
+		0x00000040
+		0x00806420
+		0x00917531
+		0x00806420
+		0x01917531
+		0x00020003
+		0x00000000
+		0x00000000
+		0x00000000
+		0x000556aa
+		0x000aaaaa
+		0x000aa955
+		0x00055555
+		0x000b3133
+		0x0004cd33
+		0x0004cecc
+		0x000b32cc
+		0x0a418820
+		0x103f0000
+		0x0000003f
+		0x00038055
+		0x03800380
+		0x03800380
+		0x00000380
+		0x42080010
+		0x00000003
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00800000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000001
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00400320
+		0x00000040
+		0x00008eca
+		0x00009fdb
+		0x00008eca
+		0x01009fdb
+		0x00020003
+		0x00000000
+		0x00000000
+		0x00000000
+		0x000556aa
+		0x000aaaaa
+		0x000aa955
+		0x00055555
+		0x000b3133
+		0x0004cd33
+		0x0004cecc
+		0x000b32cc
+		0x0004a0e6
+		0x080f0000
+		0x0000000f
+		0x00038055
+		0x03800380
+		0x03800380
+		0x00000380
+		0x42080010
+		0x00000003
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00800000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000001
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00400320
+		0x00000040
+		0x00008eca
+		0x00009fdb
+		0x00008eca
+		0x01009fdb
+		0x00020003
+		0x00000000
+		0x00000000
+		0x00000000
+		0x000556aa
+		0x000aaaaa
+		0x000aa955
+		0x00055555
+		0x000b3133
+		0x0004cd33
+		0x0004cecc
+		0x000b32cc
+		0x1ee6b16a
+		0x10000000
+		0x00000000
+		0x00038055
+		0x03800380
+		0x03800380
+		0x00000380
+		0x42080010
+		0x00000003
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000001
+		0x00000000
+		0x01000005
+		0x04000f00
+		0x00020040
+		0x00020055
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000050
+		0x00000000
+		0x00010100
+		0x00000601
+		0x00000000
+		0x00006400
+		0x01221102
+		0x00000000
+		0x00051f00
+		0x051f051f
+		0x051f051f
+		0x00030003
+		0x03000300
+		0x00000300
+		0x01221102
+		0x00000000
+		0x00000000
+		0x03020000
+		0x00000001
+		0x00000011
+		0x00000011
+		0x00000400
+		0x00000000
+		0x00000011
+		0x00000011
+		0x00004410
+		0x00004410
+		0x00004410
+		0x00004410
+		0x00004410
+		0x00000011
+		0x00004410
+		0x00000011
+		0x00004410
+		0x00000011
+		0x00004410
+		0x00000000
+		0x00000000
+		0x00000000
+		0x04000000
+		0x00000000
+		0x00000000
+		0x00000508
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0x00000000
+		0xe4000000
+		0x00000000
+		0x00000000
+		0x01010000
+		0x00000000
+	>;
+};
diff --git a/arch/arm/dts/rk3399.dtsi b/arch/arm/dts/rk3399.dtsi
index 22277ff..456fdb6 100644
--- a/arch/arm/dts/rk3399.dtsi
+++ b/arch/arm/dts/rk3399.dtsi
@@ -24,6 +24,8 @@
 		serial2 = &uart2;
 		serial3 = &uart3;
 		serial4 = &uart4;
+		mmc0 = &sdhci;
+		mmc1 = &sdmmc;
 	};
 
 	cpus {
@@ -183,6 +185,7 @@
 	};
 
 	sdhci: sdhci@fe330000 {
+		u-boot,dm-pre-reloc;
 		compatible = "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1";
 		reg = <0x0 0xfe330000 0x0 0x10000>;
 		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
@@ -416,6 +419,7 @@
 	};
 
 	pmugrf: syscon@ff320000 {
+		u-boot,dm-pre-reloc;
 		compatible = "rockchip,rk3399-pmugrf", "syscon", "simple-mfd";
 		reg = <0x0 0xff320000 0x0 0x1000>;
 		#address-cells = <1>;
@@ -427,6 +431,12 @@
 		};
 	};
 
+	pmusgrf: syscon@ff330000 {
+		u-boot,dm-pre-reloc;
+		compatible = "rockchip,rk3399-pmusgrf", "syscon";
+		reg = <0x0 0xff330000 0x0 0xe3d4>;
+	};
+
 	spi3: spi@ff350000 {
 		compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
 		reg = <0x0 0xff350000 0x0 0x1000>;
@@ -497,7 +507,40 @@
 		status = "disabled";
 	};
 
+	cic: syscon@ff620000 {
+		u-boot,dm-pre-reloc;
+		compatible = "rockchip,rk3399-cic", "syscon";
+		reg = <0x0 0xff620000 0x0 0x100>;
+	};
+
+	dfi: dfi@ff630000 {
+		reg = <0x00 0xff630000 0x00 0x4000>;
+		compatible = "rockchip,rk3399-dfi";
+		rockchip,pmu = <&pmugrf>;
+		clocks = <&cru PCLK_DDR_MON>;
+		clock-names = "pclk_ddr_mon";
+		status = "disabled";
+	};
+
+	dmc: dmc {
+		u-boot,dm-pre-reloc;
+		compatible = "rockchip,rk3399-dmc";
+		devfreq-events = <&dfi>;
+		interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH 0>;
+		clocks = <&cru SCLK_DDRCLK>;
+		clock-names = "dmc_clk";
+		reg = <0x0 0xffa80000 0x0 0x0800
+		       0x0 0xffa80800 0x0 0x1800
+		       0x0 0xffa82000 0x0 0x2000
+		       0x0 0xffa84000 0x0 0x1000
+		       0x0 0xffa88000 0x0 0x0800
+		       0x0 0xffa88800 0x0 0x1800
+		       0x0 0xffa8a000 0x0 0x2000
+		       0x0 0xffa8c000 0x0 0x1000>;
+	};
+
 	pmucru: pmu-clock-controller@ff750000 {
+		u-boot,dm-pre-reloc;
 		compatible = "rockchip,rk3399-pmucru";
 		reg = <0x0 0xff750000 0x0 0x1000>;
 		#clock-cells = <1>;
@@ -507,6 +550,7 @@
 	};
 
 	cru: clock-controller@ff760000 {
+		u-boot,dm-pre-reloc;
 		compatible = "rockchip,rk3399-cru";
 		reg = <0x0 0xff760000 0x0 0x1000>;
 		#clock-cells = <1>;
@@ -530,6 +574,7 @@
 	};
 
 	grf: syscon@ff770000 {
+		u-boot,dm-pre-reloc;
 		compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
 		reg = <0x0 0xff770000 0x0 0x10000>;
 		#address-cells = <1>;
@@ -607,6 +652,7 @@
 	};
 
 	pinctrl: pinctrl {
+		u-boot,dm-pre-reloc;
 		compatible = "rockchip,rk3399-pinctrl";
 		rockchip,grf = <&grf>;
 		rockchip,pmu = <&pmugrf>;
diff --git a/arch/arm/dts/rk3xxx.dtsi b/arch/arm/dts/rk3xxx.dtsi
new file mode 100644
index 0000000..6d9e36d
--- /dev/null
+++ b/arch/arm/dts/rk3xxx.dtsi
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+ or X11
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "skeleton.dtsi"
+
+/ {
+	interrupt-parent = <&gic>;
+
+	aliases {
+		ethernet0 = &emac;
+		i2c0 = &i2c0;
+		i2c1 = &i2c1;
+		i2c2 = &i2c2;
+		i2c3 = &i2c3;
+		i2c4 = &i2c4;
+		mshc0 = &emmc;
+		mshc1 = &mmc0;
+		mshc2 = &mmc1;
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+		serial3 = &uart3;
+		spi0 = &spi0;
+		spi1 = &spi1;
+	};
+
+	amba {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		dmac1_s: dma-controller@20018000 {
+			compatible = "arm,pl330", "arm,primecell";
+			reg = <0x20018000 0x4000>;
+			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+			#dma-cells = <1>;
+			arm,pl330-broken-no-flushp;
+			clocks = <&cru ACLK_DMA1>;
+			clock-names = "apb_pclk";
+		};
+
+		dmac1_ns: dma-controller@2001c000 {
+			compatible = "arm,pl330", "arm,primecell";
+			reg = <0x2001c000 0x4000>;
+			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+			#dma-cells = <1>;
+			arm,pl330-broken-no-flushp;
+			clocks = <&cru ACLK_DMA1>;
+			clock-names = "apb_pclk";
+			status = "disabled";
+		};
+
+		dmac2: dma-controller@20078000 {
+			compatible = "arm,pl330", "arm,primecell";
+			reg = <0x20078000 0x4000>;
+			interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+			#dma-cells = <1>;
+			arm,pl330-broken-no-flushp;
+			clocks = <&cru ACLK_DMA2>;
+			clock-names = "apb_pclk";
+		};
+	};
+
+	xin24m: oscillator {
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+		#clock-cells = <0>;
+		clock-output-names = "xin24m";
+	};
+
+	L2: l2-cache-controller@10138000 {
+		compatible = "arm,pl310-cache";
+		reg = <0x10138000 0x1000>;
+		cache-unified;
+		cache-level = <2>;
+	};
+
+	scu@1013c000 {
+		compatible = "arm,cortex-a9-scu";
+		reg = <0x1013c000 0x100>;
+	};
+
+	global_timer: global-timer@1013c200 {
+		compatible = "arm,cortex-a9-global-timer";
+		reg = <0x1013c200 0x20>;
+		interrupts = <GIC_PPI 11 0x304>;
+		clocks = <&cru CORE_PERI>;
+	};
+
+	local_timer: local-timer@1013c600 {
+		compatible = "arm,cortex-a9-twd-timer";
+		reg = <0x1013c600 0x20>;
+		interrupts = <GIC_PPI 13 0x304>;
+		clocks = <&cru CORE_PERI>;
+	};
+
+	gic: interrupt-controller@1013d000 {
+		compatible = "arm,cortex-a9-gic";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		reg = <0x1013d000 0x1000>,
+		      <0x1013c100 0x0100>;
+	};
+
+	uart0: serial@10124000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0x10124000 0x400>;
+		interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <1>;
+		clock-names = "baudclk", "apb_pclk";
+		clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>;
+		status = "disabled";
+	};
+
+	uart1: serial@10126000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0x10126000 0x400>;
+		interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <1>;
+		clock-names = "baudclk", "apb_pclk";
+		clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>;
+		status = "disabled";
+	};
+
+	noc: syscon@10128000 {
+		u-boot,dm-spl;
+		compatible = "rockchip,rk3188-noc", "syscon";
+		reg = <0x10128000 0x2000>;
+	};
+
+	usb_otg: usb@10180000 {
+		compatible = "rockchip,rk3066-usb", "snps,dwc2";
+		reg = <0x10180000 0x40000>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_OTG0>;
+		clock-names = "otg";
+		dr_mode = "otg";
+		g-np-tx-fifo-size = <16>;
+		g-rx-fifo-size = <275>;
+		g-tx-fifo-size = <256 128 128 64 64 32>;
+		g-use-dma;
+		phys = <&usbphy0>;
+		phy-names = "usb2-phy";
+		status = "disabled";
+	};
+
+	usb_host: usb@101c0000 {
+		compatible = "snps,dwc2";
+		reg = <0x101c0000 0x40000>;
+		interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_OTG1>;
+		clock-names = "otg";
+		dr_mode = "host";
+		phys = <&usbphy1>;
+		phy-names = "usb2-phy";
+		status = "disabled";
+	};
+
+	emac: ethernet@10204000 {
+		compatible = "snps,arc-emac";
+		reg = <0x10204000 0x3c>;
+		interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rockchip,grf = <&grf>;
+
+		clocks = <&cru HCLK_EMAC>, <&cru SCLK_MAC>;
+		clock-names = "hclk", "macref";
+		max-speed = <100>;
+		phy-mode = "rmii";
+
+		status = "disabled";
+	};
+
+	mmc0: dwmmc@10214000 {
+		compatible = "rockchip,rk2928-dw-mshc";
+		reg = <0x10214000 0x1000>;
+		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>;
+		clock-names = "biu", "ciu";
+		fifo-depth = <256>;
+		status = "disabled";
+	};
+
+	mmc1: dwmmc@10218000 {
+		compatible = "rockchip,rk2928-dw-mshc";
+		reg = <0x10218000 0x1000>;
+		interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>;
+		clock-names = "biu", "ciu";
+		fifo-depth = <256>;
+		status = "disabled";
+	};
+
+	emmc: dwmmc@1021c000 {
+		compatible = "rockchip,rk2928-dw-mshc";
+		reg = <0x1021c000 0x1000>;
+		interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>;
+		clock-names = "biu", "ciu";
+		fifo-depth = <256>;
+		status = "disabled";
+	};
+
+	pmu: pmu@20004000 {
+		compatible = "rockchip,rk3066-pmu", "syscon";
+		reg = <0x20004000 0x100>;
+		u-boot,dm-spl;
+	};
+
+	grf: grf@20008000 {
+		compatible = "syscon";
+		reg = <0x20008000 0x200>;
+		u-boot,dm-spl;
+	};
+
+	dmc: dmc@20020000 {
+		/* unreviewed u-boot-specific binding */
+		compatible = "rockchip,rk3188-dmc", "syscon";
+		rockchip,cru = <&cru>;
+		rockchip,grf = <&grf>;
+		rockchip,pmu = <&pmu>;
+		rockchip,noc = <&noc>;
+		reg = <0x20020000 0x3fc
+		       0x20040000 0x294>;
+		clocks = <&cru PCLK_DDRUPCTL>, <&cru PCLK_PUBL>;
+		clock-names = "pclk_ddrupctl", "pclk_publ";
+		u-boot,dm-spl;
+	};
+
+	i2c0: i2c@2002d000 {
+		compatible = "rockchip,rk3066-i2c";
+		reg = <0x2002d000 0x1000>;
+		interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rockchip,grf = <&grf>;
+
+		clock-names = "i2c";
+		clocks = <&cru PCLK_I2C0>;
+
+		status = "disabled";
+	};
+
+	i2c1: i2c@2002f000 {
+		compatible = "rockchip,rk3066-i2c";
+		reg = <0x2002f000 0x1000>;
+		interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rockchip,grf = <&grf>;
+
+		clocks = <&cru PCLK_I2C1>;
+		clock-names = "i2c";
+
+		status = "disabled";
+	};
+
+	pwm0: pwm@20030000 {
+		compatible = "rockchip,rk2928-pwm";
+		reg = <0x20030000 0x10>;
+		#pwm-cells = <2>;
+		clocks = <&cru PCLK_PWM01>;
+		status = "disabled";
+	};
+
+	pwm1: pwm@20030010 {
+		compatible = "rockchip,rk2928-pwm";
+		reg = <0x20030010 0x10>;
+		#pwm-cells = <2>;
+		clocks = <&cru PCLK_PWM01>;
+		status = "disabled";
+	};
+
+	wdt: watchdog@2004c000 {
+		compatible = "snps,dw-wdt";
+		reg = <0x2004c000 0x100>;
+		clocks = <&cru PCLK_WDT>;
+		interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
+	pwm2: pwm@20050020 {
+		compatible = "rockchip,rk2928-pwm";
+		reg = <0x20050020 0x10>;
+		#pwm-cells = <2>;
+		clocks = <&cru PCLK_PWM23>;
+		status = "disabled";
+	};
+
+	pwm3: pwm@20050030 {
+		compatible = "rockchip,rk2928-pwm";
+		reg = <0x20050030 0x10>;
+		#pwm-cells = <2>;
+		clocks = <&cru PCLK_PWM23>;
+		status = "disabled";
+	};
+
+	i2c2: i2c@20056000 {
+		compatible = "rockchip,rk3066-i2c";
+		reg = <0x20056000 0x1000>;
+		interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rockchip,grf = <&grf>;
+
+		clocks = <&cru PCLK_I2C2>;
+		clock-names = "i2c";
+
+		status = "disabled";
+	};
+
+	i2c3: i2c@2005a000 {
+		compatible = "rockchip,rk3066-i2c";
+		reg = <0x2005a000 0x1000>;
+		interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rockchip,grf = <&grf>;
+
+		clocks = <&cru PCLK_I2C3>;
+		clock-names = "i2c";
+
+		status = "disabled";
+	};
+
+	i2c4: i2c@2005e000 {
+		compatible = "rockchip,rk3066-i2c";
+		reg = <0x2005e000 0x1000>;
+		interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rockchip,grf = <&grf>;
+
+		clocks = <&cru PCLK_I2C4>;
+		clock-names = "i2c";
+
+		status = "disabled";
+	};
+
+	uart2: serial@20064000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0x20064000 0x400>;
+		interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <1>;
+		clock-frequency = <24000000>;
+		clock-names = "baudclk", "apb_pclk";
+		clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>;
+		status = "disabled";
+	};
+
+	uart3: serial@20068000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0x20068000 0x400>;
+		interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <1>;
+		clock-names = "baudclk", "apb_pclk";
+		clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>;
+		status = "disabled";
+	};
+
+	saradc: saradc@2006c000 {
+		compatible = "rockchip,saradc";
+		reg = <0x2006c000 0x100>;
+		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+		#io-channel-cells = <1>;
+		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
+		clock-names = "saradc", "apb_pclk";
+		status = "disabled";
+	};
+
+	spi0: spi@20070000 {
+		compatible = "rockchip,rk3066-spi";
+		clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
+		clock-names = "spiclk", "apb_pclk";
+		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+		reg = <0x20070000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		dmas = <&dmac2 10>, <&dmac2 11>;
+		dma-names = "tx", "rx";
+		status = "disabled";
+	};
+
+	spi1: spi@20074000 {
+		compatible = "rockchip,rk3066-spi";
+		clocks = <&cru SCLK_SPI1>, <&cru PCLK_SPI1>;
+		clock-names = "spiclk", "apb_pclk";
+		interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+		reg = <0x20074000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		dmas = <&dmac2 12>, <&dmac2 13>;
+		dma-names = "tx", "rx";
+		status = "disabled";
+	};
+};
diff --git a/arch/arm/include/asm/arch-rockchip/bootrom.h b/arch/arm/include/asm/arch-rockchip/bootrom.h
new file mode 100644
index 0000000..79fb1a0
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/bootrom.h
@@ -0,0 +1,22 @@
+/*
+ * (C) Copyright 2017 Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#ifndef _ASM_ARCH_BOOTROM_H
+#define _ASM_ARCH_BOOTROM_H
+
+/*
+ * Saved Stack pointer address.
+ * Access might be needed in some special cases.
+ */
+extern u32 SAVE_SP_ADDR;
+
+/*
+ * Hand control back to the bootrom to load another
+ * boot stage.
+ */
+extern void back_to_bootrom(void);
+
+#endif
diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h
index 804c77b..b06bb6c 100644
--- a/arch/arm/include/asm/arch-rockchip/clock.h
+++ b/arch/arm/include/asm/arch-rockchip/clock.h
@@ -17,6 +17,8 @@
 	ROCKCHIP_SYSCON_SGRF,
 	ROCKCHIP_SYSCON_PMU,
 	ROCKCHIP_SYSCON_PMUGRF,
+	ROCKCHIP_SYSCON_PMUSGRF,
+	ROCKCHIP_SYSCON_CIC,
 };
 
 /* Standard Rockchip clock numbers */
@@ -63,6 +65,13 @@
  */
 void *rockchip_get_cru(void);
 
+/**
+ * rockchip_get_pmucru() - get a pointer to the clock/reset unit registers
+ *
+ * @return pointer to registers, or -ve error on error
+ */
+void *rockchip_get_pmucru(void);
+
 struct rk3288_cru;
 struct rk3288_grf;
 
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3188.h b/arch/arm/include/asm/arch-rockchip/cru_rk3188.h
new file mode 100644
index 0000000..74f0fed
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/cru_rk3188.h
@@ -0,0 +1,191 @@
+/*
+ * (C) Copyright 2016 Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+#ifndef _ASM_ARCH_CRU_RK3188_H
+#define _ASM_ARCH_CRU_RK3188_H
+
+#define OSC_HZ		(24 * 1000 * 1000)
+
+#define APLL_HZ		(1608 * 1000000)
+#define GPLL_HZ		(594 * 1000000)
+#define CPLL_HZ		(384 * 1000000)
+
+/* The SRAM is clocked off aclk_cpu, so we want to max it out for boot speed */
+#define CPU_ACLK_HZ	297000000
+#define CPU_HCLK_HZ	148500000
+#define CPU_PCLK_HZ	74250000
+#define CPU_H2P_HZ	74250000
+
+#define PERI_ACLK_HZ	148500000
+#define PERI_HCLK_HZ	148500000
+#define PERI_PCLK_HZ	74250000
+
+/* Private data for the clock driver - used by rockchip_get_cru() */
+struct rk3188_clk_priv {
+	struct rk3188_grf *grf;
+	struct rk3188_cru *cru;
+	ulong rate;
+	bool has_bwadj;
+};
+
+struct rk3188_cru {
+	struct rk3188_pll {
+		u32 con0;
+		u32 con1;
+		u32 con2;
+		u32 con3;
+	} pll[4];
+	u32 cru_mode_con;
+	u32 cru_clksel_con[35];
+	u32 cru_clkgate_con[10];
+	u32 reserved1[2];
+	u32 cru_glb_srst_fst_value;
+	u32 cru_glb_srst_snd_value;
+	u32 reserved2[2];
+	u32 cru_softrst_con[9];
+	u32 cru_misc_con;
+	u32 reserved3[2];
+	u32 cru_glb_cnt_th;
+};
+check_member(rk3188_cru, cru_glb_cnt_th, 0x0140);
+
+/* CRU_CLKSEL0_CON */
+enum {
+	/* a9_core_div: core = core_src / (a9_core_div + 1) */
+	A9_CORE_DIV_SHIFT	= 9,
+	A9_CORE_DIV_MASK	= 0x1f,
+	CORE_PLL_SHIFT		= 8,
+	CORE_PLL_MASK		= 1,
+	CORE_PLL_SELECT_APLL	= 0,
+	CORE_PLL_SELECT_GPLL,
+
+	/* core peri div: core:core_peri = 2:1, 4:1, 8:1 or 16:1 */
+	CORE_PERI_DIV_SHIFT	= 6,
+	CORE_PERI_DIV_MASK	= 3,
+
+	/* aclk_cpu pll selection */
+	CPU_ACLK_PLL_SHIFT	= 5,
+	CPU_ACLK_PLL_MASK	= 1,
+	CPU_ACLK_PLL_SELECT_APLL	= 0,
+	CPU_ACLK_PLL_SELECT_GPLL,
+
+	/* a9_cpu_div: aclk_cpu = cpu_src / (a9_cpu_div + 1) */
+	A9_CPU_DIV_SHIFT	= 0,
+	A9_CPU_DIV_MASK		= 0x1f,
+};
+
+/* CRU_CLKSEL1_CON */
+enum {
+	/* ahb2apb_pclk_div: hclk_cpu:pclk_cpu = 1:1, 2:1 or 4:1 */
+	AHB2APB_DIV_SHIFT	= 14,
+	AHB2APB_DIV_MASK	= 3,
+
+	/* cpu_pclk_div: aclk_cpu:pclk_cpu = 1:1, 2:1, 4:1 or 8:1 */
+	CPU_PCLK_DIV_SHIFT	= 12,
+	CPU_PCLK_DIV_MASK	= 3,
+
+	/* cpu_hclk_div: aclk_cpu:hclk_cpu = 1:1, 2:1 or 4:1 */
+	CPU_HCLK_DIV_SHIFT	= 8,
+	CPU_HCLK_DIV_MASK	= 3,
+
+	/* core_aclk_div: cire:aclk_core = 1:1, 2:1, 3:1, 4:1 or 8:1 */
+	CORE_ACLK_DIV_SHIFT	= 3,
+	CORE_ACLK_DIV_MASK	= 7,
+};
+
+/* CRU_CLKSEL10_CON */
+enum {
+	PERI_SEL_PLL_MASK	= 1,
+	PERI_SEL_PLL_SHIFT	= 15,
+	PERI_SEL_CPLL		= 0,
+	PERI_SEL_GPLL,
+
+	/* peri pclk div: aclk_bus:pclk_bus = 1:1, 2:1, 4:1 or 8:1 */
+	PERI_PCLK_DIV_SHIFT	= 12,
+	PERI_PCLK_DIV_MASK	= 3,
+
+	/* peripheral bus hclk div:aclk_bus: hclk_bus = 1:1, 2:1 or 4:1 */
+	PERI_HCLK_DIV_SHIFT	= 8,
+	PERI_HCLK_DIV_MASK	= 3,
+
+	/* peri aclk div: aclk_peri = periph_src / (peri_aclk_div + 1) */
+	PERI_ACLK_DIV_SHIFT	= 0,
+	PERI_ACLK_DIV_MASK	= 0x1f,
+};
+/* CRU_CLKSEL11_CON */
+enum {
+	HSICPHY_DIV_SHIFT	= 8,
+	HSICPHY_DIV_MASK	= 0x3f,
+
+	MMC0_DIV_SHIFT		= 0,
+	MMC0_DIV_MASK		= 0x3f,
+};
+
+/* CRU_CLKSEL12_CON */
+enum {
+	UART_PLL_SHIFT		= 15,
+	UART_PLL_MASK		= 1,
+	UART_PLL_SELECT_GENERAL	= 0,
+	UART_PLL_SELECT_CODEC,
+
+	EMMC_DIV_SHIFT		= 8,
+	EMMC_DIV_MASK		= 0x3f,
+
+	SDIO_DIV_SHIFT		= 0,
+	SDIO_DIV_MASK		= 0x3f,
+};
+
+/* CRU_CLKSEL25_CON */
+enum {
+	SPI1_DIV_SHIFT		= 8,
+	SPI1_DIV_MASK		= 0x7f,
+
+	SPI0_DIV_SHIFT		= 0,
+	SPI0_DIV_MASK		= 0x7f,
+};
+
+/* CRU_MODE_CON */
+enum {
+	GPLL_MODE_SHIFT		= 12,
+	GPLL_MODE_MASK		= 3,
+	GPLL_MODE_SLOW		= 0,
+	GPLL_MODE_NORMAL,
+	GPLL_MODE_DEEP,
+
+	CPLL_MODE_SHIFT		= 8,
+	CPLL_MODE_MASK		= 3,
+	CPLL_MODE_SLOW		= 0,
+	CPLL_MODE_NORMAL,
+	CPLL_MODE_DEEP,
+
+	DPLL_MODE_SHIFT		= 4,
+	DPLL_MODE_MASK		= 3,
+	DPLL_MODE_SLOW		= 0,
+	DPLL_MODE_NORMAL,
+	DPLL_MODE_DEEP,
+
+	APLL_MODE_SHIFT		= 0,
+	APLL_MODE_MASK		= 3,
+	APLL_MODE_SLOW		= 0,
+	APLL_MODE_NORMAL,
+	APLL_MODE_DEEP,
+};
+
+/* CRU_APLL_CON0 */
+enum {
+	CLKR_SHIFT		= 8,
+	CLKR_MASK		= 0x3f,
+
+	CLKOD_SHIFT		= 0,
+	CLKOD_MASK		= 0x3f,
+};
+
+/* CRU_APLL_CON1 */
+enum {
+	CLKF_SHIFT		= 0,
+	CLKF_MASK		= 0x1fff,
+};
+
+#endif
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3328.h b/arch/arm/include/asm/arch-rockchip/cru_rk3328.h
new file mode 100644
index 0000000..948706e
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/cru_rk3328.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __ASM_ARCH_CRU_RK3328_H_
+#define __ASM_ARCH_CRU_RK3328_H_
+
+#include <common.h>
+
+struct rk3328_clk_priv {
+	struct rk3328_cru *cru;
+	ulong rate;
+};
+
+struct rk3328_cru {
+	u32 apll_con[5];
+	u32 reserved1[3];
+	u32 dpll_con[5];
+	u32 reserved2[3];
+	u32 cpll_con[5];
+	u32 reserved3[3];
+	u32 gpll_con[5];
+	u32 reserved4[3];
+	u32 mode_con;
+	u32 misc;
+	u32 reserved5[2];
+	u32 glb_cnt_th;
+	u32 glb_rst_st;
+	u32 glb_srst_snd_value;
+	u32 glb_srst_fst_value;
+	u32 npll_con[5];
+	u32 reserved6[(0x100 - 0xb4) / 4];
+	u32 clksel_con[53];
+	u32 reserved7[(0x200 - 0x1d4) / 4];
+	u32 clkgate_con[29];
+	u32 reserved8[3];
+	u32 ssgtbl[32];
+	u32 softrst_con[12];
+	u32 reserved9[(0x380 - 0x330) / 4];
+	u32 sdmmc_con[2];
+	u32 sdio_con[2];
+	u32 emmc_con[2];
+	u32 sdmmc_ext_con[2];
+};
+check_member(rk3328_cru, sdmmc_ext_con[1], 0x39c);
+#define MHz		1000000
+#define KHz		1000
+#define OSC_HZ		(24 * MHz)
+#define APLL_HZ		(600 * MHz)
+#define GPLL_HZ		(576 * MHz)
+#define CPLL_HZ		(594 * MHz)
+
+#define CLK_CORE_HZ	(600 * MHz)
+#define ACLKM_CORE_HZ	(300 * MHz)
+#define PCLK_DBG_HZ	(300 * MHz)
+
+#define PERIHP_ACLK_HZ	(144000 * KHz)
+#define PERIHP_HCLK_HZ	(72000 * KHz)
+#define PERIHP_PCLK_HZ	(72000 * KHz)
+
+#define PWM_CLOCK_HZ    (74 * MHz)
+
+enum apll_frequencies {
+	APLL_816_MHZ,
+	APLL_600_MHZ,
+};
+
+#endif	/* __ASM_ARCH_CRU_RK3328_H_ */
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3399.h b/arch/arm/include/asm/arch-rockchip/cru_rk3399.h
index 98fba2b..cf830d0 100644
--- a/arch/arm/include/asm/arch-rockchip/cru_rk3399.h
+++ b/arch/arm/include/asm/arch-rockchip/cru_rk3399.h
@@ -15,6 +15,11 @@
 	ulong rate;
 };
 
+struct rk3399_pmuclk_priv {
+	struct rk3399_pmucru *pmucru;
+	ulong rate;
+};
+
 struct rk3399_pmucru {
 	u32 ppll_con[6];
 	u32 reserved[0x1a];
diff --git a/arch/arm/include/asm/arch-rockchip/ddr_rk3188.h b/arch/arm/include/asm/arch-rockchip/ddr_rk3188.h
new file mode 100644
index 0000000..3d7929f
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/ddr_rk3188.h
@@ -0,0 +1,25 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#ifndef _ASM_ARCH_DDR_RK3188_H
+#define _ASM_ARCH_DDR_RK3188_H
+
+#include <asm/arch/ddr_rk3288.h>
+
+/*
+ * RK3188 Memory scheduler register map.
+ */
+struct rk3188_msch {
+	u32 coreid;
+	u32 revisionid;
+	u32 ddrconf;
+	u32 ddrtiming;
+	u32 ddrmode;
+	u32 readlatency;
+};
+check_member(rk3188_msch, readlatency, 0x0014);
+
+#endif
diff --git a/arch/arm/include/asm/arch-rockchip/ddr_rk3288.h b/arch/arm/include/asm/arch-rockchip/ddr_rk3288.h
index fccabcd..9a59075 100644
--- a/arch/arm/include/asm/arch-rockchip/ddr_rk3288.h
+++ b/arch/arm/include/asm/arch-rockchip/ddr_rk3288.h
@@ -425,6 +425,14 @@
 
 #define START_CMD			(1u << 31)
 
+/*
+ * DDRCONF
+ * [5:4] row(13+n)
+ * [1:0] col(9+n), assume bw=2
+ */
+#define DDRCONF_ROW_SHIFT		4
+#define DDRCONF_COL_SHIFT		0
+
 /* DEVTODEV */
 #define BUSWRTORD_SHIFT			4
 #define BUSRDTOWR_SHIFT			2
diff --git a/arch/arm/include/asm/arch-rockchip/grf_rk3188.h b/arch/arm/include/asm/arch-rockchip/grf_rk3188.h
new file mode 100644
index 0000000..ce7bac5
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/grf_rk3188.h
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2016 Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#ifndef _ASM_ARCH_GRF_RK3188_H
+#define _ASM_ARCH_GRF_RK3188_H
+
+struct rk3188_grf_gpio_lh {
+	u32 l;
+	u32 h;
+};
+
+struct rk3188_grf {
+	struct rk3188_grf_gpio_lh gpio_dir[4];
+	struct rk3188_grf_gpio_lh gpio_do[4];
+	struct rk3188_grf_gpio_lh gpio_en[4];
+
+	u32 reserved[2];
+	u32 gpio0c_iomux;
+	u32 gpio0d_iomux;
+
+	u32 gpio1a_iomux;
+	u32 gpio1b_iomux;
+	u32 gpio1c_iomux;
+	u32 gpio1d_iomux;
+
+	u32 gpio2a_iomux;
+	u32 gpio2b_iomux;
+	u32 gpio2c_iomux;
+	u32 gpio2d_iomux;
+
+	u32 gpio3a_iomux;
+	u32 gpio3b_iomux;
+	u32 gpio3c_iomux;
+	u32 gpio3d_iomux;
+
+	u32 soc_con0;
+	u32 soc_con1;
+	u32 soc_con2;
+	u32 soc_status0;
+
+	u32 busdmac_con[3];
+	u32 peridmac_con[4];
+
+	u32 cpu_con[6];
+	u32 reserved0[2];
+
+	u32 ddrc_con0;
+	u32 ddrc_stat;
+
+	u32 io_con[5];
+	u32 soc_status1;
+
+	u32 uoc0_con[4];
+	u32 uoc1_con[4];
+	u32 uoc2_con[2];
+	u32 reserved1;
+	u32 uoc3_con[2];
+	u32 hsic_stat;
+	u32 os_reg[8];
+
+	u32 gpio0_p[3];
+	u32 gpio1_p[3][4];
+
+	u32 flash_data_p;
+	u32 flash_cmd_p;
+};
+check_member(rk3188_grf, flash_cmd_p, 0x01a4);
+
+/* GRF_GPIO0D_IOMUX */
+enum {
+	GPIO0D7_SHIFT		= 14,
+	GPIO0D7_MASK		= 1,
+	GPIO0D7_GPIO		= 0,
+	GPIO0D7_SPI1_CSN0,
+
+	GPIO0D6_SHIFT		= 12,
+	GPIO0D6_MASK		= 1,
+	GPIO0D6_GPIO		= 0,
+	GPIO0D6_SPI1_CLK,
+
+	GPIO0D5_SHIFT		= 10,
+	GPIO0D5_MASK		= 1,
+	GPIO0D5_GPIO		= 0,
+	GPIO0D5_SPI1_TXD,
+
+	GPIO0D4_SHIFT		= 8,
+	GPIO0D4_MASK		= 1,
+	GPIO0D4_GPIO		= 0,
+	GPIO0D4_SPI0_RXD,
+
+	GPIO0D3_SHIFT		= 6,
+	GPIO0D3_MASK		= 3,
+	GPIO0D3_GPIO		= 0,
+	GPIO0D3_FLASH_CSN3,
+	GPIO0D3_EMMC_RSTN_OUT,
+
+	GPIO0D2_SHIFT		= 4,
+	GPIO0D2_MASK		= 3,
+	GPIO0D2_GPIO		= 0,
+	GPIO0D2_FLASH_CSN2,
+	GPIO0D2_EMMC_CMD,
+
+	GPIO0D1_SHIFT		= 2,
+	GPIO0D1_MASK		= 1,
+	GPIO0D1_GPIO		= 0,
+	GPIO0D1_FLASH_CSN1,
+
+	GPIO0D0_SHIFT		= 0,
+	GPIO0D0_MASK		= 3,
+	GPIO0D0_GPIO		= 0,
+	GPIO0D0_FLASH_DQS,
+	GPIO0D0_EMMC_CLKOUT
+};
+
+/* GRF_GPIO1A_IOMUX */
+enum {
+	GPIO1A7_SHIFT		= 14,
+	GPIO1A7_MASK		= 3,
+	GPIO1A7_GPIO		= 0,
+	GPIO1A7_UART1_RTS_N,
+	GPIO1A7_SPI0_CSN0,
+
+	GPIO1A6_SHIFT		= 12,
+	GPIO1A6_MASK		= 3,
+	GPIO1A6_GPIO		= 0,
+	GPIO1A6_UART1_CTS_N,
+	GPIO1A6_SPI0_CLK,
+
+	GPIO1A5_SHIFT		= 10,
+	GPIO1A5_MASK		= 3,
+	GPIO1A5_GPIO		= 0,
+	GPIO1A5_UART1_SOUT,
+	GPIO1A5_SPI0_TXD,
+
+	GPIO1A4_SHIFT		= 8,
+	GPIO1A4_MASK		= 3,
+	GPIO1A4_GPIO		= 0,
+	GPIO1A4_UART1_SIN,
+	GPIO1A4_SPI0_RXD,
+
+	GPIO1A3_SHIFT		= 6,
+	GPIO1A3_MASK		= 1,
+	GPIO1A3_GPIO		= 0,
+	GPIO1A3_UART0_RTS_N,
+
+	GPIO1A2_SHIFT		= 4,
+	GPIO1A2_MASK		= 1,
+	GPIO1A2_GPIO		= 0,
+	GPIO1A2_UART0_CTS_N,
+
+	GPIO1A1_SHIFT		= 2,
+	GPIO1A1_MASK		= 1,
+	GPIO1A1_GPIO		= 0,
+	GPIO1A1_UART0_SOUT,
+
+	GPIO1A0_SHIFT		= 0,
+	GPIO1A0_MASK		= 1,
+	GPIO1A0_GPIO		= 0,
+	GPIO1A0_UART0_SIN,
+};
+
+/* GRF_GPIO1B_IOMUX */
+enum {
+	GPIO1B7_SHIFT		= 14,
+	GPIO1B7_MASK		= 1,
+	GPIO1B7_GPIO		= 0,
+	GPIO1B7_SPI0_CSN1,
+
+	GPIO1B6_SHIFT		= 12,
+	GPIO1B6_MASK		= 3,
+	GPIO1B6_GPIO		= 0,
+	GPIO1B6_SPDIF_TX,
+	GPIO1B6_SPI1_CSN1,
+
+	GPIO1B5_SHIFT		= 10,
+	GPIO1B5_MASK		= 3,
+	GPIO1B5_GPIO		= 0,
+	GPIO1B5_UART3_RTS_N,
+	GPIO1B5_RESERVED,
+
+	GPIO1B4_SHIFT		= 8,
+	GPIO1B4_MASK		= 3,
+	GPIO1B4_GPIO		= 0,
+	GPIO1B4_UART3_CTS_N,
+	GPIO1B4_GPS_RFCLK,
+
+	GPIO1B3_SHIFT		= 6,
+	GPIO1B3_MASK		= 3,
+	GPIO1B3_GPIO		= 0,
+	GPIO1B3_UART3_SOUT,
+	GPIO1B3_GPS_SIG,
+
+	GPIO1B2_SHIFT		= 4,
+	GPIO1B2_MASK		= 3,
+	GPIO1B2_GPIO		= 0,
+	GPIO1B2_UART3_SIN,
+	GPIO1B2_GPS_MAG,
+
+	GPIO1B1_SHIFT		= 2,
+	GPIO1B1_MASK		= 3,
+	GPIO1B1_GPIO		= 0,
+	GPIO1B1_UART2_SOUT,
+	GPIO1B1_JTAG_TDO,
+
+	GPIO1B0_SHIFT		= 0,
+	GPIO1B0_MASK		= 3,
+	GPIO1B0_GPIO		= 0,
+	GPIO1B0_UART2_SIN,
+	GPIO1B0_JTAG_TDI,
+};
+
+/* GRF_GPIO1D_IOMUX */
+enum {
+	GPIO1D7_SHIFT		= 14,
+	GPIO1D7_MASK		= 1,
+	GPIO1D7_GPIO		= 0,
+	GPIO1D7_I2C4_SCL,
+
+	GPIO1D6_SHIFT		= 12,
+	GPIO1D6_MASK		= 1,
+	GPIO1D6_GPIO		= 0,
+	GPIO1D6_I2C4_SDA,
+
+	GPIO1D5_SHIFT		= 10,
+	GPIO1D5_MASK		= 1,
+	GPIO1D5_GPIO		= 0,
+	GPIO1D5_I2C2_SCL,
+
+	GPIO1D4_SHIFT		= 8,
+	GPIO1D4_MASK		= 1,
+	GPIO1D4_GPIO		= 0,
+	GPIO1D4_I2C2_SDA,
+
+	GPIO1D3_SHIFT		= 6,
+	GPIO1D3_MASK		= 1,
+	GPIO1D3_GPIO		= 0,
+	GPIO1D3_I2C1_SCL,
+
+	GPIO1D2_SHIFT		= 4,
+	GPIO1D2_MASK		= 1,
+	GPIO1D2_GPIO		= 0,
+	GPIO1D2_I2C1_SDA,
+
+	GPIO1D1_SHIFT		= 2,
+	GPIO1D1_MASK		= 1,
+	GPIO1D1_GPIO		= 0,
+	GPIO1D1_I2C0_SCL,
+
+	GPIO1D0_SHIFT		= 0,
+	GPIO1D0_MASK		= 1,
+	GPIO1D0_GPIO		= 0,
+	GPIO1D0_I2C0_SDA,
+};
+
+/* GRF_GPIO3A_IOMUX */
+enum {
+	GPIO3A7_SHIFT		= 14,
+	GPIO3A7_MASK		= 1,
+	GPIO3A7_GPIO		= 0,
+	GPIO3A7_SDMMC0_DATA3,
+
+	GPIO3A6_SHIFT		= 12,
+	GPIO3A6_MASK		= 1,
+	GPIO3A6_GPIO		= 0,
+	GPIO3A6_SDMMC0_DATA2,
+
+	GPIO3A5_SHIFT		= 10,
+	GPIO3A5_MASK		= 1,
+	GPIO3A5_GPIO		= 0,
+	GPIO3A5_SDMMC0_DATA1,
+
+	GPIO3A4_SHIFT		= 8,
+	GPIO3A4_MASK		= 1,
+	GPIO3A4_GPIO		= 0,
+	GPIO3A4_SDMMC0_DATA0,
+
+	GPIO3A3_SHIFT		= 6,
+	GPIO3A3_MASK		= 1,
+	GPIO3A3_GPIO		= 0,
+	GPIO3A3_SDMMC0_CMD,
+
+	GPIO3A2_SHIFT		= 4,
+	GPIO3A2_MASK		= 1,
+	GPIO3A2_GPIO		= 0,
+	GPIO3A2_SDMMC0_CLKOUT,
+
+	GPIO3A1_SHIFT		= 2,
+	GPIO3A1_MASK		= 1,
+	GPIO3A1_GPIO		= 0,
+	GPIO3A1_SDMMC0_PWREN,
+
+	GPIO3A0_SHIFT		= 0,
+	GPIO3A0_MASK		= 1,
+	GPIO3A0_GPIO		= 0,
+	GPIO3A0_SDMMC0_RSTN,
+};
+
+/* GRF_GPIO3B_IOMUX */
+enum {
+	GPIO3B7_SHIFT		= 14,
+	GPIO3B7_MASK		= 3,
+	GPIO3B7_GPIO		= 0,
+	GPIO3B7_CIF_DATA11,
+	GPIO3B7_I2C3_SCL,
+
+	GPIO3B6_SHIFT		= 12,
+	GPIO3B6_MASK		= 3,
+	GPIO3B6_GPIO		= 0,
+	GPIO3B6_CIF_DATA10,
+	GPIO3B6_I2C3_SDA,
+
+	GPIO3B5_SHIFT		= 10,
+	GPIO3B5_MASK		= 3,
+	GPIO3B5_GPIO		= 0,
+	GPIO3B5_CIF_DATA1,
+	GPIO3B5_HSADC_DATA9,
+
+	GPIO3B4_SHIFT		= 8,
+	GPIO3B4_MASK		= 3,
+	GPIO3B4_GPIO		= 0,
+	GPIO3B4_CIF_DATA0,
+	GPIO3B4_HSADC_DATA8,
+
+	GPIO3B3_SHIFT		= 6,
+	GPIO3B3_MASK		= 1,
+	GPIO3B3_GPIO		= 0,
+	GPIO3B3_CIF_CLKOUT,
+
+	GPIO3B2_SHIFT		= 4,
+	GPIO3B2_MASK		= 1,
+	GPIO3B2_GPIO		= 0,
+	/* no muxes */
+
+	GPIO3B1_SHIFT		= 2,
+	GPIO3B1_MASK		= 1,
+	GPIO3B1_GPIO		= 0,
+	GPIO3B1_SDMMC0_WRITE_PRT,
+
+	GPIO3B0_SHIFT		= 0,
+	GPIO3B0_MASK		= 1,
+	GPIO3B0_GPIO		= 0,
+	GPIO3B0_SDMMC_DETECT_N,
+};
+
+/* GRF_GPIO3C_IOMUX */
+enum {
+	GPIO3C7_SHIFT		= 14,
+	GPIO3C7_MASK		= 3,
+	GPIO3C7_GPIO		= 0,
+	GPIO3C7_SDMMC1_WRITE_PRT,
+	GPIO3C7_RMII_CRS_DVALID,
+	GPIO3C7_RESERVED,
+
+	GPIO3C6_SHIFT		= 12,
+	GPIO3C6_MASK		= 3,
+	GPIO3C6_GPIO		= 0,
+	GPIO3C6_SDMMC1_DECTN,
+	GPIO3C6_RMII_RX_ERR,
+	GPIO3C6_RESERVED,
+
+	GPIO3C5_SHIFT		= 10,
+	GPIO3C5_MASK		= 3,
+	GPIO3C5_GPIO		= 0,
+	GPIO3C5_SDMMC1_CLKOUT,
+	GPIO3C5_RMII_CLKOUT,
+	GPIO3C5_RMII_CLKIN,
+
+	GPIO3C4_SHIFT		= 8,
+	GPIO3C4_MASK		= 3,
+	GPIO3C4_GPIO		= 0,
+	GPIO3C4_SDMMC1_DATA3,
+	GPIO3C4_RMII_RXD1,
+	GPIO3C4_RESERVED,
+
+	GPIO3C3_SHIFT		= 6,
+	GPIO3C3_MASK		= 3,
+	GPIO3C3_GPIO		= 0,
+	GPIO3C3_SDMMC1_DATA2,
+	GPIO3C3_RMII_RXD0,
+	GPIO3C3_RESERVED,
+
+	GPIO3C2_SHIFT		= 4,
+	GPIO3C2_MASK		= 3,
+	GPIO3C2_GPIO		= 0,
+	GPIO3C2_SDMMC1_DATA1,
+	GPIO3C2_RMII_TXD0,
+	GPIO3C2_RESERVED,
+
+	GPIO3C1_SHIFT		= 2,
+	GPIO3C1_MASK		= 3,
+	GPIO3C1_GPIO		= 0,
+	GPIO3C1_SDMMC1_DATA0,
+	GPIO3C1_RMII_TXD1,
+	GPIO3C1_RESERVED,
+
+	GPIO3C0_SHIFT		= 0,
+	GPIO3C0_MASK		= 3,
+	GPIO3C0_GPIO		= 0,
+	GPIO3C0_SDMMC1_CMD,
+	GPIO3C0_RMII_TX_EN,
+	GPIO3C0_RESERVED,
+};
+
+/* GRF_GPIO3D_IOMUX */
+enum {
+	GPIO3D6_SHIFT		= 12,
+	GPIO3D6_MASK		= 3,
+	GPIO3D6_GPIO		= 0,
+	GPIO3D6_PWM_3,
+	GPIO3D6_JTAG_TMS,
+	GPIO3D6_HOST_DRV_VBUS,
+
+	GPIO3D5_SHIFT		= 10,
+	GPIO3D5_MASK		= 3,
+	GPIO3D5_GPIO		= 0,
+	GPIO3D5_PWM_2,
+	GPIO3D5_JTAG_TCK,
+	GPIO3D5_OTG_DRV_VBUS,
+
+	GPIO3D4_SHIFT		= 8,
+	GPIO3D4_MASK		= 3,
+	GPIO3D4_GPIO		= 0,
+	GPIO3D4_PWM_1,
+	GPIO3D4_JTAG_TRSTN,
+
+	GPIO3D3_SHIFT		= 6,
+	GPIO3D3_MASK		= 3,
+	GPIO3D3_GPIO		= 0,
+	GPIO3D3_PWM_0,
+
+	GPIO3D2_SHIFT		= 4,
+	GPIO3D2_MASK		= 3,
+	GPIO3D2_GPIO		= 0,
+	GPIO3D2_SDMMC1_INT_N,
+
+	GPIO3D1_SHIFT		= 2,
+	GPIO3D1_MASK		= 3,
+	GPIO3D1_GPIO		= 0,
+	GPIO3D1_SDMMC1_BACKEND_PWR,
+	GPIO3D1_MII_MDCLK,
+
+	GPIO3D0_SHIFT		= 0,
+	GPIO3D0_MASK		= 3,
+	GPIO3D0_GPIO		= 0,
+	GPIO3D0_SDMMC1_PWR_EN,
+	GPIO3D0_MII_MD,
+};
+
+/* GRF_SOC_CON0 */
+enum {
+	HSADC_CLK_DIR_SHIFT	= 15,
+	HSADC_CLK_DIR_MASK	= 1,
+
+	HSADC_SEL_SHIFT		= 14,
+	HSADC_SEL_MASK		= 1,
+
+	NOC_REMAP_SHIFT		= 12,
+	NOC_REMAP_MASK		= 1,
+
+	EMMC_FLASH_SEL_SHIFT	= 11,
+	EMMC_FLASH_SEL_MASK	= 1,
+
+	TZPC_REVISION_SHIFT	= 7,
+	TZPC_REVISION_MASK	= 0xf,
+
+	L2CACHE_ACC_SHIFT	= 5,
+	L2CACHE_ACC_MASK	= 3,
+
+	L2RD_WAIT_SHIFT		= 3,
+	L2RD_WAIT_MASK		= 3,
+
+	IMEMRD_WAIT_SHIFT	= 1,
+	IMEMRD_WAIT_MASK	= 3,
+};
+
+/* GRF_SOC_CON1 */
+enum {
+	RKI2C4_SEL_SHIFT	= 15,
+	RKI2C4_SEL_MASK		= 1,
+
+	RKI2C3_SEL_SHIFT	= 14,
+	RKI2C3_SEL_MASK		= 1,
+
+	RKI2C2_SEL_SHIFT	= 13,
+	RKI2C2_SEL_MASK		= 1,
+
+	RKI2C1_SEL_SHIFT	= 12,
+	RKI2C1_SEL_MASK		= 1,
+
+	RKI2C0_SEL_SHIFT	= 11,
+	RKI2C0_SEL_MASK		= 1,
+
+	VCODEC_SEL_SHIFT	= 10,
+	VCODEC_SEL_MASK		= 1,
+
+	PERI_EMEM_PAUSE_SHIFT	= 9,
+	PERI_EMEM_PAUSE_MASK	= 1,
+
+	PERI_USB_PAUSE_SHIFT	= 8,
+	PERI_USB_PAUSE_MASK	= 1,
+
+	SMC_MUX_MODE_0_SHIFT	= 6,
+	SMC_MUX_MODE_0_MASK	= 1,
+
+	SMC_SRAM_MW_0_SHIFT	= 4,
+	SMC_SRAM_MW_0_MASK	= 3,
+
+	SMC_REMAP_0_SHIFT	= 3,
+	SMC_REMAP_0_MASK	= 1,
+
+	SMC_A_GT_M0_SYNC_SHIFT	= 2,
+	SMC_A_GT_M0_SYNC_MASK	= 1,
+
+	EMAC_SPEED_SHIFT	= 1,
+	EMAC_SPEEC_MASK		= 1,
+
+	EMAC_MODE_SHIFT		= 0,
+	EMAC_MODE_MASK		= 1,
+};
+
+/* GRF_SOC_CON2 */
+enum {
+	SDIO_CLK_OUT_SR_SHIFT	= 15,
+	SDIO_CLK_OUT_SR_MASK	= 1,
+
+	MEM_EMA_L2C_SHIFT	= 11,
+	MEM_EMA_L2C_MASK	= 7,
+
+	MEM_EMA_A9_SHIFT	= 8,
+	MEM_EMA_A9_MASK		= 7,
+
+	MSCH4_MAINDDR3_SHIFT	= 7,
+	MSCH4_MAINDDR3_MASK	= 1,
+	MSCH4_MAINDDR3_DDR3	= 1,
+
+	EMAC_NEWRCV_EN_SHIFT	= 6,
+	EMAC_NEWRCV_EN_MASK	= 1,
+
+	SW_ADDR15_EN_SHIFT	= 5,
+	SW_ADDR15_EN_MASK	= 1,
+
+	SW_ADDR16_EN_SHIFT	= 4,
+	SW_ADDR16_EN_MASK	= 1,
+
+	SW_ADDR17_EN_SHIFT	= 3,
+	SW_ADDR17_EN_MASK	= 1,
+
+	BANK2_TO_RANK_EN_SHIFT	= 2,
+	BANK2_TO_RANK_EN_MASK	= 1,
+
+	RANK_TO_ROW15_EN_SHIFT	= 1,
+	RANK_TO_ROW15_EN_MASK	= 1,
+
+	UPCTL_C_ACTIVE_IN_SHIFT = 0,
+	UPCTL_C_ACTIVE_IN_MASK	= 1,
+	UPCTL_C_ACTIVE_IN_MAY	= 0,
+	UPCTL_C_ACTIVE_IN_WILL,
+};
+
+/* GRF_DDRC_CON0 */
+enum {
+	DDR_16BIT_EN_SHIFT	= 15,
+	DDR_16BIT_EN_MASK	= 1,
+
+	DTO_LB_SHIFT		= 11,
+	DTO_LB_MASK		= 3,
+
+	DTO_TE_SHIFT		= 9,
+	DTO_TE_MASK		= 3,
+
+	DTO_PDR_SHIFT		= 7,
+	DTO_PDR_MASK		= 3,
+
+	DTO_PDD_SHIFT		= 5,
+	DTO_PDD_MASK		= 3,
+
+	DTO_IOM_SHIFT		= 3,
+	DTO_IOM_MASK		= 3,
+
+	DTO_OE_SHIFT		= 1,
+	DTO_OE_MASK		= 3,
+
+	ATO_AE_SHIFT		= 0,
+	ATO_AE_MASK		= 1,
+};
+#endif
diff --git a/arch/arm/include/asm/arch-rockchip/grf_rk3328.h b/arch/arm/include/asm/arch-rockchip/grf_rk3328.h
new file mode 100644
index 0000000..2776cef
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/grf_rk3328.h
@@ -0,0 +1,134 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __SOC_ROCKCHIP_RK3328_GRF_H__
+#define __SOC_ROCKCHIP_RK3328_GRF_H__
+
+struct rk3328_grf_regs {
+	u32 gpio0a_iomux;
+	u32 gpio0b_iomux;
+	u32 gpio0c_iomux;
+	u32 gpio0d_iomux;
+	u32 gpio1a_iomux;
+	u32 gpio1b_iomux;
+	u32 gpio1c_iomux;
+	u32 gpio1d_iomux;
+	u32 gpio2a_iomux;
+	u32 gpio2bl_iomux;
+	u32 gpio2bh_iomux;
+	u32 gpio2cl_iomux;
+	u32 gpio2ch_iomux;
+	u32 gpio2d_iomux;
+	u32 gpio3al_iomux;
+	u32 gpio3ah_iomux;
+	u32 gpio3bl_iomux;
+	u32 gpio3bh_iomux;
+	u32 gpio3c_iomux;
+	u32 gpio3d_iomux;
+	u32 com_iomux;
+	u32 reserved1[(0x100 - 0x54) / 4];
+
+	u32 gpio0a_p;
+	u32 gpio0b_p;
+	u32 gpio0c_p;
+	u32 gpio0d_p;
+	u32 gpio1a_p;
+	u32 gpio1b_p;
+	u32 gpio1c_p;
+	u32 gpio1d_p;
+	u32 gpio2a_p;
+	u32 gpio2b_p;
+	u32 gpio2c_p;
+	u32 gpio2d_p;
+	u32 gpio3a_p;
+	u32 gpio3b_p;
+	u32 gpio3c_p;
+	u32 gpio3d_p;
+	u32 reserved2[(0x200 - 0x140) / 4];
+	u32 gpio0a_e;
+	u32 gpio0b_e;
+	u32 gpio0c_e;
+	u32 gpio0d_e;
+	u32 gpio1a_e;
+	u32 gpio1b_e;
+	u32 gpio1c_e;
+	u32 gpio1d_e;
+	u32 gpio2a_e;
+	u32 gpio2b_e;
+	u32 gpio2c_e;
+	u32 gpio2d_e;
+	u32 gpio3a_e;
+	u32 gpio3b_e;
+	u32 gpio3c_e;
+	u32 gpio3d_e;
+	u32 reserved3[(0x300 - 0x240) / 4];
+	u32 gpio0l_sr;
+	u32 gpio0h_sr;
+	u32 gpio1l_sr;
+	u32 gpio1h_sr;
+	u32 gpio2l_sr;
+	u32 gpio2h_sr;
+	u32 gpio3l_sr;
+	u32 gpio3h_sr;
+	u32 reserved4[(0x380 - 0x320) / 4];
+	u32 gpio0l_smt;
+	u32 gpio0h_smt;
+	u32 gpio1l_smt;
+	u32 gpio1h_smt;
+	u32 gpio2l_smt;
+	u32 gpio2h_smt;
+	u32 gpio3l_smt;
+	u32 gpio3h_smt;
+	u32 reserved5[(0x400 - 0x3a0) / 4];
+	u32 soc_con[11];
+	u32 reserved6[(0x480 - 0x42c) / 4];
+	u32 soc_status[5];
+	u32 reserved7[(0x4c0 - 0x494) / 4];
+	u32 otg3_con[2];
+	u32 reserved8[(0x500 - 0x4c8) / 4];
+	u32 cpu_con[2];
+	u32 reserved9[(0x520 - 0x508) / 4];
+	u32 cpu_status[2];
+	u32 reserved10[(0x5c8 - 0x528) / 4];
+	u32 os_reg[8];
+	u32 reserved11[(0x680 - 0x5e8) / 4];
+	u32 sig_detect_con;
+	u32 reserved12[3];
+	u32 sig_detect_status;
+	u32 reserved13[3];
+	u32 sig_detect_status_clr;
+	u32 reserved14[3];
+
+	u32 sdmmc_det_counter;
+	u32 reserved15[(0x700 - 0x6b4) / 4];
+	u32 host0_con[3];
+	u32 reserved16[(0x880 - 0x70c) / 4];
+	u32 otg_con0;
+	u32 reserved17[3];
+	u32 host0_status;
+	u32 reserved18[(0x900 - 0x894) / 4];
+	u32 mac_con[3];
+	u32 reserved19[(0xb00 - 0x90c) / 4];
+	u32 macphy_con[4];
+	u32 macphy_status;
+};
+check_member(rk3328_grf_regs, macphy_status, 0xb10);
+
+struct rk3328_sgrf_regs {
+	u32 soc_con[6];
+	u32 reserved0[(0x100 - 0x18) / 4];
+	u32 dmac_con[6];
+	u32 reserved1[(0x180 - 0x118) / 4];
+	u32 fast_boot_addr;
+	u32 reserved2[(0x200 - 0x184) / 4];
+	u32 chip_fuse_con;
+	u32 reserved3[(0x280 - 0x204) / 4];
+	u32 hdcp_key_reg[8];
+	u32 hdcp_key_access_mask;
+};
+check_member(rk3328_sgrf_regs, hdcp_key_access_mask, 0x2a0);
+
+#endif	/* __SOC_ROCKCHIP_RK3328_GRF_H__ */
diff --git a/arch/arm/include/asm/arch-rockchip/grf_rk3399.h b/arch/arm/include/asm/arch-rockchip/grf_rk3399.h
index d3d1467..62d8496 100644
--- a/arch/arm/include/asm/arch-rockchip/grf_rk3399.h
+++ b/arch/arm/include/asm/arch-rockchip/grf_rk3399.h
@@ -318,4 +318,122 @@
 };
 check_member(rk3399_pmusgrf_regs, slv_secure_con4, 0xe3d4);
 
+enum {
+	/* GRF_GPIO2B_IOMUX */
+	GRF_GPIO2B1_SEL_SHIFT	= 0,
+	GRF_GPIO2B1_SEL_MASK	= 3 << GRF_GPIO2B1_SEL_SHIFT,
+	GRF_SPI2TPM_RXD		= 1,
+	GRF_GPIO2B2_SEL_SHIFT	= 2,
+	GRF_GPIO2B2_SEL_MASK	= 3 << GRF_GPIO2B2_SEL_SHIFT,
+	GRF_SPI2TPM_TXD		= 1,
+	GRF_GPIO2B3_SEL_SHIFT	= 6,
+	GRF_GPIO2B3_SEL_MASK	= 3 << GRF_GPIO2B3_SEL_SHIFT,
+	GRF_SPI2TPM_CLK		= 1,
+	GRF_GPIO2B4_SEL_SHIFT	= 8,
+	GRF_GPIO2B4_SEL_MASK	= 3 << GRF_GPIO2B4_SEL_SHIFT,
+	GRF_SPI2TPM_CSN0	= 1,
+
+	/* GRF_GPIO3A_IOMUX */
+	GRF_GPIO3A4_SEL_SHIFT	= 8,
+	GRF_GPIO3A4_SEL_MASK	= 3 << GRF_GPIO3A4_SEL_SHIFT,
+	GRF_SPI0NORCODEC_RXD	= 2,
+	GRF_GPIO3A5_SEL_SHIFT	= 10,
+	GRF_GPIO3A5_SEL_MASK	= 3 << GRF_GPIO3A5_SEL_SHIFT,
+	GRF_SPI0NORCODEC_TXD	= 2,
+	GRF_GPIO3A6_SEL_SHIFT	= 12,
+	GRF_GPIO3A6_SEL_MASK	= 3 << GRF_GPIO3A6_SEL_SHIFT,
+	GRF_SPI0NORCODEC_CLK	= 2,
+	GRF_GPIO3A7_SEL_SHIFT	= 14,
+	GRF_GPIO3A7_SEL_MASK	= 3 << GRF_GPIO3A7_SEL_SHIFT,
+	GRF_SPI0NORCODEC_CSN0	= 2,
+
+	/* GRF_GPIO3B_IOMUX */
+	GRF_GPIO3B0_SEL_SHIFT	= 0,
+	GRF_GPIO3B0_SEL_MASK	= 3 << GRF_GPIO3B0_SEL_SHIFT,
+	GRF_SPI0NORCODEC_CSN1	= 2,
+
+	/* GRF_GPIO4B_IOMUX */
+	GRF_GPIO4B0_SEL_SHIFT	= 0,
+	GRF_GPIO4B0_SEL_MASK	= 3 << GRF_GPIO4B0_SEL_SHIFT,
+	GRF_SDMMC_DATA0		= 1,
+	GRF_UART2DBGA_SIN	= 2,
+	GRF_GPIO4B1_SEL_SHIFT	= 2,
+	GRF_GPIO4B1_SEL_MASK	= 3 << GRF_GPIO4B1_SEL_SHIFT,
+	GRF_SDMMC_DATA1		= 1,
+	GRF_UART2DBGA_SOUT	= 2,
+	GRF_GPIO4B2_SEL_SHIFT	= 4,
+	GRF_GPIO4B2_SEL_MASK	= 3 << GRF_GPIO4B2_SEL_SHIFT,
+	GRF_SDMMC_DATA2		= 1,
+	GRF_GPIO4B3_SEL_SHIFT	= 6,
+	GRF_GPIO4B3_SEL_MASK	= 3 << GRF_GPIO4B3_SEL_SHIFT,
+	GRF_SDMMC_DATA3		= 1,
+	GRF_GPIO4B4_SEL_SHIFT	= 8,
+	GRF_GPIO4B4_SEL_MASK    = 3 << GRF_GPIO4B4_SEL_SHIFT,
+	GRF_SDMMC_CLKOUT        = 1,
+	GRF_GPIO4B5_SEL_SHIFT   = 10,
+	GRF_GPIO4B5_SEL_MASK    = 3 << GRF_GPIO4B5_SEL_SHIFT,
+	GRF_SDMMC_CMD           = 1,
+
+	/*  GRF_GPIO4C_IOMUX */
+	GRF_GPIO4C0_SEL_SHIFT   = 0,
+	GRF_GPIO4C0_SEL_MASK    = 3 << GRF_GPIO4C0_SEL_SHIFT,
+	GRF_UART2DGBB_SIN       = 2,
+	GRF_GPIO4C1_SEL_SHIFT   = 2,
+	GRF_GPIO4C1_SEL_MASK    = 3 << GRF_GPIO4C1_SEL_SHIFT,
+	GRF_UART2DGBB_SOUT      = 2,
+	GRF_GPIO4C2_SEL_SHIFT   = 4,
+	GRF_GPIO4C2_SEL_MASK    = 3 << GRF_GPIO4C2_SEL_SHIFT,
+	GRF_PWM_0               = 1,
+	GRF_GPIO4C3_SEL_SHIFT   = 6,
+	GRF_GPIO4C3_SEL_MASK    = 3 << GRF_GPIO4C3_SEL_SHIFT,
+	GRF_UART2DGBC_SIN       = 1,
+	GRF_GPIO4C4_SEL_SHIFT   = 8,
+	GRF_GPIO4C4_SEL_MASK    = 3 << GRF_GPIO4C4_SEL_SHIFT,
+	GRF_UART2DBGC_SOUT      = 1,
+	GRF_GPIO4C6_SEL_SHIFT   = 12,
+	GRF_GPIO4C6_SEL_MASK    = 3 << GRF_GPIO4C6_SEL_SHIFT,
+	GRF_PWM_1               = 1,
+
+	/* GRF_SOC_CON7 */
+	GRF_UART_DBG_SEL_SHIFT	= 10,
+	GRF_UART_DBG_SEL_MASK	= 3 << GRF_UART_DBG_SEL_SHIFT,
+	GRF_UART_DBG_SEL_C	= 2,
+
+	/*  PMUGRF_GPIO0A_IOMUX */
+	PMUGRF_GPIO0A6_SEL_SHIFT        = 12,
+	PMUGRF_GPIO0A6_SEL_MASK = 3 << PMUGRF_GPIO0A6_SEL_SHIFT,
+	PMUGRF_PWM_3A           = 1,
+
+	/*  PMUGRF_GPIO1A_IOMUX */
+	PMUGRF_GPIO1A7_SEL_SHIFT        = 14,
+	PMUGRF_GPIO1A7_SEL_MASK = 3 << PMUGRF_GPIO1A7_SEL_SHIFT,
+	PMUGRF_SPI1EC_RXD       = 2,
+
+	/*  PMUGRF_GPIO1B_IOMUX */
+	PMUGRF_GPIO1B0_SEL_SHIFT        = 0,
+	PMUGRF_GPIO1B0_SEL_MASK = 3 << PMUGRF_GPIO1B0_SEL_SHIFT,
+	PMUGRF_SPI1EC_TXD       = 2,
+	PMUGRF_GPIO1B1_SEL_SHIFT        = 2,
+	PMUGRF_GPIO1B1_SEL_MASK = 3 << PMUGRF_GPIO1B1_SEL_SHIFT,
+	PMUGRF_SPI1EC_CLK       = 2,
+	PMUGRF_GPIO1B2_SEL_SHIFT        = 4,
+	PMUGRF_GPIO1B2_SEL_MASK = 3 << PMUGRF_GPIO1B2_SEL_SHIFT,
+	PMUGRF_SPI1EC_CSN0      = 2,
+	PMUGRF_GPIO1B6_SEL_SHIFT        = 12,
+	PMUGRF_GPIO1B6_SEL_MASK = 3 << PMUGRF_GPIO1B6_SEL_SHIFT,
+	PMUGRF_PWM_3B           = 1,
+	PMUGRF_GPIO1B7_SEL_SHIFT        = 14,
+	PMUGRF_GPIO1B7_SEL_MASK = 3 << PMUGRF_GPIO1B7_SEL_SHIFT,
+	PMUGRF_I2C0PMU_SDA      = 2,
+
+	/*  PMUGRF_GPIO1C_IOMUX */
+	PMUGRF_GPIO1C0_SEL_SHIFT        = 0,
+	PMUGRF_GPIO1C0_SEL_MASK = 3 << PMUGRF_GPIO1C0_SEL_SHIFT,
+	PMUGRF_I2C0PMU_SCL      = 2,
+	PMUGRF_GPIO1C3_SEL_SHIFT        = 6,
+	PMUGRF_GPIO1C3_SEL_MASK = 3 << PMUGRF_GPIO1C3_SEL_SHIFT,
+	PMUGRF_PWM_2            = 1,
+
+};
+
 #endif	/* __SOC_ROCKCHIP_RK3399_GRF_H__ */
diff --git a/arch/arm/include/asm/arch-rockchip/pmu_rk3188.h b/arch/arm/include/asm/arch-rockchip/pmu_rk3188.h
new file mode 100644
index 0000000..d3feac3
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/pmu_rk3188.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _ASM_ARCH_PMU_RK3188_H
+#define _ASM_ARCH_PMU_RK3188_H
+
+struct rk3188_pmu {
+	u32 wakeup_cfg[2];
+	u32 pwrdn_con;
+	u32 pwrdn_st;
+
+	u32 int_con;
+	u32 int_st;
+	u32 misc_con;
+
+	u32 osc_cnt;
+	u32 pll_cnt;
+	u32 pmu_cnt;
+	u32 ddrio_pwron_cnt;
+	u32 wakeup_rst_clr_cnt;
+	u32 scu_pwrdwn_cnt;
+	u32 scu_pwrup_cnt;
+	u32 misc_con1;
+	u32 gpio0_con;
+
+	u32 sys_reg[4];
+	u32 reserved0[4];
+	u32 stop_int_dly;
+	u32 gpio0_p[2];
+};
+check_member(rk3188_pmu, gpio0_p[1], 0x0068);
+
+#endif
diff --git a/arch/arm/include/asm/arch-rockchip/sdram_rk3399.h b/arch/arm/include/asm/arch-rockchip/sdram_rk3399.h
new file mode 100644
index 0000000..22a6ca9
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/sdram_rk3399.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016-2017 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef _ASM_ARCH_SDRAM_RK3399_H
+#define _ASM_ARCH_SDRAM_RK3399_H
+
+enum {
+	DDR3 = 0x3,
+	LPDDR2 = 0x5,
+	LPDDR3 = 0x6,
+	LPDDR4 = 0x7,
+	UNUSED = 0xFF
+};
+
+struct rk3399_ddr_pctl_regs {
+	u32 denali_ctl[332];
+};
+
+struct rk3399_ddr_publ_regs {
+	u32 denali_phy[959];
+};
+
+struct rk3399_ddr_pi_regs {
+	u32 denali_pi[200];
+};
+
+struct rk3399_msch_regs {
+	u32 coreid;
+	u32 revisionid;
+	u32 ddrconf;
+	u32 ddrsize;
+	u32 ddrtiminga0;
+	u32 ddrtimingb0;
+	u32 ddrtimingc0;
+	u32 devtodev0;
+	u32 reserved0[(0x110 - 0x20) / 4];
+	u32 ddrmode;
+	u32 reserved1[(0x1000 - 0x114) / 4];
+	u32 agingx0;
+};
+
+struct rk3399_msch_timings {
+	u32 ddrtiminga0;
+	u32 ddrtimingb0;
+	u32 ddrtimingc0;
+	u32 devtodev0;
+	u32 ddrmode;
+	u32 agingx0;
+};
+
+struct rk3399_ddr_cic_regs {
+	u32 cic_ctrl0;
+	u32 cic_ctrl1;
+	u32 cic_idle_th;
+	u32 cic_cg_wait_th;
+	u32 cic_status0;
+	u32 cic_status1;
+	u32 cic_ctrl2;
+	u32 cic_ctrl3;
+	u32 cic_ctrl4;
+};
+
+/* DENALI_CTL_00 */
+#define START		1
+
+/* DENALI_CTL_68 */
+#define PWRUP_SREFRESH_EXIT	(1 << 16)
+
+/* DENALI_CTL_274 */
+#define MEM_RST_VALID	1
+
+struct rk3399_sdram_channel {
+	unsigned int rank;
+	/* dram column number, 0 means this channel is invalid */
+	unsigned int col;
+	/* dram bank number, 3:8bank, 2:4bank */
+	unsigned int bk;
+	/* channel buswidth, 2:32bit, 1:16bit, 0:8bit */
+	unsigned int bw;
+	/* die buswidth, 2:32bit, 1:16bit, 0:8bit */
+	unsigned int dbw;
+	/*
+	 * row_3_4 = 1: 6Gb or 12Gb die
+	 * row_3_4 = 0: normal die, power of 2
+	 */
+	unsigned int row_3_4;
+	unsigned int cs0_row;
+	unsigned int cs1_row;
+	unsigned int ddrconfig;
+	struct rk3399_msch_timings noc_timings;
+};
+
+struct rk3399_base_params {
+	unsigned int ddr_freq;
+	unsigned int dramtype;
+	unsigned int num_channels;
+	unsigned int stride;
+	unsigned int odt;
+};
+
+struct rk3399_sdram_params {
+	struct rk3399_sdram_channel ch[2];
+	struct rk3399_base_params base;
+	struct rk3399_ddr_pctl_regs pctl_regs;
+	struct rk3399_ddr_pi_regs pi_regs;
+	struct rk3399_ddr_publ_regs phy_regs;
+};
+
+#define PI_CA_TRAINING		(1 << 0)
+#define PI_WRITE_LEVELING	(1 << 1)
+#define PI_READ_GATE_TRAINING	(1 << 2)
+#define PI_READ_LEVELING	(1 << 3)
+#define PI_WDQ_LEVELING		(1 << 4)
+#define PI_FULL_TRAINING	0xff
+
+#endif
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
index 5c4a4c2..bf8e6be 100644
--- a/arch/arm/mach-rockchip/Kconfig
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -11,6 +11,21 @@
 	  and video codec support. Peripherals include Gigabit Ethernet,
 	  USB2 host and OTG, SDIO, I2S, UART, SPI, I2C and PWMs.
 
+config ROCKCHIP_RK3188
+	bool "Support Rockchip RK3188"
+	select CPU_V7
+	select SUPPORT_SPL
+	select SUPPORT_TPL
+	select SPL
+	select TPL
+	select ROCKCHIP_BROM_HELPER
+	help
+	  The Rockchip RK3188 is a ARM-based SoC with a quad-core Cortex-A9
+	  including NEON and GPU, 512KB L2 cache, Mali-400 graphics, two
+	  video interfaces, several memory options and video codec support.
+	  Peripherals include Fast Ethernet, USB2 host and OTG, SDIO, I2S,
+	  UART, SPI, I2C and PWMs.
+
 config ROCKCHIP_RK3288
 	bool "Support Rockchip RK3288"
 	select CPU_V7
@@ -23,9 +38,22 @@
 	  and video codec support. Peripherals include Gigabit Ethernet,
 	  USB2 host and OTG, SDIO, I2S, UARTs, SPI, I2C and PWMs.
 
+config ROCKCHIP_RK3328
+	bool "Support Rockchip RK3328"
+	select ARM64
+	help
+	  The Rockchip RK3328 is a ARM-based SoC with a quad-core Cortex-A53.
+	  including NEON and GPU, 1MB L2 cache, Mali-T7 graphics, two
+	  video interfaces supporting HDMI and eDP, several DDR3 options
+	  and video codec support. Peripherals include Gigabit Ethernet,
+	  USB2 host and OTG, SDIO, I2S, UARTs, SPI, I2C and PWMs.
+
 config ROCKCHIP_RK3399
 	bool "Support Rockchip RK3399"
 	select ARM64
+	select SUPPORT_SPL
+	select SPL
+	select SPL_SEPARATE_BSS
 	help
 	  The Rockchip RK3399 is a ARM-based SoC with a dual-core Cortex-A72
 	  and quad-core Cortex-A53.
@@ -37,15 +65,21 @@
 config ROCKCHIP_SPL_BACK_TO_BROM
 	bool "SPL returns to bootrom"
 	default y if ROCKCHIP_RK3036
+	select ROCKCHIP_BROM_HELPER
 	help
 	  Rockchip SoCs have ability to load SPL & U-Boot binary. If enabled,
           SPL will return to the boot rom, which will then load the U-Boot
           binary to keep going on.
 
+config ROCKCHIP_BROM_HELPER
+	bool
+
 config SPL_MMC_SUPPORT
 	default y if !ROCKCHIP_SPL_BACK_TO_BROM
 
 source "arch/arm/mach-rockchip/rk3036/Kconfig"
+source "arch/arm/mach-rockchip/rk3188/Kconfig"
 source "arch/arm/mach-rockchip/rk3288/Kconfig"
+source "arch/arm/mach-rockchip/rk3328/Kconfig"
 source "arch/arm/mach-rockchip/rk3399/Kconfig"
 endif
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index 6e79fed..6b251c7 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -4,11 +4,17 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
-ifdef CONFIG_SPL_BUILD
+ifdef CONFIG_TPL_BUILD
+obj-$(CONFIG_ROCKCHIP_RK3188) += rk3188-board-tpl.o
+obj-$(CONFIG_ROCKCHIP_BROM_HELPER) += save_boot_param.o
+else ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_ROCKCHIP_RK3036) += rk3036-board-spl.o
+obj-$(CONFIG_ROCKCHIP_RK3188) += rk3188-board-spl.o
 obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288-board-spl.o
-obj-$(CONFIG_ROCKCHIP_SPL_BACK_TO_BROM) += save_boot_param.o
+obj-$(CONFIG_ROCKCHIP_RK3399) += rk3399-board-spl.o
+obj-$(CONFIG_ROCKCHIP_BROM_HELPER) += save_boot_param.o
 else
+obj-$(CONFIG_ROCKCHIP_RK3188) += rk3188-board.o
 obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288-board.o
 obj-$(CONFIG_ROCKCHIP_RK3036) += rk3036-board.o
 endif
@@ -16,5 +22,11 @@
 obj-y += rk_timer.o
 endif
 obj-$(CONFIG_ROCKCHIP_RK3036) += rk3036/
+
+ifndef CONFIG_TPL_BUILD
+obj-$(CONFIG_ROCKCHIP_RK3188) += rk3188/
+endif
+
 obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288/
+obj-$(CONFIG_ROCKCHIP_RK3328) += rk3328/
 obj-$(CONFIG_ROCKCHIP_RK3399) += rk3399/
diff --git a/arch/arm/mach-rockchip/rk3036-board-spl.c b/arch/arm/mach-rockchip/rk3036-board-spl.c
index 8015481..0522d65 100644
--- a/arch/arm/mach-rockchip/rk3036-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3036-board-spl.c
@@ -7,6 +7,7 @@
 #include <common.h>
 #include <debug_uart.h>
 #include <asm/io.h>
+#include <asm/arch/bootrom.h>
 #include <asm/arch/grf_rk3036.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/sdram_rk3036.h>
@@ -20,8 +21,6 @@
 
 #define DEBUG_UART_BASE	0x20068000
 
-extern void back_to_bootrom(void);
-
 void board_init_f(ulong dummy)
 {
 #ifdef EARLY_DEBUG
diff --git a/arch/arm/mach-rockchip/rk3188-board-spl.c b/arch/arm/mach-rockchip/rk3188-board-spl.c
new file mode 100644
index 0000000..f93feae0
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3188-board-spl.c
@@ -0,0 +1,218 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <debug_uart.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <led.h>
+#include <malloc.h>
+#include <ram.h>
+#include <spl.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/bootrom.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/periph.h>
+#include <asm/arch/pmu_rk3188.h>
+#include <asm/arch/sdram.h>
+#include <asm/arch/timer.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/util.h>
+#include <power/regulator.h>
+#include <syscon.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+u32 spl_boot_device(void)
+{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+	const void *blob = gd->fdt_blob;
+	struct udevice *dev;
+	const char *bootdev;
+	int node;
+	int ret;
+
+	bootdev = fdtdec_get_config_string(blob, "u-boot,boot0");
+	debug("Boot device %s\n", bootdev);
+	if (!bootdev)
+		goto fallback;
+
+	node = fdt_path_offset(blob, bootdev);
+	if (node < 0) {
+		debug("node=%d\n", node);
+		goto fallback;
+	}
+	ret = device_get_global_by_of_offset(node, &dev);
+	if (ret) {
+		debug("device at node %s/%d not found: %d\n", bootdev, node,
+		      ret);
+		goto fallback;
+	}
+	debug("Found device %s\n", dev->name);
+	switch (device_get_uclass_id(dev)) {
+	case UCLASS_SPI_FLASH:
+		return BOOT_DEVICE_SPI;
+	case UCLASS_MMC:
+		return BOOT_DEVICE_MMC1;
+	default:
+		debug("Booting from device uclass '%s' not supported\n",
+		      dev_get_uclass_name(dev));
+	}
+
+fallback:
+#endif
+	return BOOT_DEVICE_MMC1;
+}
+
+u32 spl_boot_mode(const u32 boot_device)
+{
+	return MMCSD_MODE_RAW;
+}
+
+void board_init_f(ulong dummy)
+{
+	struct udevice *pinctrl, *dev;
+	struct rk3188_pmu *pmu;
+	int ret;
+
+	/* Example code showing how to enable the debug UART on RK3188 */
+#ifdef EARLY_UART
+#include <asm/arch/grf_rk3188.h>
+	/* Enable early UART on the RK3188 */
+#define GRF_BASE	0x20008000
+	struct rk3188_grf * const grf = (void *)GRF_BASE;
+
+	rk_clrsetreg(&grf->gpio1b_iomux,
+		     GPIO1B1_MASK << GPIO1B1_SHIFT |
+		     GPIO1B0_MASK << GPIO1B0_SHIFT,
+		     GPIO1B1_UART2_SOUT << GPIO1B1_SHIFT |
+		     GPIO1B0_UART2_SIN << GPIO1B0_SHIFT);
+	/*
+	 * Debug UART can be used from here if required:
+	 *
+	 * debug_uart_init();
+	 * printch('a');
+	 * printhex8(0x1234);
+	 * printascii("string");
+	 */
+	debug_uart_init();
+	printch('s');
+	printch('p');
+	printch('l');
+	printch('\n');
+#endif
+
+	ret = spl_init();
+	if (ret) {
+		debug("spl_init() failed: %d\n", ret);
+		hang();
+	}
+
+	rockchip_timer_init();
+
+	ret = rockchip_get_clk(&dev);
+	if (ret) {
+		debug("CLK init failed: %d\n", ret);
+		return;
+	}
+
+	/*
+	 * Recover the bootrom's stackpointer.
+	 * For whatever reason needs to run after rockchip_get_clk.
+	 */
+	pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU);
+	if (IS_ERR(pmu))
+		error("pmu syscon returned %ld\n", PTR_ERR(pmu));
+	SAVE_SP_ADDR = readl(&pmu->sys_reg[2]);
+
+	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
+	if (ret) {
+		debug("Pinctrl init failed: %d\n", ret);
+		return;
+	}
+
+	ret = uclass_get_device(UCLASS_RAM, 0, &dev);
+	if (ret) {
+		debug("DRAM init failed: %d\n", ret);
+		return;
+	}
+
+#if defined(CONFIG_ROCKCHIP_SPL_BACK_TO_BROM) && !defined(CONFIG_SPL_BOARD_INIT)
+	back_to_bootrom();
+#endif
+}
+
+static int setup_led(void)
+{
+#ifdef CONFIG_SPL_LED
+	struct udevice *dev;
+	char *led_name;
+	int ret;
+
+	led_name = fdtdec_get_config_string(gd->fdt_blob, "u-boot,boot-led");
+	if (!led_name)
+		return 0;
+	ret = led_get_by_label(led_name, &dev);
+	if (ret) {
+		debug("%s: get=%d\n", __func__, ret);
+		return ret;
+	}
+	ret = led_set_on(dev, 1);
+	if (ret)
+		return ret;
+#endif
+
+	return 0;
+}
+
+void spl_board_init(void)
+{
+	struct udevice *pinctrl;
+	int ret;
+
+	ret = setup_led();
+	if (ret) {
+		debug("LED ret=%d\n", ret);
+		hang();
+	}
+
+	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
+	if (ret) {
+		debug("%s: Cannot find pinctrl device\n", __func__);
+		goto err;
+	}
+
+#ifdef CONFIG_SPL_MMC_SUPPORT
+	ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_SDCARD);
+	if (ret) {
+		debug("%s: Failed to set up SD card\n", __func__);
+		goto err;
+	}
+#endif
+
+	/* 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();
+#ifdef CONFIG_ROCKCHIP_SPL_BACK_TO_BROM
+	back_to_bootrom();
+#endif
+	return;
+
+err:
+	printf("spl_board_init: Error %d\n", ret);
+
+	/* No way to report error here */
+	hang();
+}
diff --git a/arch/arm/mach-rockchip/rk3188-board-tpl.c b/arch/arm/mach-rockchip/rk3188-board-tpl.c
new file mode 100644
index 0000000..442bfe7
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3188-board-tpl.c
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <debug_uart.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/bootrom.h>
+#include <asm/arch/pmu_rk3188.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* track how often we were entered */
+static int rk3188_num_entries __attribute__ ((section(".data")));
+
+#define PMU_BASE	0x20004000
+#define TPL_ENTRY	0x10080C00
+
+static void jump_to_spl(void)
+{
+	typedef void __noreturn (*image_entry_noargs_t)(void);
+
+	struct rk3188_pmu * const pmu = (void *)PMU_BASE;
+	image_entry_noargs_t tpl_entry =
+		(image_entry_noargs_t)(unsigned long)TPL_ENTRY;
+
+	/* Store the SAVE_SP_ADDR in a location shared with TPL. */
+	writel(SAVE_SP_ADDR, &pmu->sys_reg[2]);
+	tpl_entry();
+}
+
+void board_init_f(ulong dummy)
+{
+	/* Example code showing how to enable the debug UART on RK3188 */
+#ifdef EARLY_UART
+#include <asm/arch/grf_rk3188.h>
+	/* Enable early UART on the RK3188 */
+#define GRF_BASE	0x20008000
+	struct rk3188_grf * const grf = (void *)GRF_BASE;
+
+	rk_clrsetreg(&grf->gpio1b_iomux,
+		     GPIO1B1_MASK << GPIO1B1_SHIFT |
+		     GPIO1B0_MASK << GPIO1B0_SHIFT,
+		     GPIO1B1_UART2_SOUT << GPIO1B1_SHIFT |
+		     GPIO1B0_UART2_SIN << GPIO1B0_SHIFT);
+	/*
+	 * Debug UART can be used from here if required:
+	 *
+	 * debug_uart_init();
+	 * printch('a');
+	 * printhex8(0x1234);
+	 * printascii("string");
+	 */
+	debug_uart_init();
+
+	printch('t');
+	printch('p');
+	printch('l');
+	printch('-');
+	printch(rk3188_num_entries + 1 + '0');
+	printch('\n');
+#endif
+
+	rk3188_num_entries++;
+
+	if (rk3188_num_entries == 1) {
+		/*
+		 * The original loader did some very basic integrity
+		 * checking at this point, but the remaining few bytes
+		 * could be used for any improvement making sense
+		 * really early on.
+		 */
+
+		back_to_bootrom();
+	} else {
+		/*
+		 * TPL part of the loader should now wait for us
+		 * at offset 0xC00 in the sram. Should never return
+		 * from there.
+		 */
+		jump_to_spl();
+	}
+}
diff --git a/arch/arm/mach-rockchip/rk3188-board.c b/arch/arm/mach-rockchip/rk3188-board.c
new file mode 100644
index 0000000..16f3855
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3188-board.c
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <ram.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/periph.h>
+#include <asm/arch/pmu_rk3288.h>
+#include <asm/arch/boot_mode.h>
+#include <asm/gpio.h>
+#include <dm/pinctrl.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int board_init(void)
+{
+#if defined(CONFIG_ROCKCHIP_SPL_BACK_TO_BROM)
+	struct udevice *pinctrl;
+	int ret;
+
+	/*
+	 * We need to implement sdcard iomux here for the further
+	 * initialization, otherwise, it'll hit sdcard command sending
+	 * timeout exception.
+	 */
+	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
+	if (ret) {
+		debug("%s: Cannot find pinctrl device\n", __func__);
+		goto err;
+	}
+	ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_SDCARD);
+	if (ret) {
+		debug("%s: Failed to set up SD card\n", __func__);
+		goto err;
+	}
+
+	return 0;
+err:
+	printf("board_init: Error %d\n", ret);
+
+	/* No way to report error here */
+	hang();
+
+	return -1;
+#else
+	return 0;
+#endif
+}
+
+int dram_init(void)
+{
+	/* FIXME: read back ram size from sys_reg2 */
+	gd->ram_size = 0x40000000;
+
+	return 0;
+}
+
+#ifndef CONFIG_SYS_DCACHE_OFF
+void enable_caches(void)
+{
+	/* Enable D-cache. I-cache is already enabled in start.S */
+	dcache_enable();
+}
+#endif
diff --git a/arch/arm/mach-rockchip/rk3188/Kconfig b/arch/arm/mach-rockchip/rk3188/Kconfig
new file mode 100644
index 0000000..f8e1d03
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3188/Kconfig
@@ -0,0 +1,24 @@
+if ROCKCHIP_RK3188
+
+config SYS_SOC
+	default "rockchip"
+
+config SYS_MALLOC_F_LEN
+	default 0x0800
+
+config SPL_LIBCOMMON_SUPPORT
+	default y
+
+config SPL_LIBGENERIC_SUPPORT
+	default y
+
+config SPL_SERIAL_SUPPORT
+	default y
+
+config TPL_LIBCOMMON_SUPPORT
+	default y
+
+config TPL_SERIAL_SUPPORT
+	default y
+
+endif
diff --git a/arch/arm/mach-rockchip/rk3188/Makefile b/arch/arm/mach-rockchip/rk3188/Makefile
new file mode 100644
index 0000000..2dc9511
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3188/Makefile
@@ -0,0 +1,11 @@
+#
+# Copyright (c) 2015 Google, Inc
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+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/rk3188/clk_rk3188.c b/arch/arm/mach-rockchip/rk3188/clk_rk3188.c
new file mode 100644
index 0000000..1ec9e1c
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3188/clk_rk3188.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <syscon.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3188.h>
+
+int rockchip_get_clk(struct udevice **devp)
+{
+	return uclass_get_device_by_driver(UCLASS_CLK,
+			DM_GET_DRIVER(rockchip_rk3188_cru), devp);
+}
+
+void *rockchip_get_cru(void)
+{
+	struct rk3188_clk_priv *priv;
+	struct udevice *dev;
+	int ret;
+
+	ret = rockchip_get_clk(&dev);
+	if (ret)
+		return ERR_PTR(ret);
+
+	priv = dev_get_priv(dev);
+
+	return priv->cru;
+}
diff --git a/arch/arm/mach-rockchip/rk3188/sdram_rk3188.c b/arch/arm/mach-rockchip/rk3188/sdram_rk3188.c
new file mode 100644
index 0000000..461cfcd
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3188/sdram_rk3188.c
@@ -0,0 +1,995 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Copyright 2014 Rockchip Inc.
+ *
+ * SPDX-License-Identifier:     GPL-2.0
+ *
+ * Adapted from the very similar rk3288 ddr init.
+ */
+
+#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_rk3188.h>
+#include <asm/arch/ddr_rk3188.h>
+#include <asm/arch/grf_rk3188.h>
+#include <asm/arch/pmu_rk3188.h>
+#include <asm/arch/sdram.h>
+#include <linux/err.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct chan_info {
+	struct rk3288_ddr_pctl *pctl;
+	struct rk3288_ddr_publ *publ;
+	struct rk3188_msch *msch;
+};
+
+struct dram_info {
+	struct chan_info chan[1];
+	struct ram_info info;
+	struct clk ddr_clk;
+	struct rk3188_cru *cru;
+	struct rk3188_grf *grf;
+	struct rk3188_sgrf *sgrf;
+	struct rk3188_pmu *pmu;
+};
+
+struct rk3188_sdram_params {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct dtd_rockchip_rk3188_dmc of_plat;
+#endif
+	struct rk3288_sdram_channel ch[2];
+	struct rk3288_sdram_pctl_timing pctl_timing;
+	struct rk3288_sdram_phy_timing phy_timing;
+	struct rk3288_base_params base;
+	int num_channels;
+	struct regmap *map;
+};
+
+const int ddrconf_table[] = {
+	/*
+	 * [5:4] row(13+n)
+	 * [1:0] col(9+n), assume bw=2
+	 * row	    col,bw
+	 */
+	0,
+	((2 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT),
+	((1 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT),
+	((0 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT),
+	((2 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT),
+	((1 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT),
+	((0 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT),
+	((1 << DDRCONF_ROW_SHIFT) | 0 << DDRCONF_COL_SHIFT),
+	((0 << DDRCONF_ROW_SHIFT) | 0 << DDRCONF_COL_SHIFT),
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+};
+
+#define TEST_PATTEN	0x5aa5f00f
+#define DQS_GATE_TRAINING_ERROR_RANK0	(1 << 4)
+#define DQS_GATE_TRAINING_ERROR_RANK1	(2 << 4)
+
+#ifdef CONFIG_SPL_BUILD
+static void copy_to_reg(u32 *dest, const u32 *src, u32 n)
+{
+	int i;
+
+	for (i = 0; i < n / sizeof(u32); i++) {
+		writel(*src, dest);
+		src++;
+		dest++;
+	}
+}
+
+static void ddr_reset(struct rk3188_cru *cru, u32 ch, u32 ctl, u32 phy)
+{
+	u32 phy_ctl_srstn_shift = 13;
+	u32 ctl_psrstn_shift = 11;
+	u32 ctl_srstn_shift = 10;
+	u32 phy_psrstn_shift = 9;
+	u32 phy_srstn_shift = 8;
+
+	rk_clrsetreg(&cru->cru_softrst_con[5],
+		     1 << phy_ctl_srstn_shift | 1 << ctl_psrstn_shift |
+		     1 << ctl_srstn_shift | 1 << phy_psrstn_shift |
+		     1 << phy_srstn_shift,
+		     phy << phy_ctl_srstn_shift | ctl << ctl_psrstn_shift |
+		     ctl << ctl_srstn_shift | phy << phy_psrstn_shift |
+		     phy << phy_srstn_shift);
+}
+
+static void ddr_phy_ctl_reset(struct rk3188_cru *cru, u32 ch, u32 n)
+{
+	u32 phy_ctl_srstn_shift = 13;
+
+	rk_clrsetreg(&cru->cru_softrst_con[5],
+		     1 << phy_ctl_srstn_shift, n << phy_ctl_srstn_shift);
+}
+
+static void phy_pctrl_reset(struct rk3188_cru *cru,
+			    struct rk3288_ddr_publ *publ,
+			    int channel)
+{
+	int i;
+
+	ddr_reset(cru, channel, 1, 1);
+	udelay(1);
+	clrbits_le32(&publ->acdllcr, ACDLLCR_DLLSRST);
+	for (i = 0; i < 4; i++)
+		clrbits_le32(&publ->datx8[i].dxdllcr, DXDLLCR_DLLSRST);
+
+	udelay(10);
+	setbits_le32(&publ->acdllcr, ACDLLCR_DLLSRST);
+	for (i = 0; i < 4; i++)
+		setbits_le32(&publ->datx8[i].dxdllcr, DXDLLCR_DLLSRST);
+
+	udelay(10);
+	ddr_reset(cru, channel, 1, 0);
+	udelay(10);
+	ddr_reset(cru, channel, 0, 0);
+	udelay(10);
+}
+
+static void phy_dll_bypass_set(struct rk3288_ddr_publ *publ,
+	u32 freq)
+{
+	int i;
+
+	if (freq <= 250000000) {
+		if (freq <= 150000000)
+			clrbits_le32(&publ->dllgcr, SBIAS_BYPASS);
+		else
+			setbits_le32(&publ->dllgcr, SBIAS_BYPASS);
+		setbits_le32(&publ->acdllcr, ACDLLCR_DLLDIS);
+		for (i = 0; i < 4; i++)
+			setbits_le32(&publ->datx8[i].dxdllcr,
+				     DXDLLCR_DLLDIS);
+
+		setbits_le32(&publ->pir, PIR_DLLBYP);
+	} else {
+		clrbits_le32(&publ->dllgcr, SBIAS_BYPASS);
+		clrbits_le32(&publ->acdllcr, ACDLLCR_DLLDIS);
+		for (i = 0; i < 4; i++) {
+			clrbits_le32(&publ->datx8[i].dxdllcr,
+				     DXDLLCR_DLLDIS);
+		}
+
+		clrbits_le32(&publ->pir, PIR_DLLBYP);
+	}
+}
+
+static void dfi_cfg(struct rk3288_ddr_pctl *pctl, u32 dramtype)
+{
+	writel(DFI_INIT_START, &pctl->dfistcfg0);
+	writel(DFI_DRAM_CLK_SR_EN | DFI_DRAM_CLK_DPD_EN,
+	       &pctl->dfistcfg1);
+	writel(DFI_PARITY_INTR_EN | DFI_PARITY_EN, &pctl->dfistcfg2);
+	writel(7 << TLP_RESP_TIME_SHIFT | LP_SR_EN | LP_PD_EN,
+	       &pctl->dfilpcfg0);
+
+	writel(2 << TCTRL_DELAY_TIME_SHIFT, &pctl->dfitctrldelay);
+	writel(1 << TPHY_WRDATA_TIME_SHIFT, &pctl->dfitphywrdata);
+	writel(0xf << TPHY_RDLAT_TIME_SHIFT, &pctl->dfitphyrdlat);
+	writel(2 << TDRAM_CLK_DIS_TIME_SHIFT, &pctl->dfitdramclkdis);
+	writel(2 << TDRAM_CLK_EN_TIME_SHIFT, &pctl->dfitdramclken);
+	writel(1, &pctl->dfitphyupdtype0);
+
+	/* cs0 and cs1 write odt enable */
+	writel((RANK0_ODT_WRITE_SEL | RANK1_ODT_WRITE_SEL),
+	       &pctl->dfiodtcfg);
+	/* odt write length */
+	writel(7 << ODT_LEN_BL8_W_SHIFT, &pctl->dfiodtcfg1);
+	/* phyupd and ctrlupd disabled */
+	writel(0, &pctl->dfiupdcfg);
+}
+
+static void ddr_set_enable(struct rk3188_grf *grf, uint channel, bool enable)
+{
+	uint val = 0;
+
+	if (enable)
+		val = 1 << DDR_16BIT_EN_SHIFT;
+
+	rk_clrsetreg(&grf->ddrc_con0, 1 << DDR_16BIT_EN_SHIFT, val);
+}
+
+static void ddr_set_ddr3_mode(struct rk3188_grf *grf, uint channel,
+			      bool ddr3_mode)
+{
+	uint mask, val;
+
+	mask = MSCH4_MAINDDR3_MASK << MSCH4_MAINDDR3_SHIFT;
+	val = ddr3_mode << MSCH4_MAINDDR3_SHIFT;
+	rk_clrsetreg(&grf->soc_con2, mask, val);
+}
+
+static void ddr_rank_2_row15en(struct rk3188_grf *grf, bool enable)
+{
+	uint mask, val;
+
+	mask = RANK_TO_ROW15_EN_MASK << RANK_TO_ROW15_EN_SHIFT;
+	val = enable << RANK_TO_ROW15_EN_SHIFT;
+	rk_clrsetreg(&grf->soc_con2, mask, val);
+}
+
+static void pctl_cfg(int channel, struct rk3288_ddr_pctl *pctl,
+		     struct rk3188_sdram_params *sdram_params,
+		     struct rk3188_grf *grf)
+{
+	copy_to_reg(&pctl->togcnt1u, &sdram_params->pctl_timing.togcnt1u,
+		    sizeof(sdram_params->pctl_timing));
+	switch (sdram_params->base.dramtype) {
+	case DDR3:
+		if (sdram_params->phy_timing.mr[1] & DDR3_DLL_DISABLE) {
+			writel(sdram_params->pctl_timing.tcl - 3,
+			       &pctl->dfitrddataen);
+		} else {
+			writel(sdram_params->pctl_timing.tcl - 2,
+			       &pctl->dfitrddataen);
+		}
+		writel(sdram_params->pctl_timing.tcwl - 1,
+		       &pctl->dfitphywrlat);
+		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);
+		ddr_set_ddr3_mode(grf, channel, true);
+		ddr_set_enable(grf, channel, true);
+		break;
+	}
+
+	setbits_le32(&pctl->scfg, 1);
+}
+
+static void phy_cfg(const struct chan_info *chan, int channel,
+		    struct rk3188_sdram_params *sdram_params)
+{
+	struct rk3288_ddr_publ *publ = chan->publ;
+	struct rk3188_msch *msch = chan->msch;
+	uint ddr_freq_mhz = sdram_params->base.ddr_freq / 1000000;
+	u32 dinit2;
+	int i;
+
+	dinit2 = DIV_ROUND_UP(ddr_freq_mhz * 200000, 1000);
+	/* DDR PHY Timing */
+	copy_to_reg(&publ->dtpr[0], &sdram_params->phy_timing.dtpr0,
+		    sizeof(sdram_params->phy_timing));
+	writel(sdram_params->base.noc_timing, &msch->ddrtiming);
+	writel(0x3f, &msch->readlatency);
+	writel(DIV_ROUND_UP(ddr_freq_mhz * 5120, 1000) << PRT_DLLLOCK_SHIFT |
+	       DIV_ROUND_UP(ddr_freq_mhz * 50, 1000) << PRT_DLLSRST_SHIFT |
+	       8 << PRT_ITMSRST_SHIFT, &publ->ptr[0]);
+	writel(DIV_ROUND_UP(ddr_freq_mhz * 500000, 1000) << PRT_DINIT0_SHIFT |
+	       DIV_ROUND_UP(ddr_freq_mhz * 400, 1000) << PRT_DINIT1_SHIFT,
+	       &publ->ptr[1]);
+	writel(min(dinit2, 0x1ffffU) << PRT_DINIT2_SHIFT |
+	       DIV_ROUND_UP(ddr_freq_mhz * 1000, 1000) << PRT_DINIT3_SHIFT,
+	       &publ->ptr[2]);
+
+	switch (sdram_params->base.dramtype) {
+	case DDR3:
+		clrbits_le32(&publ->pgcr, 0x1f);
+		clrsetbits_le32(&publ->dcr, DDRMD_MASK << DDRMD_SHIFT,
+				DDRMD_DDR3 << DDRMD_SHIFT);
+		break;
+	}
+	if (sdram_params->base.odt) {
+		/*dynamic RTT enable */
+		for (i = 0; i < 4; i++)
+			setbits_le32(&publ->datx8[i].dxgcr, DQSRTT | DQRTT);
+	} else {
+		/*dynamic RTT disable */
+		for (i = 0; i < 4; i++)
+			clrbits_le32(&publ->datx8[i].dxgcr, DQSRTT | DQRTT);
+	}
+}
+
+static void phy_init(struct rk3288_ddr_publ *publ)
+{
+	setbits_le32(&publ->pir, PIR_INIT | PIR_DLLSRST
+		| PIR_DLLLOCK | PIR_ZCAL | PIR_ITMSRST | PIR_CLRSR);
+	udelay(1);
+	while ((readl(&publ->pgsr) &
+		(PGSR_IDONE | PGSR_DLDONE | PGSR_ZCDONE)) !=
+		(PGSR_IDONE | PGSR_DLDONE | PGSR_ZCDONE))
+		;
+}
+
+static void send_command(struct rk3288_ddr_pctl *pctl, u32 rank,
+			 u32 cmd, u32 arg)
+{
+	writel((START_CMD | (rank << 20) | arg | cmd), &pctl->mcmd);
+	udelay(1);
+	while (readl(&pctl->mcmd) & START_CMD)
+		;
+}
+
+static inline void send_command_op(struct rk3288_ddr_pctl *pctl,
+				   u32 rank, u32 cmd, u32 ma, u32 op)
+{
+	send_command(pctl, rank, cmd, (ma & LPDDR2_MA_MASK) << LPDDR2_MA_SHIFT |
+		     (op & LPDDR2_OP_MASK) << LPDDR2_OP_SHIFT);
+}
+
+static void memory_init(struct rk3288_ddr_publ *publ,
+			u32 dramtype)
+{
+	setbits_le32(&publ->pir,
+		     (PIR_INIT | PIR_DRAMINIT | PIR_LOCKBYP
+		      | PIR_ZCALBYP | PIR_CLRSR | PIR_ICPC
+		      | (dramtype == DDR3 ? PIR_DRAMRST : 0)));
+	udelay(1);
+	while ((readl(&publ->pgsr) & (PGSR_IDONE | PGSR_DLDONE))
+		!= (PGSR_IDONE | PGSR_DLDONE))
+		;
+}
+
+static void move_to_config_state(struct rk3288_ddr_publ *publ,
+				 struct rk3288_ddr_pctl *pctl)
+{
+	unsigned int state;
+
+	while (1) {
+		state = readl(&pctl->stat) & PCTL_STAT_MSK;
+
+		switch (state) {
+		case LOW_POWER:
+			writel(WAKEUP_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK)
+				!= ACCESS)
+				;
+			/* wait DLL lock */
+			while ((readl(&publ->pgsr) & PGSR_DLDONE)
+				!= PGSR_DLDONE)
+				;
+			/*
+			 * 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_MSK) != CONFIG)
+				;
+			break;
+		case CONFIG:
+			return;
+		default:
+			break;
+		}
+	}
+}
+
+static void set_bandwidth_ratio(const struct chan_info *chan, int channel,
+				u32 n, struct rk3188_grf *grf)
+{
+	struct rk3288_ddr_pctl *pctl = chan->pctl;
+	struct rk3288_ddr_publ *publ = chan->publ;
+	struct rk3188_msch *msch = chan->msch;
+
+	if (n == 1) {
+		setbits_le32(&pctl->ppcfg, 1);
+		ddr_set_enable(grf, channel, 1);
+		setbits_le32(&msch->ddrtiming, 1 << 31);
+		/* Data Byte disable*/
+		clrbits_le32(&publ->datx8[2].dxgcr, 1);
+		clrbits_le32(&publ->datx8[3].dxgcr, 1);
+		/* disable DLL */
+		setbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLDIS);
+		setbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLDIS);
+	} else {
+		clrbits_le32(&pctl->ppcfg, 1);
+		ddr_set_enable(grf, channel, 0);
+		clrbits_le32(&msch->ddrtiming, 1 << 31);
+		/* Data Byte enable*/
+		setbits_le32(&publ->datx8[2].dxgcr, 1);
+		setbits_le32(&publ->datx8[3].dxgcr, 1);
+
+		/* enable DLL */
+		clrbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLDIS);
+		clrbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLDIS);
+		/* reset DLL */
+		clrbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLSRST);
+		clrbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLSRST);
+		udelay(10);
+		setbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLSRST);
+		setbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLSRST);
+	}
+	setbits_le32(&pctl->dfistcfg0, 1 << 2);
+}
+
+static int data_training(const struct chan_info *chan, int channel,
+			 struct rk3188_sdram_params *sdram_params)
+{
+	unsigned int j;
+	int ret = 0;
+	u32 rank;
+	int i;
+	u32 step[2] = { PIR_QSTRN, PIR_RVTRN };
+	struct rk3288_ddr_publ *publ = chan->publ;
+	struct rk3288_ddr_pctl *pctl = chan->pctl;
+
+	/* disable auto refresh */
+	writel(0, &pctl->trefi);
+
+	if (sdram_params->base.dramtype != LPDDR3)
+		setbits_le32(&publ->pgcr, 1 << PGCR_DQSCFG_SHIFT);
+	rank = sdram_params->ch[channel].rank | 1;
+	for (j = 0; j < ARRAY_SIZE(step); j++) {
+		/*
+		 * trigger QSTRN and RVTRN
+		 * clear DTDONE status
+		 */
+		setbits_le32(&publ->pir, PIR_CLRSR);
+
+		/* trigger DTT */
+		setbits_le32(&publ->pir,
+			     PIR_INIT | step[j] | PIR_LOCKBYP | PIR_ZCALBYP |
+			     PIR_CLRSR);
+		udelay(1);
+		/* wait echo byte DTDONE */
+		while ((readl(&publ->datx8[0].dxgsr[0]) & rank)
+			!= rank)
+			;
+		while ((readl(&publ->datx8[1].dxgsr[0]) & rank)
+			!= rank)
+			;
+		if (!(readl(&pctl->ppcfg) & 1)) {
+			while ((readl(&publ->datx8[2].dxgsr[0])
+				& rank) != rank)
+				;
+			while ((readl(&publ->datx8[3].dxgsr[0])
+				& rank) != rank)
+				;
+		}
+		if (readl(&publ->pgsr) &
+		    (PGSR_DTERR | PGSR_RVERR | PGSR_RVEIRR)) {
+			ret = -1;
+			break;
+		}
+	}
+	/* send some auto refresh to complement the lost while DTT */
+	for (i = 0; i < (rank > 1 ? 8 : 4); i++)
+		send_command(pctl, rank, REF_CMD, 0);
+
+	if (sdram_params->base.dramtype != LPDDR3)
+		clrbits_le32(&publ->pgcr, 1 << PGCR_DQSCFG_SHIFT);
+
+	/* resume auto refresh */
+	writel(sdram_params->pctl_timing.trefi, &pctl->trefi);
+
+	return ret;
+}
+
+static void move_to_access_state(const struct chan_info *chan)
+{
+	struct rk3288_ddr_publ *publ = chan->publ;
+	struct rk3288_ddr_pctl *pctl = chan->pctl;
+	unsigned int state;
+
+	while (1) {
+		state = readl(&pctl->stat) & PCTL_STAT_MSK;
+
+		switch (state) {
+		case LOW_POWER:
+			if (((readl(&pctl->stat) >> LP_TRIG_SHIFT) &
+					LP_TRIG_MASK) == 1)
+				return;
+
+			writel(WAKEUP_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK) != ACCESS)
+				;
+			/* wait DLL lock */
+			while ((readl(&publ->pgsr) & PGSR_DLDONE)
+				!= PGSR_DLDONE)
+				;
+			break;
+		case INIT_MEM:
+			writel(CFG_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG)
+				;
+			/* fallthrough */
+		case CONFIG:
+			writel(GO_STATE, &pctl->sctl);
+			while ((readl(&pctl->stat) & PCTL_STAT_MSK) == CONFIG)
+				;
+			break;
+		case ACCESS:
+			return;
+		default:
+			break;
+		}
+	}
+}
+
+static void dram_cfg_rbc(const struct chan_info *chan, u32 chnum,
+			 struct rk3188_sdram_params *sdram_params)
+{
+	struct rk3288_ddr_publ *publ = chan->publ;
+
+	if (sdram_params->ch[chnum].bk == 3)
+		clrsetbits_le32(&publ->dcr, PDQ_MASK << PDQ_SHIFT,
+				1 << PDQ_SHIFT);
+	else
+		clrbits_le32(&publ->dcr, PDQ_MASK << PDQ_SHIFT);
+
+	writel(sdram_params->base.ddrconfig, &chan->msch->ddrconf);
+}
+
+static void dram_all_config(const struct dram_info *dram,
+			    struct rk3188_sdram_params *sdram_params)
+{
+	unsigned int chan;
+	u32 sys_reg = 0;
+
+	sys_reg |= sdram_params->base.dramtype << SYS_REG_DDRTYPE_SHIFT;
+	sys_reg |= (sdram_params->num_channels - 1) << SYS_REG_NUM_CH_SHIFT;
+	for (chan = 0; chan < sdram_params->num_channels; chan++) {
+		const struct rk3288_sdram_channel *info =
+			&sdram_params->ch[chan];
+
+		sys_reg |= info->row_3_4 << SYS_REG_ROW_3_4_SHIFT(chan);
+		sys_reg |= 1 << SYS_REG_CHINFO_SHIFT(chan);
+		sys_reg |= (info->rank - 1) << SYS_REG_RANK_SHIFT(chan);
+		sys_reg |= (info->col - 9) << SYS_REG_COL_SHIFT(chan);
+		sys_reg |= info->bk == 3 ? 0 : 1 << SYS_REG_BK_SHIFT(chan);
+		sys_reg |= (info->cs0_row - 13) << SYS_REG_CS0_ROW_SHIFT(chan);
+		sys_reg |= (info->cs1_row - 13) << SYS_REG_CS1_ROW_SHIFT(chan);
+		sys_reg |= (2 >> info->bw) << SYS_REG_BW_SHIFT(chan);
+		sys_reg |= (2 >> info->dbw) << SYS_REG_DBW_SHIFT(chan);
+
+		dram_cfg_rbc(&dram->chan[chan], chan, sdram_params);
+	}
+	if (sdram_params->ch[0].rank == 2)
+		ddr_rank_2_row15en(dram->grf, 0);
+	else
+		ddr_rank_2_row15en(dram->grf, 1);
+
+	writel(sys_reg, &dram->pmu->sys_reg[2]);
+}
+
+static int sdram_rank_bw_detect(struct dram_info *dram, int channel,
+		struct rk3188_sdram_params *sdram_params)
+{
+	int reg;
+	int need_trainig = 0;
+	const struct chan_info *chan = &dram->chan[channel];
+	struct rk3288_ddr_publ *publ = chan->publ;
+
+	ddr_rank_2_row15en(dram->grf, 0);
+
+	if (data_training(chan, channel, sdram_params) < 0) {
+		printf("first data training fail!\n");
+		reg = readl(&publ->datx8[0].dxgsr[0]);
+		/* Check the result for rank 0 */
+		if ((channel == 0) && (reg & DQS_GATE_TRAINING_ERROR_RANK0)) {
+			printf("data training fail!\n");
+			return -EIO;
+		}
+
+		/* Check the result for rank 1 */
+		if (reg & DQS_GATE_TRAINING_ERROR_RANK1) {
+			sdram_params->ch[channel].rank = 1;
+			clrsetbits_le32(&publ->pgcr, 0xF << 18,
+					sdram_params->ch[channel].rank << 18);
+			need_trainig = 1;
+		}
+		reg = readl(&publ->datx8[2].dxgsr[0]);
+		if (reg & (1 << 4)) {
+			sdram_params->ch[channel].bw = 1;
+			set_bandwidth_ratio(chan, channel,
+					    sdram_params->ch[channel].bw,
+					    dram->grf);
+			need_trainig = 1;
+		}
+	}
+	/* Assume the Die bit width are the same with the chip bit width */
+	sdram_params->ch[channel].dbw = sdram_params->ch[channel].bw;
+
+	if (need_trainig &&
+	    (data_training(chan, channel, sdram_params) < 0)) {
+		if (sdram_params->base.dramtype == LPDDR3) {
+			ddr_phy_ctl_reset(dram->cru, channel, 1);
+			udelay(10);
+			ddr_phy_ctl_reset(dram->cru, channel, 0);
+			udelay(10);
+		}
+		printf("2nd data training failed!");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * Detect ram columns and rows.
+ * @dram: dram info struct
+ * @channel: channel number to handle
+ * @sdram_params: sdram parameters, function will fill in col and row values
+ *
+ * Returns 0 or negative on error.
+ */
+static int sdram_col_row_detect(struct dram_info *dram, int channel,
+		struct rk3188_sdram_params *sdram_params)
+{
+	int row, col;
+	unsigned int addr;
+	const struct chan_info *chan = &dram->chan[channel];
+	struct rk3288_ddr_pctl *pctl = chan->pctl;
+	struct rk3288_ddr_publ *publ = chan->publ;
+	int ret = 0;
+
+	/* Detect col */
+	for (col = 11; col >= 9; col--) {
+		writel(0, CONFIG_SYS_SDRAM_BASE);
+		addr = CONFIG_SYS_SDRAM_BASE +
+			(1 << (col + sdram_params->ch[channel].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[channel].col = col;
+	}
+
+	ddr_rank_2_row15en(dram->grf, 1);
+	move_to_config_state(publ, pctl);
+	writel(1, &chan->msch->ddrconf);
+	move_to_access_state(chan);
+	/* Detect row, max 15,min13 in rk3188*/
+	for (row = 16; row >= 13; row--) {
+		writel(0, CONFIG_SYS_SDRAM_BASE);
+		addr = CONFIG_SYS_SDRAM_BASE + (1 << (row + 15 - 1));
+		writel(TEST_PATTEN, addr);
+		if ((readl(addr) == TEST_PATTEN) &&
+		    (readl(CONFIG_SYS_SDRAM_BASE) == 0))
+			break;
+	}
+	if (row == 12) {
+		printf("Row detect error\n");
+		ret = -EINVAL;
+	} else {
+		sdram_params->ch[channel].cs1_row = row;
+		sdram_params->ch[channel].row_3_4 = 0;
+		debug("chn %d col %d, row %d\n", channel, col, row);
+		sdram_params->ch[channel].cs0_row = row;
+	}
+
+out:
+	return ret;
+}
+
+static int sdram_get_niu_config(struct rk3188_sdram_params *sdram_params)
+{
+	int i, tmp, size, ret = 0;
+
+	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);
+	size = sizeof(ddrconf_table)/sizeof(ddrconf_table[0]);
+	for (i = 0; i < size; i++)
+		if (tmp == ddrconf_table[i])
+			break;
+	if (i >= size) {
+		printf("niu config not found\n");
+		ret = -EINVAL;
+	} else {
+		debug("niu config %d\n", i);
+		sdram_params->base.ddrconfig = i;
+	}
+
+	return ret;
+}
+
+static int sdram_init(struct dram_info *dram,
+		      struct rk3188_sdram_params *sdram_params)
+{
+	int channel;
+	int zqcr;
+	int ret;
+
+	if ((sdram_params->base.dramtype == DDR3 &&
+	     sdram_params->base.ddr_freq > 800000000)) {
+		printf("SDRAM frequency is too high!");
+		return -E2BIG;
+	}
+
+	ret = clk_set_rate(&dram->ddr_clk, sdram_params->base.ddr_freq);
+	if (ret) {
+		printf("Could not set DDR clock\n");
+		return ret;
+	}
+
+	for (channel = 0; channel < 1; channel++) {
+		const struct chan_info *chan = &dram->chan[channel];
+		struct rk3288_ddr_pctl *pctl = chan->pctl;
+		struct rk3288_ddr_publ *publ = chan->publ;
+
+		phy_pctrl_reset(dram->cru, publ, channel);
+		phy_dll_bypass_set(publ, sdram_params->base.ddr_freq);
+
+		dfi_cfg(pctl, sdram_params->base.dramtype);
+
+		pctl_cfg(channel, pctl, sdram_params, dram->grf);
+
+		phy_cfg(chan, channel, sdram_params);
+
+		phy_init(publ);
+
+		writel(POWER_UP_START, &pctl->powctl);
+		while (!(readl(&pctl->powstat) & POWER_UP_DONE))
+			;
+
+		memory_init(publ, sdram_params->base.dramtype);
+		move_to_config_state(publ, pctl);
+
+		/* Using 32bit bus width for detect */
+		sdram_params->ch[channel].bw = 2;
+		set_bandwidth_ratio(chan, channel,
+				    sdram_params->ch[channel].bw, dram->grf);
+		/*
+		 * set cs, using n=3 for detect
+		 * CS0, n=1
+		 * CS1, n=2
+		 * CS0 & CS1, n = 3
+		 */
+		sdram_params->ch[channel].rank = 2,
+		clrsetbits_le32(&publ->pgcr, 0xF << 18,
+				(sdram_params->ch[channel].rank | 1) << 18);
+
+		/* DS=40ohm,ODT=155ohm */
+		zqcr = 1 << ZDEN_SHIFT | 2 << PU_ONDIE_SHIFT |
+			2 << PD_ONDIE_SHIFT | 0x19 << PU_OUTPUT_SHIFT |
+			0x19 << PD_OUTPUT_SHIFT;
+		writel(zqcr, &publ->zq1cr[0]);
+		writel(zqcr, &publ->zq0cr[0]);
+
+		/* Detect the rank and bit-width with data-training */
+		writel(1, &chan->msch->ddrconf);
+		sdram_rank_bw_detect(dram, channel, sdram_params);
+
+		if (sdram_params->base.dramtype == LPDDR3) {
+			u32 i;
+			writel(0, &pctl->mrrcfg0);
+			for (i = 0; i < 17; i++)
+				send_command_op(pctl, 1, MRR_CMD, i, 0);
+		}
+		writel(4, &chan->msch->ddrconf);
+		move_to_access_state(chan);
+		/* DDR3 and LPDDR3 are always 8 bank, no need detect */
+		sdram_params->ch[channel].bk = 3;
+		/* Detect Col and Row number*/
+		ret = sdram_col_row_detect(dram, channel, sdram_params);
+		if (ret)
+			goto error;
+	}
+	/* Find NIU DDR configuration */
+	ret = sdram_get_niu_config(sdram_params);
+	if (ret)
+		goto error;
+
+	dram_all_config(dram, sdram_params);
+	debug("%s done\n", __func__);
+
+	return 0;
+error:
+	printf("DRAM init failed!\n");
+	hang();
+}
+#endif /* CONFIG_SPL_BUILD */
+
+size_t sdram_size_mb(struct rk3188_pmu *pmu)
+{
+	u32 rank, col, bk, cs0_row, cs1_row, bw, row_3_4;
+	size_t chipsize_mb = 0;
+	size_t size_mb = 0;
+	u32 ch;
+	u32 sys_reg = readl(&pmu->sys_reg[2]);
+	u32 chans;
+
+	chans = 1 + ((sys_reg >> SYS_REG_NUM_CH_SHIFT) & SYS_REG_NUM_CH_MASK);
+
+	for (ch = 0; ch < chans; ch++) {
+		rank = 1 + (sys_reg >> SYS_REG_RANK_SHIFT(ch) &
+			SYS_REG_RANK_MASK);
+		col = 9 + (sys_reg >> SYS_REG_COL_SHIFT(ch) & SYS_REG_COL_MASK);
+		bk = 3 - ((sys_reg >> SYS_REG_BK_SHIFT(ch)) & SYS_REG_BK_MASK);
+		cs0_row = 13 + (sys_reg >> SYS_REG_CS0_ROW_SHIFT(ch) &
+				SYS_REG_CS0_ROW_MASK);
+		cs1_row = 13 + (sys_reg >> SYS_REG_CS1_ROW_SHIFT(ch) &
+				SYS_REG_CS1_ROW_MASK);
+		bw = (2 >> ((sys_reg >> SYS_REG_BW_SHIFT(ch)) &
+			SYS_REG_BW_MASK));
+		row_3_4 = sys_reg >> SYS_REG_ROW_3_4_SHIFT(ch) &
+			SYS_REG_ROW_3_4_MASK;
+		chipsize_mb = (1 << (cs0_row + col + bk + bw - 20));
+
+		if (rank > 1)
+			chipsize_mb += chipsize_mb >>
+				(cs0_row - cs1_row);
+		if (row_3_4)
+			chipsize_mb = chipsize_mb * 3 / 4;
+		size_mb += chipsize_mb;
+	}
+
+	/* there can be no more than 2gb of memory */
+	size_mb = min(size_mb, 0x80000000 >> 20);
+
+	return size_mb;
+}
+
+#ifdef CONFIG_SPL_BUILD
+static int setup_sdram(struct udevice *dev)
+{
+	struct dram_info *priv = dev_get_priv(dev);
+	struct rk3188_sdram_params *params = dev_get_platdata(dev);
+
+	return sdram_init(priv, params);
+}
+
+static int rk3188_dmc_ofdata_to_platdata(struct udevice *dev)
+{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rk3188_sdram_params *params = dev_get_platdata(dev);
+	const void *blob = gd->fdt_blob;
+	int node = dev->of_offset;
+	int ret;
+
+	/* rk3188 supports only one-channel */
+	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_SPL_BUILD */
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+static int conv_of_platdata(struct udevice *dev)
+{
+	struct rk3188_sdram_params *plat = dev_get_platdata(dev);
+	struct dtd_rockchip_rk3188_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));
+	/* rk3188 supports dual-channel, set default channel num to 2 */
+	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 rk3188_dmc_probe(struct udevice *dev)
+{
+#ifdef CONFIG_SPL_BUILD
+	struct rk3188_sdram_params *plat = dev_get_platdata(dev);
+#endif
+	struct dram_info *priv = dev_get_priv(dev);
+	struct regmap *map;
+	int ret;
+	struct udevice *dev_clk;
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	ret = conv_of_platdata(dev);
+	if (ret)
+		return ret;
+#endif
+	map = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_NOC);
+	if (IS_ERR(map))
+		return PTR_ERR(map);
+	priv->chan[0].msch = regmap_get_range(map, 0);
+
+	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+	priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU);
+
+#ifdef CONFIG_SPL_BUILD
+	priv->chan[0].pctl = regmap_get_range(plat->map, 0);
+	priv->chan[0].publ = regmap_get_range(plat->map, 1);
+#endif
+
+	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);
+#ifdef CONFIG_SPL_BUILD
+	ret = setup_sdram(dev);
+	if (ret)
+		return ret;
+#endif
+	priv->info.base = 0;
+	priv->info.size = sdram_size_mb(priv->pmu) << 20;
+
+	return 0;
+}
+
+static int rk3188_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 rk3188_dmc_ops = {
+	.get_info = rk3188_dmc_get_info,
+};
+
+static const struct udevice_id rk3188_dmc_ids[] = {
+	{ .compatible = "rockchip,rk3188-dmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(dmc_rk3188) = {
+	.name = "rockchip_rk3188_dmc",
+	.id = UCLASS_RAM,
+	.of_match = rk3188_dmc_ids,
+	.ops = &rk3188_dmc_ops,
+#ifdef CONFIG_SPL_BUILD
+	.ofdata_to_platdata = rk3188_dmc_ofdata_to_platdata,
+#endif
+	.probe = rk3188_dmc_probe,
+	.priv_auto_alloc_size = sizeof(struct dram_info),
+#ifdef CONFIG_SPL_BUILD
+	.platdata_auto_alloc_size = sizeof(struct rk3188_sdram_params),
+#endif
+};
diff --git a/arch/arm/mach-rockchip/rk3188/syscon_rk3188.c b/arch/arm/mach-rockchip/rk3188/syscon_rk3188.c
new file mode 100644
index 0000000..aeee6bf
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3188/syscon_rk3188.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <syscon.h>
+#include <asm/arch/clock.h>
+
+static const struct udevice_id rk3188_syscon_ids[] = {
+	{ .compatible = "rockchip,rk3188-noc", .data = ROCKCHIP_SYSCON_NOC },
+	{ .compatible = "rockchip,rk3188-grf", .data = ROCKCHIP_SYSCON_GRF },
+	{ .compatible = "rockchip,rk3188-pmu", .data = ROCKCHIP_SYSCON_PMU },
+	{ }
+};
+
+U_BOOT_DRIVER(syscon_rk3188) = {
+	.name = "rk3188_syscon",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3188_syscon_ids,
+};
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+static int rk3188_syscon_bind_of_platdata(struct udevice *dev)
+{
+	dev->driver_data = dev->driver->of_match->data;
+	debug("syscon: %s %d\n", dev->name, (uint)dev->driver_data);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(rockchip_rk3188_noc) = {
+	.name = "rockchip_rk3188_noc",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3188_syscon_ids,
+	.bind = rk3188_syscon_bind_of_platdata,
+};
+
+U_BOOT_DRIVER(rockchip_rk3188_grf) = {
+	.name = "rockchip_rk3188_grf",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3188_syscon_ids + 1,
+	.bind = rk3188_syscon_bind_of_platdata,
+};
+
+U_BOOT_DRIVER(rockchip_rk3188_pmu) = {
+	.name = "rockchip_rk3188_pmu",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3188_syscon_ids + 2,
+	.bind = rk3188_syscon_bind_of_platdata,
+};
+#endif
diff --git a/arch/arm/mach-rockchip/rk3288-board-spl.c b/arch/arm/mach-rockchip/rk3288-board-spl.c
index 930939a..74f3379 100644
--- a/arch/arm/mach-rockchip/rk3288-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3288-board-spl.c
@@ -14,6 +14,7 @@
 #include <spl.h>
 #include <asm/gpio.h>
 #include <asm/io.h>
+#include <asm/arch/bootrom.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/periph.h>
@@ -155,7 +156,7 @@
 	return 0;
 }
 #endif
-extern void back_to_bootrom(void);
+
 void board_init_f(ulong dummy)
 {
 	struct udevice *pinctrl;
@@ -184,9 +185,9 @@
 	debug_uart_init();
 #endif
 
-	ret = spl_init();
+	ret = spl_early_init();
 	if (ret) {
-		debug("spl_init() failed: %d\n", ret);
+		debug("spl_early_init() failed: %d\n", ret);
 		hang();
 	}
 
diff --git a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
index 89fd8e6..8549b28 100644
--- a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
+++ b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
@@ -57,6 +57,26 @@
 	struct regmap *map;
 };
 
+const int ddrconf_table[] = {
+	/* row	    col,bw */
+	0,
+	((1 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT),
+	((2 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT),
+	((3 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT),
+	((4 << DDRCONF_ROW_SHIFT) | 1 << DDRCONF_COL_SHIFT),
+	((1 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT),
+	((2 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT),
+	((3 << DDRCONF_ROW_SHIFT) | 2 << DDRCONF_COL_SHIFT),
+	((1 << DDRCONF_ROW_SHIFT) | 0 << DDRCONF_COL_SHIFT),
+	((2 << DDRCONF_ROW_SHIFT) | 0 << DDRCONF_COL_SHIFT),
+	((3 << DDRCONF_ROW_SHIFT) | 0 << DDRCONF_COL_SHIFT),
+	0,
+	0,
+	0,
+	0,
+	((4 << 4) | 2),
+};
+
 #define TEST_PATTEN	0x5aa5f00f
 #define DQS_GATE_TRAINING_ERROR_RANK0	(1 << 4)
 #define DQS_GATE_TRAINING_ERROR_RANK1	(2 << 4)
@@ -100,7 +120,7 @@
 
 static void phy_pctrl_reset(struct rk3288_cru *cru,
 			    struct rk3288_ddr_publ *publ,
-			    u32 channel)
+			    int channel)
 {
 	int i;
 
@@ -126,6 +146,7 @@
 	u32 freq)
 {
 	int i;
+
 	if (freq <= 250000000) {
 		if (freq <= 150000000)
 			clrbits_le32(&publ->dllgcr, SBIAS_BYPASS);
@@ -217,7 +238,7 @@
 				UPCTL0_LPDDR3_ODT_EN_SHIFT));
 }
 
-static void pctl_cfg(u32 channel, struct rk3288_ddr_pctl *pctl,
+static void pctl_cfg(int channel, struct rk3288_ddr_pctl *pctl,
 		     struct rk3288_sdram_params *sdram_params,
 		     struct rk3288_grf *grf)
 {
@@ -267,7 +288,7 @@
 	setbits_le32(&pctl->scfg, 1);
 }
 
-static void phy_cfg(const struct chan_info *chan, u32 channel,
+static void phy_cfg(const struct chan_info *chan, int channel,
 		    struct rk3288_sdram_params *sdram_params)
 {
 	struct rk3288_ddr_publ *publ = chan->publ;
@@ -392,7 +413,8 @@
 			while ((readl(&publ->pgsr) & PGSR_DLDONE)
 				!= PGSR_DLDONE)
 				;
-			/* if at low power state,need wakeup first,
+			/*
+			 * if at low power state,need wakeup first,
 			 * and then enter the config
 			 * so here no break.
 			 */
@@ -411,7 +433,7 @@
 	}
 }
 
-static void set_bandwidth_ratio(const struct chan_info *chan, u32 channel,
+static void set_bandwidth_ratio(const struct chan_info *chan, int channel,
 				u32 n, struct rk3288_grf *grf)
 {
 	struct rk3288_ddr_pctl *pctl = chan->pctl;
@@ -449,7 +471,7 @@
 	setbits_le32(&pctl->dfistcfg0, 1 << 2);
 }
 
-static int data_training(const struct chan_info *chan, u32 channel,
+static int data_training(const struct chan_info *chan, int channel,
 			 struct rk3288_sdram_params *sdram_params)
 {
 	unsigned int j;
@@ -593,25 +615,6 @@
 	writel(sys_reg, &dram->pmu->sys_reg[2]);
 	rk_clrsetreg(&dram->sgrf->soc_con2, 0x1f, sdram_params->base.stride);
 }
-const int ddrconf_table[] = {
-	/* row	    col,bw */
-	0,
-	((1 << 4) | 1),
-	((2 << 4) | 1),
-	((3 << 4) | 1),
-	((4 << 4) | 1),
-	((1 << 4) | 2),
-	((2 << 4) | 2),
-	((3 << 4) | 2),
-	((1 << 4) | 0),
-	((2 << 4) | 0),
-	((3 << 4) | 0),
-	0,
-	0,
-	0,
-	0,
-	((4 << 4) | 2),
-};
 
 static int sdram_rank_bw_detect(struct dram_info *dram, int channel,
 		struct rk3288_sdram_params *sdram_params)
@@ -621,12 +624,12 @@
 	const struct chan_info *chan = &dram->chan[channel];
 	struct rk3288_ddr_publ *publ = chan->publ;
 
-	if (-1 == data_training(chan, channel, sdram_params)) {
+	if (data_training(chan, channel, sdram_params) < 0) {
 		reg = readl(&publ->datx8[0].dxgsr[0]);
 		/* Check the result for rank 0 */
 		if ((channel == 0) && (reg & DQS_GATE_TRAINING_ERROR_RANK0)) {
 			debug("data training fail!\n");
-				return -EIO;
+			return -EIO;
 		} else if ((channel == 1) &&
 			   (reg & DQS_GATE_TRAINING_ERROR_RANK0)) {
 			sdram_params->num_channels = 1;
@@ -652,7 +655,7 @@
 	sdram_params->ch[channel].dbw = sdram_params->ch[channel].bw;
 
 	if (need_trainig &&
-	    (-1 == data_training(chan, channel, sdram_params))) {
+	    (data_training(chan, channel, sdram_params) < 0)) {
 		if (sdram_params->base.dramtype == LPDDR3) {
 			ddr_phy_ctl_reset(dram->cru, channel, 1);
 			udelay(10);
diff --git a/arch/arm/mach-rockchip/rk3328/Kconfig b/arch/arm/mach-rockchip/rk3328/Kconfig
new file mode 100644
index 0000000..43afba2
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3328/Kconfig
@@ -0,0 +1,23 @@
+if ROCKCHIP_RK3328
+
+choice
+	prompt "RK3328 board select"
+
+config TARGET_EVB_RK3328
+	bool "RK3328 evaluation board"
+	help
+	  RK3328evb is a evaluation board for Rockchip rk3328,
+	  with full function and phisical connectors support like
+	  usb2.0 host ports, LVDS, JTAG, MAC, SDcard, HDMI, USB-2-serial...
+
+endchoice
+
+config SYS_SOC
+	default "rockchip"
+
+config SYS_MALLOC_F_LEN
+	default 0x0800
+
+source "board/rockchip/evb_rk3328/Kconfig"
+
+endif
diff --git a/arch/arm/mach-rockchip/rk3328/Makefile b/arch/arm/mach-rockchip/rk3328/Makefile
new file mode 100644
index 0000000..bbab036
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3328/Makefile
@@ -0,0 +1,9 @@
+#
+# (C) Copyright 2016 Rockchip Electronics Co., Ltd
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y += clk_rk3328.o
+obj-y += rk3328.o
+obj-y += syscon_rk3328.o
diff --git a/arch/arm/mach-rockchip/rk3328/clk_rk3328.c b/arch/arm/mach-rockchip/rk3328/clk_rk3328.c
new file mode 100644
index 0000000..1205516
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3328/clk_rk3328.c
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 2017 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3328.h>
+
+int rockchip_get_clk(struct udevice **devp)
+{
+	return uclass_get_device_by_driver(UCLASS_CLK,
+			DM_GET_DRIVER(rockchip_rk3328_cru), devp);
+}
+
+void *rockchip_get_cru(void)
+{
+	struct rk3328_clk_priv *priv;
+	struct udevice *dev;
+	int ret;
+
+	ret = rockchip_get_clk(&dev);
+	if (ret)
+		return ERR_PTR(ret);
+
+	priv = dev_get_addr_ptr(dev);
+
+	return priv->cru;
+}
diff --git a/arch/arm/mach-rockchip/rk3328/rk3328.c b/arch/arm/mach-rockchip/rk3328/rk3328.c
new file mode 100644
index 0000000..857f014
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3328/rk3328.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/hardware.h>
+#include <asm/armv8/mmu.h>
+#include <asm/io.h>
+
+static struct mm_region rk3328_mem_map[] = {
+	{
+		.virt = 0x0UL,
+		.phys = 0x0UL,
+		.size = 0x80000000UL,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+			 PTE_BLOCK_INNER_SHARE
+	}, {
+		.virt = 0xf0000000UL,
+		.phys = 0xf0000000UL,
+		.size = 0x10000000UL,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
+			 PTE_BLOCK_NON_SHARE |
+			 PTE_BLOCK_PXN | PTE_BLOCK_UXN
+	}, {
+		/* List terminator */
+		0,
+	}
+};
+
+struct mm_region *mem_map = rk3328_mem_map;
+
+int arch_cpu_init(void)
+{
+	/* We do some SoC one time setting here. */
+
+	return 0;
+}
diff --git a/arch/arm/mach-rockchip/rk3328/syscon_rk3328.c b/arch/arm/mach-rockchip/rk3328/syscon_rk3328.c
new file mode 100644
index 0000000..a1a368f
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3328/syscon_rk3328.c
@@ -0,0 +1,20 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/clock.h>
+#include <dm.h>
+#include <syscon.h>
+
+static const struct udevice_id rk3328_syscon_ids[] = {
+	{ .compatible = "rockchip,rk3328-grf", .data = ROCKCHIP_SYSCON_GRF },
+};
+
+U_BOOT_DRIVER(syscon_rk3328) = {
+	.name = "rk3328_syscon",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3328_syscon_ids,
+};
diff --git a/arch/arm/mach-rockchip/rk3399-board-spl.c b/arch/arm/mach-rockchip/rk3399-board-spl.c
new file mode 100644
index 0000000..8ae3055
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3399-board-spl.c
@@ -0,0 +1,158 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <debug_uart.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <led.h>
+#include <malloc.h>
+#include <ram.h>
+#include <spl.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/periph.h>
+#include <asm/arch/sdram.h>
+#include <asm/arch/timer.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/util.h>
+#include <power/regulator.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+u32 spl_boot_device(void)
+{
+	return BOOT_DEVICE_MMC1;
+}
+
+u32 spl_boot_mode(const u32 boot_device)
+{
+	return MMCSD_MODE_RAW;
+}
+
+#define TIMER_CHN10_BASE	0xff8680a0
+#define TIMER_END_COUNT_L	0x00
+#define TIMER_END_COUNT_H	0x04
+#define TIMER_INIT_COUNT_L	0x10
+#define TIMER_INIT_COUNT_H	0x14
+#define TIMER_CONTROL_REG	0x1c
+
+#define TIMER_EN	0x1
+#define	TIMER_FMODE	(0 << 1)
+#define	TIMER_RMODE	(1 << 1)
+
+void secure_timer_init(void)
+{
+	writel(0xffffffff, TIMER_CHN10_BASE + TIMER_END_COUNT_L);
+	writel(0xffffffff, TIMER_CHN10_BASE + TIMER_END_COUNT_H);
+	writel(0, TIMER_CHN10_BASE + TIMER_INIT_COUNT_L);
+	writel(0, TIMER_CHN10_BASE + TIMER_INIT_COUNT_H);
+	writel(TIMER_EN | TIMER_FMODE, TIMER_CHN10_BASE + TIMER_CONTROL_REG);
+}
+
+#define GRF_EMMCCORE_CON11 0xff77f02c
+void board_init_f(ulong dummy)
+{
+	struct udevice *pinctrl;
+	struct udevice *dev;
+	int ret;
+
+	/* Example code showing how to enable the debug UART on RK3288 */
+#include <asm/arch/grf_rk3399.h>
+	/* Enable early UART2 channel C on the RK3399 */
+#define GRF_BASE	0xff770000
+	struct rk3399_grf_regs * const grf = (void *)GRF_BASE;
+
+	rk_clrsetreg(&grf->gpio4c_iomux,
+		     GRF_GPIO4C3_SEL_MASK,
+		     GRF_UART2DGBC_SIN << GRF_GPIO4C3_SEL_SHIFT);
+	rk_clrsetreg(&grf->gpio4c_iomux,
+		     GRF_GPIO4C4_SEL_MASK,
+		     GRF_UART2DBGC_SOUT << GRF_GPIO4C4_SEL_SHIFT);
+	/* Set channel C as UART2 input */
+	rk_clrsetreg(&grf->soc_con7,
+		     GRF_UART_DBG_SEL_MASK,
+		     GRF_UART_DBG_SEL_C << GRF_UART_DBG_SEL_SHIFT);
+#define EARLY_UART
+#ifdef EARLY_UART
+	/*
+	 * Debug UART can be used from here if required:
+	 *
+	 * debug_uart_init();
+	 * printch('a');
+	 * printhex8(0x1234);
+	 * printascii("string");
+	 */
+	debug_uart_init();
+	printascii("U-Boot SPL board init");
+#endif
+	/*  Emmc clock generator: disable the clock multipilier */
+	rk_clrreg(GRF_EMMCCORE_CON11, 0x0ff);
+
+	ret = spl_init();
+	if (ret) {
+		debug("spl_init() failed: %d\n", ret);
+		hang();
+	}
+
+	secure_timer_init();
+
+	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
+	if (ret) {
+		debug("Pinctrl 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 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();
+#ifdef CONFIG_ROCKCHIP_SPL_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)
+{
+	/* Just empty function now - can't decide what to choose */
+	debug("%s: %s\n", __func__, name);
+
+	return 0;
+}
+#endif
diff --git a/arch/arm/mach-rockchip/rk3399/Makefile b/arch/arm/mach-rockchip/rk3399/Makefile
index 98ebeac..793ce31 100644
--- a/arch/arm/mach-rockchip/rk3399/Makefile
+++ b/arch/arm/mach-rockchip/rk3399/Makefile
@@ -6,4 +6,5 @@
 
 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/rk3399/clk_rk3399.c b/arch/arm/mach-rockchip/rk3399/clk_rk3399.c
index ce706a6..cf5b8c9 100644
--- a/arch/arm/mach-rockchip/rk3399/clk_rk3399.c
+++ b/arch/arm/mach-rockchip/rk3399/clk_rk3399.c
@@ -31,3 +31,24 @@
 
 	return priv->cru;
 }
+
+static int rockchip_get_pmucruclk(struct udevice **devp)
+{
+	return uclass_get_device_by_driver(UCLASS_CLK,
+			DM_GET_DRIVER(rockchip_rk3399_pmuclk), devp);
+}
+
+void *rockchip_get_pmucru(void)
+{
+	struct rk3399_pmuclk_priv *priv;
+	struct udevice *dev;
+	int ret;
+
+	ret = rockchip_get_pmucruclk(&dev);
+	if (ret)
+		return ERR_PTR(ret);
+
+	priv = dev_get_priv(dev);
+
+	return priv->pmucru;
+}
diff --git a/arch/arm/mach-rockchip/rk3399/rk3399.c b/arch/arm/mach-rockchip/rk3399/rk3399.c
index 8bb950e..cbfd3fa 100644
--- a/arch/arm/mach-rockchip/rk3399/rk3399.c
+++ b/arch/arm/mach-rockchip/rk3399/rk3399.c
@@ -40,5 +40,6 @@
 	/* Emmc clock generator: disable the clock multipilier */
 	rk_clrreg(GRF_EMMCCORE_CON11, 0x0ff);
 
+	printf("time %x, %x\n", readl(0xff8680a8), readl(0xff8680ac));
 	return 0;
 }
diff --git a/arch/arm/mach-rockchip/rk3399/sdram_rk3399.c b/arch/arm/mach-rockchip/rk3399/sdram_rk3399.c
new file mode 100644
index 0000000..749b52c
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3399/sdram_rk3399.c
@@ -0,0 +1,1321 @@
+/*
+ * (C) Copyright 2016-2017 Rockchip Inc.
+ *
+ * SPDX-License-Identifier:     GPL-2.0
+ *
+ * Adapted from coreboot.
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <ram.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sdram_rk3399.h>
+#include <asm/arch/cru_rk3399.h>
+#include <asm/arch/grf_rk3399.h>
+#include <asm/arch/hardware.h>
+#include <linux/err.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+struct chan_info {
+	struct rk3399_ddr_pctl_regs *pctl;
+	struct rk3399_ddr_pi_regs *pi;
+	struct rk3399_ddr_publ_regs *publ;
+	struct rk3399_msch_regs *msch;
+};
+
+struct dram_info {
+#ifdef CONFIG_SPL_BUILD
+	struct chan_info chan[2];
+	struct clk ddr_clk;
+	struct rk3399_cru *cru;
+	struct rk3399_pmucru *pmucru;
+	struct rk3399_pmusgrf_regs *pmusgrf;
+	struct rk3399_ddr_cic_regs *cic;
+#endif
+	struct ram_info info;
+	struct rk3399_pmugrf_regs *pmugrf;
+};
+
+/*
+ * sys_reg bitfield struct
+ * [31]		row_3_4_ch1
+ * [30]		row_3_4_ch0
+ * [29:28]	chinfo
+ * [27]		rank_ch1
+ * [26:25]	col_ch1
+ * [24]		bk_ch1
+ * [23:22]	cs0_row_ch1
+ * [21:20]	cs1_row_ch1
+ * [19:18]	bw_ch1
+ * [17:16]	dbw_ch1;
+ * [15:13]	ddrtype
+ * [12]		channelnum
+ * [11]		rank_ch0
+ * [10:9]	col_ch0
+ * [8]		bk_ch0
+ * [7:6]	cs0_row_ch0
+ * [5:4]	cs1_row_ch0
+ * [3:2]	bw_ch0
+ * [1:0]	dbw_ch0
+*/
+#define SYS_REG_DDRTYPE_SHIFT		13
+#define SYS_REG_DDRTYPE_MASK		7
+#define SYS_REG_NUM_CH_SHIFT		12
+#define SYS_REG_NUM_CH_MASK		1
+#define SYS_REG_ROW_3_4_SHIFT(ch)	(30 + (ch))
+#define SYS_REG_ROW_3_4_MASK		1
+#define SYS_REG_CHINFO_SHIFT(ch)	(28 + (ch))
+#define SYS_REG_RANK_SHIFT(ch)		(11 + (ch) * 16)
+#define SYS_REG_RANK_MASK		1
+#define SYS_REG_COL_SHIFT(ch)		(9 + (ch) * 16)
+#define SYS_REG_COL_MASK		3
+#define SYS_REG_BK_SHIFT(ch)		(8 + (ch) * 16)
+#define SYS_REG_BK_MASK			1
+#define SYS_REG_CS0_ROW_SHIFT(ch)	(6 + (ch) * 16)
+#define SYS_REG_CS0_ROW_MASK		3
+#define SYS_REG_CS1_ROW_SHIFT(ch)	(4 + (ch) * 16)
+#define SYS_REG_CS1_ROW_MASK		3
+#define SYS_REG_BW_SHIFT(ch)		(2 + (ch) * 16)
+#define SYS_REG_BW_MASK			3
+#define SYS_REG_DBW_SHIFT(ch)		((ch) * 16)
+#define SYS_REG_DBW_MASK		3
+
+#define PRESET_SGRF_HOLD(n)	((0x1 << (6 + 16)) | ((n) << 6))
+#define PRESET_GPIO0_HOLD(n)	((0x1 << (7 + 16)) | ((n) << 7))
+#define PRESET_GPIO1_HOLD(n)	((0x1 << (8 + 16)) | ((n) << 8))
+
+#define PHY_DRV_ODT_Hi_Z	0x0
+#define PHY_DRV_ODT_240		0x1
+#define PHY_DRV_ODT_120		0x8
+#define PHY_DRV_ODT_80		0x9
+#define PHY_DRV_ODT_60		0xc
+#define PHY_DRV_ODT_48		0xd
+#define PHY_DRV_ODT_40		0xe
+#define PHY_DRV_ODT_34_3	0xf
+
+#ifdef CONFIG_SPL_BUILD
+
+struct rockchip_dmc_plat {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct dtd_rockchip_rk3399_dmc dtplat;
+#else
+	struct rk3399_sdram_params sdram_params;
+#endif
+	struct regmap *map;
+};
+
+static void copy_to_reg(u32 *dest, const u32 *src, u32 n)
+{
+	int i;
+
+	for (i = 0; i < n / sizeof(u32); i++) {
+		writel(*src, dest);
+		src++;
+		dest++;
+	}
+}
+
+static void phy_dll_bypass_set(struct rk3399_ddr_publ_regs *ddr_publ_regs,
+			       u32 freq)
+{
+	u32 *denali_phy = ddr_publ_regs->denali_phy;
+
+	/* From IP spec, only freq small than 125 can enter dll bypass mode */
+	if (freq <= 125) {
+		/* phy_sw_master_mode_X PHY_86/214/342/470 4bits offset_8 */
+		setbits_le32(&denali_phy[86], (0x3 << 2) << 8);
+		setbits_le32(&denali_phy[214], (0x3 << 2) << 8);
+		setbits_le32(&denali_phy[342], (0x3 << 2) << 8);
+		setbits_le32(&denali_phy[470], (0x3 << 2) << 8);
+
+		/* phy_adrctl_sw_master_mode PHY_547/675/803 4bits offset_16 */
+		setbits_le32(&denali_phy[547], (0x3 << 2) << 16);
+		setbits_le32(&denali_phy[675], (0x3 << 2) << 16);
+		setbits_le32(&denali_phy[803], (0x3 << 2) << 16);
+	} else {
+		/* phy_sw_master_mode_X PHY_86/214/342/470 4bits offset_8 */
+		clrbits_le32(&denali_phy[86], (0x3 << 2) << 8);
+		clrbits_le32(&denali_phy[214], (0x3 << 2) << 8);
+		clrbits_le32(&denali_phy[342], (0x3 << 2) << 8);
+		clrbits_le32(&denali_phy[470], (0x3 << 2) << 8);
+
+		/* phy_adrctl_sw_master_mode PHY_547/675/803 4bits offset_16 */
+		clrbits_le32(&denali_phy[547], (0x3 << 2) << 16);
+		clrbits_le32(&denali_phy[675], (0x3 << 2) << 16);
+		clrbits_le32(&denali_phy[803], (0x3 << 2) << 16);
+	}
+}
+
+static void set_memory_map(const struct chan_info *chan, u32 channel,
+			   const struct rk3399_sdram_params *sdram_params)
+{
+	const struct rk3399_sdram_channel *sdram_ch =
+		&sdram_params->ch[channel];
+	u32 *denali_ctl = chan->pctl->denali_ctl;
+	u32 *denali_pi = chan->pi->denali_pi;
+	u32 cs_map;
+	u32 reduc;
+	u32 row;
+
+	/* Get row number from ddrconfig setting */
+	if (sdram_ch->ddrconfig < 2 || sdram_ch->ddrconfig == 4)
+		row = 16;
+	else if (sdram_ch->ddrconfig == 3)
+		row = 14;
+	else
+		row = 15;
+
+	cs_map = (sdram_ch->rank > 1) ? 3 : 1;
+	reduc = (sdram_ch->bw == 2) ? 0 : 1;
+
+	/* Set the dram configuration to ctrl */
+	clrsetbits_le32(&denali_ctl[191], 0xF, (12 - sdram_ch->col));
+	clrsetbits_le32(&denali_ctl[190], (0x3 << 16) | (0x7 << 24),
+			((3 - sdram_ch->bk) << 16) |
+			((16 - row) << 24));
+
+	clrsetbits_le32(&denali_ctl[196], 0x3 | (1 << 16),
+			cs_map | (reduc << 16));
+
+	/* PI_199 PI_COL_DIFF:RW:0:4 */
+	clrsetbits_le32(&denali_pi[199], 0xF, (12 - sdram_ch->col));
+
+	/* PI_155 PI_ROW_DIFF:RW:24:3 PI_BANK_DIFF:RW:16:2 */
+	clrsetbits_le32(&denali_pi[155], (0x3 << 16) | (0x7 << 24),
+			((3 - sdram_ch->bk) << 16) |
+			((16 - row) << 24));
+	/* PI_41 PI_CS_MAP:RW:24:4 */
+	clrsetbits_le32(&denali_pi[41], 0xf << 24, cs_map << 24);
+	if ((sdram_ch->rank == 1) && (sdram_params->base.dramtype == DDR3))
+		writel(0x2EC7FFFF, &denali_pi[34]);
+}
+
+static void set_ds_odt(const struct chan_info *chan,
+		       const struct rk3399_sdram_params *sdram_params)
+{
+	u32 *denali_phy = chan->publ->denali_phy;
+
+	u32 tsel_idle_en, tsel_wr_en, tsel_rd_en;
+	u32 tsel_idle_select_p, tsel_wr_select_p, tsel_rd_select_p;
+	u32 ca_tsel_wr_select_p, ca_tsel_wr_select_n;
+	u32 tsel_idle_select_n, tsel_wr_select_n, tsel_rd_select_n;
+	u32 reg_value;
+
+	if (sdram_params->base.dramtype == LPDDR4) {
+		tsel_rd_select_p = PHY_DRV_ODT_Hi_Z;
+		tsel_wr_select_p = PHY_DRV_ODT_40;
+		ca_tsel_wr_select_p = PHY_DRV_ODT_40;
+		tsel_idle_select_p = PHY_DRV_ODT_Hi_Z;
+
+		tsel_rd_select_n = PHY_DRV_ODT_240;
+		tsel_wr_select_n = PHY_DRV_ODT_40;
+		ca_tsel_wr_select_n = PHY_DRV_ODT_40;
+		tsel_idle_select_n = PHY_DRV_ODT_240;
+	} else if (sdram_params->base.dramtype == LPDDR3) {
+		tsel_rd_select_p = PHY_DRV_ODT_240;
+		tsel_wr_select_p = PHY_DRV_ODT_34_3;
+		ca_tsel_wr_select_p = PHY_DRV_ODT_48;
+		tsel_idle_select_p = PHY_DRV_ODT_240;
+
+		tsel_rd_select_n = PHY_DRV_ODT_Hi_Z;
+		tsel_wr_select_n = PHY_DRV_ODT_34_3;
+		ca_tsel_wr_select_n = PHY_DRV_ODT_48;
+		tsel_idle_select_n = PHY_DRV_ODT_Hi_Z;
+	} else {
+		tsel_rd_select_p = PHY_DRV_ODT_240;
+		tsel_wr_select_p = PHY_DRV_ODT_34_3;
+		ca_tsel_wr_select_p = PHY_DRV_ODT_34_3;
+		tsel_idle_select_p = PHY_DRV_ODT_240;
+
+		tsel_rd_select_n = PHY_DRV_ODT_240;
+		tsel_wr_select_n = PHY_DRV_ODT_34_3;
+		ca_tsel_wr_select_n = PHY_DRV_ODT_34_3;
+		tsel_idle_select_n = PHY_DRV_ODT_240;
+	}
+
+	if (sdram_params->base.odt == 1)
+		tsel_rd_en = 1;
+	else
+		tsel_rd_en = 0;
+
+	tsel_wr_en = 0;
+	tsel_idle_en = 0;
+
+	/*
+	 * phy_dq_tsel_select_X 24bits DENALI_PHY_6/134/262/390 offset_0
+	 * sets termination values for read/idle cycles and drive strength
+	 * for write cycles for DQ/DM
+	 */
+	reg_value = tsel_rd_select_n | (tsel_rd_select_p << 0x4) |
+		    (tsel_wr_select_n << 8) | (tsel_wr_select_p << 12) |
+		    (tsel_idle_select_n << 16) | (tsel_idle_select_p << 20);
+	clrsetbits_le32(&denali_phy[6], 0xffffff, reg_value);
+	clrsetbits_le32(&denali_phy[134], 0xffffff, reg_value);
+	clrsetbits_le32(&denali_phy[262], 0xffffff, reg_value);
+	clrsetbits_le32(&denali_phy[390], 0xffffff, reg_value);
+
+	/*
+	 * phy_dqs_tsel_select_X 24bits DENALI_PHY_7/135/263/391 offset_0
+	 * sets termination values for read/idle cycles and drive strength
+	 * for write cycles for DQS
+	 */
+	clrsetbits_le32(&denali_phy[7], 0xffffff, reg_value);
+	clrsetbits_le32(&denali_phy[135], 0xffffff, reg_value);
+	clrsetbits_le32(&denali_phy[263], 0xffffff, reg_value);
+	clrsetbits_le32(&denali_phy[391], 0xffffff, reg_value);
+
+	/* phy_adr_tsel_select_ 8bits DENALI_PHY_544/672/800 offset_0 */
+	reg_value = ca_tsel_wr_select_n | (ca_tsel_wr_select_p << 0x4);
+	clrsetbits_le32(&denali_phy[544], 0xff, reg_value);
+	clrsetbits_le32(&denali_phy[672], 0xff, reg_value);
+	clrsetbits_le32(&denali_phy[800], 0xff, reg_value);
+
+	/* phy_pad_addr_drive 8bits DENALI_PHY_928 offset_0 */
+	clrsetbits_le32(&denali_phy[928], 0xff, reg_value);
+
+	/* phy_pad_rst_drive 8bits DENALI_PHY_937 offset_0 */
+	clrsetbits_le32(&denali_phy[937], 0xff, reg_value);
+
+	/* phy_pad_cke_drive 8bits DENALI_PHY_935 offset_0 */
+	clrsetbits_le32(&denali_phy[935], 0xff, reg_value);
+
+	/* phy_pad_cs_drive 8bits DENALI_PHY_939 offset_0 */
+	clrsetbits_le32(&denali_phy[939], 0xff, reg_value);
+
+	/* phy_pad_clk_drive 8bits DENALI_PHY_929 offset_0 */
+	clrsetbits_le32(&denali_phy[929], 0xff, reg_value);
+
+	/* phy_pad_fdbk_drive 23bit DENALI_PHY_924/925 */
+	clrsetbits_le32(&denali_phy[924], 0xff,
+			tsel_wr_select_n | (tsel_wr_select_p << 4));
+	clrsetbits_le32(&denali_phy[925], 0xff,
+			tsel_rd_select_n | (tsel_rd_select_p << 4));
+
+	/* phy_dq_tsel_enable_X 3bits DENALI_PHY_5/133/261/389 offset_16 */
+	reg_value = (tsel_rd_en | (tsel_wr_en << 1) | (tsel_idle_en << 2))
+		<< 16;
+	clrsetbits_le32(&denali_phy[5], 0x7 << 16, reg_value);
+	clrsetbits_le32(&denali_phy[133], 0x7 << 16, reg_value);
+	clrsetbits_le32(&denali_phy[261], 0x7 << 16, reg_value);
+	clrsetbits_le32(&denali_phy[389], 0x7 << 16, reg_value);
+
+	/* phy_dqs_tsel_enable_X 3bits DENALI_PHY_6/134/262/390 offset_24 */
+	reg_value = (tsel_rd_en | (tsel_wr_en << 1) | (tsel_idle_en << 2))
+		<< 24;
+	clrsetbits_le32(&denali_phy[6], 0x7 << 24, reg_value);
+	clrsetbits_le32(&denali_phy[134], 0x7 << 24, reg_value);
+	clrsetbits_le32(&denali_phy[262], 0x7 << 24, reg_value);
+	clrsetbits_le32(&denali_phy[390], 0x7 << 24, reg_value);
+
+	/* phy_adr_tsel_enable_ 1bit DENALI_PHY_518/646/774 offset_8 */
+	reg_value = tsel_wr_en << 8;
+	clrsetbits_le32(&denali_phy[518], 0x1 << 8, reg_value);
+	clrsetbits_le32(&denali_phy[646], 0x1 << 8, reg_value);
+	clrsetbits_le32(&denali_phy[774], 0x1 << 8, reg_value);
+
+	/* phy_pad_addr_term tsel 1bit DENALI_PHY_933 offset_17 */
+	reg_value = tsel_wr_en << 17;
+	clrsetbits_le32(&denali_phy[933], 0x1 << 17, reg_value);
+	/*
+	 * pad_rst/cke/cs/clk_term tsel 1bits
+	 * DENALI_PHY_938/936/940/934 offset_17
+	 */
+	clrsetbits_le32(&denali_phy[938], 0x1 << 17, reg_value);
+	clrsetbits_le32(&denali_phy[936], 0x1 << 17, reg_value);
+	clrsetbits_le32(&denali_phy[940], 0x1 << 17, reg_value);
+	clrsetbits_le32(&denali_phy[934], 0x1 << 17, reg_value);
+
+	/* phy_pad_fdbk_term 1bit DENALI_PHY_930 offset_17 */
+	clrsetbits_le32(&denali_phy[930], 0x1 << 17, reg_value);
+}
+
+static int phy_io_config(const struct chan_info *chan,
+			  const struct rk3399_sdram_params *sdram_params)
+{
+	u32 *denali_phy = chan->publ->denali_phy;
+	u32 vref_mode_dq, vref_value_dq, vref_mode_ac, vref_value_ac;
+	u32 mode_sel;
+	u32 reg_value;
+	u32 drv_value, odt_value;
+	u32 speed;
+
+	/* vref setting */
+	if (sdram_params->base.dramtype == LPDDR4) {
+		/* LPDDR4 */
+		vref_mode_dq = 0x6;
+		vref_value_dq = 0x1f;
+		vref_mode_ac = 0x6;
+		vref_value_ac = 0x1f;
+	} else if (sdram_params->base.dramtype == LPDDR3) {
+		if (sdram_params->base.odt == 1) {
+			vref_mode_dq = 0x5;  /* LPDDR3 ODT */
+			drv_value = (readl(&denali_phy[6]) >> 12) & 0xf;
+			odt_value = (readl(&denali_phy[6]) >> 4) & 0xf;
+			if (drv_value == PHY_DRV_ODT_48) {
+				switch (odt_value) {
+				case PHY_DRV_ODT_240:
+					vref_value_dq = 0x16;
+					break;
+				case PHY_DRV_ODT_120:
+					vref_value_dq = 0x26;
+					break;
+				case PHY_DRV_ODT_60:
+					vref_value_dq = 0x36;
+					break;
+				default:
+					debug("Invalid ODT value.\n");
+					return -EINVAL;
+				}
+			} else if (drv_value == PHY_DRV_ODT_40) {
+				switch (odt_value) {
+				case PHY_DRV_ODT_240:
+					vref_value_dq = 0x19;
+					break;
+				case PHY_DRV_ODT_120:
+					vref_value_dq = 0x23;
+					break;
+				case PHY_DRV_ODT_60:
+					vref_value_dq = 0x31;
+					break;
+				default:
+					debug("Invalid ODT value.\n");
+					return -EINVAL;
+				}
+			} else if (drv_value == PHY_DRV_ODT_34_3) {
+				switch (odt_value) {
+				case PHY_DRV_ODT_240:
+					vref_value_dq = 0x17;
+					break;
+				case PHY_DRV_ODT_120:
+					vref_value_dq = 0x20;
+					break;
+				case PHY_DRV_ODT_60:
+					vref_value_dq = 0x2e;
+					break;
+				default:
+					debug("Invalid ODT value.\n");
+					return -EINVAL;
+				}
+			} else {
+				debug("Invalid DRV value.\n");
+				return -EINVAL;
+			}
+		} else {
+			vref_mode_dq = 0x2;  /* LPDDR3 */
+			vref_value_dq = 0x1f;
+		}
+		vref_mode_ac = 0x2;
+		vref_value_ac = 0x1f;
+	} else if (sdram_params->base.dramtype == DDR3) {
+		/* DDR3L */
+		vref_mode_dq = 0x1;
+		vref_value_dq = 0x1f;
+		vref_mode_ac = 0x1;
+		vref_value_ac = 0x1f;
+	} else {
+		debug("Unknown DRAM type.\n");
+		return -EINVAL;
+	}
+
+	reg_value = (vref_mode_dq << 9) | (0x1 << 8) | vref_value_dq;
+
+	/* PHY_913 PHY_PAD_VREF_CTRL_DQ_0 12bits offset_8 */
+	clrsetbits_le32(&denali_phy[913], 0xfff << 8, reg_value << 8);
+	/* PHY_914 PHY_PAD_VREF_CTRL_DQ_1 12bits offset_0 */
+	clrsetbits_le32(&denali_phy[914], 0xfff, reg_value);
+	/* PHY_914 PHY_PAD_VREF_CTRL_DQ_2 12bits offset_16 */
+	clrsetbits_le32(&denali_phy[914], 0xfff << 16, reg_value << 16);
+	/* PHY_915 PHY_PAD_VREF_CTRL_DQ_3 12bits offset_0 */
+	clrsetbits_le32(&denali_phy[915], 0xfff, reg_value);
+
+	reg_value = (vref_mode_ac << 9) | (0x1 << 8) | vref_value_ac;
+
+	/* PHY_915 PHY_PAD_VREF_CTRL_AC 12bits offset_16 */
+	clrsetbits_le32(&denali_phy[915], 0xfff << 16, reg_value << 16);
+
+	if (sdram_params->base.dramtype == LPDDR4)
+		mode_sel = 0x6;
+	else if (sdram_params->base.dramtype == LPDDR3)
+		mode_sel = 0x0;
+	else if (sdram_params->base.dramtype == DDR3)
+		mode_sel = 0x1;
+	else
+		return -EINVAL;
+
+	/* PHY_924 PHY_PAD_FDBK_DRIVE */
+	clrsetbits_le32(&denali_phy[924], 0x7 << 15, mode_sel << 15);
+	/* PHY_926 PHY_PAD_DATA_DRIVE */
+	clrsetbits_le32(&denali_phy[926], 0x7 << 6, mode_sel << 6);
+	/* PHY_927 PHY_PAD_DQS_DRIVE */
+	clrsetbits_le32(&denali_phy[927], 0x7 << 6, mode_sel << 6);
+	/* PHY_928 PHY_PAD_ADDR_DRIVE */
+	clrsetbits_le32(&denali_phy[928], 0x7 << 14, mode_sel << 14);
+	/* PHY_929 PHY_PAD_CLK_DRIVE */
+	clrsetbits_le32(&denali_phy[929], 0x7 << 14, mode_sel << 14);
+	/* PHY_935 PHY_PAD_CKE_DRIVE */
+	clrsetbits_le32(&denali_phy[935], 0x7 << 14, mode_sel << 14);
+	/* PHY_937 PHY_PAD_RST_DRIVE */
+	clrsetbits_le32(&denali_phy[937], 0x7 << 14, mode_sel << 14);
+	/* PHY_939 PHY_PAD_CS_DRIVE */
+	clrsetbits_le32(&denali_phy[939], 0x7 << 14, mode_sel << 14);
+
+
+	/* speed setting */
+	if (sdram_params->base.ddr_freq < 400)
+		speed = 0x0;
+	else if (sdram_params->base.ddr_freq < 800)
+		speed = 0x1;
+	else if (sdram_params->base.ddr_freq < 1200)
+		speed = 0x2;
+	else
+		speed = 0x3;
+
+	/* PHY_924 PHY_PAD_FDBK_DRIVE */
+	clrsetbits_le32(&denali_phy[924], 0x3 << 21, speed << 21);
+	/* PHY_926 PHY_PAD_DATA_DRIVE */
+	clrsetbits_le32(&denali_phy[926], 0x3 << 9, speed << 9);
+	/* PHY_927 PHY_PAD_DQS_DRIVE */
+	clrsetbits_le32(&denali_phy[927], 0x3 << 9, speed << 9);
+	/* PHY_928 PHY_PAD_ADDR_DRIVE */
+	clrsetbits_le32(&denali_phy[928], 0x3 << 17, speed << 17);
+	/* PHY_929 PHY_PAD_CLK_DRIVE */
+	clrsetbits_le32(&denali_phy[929], 0x3 << 17, speed << 17);
+	/* PHY_935 PHY_PAD_CKE_DRIVE */
+	clrsetbits_le32(&denali_phy[935], 0x3 << 17, speed << 17);
+	/* PHY_937 PHY_PAD_RST_DRIVE */
+	clrsetbits_le32(&denali_phy[937], 0x3 << 17, speed << 17);
+	/* PHY_939 PHY_PAD_CS_DRIVE */
+	clrsetbits_le32(&denali_phy[939], 0x3 << 17, speed << 17);
+
+	return 0;
+}
+
+static int pctl_cfg(const struct chan_info *chan, u32 channel,
+		    const struct rk3399_sdram_params *sdram_params)
+{
+	u32 *denali_ctl = chan->pctl->denali_ctl;
+	u32 *denali_pi = chan->pi->denali_pi;
+	u32 *denali_phy = chan->publ->denali_phy;
+	const u32 *params_ctl = sdram_params->pctl_regs.denali_ctl;
+	const u32 *params_phy = sdram_params->phy_regs.denali_phy;
+	u32 tmp, tmp1, tmp2;
+	u32 pwrup_srefresh_exit;
+	int ret;
+
+	/*
+	 * work around controller bug:
+	 * Do not program DRAM_CLASS until NO_PHY_IND_TRAIN_INT is programmed
+	 */
+	copy_to_reg(&denali_ctl[1], &params_ctl[1],
+		    sizeof(struct rk3399_ddr_pctl_regs) - 4);
+	writel(params_ctl[0], &denali_ctl[0]);
+	copy_to_reg(denali_pi, &sdram_params->pi_regs.denali_pi[0],
+		    sizeof(struct rk3399_ddr_pi_regs));
+	/* rank count need to set for init */
+	set_memory_map(chan, channel, sdram_params);
+
+	writel(sdram_params->phy_regs.denali_phy[910], &denali_phy[910]);
+	writel(sdram_params->phy_regs.denali_phy[911], &denali_phy[911]);
+	writel(sdram_params->phy_regs.denali_phy[912], &denali_phy[912]);
+
+	pwrup_srefresh_exit = readl(&denali_ctl[68]) & PWRUP_SREFRESH_EXIT;
+	clrbits_le32(&denali_ctl[68], PWRUP_SREFRESH_EXIT);
+
+	/* PHY_DLL_RST_EN */
+	clrsetbits_le32(&denali_phy[957], 0x3 << 24, 1 << 24);
+
+	setbits_le32(&denali_pi[0], START);
+	setbits_le32(&denali_ctl[0], START);
+
+	/* Wating for phy DLL lock */
+	while (1) {
+		tmp = readl(&denali_phy[920]);
+		tmp1 = readl(&denali_phy[921]);
+		tmp2 = readl(&denali_phy[922]);
+		if ((((tmp >> 16) & 0x1) == 0x1) &&
+		    (((tmp1 >> 16) & 0x1) == 0x1) &&
+		    (((tmp1 >> 0) & 0x1) == 0x1) &&
+		    (((tmp2 >> 0) & 0x1) == 0x1))
+			break;
+	}
+
+	copy_to_reg(&denali_phy[896], &params_phy[896], (958 - 895) * 4);
+	copy_to_reg(&denali_phy[0], &params_phy[0], (90 - 0 + 1) * 4);
+	copy_to_reg(&denali_phy[128], &params_phy[128], (218 - 128 + 1) * 4);
+	copy_to_reg(&denali_phy[256], &params_phy[256], (346 - 256 + 1) * 4);
+	copy_to_reg(&denali_phy[384], &params_phy[384], (474 - 384 + 1) * 4);
+	copy_to_reg(&denali_phy[512], &params_phy[512], (549 - 512 + 1) * 4);
+	copy_to_reg(&denali_phy[640], &params_phy[640], (677 - 640 + 1) * 4);
+	copy_to_reg(&denali_phy[768], &params_phy[768], (805 - 768 + 1) * 4);
+	set_ds_odt(chan, sdram_params);
+
+	/*
+	 * phy_dqs_tsel_wr_timing_X 8bits DENALI_PHY_84/212/340/468 offset_8
+	 * dqs_tsel_wr_end[7:4] add Half cycle
+	 */
+	tmp = (readl(&denali_phy[84]) >> 8) & 0xff;
+	clrsetbits_le32(&denali_phy[84], 0xff << 8, (tmp + 0x10) << 8);
+	tmp = (readl(&denali_phy[212]) >> 8) & 0xff;
+	clrsetbits_le32(&denali_phy[212], 0xff << 8, (tmp + 0x10) << 8);
+	tmp = (readl(&denali_phy[340]) >> 8) & 0xff;
+	clrsetbits_le32(&denali_phy[340], 0xff << 8, (tmp + 0x10) << 8);
+	tmp = (readl(&denali_phy[468]) >> 8) & 0xff;
+	clrsetbits_le32(&denali_phy[468], 0xff << 8, (tmp + 0x10) << 8);
+
+	/*
+	 * phy_dqs_tsel_wr_timing_X 8bits DENALI_PHY_83/211/339/467 offset_8
+	 * dq_tsel_wr_end[7:4] add Half cycle
+	 */
+	tmp = (readl(&denali_phy[83]) >> 16) & 0xff;
+	clrsetbits_le32(&denali_phy[83], 0xff << 16, (tmp + 0x10) << 16);
+	tmp = (readl(&denali_phy[211]) >> 16) & 0xff;
+	clrsetbits_le32(&denali_phy[211], 0xff << 16, (tmp + 0x10) << 16);
+	tmp = (readl(&denali_phy[339]) >> 16) & 0xff;
+	clrsetbits_le32(&denali_phy[339], 0xff << 16, (tmp + 0x10) << 16);
+	tmp = (readl(&denali_phy[467]) >> 16) & 0xff;
+	clrsetbits_le32(&denali_phy[467], 0xff << 16, (tmp + 0x10) << 16);
+
+	ret = phy_io_config(chan, sdram_params);
+	if (ret)
+		return ret;
+
+	/* PHY_DLL_RST_EN */
+	clrsetbits_le32(&denali_phy[957], 0x3 << 24, 0x2 << 24);
+
+	/* Wating for PHY and DRAM init complete */
+	tmp = 0;
+	while (!(readl(&denali_ctl[203]) & (1 << 3))) {
+		mdelay(10);
+		tmp++;
+		if (tmp > 10)
+			return -ETIME;
+	}
+
+	clrsetbits_le32(&denali_ctl[68], PWRUP_SREFRESH_EXIT,
+			pwrup_srefresh_exit);
+	return 0;
+}
+
+static void select_per_cs_training_index(const struct chan_info *chan,
+					 u32 rank)
+{
+	u32 *denali_phy = chan->publ->denali_phy;
+
+	/* PHY_84 PHY_PER_CS_TRAINING_EN_0 1bit offset_16 */
+	if ((readl(&denali_phy[84])>>16) & 1) {
+		/*
+		 * PHY_8/136/264/392
+		 * phy_per_cs_training_index_X 1bit offset_24
+		 */
+		clrsetbits_le32(&denali_phy[8], 0x1 << 24, rank << 24);
+		clrsetbits_le32(&denali_phy[136], 0x1 << 24, rank << 24);
+		clrsetbits_le32(&denali_phy[264], 0x1 << 24, rank << 24);
+		clrsetbits_le32(&denali_phy[392], 0x1 << 24, rank << 24);
+	}
+}
+
+static void override_write_leveling_value(const struct chan_info *chan)
+{
+	u32 *denali_ctl = chan->pctl->denali_ctl;
+	u32 *denali_phy = chan->publ->denali_phy;
+	u32 byte;
+
+	/* PHY_896 PHY_FREQ_SEL_MULTICAST_EN 1bit offset_0 */
+	setbits_le32(&denali_phy[896], 1);
+
+	/*
+	 * PHY_8/136/264/392
+	 * phy_per_cs_training_multicast_en_X 1bit offset_16
+	 */
+	clrsetbits_le32(&denali_phy[8], 0x1 << 16, 1 << 16);
+	clrsetbits_le32(&denali_phy[136], 0x1 << 16, 1 << 16);
+	clrsetbits_le32(&denali_phy[264], 0x1 << 16, 1 << 16);
+	clrsetbits_le32(&denali_phy[392], 0x1 << 16, 1 << 16);
+
+	for (byte = 0; byte < 4; byte++)
+		clrsetbits_le32(&denali_phy[63 + (128 * byte)], 0xffff << 16,
+				0x200 << 16);
+
+	/* PHY_896 PHY_FREQ_SEL_MULTICAST_EN 1bit offset_0 */
+	clrbits_le32(&denali_phy[896], 1);
+
+	/* CTL_200 ctrlupd_req 1bit offset_8 */
+	clrsetbits_le32(&denali_ctl[200], 0x1 << 8, 0x1 << 8);
+}
+
+static int data_training_ca(const struct chan_info *chan, u32 channel,
+			    const struct rk3399_sdram_params *sdram_params)
+{
+	u32 *denali_pi = chan->pi->denali_pi;
+	u32 *denali_phy = chan->publ->denali_phy;
+	u32 i, tmp;
+	u32 obs_0, obs_1, obs_2, obs_err = 0;
+	u32 rank = sdram_params->ch[channel].rank;
+
+	for (i = 0; i < rank; i++) {
+		select_per_cs_training_index(chan, i);
+		/* PI_100 PI_CALVL_EN:RW:8:2 */
+		clrsetbits_le32(&denali_pi[100], 0x3 << 8, 0x2 << 8);
+		/* PI_92 PI_CALVL_REQ:WR:16:1,PI_CALVL_CS:RW:24:2 */
+		clrsetbits_le32(&denali_pi[92],
+				(0x1 << 16) | (0x3 << 24),
+				(0x1 << 16) | (i << 24));
+
+		/* Waiting for training complete */
+		while (1) {
+			/* PI_174 PI_INT_STATUS:RD:8:18 */
+			tmp = readl(&denali_pi[174]) >> 8;
+			/*
+			 * check status obs
+			 * PHY_532/660/789 phy_adr_calvl_obs1_:0:32
+			 */
+			obs_0 = readl(&denali_phy[532]);
+			obs_1 = readl(&denali_phy[660]);
+			obs_2 = readl(&denali_phy[788]);
+			if (((obs_0 >> 30) & 0x3) ||
+			    ((obs_1 >> 30) & 0x3) ||
+			    ((obs_2 >> 30) & 0x3))
+				obs_err = 1;
+			if ((((tmp >> 11) & 0x1) == 0x1) &&
+			    (((tmp >> 13) & 0x1) == 0x1) &&
+			    (((tmp >> 5) & 0x1) == 0x0) &&
+			    (obs_err == 0))
+				break;
+			else if ((((tmp >> 5) & 0x1) == 0x1) ||
+				 (obs_err == 1))
+				return -EIO;
+		}
+		/* clear interrupt,PI_175 PI_INT_ACK:WR:0:17 */
+		writel(0x00003f7c, (&denali_pi[175]));
+	}
+	clrbits_le32(&denali_pi[100], 0x3 << 8);
+
+	return 0;
+}
+
+static int data_training_wl(const struct chan_info *chan, u32 channel,
+			    const struct rk3399_sdram_params *sdram_params)
+{
+	u32 *denali_pi = chan->pi->denali_pi;
+	u32 *denali_phy = chan->publ->denali_phy;
+	u32 i, tmp;
+	u32 obs_0, obs_1, obs_2, obs_3, obs_err = 0;
+	u32 rank = sdram_params->ch[channel].rank;
+
+	for (i = 0; i < rank; i++) {
+		select_per_cs_training_index(chan, i);
+		/* PI_60 PI_WRLVL_EN:RW:8:2 */
+		clrsetbits_le32(&denali_pi[60], 0x3 << 8, 0x2 << 8);
+		/* PI_59 PI_WRLVL_REQ:WR:8:1,PI_WRLVL_CS:RW:16:2 */
+		clrsetbits_le32(&denali_pi[59],
+				(0x1 << 8) | (0x3 << 16),
+				(0x1 << 8) | (i << 16));
+
+		/* Waiting for training complete */
+		while (1) {
+			/* PI_174 PI_INT_STATUS:RD:8:18 */
+			tmp = readl(&denali_pi[174]) >> 8;
+
+			/*
+			 * check status obs, if error maybe can not
+			 * get leveling done PHY_40/168/296/424
+			 * phy_wrlvl_status_obs_X:0:13
+			 */
+			obs_0 = readl(&denali_phy[40]);
+			obs_1 = readl(&denali_phy[168]);
+			obs_2 = readl(&denali_phy[296]);
+			obs_3 = readl(&denali_phy[424]);
+			if (((obs_0 >> 12) & 0x1) ||
+			    ((obs_1 >> 12) & 0x1) ||
+			    ((obs_2 >> 12) & 0x1) ||
+			    ((obs_3 >> 12) & 0x1))
+				obs_err = 1;
+			if ((((tmp >> 10) & 0x1) == 0x1) &&
+			    (((tmp >> 13) & 0x1) == 0x1) &&
+			    (((tmp >> 4) & 0x1) == 0x0) &&
+			    (obs_err == 0))
+				break;
+			else if ((((tmp >> 4) & 0x1) == 0x1) ||
+				 (obs_err == 1))
+				return -EIO;
+		}
+		/* clear interrupt,PI_175 PI_INT_ACK:WR:0:17 */
+		writel(0x00003f7c, (&denali_pi[175]));
+	}
+
+	override_write_leveling_value(chan);
+	clrbits_le32(&denali_pi[60], 0x3 << 8);
+
+	return 0;
+}
+
+static int data_training_rg(const struct chan_info *chan, u32 channel,
+			    const struct rk3399_sdram_params *sdram_params)
+{
+	u32 *denali_pi = chan->pi->denali_pi;
+	u32 *denali_phy = chan->publ->denali_phy;
+	u32 i, tmp;
+	u32 obs_0, obs_1, obs_2, obs_3, obs_err = 0;
+	u32 rank = sdram_params->ch[channel].rank;
+
+	for (i = 0; i < rank; i++) {
+		select_per_cs_training_index(chan, i);
+		/* PI_80 PI_RDLVL_GATE_EN:RW:24:2 */
+		clrsetbits_le32(&denali_pi[80], 0x3 << 24, 0x2 << 24);
+		/*
+		 * PI_74 PI_RDLVL_GATE_REQ:WR:16:1
+		 * PI_RDLVL_CS:RW:24:2
+		 */
+		clrsetbits_le32(&denali_pi[74],
+				(0x1 << 16) | (0x3 << 24),
+				(0x1 << 16) | (i << 24));
+
+		/* Waiting for training complete */
+		while (1) {
+			/* PI_174 PI_INT_STATUS:RD:8:18 */
+			tmp = readl(&denali_pi[174]) >> 8;
+
+			/*
+			 * check status obs
+			 * PHY_43/171/299/427
+			 *     PHY_GTLVL_STATUS_OBS_x:16:8
+			 */
+			obs_0 = readl(&denali_phy[43]);
+			obs_1 = readl(&denali_phy[171]);
+			obs_2 = readl(&denali_phy[299]);
+			obs_3 = readl(&denali_phy[427]);
+			if (((obs_0 >> (16 + 6)) & 0x3) ||
+			    ((obs_1 >> (16 + 6)) & 0x3) ||
+			    ((obs_2 >> (16 + 6)) & 0x3) ||
+			    ((obs_3 >> (16 + 6)) & 0x3))
+				obs_err = 1;
+			if ((((tmp >> 9) & 0x1) == 0x1) &&
+			    (((tmp >> 13) & 0x1) == 0x1) &&
+			    (((tmp >> 3) & 0x1) == 0x0) &&
+			    (obs_err == 0))
+				break;
+			else if ((((tmp >> 3) & 0x1) == 0x1) ||
+				 (obs_err == 1))
+				return -EIO;
+		}
+		/* clear interrupt,PI_175 PI_INT_ACK:WR:0:17 */
+		writel(0x00003f7c, (&denali_pi[175]));
+	}
+	clrbits_le32(&denali_pi[80], 0x3 << 24);
+
+	return 0;
+}
+
+static int data_training_rl(const struct chan_info *chan, u32 channel,
+			    const struct rk3399_sdram_params *sdram_params)
+{
+	u32 *denali_pi = chan->pi->denali_pi;
+	u32 i, tmp;
+	u32 rank = sdram_params->ch[channel].rank;
+
+	for (i = 0; i < rank; i++) {
+		select_per_cs_training_index(chan, i);
+		/* PI_80 PI_RDLVL_EN:RW:16:2 */
+		clrsetbits_le32(&denali_pi[80], 0x3 << 16, 0x2 << 16);
+		/* PI_74 PI_RDLVL_REQ:WR:8:1,PI_RDLVL_CS:RW:24:2 */
+		clrsetbits_le32(&denali_pi[74],
+				(0x1 << 8) | (0x3 << 24),
+				(0x1 << 8) | (i << 24));
+
+		/* Waiting for training complete */
+		while (1) {
+			/* PI_174 PI_INT_STATUS:RD:8:18 */
+			tmp = readl(&denali_pi[174]) >> 8;
+
+			/*
+			 * make sure status obs not report error bit
+			 * PHY_46/174/302/430
+			 *     phy_rdlvl_status_obs_X:16:8
+			 */
+			if ((((tmp >> 8) & 0x1) == 0x1) &&
+			    (((tmp >> 13) & 0x1) == 0x1) &&
+			    (((tmp >> 2) & 0x1) == 0x0))
+				break;
+			else if (((tmp >> 2) & 0x1) == 0x1)
+				return -EIO;
+		}
+		/* clear interrupt,PI_175 PI_INT_ACK:WR:0:17 */
+		writel(0x00003f7c, (&denali_pi[175]));
+	}
+	clrbits_le32(&denali_pi[80], 0x3 << 16);
+
+	return 0;
+}
+
+static int data_training_wdql(const struct chan_info *chan, u32 channel,
+			      const struct rk3399_sdram_params *sdram_params)
+{
+	u32 *denali_pi = chan->pi->denali_pi;
+	u32 i, tmp;
+	u32 rank = sdram_params->ch[channel].rank;
+
+	for (i = 0; i < rank; i++) {
+		select_per_cs_training_index(chan, i);
+		/*
+		 * disable PI_WDQLVL_VREF_EN before wdq leveling?
+		 * PI_181 PI_WDQLVL_VREF_EN:RW:8:1
+		 */
+		clrbits_le32(&denali_pi[181], 0x1 << 8);
+		/* PI_124 PI_WDQLVL_EN:RW:16:2 */
+		clrsetbits_le32(&denali_pi[124], 0x3 << 16, 0x2 << 16);
+		/* PI_121 PI_WDQLVL_REQ:WR:8:1,PI_WDQLVL_CS:RW:16:2 */
+		clrsetbits_le32(&denali_pi[121],
+				(0x1 << 8) | (0x3 << 16),
+				(0x1 << 8) | (i << 16));
+
+		/* Waiting for training complete */
+		while (1) {
+			/* PI_174 PI_INT_STATUS:RD:8:18 */
+			tmp = readl(&denali_pi[174]) >> 8;
+			if ((((tmp >> 12) & 0x1) == 0x1) &&
+			    (((tmp >> 13) & 0x1) == 0x1) &&
+			    (((tmp >> 6) & 0x1) == 0x0))
+				break;
+			else if (((tmp >> 6) & 0x1) == 0x1)
+				return -EIO;
+		}
+		/* clear interrupt,PI_175 PI_INT_ACK:WR:0:17 */
+		writel(0x00003f7c, (&denali_pi[175]));
+	}
+	clrbits_le32(&denali_pi[124], 0x3 << 16);
+
+	return 0;
+}
+
+static int data_training(const struct chan_info *chan, u32 channel,
+			 const struct rk3399_sdram_params *sdram_params,
+			 u32 training_flag)
+{
+	u32 *denali_phy = chan->publ->denali_phy;
+
+	/* PHY_927 PHY_PAD_DQS_DRIVE  RPULL offset_22 */
+	setbits_le32(&denali_phy[927], (1 << 22));
+
+	if (training_flag == PI_FULL_TRAINING) {
+		if (sdram_params->base.dramtype == LPDDR4) {
+			training_flag = PI_CA_TRAINING | PI_WRITE_LEVELING |
+					PI_READ_GATE_TRAINING |
+					PI_READ_LEVELING | PI_WDQ_LEVELING;
+		} else if (sdram_params->base.dramtype == LPDDR3) {
+			training_flag = PI_CA_TRAINING | PI_WRITE_LEVELING |
+					PI_READ_GATE_TRAINING;
+		} else if (sdram_params->base.dramtype == DDR3) {
+			training_flag = PI_WRITE_LEVELING |
+					PI_READ_GATE_TRAINING |
+					PI_READ_LEVELING;
+		}
+	}
+
+	/* ca training(LPDDR4,LPDDR3 support) */
+	if ((training_flag & PI_CA_TRAINING) == PI_CA_TRAINING)
+		data_training_ca(chan, channel, sdram_params);
+
+	/* write leveling(LPDDR4,LPDDR3,DDR3 support) */
+	if ((training_flag & PI_WRITE_LEVELING) == PI_WRITE_LEVELING)
+		data_training_wl(chan, channel, sdram_params);
+
+	/* read gate training(LPDDR4,LPDDR3,DDR3 support) */
+	if ((training_flag & PI_READ_GATE_TRAINING) == PI_READ_GATE_TRAINING)
+		data_training_rg(chan, channel, sdram_params);
+
+	/* read leveling(LPDDR4,LPDDR3,DDR3 support) */
+	if ((training_flag & PI_READ_LEVELING) == PI_READ_LEVELING)
+		data_training_rl(chan, channel, sdram_params);
+
+	/* wdq leveling(LPDDR4 support) */
+	if ((training_flag & PI_WDQ_LEVELING) == PI_WDQ_LEVELING)
+		data_training_wdql(chan, channel, sdram_params);
+
+	/* PHY_927 PHY_PAD_DQS_DRIVE  RPULL offset_22 */
+	clrbits_le32(&denali_phy[927], (1 << 22));
+
+	return 0;
+}
+
+static void set_ddrconfig(const struct chan_info *chan,
+			  const struct rk3399_sdram_params *sdram_params,
+			  unsigned char channel, u32 ddrconfig)
+{
+	/* only need to set ddrconfig */
+	struct rk3399_msch_regs *ddr_msch_regs = chan->msch;
+	unsigned int cs0_cap = 0;
+	unsigned int cs1_cap = 0;
+
+	cs0_cap = (1 << (sdram_params->ch[channel].cs0_row
+			+ sdram_params->ch[channel].col
+			+ sdram_params->ch[channel].bk
+			+ sdram_params->ch[channel].bw - 20));
+	if (sdram_params->ch[channel].rank > 1)
+		cs1_cap = cs0_cap >> (sdram_params->ch[channel].cs0_row
+				- sdram_params->ch[channel].cs1_row);
+	if (sdram_params->ch[channel].row_3_4) {
+		cs0_cap = cs0_cap * 3 / 4;
+		cs1_cap = cs1_cap * 3 / 4;
+	}
+
+	writel(ddrconfig | (ddrconfig << 8), &ddr_msch_regs->ddrconf);
+	writel(((cs0_cap / 32) & 0xff) | (((cs1_cap / 32) & 0xff) << 8),
+	       &ddr_msch_regs->ddrsize);
+}
+
+static void dram_all_config(struct dram_info *dram,
+			    const struct rk3399_sdram_params *sdram_params)
+{
+	u32 sys_reg = 0;
+	unsigned int channel, idx;
+
+	sys_reg |= sdram_params->base.dramtype << SYS_REG_DDRTYPE_SHIFT;
+	sys_reg |= (sdram_params->base.num_channels - 1)
+		    << SYS_REG_NUM_CH_SHIFT;
+	for (channel = 0, idx = 0;
+	     (idx < sdram_params->base.num_channels) && (channel < 2);
+	     channel++) {
+		const struct rk3399_sdram_channel *info =
+			&sdram_params->ch[channel];
+		struct rk3399_msch_regs *ddr_msch_regs;
+		const struct rk3399_msch_timings *noc_timing;
+
+		if (sdram_params->ch[channel].col == 0)
+			continue;
+		idx++;
+		sys_reg |= info->row_3_4 << SYS_REG_ROW_3_4_SHIFT(channel);
+		sys_reg |= 1 << SYS_REG_CHINFO_SHIFT(channel);
+		sys_reg |= (info->rank - 1) << SYS_REG_RANK_SHIFT(channel);
+		sys_reg |= (info->col - 9) << SYS_REG_COL_SHIFT(channel);
+		sys_reg |= info->bk == 3 ? 0 : 1 << SYS_REG_BK_SHIFT(channel);
+		sys_reg |= (info->cs0_row - 13) << SYS_REG_CS0_ROW_SHIFT(channel);
+		sys_reg |= (info->cs1_row - 13) << SYS_REG_CS1_ROW_SHIFT(channel);
+		sys_reg |= (2 >> info->bw) << SYS_REG_BW_SHIFT(channel);
+		sys_reg |= (2 >> info->dbw) << SYS_REG_DBW_SHIFT(channel);
+
+		ddr_msch_regs = dram->chan[channel].msch;
+		noc_timing = &sdram_params->ch[channel].noc_timings;
+		writel(noc_timing->ddrtiminga0,
+		       &ddr_msch_regs->ddrtiminga0);
+		writel(noc_timing->ddrtimingb0,
+		       &ddr_msch_regs->ddrtimingb0);
+		writel(noc_timing->ddrtimingc0,
+		       &ddr_msch_regs->ddrtimingc0);
+		writel(noc_timing->devtodev0,
+		       &ddr_msch_regs->devtodev0);
+		writel(noc_timing->ddrmode,
+		       &ddr_msch_regs->ddrmode);
+
+		/* rank 1 memory clock disable (dfi_dram_clk_disable = 1) */
+		if (sdram_params->ch[channel].rank == 1)
+			setbits_le32(&dram->chan[channel].pctl->denali_ctl[276],
+				     1 << 17);
+	}
+
+	writel(sys_reg, &dram->pmugrf->os_reg2);
+	rk_clrsetreg(&dram->pmusgrf->soc_con4, 0x1f << 10,
+		     sdram_params->base.stride << 10);
+
+	/* reboot hold register set */
+	writel(PRESET_SGRF_HOLD(0) | PRESET_GPIO0_HOLD(1) |
+		PRESET_GPIO1_HOLD(1),
+		&dram->pmucru->pmucru_rstnhold_con[1]);
+	clrsetbits_le32(&dram->cru->glb_rst_con, 0x3, 0x3);
+}
+
+static int switch_to_phy_index1(struct dram_info *dram,
+				 const struct rk3399_sdram_params *sdram_params)
+{
+	u32 channel;
+	u32 *denali_phy;
+	u32 ch_count = sdram_params->base.num_channels;
+	int ret;
+	int i = 0;
+
+	writel(RK_CLRSETBITS(0x03 << 4 | 1 << 2 | 1,
+			     1 << 4 | 1 << 2 | 1),
+			&dram->cic->cic_ctrl0);
+	while (!(readl(&dram->cic->cic_status0) & (1 << 2))) {
+		mdelay(10);
+		i++;
+		if (i > 10) {
+			debug("index1 frequency change overtime\n");
+			return -ETIME;
+		}
+	}
+
+	i = 0;
+	writel(RK_CLRSETBITS(1 << 1, 1 << 1), &dram->cic->cic_ctrl0);
+	while (!(readl(&dram->cic->cic_status0) & (1 << 0))) {
+		mdelay(10);
+		if (i > 10) {
+			debug("index1 frequency done overtime\n");
+			return -ETIME;
+		}
+	}
+
+	for (channel = 0; channel < ch_count; channel++) {
+		denali_phy = dram->chan[channel].publ->denali_phy;
+		clrsetbits_le32(&denali_phy[896], (0x3 << 8) | 1, 1 << 8);
+		ret = data_training(&dram->chan[channel], channel,
+				  sdram_params, PI_FULL_TRAINING);
+		if (ret) {
+			debug("index1 training failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int sdram_init(struct dram_info *dram,
+		      const struct rk3399_sdram_params *sdram_params)
+{
+	unsigned char dramtype = sdram_params->base.dramtype;
+	unsigned int ddr_freq = sdram_params->base.ddr_freq;
+	int channel;
+
+	debug("Starting SDRAM initialization...\n");
+
+	if ((dramtype == DDR3 && ddr_freq > 800) ||
+	    (dramtype == LPDDR3 && ddr_freq > 933) ||
+	    (dramtype == LPDDR4 && ddr_freq > 800)) {
+		debug("SDRAM frequency is to high!");
+		return -E2BIG;
+	}
+
+	for (channel = 0; channel < 2; channel++) {
+		const struct chan_info *chan = &dram->chan[channel];
+		struct rk3399_ddr_publ_regs *publ = chan->publ;
+
+		phy_dll_bypass_set(publ, ddr_freq);
+
+		if (channel >= sdram_params->base.num_channels)
+			continue;
+
+		if (pctl_cfg(chan, channel, sdram_params) != 0) {
+			printf("pctl_cfg fail, reset\n");
+			return -EIO;
+		}
+
+		/* LPDDR2/LPDDR3 need to wait DAI complete, max 10us */
+		if (dramtype == LPDDR3)
+			udelay(10);
+
+		if (data_training(chan, channel,
+				  sdram_params, PI_FULL_TRAINING)) {
+			printf("SDRAM initialization failed, reset\n");
+			return -EIO;
+		}
+
+		set_ddrconfig(chan, sdram_params, channel,
+			      sdram_params->ch[channel].ddrconfig);
+	}
+	dram_all_config(dram, sdram_params);
+	switch_to_phy_index1(dram, sdram_params);
+
+	debug("Finish SDRAM initialization...\n");
+	return 0;
+}
+
+static int rk3399_dmc_ofdata_to_platdata(struct udevice *dev)
+{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rockchip_dmc_plat *plat = dev_get_platdata(dev);
+	const void *blob = gd->fdt_blob;
+	int node = dev->of_offset;
+	int ret;
+
+	ret = fdtdec_get_int_array(blob, node, "rockchip,sdram-params",
+			(u32 *)&plat->sdram_params,
+			sizeof(plat->sdram_params) / sizeof(u32));
+	if (ret) {
+		printf("%s: Cannot read rockchip,sdram-params %d\n",
+		       __func__, ret);
+		return ret;
+	}
+	ret = regmap_init_mem(dev, &plat->map);
+	if (ret)
+		printf("%s: regmap failed %d\n", __func__, ret);
+
+#endif
+	return 0;
+}
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+static int conv_of_platdata(struct udevice *dev)
+{
+	struct rockchip_dmc_plat *plat = dev_get_platdata(dev);
+	struct dtd_rockchip_rk3399_dmc *dtplat = &plat->dtplat;
+	int ret;
+
+	ret = regmap_init_mem_platdata(dev, dtplat->reg,
+			ARRAY_SIZE(dtplat->reg) / 4,
+			&plat->map);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+#endif
+
+static int rk3399_dmc_init(struct udevice *dev)
+{
+	struct dram_info *priv = dev_get_priv(dev);
+	struct rockchip_dmc_plat *plat = dev_get_platdata(dev);
+	int ret;
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rk3399_sdram_params *params = &plat->sdram_params;
+#else
+	struct dtd_rockchip_rk3399_dmc *dtplat = &plat->dtplat;
+	struct rk3399_sdram_params *params =
+					(void *)dtplat->rockchip_sdram_params;
+
+	ret = conv_of_platdata(dev);
+	if (ret)
+		return ret;
+#endif
+
+	priv->cic = syscon_get_first_range(ROCKCHIP_SYSCON_CIC);
+	priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF);
+	priv->pmusgrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUSGRF);
+	priv->pmucru = rockchip_get_pmucru();
+	priv->cru = rockchip_get_cru();
+	priv->chan[0].pctl = regmap_get_range(plat->map, 0);
+	priv->chan[0].pi = regmap_get_range(plat->map, 1);
+	priv->chan[0].publ = regmap_get_range(plat->map, 2);
+	priv->chan[0].msch = regmap_get_range(plat->map, 3);
+	priv->chan[1].pctl = regmap_get_range(plat->map, 4);
+	priv->chan[1].pi = regmap_get_range(plat->map, 5);
+	priv->chan[1].publ = regmap_get_range(plat->map, 6);
+	priv->chan[1].msch = regmap_get_range(plat->map, 7);
+
+	debug("con reg %p %p %p %p %p %p %p %p\n",
+	      priv->chan[0].pctl, priv->chan[0].pi,
+	      priv->chan[0].publ, priv->chan[0].msch,
+	      priv->chan[1].pctl, priv->chan[1].pi,
+	      priv->chan[1].publ, priv->chan[1].msch);
+	debug("cru %p, cic %p, grf %p, sgrf %p, pmucru %p\n", priv->cru,
+	      priv->cic, priv->pmugrf, priv->pmusgrf, priv->pmucru);
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->ddr_clk);
+#else
+	ret = clk_get_by_index(dev, 0, &priv->ddr_clk);
+#endif
+	if (ret) {
+		printf("%s clk get failed %d\n", __func__, ret);
+		return ret;
+	}
+	ret = clk_set_rate(&priv->ddr_clk, params->base.ddr_freq * MHz);
+	if (ret < 0) {
+		printf("%s clk set failed %d\n", __func__, ret);
+		return ret;
+	}
+	ret = sdram_init(priv, params);
+	if (ret < 0) {
+		printf("%s DRAM init failed%d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+#endif
+
+size_t sdram_size_mb(struct dram_info *dram)
+{
+	u32 rank, col, bk, cs0_row, cs1_row, bw, row_3_4;
+	size_t chipsize_mb = 0;
+	size_t size_mb = 0;
+	u32 ch;
+
+	u32 sys_reg = readl(&dram->pmugrf->os_reg2);
+	u32 ch_num = 1 + ((sys_reg >> SYS_REG_NUM_CH_SHIFT)
+		       & SYS_REG_NUM_CH_MASK);
+
+	for (ch = 0; ch < ch_num; ch++) {
+		rank = 1 + (sys_reg >> SYS_REG_RANK_SHIFT(ch) &
+			SYS_REG_RANK_MASK);
+		col = 9 + (sys_reg >> SYS_REG_COL_SHIFT(ch) & SYS_REG_COL_MASK);
+		bk = 3 - ((sys_reg >> SYS_REG_BK_SHIFT(ch)) & SYS_REG_BK_MASK);
+		cs0_row = 13 + (sys_reg >> SYS_REG_CS0_ROW_SHIFT(ch) &
+				SYS_REG_CS0_ROW_MASK);
+		cs1_row = 13 + (sys_reg >> SYS_REG_CS1_ROW_SHIFT(ch) &
+				SYS_REG_CS1_ROW_MASK);
+		bw = (2 >> ((sys_reg >> SYS_REG_BW_SHIFT(ch)) &
+			SYS_REG_BW_MASK));
+		row_3_4 = sys_reg >> SYS_REG_ROW_3_4_SHIFT(ch) &
+			SYS_REG_ROW_3_4_MASK;
+
+		chipsize_mb = (1 << (cs0_row + col + bk + bw - 20));
+
+		if (rank > 1)
+			chipsize_mb += chipsize_mb >> (cs0_row - cs1_row);
+		if (row_3_4)
+			chipsize_mb = chipsize_mb * 3 / 4;
+		size_mb += chipsize_mb;
+	}
+
+	/*
+	 * we use the 0x00000000~0xf7ffffff space
+	 * since 0xf8000000~0xffffffff is soc register space
+	 * so we reserve it
+	 */
+	size_mb = min_t(size_t, size_mb, 0xf8000000/(1<<20));
+
+	return size_mb;
+}
+
+static int rk3399_dmc_probe(struct udevice *dev)
+{
+#ifdef CONFIG_SPL_BUILD
+	if (rk3399_dmc_init(dev))
+		return 0;
+#else
+	struct dram_info *priv = dev_get_priv(dev);
+
+	priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF);
+	debug("%s: pmugrf=%p\n", __func__, priv->pmugrf);
+#endif
+	return 0;
+}
+
+static int rk3399_dmc_get_info(struct udevice *dev, struct ram_info *info)
+{
+	struct dram_info *priv = dev_get_priv(dev);
+
+	info = &priv->info;
+	priv->info.base = 0;
+	priv->info.size = sdram_size_mb(priv) << 20;
+
+	return 0;
+}
+
+static struct ram_ops rk3399_dmc_ops = {
+	.get_info = rk3399_dmc_get_info,
+};
+
+
+static const struct udevice_id rk3399_dmc_ids[] = {
+	{ .compatible = "rockchip,rk3399-dmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(dmc_rk3399) = {
+	.name = "rockchip_rk3399_dmc",
+	.id = UCLASS_RAM,
+	.of_match = rk3399_dmc_ids,
+	.ops = &rk3399_dmc_ops,
+#ifdef CONFIG_SPL_BUILD
+	.ofdata_to_platdata = rk3399_dmc_ofdata_to_platdata,
+#endif
+	.probe = rk3399_dmc_probe,
+#ifdef CONFIG_SPL_BUILD
+	.priv_auto_alloc_size = sizeof(struct dram_info),
+	.platdata_auto_alloc_size = sizeof(struct rockchip_dmc_plat),
+#endif
+};
diff --git a/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c b/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c
index 2cef68b..d32985b 100644
--- a/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c
+++ b/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c
@@ -12,6 +12,8 @@
 static const struct udevice_id rk3399_syscon_ids[] = {
 	{ .compatible = "rockchip,rk3399-grf", .data = ROCKCHIP_SYSCON_GRF },
 	{ .compatible = "rockchip,rk3399-pmugrf", .data = ROCKCHIP_SYSCON_PMUGRF },
+	{ .compatible = "rockchip,rk3399-pmusgrf", .data = ROCKCHIP_SYSCON_PMUSGRF },
+	{ .compatible = "rockchip,rk3399-cic", .data = ROCKCHIP_SYSCON_CIC },
 };
 
 U_BOOT_DRIVER(syscon_rk3399) = {
@@ -19,3 +21,41 @@
 	.id = UCLASS_SYSCON,
 	.of_match = rk3399_syscon_ids,
 };
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+static int rk3399_syscon_bind_of_platdata(struct udevice *dev)
+{
+	dev->driver_data = dev->driver->of_match->data;
+	debug("syscon: %s %d\n", dev->name, (uint)dev->driver_data);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(rockchip_rk3399_grf) = {
+	.name = "rockchip_rk3399_grf",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3399_syscon_ids,
+	.bind = rk3399_syscon_bind_of_platdata,
+};
+
+U_BOOT_DRIVER(rockchip_rk3399_pmugrf) = {
+	.name = "rockchip_rk3399_pmugrf",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3399_syscon_ids + 1,
+	.bind = rk3399_syscon_bind_of_platdata,
+};
+
+U_BOOT_DRIVER(rockchip_rk3399_pmusgrf) = {
+	.name = "rockchip_rk3399_pmusgrf",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3399_syscon_ids + 2,
+	.bind = rk3399_syscon_bind_of_platdata,
+};
+
+U_BOOT_DRIVER(rockchip_rk3399_cic) = {
+	.name = "rockchip_rk3399_cic",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3399_syscon_ids + 3,
+	.bind = rk3399_syscon_bind_of_platdata,
+};
+#endif
diff --git a/board/rockchip/evb_rk3328/Kconfig b/board/rockchip/evb_rk3328/Kconfig
new file mode 100644
index 0000000..ef446b4
--- /dev/null
+++ b/board/rockchip/evb_rk3328/Kconfig
@@ -0,0 +1,15 @@
+if TARGET_EVB_RK3328
+
+config SYS_BOARD
+	default "evb_rk3328"
+
+config SYS_VENDOR
+	default "rockchip"
+
+config SYS_CONFIG_NAME
+	default "evb_rk3328"
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+	def_bool y
+
+endif
diff --git a/board/rockchip/evb_rk3328/MAINTAINERS b/board/rockchip/evb_rk3328/MAINTAINERS
new file mode 100644
index 0000000..9db604f
--- /dev/null
+++ b/board/rockchip/evb_rk3328/MAINTAINERS
@@ -0,0 +1,6 @@
+EVB-RK3328
+M:      William Zhang <william.zhang@rock-chips.com>
+S:      Maintained
+F:      board/rockchip/evb_rk3328
+F:      include/configs/evb_rk3328.h
+F:      configs/evb-rk3328_defconfig
diff --git a/board/rockchip/evb_rk3328/Makefile b/board/rockchip/evb_rk3328/Makefile
new file mode 100644
index 0000000..81c5de8
--- /dev/null
+++ b/board/rockchip/evb_rk3328/Makefile
@@ -0,0 +1,7 @@
+#
+# (C) Copyright 2016 Rockchip Electronics Co., Ltd
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y	+= evb-rk3328.o
diff --git a/board/rockchip/evb_rk3328/README b/board/rockchip/evb_rk3328/README
new file mode 100644
index 0000000..6cbb66a
--- /dev/null
+++ b/board/rockchip/evb_rk3328/README
@@ -0,0 +1,70 @@
+Introduction
+============
+
+RK3328 key features we might use in U-Boot:
+* CPU: ARMv8 64bit quad-core Cortex-A53
+* IRAM: 36KB
+* DRAM: 4GB-16MB dual-channel
+* eMMC: support eMMC 5.0/5.1, suport HS400, HS200, DDR50
+* SD/MMC: support SD 3.0, MMC 4.51
+* USB: USB2.0 EHCI host port *2
+* Display: RGB/HDMI/DP/MIPI/EDP
+
+evb key features:
+* regulator: pwm regulator for CPU B/L
+* PMIC: rk808
+* debug console: UART2
+
+In order to support Arm Trust Firmware(ATF), we need to use the
+miniloader from rockchip which:
+* do DRAM init
+* load and verify ATF image
+* load and verify U-Boot image
+
+Here is the step-by-step to boot to U-Boot on rk3328.
+
+Get the Source and prebuild binary
+==================================
+
+  > mkdir ~/evb_rk3328
+  > cd ~/evb_rk3328
+  > git clone https://github.com/ARM-software/arm-trusted-firmware.git
+  > git clone https://github.com/rockchip-linux/rkbin
+  > git clone https://github.com/rockchip-linux/rkflashtool
+
+Compile ATF
+===============
+
+  > cd arm-trusted-firmware
+  > make realclean
+  > make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3328 bl31
+
+Compile U-Boot
+==================
+
+  > cd ../u-boot
+  > make CROSS_COMPILE=aarch64-linux-gnu- evb-rk3328_defconfig all
+
+Compile rkflashtool
+=======================
+
+  > cd ../rkflashtool
+  > make
+
+Package image for miniloader
+================================
+  > cd ..
+  > cp arm-trusted-firmware/build/rk3328/release/bl31.bin rkbin/rk33
+  > ./rkbin/tools/trust_merger rkbin/tools/RK3328TRUST.ini
+  > ./rkbin/tools/loaderimage --pack --uboot u-boot/u-boot-dtb.bin uboot.img
+  > mkdir image
+  > mv trust.img ./image/
+  > mv uboot.img ./image/rk3328evb-uboot.bin
+
+Flash image
+===============
+Power on(or reset with RESET KEY) with MASKROM KEY preesed, and then:
+
+  > ./rkflashtool/rkflashloader rk3328evb
+
+You should be able to get U-Boot log message in console/UART2 now.
diff --git a/board/rockchip/evb_rk3328/evb-rk3328.c b/board/rockchip/evb_rk3328/evb-rk3328.c
new file mode 100644
index 0000000..7e70f38
--- /dev/null
+++ b/board/rockchip/evb_rk3328/evb-rk3328.c
@@ -0,0 +1,40 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/armv8/mmu.h>
+#include <dwc3-uboot.h>
+#include <usb.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int board_init(void)
+{
+	return 0;
+}
+
+int dram_init(void)
+{
+	gd->ram_size = 0x80000000;
+	return 0;
+}
+
+void dram_init_banksize(void)
+{
+	/* Reserve 0x200000 for ATF bl31 */
+	gd->bd->bi_dram[0].start = 0x200000;
+	gd->bd->bi_dram[0].size = 0x7e000000;
+}
+
+int usb_gadget_handle_interrupts(void)
+{
+	return 0;
+}
+
+int board_usb_init(int index, enum usb_init_type init)
+{
+	return 0;
+}
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 766fb3d..2bc8b42 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -170,22 +170,20 @@
 	image_entry();
 }
 
-int spl_init(void)
+static int spl_common_init(bool setup_malloc)
 {
 	int ret;
 
+	debug("spl_early_init()\n");
+
-	debug("spl_init()\n");
-/*
- * with CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN we set malloc_base and
- * malloc_limit in spl_relocate_stack_gd
- */
-#if defined(CONFIG_SYS_MALLOC_F_LEN) && \
-	!defined(CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN)
+#if defined(CONFIG_SYS_MALLOC_F_LEN)
+	if (setup_malloc) {
 #ifdef CONFIG_MALLOC_F_ADDR
-	gd->malloc_base = CONFIG_MALLOC_F_ADDR;
+		gd->malloc_base = CONFIG_MALLOC_F_ADDR;
 #endif
-	gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
-	gd->malloc_ptr = 0;
+		gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
+		gd->malloc_ptr = 0;
+	}
 #endif
 	if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
 		ret = fdtdec_setup();
@@ -202,6 +200,32 @@
 			return ret;
 		}
 	}
+
+	return 0;
+}
+
+int spl_early_init(void)
+{
+	int ret;
+
+	ret = spl_common_init(true);
+	if (ret)
+		return ret;
+	gd->flags |= GD_FLG_SPL_EARLY_INIT;
+
+	return 0;
+}
+
+int spl_init(void)
+{
+	int ret;
+
+	if (!(gd->flags & GD_FLG_SPL_EARLY_INIT)) {
+		ret = spl_common_init(
+			!IS_ENABLED(CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN));
+		if (ret)
+			return ret;
+	}
 	gd->flags |= GD_FLG_SPL_INIT;
 
 	return 0;
diff --git a/configs/evb-rk3288_defconfig b/configs/evb-rk3288_defconfig
index a0cc7a7..aad2533 100644
--- a/configs/evb-rk3288_defconfig
+++ b/configs/evb-rk3288_defconfig
@@ -41,8 +41,11 @@
 CONFIG_LED_GPIO=y
 CONFIG_MMC_DW=y
 CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_DM_ETH=y
+CONFIG_NETDEVICES=y
+CONFIG_ETH_DESIGNWARE=y
+CONFIG_GMAC_ROCKCHIP=y
 CONFIG_PINCTRL=y
-# CONFIG_PINCTRL_FULL is not set
 CONFIG_SPL_PINCTRL=y
 # CONFIG_SPL_PINCTRL_FULL is not set
 CONFIG_ROCKCHIP_RK3288_PINCTRL=y
diff --git a/configs/evb-rk3328_defconfig b/configs/evb-rk3328_defconfig
new file mode 100644
index 0000000..df22105
--- /dev/null
+++ b/configs/evb-rk3328_defconfig
@@ -0,0 +1,36 @@
+CONFIG_ARM=y
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_ROCKCHIP_RK3328=y
+CONFIG_DEFAULT_DEVICE_TREE="rk3328-evb"
+CONFIG_FIT=y
+# CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_CMD_BOOTZ=y
+# CONFIG_CMD_IMLS is not set
+CONFIG_CMD_GPT=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_SF=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_TIME=y
+CONFIG_CMD_EXT2=y
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
+CONFIG_CLK=y
+CONFIG_ROCKCHIP_GPIO=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_PINCTRL=y
+CONFIG_ROCKCHIP_RK3328_PINCTRL=y
+CONFIG_REGULATOR_PWM=y
+CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_RAM=y
+CONFIG_DEBUG_UART=y
+CONFIG_DEBUG_UART_BASE=0xFF130000
+CONFIG_DEBUG_UART_CLOCK=24000000
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_SYS_NS16550=y
+CONFIG_SYSRESET=y
+CONFIG_USE_TINY_PRINTF=y
+CONFIG_ERRNO_STR=y
diff --git a/configs/evb-rk3399_defconfig b/configs/evb-rk3399_defconfig
index ad51ed2..7ef7cb0 100644
--- a/configs/evb-rk3399_defconfig
+++ b/configs/evb-rk3399_defconfig
@@ -3,7 +3,16 @@
 CONFIG_ROCKCHIP_RK3399=y
 CONFIG_DEFAULT_DEVICE_TREE="rk3399-evb"
 CONFIG_FIT=y
+CONFIG_SPL_LOAD_FIT=y
+CONFIG_SPL_OF_LIBFDT=y
+CONFIG_SPL_ATF_SUPPORT=y
+CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x200
+CONFIG_SPL_ATF_TEXT_BASE=0x00010000
 # CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_SPL_STACK_R=y
+CONFIG_SPL_STACK_R_ADDR=0x80000
+CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x4000
+CONFIG_SYS_MALLOC_F_LEN=0x4000
 CONFIG_CMD_BOOTZ=y
 # CONFIG_CMD_IMLS is not set
 CONFIG_CMD_GPT=y
@@ -12,20 +21,28 @@
 CONFIG_CMD_USB=y
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_CMD_TIME=y
+CONFIG_CMD_PXE=y
+CONFIG_SPL_OF_CONTROL=y
+CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
+CONFIG_SPL_OF_PLATDATA=y
 CONFIG_REGMAP=y
+CONFIG_SPL_REGMAP=y
 CONFIG_SYSCON=y
+CONFIG_SPL_SYSCON=y
 CONFIG_CLK=y
+CONFIG_SPL_CLK=y
 CONFIG_ROCKCHIP_GPIO=y
 CONFIG_MMC_DW=y
-CONFIG_MMC_DW_ROCKCHIP=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_ROCKCHIP=y
 CONFIG_PINCTRL=y
+CONFIG_SPL_PINCTRL=y
 CONFIG_ROCKCHIP_RK3399_PINCTRL=y
 CONFIG_REGULATOR_PWM=y
 CONFIG_DM_REGULATOR_FIXED=y
 CONFIG_PWM_ROCKCHIP=y
 CONFIG_RAM=y
+CONFIG_SPL_RAM=y
 CONFIG_DEBUG_UART=y
 CONFIG_DEBUG_UART_BASE=0xFF1A0000
 CONFIG_DEBUG_UART_CLOCK=24000000
diff --git a/configs/fennec-rk3288_defconfig b/configs/fennec-rk3288_defconfig
index a42cd9c..d1b0ffc 100644
--- a/configs/fennec-rk3288_defconfig
+++ b/configs/fennec-rk3288_defconfig
@@ -17,6 +17,7 @@
 CONFIG_CMD_SF=y
 CONFIG_CMD_SPI=y
 CONFIG_CMD_I2C=y
+CONFIG_CMD_USB=y
 CONFIG_CMD_GPIO=y
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_CMD_CACHE=y
@@ -40,8 +41,11 @@
 CONFIG_SYS_I2C_ROCKCHIP=y
 CONFIG_MMC_DW=y
 CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_DM_ETH=y
+CONFIG_NETDEVICES=y
+CONFIG_ETH_DESIGNWARE=y
+CONFIG_GMAC_ROCKCHIP=y
 CONFIG_PINCTRL=y
-# CONFIG_PINCTRL_FULL is not set
 CONFIG_SPL_PINCTRL=y
 # CONFIG_SPL_PINCTRL_FULL is not set
 CONFIG_ROCKCHIP_RK3288_PINCTRL=y
@@ -58,6 +62,8 @@
 CONFIG_DEBUG_UART_SHIFT=2
 CONFIG_SYS_NS16550=y
 CONFIG_SYSRESET=y
+CONFIG_USB=y
+CONFIG_USB_STORAGE=y
 CONFIG_USE_TINY_PRINTF=y
 CONFIG_CMD_DHRYSTONE=y
 CONFIG_ERRNO_STR=y
diff --git a/configs/firefly-rk3288_defconfig b/configs/firefly-rk3288_defconfig
index 00bfca0..b0741d7 100644
--- a/configs/firefly-rk3288_defconfig
+++ b/configs/firefly-rk3288_defconfig
@@ -16,6 +16,7 @@
 CONFIG_CMD_SF=y
 CONFIG_CMD_SPI=y
 CONFIG_CMD_I2C=y
+CONFIG_CMD_USB=y
 CONFIG_CMD_GPIO=y
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_CMD_CACHE=y
@@ -28,7 +29,6 @@
 CONFIG_SPL_PARTITION_UUIDS=y
 CONFIG_SPL_OF_CONTROL=y
 CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
-CONFIG_SPL_OF_PLATDATA=y
 CONFIG_NET_RANDOM_ETHADDR=y
 CONFIG_REGMAP=y
 CONFIG_SPL_REGMAP=y
@@ -65,6 +65,8 @@
 CONFIG_SYS_NS16550=y
 CONFIG_ROCKCHIP_SERIAL=y
 CONFIG_SYSRESET=y
+CONFIG_USB=y
+CONFIG_USB_STORAGE=y
 CONFIG_DM_VIDEO=y
 CONFIG_DISPLAY=y
 CONFIG_VIDEO_ROCKCHIP=y
diff --git a/configs/popmetal-rk3288_defconfig b/configs/popmetal-rk3288_defconfig
index 7b5ed94..dfc84b9 100644
--- a/configs/popmetal-rk3288_defconfig
+++ b/configs/popmetal-rk3288_defconfig
@@ -41,6 +41,10 @@
 CONFIG_SYS_I2C_ROCKCHIP=y
 CONFIG_MMC_DW=y
 CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_DM_ETH=y
+CONFIG_NETDEVICES=y
+CONFIG_ETH_DESIGNWARE=y
+CONFIG_GMAC_ROCKCHIP=y
 CONFIG_PINCTRL=y
 CONFIG_SPL_PINCTRL=y
 # CONFIG_SPL_PINCTRL_FULL is not set
diff --git a/configs/tinker-rk3288_defconfig b/configs/tinker-rk3288_defconfig
index 7573a2e..cec3938 100644
--- a/configs/tinker-rk3288_defconfig
+++ b/configs/tinker-rk3288_defconfig
@@ -17,6 +17,7 @@
 CONFIG_CMD_SF=y
 CONFIG_CMD_SPI=y
 CONFIG_CMD_I2C=y
+CONFIG_CMD_USB=y
 CONFIG_CMD_GPIO=y
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_CMD_CACHE=y
@@ -40,6 +41,10 @@
 CONFIG_SYS_I2C_ROCKCHIP=y
 CONFIG_MMC_DW=y
 CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_DM_ETH=y
+CONFIG_NETDEVICES=y
+CONFIG_ETH_DESIGNWARE=y
+CONFIG_GMAC_ROCKCHIP=y
 CONFIG_PINCTRL=y
 CONFIG_SPL_PINCTRL=y
 # CONFIG_SPL_PINCTRL_FULL is not set
@@ -57,6 +62,8 @@
 CONFIG_DEBUG_UART_SHIFT=2
 CONFIG_SYS_NS16550=y
 CONFIG_SYSRESET=y
+CONFIG_USB=y
+CONFIG_USB_STORAGE=y
 CONFIG_USE_TINY_PRINTF=y
 CONFIG_CMD_DHRYSTONE=y
 CONFIG_ERRNO_STR=y
diff --git a/doc/device-tree-bindings/clock/rockchip,rk3399-dmc.txt b/doc/device-tree-bindings/clock/rockchip,rk3399-dmc.txt
new file mode 100644
index 0000000..a15dc5d
--- /dev/null
+++ b/doc/device-tree-bindings/clock/rockchip,rk3399-dmc.txt
@@ -0,0 +1,42 @@
+Rockchip Dynamic Memory Controller Driver
+Required properties:
+- compatible: "rockchip,rk3399-dmc", "syscon"
+- rockchip,cru: this driver should access cru regs, so need get cru here
+- rockchip,pmucru: this driver should access pmucru regs, so need get pmucru here
+- rockchip,pmugrf: this driver should access pmugrf regs, so need get pmugrf here
+- rockchip,pmusgrf: this driver should access pmusgrf regs, so need get pmusgrf here
+- rockchip,cic: this driver should access cic regs, so need get cic here
+- reg: dynamic ram protocol controller(PCTL) address, PHY Independent(PI) address, phy controller(PHYCTL) address and memory schedule(MSCH) address
+- clock: must include clock specifiers corresponding to entries in the clock-names property.
+    Must contain
+      dmc_clk: for ddr working frequency
+- rockchip,sdram-params: SDRAM parameters, including all the information by ddr driver:
+    Must contain
+      Genarate by vendor tool and adjust for U-Boot dtsi.
+
+Example:
+	dmc: dmc {
+		u-boot,dm-pre-reloc;
+		compatible = "rockchip,rk3399-dmc";
+		devfreq-events = <&dfi>;
+		interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH 0>;
+		clocks = <&cru SCLK_DDRCLK>;
+		clock-names = "dmc_clk";
+		reg = <0x0 0xffa80000 0x0 0x0800
+		       0x0 0xffa80800 0x0 0x1800
+		       0x0 0xffa82000 0x0 0x2000
+		       0x0 0xffa84000 0x0 0x1000
+		       0x0 0xffa88000 0x0 0x0800
+		       0x0 0xffa88800 0x0 0x1800
+		       0x0 0xffa8a000 0x0 0x2000
+		       0x0 0xffa8c000 0x0 0x1000>;
+	};
+
+	&dmc {
+		rockchip,sdram-params = <
+		0x2
+		0xa
+		0x3
+		...
+		>;
+	};
diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt
index fea324e..0853477 100644
--- a/doc/driver-model/README.txt
+++ b/doc/driver-model/README.txt
@@ -825,6 +825,10 @@
 'u-boot,dm-pre-reloc' flag are initialised prior to relocation. This helps
 to reduce the driver model overhead.
 
+It is possible to limit this to specific relocation steps, by using
+the more specialized 'u-boot,dm-spl' and 'u-boot,dm-tpl' flags
+in the devicetree.
+
 Then post relocation we throw that away and re-init driver model again.
 For drivers which require some sort of continuity between pre- and
 post-relocation devices, we can provide access to the pre-relocation
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index c73156a..fcd693a 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -10,6 +10,7 @@
 #include <dm/device.h>
 #include <dm/lists.h>
 #include <dm/root.h>
+#include <dm/util.h>
 #include "pmc.h"
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -56,7 +57,7 @@
 	     offset > 0;
 	     offset = fdt_next_subnode(fdt, offset)) {
 		if (pre_reloc_only &&
-		    !fdt_getprop(fdt, offset, "u-boot,dm-pre-reloc", NULL))
+		    !dm_fdt_pre_reloc(fdt, offset))
 			continue;
 		/*
 		 * If this node has "compatible" property, this is not
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 1f8e417..1091a76 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -5,5 +5,7 @@
 #
 
 obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
+obj-$(CONFIG_ROCKCHIP_RK3188) += clk_rk3188.o
 obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
+obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
 obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
diff --git a/drivers/clk/rockchip/clk_rk3188.c b/drivers/clk/rockchip/clk_rk3188.c
new file mode 100644
index 0000000..459649f
--- /dev/null
+++ b/drivers/clk/rockchip/clk_rk3188.c
@@ -0,0 +1,527 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * (C) Copyright 2016 Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <errno.h>
+#include <mapmem.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3188.h>
+#include <asm/arch/grf_rk3188.h>
+#include <asm/arch/hardware.h>
+#include <dt-bindings/clock/rk3188-cru.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+#include <linux/log2.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum rk3188_clk_type {
+	RK3188_CRU,
+	RK3188A_CRU,
+};
+
+struct rk3188_clk_plat {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct dtd_rockchip_rk3188_cru dtd;
+#endif
+};
+
+struct pll_div {
+	u32 nr;
+	u32 nf;
+	u32 no;
+};
+
+enum {
+	VCO_MAX_HZ	= 2200U * 1000000,
+	VCO_MIN_HZ	= 440 * 1000000,
+	OUTPUT_MAX_HZ	= 2200U * 1000000,
+	OUTPUT_MIN_HZ	= 30 * 1000000,
+	FREF_MAX_HZ	= 2200U * 1000000,
+	FREF_MIN_HZ	= 30 * 1000,
+};
+
+enum {
+	/* PLL CON0 */
+	PLL_OD_MASK		= 0x0f,
+
+	/* PLL CON1 */
+	PLL_NF_MASK		= 0x1fff,
+
+	/* PLL CON2 */
+	PLL_BWADJ_MASK		= 0x0fff,
+
+	/* PLL CON3 */
+	PLL_RESET_SHIFT		= 5,
+
+	/* GRF_SOC_STATUS0 */
+	SOCSTS_DPLL_LOCK	= 1 << 5,
+	SOCSTS_APLL_LOCK	= 1 << 6,
+	SOCSTS_CPLL_LOCK	= 1 << 7,
+	SOCSTS_GPLL_LOCK	= 1 << 8,
+};
+
+#define RATE_TO_DIV(input_rate, output_rate) \
+	((input_rate) / (output_rate) - 1);
+
+#define DIV_TO_RATE(input_rate, div)	((input_rate) / ((div) + 1))
+
+#define PLL_DIVISORS(hz, _nr, _no) {\
+	.nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\
+	_Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
+		       (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\
+		       "divisors on line " __stringify(__LINE__));
+
+/* Keep divisors as low as possible to reduce jitter and power usage */
+#ifdef CONFIG_SPL_BUILD
+static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
+static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
+#endif
+
+static int rkclk_set_pll(struct rk3188_cru *cru, enum rk_clk_id clk_id,
+			 const struct pll_div *div, bool has_bwadj)
+{
+	int pll_id = rk_pll_id(clk_id);
+	struct rk3188_pll *pll = &cru->pll[pll_id];
+	/* All PLLs have same VCO and output frequency range restrictions. */
+	uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
+	uint output_hz = vco_hz / div->no;
+
+	debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
+	      (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz);
+	assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
+	       output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ &&
+	       (div->no == 1 || !(div->no % 2)));
+
+	/* enter reset */
+	rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
+
+	rk_clrsetreg(&pll->con0,
+		     CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK,
+		     ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1));
+	rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1);
+
+	if (has_bwadj)
+		rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
+
+	udelay(10);
+
+	/* return from reset */
+	rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
+
+	return 0;
+}
+
+static int rkclk_configure_ddr(struct rk3188_cru *cru, struct rk3188_grf *grf,
+			       unsigned int hz, bool has_bwadj)
+{
+	static const struct pll_div dpll_cfg[] = {
+		{.nf = 25, .nr = 2, .no = 1},
+		{.nf = 400, .nr = 9, .no = 2},
+		{.nf = 500, .nr = 9, .no = 2},
+		{.nf = 100, .nr = 3, .no = 1},
+	};
+	int cfg;
+
+	switch (hz) {
+	case 300000000:
+		cfg = 0;
+		break;
+	case 533000000:	/* actually 533.3P MHz */
+		cfg = 1;
+		break;
+	case 666000000:	/* actually 666.6P MHz */
+		cfg = 2;
+		break;
+	case 800000000:
+		cfg = 3;
+		break;
+	default:
+		debug("Unsupported SDRAM frequency");
+		return -EINVAL;
+	}
+
+	/* pll enter slow-mode */
+	rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
+		     DPLL_MODE_SLOW << DPLL_MODE_SHIFT);
+
+	rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg], has_bwadj);
+
+	/* wait for pll lock */
+	while (!(readl(&grf->soc_status0) & SOCSTS_DPLL_LOCK))
+		udelay(1);
+
+	/* PLL enter normal-mode */
+	rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
+		     DPLL_MODE_NORMAL << DPLL_MODE_SHIFT);
+
+	return 0;
+}
+
+/* Get pll rate by id */
+static uint32_t rkclk_pll_get_rate(struct rk3188_cru *cru,
+				   enum rk_clk_id clk_id)
+{
+	uint32_t nr, no, nf;
+	uint32_t con;
+	int pll_id = rk_pll_id(clk_id);
+	struct rk3188_pll *pll = &cru->pll[pll_id];
+	static u8 clk_shift[CLK_COUNT] = {
+		0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
+		GPLL_MODE_SHIFT
+	};
+	uint shift;
+
+	con = readl(&cru->cru_mode_con);
+	shift = clk_shift[clk_id];
+	switch ((con >> shift) & APLL_MODE_MASK) {
+	case APLL_MODE_SLOW:
+		return OSC_HZ;
+	case APLL_MODE_NORMAL:
+		/* normal mode */
+		con = readl(&pll->con0);
+		no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1;
+		nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1;
+		con = readl(&pll->con1);
+		nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1;
+
+		return (24 * nf / (nr * no)) * 1000000;
+	case APLL_MODE_DEEP:
+	default:
+		return 32768;
+	}
+}
+
+static ulong rockchip_mmc_get_clk(struct rk3188_cru *cru, uint gclk_rate,
+				  int periph)
+{
+	uint div;
+	u32 con;
+
+	switch (periph) {
+	case HCLK_EMMC:
+		con = readl(&cru->cru_clksel_con[12]);
+		div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
+		break;
+	case HCLK_SDMMC:
+		con = readl(&cru->cru_clksel_con[11]);
+		div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK;
+		break;
+	case HCLK_SDIO:
+		con = readl(&cru->cru_clksel_con[12]);
+		div = (con >> SDIO_DIV_SHIFT) & SDIO_DIV_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return DIV_TO_RATE(gclk_rate, div);
+}
+
+static ulong rockchip_mmc_set_clk(struct rk3188_cru *cru, uint gclk_rate,
+				  int  periph, uint freq)
+{
+	int src_clk_div;
+
+	debug("%s: gclk_rate=%u\n", __func__, gclk_rate);
+	src_clk_div = RATE_TO_DIV(gclk_rate, freq);
+	assert(src_clk_div <= 0x3f);
+
+	switch (periph) {
+	case HCLK_EMMC:
+		rk_clrsetreg(&cru->cru_clksel_con[12],
+			     EMMC_DIV_MASK << EMMC_DIV_SHIFT,
+			     src_clk_div << EMMC_DIV_SHIFT);
+		break;
+	case HCLK_SDMMC:
+		rk_clrsetreg(&cru->cru_clksel_con[11],
+			     MMC0_DIV_MASK << MMC0_DIV_SHIFT,
+			     src_clk_div << MMC0_DIV_SHIFT);
+		break;
+	case HCLK_SDIO:
+		rk_clrsetreg(&cru->cru_clksel_con[12],
+			     SDIO_DIV_MASK << SDIO_DIV_SHIFT,
+			     src_clk_div << SDIO_DIV_SHIFT);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return rockchip_mmc_get_clk(cru, gclk_rate, periph);
+}
+
+static ulong rockchip_spi_get_clk(struct rk3188_cru *cru, uint gclk_rate,
+				  int periph)
+{
+	uint div;
+	u32 con;
+
+	switch (periph) {
+	case SCLK_SPI0:
+		con = readl(&cru->cru_clksel_con[25]);
+		div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK;
+		break;
+	case SCLK_SPI1:
+		con = readl(&cru->cru_clksel_con[25]);
+		div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return DIV_TO_RATE(gclk_rate, div);
+}
+
+static ulong rockchip_spi_set_clk(struct rk3188_cru *cru, uint gclk_rate,
+				  int periph, uint freq)
+{
+	int src_clk_div = RATE_TO_DIV(gclk_rate, freq);
+
+	switch (periph) {
+	case SCLK_SPI0:
+		assert(src_clk_div <= SPI0_DIV_MASK);
+		rk_clrsetreg(&cru->cru_clksel_con[25],
+			     SPI0_DIV_MASK << SPI0_DIV_SHIFT,
+			     src_clk_div << SPI0_DIV_SHIFT);
+		break;
+	case SCLK_SPI1:
+		assert(src_clk_div <= SPI1_DIV_MASK);
+		rk_clrsetreg(&cru->cru_clksel_con[25],
+			     SPI1_DIV_MASK << SPI1_DIV_SHIFT,
+			     src_clk_div << SPI1_DIV_SHIFT);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return rockchip_spi_get_clk(cru, gclk_rate, periph);
+}
+
+#ifdef CONFIG_SPL_BUILD
+static void rkclk_init(struct rk3188_cru *cru, struct rk3188_grf *grf,
+		       bool has_bwadj)
+{
+	u32 aclk_div, hclk_div, pclk_div, h2p_div;
+
+	/* pll enter slow-mode */
+	rk_clrsetreg(&cru->cru_mode_con,
+		     GPLL_MODE_MASK << GPLL_MODE_SHIFT |
+		     CPLL_MODE_MASK << CPLL_MODE_SHIFT,
+		     GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
+		     CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
+
+	/* init pll */
+	rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg, has_bwadj);
+	rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg, has_bwadj);
+
+	/* waiting for pll lock */
+	while ((readl(&grf->soc_status0) &
+			(SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) !=
+			(SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK))
+		udelay(1);
+
+	/*
+	 * cpu clock pll source selection and
+	 * reparent aclk_cpu_pre from apll to gpll
+	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
+	 */
+	aclk_div = RATE_TO_DIV(GPLL_HZ, CPU_ACLK_HZ);
+	assert((aclk_div + 1) * CPU_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
+
+	rk_clrsetreg(&cru->cru_clksel_con[0],
+		     CPU_ACLK_PLL_MASK << CPU_ACLK_PLL_SHIFT |
+		     A9_CPU_DIV_MASK << A9_CPU_DIV_SHIFT,
+		     CPU_ACLK_PLL_SELECT_GPLL << CPU_ACLK_PLL_SHIFT |
+		     aclk_div << A9_CPU_DIV_SHIFT);
+
+	hclk_div = ilog2(CPU_ACLK_HZ / CPU_HCLK_HZ);
+	assert((1 << hclk_div) * CPU_HCLK_HZ == CPU_ACLK_HZ && hclk_div < 0x3);
+	pclk_div = ilog2(CPU_ACLK_HZ / CPU_PCLK_HZ);
+	assert((1 << pclk_div) * CPU_PCLK_HZ == CPU_ACLK_HZ && pclk_div < 0x4);
+	h2p_div = ilog2(CPU_HCLK_HZ / CPU_H2P_HZ);
+	assert((1 << h2p_div) * CPU_H2P_HZ == CPU_HCLK_HZ && pclk_div < 0x3);
+
+	rk_clrsetreg(&cru->cru_clksel_con[1],
+		     AHB2APB_DIV_MASK << AHB2APB_DIV_SHIFT |
+		     CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT |
+		     CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT,
+		     h2p_div << AHB2APB_DIV_SHIFT |
+		     pclk_div << CPU_PCLK_DIV_SHIFT |
+		     hclk_div << CPU_HCLK_DIV_SHIFT);
+
+	/*
+	 * peri clock pll source selection and
+	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
+	 */
+	aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
+	assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
+
+	hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ);
+	assert((1 << hclk_div) * PERI_HCLK_HZ ==
+		PERI_ACLK_HZ && (hclk_div < 0x4));
+
+	pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ);
+	assert((1 << pclk_div) * PERI_PCLK_HZ ==
+		PERI_ACLK_HZ && (pclk_div < 0x4));
+
+	rk_clrsetreg(&cru->cru_clksel_con[10],
+		     PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT |
+		     PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT |
+		     PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT,
+		     PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT |
+		     pclk_div << PERI_PCLK_DIV_SHIFT |
+		     hclk_div << PERI_HCLK_DIV_SHIFT |
+		     aclk_div << PERI_ACLK_DIV_SHIFT);
+
+	/* PLL enter normal-mode */
+	rk_clrsetreg(&cru->cru_mode_con,
+		     GPLL_MODE_MASK << GPLL_MODE_SHIFT |
+		     CPLL_MODE_MASK << CPLL_MODE_SHIFT,
+		     GPLL_MODE_NORMAL << GPLL_MODE_SHIFT |
+		     CPLL_MODE_NORMAL << CPLL_MODE_SHIFT);
+
+	rockchip_mmc_set_clk(cru, PERI_HCLK_HZ, HCLK_SDMMC, 16000000);
+}
+#endif
+
+static ulong rk3188_clk_get_rate(struct clk *clk)
+{
+	struct rk3188_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong new_rate, gclk_rate;
+
+	gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
+	switch (clk->id) {
+	case 1 ... 4:
+		new_rate = rkclk_pll_get_rate(priv->cru, clk->id);
+		break;
+	case HCLK_EMMC:
+	case HCLK_SDMMC:
+	case HCLK_SDIO:
+		new_rate = rockchip_mmc_get_clk(priv->cru, PERI_HCLK_HZ,
+						clk->id);
+		break;
+	case SCLK_SPI0:
+	case SCLK_SPI1:
+		new_rate = rockchip_spi_get_clk(priv->cru, PERI_PCLK_HZ,
+						clk->id);
+		break;
+	case PCLK_I2C0:
+	case PCLK_I2C1:
+	case PCLK_I2C2:
+	case PCLK_I2C3:
+	case PCLK_I2C4:
+		return gclk_rate;
+	default:
+		return -ENOENT;
+	}
+
+	return new_rate;
+}
+
+static ulong rk3188_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct rk3188_clk_priv *priv = dev_get_priv(clk->dev);
+	struct rk3188_cru *cru = priv->cru;
+	ulong new_rate;
+
+	switch (clk->id) {
+	case CLK_DDR:
+		new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate,
+					       priv->has_bwadj);
+		break;
+	case HCLK_EMMC:
+	case HCLK_SDMMC:
+	case HCLK_SDIO:
+		new_rate = rockchip_mmc_set_clk(cru, PERI_HCLK_HZ,
+						clk->id, rate);
+		break;
+	case SCLK_SPI0:
+	case SCLK_SPI1:
+		new_rate = rockchip_spi_set_clk(cru, PERI_PCLK_HZ,
+						clk->id, rate);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return new_rate;
+}
+
+static struct clk_ops rk3188_clk_ops = {
+	.get_rate	= rk3188_clk_get_rate,
+	.set_rate	= rk3188_clk_set_rate,
+};
+
+static int rk3188_clk_ofdata_to_platdata(struct udevice *dev)
+{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rk3188_clk_priv *priv = dev_get_priv(dev);
+
+	priv->cru = (struct rk3188_cru *)dev_get_addr(dev);
+#endif
+
+	return 0;
+}
+
+static int rk3188_clk_probe(struct udevice *dev)
+{
+	struct rk3188_clk_priv *priv = dev_get_priv(dev);
+	enum rk3188_clk_type type = dev_get_driver_data(dev);
+
+	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+	if (IS_ERR(priv->grf))
+		return PTR_ERR(priv->grf);
+	priv->has_bwadj = (type == RK3188A_CRU) ? 1 : 0;
+
+#ifdef CONFIG_SPL_BUILD
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rk3188_clk_plat *plat = dev_get_platdata(dev);
+
+	priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
+#endif
+
+	rkclk_init(priv->cru, priv->grf, priv->has_bwadj);
+#endif
+
+	return 0;
+}
+
+static int rk3188_clk_bind(struct udevice *dev)
+{
+	int ret;
+
+	/* The reset driver does not have a device node, so bind it here */
+	ret = device_bind_driver(gd->dm_root, "rk3188_sysreset", "reset", &dev);
+	if (ret)
+		debug("Warning: No rk3188 reset driver: ret=%d\n", ret);
+
+	return 0;
+}
+
+static const struct udevice_id rk3188_clk_ids[] = {
+	{ .compatible = "rockchip,rk3188-cru", .data = RK3188_CRU },
+	{ .compatible = "rockchip,rk3188a-cru", .data = RK3188A_CRU },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_rk3188_cru) = {
+	.name			= "rockchip_rk3188_cru",
+	.id			= UCLASS_CLK,
+	.of_match		= rk3188_clk_ids,
+	.priv_auto_alloc_size	= sizeof(struct rk3188_clk_priv),
+	.platdata_auto_alloc_size = sizeof(struct rk3188_clk_plat),
+	.ops			= &rk3188_clk_ops,
+	.bind			= rk3188_clk_bind,
+	.ofdata_to_platdata	= rk3188_clk_ofdata_to_platdata,
+	.probe			= rk3188_clk_probe,
+};
diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c
index d15504c..7835676 100644
--- a/drivers/clk/rockchip/clk_rk3288.c
+++ b/drivers/clk/rockchip/clk_rk3288.c
@@ -131,8 +131,10 @@
 
 /* Keep divisors as low as possible to reduce jitter and power usage */
 static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 1);
+#ifdef CONFIG_SPL_BUILD
 static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
 static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
+#endif
 
 static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id,
 			 const struct pll_div *div)
diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c
new file mode 100644
index 0000000..0ff1e30
--- /dev/null
+++ b/drivers/clk/rockchip/clk_rk3328.c
@@ -0,0 +1,581 @@
+/*
+ * (C) Copyright 2017 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3328.h>
+#include <asm/arch/hardware.h>
+#include <asm/io.h>
+#include <dm/lists.h>
+#include <dt-bindings/clock/rk3328-cru.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pll_div {
+	u32 refdiv;
+	u32 fbdiv;
+	u32 postdiv1;
+	u32 postdiv2;
+	u32 frac;
+};
+
+#define RATE_TO_DIV(input_rate, output_rate) \
+	((input_rate) / (output_rate) - 1);
+#define DIV_TO_RATE(input_rate, div)    ((input_rate) / ((div) + 1))
+
+#define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
+	.refdiv = _refdiv,\
+	.fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
+	.postdiv1 = _postdiv1, .postdiv2 = _postdiv2};
+
+static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 4, 1);
+static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 2, 2, 1);
+
+static const struct pll_div apll_816_cfg = PLL_DIVISORS(816 * MHz, 1, 2, 1);
+static const struct pll_div apll_600_cfg = PLL_DIVISORS(600 * MHz, 1, 3, 1);
+
+static const struct pll_div *apll_cfgs[] = {
+	[APLL_816_MHZ] = &apll_816_cfg,
+	[APLL_600_MHZ] = &apll_600_cfg,
+};
+
+enum {
+	/* PLL_CON0 */
+	PLL_POSTDIV1_SHIFT		= 12,
+	PLL_POSTDIV1_MASK		= 0x7 << PLL_POSTDIV1_SHIFT,
+	PLL_FBDIV_SHIFT			= 0,
+	PLL_FBDIV_MASK			= 0xfff,
+
+	/* PLL_CON1 */
+	PLL_DSMPD_SHIFT			= 12,
+	PLL_DSMPD_MASK			= 1 << PLL_DSMPD_SHIFT,
+	PLL_INTEGER_MODE		= 1,
+	PLL_LOCK_STATUS_SHIFT		= 10,
+	PLL_LOCK_STATUS_MASK		= 1 << PLL_LOCK_STATUS_SHIFT,
+	PLL_POSTDIV2_SHIFT		= 6,
+	PLL_POSTDIV2_MASK		= 0x7 << PLL_POSTDIV2_SHIFT,
+	PLL_REFDIV_SHIFT		= 0,
+	PLL_REFDIV_MASK			= 0x3f,
+
+	/* PLL_CON2 */
+	PLL_FRACDIV_SHIFT		= 0,
+	PLL_FRACDIV_MASK		= 0xffffff,
+
+	/* MODE_CON */
+	APLL_MODE_SHIFT			= 0,
+	NPLL_MODE_SHIFT			= 1,
+	DPLL_MODE_SHIFT			= 4,
+	CPLL_MODE_SHIFT			= 8,
+	GPLL_MODE_SHIFT			= 12,
+	PLL_MODE_SLOW			= 0,
+	PLL_MODE_NORM,
+
+	/* CLKSEL_CON0 */
+	CLK_CORE_PLL_SEL_APLL		= 0,
+	CLK_CORE_PLL_SEL_GPLL,
+	CLK_CORE_PLL_SEL_DPLL,
+	CLK_CORE_PLL_SEL_NPLL,
+	CLK_CORE_PLL_SEL_SHIFT		= 6,
+	CLK_CORE_PLL_SEL_MASK		= 3 << CLK_CORE_PLL_SEL_SHIFT,
+	CLK_CORE_DIV_SHIFT		= 0,
+	CLK_CORE_DIV_MASK		= 0x1f,
+
+	/* CLKSEL_CON1 */
+	ACLKM_CORE_DIV_SHIFT		= 4,
+	ACLKM_CORE_DIV_MASK		= 0x7 << ACLKM_CORE_DIV_SHIFT,
+	PCLK_DBG_DIV_SHIFT		= 0,
+	PCLK_DBG_DIV_MASK		= 0xF << PCLK_DBG_DIV_SHIFT,
+
+	/* CLKSEL_CON28 */
+	ACLK_PERIHP_PLL_SEL_CPLL	= 0,
+	ACLK_PERIHP_PLL_SEL_GPLL,
+	ACLK_PERIHP_PLL_SEL_HDMIPHY,
+	ACLK_PERIHP_PLL_SEL_SHIFT	= 6,
+	ACLK_PERIHP_PLL_SEL_MASK	= 3 << ACLK_PERIHP_PLL_SEL_SHIFT,
+	ACLK_PERIHP_DIV_CON_SHIFT	= 0,
+	ACLK_PERIHP_DIV_CON_MASK	= 0x1f,
+
+	/* CLKSEL_CON29 */
+	PCLK_PERIHP_DIV_CON_SHIFT	= 4,
+	PCLK_PERIHP_DIV_CON_MASK	= 0x7 << PCLK_PERIHP_DIV_CON_SHIFT,
+	HCLK_PERIHP_DIV_CON_SHIFT	= 0,
+	HCLK_PERIHP_DIV_CON_MASK	= 3 << HCLK_PERIHP_DIV_CON_SHIFT,
+
+	/* CLKSEL_CON22 */
+	CLK_TSADC_DIV_CON_SHIFT		= 0,
+	CLK_TSADC_DIV_CON_MASK		= 0x3ff,
+
+	/* CLKSEL_CON23 */
+	CLK_SARADC_DIV_CON_SHIFT	= 0,
+	CLK_SARADC_DIV_CON_MASK		= 0x3ff << CLK_SARADC_DIV_CON_SHIFT,
+
+	/* CLKSEL_CON24 */
+	CLK_PWM_PLL_SEL_CPLL		= 0,
+	CLK_PWM_PLL_SEL_GPLL,
+	CLK_PWM_PLL_SEL_SHIFT		= 15,
+	CLK_PWM_PLL_SEL_MASK		= 1 << CLK_PWM_PLL_SEL_SHIFT,
+	CLK_PWM_DIV_CON_SHIFT		= 8,
+	CLK_PWM_DIV_CON_MASK		= 0x7f << CLK_PWM_DIV_CON_SHIFT,
+
+	CLK_SPI_PLL_SEL_CPLL		= 0,
+	CLK_SPI_PLL_SEL_GPLL,
+	CLK_SPI_PLL_SEL_SHIFT		= 7,
+	CLK_SPI_PLL_SEL_MASK		= 1 << CLK_SPI_PLL_SEL_SHIFT,
+	CLK_SPI_DIV_CON_SHIFT		= 0,
+	CLK_SPI_DIV_CON_MASK		= 0x7f << CLK_SPI_DIV_CON_SHIFT,
+
+	/* CLKSEL_CON30 */
+	CLK_SDMMC_PLL_SEL_CPLL		= 0,
+	CLK_SDMMC_PLL_SEL_GPLL,
+	CLK_SDMMC_PLL_SEL_24M,
+	CLK_SDMMC_PLL_SEL_USBPHY,
+	CLK_SDMMC_PLL_SHIFT		= 8,
+	CLK_SDMMC_PLL_MASK		= 0x3 << CLK_SDMMC_PLL_SHIFT,
+	CLK_SDMMC_DIV_CON_SHIFT          = 0,
+	CLK_SDMMC_DIV_CON_MASK           = 0xff << CLK_SDMMC_DIV_CON_SHIFT,
+
+	/* CLKSEL_CON32 */
+	CLK_EMMC_PLL_SEL_CPLL		= 0,
+	CLK_EMMC_PLL_SEL_GPLL,
+	CLK_EMMC_PLL_SEL_24M,
+	CLK_EMMC_PLL_SEL_USBPHY,
+	CLK_EMMC_PLL_SHIFT		= 8,
+	CLK_EMMC_PLL_MASK		= 0x3 << CLK_EMMC_PLL_SHIFT,
+	CLK_EMMC_DIV_CON_SHIFT          = 0,
+	CLK_EMMC_DIV_CON_MASK           = 0xff << CLK_EMMC_DIV_CON_SHIFT,
+
+	/* CLKSEL_CON34 */
+	CLK_I2C_PLL_SEL_CPLL		= 0,
+	CLK_I2C_PLL_SEL_GPLL,
+	CLK_I2C_DIV_CON_MASK		= 0x7f,
+	CLK_I2C_PLL_SEL_MASK		= 1,
+	CLK_I2C1_PLL_SEL_SHIFT		= 15,
+	CLK_I2C1_DIV_CON_SHIFT		= 8,
+	CLK_I2C0_PLL_SEL_SHIFT		= 7,
+	CLK_I2C0_DIV_CON_SHIFT		= 0,
+
+	/* CLKSEL_CON35 */
+	CLK_I2C3_PLL_SEL_SHIFT		= 15,
+	CLK_I2C3_DIV_CON_SHIFT		= 8,
+	CLK_I2C2_PLL_SEL_SHIFT		= 7,
+	CLK_I2C2_DIV_CON_SHIFT		= 0,
+};
+
+#define VCO_MAX_KHZ	(3200 * (MHz / KHz))
+#define VCO_MIN_KHZ	(800 * (MHz / KHz))
+#define OUTPUT_MAX_KHZ	(3200 * (MHz / KHz))
+#define OUTPUT_MIN_KHZ	(16 * (MHz / KHz))
+
+/*
+ *  the div restructions of pll in integer mode, these are defined in
+ *  * CRU_*PLL_CON0 or PMUCRU_*PLL_CON0
+ */
+#define PLL_DIV_MIN	16
+#define PLL_DIV_MAX	3200
+
+/*
+ * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
+ * Formulas also embedded within the Fractional PLL Verilog model:
+ * If DSMPD = 1 (DSM is disabled, "integer mode")
+ * FOUTVCO = FREF / REFDIV * FBDIV
+ * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
+ * Where:
+ * FOUTVCO = Fractional PLL non-divided output frequency
+ * FOUTPOSTDIV = Fractional PLL divided output frequency
+ *               (output of second post divider)
+ * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
+ * REFDIV = Fractional PLL input reference clock divider
+ * FBDIV = Integer value programmed into feedback divide
+ *
+ */
+static void rkclk_set_pll(struct rk3328_cru *cru, enum rk_clk_id clk_id,
+			const struct pll_div *div)
+{
+	u32 *pll_con;
+	u32 mode_shift, mode_mask;
+
+	pll_con = NULL;
+	mode_shift = 0;
+	switch (clk_id) {
+	case CLK_ARM:
+		pll_con = cru->apll_con;
+		mode_shift = APLL_MODE_SHIFT;
+		break;
+	case CLK_DDR:
+		pll_con = cru->dpll_con;
+		mode_shift = DPLL_MODE_SHIFT;
+		break;
+	case CLK_CODEC:
+		pll_con = cru->cpll_con;
+		mode_shift = CPLL_MODE_SHIFT;
+		break;
+	case CLK_GENERAL:
+		pll_con = cru->gpll_con;
+		mode_shift = GPLL_MODE_SHIFT;
+		break;
+	case CLK_NEW:
+		pll_con = cru->npll_con;
+		mode_shift = NPLL_MODE_SHIFT;
+		break;
+	default:
+		break;
+	}
+	mode_mask = 1 << mode_shift;
+
+	/* All 8 PLLs have same VCO and output frequency range restrictions. */
+	u32 vco_khz = OSC_HZ / 1000 * div->fbdiv / div->refdiv;
+	u32 output_khz = vco_khz / div->postdiv1 / div->postdiv2;
+
+	debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, \
+	      postdiv2=%d, vco=%u khz, output=%u khz\n",
+	      pll_con, div->fbdiv, div->refdiv, div->postdiv1,
+	      div->postdiv2, vco_khz, output_khz);
+	assert(vco_khz >= VCO_MIN_KHZ && vco_khz <= VCO_MAX_KHZ &&
+	       output_khz >= OUTPUT_MIN_KHZ && output_khz <= OUTPUT_MAX_KHZ &&
+	       div->fbdiv >= PLL_DIV_MIN && div->fbdiv <= PLL_DIV_MAX);
+
+	/*
+	 * When power on or changing PLL setting,
+	 * we must force PLL into slow mode to ensure output stable clock.
+	 */
+	rk_clrsetreg(&cru->mode_con, mode_mask, PLL_MODE_SLOW << mode_shift);
+
+	/* use integer mode */
+	rk_clrsetreg(&pll_con[1], PLL_DSMPD_MASK,
+		     PLL_INTEGER_MODE << PLL_DSMPD_SHIFT);
+
+	rk_clrsetreg(&pll_con[0],
+		     PLL_FBDIV_MASK | PLL_POSTDIV1_MASK,
+		     (div->fbdiv << PLL_FBDIV_SHIFT) |
+		     (div->postdiv1 << PLL_POSTDIV1_SHIFT));
+	rk_clrsetreg(&pll_con[1],
+		     PLL_POSTDIV2_MASK | PLL_REFDIV_MASK,
+		     (div->postdiv2 << PLL_POSTDIV2_SHIFT) |
+		     (div->refdiv << PLL_REFDIV_SHIFT));
+
+	/* waiting for pll lock */
+	while (!(readl(&pll_con[1]) & (1 << PLL_LOCK_STATUS_SHIFT)))
+		udelay(1);
+
+	/* pll enter normal mode */
+	rk_clrsetreg(&cru->mode_con, mode_mask, PLL_MODE_NORM << mode_shift);
+}
+
+static void rkclk_init(struct rk3328_cru *cru)
+{
+	u32 aclk_div;
+	u32 hclk_div;
+	u32 pclk_div;
+
+	/* configure gpll cpll */
+	rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg);
+	rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg);
+
+	/* configure perihp aclk, hclk, pclk */
+	aclk_div = GPLL_HZ / PERIHP_ACLK_HZ - 1;
+	hclk_div = PERIHP_ACLK_HZ / PERIHP_HCLK_HZ - 1;
+	pclk_div = PERIHP_ACLK_HZ / PERIHP_PCLK_HZ - 1;
+
+	rk_clrsetreg(&cru->clksel_con[28],
+		     ACLK_PERIHP_PLL_SEL_MASK | ACLK_PERIHP_DIV_CON_MASK,
+		     ACLK_PERIHP_PLL_SEL_GPLL << ACLK_PERIHP_PLL_SEL_SHIFT |
+		     aclk_div << ACLK_PERIHP_DIV_CON_SHIFT);
+	rk_clrsetreg(&cru->clksel_con[29],
+		     PCLK_PERIHP_DIV_CON_MASK | HCLK_PERIHP_DIV_CON_MASK,
+		     pclk_div << PCLK_PERIHP_DIV_CON_SHIFT |
+		     hclk_div << HCLK_PERIHP_DIV_CON_SHIFT);
+}
+
+void rk3328_configure_cpu(struct rk3328_cru *cru,
+			  enum apll_frequencies apll_freq)
+{
+	u32 clk_core_div;
+	u32 aclkm_div;
+	u32 pclk_dbg_div;
+
+	rkclk_set_pll(cru, CLK_ARM, apll_cfgs[apll_freq]);
+
+	clk_core_div = APLL_HZ / CLK_CORE_HZ - 1;
+	aclkm_div = APLL_HZ / ACLKM_CORE_HZ / (clk_core_div + 1) - 1;
+	pclk_dbg_div = APLL_HZ / PCLK_DBG_HZ / (clk_core_div + 1) - 1;
+
+	rk_clrsetreg(&cru->clksel_con[0],
+		     CLK_CORE_PLL_SEL_MASK | CLK_CORE_DIV_MASK,
+		     CLK_CORE_PLL_SEL_APLL << CLK_CORE_PLL_SEL_SHIFT |
+		     clk_core_div << CLK_CORE_DIV_SHIFT);
+
+	rk_clrsetreg(&cru->clksel_con[1],
+		     PCLK_DBG_DIV_MASK | ACLKM_CORE_DIV_MASK,
+		     pclk_dbg_div << PCLK_DBG_DIV_SHIFT |
+		     aclkm_div << ACLKM_CORE_DIV_SHIFT);
+}
+
+
+static ulong rk3328_i2c_get_clk(struct rk3328_cru *cru, ulong clk_id)
+{
+	u32 div, con;
+
+	switch (clk_id) {
+	case SCLK_I2C0:
+		con = readl(&cru->clksel_con[34]);
+		div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
+		break;
+	case SCLK_I2C1:
+		con = readl(&cru->clksel_con[34]);
+		div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
+		break;
+	case SCLK_I2C2:
+		con = readl(&cru->clksel_con[35]);
+		div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
+		break;
+	case SCLK_I2C3:
+		con = readl(&cru->clksel_con[35]);
+		div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
+		break;
+	default:
+		printf("do not support this i2c bus\n");
+		return -EINVAL;
+	}
+
+	return DIV_TO_RATE(GPLL_HZ, div);
+}
+
+static ulong rk3328_i2c_set_clk(struct rk3328_cru *cru, ulong clk_id, uint hz)
+{
+	int src_clk_div;
+
+	src_clk_div = GPLL_HZ / hz;
+	assert(src_clk_div - 1 < 127);
+
+	switch (clk_id) {
+	case SCLK_I2C0:
+		rk_clrsetreg(&cru->clksel_con[34],
+			     CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT |
+			     CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT,
+			     (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT |
+			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT);
+		break;
+	case SCLK_I2C1:
+		rk_clrsetreg(&cru->clksel_con[34],
+			     CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT |
+			     CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT,
+			     (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT |
+			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT);
+		break;
+	case SCLK_I2C2:
+		rk_clrsetreg(&cru->clksel_con[35],
+			     CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT |
+			     CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT,
+			     (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT |
+			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT);
+		break;
+	case SCLK_I2C3:
+		rk_clrsetreg(&cru->clksel_con[35],
+			     CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT |
+			     CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT,
+			     (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT |
+			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT);
+		break;
+	default:
+		printf("do not support this i2c bus\n");
+		return -EINVAL;
+	}
+
+	return DIV_TO_RATE(GPLL_HZ, src_clk_div);
+}
+
+static ulong rk3328_mmc_get_clk(struct rk3328_cru *cru, uint clk_id)
+{
+	u32 div, con, con_id;
+
+	switch (clk_id) {
+	case HCLK_SDMMC:
+		con_id = 30;
+		break;
+	case HCLK_EMMC:
+		con_id = 32;
+		break;
+	default:
+		return -EINVAL;
+	}
+	con = readl(&cru->clksel_con[con_id]);
+	div = (con & CLK_EMMC_DIV_CON_MASK) >> CLK_EMMC_DIV_CON_SHIFT;
+
+	if ((con & CLK_EMMC_PLL_MASK) >> CLK_EMMC_PLL_SHIFT
+	    == CLK_EMMC_PLL_SEL_24M)
+		return DIV_TO_RATE(OSC_HZ, div);
+	else
+		return DIV_TO_RATE(GPLL_HZ, div);
+}
+
+static ulong rk3328_mmc_set_clk(struct rk3328_cru *cru,
+				ulong clk_id, ulong set_rate)
+{
+	int src_clk_div;
+	u32 con_id;
+
+	switch (clk_id) {
+	case HCLK_SDMMC:
+		con_id = 30;
+		break;
+	case HCLK_EMMC:
+		con_id = 32;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Select clk_sdmmc/emmc source from GPLL by default */
+	src_clk_div = GPLL_HZ / set_rate;
+
+	if (src_clk_div > 127) {
+		/* use 24MHz source for 400KHz clock */
+		src_clk_div = OSC_HZ / set_rate;
+		rk_clrsetreg(&cru->clksel_con[con_id],
+			     CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK,
+			     CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT |
+			     (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT);
+	} else {
+		rk_clrsetreg(&cru->clksel_con[con_id],
+			     CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK,
+			     CLK_EMMC_PLL_SEL_GPLL << CLK_EMMC_PLL_SHIFT |
+			     (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT);
+	}
+
+	return rk3328_mmc_get_clk(cru, clk_id);
+}
+
+static ulong rk3328_pwm_get_clk(struct rk3328_cru *cru)
+{
+	u32 div, con;
+
+	con = readl(&cru->clksel_con[24]);
+	div = (con & CLK_PWM_DIV_CON_MASK) >> CLK_PWM_DIV_CON_SHIFT;
+
+	return DIV_TO_RATE(GPLL_HZ, div);
+}
+
+static ulong rk3328_pwm_set_clk(struct rk3328_cru *cru, uint hz)
+{
+	u32 div = GPLL_HZ / hz;
+
+	rk_clrsetreg(&cru->clksel_con[24],
+		     CLK_PWM_PLL_SEL_MASK | CLK_PWM_DIV_CON_MASK,
+		     CLK_PWM_PLL_SEL_GPLL << CLK_PWM_PLL_SEL_SHIFT |
+		     (div - 1) << CLK_PWM_DIV_CON_SHIFT);
+
+	return DIV_TO_RATE(GPLL_HZ, div);
+}
+
+static ulong rk3328_clk_get_rate(struct clk *clk)
+{
+	struct rk3328_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong rate = 0;
+
+	switch (clk->id) {
+	case 0 ... 29:
+		return 0;
+	case HCLK_SDMMC:
+	case HCLK_EMMC:
+		rate = rk3328_mmc_get_clk(priv->cru, clk->id);
+		break;
+	case SCLK_I2C0:
+	case SCLK_I2C1:
+	case SCLK_I2C2:
+	case SCLK_I2C3:
+		rate = rk3328_i2c_get_clk(priv->cru, clk->id);
+		break;
+	case SCLK_PWM:
+		rate = rk3328_pwm_get_clk(priv->cru);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rate;
+}
+
+static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct rk3328_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong ret = 0;
+
+	switch (clk->id) {
+	case 0 ... 29:
+		return 0;
+	case HCLK_SDMMC:
+	case HCLK_EMMC:
+		ret = rk3328_mmc_set_clk(priv->cru, clk->id, rate);
+		break;
+	case SCLK_I2C0:
+	case SCLK_I2C1:
+	case SCLK_I2C2:
+	case SCLK_I2C3:
+		ret = rk3328_i2c_set_clk(priv->cru, clk->id, rate);
+		break;
+	case SCLK_PWM:
+		ret = rk3328_pwm_set_clk(priv->cru, rate);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return ret;
+}
+
+static struct clk_ops rk3328_clk_ops = {
+	.get_rate = rk3328_clk_get_rate,
+	.set_rate = rk3328_clk_set_rate,
+};
+
+static int rk3328_clk_probe(struct udevice *dev)
+{
+	struct rk3328_clk_priv *priv = dev_get_priv(dev);
+
+	rkclk_init(priv->cru);
+
+	return 0;
+}
+
+static int rk3328_clk_ofdata_to_platdata(struct udevice *dev)
+{
+	struct rk3328_clk_priv *priv = dev_get_priv(dev);
+
+	priv->cru = (struct rk3328_cru *)dev_get_addr(dev);
+
+	return 0;
+}
+
+static int rk3328_clk_bind(struct udevice *dev)
+{
+	int ret;
+
+	/* The reset driver does not have a device node, so bind it here */
+	ret = device_bind_driver(gd->dm_root, "rk3328_sysreset", "reset", &dev);
+	if (ret)
+		printf("Warning: No RK3328 reset driver: ret=%d\n", ret);
+
+	return ret;
+}
+
+static const struct udevice_id rk3328_clk_ids[] = {
+	{ .compatible = "rockchip,rk3328-cru" },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_rk3328_cru) = {
+	.name		= "rockchip_rk3328_cru",
+	.id		= UCLASS_CLK,
+	.of_match	= rk3328_clk_ids,
+	.priv_auto_alloc_size = sizeof(struct rk3328_clk_priv),
+	.ofdata_to_platdata = rk3328_clk_ofdata_to_platdata,
+	.ops		= &rk3328_clk_ops,
+	.bind		= rk3328_clk_bind,
+	.probe		= rk3328_clk_probe,
+};
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index 2e87e4b..922ce7e 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -7,7 +7,9 @@
 #include <common.h>
 #include <clk-uclass.h>
 #include <dm.h>
+#include <dt-structs.h>
 #include <errno.h>
+#include <mapmem.h>
 #include <syscon.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
@@ -18,10 +20,16 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-struct rk3399_pmuclk_priv {
-	struct rk3399_pmucru *pmucru;
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+struct rk3399_clk_plat {
+	struct dtd_rockchip_rk3399_cru dtd;
 };
 
+struct rk3399_pmuclk_plat {
+	struct dtd_rockchip_rk3399_pmucru dtd;
+};
+#endif
+
 struct pll_div {
 	u32 refdiv;
 	u32 fbdiv;
@@ -381,6 +389,7 @@
 	return 0;
 }
 
+#ifdef CONFIG_SPL_BUILD
 static void rkclk_init(struct rk3399_cru *cru)
 {
 	u32 aclk_div;
@@ -456,6 +465,7 @@
 		     hclk_div << HCLK_PERILP1_DIV_CON_SHIFT |
 		     HCLK_PERILP1_PLL_SEL_GPLL << HCLK_PERILP1_PLL_SEL_SHIFT);
 }
+#endif
 
 void rk3399_configure_cpu(struct rk3399_cru *cru,
 			  enum apll_l_frequencies apll_l_freq)
@@ -709,6 +719,44 @@
 	return rk3399_mmc_get_clk(cru, clk_id);
 }
 
+#define PMUSGRF_DDR_RGN_CON16 0xff330040
+static ulong rk3399_ddr_set_clk(struct rk3399_cru *cru,
+				ulong set_rate)
+{
+	struct pll_div dpll_cfg;
+
+	/*  IC ECO bug, need to set this register */
+	writel(0xc000c000, PMUSGRF_DDR_RGN_CON16);
+
+	/*  clk_ddrc == DPLL = 24MHz / refdiv * fbdiv / postdiv1 / postdiv2 */
+	switch (set_rate) {
+	case 200*MHz:
+		dpll_cfg = (struct pll_div)
+		{.refdiv = 1, .fbdiv = 50, .postdiv1 = 6, .postdiv2 = 1};
+		break;
+	case 300*MHz:
+		dpll_cfg = (struct pll_div)
+		{.refdiv = 2, .fbdiv = 100, .postdiv1 = 4, .postdiv2 = 1};
+		break;
+	case 666*MHz:
+		dpll_cfg = (struct pll_div)
+		{.refdiv = 2, .fbdiv = 111, .postdiv1 = 2, .postdiv2 = 1};
+		break;
+	case 800*MHz:
+		dpll_cfg = (struct pll_div)
+		{.refdiv = 1, .fbdiv = 100, .postdiv1 = 3, .postdiv2 = 1};
+		break;
+	case 933*MHz:
+		dpll_cfg = (struct pll_div)
+		{.refdiv = 1, .fbdiv = 116, .postdiv1 = 3, .postdiv2 = 1};
+		break;
+	default:
+		error("Unsupported SDRAM frequency!,%ld\n", set_rate);
+	}
+	rkclk_set_pll(&cru->dpll_con[0], &dpll_cfg);
+
+	return set_rate;
+}
 static ulong rk3399_clk_get_rate(struct clk *clk)
 {
 	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
@@ -763,6 +811,9 @@
 	case DCLK_VOP1:
 		ret = rk3399_vop_set_clk(priv->cru, clk->id, rate);
 		break;
+	case SCLK_DDRCLK:
+		ret = rk3399_ddr_set_clk(priv->cru, rate);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -777,19 +828,26 @@
 
 static int rk3399_clk_probe(struct udevice *dev)
 {
+#ifdef CONFIG_SPL_BUILD
 	struct rk3399_clk_priv *priv = dev_get_priv(dev);
 
-	rkclk_init(priv->cru);
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rk3399_clk_plat *plat = dev_get_platdata(dev);
 
+	priv->cru = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]);
+#endif
+	rkclk_init(priv->cru);
+#endif
 	return 0;
 }
 
 static int rk3399_clk_ofdata_to_platdata(struct udevice *dev)
 {
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 	struct rk3399_clk_priv *priv = dev_get_priv(dev);
 
 	priv->cru = (struct rk3399_cru *)dev_get_addr(dev);
-
+#endif
 	return 0;
 }
 
@@ -811,7 +869,7 @@
 };
 
 U_BOOT_DRIVER(clk_rk3399) = {
-	.name		= "clk_rk3399",
+	.name		= "rockchip_rk3399_cru",
 	.id		= UCLASS_CLK,
 	.of_match	= rk3399_clk_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3399_clk_priv),
@@ -819,6 +877,9 @@
 	.ops		= &rk3399_clk_ops,
 	.bind		= rk3399_clk_bind,
 	.probe		= rk3399_clk_probe,
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	.platdata_auto_alloc_size = sizeof(struct rk3399_clk_plat),
+#endif
 };
 
 static ulong rk3399_i2c_get_pmuclk(struct rk3399_pmucru *pmucru, ulong clk_id)
@@ -930,6 +991,7 @@
 	.set_rate = rk3399_pmuclk_set_rate,
 };
 
+#ifndef CONFIG_SPL_BUILD
 static void pmuclk_init(struct rk3399_pmucru *pmucru)
 {
 	u32 pclk_div;
@@ -939,27 +1001,35 @@
 
 	/*  configure pmu pclk */
 	pclk_div = PPLL_HZ / PMU_PCLK_HZ - 1;
-	assert((pclk_div + 1) * PMU_PCLK_HZ == PPLL_HZ && pclk_div < 0x1f);
 	rk_clrsetreg(&pmucru->pmucru_clksel[0],
 		     PMU_PCLK_DIV_CON_MASK,
 		     pclk_div << PMU_PCLK_DIV_CON_SHIFT);
 }
+#endif
 
 static int rk3399_pmuclk_probe(struct udevice *dev)
 {
 	struct rk3399_pmuclk_priv *priv = dev_get_priv(dev);
 
-	pmuclk_init(priv->pmucru);
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct rk3399_pmuclk_plat *plat = dev_get_platdata(dev);
 
+	priv->pmucru = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]);
+#endif
+
+#ifndef CONFIG_SPL_BUILD
+	pmuclk_init(priv->pmucru);
+#endif
 	return 0;
 }
 
 static int rk3399_pmuclk_ofdata_to_platdata(struct udevice *dev)
 {
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 	struct rk3399_pmuclk_priv *priv = dev_get_priv(dev);
 
 	priv->pmucru = (struct rk3399_pmucru *)dev_get_addr(dev);
-
+#endif
 	return 0;
 }
 
@@ -969,11 +1039,14 @@
 };
 
 U_BOOT_DRIVER(rockchip_rk3399_pmuclk) = {
-	.name		= "pmuclk_rk3399",
+	.name		= "rockchip_rk3399_pmucru",
 	.id		= UCLASS_CLK,
 	.of_match	= rk3399_pmuclk_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3399_pmuclk_priv),
 	.ofdata_to_platdata = rk3399_pmuclk_ofdata_to_platdata,
 	.ops		= &rk3399_pmuclk_ops,
 	.probe		= rk3399_pmuclk_probe,
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	.platdata_auto_alloc_size = sizeof(struct rk3399_pmuclk_plat),
+#endif
 };
diff --git a/drivers/core/root.c b/drivers/core/root.c
index 175fd3f..93ab568 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -205,7 +205,7 @@
 	     offset > 0;
 	     offset = fdt_next_subnode(blob, offset)) {
 		if (pre_reloc_only &&
-		    !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL))
+		    !dm_fdt_pre_reloc(blob, offset))
 			continue;
 		if (!fdtdec_get_is_enabled(blob, offset)) {
 			dm_dbg("   - ignoring disabled device\n");
diff --git a/drivers/core/util.c b/drivers/core/util.c
index e01dd06..5ceac8b 100644
--- a/drivers/core/util.c
+++ b/drivers/core/util.c
@@ -5,6 +5,7 @@
  */
 
 #include <common.h>
+#include <libfdt.h>
 #include <vsprintf.h>
 
 void dm_warn(const char *fmt, ...)
@@ -35,3 +36,27 @@
 
 	return count;
 }
+
+bool dm_fdt_pre_reloc(const void *blob, int offset)
+{
+	if (fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL))
+		return true;
+
+#ifdef CONFIG_TPL_BUILD
+	if (fdt_getprop(blob, offset, "u-boot,dm-tpl", NULL))
+		return true;
+#elif defined(CONFIG_SPL_BUILD)
+	if (fdt_getprop(blob, offset, "u-boot,dm-spl", NULL))
+		return true;
+#else
+	/*
+	 * In regular builds individual spl and tpl handling both
+	 * count as handled pre-relocation for later second init.
+	 */
+	if (fdt_getprop(blob, offset, "u-boot,dm-spl", NULL) ||
+	    fdt_getprop(blob, offset, "u-boot,dm-tpl", NULL))
+		return true;
+#endif
+
+	return false;
+}
diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c
index bd91f91..bdde831 100644
--- a/drivers/mmc/rockchip_sdhci.c
+++ b/drivers/mmc/rockchip_sdhci.c
@@ -8,9 +8,11 @@
 
 #include <common.h>
 #include <dm.h>
+#include <dt-structs.h>
 #include <fdtdec.h>
 #include <libfdt.h>
 #include <malloc.h>
+#include <mapmem.h>
 #include <sdhci.h>
 #include <clk.h>
 
@@ -19,6 +21,9 @@
 #define EMMC_MIN_FREQ	400000
 
 struct rockchip_sdhc_plat {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct dtd_rockchip_rk3399_sdhci_5_1 dtplat;
+#endif
 	struct mmc_config cfg;
 	struct mmc mmc;
 };
@@ -37,10 +42,18 @@
 	int max_frequency, ret;
 	struct clk clk;
 
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct dtd_rockchip_rk3399_sdhci_5_1 *dtplat = &plat->dtplat;
 
+	host->name = dev->name;
+	host->ioaddr = map_sysmem(dtplat->reg[1], dtplat->reg[3]);
+	max_frequency = dtplat->max_frequency;
+	ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &clk);
+#else
 	max_frequency = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
 			"max-frequency", 0);
 	ret = clk_get_by_index(dev, 0, &clk);
+#endif
 	if (!ret) {
 		ret = clk_set_rate(&clk, max_frequency);
 		if (IS_ERR_VALUE(ret))
@@ -66,10 +79,12 @@
 
 static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev)
 {
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 	struct sdhci_host *host = dev_get_priv(dev);
 
 	host->name = dev->name;
 	host->ioaddr = dev_get_addr_ptr(dev);
+#endif
 
 	return 0;
 }
@@ -87,7 +102,7 @@
 };
 
 U_BOOT_DRIVER(arasan_sdhci_drv) = {
-	.name		= "arasan_sdhci",
+	.name		= "rockchip_rk3399_sdhci_5_1",
 	.id		= UCLASS_MMC,
 	.of_match	= arasan_sdhci_ids,
 	.ofdata_to_platdata = arasan_sdhci_ofdata_to_platdata,
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index d011dad..f3e3072 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -132,6 +132,15 @@
 	  definitions and pin control functions for each available multiplex
 	  function.
 
+config ROCKCHIP_RK3188_PINCTRL
+	bool "Rockchip pin control driver"
+	depends on DM
+	help
+	  Support pin multiplexing control on Rockchip rk3188 SoCs. The driver
+	  is controlled by a device tree node which contains both the GPIO
+	  definitions and pin control functions for each available multiplex
+	  function.
+
 config ROCKCHIP_RK3288_PINCTRL
 	bool "Rockchip pin control driver"
 	depends on DM
@@ -148,6 +157,15 @@
 	  This option is to enable the AT91 pinctrl driver for AT91 PIO4
 	  controller which is available on SAMA5D2 SoC.
 
+config ROCKCHIP_RK3328_PINCTRL
+	bool "Rockchip pin control driver"
+	depends on DM
+	help
+	  Support pin multiplexing control on Rockchip rk3328 SoCs. The driver
+	  is controlled by a device tree node which contains both the GPIO
+	  definitions and pin control functions for each available multiplex
+	  function.
+
 config ROCKCHIP_RK3399_PINCTRL
 	bool "Rockchip pin control driver"
 	depends on DM
diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c
index 49afe91..9efad06 100644
--- a/drivers/pinctrl/pinctrl-uclass.c
+++ b/drivers/pinctrl/pinctrl-uclass.c
@@ -12,6 +12,7 @@
 #include <dm/lists.h>
 #include <dm/pinctrl.h>
 #include <dm/uclass.h>
+#include <dm/util.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -131,7 +132,7 @@
 	     offset > 0;
 	     offset = fdt_next_subnode(fdt, offset)) {
 		if (pre_reloc_only &&
-		    !fdt_getprop(fdt, offset, "u-boot,dm-pre-reloc", NULL))
+		    !dm_fdt_pre_reloc(fdt, offset))
 			continue;
 		/*
 		 * If this node has "compatible" property, this is not
diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile
index 805c833..b0b698a 100644
--- a/drivers/pinctrl/rockchip/Makefile
+++ b/drivers/pinctrl/rockchip/Makefile
@@ -6,5 +6,7 @@
 #
 
 obj-$(CONFIG_ROCKCHIP_RK3036_PINCTRL) += pinctrl_rk3036.o
+obj-$(CONFIG_ROCKCHIP_RK3188_PINCTRL) += pinctrl_rk3188.o
 obj-$(CONFIG_ROCKCHIP_RK3288_PINCTRL) += pinctrl_rk3288.o
+obj-$(CONFIG_ROCKCHIP_RK3328_PINCTRL) += pinctrl_rk3328.o
 obj-$(CONFIG_ROCKCHIP_RK3399_PINCTRL) += pinctrl_rk3399.o
diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3188.c b/drivers/pinctrl/rockchip/pinctrl_rk3188.c
new file mode 100644
index 0000000..ef94dab
--- /dev/null
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3188.c
@@ -0,0 +1,611 @@
+/*
+ * Pinctrl driver for Rockchip RK3188 SoCs
+ * Copyright (c) 2016 Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/grf_rk3188.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/periph.h>
+#include <asm/arch/pmu_rk3188.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rk3188_pinctrl_priv {
+	struct rk3188_grf *grf;
+	struct rk3188_pmu *pmu;
+	int num_banks;
+};
+
+/**
+ * Encode variants of iomux registers into a type variable
+ */
+#define IOMUX_GPIO_ONLY		BIT(0)
+
+/**
+ * @type: iomux variant using IOMUX_* constants
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ *	    an initial offset value the relevant source offset can be reset
+ *	    to a new value for autocalculating the following iomux registers.
+ */
+struct rockchip_iomux {
+	u8 type;
+	s16 offset;
+};
+
+/**
+ * @reg: register offset of the gpio bank
+ * @nr_pins: number of pins in this bank
+ * @bank_num: number of the bank, to account for holes
+ * @name: name of the bank
+ * @iomux: array describing the 4 iomux sources of the bank
+ */
+struct rockchip_pin_bank {
+	u16 reg;
+	u8 nr_pins;
+	u8 bank_num;
+	char *name;
+	struct rockchip_iomux iomux[4];
+};
+
+#define PIN_BANK(id, pins, label)			\
+	{						\
+		.bank_num	= id,			\
+		.nr_pins	= pins,			\
+		.name		= label,		\
+		.iomux		= {			\
+			{ .offset = -1 },		\
+			{ .offset = -1 },		\
+			{ .offset = -1 },		\
+			{ .offset = -1 },		\
+		},					\
+	}
+
+#define PIN_BANK_IOMUX_FLAGS(id, pins, label, iom0, iom1, iom2, iom3)	\
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .type = iom0, .offset = -1 },			\
+			{ .type = iom1, .offset = -1 },			\
+			{ .type = iom2, .offset = -1 },			\
+			{ .type = iom3, .offset = -1 },			\
+		},							\
+	}
+
+#ifndef CONFIG_SPL_BUILD
+static struct rockchip_pin_bank rk3188_pin_banks[] = {
+	PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_GPIO_ONLY, 0, 0, 0),
+	PIN_BANK(1, 32, "gpio1"),
+	PIN_BANK(2, 32, "gpio2"),
+	PIN_BANK(3, 32, "gpio3"),
+};
+#endif
+
+static void pinctrl_rk3188_pwm_config(struct rk3188_grf *grf, int pwm_id)
+{
+	switch (pwm_id) {
+	case PERIPH_ID_PWM0:
+		rk_clrsetreg(&grf->gpio3d_iomux, GPIO3D3_MASK << GPIO3D3_SHIFT,
+			     GPIO3D3_PWM_0 << GPIO3D3_SHIFT);
+		break;
+	case PERIPH_ID_PWM1:
+		rk_clrsetreg(&grf->gpio3d_iomux, GPIO3D4_MASK << GPIO3D4_SHIFT,
+			     GPIO3D4_PWM_1 << GPIO3D4_SHIFT);
+		break;
+	case PERIPH_ID_PWM2:
+		rk_clrsetreg(&grf->gpio3d_iomux, GPIO3D5_MASK << GPIO3D5_SHIFT,
+			     GPIO3D5_PWM_2 << GPIO3D5_SHIFT);
+		break;
+	case PERIPH_ID_PWM3:
+		rk_clrsetreg(&grf->gpio3d_iomux, GPIO3D6_MASK << GPIO3D6_SHIFT,
+			     GPIO3D6_PWM_3 << GPIO3D6_SHIFT);
+		break;
+	default:
+		debug("pwm id = %d iomux error!\n", pwm_id);
+		break;
+	}
+}
+
+static void pinctrl_rk3188_i2c_config(struct rk3188_grf *grf,
+				      struct rk3188_pmu *pmu, int i2c_id)
+{
+	switch (i2c_id) {
+	case PERIPH_ID_I2C0:
+		rk_clrsetreg(&grf->gpio1d_iomux,
+				GPIO1D1_MASK << GPIO1D1_SHIFT |
+				GPIO1D0_MASK << GPIO1D0_SHIFT,
+				GPIO1D1_I2C0_SCL << GPIO1D1_SHIFT |
+				GPIO1D0_I2C0_SDA << GPIO1D0_SHIFT);
+		/* enable new i2c controller */
+		rk_clrsetreg(&grf->soc_con1, 1 << RKI2C0_SEL_SHIFT,
+					     1 << RKI2C0_SEL_SHIFT);
+		break;
+	case PERIPH_ID_I2C1:
+		rk_clrsetreg(&grf->gpio1d_iomux,
+				GPIO1D3_MASK << GPIO1D3_SHIFT |
+				GPIO1D2_MASK << GPIO1D2_SHIFT,
+				GPIO1D3_I2C1_SCL << GPIO1D2_SHIFT |
+				GPIO1D2_I2C1_SDA << GPIO1D2_SHIFT);
+		rk_clrsetreg(&grf->soc_con1, 1 << RKI2C1_SEL_SHIFT,
+					     1 << RKI2C1_SEL_SHIFT);
+		break;
+	case PERIPH_ID_I2C2:
+		rk_clrsetreg(&grf->gpio1d_iomux,
+				GPIO1D5_MASK << GPIO1D5_SHIFT |
+				GPIO1D4_MASK << GPIO1D4_SHIFT,
+				GPIO1D5_I2C2_SCL << GPIO1D5_SHIFT |
+				GPIO1D4_I2C2_SDA << GPIO1D4_SHIFT);
+		rk_clrsetreg(&grf->soc_con1, 1 << RKI2C2_SEL_SHIFT,
+					     1 << RKI2C2_SEL_SHIFT);
+		break;
+	case PERIPH_ID_I2C3:
+		rk_clrsetreg(&grf->gpio3b_iomux,
+				GPIO3B7_MASK << GPIO3B7_SHIFT |
+				GPIO3B6_MASK << GPIO3B6_SHIFT,
+				GPIO3B7_I2C3_SCL << GPIO3B7_SHIFT |
+				GPIO3B6_I2C3_SDA << GPIO3B6_SHIFT);
+		rk_clrsetreg(&grf->soc_con1, 1 << RKI2C3_SEL_SHIFT,
+					     1 << RKI2C3_SEL_SHIFT);
+		break;
+	case PERIPH_ID_I2C4:
+		rk_clrsetreg(&grf->gpio1d_iomux,
+				GPIO1D7_MASK << GPIO1D7_SHIFT |
+				GPIO1D6_MASK << GPIO1D6_SHIFT,
+				GPIO1D7_I2C4_SCL << GPIO1D7_SHIFT |
+				GPIO1D6_I2C4_SDA << GPIO1D6_SHIFT);
+		rk_clrsetreg(&grf->soc_con1, 1 << RKI2C4_SEL_SHIFT,
+					     1 << RKI2C4_SEL_SHIFT);
+		break;
+	default:
+		debug("i2c id = %d iomux error!\n", i2c_id);
+		break;
+	}
+}
+
+static int pinctrl_rk3188_spi_config(struct rk3188_grf *grf,
+				     enum periph_id spi_id, int cs)
+{
+	switch (spi_id) {
+	case PERIPH_ID_SPI0:
+		switch (cs) {
+		case 0:
+			rk_clrsetreg(&grf->gpio1a_iomux,
+				     GPIO1A7_MASK << GPIO1A7_SHIFT,
+				     GPIO1A7_SPI0_CSN0 << GPIO1A7_SHIFT);
+			break;
+		case 1:
+			rk_clrsetreg(&grf->gpio1b_iomux,
+				     GPIO1B7_MASK << GPIO1B7_SHIFT,
+				     GPIO1B7_SPI0_CSN1 << GPIO1B7_SHIFT);
+			break;
+		default:
+			goto err;
+		}
+		rk_clrsetreg(&grf->gpio1a_iomux,
+			     GPIO1A4_MASK << GPIO1A4_SHIFT |
+			     GPIO1A5_MASK << GPIO1A5_SHIFT |
+			     GPIO1A6_MASK << GPIO1A6_SHIFT,
+			     GPIO1A4_SPI0_RXD << GPIO1A4_SHIFT |
+			     GPIO1A5_SPI0_TXD << GPIO1A5_SHIFT |
+			     GPIO1A6_SPI0_CLK << GPIO1A6_SHIFT);
+		break;
+	case PERIPH_ID_SPI1:
+		switch (cs) {
+		case 0:
+			rk_clrsetreg(&grf->gpio0d_iomux,
+				     GPIO0D7_MASK << GPIO0D7_SHIFT,
+				     GPIO0D7_SPI1_CSN0 << GPIO0D7_SHIFT);
+			break;
+		case 1:
+			rk_clrsetreg(&grf->gpio1b_iomux,
+				     GPIO1B6_MASK << GPIO1B6_SHIFT,
+				     GPIO1B6_SPI1_CSN1 << GPIO1B6_SHIFT);
+			break;
+		default:
+			goto err;
+		}
+		rk_clrsetreg(&grf->gpio0d_iomux,
+			     GPIO0D4_MASK << GPIO0D4_SHIFT |
+			     GPIO0D5_MASK << GPIO0D5_SHIFT |
+			     GPIO0D6_MASK << GPIO0D6_SHIFT,
+			     GPIO0D4_SPI0_RXD << GPIO0D4_SHIFT |
+			     GPIO0D5_SPI1_TXD << GPIO0D5_SHIFT |
+			     GPIO0D6_SPI1_CLK << GPIO0D6_SHIFT);
+		break;
+	default:
+		goto err;
+	}
+
+	return 0;
+err:
+	debug("rkspi: periph%d cs=%d not supported", spi_id, cs);
+	return -ENOENT;
+}
+
+static void pinctrl_rk3188_uart_config(struct rk3188_grf *grf, int uart_id)
+{
+	switch (uart_id) {
+	case PERIPH_ID_UART0:
+		rk_clrsetreg(&grf->gpio1a_iomux,
+			     GPIO1A3_MASK << GPIO1A3_SHIFT |
+			     GPIO1A2_MASK << GPIO1A2_SHIFT |
+			     GPIO1A1_MASK << GPIO1A1_SHIFT |
+			     GPIO1A0_MASK << GPIO1A0_SHIFT,
+			     GPIO1A3_UART0_RTS_N << GPIO1A3_SHIFT |
+			     GPIO1A2_UART0_CTS_N << GPIO1A2_SHIFT |
+			     GPIO1A1_UART0_SOUT << GPIO1A1_SHIFT |
+			     GPIO1A0_UART0_SIN << GPIO1A0_SHIFT);
+		break;
+	case PERIPH_ID_UART1:
+		rk_clrsetreg(&grf->gpio1a_iomux,
+			     GPIO1A7_MASK << GPIO1A7_SHIFT |
+			     GPIO1A6_MASK << GPIO1A6_SHIFT |
+			     GPIO1A5_MASK << GPIO1A5_SHIFT |
+			     GPIO1A4_MASK << GPIO1A4_SHIFT,
+			     GPIO1A7_UART1_RTS_N << GPIO1A7_SHIFT |
+			     GPIO1A6_UART1_CTS_N << GPIO1A6_SHIFT |
+			     GPIO1A5_UART1_SOUT << GPIO1A5_SHIFT |
+			     GPIO1A4_UART1_SIN << GPIO1A4_SHIFT);
+		break;
+	case PERIPH_ID_UART2:
+		rk_clrsetreg(&grf->gpio1b_iomux,
+			     GPIO1B1_MASK << GPIO1B1_SHIFT |
+			     GPIO1B0_MASK << GPIO1B0_SHIFT,
+			     GPIO1B1_UART2_SOUT << GPIO1B1_SHIFT |
+			     GPIO1B0_UART2_SIN << GPIO1B0_SHIFT);
+		break;
+	case PERIPH_ID_UART3:
+		rk_clrsetreg(&grf->gpio1b_iomux,
+			     GPIO1B5_MASK << GPIO1B5_SHIFT |
+			     GPIO1B4_MASK << GPIO1B4_SHIFT |
+			     GPIO1B3_MASK << GPIO1B3_SHIFT |
+			     GPIO1B2_MASK << GPIO1B2_SHIFT,
+			     GPIO1B5_UART3_RTS_N << GPIO1B5_SHIFT |
+			     GPIO1B4_UART3_CTS_N << GPIO1B4_SHIFT |
+			     GPIO1B3_UART3_SOUT << GPIO1B3_SHIFT |
+			     GPIO1B2_UART3_SIN << GPIO1B2_SHIFT);
+		break;
+	default:
+		debug("uart id = %d iomux error!\n", uart_id);
+		break;
+	}
+}
+
+static void pinctrl_rk3188_sdmmc_config(struct rk3188_grf *grf, int mmc_id)
+{
+	switch (mmc_id) {
+	case PERIPH_ID_EMMC:
+		rk_clrsetreg(&grf->soc_con0, 1 << EMMC_FLASH_SEL_SHIFT,
+					     1 << EMMC_FLASH_SEL_SHIFT);
+		rk_clrsetreg(&grf->gpio0d_iomux,
+			     GPIO0D2_MASK << GPIO0D2_SHIFT |
+			     GPIO0D0_MASK << GPIO0D0_SHIFT,
+			     GPIO0D2_EMMC_CMD << GPIO0D2_SHIFT |
+			     GPIO0D0_EMMC_CLKOUT << GPIO0D0_SHIFT);
+		break;
+	case PERIPH_ID_SDCARD:
+		rk_clrsetreg(&grf->gpio3b_iomux,
+			     GPIO3B0_MASK << GPIO3B0_SHIFT,
+			     GPIO3B0_SDMMC_DETECT_N << GPIO3B0_SHIFT);
+		rk_clrsetreg(&grf->gpio3a_iomux,
+			     GPIO3A7_MASK << GPIO3A7_SHIFT |
+			     GPIO3A6_MASK << GPIO3A6_SHIFT |
+			     GPIO3A5_MASK << GPIO3A5_SHIFT |
+			     GPIO3A4_MASK << GPIO3A4_SHIFT |
+			     GPIO3A3_MASK << GPIO3A3_SHIFT |
+			     GPIO3A3_MASK << GPIO3A2_SHIFT,
+			     GPIO3A7_SDMMC0_DATA3 << GPIO3A7_SHIFT |
+			     GPIO3A6_SDMMC0_DATA2 << GPIO3A6_SHIFT |
+			     GPIO3A5_SDMMC0_DATA1 << GPIO3A5_SHIFT |
+			     GPIO3A4_SDMMC0_DATA0 << GPIO3A4_SHIFT |
+			     GPIO3A3_SDMMC0_CMD << GPIO3A3_SHIFT |
+			     GPIO3A2_SDMMC0_CLKOUT << GPIO3A2_SHIFT);
+		break;
+	default:
+		debug("mmc id = %d iomux error!\n", mmc_id);
+		break;
+	}
+}
+
+static int rk3188_pinctrl_request(struct udevice *dev, int func, int flags)
+{
+	struct rk3188_pinctrl_priv *priv = dev_get_priv(dev);
+
+	debug("%s: func=%x, flags=%x\n", __func__, func, flags);
+	switch (func) {
+	case PERIPH_ID_PWM0:
+	case PERIPH_ID_PWM1:
+	case PERIPH_ID_PWM2:
+	case PERIPH_ID_PWM3:
+	case PERIPH_ID_PWM4:
+		pinctrl_rk3188_pwm_config(priv->grf, func);
+		break;
+	case PERIPH_ID_I2C0:
+	case PERIPH_ID_I2C1:
+	case PERIPH_ID_I2C2:
+	case PERIPH_ID_I2C3:
+	case PERIPH_ID_I2C4:
+	case PERIPH_ID_I2C5:
+		pinctrl_rk3188_i2c_config(priv->grf, priv->pmu, func);
+		break;
+	case PERIPH_ID_SPI0:
+	case PERIPH_ID_SPI1:
+	case PERIPH_ID_SPI2:
+		pinctrl_rk3188_spi_config(priv->grf, func, flags);
+		break;
+	case PERIPH_ID_UART0:
+	case PERIPH_ID_UART1:
+	case PERIPH_ID_UART2:
+	case PERIPH_ID_UART3:
+	case PERIPH_ID_UART4:
+		pinctrl_rk3188_uart_config(priv->grf, func);
+		break;
+		break;
+	case PERIPH_ID_SDMMC0:
+	case PERIPH_ID_SDMMC1:
+		pinctrl_rk3188_sdmmc_config(priv->grf, func);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rk3188_pinctrl_get_periph_id(struct udevice *dev,
+					struct udevice *periph)
+{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+	u32 cell[3];
+	int ret;
+
+	ret = fdtdec_get_int_array(gd->fdt_blob, periph->of_offset,
+				   "interrupts", cell, ARRAY_SIZE(cell));
+	if (ret < 0)
+		return -EINVAL;
+
+	switch (cell[1]) {
+	case 44:
+		return PERIPH_ID_SPI0;
+	case 45:
+		return PERIPH_ID_SPI1;
+	case 46:
+		return PERIPH_ID_SPI2;
+	case 60:
+		return PERIPH_ID_I2C0;
+	case 62: /* Note strange order */
+		return PERIPH_ID_I2C1;
+	case 61:
+		return PERIPH_ID_I2C2;
+	case 63:
+		return PERIPH_ID_I2C3;
+	case 64:
+		return PERIPH_ID_I2C4;
+	case 65:
+		return PERIPH_ID_I2C5;
+	}
+#endif
+
+	return -ENOENT;
+}
+
+static int rk3188_pinctrl_set_state_simple(struct udevice *dev,
+					   struct udevice *periph)
+{
+	int func;
+
+	func = rk3188_pinctrl_get_periph_id(dev, periph);
+	if (func < 0)
+		return func;
+	return rk3188_pinctrl_request(dev, func, 0);
+}
+
+#ifndef CONFIG_SPL_BUILD
+int rk3188_pinctrl_get_pin_info(struct rk3188_pinctrl_priv *priv,
+				int banknum, int ind, u32 **addrp, uint *shiftp,
+				uint *maskp)
+{
+	struct rockchip_pin_bank *bank = &rk3188_pin_banks[banknum];
+	uint muxnum;
+	u32 *addr;
+
+	for (muxnum = 0; muxnum < 4; muxnum++) {
+		struct rockchip_iomux *mux = &bank->iomux[muxnum];
+
+		if (ind >= 8) {
+			ind -= 8;
+			continue;
+		}
+
+		addr = &priv->grf->gpio0c_iomux - 2;
+		addr += mux->offset;
+		*shiftp = ind & 7;
+		*maskp = 3;
+		*shiftp *= 2;
+
+		debug("%s: addr=%p, mask=%x, shift=%x\n", __func__, addr,
+		      *maskp, *shiftp);
+		*addrp = addr;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int rk3188_pinctrl_get_gpio_mux(struct udevice *dev, int banknum,
+				       int index)
+{
+	struct rk3188_pinctrl_priv *priv = dev_get_priv(dev);
+	uint shift;
+	uint mask;
+	u32 *addr;
+	int ret;
+
+	ret = rk3188_pinctrl_get_pin_info(priv, banknum, index, &addr, &shift,
+					  &mask);
+	if (ret)
+		return ret;
+	return (readl(addr) & mask) >> shift;
+}
+
+static int rk3188_pinctrl_set_pins(struct udevice *dev, int banknum, int index,
+				   int muxval, int flags)
+{
+	struct rk3188_pinctrl_priv *priv = dev_get_priv(dev);
+	uint shift, ind = index;
+	uint mask;
+	u32 *addr;
+	int ret;
+
+	debug("%s: %x %x %x %x\n", __func__, banknum, index, muxval, flags);
+	ret = rk3188_pinctrl_get_pin_info(priv, banknum, index, &addr, &shift,
+					  &mask);
+	if (ret)
+		return ret;
+	rk_clrsetreg(addr, mask << shift, muxval << shift);
+
+	/* Handle pullup/pulldown */
+	if (flags) {
+		uint val = 0;
+
+		if (flags & (1 << PIN_CONFIG_BIAS_PULL_UP))
+			val = 1;
+		else if (flags & (1 << PIN_CONFIG_BIAS_PULL_DOWN))
+			val = 2;
+
+		ind = index >> 3;
+
+		if (banknum == 0 && index < 12) {
+			addr = &priv->pmu->gpio0_p[ind];
+			shift = (index & 7) * 2;
+		} else if (banknum == 0 && index >= 12) {
+			addr = &priv->grf->gpio0_p[ind - 1];
+			/*
+			 * The bits in the grf-registers have an inverse
+			 * ordering with the lowest pin being in bits 15:14
+			 * and the highest pin in bits 1:0 .
+			 */
+			shift = (7 - (index & 7)) * 2;
+		} else {
+			addr = &priv->grf->gpio1_p[banknum - 1][ind];
+			shift = (7 - (index & 7)) * 2;
+		}
+		debug("%s: addr=%p, val=%x, shift=%x\n", __func__, addr, val,
+		      shift);
+		rk_clrsetreg(addr, 3 << shift, val << shift);
+	}
+
+	return 0;
+}
+
+static int rk3188_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+	const void *blob = gd->fdt_blob;
+	int pcfg_node, ret, flags, count, i;
+	u32 cell[60], *ptr;
+
+	debug("%s: %s %s\n", __func__, dev->name, config->name);
+	ret = fdtdec_get_int_array_count(blob, config->of_offset,
+					 "rockchip,pins", cell,
+					 ARRAY_SIZE(cell));
+	if (ret < 0) {
+		debug("%s: bad array %d\n", __func__, ret);
+		return -EINVAL;
+	}
+	count = ret;
+	for (i = 0, ptr = cell; i < count; i += 4, ptr += 4) {
+		pcfg_node = fdt_node_offset_by_phandle(blob, ptr[3]);
+		if (pcfg_node < 0)
+			return -EINVAL;
+		flags = pinctrl_decode_pin_config(blob, pcfg_node);
+		if (flags < 0)
+			return flags;
+
+		ret = rk3188_pinctrl_set_pins(dev, ptr[0], ptr[1], ptr[2],
+					      flags);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+#endif
+
+static struct pinctrl_ops rk3188_pinctrl_ops = {
+#ifndef CONFIG_SPL_BUILD
+	.set_state	= rk3188_pinctrl_set_state,
+	.get_gpio_mux	= rk3188_pinctrl_get_gpio_mux,
+#endif
+	.set_state_simple	= rk3188_pinctrl_set_state_simple,
+	.request	= rk3188_pinctrl_request,
+	.get_periph_id	= rk3188_pinctrl_get_periph_id,
+};
+
+#ifndef CONFIG_SPL_BUILD
+static int rk3188_pinctrl_parse_tables(struct rk3188_pinctrl_priv *priv,
+				       struct rockchip_pin_bank *banks,
+				       int count)
+{
+	struct rockchip_pin_bank *bank;
+	uint reg, muxnum, banknum;
+
+	reg = 0;
+	for (banknum = 0; banknum < count; banknum++) {
+		bank = &banks[banknum];
+		bank->reg = reg;
+		debug("%s: bank %d, reg %x\n", __func__, banknum, reg * 4);
+		for (muxnum = 0; muxnum < 4; muxnum++) {
+			struct rockchip_iomux *mux = &bank->iomux[muxnum];
+
+			mux->offset = reg;
+			reg += 1;
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static int rk3188_pinctrl_probe(struct udevice *dev)
+{
+	struct rk3188_pinctrl_priv *priv = dev_get_priv(dev);
+	int ret = 0;
+
+	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+	priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU);
+	debug("%s: grf=%p, pmu=%p\n", __func__, priv->grf, priv->pmu);
+#ifndef CONFIG_SPL_BUILD
+	ret = rk3188_pinctrl_parse_tables(priv, rk3188_pin_banks,
+					  ARRAY_SIZE(rk3188_pin_banks));
+#endif
+
+	return ret;
+}
+
+static const struct udevice_id rk3188_pinctrl_ids[] = {
+	{ .compatible = "rockchip,rk3188-pinctrl" },
+	{ }
+};
+
+U_BOOT_DRIVER(pinctrl_rk3188) = {
+	.name		= "rockchip_rk3188_pinctrl",
+	.id		= UCLASS_PINCTRL,
+	.of_match	= rk3188_pinctrl_ids,
+	.priv_auto_alloc_size = sizeof(struct rk3188_pinctrl_priv),
+	.ops		= &rk3188_pinctrl_ops,
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+	.bind		= dm_scan_fdt_dev,
+#endif
+	.probe		= rk3188_pinctrl_probe,
+};
diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3328.c b/drivers/pinctrl/rockchip/pinctrl_rk3328.c
new file mode 100644
index 0000000..5ca6782
--- /dev/null
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3328.c
@@ -0,0 +1,419 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/grf_rk3328.h>
+#include <asm/arch/periph.h>
+#include <asm/io.h>
+#include <dm/pinctrl.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rk3328_pinctrl_priv {
+	struct rk3328_grf_regs *grf;
+};
+
+enum {
+	/* GRF_GPIO0A_IOMUX */
+	GRF_GPIO0A5_SEL_SHIFT	= 10,
+	GRF_GPIO0A5_SEL_MASK	= 3 << GRF_GPIO0A5_SEL_SHIFT,
+	GRF_I2C3_SCL		= 2,
+
+	GRF_GPIO0A6_SEL_SHIFT	= 12,
+	GRF_GPIO0A6_SEL_MASK	= 3 << GRF_GPIO0A6_SEL_SHIFT,
+	GRF_I2C3_SDA		= 2,
+
+	GRF_GPIO0A7_SEL_SHIFT	= 14,
+	GRF_GPIO0A7_SEL_MASK	= 3 << GRF_GPIO0A7_SEL_SHIFT,
+	GRF_EMMC_DATA0		= 2,
+
+	/* GRF_GPIO1A_IOMUX */
+	GRF_GPIO1A0_SEL_SHIFT	= 0,
+	GRF_GPIO1A0_SEL_MASK	= 0x3fff << GRF_GPIO1A0_SEL_SHIFT,
+	GRF_CARD_DATA_CLK_CMD_DETN	= 0x1555,
+
+	/* GRF_GPIO2A_IOMUX */
+	GRF_GPIO2A0_SEL_SHIFT	= 0,
+	GRF_GPIO2A0_SEL_MASK	= 3 << GRF_GPIO2A0_SEL_SHIFT,
+	GRF_UART2_TX_M1		= 1,
+
+	GRF_GPIO2A1_SEL_SHIFT	= 2,
+	GRF_GPIO2A1_SEL_MASK	= 3 << GRF_GPIO2A1_SEL_SHIFT,
+	GRF_UART2_RX_M1		= 1,
+
+	GRF_GPIO2A2_SEL_SHIFT	= 4,
+	GRF_GPIO2A2_SEL_MASK	= 3 << GRF_GPIO2A2_SEL_SHIFT,
+	GRF_PWM_IR		= 1,
+
+	GRF_GPIO2A4_SEL_SHIFT	= 8,
+	GRF_GPIO2A4_SEL_MASK	= 3 << GRF_GPIO2A4_SEL_SHIFT,
+	GRF_PWM_0		= 1,
+	GRF_I2C1_SDA,
+
+	GRF_GPIO2A5_SEL_SHIFT	= 10,
+	GRF_GPIO2A5_SEL_MASK	= 3 << GRF_GPIO2A5_SEL_SHIFT,
+	GRF_PWM_1		= 1,
+	GRF_I2C1_SCL,
+
+	GRF_GPIO2A6_SEL_SHIFT	= 12,
+	GRF_GPIO2A6_SEL_MASK	= 3 << GRF_GPIO2A6_SEL_SHIFT,
+	GRF_PWM_2		= 1,
+
+	GRF_GPIO2A7_SEL_SHIFT	= 14,
+	GRF_GPIO2A7_SEL_MASK	= 3 << GRF_GPIO2A7_SEL_SHIFT,
+	GRF_CARD_PWR_EN_M0	= 1,
+
+	/* GRF_GPIO2BL_IOMUX */
+	GRF_GPIO2BL0_SEL_SHIFT	= 0,
+	GRF_GPIO2BL0_SEL_MASK	= 0x3f << GRF_GPIO2BL0_SEL_SHIFT,
+	GRF_SPI_CLK_TX_RX_M0	= 0x15,
+
+	GRF_GPIO2BL3_SEL_SHIFT	= 6,
+	GRF_GPIO2BL3_SEL_MASK	= 3 << GRF_GPIO2BL3_SEL_SHIFT,
+	GRF_SPI_CSN0_M0		= 1,
+
+	GRF_GPIO2BL4_SEL_SHIFT	= 8,
+	GRF_GPIO2BL4_SEL_MASK	= 3 << GRF_GPIO2BL4_SEL_SHIFT,
+	GRF_SPI_CSN1_M0		= 1,
+
+	GRF_GPIO2BL5_SEL_SHIFT	= 10,
+	GRF_GPIO2BL5_SEL_MASK	= 3 << GRF_GPIO2BL5_SEL_SHIFT,
+	GRF_I2C2_SDA		= 1,
+
+	GRF_GPIO2BL6_SEL_SHIFT	= 12,
+	GRF_GPIO2BL6_SEL_MASK	= 3 << GRF_GPIO2BL6_SEL_SHIFT,
+	GRF_I2C2_SCL		= 1,
+
+	/* GRF_GPIO2D_IOMUX */
+	GRF_GPIO2D0_SEL_SHIFT	= 0,
+	GRF_GPIO2D0_SEL_MASK	= 3 << GRF_GPIO2D0_SEL_SHIFT,
+	GRF_I2C0_SCL		= 1,
+
+	GRF_GPIO2D1_SEL_SHIFT	= 2,
+	GRF_GPIO2D1_SEL_MASK	= 3 << GRF_GPIO2D1_SEL_SHIFT,
+	GRF_I2C0_SDA		= 1,
+
+	GRF_GPIO2D4_SEL_SHIFT	= 8,
+	GRF_GPIO2D4_SEL_MASK	= 0xff << GRF_GPIO2D4_SEL_SHIFT,
+	GRF_EMMC_DATA123	= 0xaa,
+
+	/* GRF_GPIO3C_IOMUX */
+	GRF_GPIO3C0_SEL_SHIFT	= 0,
+	GRF_GPIO3C0_SEL_MASK	= 0x3fff << GRF_GPIO3C0_SEL_SHIFT,
+	GRF_EMMC_DATA567_PWR_CLK_RSTN_CMD	= 0x2aaa,
+
+	/* GRF_COM_IOMUX */
+	GRF_UART2_IOMUX_SEL_SHIFT	= 0,
+	GRF_UART2_IOMUX_SEL_MASK	= 3 << GRF_UART2_IOMUX_SEL_SHIFT,
+	GRF_UART2_IOMUX_SEL_M0		= 0,
+	GRF_UART2_IOMUX_SEL_M1,
+
+	GRF_SPI_IOMUX_SEL_SHIFT = 4,
+	GRF_SPI_IOMUX_SEL_MASK	= 3 << GRF_SPI_IOMUX_SEL_SHIFT,
+	GRF_SPI_IOMUX_SEL_M0	= 0,
+	GRF_SPI_IOMUX_SEL_M1,
+	GRF_SPI_IOMUX_SEL_M2,
+
+	GRF_CARD_IOMUX_SEL_SHIFT	= 7,
+	GRF_CARD_IOMUX_SEL_MASK		= 1 << GRF_CARD_IOMUX_SEL_SHIFT,
+	GRF_CARD_IOMUX_SEL_M0		= 0,
+	GRF_CARD_IOMUX_SEL_M1,
+};
+
+static void pinctrl_rk3328_pwm_config(struct rk3328_grf_regs *grf, int pwm_id)
+{
+	switch (pwm_id) {
+	case PERIPH_ID_PWM0:
+		rk_clrsetreg(&grf->gpio2a_iomux,
+			     GRF_GPIO2A4_SEL_MASK,
+			     GRF_PWM_0 << GRF_GPIO2A4_SEL_SHIFT);
+		break;
+	case PERIPH_ID_PWM1:
+		rk_clrsetreg(&grf->gpio2a_iomux,
+			     GRF_GPIO2A5_SEL_MASK,
+			     GRF_PWM_1 << GRF_GPIO2A5_SEL_SHIFT);
+		break;
+	case PERIPH_ID_PWM2:
+		rk_clrsetreg(&grf->gpio2a_iomux,
+			     GRF_GPIO2A6_SEL_MASK,
+			     GRF_PWM_2 << GRF_GPIO2A6_SEL_SHIFT);
+		break;
+	case PERIPH_ID_PWM3:
+		rk_clrsetreg(&grf->gpio2a_iomux,
+			     GRF_GPIO2A2_SEL_MASK,
+			     GRF_PWM_IR << GRF_GPIO2A2_SEL_SHIFT);
+		break;
+	default:
+		debug("pwm id = %d iomux error!\n", pwm_id);
+		break;
+	}
+}
+
+static void pinctrl_rk3328_i2c_config(struct rk3328_grf_regs *grf, int i2c_id)
+{
+	switch (i2c_id) {
+	case PERIPH_ID_I2C0:
+		rk_clrsetreg(&grf->gpio2d_iomux,
+			     GRF_GPIO2D0_SEL_MASK | GRF_GPIO2D1_SEL_MASK,
+			     GRF_I2C0_SCL << GRF_GPIO2D0_SEL_SHIFT
+			     | GRF_I2C0_SDA << GRF_GPIO2D1_SEL_SHIFT);
+		break;
+	case PERIPH_ID_I2C1:
+		rk_clrsetreg(&grf->gpio2a_iomux,
+			     GRF_GPIO2A4_SEL_MASK | GRF_GPIO2A5_SEL_MASK,
+			     GRF_I2C1_SCL << GRF_GPIO2A5_SEL_SHIFT
+			     | GRF_I2C1_SDA << GRF_GPIO2A4_SEL_SHIFT);
+		break;
+	case PERIPH_ID_I2C2:
+		rk_clrsetreg(&grf->gpio2bl_iomux,
+			     GRF_GPIO2BL5_SEL_MASK | GRF_GPIO2BL6_SEL_MASK,
+			     GRF_I2C2_SCL << GRF_GPIO2BL6_SEL_SHIFT
+			     | GRF_I2C2_SDA << GRF_GPIO2BL6_SEL_SHIFT);
+		break;
+	case PERIPH_ID_I2C3:
+		rk_clrsetreg(&grf->gpio0a_iomux,
+			     GRF_GPIO0A5_SEL_MASK | GRF_GPIO0A6_SEL_MASK,
+			     GRF_I2C3_SCL << GRF_GPIO0A5_SEL_SHIFT
+			     | GRF_I2C3_SDA << GRF_GPIO0A6_SEL_SHIFT);
+		break;
+	default:
+		debug("i2c id = %d iomux error!\n", i2c_id);
+		break;
+	}
+}
+
+static void pinctrl_rk3328_lcdc_config(struct rk3328_grf_regs *grf, int lcd_id)
+{
+	switch (lcd_id) {
+	case PERIPH_ID_LCDC0:
+		break;
+	default:
+		debug("lcdc id = %d iomux error!\n", lcd_id);
+		break;
+	}
+}
+
+static int pinctrl_rk3328_spi_config(struct rk3328_grf_regs *grf,
+				     enum periph_id spi_id, int cs)
+{
+	rk_clrsetreg(&grf->com_iomux,
+		     GRF_SPI_IOMUX_SEL_MASK,
+		     GRF_SPI_IOMUX_SEL_M0 << GRF_SPI_IOMUX_SEL_SHIFT);
+
+	switch (spi_id) {
+	case PERIPH_ID_SPI0:
+		switch (cs) {
+		case 0:
+			rk_clrsetreg(&grf->gpio2bl_iomux,
+				     GRF_GPIO2BL3_SEL_MASK,
+				     GRF_SPI_CSN0_M0 << GRF_GPIO2BL3_SEL_SHIFT);
+			break;
+		case 1:
+			rk_clrsetreg(&grf->gpio2bl_iomux,
+				     GRF_GPIO2BL4_SEL_MASK,
+				     GRF_SPI_CSN1_M0 << GRF_GPIO2BL4_SEL_SHIFT);
+			break;
+		default:
+			goto err;
+		}
+		rk_clrsetreg(&grf->gpio2bl_iomux,
+			     GRF_GPIO2BL0_SEL_MASK,
+			     GRF_SPI_CLK_TX_RX_M0 << GRF_GPIO2BL0_SEL_SHIFT);
+		break;
+	default:
+		goto err;
+	}
+
+	return 0;
+err:
+	debug("rkspi: periph%d cs=%d not supported", spi_id, cs);
+	return -ENOENT;
+}
+
+static void pinctrl_rk3328_uart_config(struct rk3328_grf_regs *grf, int uart_id)
+{
+	switch (uart_id) {
+	case PERIPH_ID_UART2:
+		break;
+		/* uart2 iomux select m1 */
+		rk_clrsetreg(&grf->com_iomux,
+			     GRF_UART2_IOMUX_SEL_MASK,
+			     GRF_UART2_IOMUX_SEL_M1
+			     << GRF_UART2_IOMUX_SEL_SHIFT);
+		rk_clrsetreg(&grf->gpio2a_iomux,
+			     GRF_GPIO2A0_SEL_MASK | GRF_GPIO2A1_SEL_MASK,
+			     GRF_UART2_TX_M1 << GRF_GPIO2A0_SEL_SHIFT |
+			     GRF_UART2_RX_M1 << GRF_GPIO2A1_SEL_SHIFT);
+		break;
+	case PERIPH_ID_UART0:
+	case PERIPH_ID_UART1:
+	case PERIPH_ID_UART3:
+	case PERIPH_ID_UART4:
+	default:
+		debug("uart id = %d iomux error!\n", uart_id);
+		break;
+	}
+}
+
+static void pinctrl_rk3328_sdmmc_config(struct rk3328_grf_regs *grf,
+					int mmc_id)
+{
+	switch (mmc_id) {
+	case PERIPH_ID_EMMC:
+		rk_clrsetreg(&grf->gpio0a_iomux,
+			     GRF_GPIO0A7_SEL_MASK,
+			     GRF_EMMC_DATA0 << GRF_GPIO0A7_SEL_SHIFT);
+		rk_clrsetreg(&grf->gpio2d_iomux,
+			     GRF_GPIO2D4_SEL_MASK,
+			     GRF_EMMC_DATA123 << GRF_GPIO2D4_SEL_SHIFT);
+		rk_clrsetreg(&grf->gpio3c_iomux,
+			     GRF_GPIO3C0_SEL_MASK,
+			     GRF_EMMC_DATA567_PWR_CLK_RSTN_CMD
+			     << GRF_GPIO3C0_SEL_SHIFT);
+		break;
+	case PERIPH_ID_SDCARD:
+		/* sdcard iomux select m0 */
+		rk_clrsetreg(&grf->com_iomux,
+			     GRF_CARD_IOMUX_SEL_MASK,
+			     GRF_CARD_IOMUX_SEL_M0 << GRF_CARD_IOMUX_SEL_SHIFT);
+		rk_clrsetreg(&grf->gpio2a_iomux,
+			     GRF_GPIO2A7_SEL_MASK,
+			     GRF_CARD_PWR_EN_M0 << GRF_GPIO2A7_SEL_SHIFT);
+		rk_clrsetreg(&grf->gpio1a_iomux,
+			     GRF_GPIO1A0_SEL_MASK,
+			     GRF_CARD_DATA_CLK_CMD_DETN
+			     << GRF_GPIO1A0_SEL_SHIFT);
+		break;
+	default:
+		debug("mmc id = %d iomux error!\n", mmc_id);
+		break;
+	}
+}
+
+static int rk3328_pinctrl_request(struct udevice *dev, int func, int flags)
+{
+	struct rk3328_pinctrl_priv *priv = dev_get_priv(dev);
+
+	debug("%s: func=%x, flags=%x\n", __func__, func, flags);
+	switch (func) {
+	case PERIPH_ID_PWM0:
+	case PERIPH_ID_PWM1:
+	case PERIPH_ID_PWM2:
+	case PERIPH_ID_PWM3:
+		pinctrl_rk3328_pwm_config(priv->grf, func);
+		break;
+	case PERIPH_ID_I2C0:
+	case PERIPH_ID_I2C1:
+	case PERIPH_ID_I2C2:
+	case PERIPH_ID_I2C3:
+		pinctrl_rk3328_i2c_config(priv->grf, func);
+		break;
+	case PERIPH_ID_SPI0:
+		pinctrl_rk3328_spi_config(priv->grf, func, flags);
+		break;
+	case PERIPH_ID_UART0:
+	case PERIPH_ID_UART1:
+	case PERIPH_ID_UART2:
+	case PERIPH_ID_UART3:
+	case PERIPH_ID_UART4:
+		pinctrl_rk3328_uart_config(priv->grf, func);
+		break;
+	case PERIPH_ID_LCDC0:
+	case PERIPH_ID_LCDC1:
+		pinctrl_rk3328_lcdc_config(priv->grf, func);
+		break;
+	case PERIPH_ID_SDMMC0:
+	case PERIPH_ID_SDMMC1:
+		pinctrl_rk3328_sdmmc_config(priv->grf, func);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rk3328_pinctrl_get_periph_id(struct udevice *dev,
+					struct udevice *periph)
+{
+	u32 cell[3];
+	int ret;
+
+	ret = fdtdec_get_int_array(gd->fdt_blob, periph->of_offset,
+				   "interrupts", cell, ARRAY_SIZE(cell));
+	if (ret < 0)
+		return -EINVAL;
+
+	switch (cell[1]) {
+	case 49:
+		return PERIPH_ID_SPI0;
+	case 50:
+		return PERIPH_ID_PWM0;
+	case 36:
+		return PERIPH_ID_I2C0;
+	case 37: /* Note strange order */
+		return PERIPH_ID_I2C1;
+	case 38:
+		return PERIPH_ID_I2C2;
+	case 39:
+		return PERIPH_ID_I2C3;
+	case 12:
+		return PERIPH_ID_SDCARD;
+	case 14:
+		return PERIPH_ID_EMMC;
+	}
+
+	return -ENOENT;
+}
+
+static int rk3328_pinctrl_set_state_simple(struct udevice *dev,
+					   struct udevice *periph)
+{
+	int func;
+
+	func = rk3328_pinctrl_get_periph_id(dev, periph);
+	if (func < 0)
+		return func;
+
+	return rk3328_pinctrl_request(dev, func, 0);
+}
+
+static struct pinctrl_ops rk3328_pinctrl_ops = {
+	.set_state_simple	= rk3328_pinctrl_set_state_simple,
+	.request	= rk3328_pinctrl_request,
+	.get_periph_id	= rk3328_pinctrl_get_periph_id,
+};
+
+static int rk3328_pinctrl_probe(struct udevice *dev)
+{
+	struct rk3328_pinctrl_priv *priv = dev_get_priv(dev);
+	int ret = 0;
+
+	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+	debug("%s: grf=%p\n", __func__, priv->grf);
+
+	return ret;
+}
+
+static const struct udevice_id rk3328_pinctrl_ids[] = {
+	{ .compatible = "rockchip,rk3328-pinctrl" },
+	{ }
+};
+
+U_BOOT_DRIVER(pinctrl_rk3328) = {
+	.name		= "rockchip_rk3328_pinctrl",
+	.id		= UCLASS_PINCTRL,
+	.of_match	= rk3328_pinctrl_ids,
+	.priv_auto_alloc_size = sizeof(struct rk3328_pinctrl_priv),
+	.ops		= &rk3328_pinctrl_ops,
+	.bind		= dm_scan_fdt_dev,
+	.probe		= rk3328_pinctrl_probe,
+};
diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3399.c b/drivers/pinctrl/rockchip/pinctrl_rk3399.c
index da30154..a74793a 100644
--- a/drivers/pinctrl/rockchip/pinctrl_rk3399.c
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3399.c
@@ -22,112 +22,6 @@
 	struct rk3399_pmugrf_regs *pmugrf;
 };
 
-enum {
-	/* GRF_GPIO2B_IOMUX */
-	GRF_GPIO2B1_SEL_SHIFT	= 0,
-	GRF_GPIO2B1_SEL_MASK	= 3 << GRF_GPIO2B1_SEL_SHIFT,
-	GRF_SPI2TPM_RXD		= 1,
-	GRF_GPIO2B2_SEL_SHIFT	= 2,
-	GRF_GPIO2B2_SEL_MASK	= 3 << GRF_GPIO2B2_SEL_SHIFT,
-	GRF_SPI2TPM_TXD		= 1,
-	GRF_GPIO2B3_SEL_SHIFT	= 6,
-	GRF_GPIO2B3_SEL_MASK	= 3 << GRF_GPIO2B3_SEL_SHIFT,
-	GRF_SPI2TPM_CLK		= 1,
-	GRF_GPIO2B4_SEL_SHIFT	= 8,
-	GRF_GPIO2B4_SEL_MASK	= 3 << GRF_GPIO2B4_SEL_SHIFT,
-	GRF_SPI2TPM_CSN0	= 1,
-
-	/* GRF_GPIO3A_IOMUX */
-	GRF_GPIO3A4_SEL_SHIFT	= 8,
-	GRF_GPIO3A4_SEL_MASK	= 3 << GRF_GPIO3A4_SEL_SHIFT,
-	GRF_SPI0NORCODEC_RXD	= 2,
-	GRF_GPIO3A5_SEL_SHIFT	= 10,
-	GRF_GPIO3A5_SEL_MASK	= 3 << GRF_GPIO3A5_SEL_SHIFT,
-	GRF_SPI0NORCODEC_TXD	= 2,
-	GRF_GPIO3A6_SEL_SHIFT	= 12,
-	GRF_GPIO3A6_SEL_MASK	= 3 << GRF_GPIO3A6_SEL_SHIFT,
-	GRF_SPI0NORCODEC_CLK	= 2,
-	GRF_GPIO3A7_SEL_SHIFT	= 14,
-	GRF_GPIO3A7_SEL_MASK	= 3 << GRF_GPIO3A7_SEL_SHIFT,
-	GRF_SPI0NORCODEC_CSN0	= 2,
-
-	/* GRF_GPIO3B_IOMUX */
-	GRF_GPIO3B0_SEL_SHIFT	= 0,
-	GRF_GPIO3B0_SEL_MASK	= 3 << GRF_GPIO3B0_SEL_SHIFT,
-	GRF_SPI0NORCODEC_CSN1	= 2,
-
-	/* GRF_GPIO4B_IOMUX */
-	GRF_GPIO4B0_SEL_SHIFT	= 0,
-	GRF_GPIO4B0_SEL_MASK	= 3 << GRF_GPIO4B0_SEL_SHIFT,
-	GRF_SDMMC_DATA0		= 1,
-	GRF_UART2DBGA_SIN	= 2,
-	GRF_GPIO4B1_SEL_SHIFT	= 2,
-	GRF_GPIO4B1_SEL_MASK	= 3 << GRF_GPIO4B1_SEL_SHIFT,
-	GRF_SDMMC_DATA1		= 1,
-	GRF_UART2DBGA_SOUT	= 2,
-	GRF_GPIO4B2_SEL_SHIFT	= 4,
-	GRF_GPIO4B2_SEL_MASK	= 3 << GRF_GPIO4B2_SEL_SHIFT,
-	GRF_SDMMC_DATA2		= 1,
-	GRF_GPIO4B3_SEL_SHIFT	= 6,
-	GRF_GPIO4B3_SEL_MASK	= 3 << GRF_GPIO4B3_SEL_SHIFT,
-	GRF_SDMMC_DATA3		= 1,
-	GRF_GPIO4B4_SEL_SHIFT	= 8,
-	GRF_GPIO4B4_SEL_MASK	= 3 << GRF_GPIO4B4_SEL_SHIFT,
-	GRF_SDMMC_CLKOUT	= 1,
-	GRF_GPIO4B5_SEL_SHIFT	= 10,
-	GRF_GPIO4B5_SEL_MASK	= 3 << GRF_GPIO4B5_SEL_SHIFT,
-	GRF_SDMMC_CMD		= 1,
-
-	/* GRF_GPIO4C_IOMUX */
-	GRF_GPIO4C2_SEL_SHIFT	= 4,
-	GRF_GPIO4C2_SEL_MASK	= 3 << GRF_GPIO4C2_SEL_SHIFT,
-	GRF_PWM_0		= 1,
-	GRF_GPIO4C3_SEL_SHIFT	= 6,
-	GRF_GPIO4C3_SEL_MASK	= 3 << GRF_GPIO4C3_SEL_SHIFT,
-	GRF_UART2DGBC_SIN	= 1,
-	GRF_GPIO4C4_SEL_SHIFT	= 8,
-	GRF_GPIO4C4_SEL_MASK	= 3 << GRF_GPIO4C4_SEL_SHIFT,
-	GRF_UART2DBGC_SOUT	= 1,
-	GRF_GPIO4C6_SEL_SHIFT	= 12,
-	GRF_GPIO4C6_SEL_MASK	= 3 << GRF_GPIO4C6_SEL_SHIFT,
-	GRF_PWM_1		= 1,
-
-	/* PMUGRF_GPIO0A_IOMUX */
-	PMUGRF_GPIO0A6_SEL_SHIFT	= 12,
-	PMUGRF_GPIO0A6_SEL_MASK	= 3 << PMUGRF_GPIO0A6_SEL_SHIFT,
-	PMUGRF_PWM_3A		= 1,
-
-	/* PMUGRF_GPIO1A_IOMUX */
-	PMUGRF_GPIO1A7_SEL_SHIFT	= 14,
-	PMUGRF_GPIO1A7_SEL_MASK	= 3 << PMUGRF_GPIO1A7_SEL_SHIFT,
-	PMUGRF_SPI1EC_RXD	= 2,
-
-	/* PMUGRF_GPIO1B_IOMUX */
-	PMUGRF_GPIO1B0_SEL_SHIFT	= 0,
-	PMUGRF_GPIO1B0_SEL_MASK = 3 << PMUGRF_GPIO1B0_SEL_SHIFT,
-	PMUGRF_SPI1EC_TXD	= 2,
-	PMUGRF_GPIO1B1_SEL_SHIFT	= 2,
-	PMUGRF_GPIO1B1_SEL_MASK = 3 << PMUGRF_GPIO1B1_SEL_SHIFT,
-	PMUGRF_SPI1EC_CLK	= 2,
-	PMUGRF_GPIO1B2_SEL_SHIFT	= 4,
-	PMUGRF_GPIO1B2_SEL_MASK = 3 << PMUGRF_GPIO1B2_SEL_SHIFT,
-	PMUGRF_SPI1EC_CSN0	= 2,
-	PMUGRF_GPIO1B6_SEL_SHIFT	= 12,
-	PMUGRF_GPIO1B6_SEL_MASK	= 3 << PMUGRF_GPIO1B6_SEL_SHIFT,
-	PMUGRF_PWM_3B		= 1,
-	PMUGRF_GPIO1B7_SEL_SHIFT	= 14,
-	PMUGRF_GPIO1B7_SEL_MASK	= 3 << PMUGRF_GPIO1B7_SEL_SHIFT,
-	PMUGRF_I2C0PMU_SDA	= 2,
-
-	/* PMUGRF_GPIO1C_IOMUX */
-	PMUGRF_GPIO1C0_SEL_SHIFT	= 0,
-	PMUGRF_GPIO1C0_SEL_MASK	= 3 << PMUGRF_GPIO1C0_SEL_SHIFT,
-	PMUGRF_I2C0PMU_SCL	= 2,
-	PMUGRF_GPIO1C3_SEL_SHIFT	= 6,
-	PMUGRF_GPIO1C3_SEL_MASK	= 3 << PMUGRF_GPIO1C3_SEL_SHIFT,
-	PMUGRF_PWM_2		= 1,
-
-};
 static void pinctrl_rk3399_pwm_config(struct rk3399_grf_regs *grf,
 		struct rk3399_pmugrf_regs *pmugrf, int pwm_id)
 {
@@ -359,6 +253,7 @@
 static int rk3399_pinctrl_get_periph_id(struct udevice *dev,
 					struct udevice *periph)
 {
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 	u32 cell[3];
 	int ret;
 
@@ -389,7 +284,7 @@
 	case 65:
 		return PERIPH_ID_SDMMC1;
 	}
-
+#endif
 	return -ENOENT;
 }
 
@@ -434,6 +329,8 @@
 	.of_match	= rk3399_pinctrl_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3399_pinctrl_priv),
 	.ops		= &rk3399_pinctrl_ops,
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 	.bind		= dm_scan_fdt_dev,
+#endif
 	.probe		= rk3399_pinctrl_probe,
 };
diff --git a/drivers/serial/serial_rockchip.c b/drivers/serial/serial_rockchip.c
index c06afc5..734cee2 100644
--- a/drivers/serial/serial_rockchip.c
+++ b/drivers/serial/serial_rockchip.c
@@ -12,12 +12,19 @@
 #include <serial.h>
 #include <asm/arch/clock.h>
 
+#if defined(CONFIG_ROCKCHIP_RK3188)
+struct rockchip_uart_platdata {
+	struct dtd_rockchip_rk3188_uart dtplat;
+	struct ns16550_platdata plat;
+};
+struct dtd_rockchip_rk3188_uart *dtplat, s_dtplat;
+#elif defined(CONFIG_ROCKCHIP_RK3288)
 struct rockchip_uart_platdata {
 	struct dtd_rockchip_rk3288_uart dtplat;
 	struct ns16550_platdata plat;
 };
-
 struct dtd_rockchip_rk3288_uart *dtplat, s_dtplat;
+#endif
 
 static int rockchip_serial_probe(struct udevice *dev)
 {
@@ -33,6 +40,16 @@
 	return ns16550_serial_probe(dev);
 }
 
+U_BOOT_DRIVER(rockchip_rk3188_uart) = {
+	.name	= "rockchip_rk3188_uart",
+	.id	= UCLASS_SERIAL,
+	.priv_auto_alloc_size = sizeof(struct NS16550),
+	.platdata_auto_alloc_size = sizeof(struct rockchip_uart_platdata),
+	.probe	= rockchip_serial_probe,
+	.ops	= &ns16550_serial_ops,
+	.flags	= DM_FLAG_PRE_RELOC,
+};
+
 U_BOOT_DRIVER(rockchip_rk3288_uart) = {
 	.name	= "rockchip_rk3288_uart",
 	.id	= UCLASS_SERIAL,
diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile
index 21bcc21..49b8bb6 100644
--- a/drivers/sysreset/Makefile
+++ b/drivers/sysreset/Makefile
@@ -9,7 +9,9 @@
 ifndef CONFIG_SPL_BUILD
 obj-$(CONFIG_ROCKCHIP_RK3036) += sysreset_rk3036.o
 endif
+obj-$(CONFIG_ROCKCHIP_RK3188) += sysreset_rk3188.o
 obj-$(CONFIG_ROCKCHIP_RK3288) += sysreset_rk3288.o
+obj-$(CONFIG_ROCKCHIP_RK3328) += sysreset_rk3328.o
 obj-$(CONFIG_ROCKCHIP_RK3399) += sysreset_rk3399.o
 obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o
 obj-$(CONFIG_ARCH_SNAPDRAGON) += sysreset_snapdragon.o
diff --git a/drivers/sysreset/sysreset_rk3188.c b/drivers/sysreset/sysreset_rk3188.c
new file mode 100644
index 0000000..36ae476
--- /dev/null
+++ b/drivers/sysreset/sysreset_rk3188.c
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <sysreset.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3188.h>
+#include <asm/arch/hardware.h>
+#include <linux/err.h>
+
+int rk3188_sysreset_request(struct udevice *dev, enum sysreset_t type)
+{
+	struct rk3188_cru *cru = rockchip_get_cru();
+
+	if (IS_ERR(cru))
+		return PTR_ERR(cru);
+	switch (type) {
+	case SYSRESET_WARM:
+		rk_clrreg(&cru->cru_mode_con, 0xffff);
+		writel(0xeca8, &cru->cru_glb_srst_snd_value);
+		break;
+	case SYSRESET_COLD:
+		rk_clrreg(&cru->cru_mode_con, 0xffff);
+		writel(0xfdb9, &cru->cru_glb_srst_fst_value);
+		break;
+	default:
+		return -EPROTONOSUPPORT;
+	}
+
+	return -EINPROGRESS;
+}
+
+static struct sysreset_ops rk3188_sysreset = {
+	.request	= rk3188_sysreset_request,
+};
+
+U_BOOT_DRIVER(sysreset_rk3188) = {
+	.name	= "rk3188_sysreset",
+	.id	= UCLASS_SYSRESET,
+	.ops	= &rk3188_sysreset,
+};
diff --git a/drivers/sysreset/sysreset_rk3328.c b/drivers/sysreset/sysreset_rk3328.c
new file mode 100644
index 0000000..7b9af09
--- /dev/null
+++ b/drivers/sysreset/sysreset_rk3328.c
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <sysreset.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3328.h>
+#include <asm/arch/hardware.h>
+#include <asm/io.h>
+#include <linux/err.h>
+
+int rk3328_sysreset_request(struct udevice *dev, enum sysreset_t type)
+{
+	struct rk3328_cru *cru = rockchip_get_cru();
+
+	if (IS_ERR(cru))
+		return PTR_ERR(cru);
+	switch (type) {
+	case SYSRESET_WARM:
+		writel(0xeca8, &cru->glb_srst_snd_value);
+		break;
+	case SYSRESET_COLD:
+		writel(0xfdb9, &cru->glb_srst_fst_value);
+		break;
+	default:
+		return -EPROTONOSUPPORT;
+	}
+
+	return -EINPROGRESS;
+}
+
+static struct sysreset_ops rk3328_sysreset = {
+	.request	= rk3328_sysreset_request,
+};
+
+U_BOOT_DRIVER(sysreset_rk3328) = {
+	.name	= "rk3328_sysreset",
+	.id	= UCLASS_SYSRESET,
+	.ops	= &rk3328_sysreset,
+};
diff --git a/drivers/video/rockchip/rk_hdmi.c b/drivers/video/rockchip/rk_hdmi.c
index 7b0c43b..c8608db 100644
--- a/drivers/video/rockchip/rk_hdmi.c
+++ b/drivers/video/rockchip/rk_hdmi.c
@@ -32,37 +32,37 @@
 
 static const struct tmds_n_cts n_cts_table[] = {
 	{
-		.tmds = 25175, .n = 6144, .cts = 25175,
+		.tmds = 25175000, .n = 6144, .cts = 25175,
 	}, {
-		.tmds = 25200, .n = 6144, .cts = 25200,
+		.tmds = 25200000, .n = 6144, .cts = 25200,
 	}, {
-		.tmds = 27000, .n = 6144, .cts = 27000,
+		.tmds = 27000000, .n = 6144, .cts = 27000,
 	}, {
-		.tmds = 27027, .n = 6144, .cts = 27027,
+		.tmds = 27027000, .n = 6144, .cts = 27027,
 	}, {
-		.tmds = 40000, .n = 6144, .cts = 40000,
+		.tmds = 40000000, .n = 6144, .cts = 40000,
 	}, {
-		.tmds = 54000, .n = 6144, .cts = 54000,
+		.tmds = 54000000, .n = 6144, .cts = 54000,
 	}, {
-		.tmds = 54054, .n = 6144, .cts = 54054,
+		.tmds = 54054000, .n = 6144, .cts = 54054,
 	}, {
-		.tmds = 65000, .n = 6144, .cts = 65000,
+		.tmds = 65000000, .n = 6144, .cts = 65000,
 	}, {
-		.tmds = 74176, .n = 11648, .cts = 140625,
+		.tmds = 74176000, .n = 11648, .cts = 140625,
 	}, {
-		.tmds = 74250, .n = 6144, .cts = 74250,
+		.tmds = 74250000, .n = 6144, .cts = 74250,
 	}, {
-		.tmds = 83500, .n = 6144, .cts = 83500,
+		.tmds = 83500000, .n = 6144, .cts = 83500,
 	}, {
-		.tmds = 106500, .n = 6144, .cts = 106500,
+		.tmds = 106500000, .n = 6144, .cts = 106500,
 	}, {
-		.tmds = 108000, .n = 6144, .cts = 108000,
+		.tmds = 108000000, .n = 6144, .cts = 108000,
 	}, {
-		.tmds = 148352, .n = 5824, .cts = 140625,
+		.tmds = 148352000, .n = 5824, .cts = 140625,
 	}, {
-		.tmds = 148500, .n = 6144, .cts = 148500,
+		.tmds = 148500000, .n = 6144, .cts = 148500,
 	}, {
-		.tmds = 297000, .n = 5120, .cts = 247500,
+		.tmds = 297000000, .n = 5120, .cts = 247500,
 	}
 };
 
@@ -124,12 +124,6 @@
 	}
 };
 
-static const u32 csc_coeff_default[3][4] = {
-	{ 0x2000, 0x0000, 0x0000, 0x0000 },
-	{ 0x0000, 0x2000, 0x0000, 0x0000 },
-	{ 0x0000, 0x0000, 0x2000, 0x0000 }
-};
-
 static void hdmi_set_clock_regenerator(struct rk3288_hdmi *regs, u32 n, u32 cts)
 {
 	uint cts3;
@@ -220,37 +214,6 @@
 	writel(0x0, &regs->tx_bcbdata1);
 }
 
-static void hdmi_update_csc_coeffs(struct rk3288_hdmi *regs)
-{
-	u32 i, j;
-	u32 csc_scale = 1;
-
-	/* the csc registers are sequential, alternating msb then lsb */
-	for (i = 0; i < ARRAY_SIZE(csc_coeff_default); i++) {
-		for (j = 0; j < ARRAY_SIZE(csc_coeff_default[0]); j++) {
-			u32 coeff = csc_coeff_default[i][j];
-			writel(coeff >> 8, &regs->csc_coef[i][j].msb);
-			writel(coeff && 0xff, &regs->csc_coef[i][j].lsb);
-		}
-	}
-
-	clrsetbits_le32(&regs->csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
-			csc_scale);
-}
-
-static void hdmi_video_csc(struct rk3288_hdmi *regs)
-{
-	u32 color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
-	u32 interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
-
-	/* configure the csc registers */
-	writel(interpolation, &regs->csc_cfg);
-	clrsetbits_le32(&regs->csc_scale,
-			HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, color_depth);
-
-	hdmi_update_csc_coeffs(regs);
-}
-
 static void hdmi_video_packetize(struct rk3288_hdmi *regs)
 {
 	u32 output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
@@ -467,7 +430,6 @@
 		hdmi_phy_enable_tmds(regs, 0);
 		hdmi_phy_enable_power(regs, 0);
 
-		/* enable csc */
 		ret = hdmi_phy_configure(regs, mpixelclock);
 		if (ret) {
 			debug("hdmi phy config failure %d\n", ret);
@@ -837,7 +799,6 @@
 	hdmi_audio_set_samplerate(regs, edid->pixelclock.typ);
 
 	hdmi_video_packetize(regs);
-	hdmi_video_csc(regs);
 	hdmi_video_sample(regs);
 
 	hdmi_clear_overflow(regs);
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index e02863d..5b356dd 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -127,5 +127,6 @@
 #define GD_FLG_SKIP_RELOC	0x00800	/* Don't relocate		   */
 #define GD_FLG_RECORD		0x01000	/* Record console		   */
 #define GD_FLG_ENV_DEFAULT	0x02000 /* Default variable flag	   */
+#define GD_FLG_SPL_EARLY_INIT	0x04000 /* Early SPL init is done	   */
 
 #endif /* __ASM_GENERIC_GBL_DATA_H */
diff --git a/include/configs/evb_rk3328.h b/include/configs/evb_rk3328.h
new file mode 100644
index 0000000..3a39a1b
--- /dev/null
+++ b/include/configs/evb_rk3328.h
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __EVB_RK3328_H
+#define __EVB_RK3328_H
+
+#include <configs/rk3328_common.h>
+
+#define CONFIG_ENV_IS_IN_MMC
+#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)
+
+#define CONFIG_SYS_WHITE_ON_BLACK
+#define CONFIG_CONSOLE_SCROLL_LINES		10
+
+#endif
diff --git a/include/configs/rk3188_common.h b/include/configs/rk3188_common.h
new file mode 100644
index 0000000..2ff7cd7
--- /dev/null
+++ b/include/configs/rk3188_common.h
@@ -0,0 +1,120 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __CONFIG_RK3188_COMMON_H
+#define __CONFIG_RK3188_COMMON_H
+
+#define CONFIG_SYS_CACHELINE_SIZE	64
+
+#include <asm/arch/hardware.h>
+#include "rockchip-common.h"
+
+#define CONFIG_SKIP_LOWLEVEL_INIT_ONLY
+#define CONFIG_NR_DRAM_BANKS		1
+#define CONFIG_ENV_SIZE			0x2000
+#define CONFIG_SYS_MAXARGS		16
+#define CONFIG_BAUDRATE			115200
+#define CONFIG_SYS_MALLOC_LEN		(32 << 20)
+#define CONFIG_SYS_CBSIZE		1024
+#define CONFIG_SYS_THUMB_BUILD
+
+#define CONFIG_SYS_TIMER_RATE		(24 * 1000 * 1000)
+#define CONFIG_SYS_TIMER_BASE		0x2000e000 /* TIMER3 */
+#define CONFIG_SYS_TIMER_COUNTER	(CONFIG_SYS_TIMER_BASE + 8)
+#define CONFIG_SYS_TIMER_COUNTS_DOWN
+
+#define CONFIG_SYS_NS16550_MEM32
+#define CONFIG_SPL_BOARD_INIT
+
+#ifdef CONFIG_ROCKCHIP_SPL_BACK_TO_BROM
+/* Bootrom will load u-boot binary to 0x60000000 once return from SPL */
+#define CONFIG_SYS_TEXT_BASE		0x60000000
+#else
+#define CONFIG_SYS_TEXT_BASE		0x60100000
+#endif
+#define CONFIG_SYS_INIT_SP_ADDR		0x60100000
+#define CONFIG_SYS_LOAD_ADDR		0x60800800
+
+#define CONFIG_ROCKCHIP_MAX_INIT_SIZE	(0x8000 - 0x800)
+#define CONFIG_ROCKCHIP_CHIP_TAG	"RK31"
+
+#ifdef CONFIG_TPL_BUILD
+#define CONFIG_SPL_TEXT_BASE		0x10080804
+/* tpl size 1kb - 4byte RK31 header */
+#define CONFIG_SPL_MAX_SIZE		(0x400 - 0x4)
+#elif defined(CONFIG_SPL_BUILD)
+/* spl size 32kb sram - 2kb bootrom - 1kb spl */
+#define CONFIG_SPL_MAX_SIZE		(0x8000 - 0xC00)
+#define CONFIG_SPL_TEXT_BASE		0x10080C00
+#define CONFIG_SPL_FRAMEWORK		1
+#define CONFIG_SPL_CLK			1
+#define CONFIG_SPL_PINCTRL		1
+#define CONFIG_SPL_REGMAP		1
+#define CONFIG_SPL_SYSCON		1
+#define CONFIG_SPL_RAM			1
+#define CONFIG_SPL_DRIVERS_MISC_SUPPORT	1
+#define CONFIG_ROCKCHIP_SERIAL		1
+#endif
+
+#define CONFIG_SPL_STACK		0x10087fff
+
+/* MMC/SD IP block */
+#define CONFIG_BOUNCE_BUFFER
+
+#define CONFIG_FAT_WRITE
+
+#define CONFIG_SYS_SDRAM_BASE		0x60000000
+#define CONFIG_NR_DRAM_BANKS		1
+#define SDRAM_BANK_SIZE			(2UL << 30)
+
+#define CONFIG_SPI_FLASH
+#define CONFIG_SPI
+#define CONFIG_SF_DEFAULT_SPEED 20000000
+
+#ifndef CONFIG_SPL_BUILD
+/* usb otg */
+#define CONFIG_USB_GADGET
+#define CONFIG_USB_GADGET_DUALSPEED
+#define CONFIG_USB_GADGET_DWC2_OTG
+#define CONFIG_ROCKCHIP_USB2_PHY
+#define CONFIG_USB_GADGET_VBUS_DRAW	0
+
+#define CONFIG_USB_GADGET_DOWNLOAD
+#define CONFIG_G_DNL_MANUFACTURER	"Rockchip"
+#define CONFIG_G_DNL_VENDOR_NUM		0x2207
+#define CONFIG_G_DNL_PRODUCT_NUM	0x310a
+
+/* usb host support */
+#ifdef CONFIG_CMD_USB
+#define CONFIG_USB_DWC2
+#define CONFIG_USB_HOST_ETHER
+#define CONFIG_USB_ETHER_SMSC95XX
+#define CONFIG_USB_ETHER_ASIX
+#endif
+#define ENV_MEM_LAYOUT_SETTINGS \
+	"scriptaddr=0x60000000\0" \
+	"pxefile_addr_r=0x60100000\0" \
+	"fdt_addr_r=0x61f00000\0" \
+	"kernel_addr_r=0x62000000\0" \
+	"ramdisk_addr_r=0x64000000\0"
+
+#include <config_distro_bootcmd.h>
+
+/* Linux fails to load the fdt if it's loaded above 256M on a Rock board,
+ * so limit the fdt reallocation to that */
+#define CONFIG_EXTRA_ENV_SETTINGS \
+	"fdt_high=0x6fffffff\0" \
+	"initrd_high=0x6fffffff\0" \
+	"partitions=" PARTS_DEFAULT \
+	ENV_MEM_LAYOUT_SETTINGS \
+	ROCKCHIP_DEVICE_SETTINGS \
+	BOOTENV
+
+#endif /* CONFIG_SPL_BUILD */
+
+#define CONFIG_PREBOOT
+
+#endif
diff --git a/include/configs/rk3328_common.h b/include/configs/rk3328_common.h
new file mode 100644
index 0000000..eacf716
--- /dev/null
+++ b/include/configs/rk3328_common.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __CONFIG_RK3328_COMMON_H
+#define __CONFIG_RK3328_COMMON_H
+
+#include "rockchip-common.h"
+
+#define CONFIG_NR_DRAM_BANKS		1
+#define CONFIG_ENV_SIZE			0x2000
+#define CONFIG_SYS_MAXARGS		16
+#define CONFIG_BAUDRATE			1500000
+#define CONFIG_SYS_MALLOC_LEN		(32 << 20)
+#define CONFIG_SYS_CBSIZE		1024
+#define CONFIG_SKIP_LOWLEVEL_INIT
+
+#define CONFIG_SYS_NS16550_MEM32
+
+#define CONFIG_SYS_TEXT_BASE		0x00200000
+#define CONFIG_SYS_INIT_SP_ADDR		0x00300000
+#define CONFIG_SYS_LOAD_ADDR		0x00800800
+
+#define CONFIG_SYS_BOOTM_LEN	(64 << 20)	/* 64M */
+
+/* MMC/SD IP block */
+#define CONFIG_BOUNCE_BUFFER
+
+#define CONFIG_SUPPORT_VFAT
+#define CONFIG_FS_FAT
+#define CONFIG_FAT_WRITE
+#define CONFIG_FS_EXT4
+
+/* RAW SD card / eMMC locations. */
+#define CONFIG_SYS_SPI_U_BOOT_OFFS	(128 << 10)
+
+/* FAT sd card locations. */
+#define CONFIG_SYS_MMCSD_FS_BOOT_PARTITION	1
+#define CONFIG_SYS_SDRAM_BASE		0
+#define CONFIG_NR_DRAM_BANKS		1
+
+#define CONFIG_SPI_FLASH
+#define CONFIG_SPI
+#define CONFIG_SF_DEFAULT_SPEED 20000000
+
+#ifndef CONFIG_SPL_BUILD
+
+#define ENV_MEM_LAYOUT_SETTINGS \
+	"scriptaddr=0x00500000\0" \
+	"pxefile_addr_r=0x00600000\0" \
+	"fdt_addr_r=0x01f00000\0" \
+	"kernel_addr_r=0x02000000\0" \
+	"ramdisk_addr_r=0x04000000\0"
+
+#include <config_distro_bootcmd.h>
+#define CONFIG_EXTRA_ENV_SETTINGS \
+	ENV_MEM_LAYOUT_SETTINGS \
+	"partitions=" PARTS_DEFAULT \
+	BOOTENV
+
+#endif
+
+#endif
diff --git a/include/configs/rk3399_common.h b/include/configs/rk3399_common.h
index ce64476..eb38376 100644
--- a/include/configs/rk3399_common.h
+++ b/include/configs/rk3399_common.h
@@ -16,12 +16,23 @@
 #define CONFIG_SYS_MALLOC_LEN		(32 << 20)
 #define CONFIG_SYS_CBSIZE		1024
 #define CONFIG_SKIP_LOWLEVEL_INIT
+#define CONFIG_SPL_FRAMEWORK
+#define CONFIG_SPL_DRIVERS_MISC_SUPPORT
+#define CONFIG_SPL_LIBCOMMON_SUPPORT
+#define CONFIG_SPL_LIBGENERIC_SUPPORT
+#define CONFIG_SPL_SERIAL_SUPPORT
 
 #define CONFIG_SYS_NS16550_MEM32
 
 #define CONFIG_SYS_TEXT_BASE		0x00200000
 #define CONFIG_SYS_INIT_SP_ADDR		0x00300000
 #define CONFIG_SYS_LOAD_ADDR		0x00800800
+#define CONFIG_SPL_STACK		0xff8effff
+#define CONFIG_SPL_TEXT_BASE		0xff8c2008
+#define CONFIG_SPL_MAX_SIZE		0x30000
+/*  BSS setup */
+#define CONFIG_SPL_BSS_START_ADDR       0xff8e0000
+#define CONFIG_SPL_BSS_MAX_SIZE         0x10000
 
 #define CONFIG_SYS_BOOTM_LEN	(64 << 20)	/* 64M */
 
diff --git a/include/configs/tinker_rk3288.h b/include/configs/tinker_rk3288.h
index 860eb40..c398e07 100644
--- a/include/configs/tinker_rk3288.h
+++ b/include/configs/tinker_rk3288.h
@@ -13,7 +13,7 @@
 #undef BOOT_TARGET_DEVICES
 
 #define BOOT_TARGET_DEVICES(func) \
-	func(MMC, mmc, 0)
+	func(MMC, mmc, 1)
 
 #define CONFIG_ENV_IS_IN_MMC
 #define CONFIG_SYS_MMC_ENV_DEV 0
diff --git a/include/dm/util.h b/include/dm/util.h
index 15daa3d..45529ce 100644
--- a/include/dm/util.h
+++ b/include/dm/util.h
@@ -48,4 +48,30 @@
 }
 #endif
 
+/**
+ * Check if a dt node should be or was bound before relocation.
+ *
+ * Devicetree nodes can be marked as needed to be bound
+ * in the loader stages via special devicetree properties.
+ *
+ * Before relocation this function can be used to check if nodes
+ * are required in either SPL or TPL stages.
+ *
+ * After relocation and jumping into the real U-Boot binary
+ * it is possible to determine if a node was bound in one of
+ * SPL/TPL stages.
+ *
+ * There are 3 settings currently in use
+ * -
+ * - u-boot,dm-pre-reloc: legacy and indicates any of TPL or SPL
+ *   Existing platforms only use it to indicate nodes needee in
+ *   SPL. Should probably be replaced by u-boot,dm-spl for
+ *   existing platforms.
+ * @blob: devicetree
+ * @offset: node offset
+ *
+ * Returns true if node is needed in SPL/TL, false otherwise.
+ */
+bool dm_fdt_pre_reloc(const void *blob, int offset);
+
 #endif
diff --git a/include/dt-bindings/clock/rk3066a-cru.h b/include/dt-bindings/clock/rk3066a-cru.h
new file mode 100644
index 0000000..549ae6a
--- /dev/null
+++ b/include/dt-bindings/clock/rk3066a-cru.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RK3066A_H
+#define _DT_BINDINGS_CLK_ROCKCHIP_RK3066A_H
+
+#include <dt-bindings/clock/rk3188-cru-common.h>
+
+/* soft-reset indices */
+#define SRST_SRST1		0
+#define SRST_SRST2		1
+
+#define SRST_L2MEM		18
+#define SRST_I2S0		23
+#define SRST_I2S1		24
+#define SRST_I2S2		25
+#define SRST_TIMER2		29
+
+#define SRST_GPIO4		36
+#define SRST_GPIO6		38
+
+#define SRST_TSADC		92
+
+#define SRST_HDMI		96
+#define SRST_HDMI_APB		97
+#define SRST_CIF1		111
+
+#endif
diff --git a/include/dt-bindings/clock/rk3188-cru-common.h b/include/dt-bindings/clock/rk3188-cru-common.h
new file mode 100644
index 0000000..8fc818b
--- /dev/null
+++ b/include/dt-bindings/clock/rk3188-cru-common.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RK3188_COMMON_H
+#define _DT_BINDINGS_CLK_ROCKCHIP_RK3188_COMMON_H
+
+/* core clocks from */
+#define PLL_APLL		1
+#define PLL_DPLL		2
+#define PLL_CPLL		3
+#define PLL_GPLL		4
+#define CORE_PERI		5
+#define CORE_L2C		6
+#define ARMCLK			7
+
+/* sclk gates (special clocks) */
+#define SCLK_UART0		64
+#define SCLK_UART1		65
+#define SCLK_UART2		66
+#define SCLK_UART3		67
+#define SCLK_MAC		68
+#define SCLK_SPI0		69
+#define SCLK_SPI1		70
+#define SCLK_SARADC		71
+#define SCLK_SDMMC		72
+#define SCLK_SDIO		73
+#define SCLK_EMMC		74
+#define SCLK_I2S0		75
+#define SCLK_I2S1		76
+#define SCLK_I2S2		77
+#define SCLK_SPDIF		78
+#define SCLK_CIF0		79
+#define SCLK_CIF1		80
+#define SCLK_OTGPHY0		81
+#define SCLK_OTGPHY1		82
+#define SCLK_HSADC		83
+#define SCLK_TIMER0		84
+#define SCLK_TIMER1		85
+#define SCLK_TIMER2		86
+#define SCLK_TIMER3		87
+#define SCLK_TIMER4		88
+#define SCLK_TIMER5		89
+#define SCLK_TIMER6		90
+#define SCLK_JTAG		91
+#define SCLK_SMC		92
+#define SCLK_TSADC		93
+
+#define DCLK_LCDC0		190
+#define DCLK_LCDC1		191
+
+/* aclk gates */
+#define ACLK_DMA1		192
+#define ACLK_DMA2		193
+#define ACLK_GPS		194
+#define ACLK_LCDC0		195
+#define ACLK_LCDC1		196
+#define ACLK_GPU		197
+#define ACLK_SMC		198
+#define ACLK_CIF		199
+#define ACLK_IPP		200
+#define ACLK_RGA		201
+#define ACLK_CIF0		202
+#define ACLK_CPU		203
+#define ACLK_PERI		204
+
+/* pclk gates */
+#define PCLK_GRF		320
+#define PCLK_PMU		321
+#define PCLK_TIMER0		322
+#define PCLK_TIMER1		323
+#define PCLK_TIMER2		324
+#define PCLK_TIMER3		325
+#define PCLK_PWM01		326
+#define PCLK_PWM23		327
+#define PCLK_SPI0		328
+#define PCLK_SPI1		329
+#define PCLK_SARADC		330
+#define PCLK_WDT		331
+#define PCLK_UART0		332
+#define PCLK_UART1		333
+#define PCLK_UART2		334
+#define PCLK_UART3		335
+#define PCLK_I2C0		336
+#define PCLK_I2C1		337
+#define PCLK_I2C2		338
+#define PCLK_I2C3		339
+#define PCLK_I2C4		340
+#define PCLK_GPIO0		341
+#define PCLK_GPIO1		342
+#define PCLK_GPIO2		343
+#define PCLK_GPIO3		344
+#define PCLK_GPIO4		345
+#define PCLK_GPIO6		346
+#define PCLK_EFUSE		347
+#define PCLK_TZPC		348
+#define PCLK_TSADC		349
+#define PCLK_CPU		350
+#define PCLK_PERI		351
+#define PCLK_DDRUPCTL		352
+#define PCLK_PUBL		353
+
+/* hclk gates */
+#define HCLK_SDMMC		448
+#define HCLK_SDIO		449
+#define HCLK_EMMC		450
+#define HCLK_OTG0		451
+#define HCLK_EMAC		452
+#define HCLK_SPDIF		453
+#define HCLK_I2S0		454
+#define HCLK_I2S1		455
+#define HCLK_I2S2		456
+#define HCLK_OTG1		457
+#define HCLK_HSIC		458
+#define HCLK_HSADC		459
+#define HCLK_PIDF		460
+#define HCLK_LCDC0		461
+#define HCLK_LCDC1		462
+#define HCLK_ROM		463
+#define HCLK_CIF0		464
+#define HCLK_IPP		465
+#define HCLK_RGA		466
+#define HCLK_NANDC0		467
+#define HCLK_CPU		468
+#define HCLK_PERI		469
+
+#define CLK_NR_CLKS		(HCLK_PERI + 1)
+
+/* soft-reset indices */
+#define SRST_MCORE		2
+#define SRST_CORE0		3
+#define SRST_CORE1		4
+#define SRST_MCORE_DBG		7
+#define SRST_CORE0_DBG		8
+#define SRST_CORE1_DBG		9
+#define SRST_CORE0_WDT		12
+#define SRST_CORE1_WDT		13
+#define SRST_STRC_SYS		14
+#define SRST_L2C		15
+
+#define SRST_CPU_AHB		17
+#define SRST_AHB2APB		19
+#define SRST_DMA1		20
+#define SRST_INTMEM		21
+#define SRST_ROM		22
+#define SRST_SPDIF		26
+#define SRST_TIMER0		27
+#define SRST_TIMER1		28
+#define SRST_EFUSE		30
+
+#define SRST_GPIO0		32
+#define SRST_GPIO1		33
+#define SRST_GPIO2		34
+#define SRST_GPIO3		35
+
+#define SRST_UART0		39
+#define SRST_UART1		40
+#define SRST_UART2		41
+#define SRST_UART3		42
+#define SRST_I2C0		43
+#define SRST_I2C1		44
+#define SRST_I2C2		45
+#define SRST_I2C3		46
+#define SRST_I2C4		47
+
+#define SRST_PWM0		48
+#define SRST_PWM1		49
+#define SRST_DAP_PO		50
+#define SRST_DAP		51
+#define SRST_DAP_SYS		52
+#define SRST_TPIU_ATB		53
+#define SRST_PMU_APB		54
+#define SRST_GRF		55
+#define SRST_PMU		56
+#define SRST_PERI_AXI		57
+#define SRST_PERI_AHB		58
+#define SRST_PERI_APB		59
+#define SRST_PERI_NIU		60
+#define SRST_CPU_PERI		61
+#define SRST_EMEM_PERI		62
+#define SRST_USB_PERI		63
+
+#define SRST_DMA2		64
+#define SRST_SMC		65
+#define SRST_MAC		66
+#define SRST_NANC0		68
+#define SRST_USBOTG0		69
+#define SRST_USBPHY0		70
+#define SRST_OTGC0		71
+#define SRST_USBOTG1		72
+#define SRST_USBPHY1		73
+#define SRST_OTGC1		74
+#define SRST_HSADC		76
+#define SRST_PIDFILTER		77
+#define SRST_DDR_MSCH		79
+
+#define SRST_TZPC		80
+#define SRST_SDMMC		81
+#define SRST_SDIO		82
+#define SRST_EMMC		83
+#define SRST_SPI0		84
+#define SRST_SPI1		85
+#define SRST_WDT		86
+#define SRST_SARADC		87
+#define SRST_DDRPHY		88
+#define SRST_DDRPHY_APB		89
+#define SRST_DDRCTL		90
+#define SRST_DDRCTL_APB		91
+#define SRST_DDRPUB		93
+
+#define SRST_VIO0_AXI		98
+#define SRST_VIO0_AHB		99
+#define SRST_LCDC0_AXI		100
+#define SRST_LCDC0_AHB		101
+#define SRST_LCDC0_DCLK		102
+#define SRST_LCDC1_AXI		103
+#define SRST_LCDC1_AHB		104
+#define SRST_LCDC1_DCLK		105
+#define SRST_IPP_AXI		106
+#define SRST_IPP_AHB		107
+#define SRST_RGA_AXI		108
+#define SRST_RGA_AHB		109
+#define SRST_CIF0		110
+
+#define SRST_VCODEC_AXI		112
+#define SRST_VCODEC_AHB		113
+#define SRST_VIO1_AXI		114
+#define SRST_VCODEC_CPU		115
+#define SRST_VCODEC_NIU		116
+#define SRST_GPU		120
+#define SRST_GPU_NIU		122
+#define SRST_TFUN_ATB		125
+#define SRST_TFUN_APB		126
+#define SRST_CTI4_APB		127
+
+#define SRST_TPIU_APB		128
+#define SRST_TRACE		129
+#define SRST_CORE_DBG		130
+#define SRST_DBG_APB		131
+#define SRST_CTI0		132
+#define SRST_CTI0_APB		133
+#define SRST_CTI1		134
+#define SRST_CTI1_APB		135
+#define SRST_PTM_CORE0		136
+#define SRST_PTM_CORE1		137
+#define SRST_PTM0		138
+#define SRST_PTM0_ATB		139
+#define SRST_PTM1		140
+#define SRST_PTM1_ATB		141
+#define SRST_CTM		142
+#define SRST_TS			143
+
+#endif
diff --git a/include/dt-bindings/clock/rk3188-cru.h b/include/dt-bindings/clock/rk3188-cru.h
new file mode 100644
index 0000000..b6960b0
--- /dev/null
+++ b/include/dt-bindings/clock/rk3188-cru.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RK3188_H
+#define _DT_BINDINGS_CLK_ROCKCHIP_RK3188_H
+
+#include <dt-bindings/clock/rk3188-cru-common.h>
+
+/* soft-reset indices */
+#define SRST_PTM_CORE2		0
+#define SRST_PTM_CORE3		1
+#define SRST_CORE2		5
+#define SRST_CORE3		6
+#define SRST_CORE2_DBG		10
+#define SRST_CORE3_DBG		11
+
+#define SRST_TIMER2		16
+#define SRST_TIMER4		23
+#define SRST_I2S0		24
+#define SRST_TIMER5		25
+#define SRST_TIMER3		29
+#define SRST_TIMER6		31
+
+#define SRST_PTM3		36
+#define SRST_PTM3_ATB		37
+
+#define SRST_GPS		67
+#define SRST_HSICPHY		75
+#define SRST_TIMER		78
+
+#define SRST_PTM2		92
+#define SRST_CORE2_WDT		94
+#define SRST_CORE3_WDT		95
+
+#define SRST_PTM2_ATB		111
+
+#define SRST_HSIC		117
+#define SRST_CTI2		118
+#define SRST_CTI2_APB		119
+#define SRST_GPU_BRIDGE		121
+#define SRST_CTI3		123
+#define SRST_CTI3_APB		124
+
+#endif
diff --git a/include/dt-bindings/clock/rk3328-cru.h b/include/dt-bindings/clock/rk3328-cru.h
new file mode 100644
index 0000000..6d8bf13
--- /dev/null
+++ b/include/dt-bindings/clock/rk3328-cru.h
@@ -0,0 +1,394 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RK3328_H
+#define _DT_BINDINGS_CLK_ROCKCHIP_RK3328_H
+
+/* core clocks */
+#define PLL_APLL		1
+#define PLL_DPLL		2
+#define PLL_CPLL		3
+#define PLL_GPLL		4
+#define PLL_NPLL		5
+#define ARMCLK			6
+
+/* sclk gates (special clocks) */
+#define SCLK_RTC32K		30
+#define SCLK_SDMMC_EXT		31
+#define SCLK_SPI		32
+#define SCLK_SDMMC		33
+#define SCLK_SDIO		34
+#define SCLK_EMMC		35
+#define SCLK_TSADC		36
+#define SCLK_SARADC		37
+#define SCLK_UART0		38
+#define SCLK_UART1		39
+#define SCLK_UART2		40
+#define SCLK_I2S0		41
+#define SCLK_I2S1		42
+#define SCLK_I2S2		43
+#define SCLK_I2S1_OUT		44
+#define SCLK_I2S2_OUT		45
+#define SCLK_SPDIF		46
+#define SCLK_TIMER0		47
+#define SCLK_TIMER1		48
+#define SCLK_TIMER2		49
+#define SCLK_TIMER3		50
+#define SCLK_TIMER4		51
+#define SCLK_TIMER5		52
+#define SCLK_WIFI		53
+#define SCLK_CIF_OUT		54
+#define SCLK_I2C0		55
+#define SCLK_I2C1		56
+#define SCLK_I2C2		57
+#define SCLK_I2C3		58
+#define SCLK_CRYPTO		59
+#define SCLK_PWM		60
+#define SCLK_PDM		61
+#define SCLK_EFUSE		62
+#define SCLK_OTP		63
+#define SCLK_DDRCLK		64
+#define SCLK_VDEC_CABAC		65
+#define SCLK_VDEC_CORE		66
+#define SCLK_VENC_DSP		67
+#define SCLK_VENC_CORE		68
+#define SCLK_RGA		69
+#define SCLK_HDMI_SFC		70
+#define SCLK_HDMI_CEC		71
+#define SCLK_USB3_REF		72
+#define SCLK_USB3_SUSPEND	73
+#define SCLK_SDMMC_DRV		74
+#define SCLK_SDIO_DRV		75
+#define SCLK_EMMC_DRV		76
+#define SCLK_SDMMC_EXT_DRV	77
+#define SCLK_SDMMC_SAMPLE	78
+#define SCLK_SDIO_SAMPLE	79
+#define SCLK_EMMC_SAMPLE	80
+#define SCLK_SDMMC_EXT_SAMPLE	81
+#define SCLK_VOP		82
+#define SCLK_MAC2PHY_RXTX	83
+#define SCLK_MAC2PHY_SRC	84
+#define SCLK_MAC2PHY_REF	85
+#define SCLK_MAC2PHY_OUT	86
+#define SCLK_MAC2IO_RX		87
+#define SCLK_MAC2IO_TX		88
+#define SCLK_MAC2IO_REFOUT	89
+#define SCLK_MAC2IO_REF		90
+#define SCLK_MAC2IO_OUT		91
+#define SCLK_TSP		92
+#define SCLK_HSADC_TSP		93
+#define SCLK_USB3PHY_REF	94
+#define SCLK_REF_USB3OTG	95
+#define SCLK_USB3OTG_REF	96
+#define SCLK_USB3OTG_SUSPEND	97
+#define SCLK_REF_USB3OTG_SRC	98
+#define SCLK_MAC2IO_SRC		99
+
+/* dclk gates */
+#define DCLK_LCDC		180
+#define DCLK_HDMIPHY		181
+#define HDMIPHY			182
+#define USB480M			183
+#define DCLK_LCDC_SRC		184
+
+/* aclk gates */
+#define ACLK_AXISRAM		190
+#define ACLK_VOP_PRE		191
+#define ACLK_USB3OTG		192
+#define ACLK_RGA_PRE		193
+#define ACLK_DMAC		194
+#define ACLK_GPU		195
+#define ACLK_BUS_PRE		196
+#define ACLK_PERI_PRE		197
+#define ACLK_RKVDEC_PRE		198
+#define ACLK_RKVDEC		199
+#define ACLK_RKVENC		200
+#define ACLK_VPU_PRE		201
+#define ACLK_VIO_PRE		202
+#define ACLK_VPU		203
+#define ACLK_VIO		204
+#define ACLK_VOP		205
+#define ACLK_GMAC		206
+#define ACLK_H265		207
+#define ACLK_H264		208
+#define ACLK_MAC2PHY		209
+#define ACLK_MAC2IO		210
+#define ACLK_DCF		211
+#define ACLK_TSP		212
+#define ACLK_PERI		213
+#define ACLK_RGA		214
+#define ACLK_IEP		215
+#define ACLK_CIF		216
+#define ACLK_HDCP		217
+
+/* pclk gates */
+#define PCLK_GPIO0		300
+#define PCLK_GPIO1		301
+#define PCLK_GPIO2		302
+#define PCLK_GPIO3		303
+#define PCLK_GRF		304
+#define PCLK_I2C0		305
+#define PCLK_I2C1		306
+#define PCLK_I2C2		307
+#define PCLK_I2C3		308
+#define PCLK_SPI		309
+#define PCLK_UART0		310
+#define PCLK_UART1		311
+#define PCLK_UART2		312
+#define PCLK_TSADC		313
+#define PCLK_PWM		314
+#define PCLK_TIMER		315
+#define PCLK_BUS_PRE		316
+#define PCLK_PERI_PRE		317
+#define PCLK_HDMI_CTRL		318
+#define PCLK_HDMI_PHY		319
+#define PCLK_GMAC		320
+#define PCLK_H265		321
+#define PCLK_MAC2PHY		322
+#define PCLK_MAC2IO		323
+#define PCLK_USB3PHY_OTG	324
+#define PCLK_USB3PHY_PIPE	325
+#define PCLK_USB3_GRF		326
+#define PCLK_USB2_GRF		327
+#define PCLK_HDMIPHY		328
+#define PCLK_DDR		329
+#define PCLK_PERI		330
+#define PCLK_HDMI		331
+#define PCLK_HDCP		332
+#define PCLK_DCF		333
+#define PCLK_SARADC		334
+
+/* hclk gates */
+#define HCLK_PERI		408
+#define HCLK_TSP		409
+#define HCLK_GMAC		410
+#define HCLK_I2S0_8CH		411
+#define HCLK_I2S1_8CH		413
+#define HCLK_I2S2_2CH		413
+#define HCLK_SPDIF_8CH		414
+#define HCLK_VOP		415
+#define HCLK_NANDC		416
+#define HCLK_SDMMC		417
+#define HCLK_SDIO		418
+#define HCLK_EMMC		419
+#define HCLK_SDMMC_EXT		420
+#define HCLK_RKVDEC_PRE		421
+#define HCLK_RKVDEC		422
+#define HCLK_RKVENC		423
+#define HCLK_VPU_PRE		424
+#define HCLK_VIO_PRE		425
+#define HCLK_VPU		426
+#define HCLK_VIO		427
+#define HCLK_BUS_PRE		428
+#define HCLK_PERI_PRE		429
+#define HCLK_H264		430
+#define HCLK_CIF		431
+#define HCLK_OTG_PMU		432
+#define HCLK_OTG		433
+#define HCLK_HOST0		434
+#define HCLK_HOST0_ARB		435
+#define HCLK_CRYPTO_MST		436
+#define HCLK_CRYPTO_SLV		437
+#define HCLK_PDM		438
+#define HCLK_IEP		439
+#define HCLK_RGA		440
+#define HCLK_HDCP		441
+
+#define CLK_NR_CLKS		(HCLK_HDCP + 1)
+
+#define SCLK_MAC2IO		0
+#define SCLK_MAC2PHY		1
+
+#define CLKGRF_NR_CLKS		(SCLK_MAC2PHY + 1)
+
+/* soft-reset indices */
+#define SRST_CORE0_PO		0
+#define SRST_CORE1_PO		1
+#define SRST_CORE2_PO		2
+#define SRST_CORE3_PO		3
+#define SRST_CORE0		4
+#define SRST_CORE1		5
+#define SRST_CORE2		6
+#define SRST_CORE3		7
+#define SRST_CORE0_DBG		8
+#define SRST_CORE1_DBG		9
+#define SRST_CORE2_DBG		10
+#define SRST_CORE3_DBG		11
+#define SRST_TOPDBG		12
+#define SRST_CORE_NIU		13
+#define SRST_STRC_A		14
+#define SRST_L2C		15
+
+#define SRST_A53_GIC		18
+#define SRST_DAP		19
+#define SRST_PMU_P		21
+#define SRST_EFUSE		22
+#define SRST_BUSSYS_H		23
+#define SRST_BUSSYS_P		24
+#define SRST_SPDIF		25
+#define SRST_INTMEM		26
+#define SRST_ROM		27
+#define SRST_GPIO0		28
+#define SRST_GPIO1		29
+#define SRST_GPIO2		30
+#define SRST_GPIO3		31
+
+#define SRST_I2S0		32
+#define SRST_I2S1		33
+#define SRST_I2S2		34
+#define SRST_I2S0_H		35
+#define SRST_I2S1_H		36
+#define SRST_I2S2_H		37
+#define SRST_UART0		38
+#define SRST_UART1		39
+#define SRST_UART2		40
+#define SRST_UART0_P		41
+#define SRST_UART1_P		42
+#define SRST_UART2_P		43
+#define SRST_I2C0		44
+#define SRST_I2C1		45
+#define SRST_I2C2		46
+#define SRST_I2C3		47
+
+#define SRST_I2C0_P		48
+#define SRST_I2C1_P		49
+#define SRST_I2C2_P		50
+#define SRST_I2C3_P		51
+#define SRST_EFUSE_SE_P		52
+#define SRST_EFUSE_NS_P		53
+#define SRST_PWM0		54
+#define SRST_PWM0_P		55
+#define SRST_DMA		56
+#define SRST_TSP_A		57
+#define SRST_TSP_H		58
+#define SRST_TSP		59
+#define SRST_TSP_HSADC		60
+#define SRST_DCF_A		61
+#define SRST_DCF_P		62
+
+#define SRST_SCR		64
+#define SRST_SPI		65
+#define SRST_TSADC		66
+#define SRST_TSADC_P		67
+#define SRST_CRYPTO		68
+#define SRST_SGRF		69
+#define SRST_GRF		70
+#define SRST_USB_GRF		71
+#define SRST_TIMER_6CH_P	72
+#define SRST_TIMER0		73
+#define SRST_TIMER1		74
+#define SRST_TIMER2		75
+#define SRST_TIMER3		76
+#define SRST_TIMER4		77
+#define SRST_TIMER5		78
+#define SRST_USB3GRF		79
+
+#define SRST_PHYNIU		80
+#define SRST_HDMIPHY		81
+#define SRST_VDAC		82
+#define SRST_ACODEC_p		83
+#define SRST_SARADC		85
+#define SRST_SARADC_P		86
+#define SRST_GRF_DDR		87
+#define SRST_DFIMON		88
+#define SRST_MSCH		89
+#define SRST_DDRMSCH		91
+#define SRST_DDRCTRL		92
+#define SRST_DDRCTRL_P		93
+#define SRST_DDRPHY		94
+#define SRST_DDRPHY_P		95
+
+#define SRST_GMAC_NIU_A		96
+#define SRST_GMAC_NIU_P		97
+#define SRST_GMAC2PHY_A		98
+#define SRST_GMAC2IO_A		99
+#define SRST_MACPHY		100
+#define SRST_OTP_PHY		101
+#define SRST_GPU_A		102
+#define SRST_GPU_NIU_A		103
+#define SRST_SDMMCEXT		104
+#define SRST_PERIPH_NIU_A	105
+#define SRST_PERIHP_NIU_H	106
+#define SRST_PERIHP_P		107
+#define SRST_PERIPHSYS_H	108
+#define SRST_MMC0		109
+#define SRST_SDIO		110
+#define SRST_EMMC		111
+
+#define SRST_USB2OTG_H		112
+#define SRST_USB2OTG		113
+#define SRST_USB2OTG_ADP	114
+#define SRST_USB2HOST_H		115
+#define SRST_USB2HOST_ARB	116
+#define SRST_USB2HOST_AUX	117
+#define SRST_USB2HOST_EHCIPHY	118
+#define SRST_USB2HOST_UTMI	119
+#define SRST_USB3OTG		120
+#define SRST_USBPOR		121
+#define SRST_USB2OTG_UTMI	122
+#define SRST_USB2HOST_PHY_UTMI	123
+#define SRST_USB3OTG_UTMI	124
+#define SRST_USB3PHY_U2		125
+#define SRST_USB3PHY_U3		126
+#define SRST_USB3PHY_PIPE	127
+
+#define SRST_VIO_A		128
+#define SRST_VIO_BUS_H		129
+#define SRST_VIO_H2P_H		130
+#define SRST_VIO_ARBI_H		131
+#define SRST_VOP_NIU_A		132
+#define SRST_VOP_A		133
+#define SRST_VOP_H		134
+#define SRST_VOP_D		135
+#define SRST_RGA		136
+#define SRST_RGA_NIU_A		137
+#define SRST_RGA_A		138
+#define SRST_RGA_H		139
+#define SRST_IEP_A		140
+#define SRST_IEP_H		141
+#define SRST_HDMI		142
+#define SRST_HDMI_P		143
+
+#define SRST_HDCP_A		144
+#define SRST_HDCP		145
+#define SRST_HDCP_H		146
+#define SRST_CIF_A		147
+#define SRST_CIF_H		148
+#define SRST_CIF_P		149
+#define SRST_OTP_P		150
+#define SRST_OTP_SBPI		151
+#define SRST_OTP_USER		152
+#define SRST_DDRCTRL_A		153
+#define SRST_DDRSTDY_P		154
+#define SRST_DDRSTDY		155
+#define SRST_PDM_H		156
+#define SRST_PDM		157
+#define SRST_USB3PHY_OTG_P	158
+#define SRST_USB3PHY_PIPE_P	159
+
+#define SRST_VCODEC_A		160
+#define SRST_VCODEC_NIU_A	161
+#define SRST_VCODEC_H		162
+#define SRST_VCODEC_NIU_H	163
+#define SRST_VDEC_A		164
+#define SRST_VDEC_NIU_A		165
+#define SRST_VDEC_H		166
+#define SRST_VDEC_NIU_H		167
+#define SRST_VDEC_CORE		168
+#define SRST_VDEC_CABAC		169
+#define SRST_DDRPHYDIV		175
+
+#define SRST_RKVENC_NIU_A	176
+#define SRST_RKVENC_NIU_H	177
+#define SRST_RKVENC_H265_A	178
+#define SRST_RKVENC_H265_P	179
+#define SRST_RKVENC_H265_CORE	180
+#define SRST_RKVENC_H265_DSP	181
+#define SRST_RKVENC_H264_A	182
+#define SRST_RKVENC_H264_H	183
+#define SRST_RKVENC_INTMEM	184
+
+#endif
diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h
index 0a86aec..d4bdcc6 100644
--- a/include/dt-bindings/clock/rk3399-cru.h
+++ b/include/dt-bindings/clock/rk3399-cru.h
@@ -122,6 +122,10 @@
 #define SCLK_DPHY_RX0_CFG		165
 #define SCLK_RMII_SRC			166
 #define SCLK_PCIEPHY_REF100M		167
+#define SCLK_USBPHY0_480M_SRC		168
+#define SCLK_USBPHY1_480M_SRC		169
+#define SCLK_DDRCLK			170
+#define SCLK_TESTOUT2			171
 
 #define DCLK_VOP0			180
 #define DCLK_VOP1			181
@@ -589,13 +593,13 @@
 #define SRST_P_SPI0			214
 #define SRST_P_SPI1			215
 #define SRST_P_SPI2			216
-#define SRST_P_SPI3			217
-#define SRST_P_SPI4			218
+#define SRST_P_SPI4			217
+#define SRST_P_SPI5			218
 #define SRST_SPI0			219
 #define SRST_SPI1			220
 #define SRST_SPI2			221
-#define SRST_SPI3			222
-#define SRST_SPI4			223
+#define SRST_SPI4			222
+#define SRST_SPI5			223
 
 /* cru_softrst_con14 */
 #define SRST_I2S0_8CH			224
@@ -717,8 +721,8 @@
 #define SRST_H_CM0S_NOC			3
 #define SRST_DBG_CM0S			4
 #define SRST_PO_CM0S			5
-#define SRST_P_SPI6			6
-#define SRST_SPI6			7
+#define SRST_P_SPI3			6
+#define SRST_SPI3			7
 #define SRST_P_TIMER_0_1		8
 #define SRST_P_TIMER_0			9
 #define SRST_P_TIMER_1			10
diff --git a/include/dt-bindings/pinctrl/rockchip.h b/include/dt-bindings/pinctrl/rockchip.h
index 56887e1..ecb76c7 100644
--- a/include/dt-bindings/pinctrl/rockchip.h
+++ b/include/dt-bindings/pinctrl/rockchip.h
@@ -22,5 +22,7 @@
 #define RK_FUNC_2	2
 #define RK_FUNC_3	3
 #define RK_FUNC_4	4
+#define RK_FUNC_5	5
+#define RK_FUNC_6	6
 
 #endif
diff --git a/include/spl.h b/include/spl.h
index bde4437..cdd196d 100644
--- a/include/spl.h
+++ b/include/spl.h
@@ -213,11 +213,29 @@
 			  struct blk_desc *block_dev, int partition);
 
 /**
- * spl_init() - Set up device tree and driver model in SPL if enabled
+ * spl_early_init() - Set up device tree and driver model in SPL if enabled
  *
  * Call this function in board_init_f() if you want to use device tree and
- * driver model early, before board_init_r() is called. This function will
- * be called from board_init_r() if not called earlier.
+ * driver model early, before board_init_r() is called.
+ *
+ * If this is not called, then driver model will be inactive in SPL's
+ * board_init_f(), and no device tree will be available.
+ */
+int spl_early_init(void);
+
+/**
+ * spl_init() - Set up device tree and driver model in SPL if enabled
+ *
+ * You can optionally call spl_early_init(), then optionally call spl_init().
+ * This function will be called from board_init_r() if not called earlier.
+ *
+ * Both spl_early_init() and spl_init() perform a similar function except that
+ * the latter will not set up the malloc() area if
+ * CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN is enabled, since it is assumed to
+ * already be done by a calll to spl_relocate_stack_gd() before board_init_r()
+ * is reached.
+ *
+ * This function will be called from board_init_r() if not called earlier.
  *
  * If this is not called, then driver model will be inactive in SPL's
  * board_init_f(), and no device tree will be available.
diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl
index b52f996..5370648 100644
--- a/scripts/Makefile.spl
+++ b/scripts/Makefile.spl
@@ -215,8 +215,13 @@
 # 'u-boot,dm-pre-reloc' property and thus are not needed by SPL. The second
 # pass removes various unused properties from the remaining nodes.
 # The output is typically a much smaller device tree file.
+ifeq ($(CONFIG_TPL_BUILD),y)
+fdtgrep_props := -b u-boot,dm-pre-reloc -b u-boot,dm-tpl
+else
+fdtgrep_props := -b u-boot,dm-pre-reloc -b u-boot,dm-spl
+endif
 quiet_cmd_fdtgrep = FDTGREP $@
-      cmd_fdtgrep = $(objtree)/tools/fdtgrep -b u-boot,dm-pre-reloc -RT $< \
+      cmd_fdtgrep = $(objtree)/tools/fdtgrep $(fdtgrep_props) -RT $< \
 		-n /chosen -O dtb | \
 	$(objtree)/tools/fdtgrep -r -O dtb - -o $@ \
 		$(addprefix -P ,$(subst $\",,$(CONFIG_OF_SPL_REMOVE_PROPS)))
diff --git a/tools/dtoc/dtoc.py b/tools/dtoc/dtoc.py
index 6df7b0d..bf67ec8 100755
--- a/tools/dtoc/dtoc.py
+++ b/tools/dtoc/dtoc.py
@@ -30,6 +30,8 @@
     "status",
     'phandle',
     'u-boot,dm-pre-reloc',
+    'u-boot,dm-tpl',
+    'u-boot,dm-spl',
 ]
 
 # C type declarations for the tyues we support
diff --git a/tools/rkcommon.c b/tools/rkcommon.c
index 0a072aa..6595e02 100644
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -46,17 +46,20 @@
  * @imagename:		Image name(passed by "mkimage -n")
  * @spl_hdr:		Boot ROM requires a 4-bytes spl header
  * @spl_size:		Spl size(include extra 4-bytes spl header)
+ * @spl_rc4:		RC4 encode the SPL binary (same key as header)
  */
 struct spl_info {
 	const char *imagename;
 	const char *spl_hdr;
 	const uint32_t spl_size;
+	const bool spl_rc4;
 };
 
 static struct spl_info spl_infos[] = {
-	{ "rk3036", "RK30", 0x1000 },
-	{ "rk3288", "RK32", 0x8000 },
-	{ "rk3399", "RK33", 0x20000 },
+	{ "rk3036", "RK30", 0x1000, false },
+	{ "rk3188", "RK31", 0x8000 - 0x800, true },
+	{ "rk3288", "RK32", 0x8000, false },
+	{ "rk3399", "RK33", 0x20000, false },
 };
 
 static unsigned char rc4_key[16] = {
@@ -113,6 +116,16 @@
 	return info->spl_size;
 }
 
+bool rkcommon_need_rc4_spl(struct image_tool_params *params)
+{
+	struct spl_info *info = rkcommon_get_spl_info(params->imagename);
+
+	/*
+	 * info would not be NULL, because of we checked params before.
+	 */
+	return info->spl_rc4;
+}
+
 int rkcommon_set_header(void *buf, uint file_size,
 			struct image_tool_params *params)
 {
@@ -124,7 +137,7 @@
 	memset(buf,  '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
 	hdr = (struct header0_info *)buf;
 	hdr->signature = RK_SIGNATURE;
-	hdr->disable_rc4 = 1;
+	hdr->disable_rc4 = !rkcommon_need_rc4_spl(params);
 	hdr->init_offset = RK_INIT_OFFSET;
 
 	hdr->init_size = (file_size + RK_BLK_SIZE - 1) / RK_BLK_SIZE;
@@ -135,3 +148,16 @@
 
 	return 0;
 }
+
+void rkcommon_rc4_encode_spl(void *buf, unsigned int offset, unsigned int size)
+{
+	unsigned int remaining = size;
+
+	while (remaining > 0) {
+		int step = (remaining > RK_BLK_SIZE) ? RK_BLK_SIZE : remaining;
+
+		rc4_encode(buf + offset, step, rc4_key);
+		offset += RK_BLK_SIZE;
+		remaining -= step;
+	}
+}
diff --git a/tools/rkcommon.h b/tools/rkcommon.h
index c69540f..b4f6f32 100644
--- a/tools/rkcommon.h
+++ b/tools/rkcommon.h
@@ -55,4 +55,26 @@
 int rkcommon_set_header(void *buf, uint file_size,
 			struct image_tool_params *params);
 
+/**
+ * rkcommon_need_rc4_spl() - check if rc4 encoded spl is required
+ *
+ * Some socs cannot disable the rc4-encryption of the spl binary.
+ * rc4 encryption is disabled normally except on socs that cannot
+ * handle unencrypted binaries.
+ * @return true or false depending on rc4 being required.
+ */
+bool rkcommon_need_rc4_spl(struct image_tool_params *params);
+
+/**
+ * rkcommon_rc4_encode_spl() - encode the spl binary
+ *
+ * Encrypts the SPL binary using the generic rc4 key as required
+ * by some socs.
+ *
+ * @buf:	Pointer to the SPL data (header and SPL binary)
+ * @offset:	offset inside buf to start at
+ * @size:	number of bytes to encode
+ */
+void rkcommon_rc4_encode_spl(void *buf, unsigned int offset, unsigned int size);
+
 #endif
diff --git a/tools/rkimage.c b/tools/rkimage.c
index ef31cb6..44d098c 100644
--- a/tools/rkimage.c
+++ b/tools/rkimage.c
@@ -28,6 +28,9 @@
 {
 	memcpy(buf + RK_SPL_HDR_START, rkcommon_get_spl_hdr(params),
 	       RK_SPL_HDR_SIZE);
+
+	if (rkcommon_need_rc4_spl(params))
+		rkcommon_rc4_encode_spl(buf, 4, params->file_size);
 }
 
 static int rkimage_extract_subimage(void *buf, struct image_tool_params *params)
diff --git a/tools/rksd.c b/tools/rksd.c
index a2baa74..ff2233f 100644
--- a/tools/rksd.c
+++ b/tools/rksd.c
@@ -41,6 +41,10 @@
 
 	memcpy(buf + RK_SPL_HDR_START, rkcommon_get_spl_hdr(params),
 	       RK_SPL_HDR_SIZE);
+
+	if (rkcommon_need_rc4_spl(params))
+		rkcommon_rc4_encode_spl(buf, RK_SPL_START - 4,
+					params->file_size - RK_SPL_START + 4);
 }
 
 static int rksd_extract_subimage(void *buf,  struct image_tool_params *params)
diff --git a/tools/rkspi.c b/tools/rkspi.c
index a0b0051..0271d2e 100644
--- a/tools/rkspi.c
+++ b/tools/rkspi.c
@@ -48,6 +48,10 @@
 	memcpy(buf + RK_SPL_HDR_START, rkcommon_get_spl_hdr(params),
 	       RK_SPL_HDR_SIZE);
 
+	if (rkcommon_need_rc4_spl(params))
+		rkcommon_rc4_encode_spl(buf, RK_SPL_START - 4,
+					params->file_size - RK_SPL_START + 4);
+
 	/*
 	 * Spread the image out so we only use the first 2KB of each 4KB
 	 * region. This is a feature of the SPI format required by the Rockchip