Merge git://git.denx.de/u-boot-fsl-qoriq
diff --git a/Makefile b/Makefile
index 7ce933c..bccfbaa 100644
--- a/Makefile
+++ b/Makefile
@@ -3,9 +3,9 @@
 #
 
 VERSION = 2016
-PATCHLEVEL = 07
+PATCHLEVEL = 09
 SUBLEVEL =
-EXTRAVERSION =
+EXTRAVERSION = -rc1
 NAME =
 
 # *DOCUMENTATION*
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 397981a..4a62d4b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -840,15 +840,12 @@
 
 config ARCH_ROCKCHIP
 	bool "Support Rockchip SoCs"
-	select SUPPORT_SPL
-	select SPL
 	select OF_CONTROL
-	select CPU_V7
 	select BLK
 	select DM
-	select SPL_DM
+	select SPL_DM if SPL
 	select SYS_MALLOC_F
-	select SPL_SYS_MALLOC_SIMPLE
+	select SPL_SYS_MALLOC_SIMPLE if SPL
 	select DM_GPIO
 	select DM_I2C
 	select DM_MMC
diff --git a/arch/arm/cpu/armv7/am33xx/ddr.c b/arch/arm/cpu/armv7/am33xx/ddr.c
index 888cf1f..6acf30c 100644
--- a/arch/arm/cpu/armv7/am33xx/ddr.c
+++ b/arch/arm/cpu/armv7/am33xx/ddr.c
@@ -120,12 +120,15 @@
 
 	writel(regs->sdram_config, &emif_reg[nr]->emif_sdram_config);
 	writel(regs->sdram_config, &cstat->secure_emif_sdram_config);
+
+	/* Wait 1ms because of L3 timeout error */
+	udelay(1000);
+
 	writel(regs->ref_ctrl, &emif_reg[nr]->emif_sdram_ref_ctrl);
 	writel(regs->ref_ctrl, &emif_reg[nr]->emif_sdram_ref_ctrl_shdw);
 
 	/* Perform hardware leveling for DDR3 */
 	if (emif_sdram_type(regs->sdram_config) == EMIF_SDRAM_TYPE_DDR3) {
-		udelay(1000);
 		writel(readl(&emif_reg[nr]->emif_ddr_ext_phy_ctrl_36) |
 		       0x100, &emif_reg[nr]->emif_ddr_ext_phy_ctrl_36);
 		writel(readl(&emif_reg[nr]->emif_ddr_ext_phy_ctrl_36_shdw) |
@@ -289,19 +292,14 @@
 void config_ddr_phy(const struct emif_regs *regs, int nr)
 {
 	/*
-	 * Disable initialization and refreshes for now until we
-	 * finish programming EMIF regs.
-	 * Also set time between rising edge of DDR_RESET to rising
-	 * edge of DDR_CKE to > 500us per memory spec.
+	 * Disable initialization and refreshes for now until we finish
+	 * programming EMIF regs and set time between rising edge of
+	 * DDR_RESET to rising edge of DDR_CKE to > 500us per memory spec.
+	 * We currently hardcode a value based on a max expected frequency
+	 * of 400MHz.
 	 */
-#ifndef CONFIG_AM43XX
-	setbits_le32(&emif_reg[nr]->emif_sdram_ref_ctrl,
-		     EMIF_REG_INITREF_DIS_MASK);
-#endif
-	if (regs->zq_config)
-		/* Set time between rising edge of DDR_RESET to rising
-		 * edge of DDR_CKE to > 500us per memory spec. */
-		writel(0x00003100, &emif_reg[nr]->emif_sdram_ref_ctrl);
+	writel(EMIF_REG_INITREF_DIS_MASK | 0x3100,
+		&emif_reg[nr]->emif_sdram_ref_ctrl);
 
 	writel(regs->emif_ddr_phy_ctlr_1,
 		&emif_reg[nr]->emif_ddr_phy_ctrl_1);
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index ca8712a..c97e3f6 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -31,7 +31,9 @@
 	rk3288-firefly.dtb \
 	rk3288-jerry.dtb \
 	rk3288-rock2-square.dtb \
-	rk3036-sdk.dtb
+	rk3288-evb.dtb \
+	rk3036-sdk.dtb \
+	rk3399-evb.dtb
 dtb-$(CONFIG_ARCH_MESON) += \
 	meson-gxbb-odroidc2.dtb
 dtb-$(CONFIG_TEGRA) += tegra20-harmony.dtb \
diff --git a/arch/arm/dts/rk3288-evb.dts b/arch/arm/dts/rk3288-evb.dts
new file mode 100644
index 0000000..caf24ee
--- /dev/null
+++ b/arch/arm/dts/rk3288-evb.dts
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+ X11
+ */
+
+/dts-v1/;
+#include "rk3288-evb.dtsi"
+
+/ {
+	model = "Evb-RK3288";
+	compatible = "evb-rk3288,evb-rk3288", "rockchip,rk3288";
+
+	chosen {
+		stdout-path = &uart2;
+	};
+};
+
+&dmc {
+	rockchip,num-channels = <2>;
+	rockchip,pctl-timing = <0x215 0xc8 0x0 0x35 0x26 0x2 0x70 0x2000d
+		0x6 0x0 0x8 0x4 0x17 0x24 0xd 0x6
+		0x4 0x8 0x4 0x76 0x4 0x0 0x30 0x0
+		0x1 0x2 0x2 0x4 0x0 0x0 0xc0 0x4
+		0x8 0x1f4>;
+	rockchip,phy-timing = <0x48d7dd93 0x187008d8 0x121076
+		0x0 0xc3 0x6 0x2>;
+	rockchip,sdram-channel = /bits/ 8 <0x2 0xa 0x3 0x2 0x2 0x0 0xe 0xe>;
+	rockchip,sdram-params = <0x20d266a4 0x5b6 2 533000000 6 9 0>;
+};
+
+&pinctrl {
+	u-boot,dm-pre-reloc;
+};
+
+&pwm1 {
+	status = "okay";
+};
+
+&uart2 {
+	u-boot,dm-pre-reloc;
+	reg-shift = <2>;
+};
+
+&sdmmc {
+	u-boot,dm-pre-reloc;
+};
+
+&emmc {
+	u-boot,dm-pre-reloc;
+};
+
+&gpio3 {
+	u-boot,dm-pre-reloc;
+};
+
+&gpio8 {
+	u-boot,dm-pre-reloc;
+};
diff --git a/arch/arm/dts/rk3288-evb.dtsi b/arch/arm/dts/rk3288-evb.dtsi
new file mode 100644
index 0000000..cb7d03e
--- /dev/null
+++ b/arch/arm/dts/rk3288-evb.dtsi
@@ -0,0 +1,379 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+ X11
+ */
+
+#include "rk3288.dtsi"
+
+/ {
+	memory {
+		reg = <0 0x80000000>;
+	};
+
+	keys: gpio-keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		button@0 {
+			gpio-key,wakeup = <1>;
+			gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
+			label = "GPIO Power";
+			linux,code = <116>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwr_key>;
+		};
+	};
+
+	vcc_sys: vsys-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+
+	vcc_flash: flash-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_flash";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_5v: usb-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vcc_host_5v: 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 = "vcc_host_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		vin-supply = <&vcc_5v>;
+	};
+
+	vcc_otg_5v: usb-otg-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&otg_vbus_drv>;
+		regulator-name = "vcc_otg_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		vin-supply = <&vcc_5v>;
+	};
+};
+
+&cpu0 {
+	cpu0-supply = <&vdd_cpu>;
+};
+
+&emmc {
+	broken-cd;
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	disable-wp;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_clk>, <&emmc_cmd>, <&emmc_pwr>, <&emmc_bus8>;
+	vmmc-supply = <&vcc_io>;
+	vqmmc-supply = <&vcc_flash>;
+	status = "okay";
+};
+
+&hdmi {
+	ddc-i2c-bus = <&i2c5>;
+	status = "okay";
+};
+
+&i2c0 {
+	clock-frequency = <400000>;
+	status = "okay";
+
+	vdd_cpu: syr827@40 {
+		compatible = "silergy,syr827";
+		fcs,suspend-voltage-selector = <1>;
+		reg = <0x40>;
+		regulator-name = "vdd_cpu";
+		regulator-min-microvolt = <850000>;
+		regulator-max-microvolt = <1350000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vdd_gpu: syr828@41 {
+		compatible = "silergy,syr828";
+		fcs,suspend-voltage-selector = <1>;
+		reg = <0x41>;
+		regulator-name = "vdd_gpu";
+		regulator-min-microvolt = <850000>;
+		regulator-max-microvolt = <1350000>;
+		regulator-always-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	hym8563: hym8563@51 {
+		compatible = "haoyu,hym8563";
+		reg = <0x51>;
+		#clock-cells = <0>;
+		clock-frequency = <32768>;
+		clock-output-names = "xin32k";
+		interrupt-parent = <&gpio7>;
+		interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&rtc_int>;
+	};
+
+	act8846: act8846@5a {
+		compatible = "active-semi,act8846";
+		reg = <0x5a>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwr_hold>;
+		system-power-controller;
+
+		regulators {
+			vcc_ddr: REG1 {
+				regulator-name = "vcc_ddr";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+				regulator-always-on;
+			};
+
+			vcc_io: REG2 {
+				regulator-name = "vcc_io";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vdd_log: REG3 {
+				regulator-name = "vdd_log";
+				regulator-min-microvolt = <1100000>;
+				regulator-max-microvolt = <1100000>;
+				regulator-always-on;
+			};
+
+			vcc_20: REG4 {
+				regulator-name = "vcc_20";
+				regulator-min-microvolt = <2000000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-always-on;
+			};
+
+			vccio_sd: REG5 {
+				regulator-name = "vccio_sd";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vdd10_lcd: REG6 {
+				regulator-name = "vdd10_lcd";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+
+			vcca_codec: REG7 {
+				regulator-name = "vcca_codec";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vcc_tp: REG8 {
+				regulator-name = "vcca_33";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vccio_pmu: REG9 {
+				regulator-name = "vccio_pmu";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vdd_10: REG10 {
+				regulator-name = "vdd_10";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+
+			vcc_18: REG11 {
+				regulator-name = "vcc_18";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+
+			vcc18_lcd: REG12 {
+				regulator-name = "vcc18_lcd";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+		};
+	};
+};
+
+&i2c1 {
+	status = "okay";
+};
+
+&i2c2 {
+	status = "okay";
+};
+
+&i2c4 {
+	status = "okay";
+};
+
+&i2c5 {
+	status = "okay";
+};
+
+&pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	pcfg_output_low: pcfg-output-low {
+		output-low;
+	};
+
+	act8846 {
+		pwr_hold: pwr-hold {
+			rockchip,pins = <0 9 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+
+	hym8563 {
+		rtc_int: rtc-int {
+			rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	keys {
+		pwr_key: pwr-key {
+			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	sdmmc {
+		sdmmc_pwr: sdmmc-pwr {
+			rockchip,pins = <7 11 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	usb_host {
+		host_vbus_drv: host-vbus-drv {
+			rockchip,pins = <0 14 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	usb_otg {
+		otg_vbus_drv: otg-vbus-drv {
+			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&saradc {
+	vref-supply = <&vcc_18>;
+	status = "okay";
+};
+
+&sdio0 {
+	broken-cd;
+	bus-width = <4>;
+	disable-wp;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio0_bus4>, <&sdio0_cmd>, <&sdio0_clk>;
+	vmmc-supply = <&vcc_18>;
+	status = "disabled";
+};
+
+&sdmmc {
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	card-detect-delay = <200>;
+	disable-wp;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
+	vmmc-supply = <&vccio_sd>;
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_clk>, <&spi0_cs0>, <&spi0_tx>, <&spi0_rx>, <&spi0_cs1>;
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_xfer>, <&uart0_cts>, <&uart0_rts>;
+	status = "okay";
+};
+
+&uart1 {
+	status = "okay";
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&uart3 {
+	status = "okay";
+};
+
+&usb_host1 {
+	status = "okay";
+};
+
+&usb_otg {
+	status = "okay";
+};
+
+&vopb {
+	status = "okay";
+};
+
+&vopb_mmu {
+	status = "okay";
+};
+
+&vopl {
+	status = "okay";
+};
+
+&vopl_mmu {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
diff --git a/arch/arm/dts/rk3288.dtsi b/arch/arm/dts/rk3288.dtsi
index 3dab0fc..bcf051a 100644
--- a/arch/arm/dts/rk3288.dtsi
+++ b/arch/arm/dts/rk3288.dtsi
@@ -454,6 +454,7 @@
 		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru HCLK_OTG0>;
 		clock-names = "otg";
+		dr_mode = "otg";
 		phys = <&usbphy0>;
 		phy-names = "usb2-phy";
 		status = "disabled";
diff --git a/arch/arm/dts/rk3399-evb.dts b/arch/arm/dts/rk3399-evb.dts
new file mode 100644
index 0000000..bbcfcd0
--- /dev/null
+++ b/arch/arm/dts/rk3399-evb.dts
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+/dts-v1/;
+#include <dt-bindings/pwm/pwm.h>
+#include "rk3399.dtsi"
+
+/ {
+	model = "Rockchip RK3399 Evaluation Board";
+	compatible = "rockchip,rk3399-evb", "rockchip,rk3399",
+		     "google,rk3399evb-rev2";
+
+	chosen {
+		stdout-path = &uart2;
+	};
+
+	vdd_center: vdd-center {
+		compatible = "pwm-regulator";
+		pwms = <&pwm3 0 25000 0>;
+		regulator-name = "vdd_center";
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <1400000>;
+		regulator-always-on;
+		regulator-boot-on;
+		status = "okay";
+	};
+
+	vcc3v3_sys: vcc3v3-sys {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc3v3_sys";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc_phy: vcc-phy-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_phy";
+		regulator-always-on;
+		regulator-boot-on;
+	};
+};
+
+&emmc_phy {
+	status = "okay";
+};
+
+&pwm0 {
+	status = "okay";
+};
+
+&pwm2 {
+	status = "okay";
+};
+
+&pwm3 {
+	status = "okay";
+};
+
+&sdhci {
+	bus-width = <8>;
+	mmc-hs400-1_8v;
+	mmc-hs400-enhanced-strobe;
+	non-removable;
+	status = "okay";
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&usb_host0_ehci {
+	status = "okay";
+};
+
+&usb_host0_ohci {
+	status = "okay";
+};
+
+&usb_host1_ehci {
+	status = "okay";
+};
+
+&usb_host1_ohci {
+	status = "okay";
+};
+
+&pinctrl {
+	pmic {
+		pmic_int_l: pmic-int-l {
+			rockchip,pins =
+				<1 21 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+
+		pmic_dvs2: pmic-dvs2 {
+			rockchip,pins =
+				<1 18 RK_FUNC_GPIO &pcfg_pull_down>;
+		};
+	};
+};
diff --git a/arch/arm/dts/rk3399.dtsi b/arch/arm/dts/rk3399.dtsi
new file mode 100644
index 0000000..fb5af54
--- /dev/null
+++ b/arch/arm/dts/rk3399.dtsi
@@ -0,0 +1,1028 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <dt-bindings/clock/rk3399-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,rk3399";
+
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+		serial3 = &uart3;
+		serial4 = &uart4;
+	};
+
+	cpus {
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		cpu-map {
+			cluster0 {
+				core0 {
+					cpu = <&cpu_l0>;
+				};
+				core1 {
+					cpu = <&cpu_l1>;
+				};
+				core2 {
+					cpu = <&cpu_l2>;
+				};
+				core3 {
+					cpu = <&cpu_l3>;
+				};
+			};
+
+			cluster1 {
+				core0 {
+					cpu = <&cpu_b0>;
+				};
+				core1 {
+					cpu = <&cpu_b1>;
+				};
+			};
+		};
+
+		cpu_l0: cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53", "arm,armv8";
+			reg = <0x0 0x0>;
+			enable-method = "psci";
+			#cooling-cells = <2>; /* min followed by max */
+			clocks = <&cru ARMCLKL>;
+		};
+
+		cpu_l1: cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53", "arm,armv8";
+			reg = <0x0 0x1>;
+			enable-method = "psci";
+			clocks = <&cru ARMCLKL>;
+		};
+
+		cpu_l2: cpu@2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53", "arm,armv8";
+			reg = <0x0 0x2>;
+			enable-method = "psci";
+			clocks = <&cru ARMCLKL>;
+		};
+
+		cpu_l3: cpu@3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53", "arm,armv8";
+			reg = <0x0 0x3>;
+			enable-method = "psci";
+			clocks = <&cru ARMCLKL>;
+		};
+
+		cpu_b0: cpu@100 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a72", "arm,armv8";
+			reg = <0x0 0x100>;
+			enable-method = "psci";
+			#cooling-cells = <2>; /* min followed by max */
+			clocks = <&cru ARMCLKB>;
+		};
+
+		cpu_b1: cpu@101 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a72", "arm,armv8";
+			reg = <0x0 0x101>;
+			enable-method = "psci";
+			clocks = <&cru ARMCLKB>;
+		};
+	};
+
+	psci {
+		compatible = "arm,psci-1.0";
+		method = "smc";
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+	};
+
+	xin24m: xin24m {
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+		clock-output-names = "xin24m";
+		#clock-cells = <0>;
+	};
+
+	amba {
+		compatible = "simple-bus";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		dmac_bus: dma-controller@ff6d0000 {
+			compatible = "arm,pl330", "arm,primecell";
+			reg = <0x0 0xff6d0000 0x0 0x4000>;
+			interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+			#dma-cells = <1>;
+			clocks = <&cru ACLK_DMAC0_PERILP>;
+			clock-names = "apb_pclk";
+		};
+
+		dmac_peri: dma-controller@ff6e0000 {
+			compatible = "arm,pl330", "arm,primecell";
+			reg = <0x0 0xff6e0000 0x0 0x4000>;
+			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+			#dma-cells = <1>;
+			clocks = <&cru ACLK_DMAC1_PERILP>;
+			clock-names = "apb_pclk";
+		};
+	};
+
+	sdio0: dwmmc@fe310000 {
+		compatible = "rockchip,rk3399-dw-mshc",
+			     "rockchip,rk3288-dw-mshc";
+		reg = <0x0 0xfe310000 0x0 0x4000>;
+		interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+		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-drive", "ciu-sample";
+		fifo-depth = <0x100>;
+		status = "disabled";
+	};
+
+	sdmmc: dwmmc@fe320000 {
+		compatible = "rockchip,rk3399-dw-mshc",
+			     "rockchip,rk3288-dw-mshc";
+		reg = <0x0 0xfe320000 0x0 0x4000>;
+		interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+		clock-freq-min-max = <400000 150000000>;
+		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
+			 <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
+		clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
+		fifo-depth = <0x100>;
+		status = "disabled";
+	};
+
+	sdhci: sdhci@fe330000 {
+		compatible = "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1";
+		reg = <0x0 0xfe330000 0x0 0x10000>;
+		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+		assigned-clocks = <&cru SCLK_EMMC>;
+		assigned-clock-rates = <200000000>;
+		clocks = <&cru SCLK_EMMC>, <&cru ACLK_EMMC>;
+		clock-names = "clk_xin", "clk_ahb";
+		phys = <&emmc_phy>;
+		phy-names = "phy_arasan";
+		status = "disabled";
+	};
+
+	usb_host0_ehci: usb@fe380000 {
+		compatible = "generic-ehci";
+		reg = <0x0 0xfe380000 0x0 0x20000>;
+		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
+		clock-names = "hclk_host0", "hclk_host0_arb";
+		status = "disabled";
+	};
+
+	usb_host0_ohci: usb@fe3a0000 {
+		compatible = "generic-ohci";
+		reg = <0x0 0xfe3a0000 0x0 0x20000>;
+		interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
+		clock-names = "hclk_host0", "hclk_host0_arb";
+		status = "disabled";
+	};
+
+	usb_host1_ehci: usb@fe3c0000 {
+		compatible = "generic-ehci";
+		reg = <0x0 0xfe3c0000 0x0 0x20000>;
+		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
+		clock-names = "hclk_host1", "hclk_host1_arb";
+		status = "disabled";
+	};
+
+	usb_host1_ohci: usb@fe3e0000 {
+		compatible = "generic-ohci";
+		reg = <0x0 0xfe3e0000 0x0 0x20000>;
+		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
+		clock-names = "hclk_host1", "hclk_host1_arb";
+		status = "disabled";
+	};
+
+	gic: interrupt-controller@fee00000 {
+		compatible = "arm,gic-v3";
+		#interrupt-cells = <3>;
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		interrupt-controller;
+
+		reg = <0x0 0xfee00000 0 0x10000>, /* GICD */
+		      <0x0 0xfef00000 0 0xc0000>, /* GICR */
+		      <0x0 0xfff00000 0 0x10000>, /* GICC */
+		      <0x0 0xfff10000 0 0x10000>, /* GICH */
+		      <0x0 0xfff20000 0 0x10000>; /* GICV */
+		interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+		its: interrupt-controller@fee20000 {
+			compatible = "arm,gic-v3-its";
+			msi-controller;
+			reg = <0x0 0xfee20000 0x0 0x20000>;
+		};
+	};
+
+	uart0: serial@ff180000 {
+		compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart";
+		reg = <0x0 0xff180000 0x0 0x100>;
+		clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>;
+		clock-names = "baudclk", "apb_pclk";
+		interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart0_xfer>;
+		status = "disabled";
+	};
+
+	uart1: serial@ff190000 {
+		compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart";
+		reg = <0x0 0xff190000 0x0 0x100>;
+		clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>;
+		clock-names = "baudclk", "apb_pclk";
+		interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart1_xfer>;
+		status = "disabled";
+	};
+
+	uart2: serial@ff1a0000 {
+		compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart";
+		reg = <0x0 0xff1a0000 0x0 0x100>;
+		clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>;
+		clock-names = "baudclk", "apb_pclk";
+		interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+		clock-frequency = <24000000>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart2c_xfer>;
+		status = "disabled";
+	};
+
+	uart3: serial@ff1b0000 {
+		compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart";
+		reg = <0x0 0xff1b0000 0x0 0x100>;
+		clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>;
+		clock-names = "baudclk", "apb_pclk";
+		interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart3_xfer>;
+		status = "disabled";
+	};
+
+	spi0: spi@ff1c0000 {
+		compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
+		reg = <0x0 0xff1c0000 0x0 0x1000>;
+		clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
+		clock-names = "spiclk", "apb_pclk";
+		interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};
+
+	spi1: spi@ff1d0000 {
+		compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
+		reg = <0x0 0xff1d0000 0x0 0x1000>;
+		clocks = <&cru SCLK_SPI1>, <&cru PCLK_SPI1>;
+		clock-names = "spiclk", "apb_pclk";
+		interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi1_clk &spi1_tx &spi1_rx &spi1_cs0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};
+
+	spi2: spi@ff1e0000 {
+		compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
+		reg = <0x0 0xff1e0000 0x0 0x1000>;
+		clocks = <&cru SCLK_SPI2>, <&cru PCLK_SPI2>;
+		clock-names = "spiclk", "apb_pclk";
+		interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi2_clk &spi2_tx &spi2_rx &spi2_cs0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};
+
+	spi4: spi@ff1f0000 {
+		compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
+		reg = <0x0 0xff1f0000 0x0 0x1000>;
+		clocks = <&cru SCLK_SPI4>, <&cru PCLK_SPI4>;
+		clock-names = "spiclk", "apb_pclk";
+		interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi4_clk &spi4_tx &spi4_rx &spi4_cs0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};
+
+	spi5: spi@ff200000 {
+		compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
+		reg = <0x0 0xff200000 0x0 0x1000>;
+		clocks = <&cru SCLK_SPI5>, <&cru PCLK_SPI5>;
+		clock-names = "spiclk", "apb_pclk";
+		interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi5_clk &spi5_tx &spi5_rx &spi5_cs0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};
+
+	pmugrf: syscon@ff320000 {
+		compatible = "rockchip,rk3399-pmugrf", "syscon", "simple-mfd";
+		reg = <0x0 0xff320000 0x0 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		pmu_io_domains: io-domains {
+			compatible = "rockchip,rk3399-pmu-io-voltage-domain";
+			status = "disabled";
+		};
+	};
+
+	spi3: spi@ff350000 {
+		compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
+		reg = <0x0 0xff350000 0x0 0x1000>;
+		clocks = <&pmucru SCLK_SPI3_PMU>, <&pmucru PCLK_SPI3_PMU>;
+		clock-names = "spiclk", "apb_pclk";
+		interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi3_clk &spi3_tx &spi3_rx &spi3_cs0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};
+
+	uart4: serial@ff370000 {
+		compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart";
+		reg = <0x0 0xff370000 0x0 0x100>;
+		clocks = <&pmucru SCLK_UART4_PMU>, <&pmucru PCLK_UART4_PMU>;
+		clock-names = "baudclk", "apb_pclk";
+		interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart4_xfer>;
+		status = "disabled";
+	};
+
+	pwm0: pwm@ff420000 {
+		compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
+		reg = <0x0 0xff420000 0x0 0x10>;
+		#pwm-cells = <3>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwm0_pin>;
+		clocks = <&pmucru PCLK_RKPWM_PMU>;
+		clock-names = "pwm";
+		status = "disabled";
+	};
+
+	pwm1: pwm@ff420010 {
+		compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
+		reg = <0x0 0xff420010 0x0 0x10>;
+		#pwm-cells = <3>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwm1_pin>;
+		clocks = <&pmucru PCLK_RKPWM_PMU>;
+		clock-names = "pwm";
+		status = "disabled";
+	};
+
+	pwm2: pwm@ff420020 {
+		compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
+		reg = <0x0 0xff420020 0x0 0x10>;
+		#pwm-cells = <3>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwm2_pin>;
+		clocks = <&pmucru PCLK_RKPWM_PMU>;
+		clock-names = "pwm";
+		status = "disabled";
+	};
+
+	pwm3: pwm@ff420030 {
+		compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
+		reg = <0x0 0xff420030 0x0 0x10>;
+		#pwm-cells = <3>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwm3a_pin>;
+		clocks = <&pmucru PCLK_RKPWM_PMU>;
+		clock-names = "pwm";
+		status = "disabled";
+	};
+
+	pmucru: pmu-clock-controller@ff750000 {
+		compatible = "rockchip,rk3399-pmucru";
+		reg = <0x0 0xff750000 0x0 0x1000>;
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+		assigned-clocks = <&pmucru PLL_PPLL>;
+		assigned-clock-rates = <676000000>;
+	};
+
+	cru: clock-controller@ff760000 {
+		compatible = "rockchip,rk3399-cru";
+		reg = <0x0 0xff760000 0x0 0x1000>;
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+		assigned-clocks =
+			<&cru PLL_GPLL>, <&cru PLL_CPLL>,
+			<&cru PLL_NPLL>,
+			<&cru ACLK_PERIHP>, <&cru HCLK_PERIHP>,
+			<&cru PCLK_PERIHP>,
+			<&cru ACLK_PERILP0>, <&cru HCLK_PERILP0>,
+			<&cru PCLK_PERILP0>,
+			<&cru HCLK_PERILP1>, <&cru PCLK_PERILP1>;
+		assigned-clock-rates =
+			 <594000000>,  <800000000>,
+			<1000000000>,
+			 <150000000>,   <75000000>,
+			  <37500000>,
+			 <100000000>,  <100000000>,
+			  <50000000>,
+			 <100000000>,   <50000000>;
+	};
+
+	grf: syscon@ff770000 {
+		compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
+		reg = <0x0 0xff770000 0x0 0x10000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		io_domains: io-domains {
+			compatible = "rockchip,rk3399-io-voltage-domain";
+			status = "disabled";
+		};
+
+		emmc_phy: phy@f780 {
+			compatible = "rockchip,rk3399-emmc-phy";
+			reg = <0xf780 0x24>;
+			#phy-cells = <0>;
+			status = "disabled";
+		};
+	};
+
+	watchdog@ff840000 {
+		compatible = "snps,dw-wdt";
+		reg = <0x0 0xff840000 0x0 0x100>;
+		clocks = <&cru PCLK_WDT>;
+		interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	spdif: spdif@ff870000 {
+		compatible = "rockchip,rk3399-spdif";
+		reg = <0x0 0xff870000 0x0 0x1000>;
+		interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+		dmas = <&dmac_bus 7>;
+		dma-names = "tx";
+		clock-names = "mclk", "hclk";
+		clocks = <&cru SCLK_SPDIF_8CH>, <&cru HCLK_SPDIF>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spdif_bus>;
+		status = "disabled";
+	};
+
+	i2s0: i2s@ff880000 {
+		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
+		reg = <0x0 0xff880000 0x0 0x1000>;
+		rockchip,grf = <&grf>;
+		interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+		dmas = <&dmac_bus 0>, <&dmac_bus 1>;
+		dma-names = "tx", "rx";
+		clock-names = "i2s_clk", "i2s_hclk";
+		clocks = <&cru SCLK_I2S0_8CH>, <&cru HCLK_I2S0_8CH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2s0_8ch_bus>;
+		status = "disabled";
+	};
+
+	i2s1: i2s@ff890000 {
+		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
+		reg = <0x0 0xff890000 0x0 0x1000>;
+		interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+		dmas = <&dmac_bus 2>, <&dmac_bus 3>;
+		dma-names = "tx", "rx";
+		clock-names = "i2s_clk", "i2s_hclk";
+		clocks = <&cru SCLK_I2S1_8CH>, <&cru HCLK_I2S1_8CH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2s1_2ch_bus>;
+		status = "disabled";
+	};
+
+	i2s2: i2s@ff8a0000 {
+		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
+		reg = <0x0 0xff8a0000 0x0 0x1000>;
+		interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+		dmas = <&dmac_bus 4>, <&dmac_bus 5>;
+		dma-names = "tx", "rx";
+		clock-names = "i2s_clk", "i2s_hclk";
+		clocks = <&cru SCLK_I2S2_8CH>, <&cru HCLK_I2S2_8CH>;
+		status = "disabled";
+	};
+
+	pinctrl: pinctrl {
+		compatible = "rockchip,rk3399-pinctrl";
+		rockchip,grf = <&grf>;
+		rockchip,pmu = <&pmugrf>;
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		gpio0: gpio0@ff720000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff720000 0x0 0x100>;
+			clocks = <&pmucru PCLK_GPIO0_PMU>;
+			interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+
+			gpio-controller;
+			#gpio-cells = <0x2>;
+
+			interrupt-controller;
+			#interrupt-cells = <0x2>;
+		};
+
+		gpio1: gpio1@ff730000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff730000 0x0 0x100>;
+			clocks = <&pmucru PCLK_GPIO1_PMU>;
+			interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+
+			gpio-controller;
+			#gpio-cells = <0x2>;
+
+			interrupt-controller;
+			#interrupt-cells = <0x2>;
+		};
+
+		gpio2: gpio2@ff780000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff780000 0x0 0x100>;
+			clocks = <&cru PCLK_GPIO2>;
+			interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+
+			gpio-controller;
+			#gpio-cells = <0x2>;
+
+			interrupt-controller;
+			#interrupt-cells = <0x2>;
+		};
+
+		gpio3: gpio3@ff788000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff788000 0x0 0x100>;
+			clocks = <&cru PCLK_GPIO3>;
+			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+
+			gpio-controller;
+			#gpio-cells = <0x2>;
+
+			interrupt-controller;
+			#interrupt-cells = <0x2>;
+		};
+
+		gpio4: gpio4@ff790000 {
+			compatible = "rockchip,gpio-bank";
+			reg = <0x0 0xff790000 0x0 0x100>;
+			clocks = <&cru PCLK_GPIO4>;
+			interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
+
+			gpio-controller;
+			#gpio-cells = <0x2>;
+
+			interrupt-controller;
+			#interrupt-cells = <0x2>;
+		};
+
+		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_12ma: pcfg-pull-none-12ma {
+			bias-disable;
+			drive-strength = <12>;
+		};
+
+		pcfg_pull_up_8ma: pcfg-pull-up-8ma {
+			bias-pull-up;
+			drive-strength = <8>;
+		};
+
+		pcfg_pull_down_4ma: pcfg-pull-down-4ma {
+			bias-pull-down;
+			drive-strength = <4>;
+		};
+
+		pcfg_pull_up_2ma: pcfg-pull-up-2ma {
+			bias-pull-up;
+			drive-strength = <2>;
+		};
+
+		pcfg_pull_down_12ma: pcfg-pull-down-12ma {
+			bias-pull-down;
+			drive-strength = <12>;
+		};
+
+		pcfg_pull_none_13ma: pcfg-pull-none-13ma {
+			bias-disable;
+			drive-strength = <13>;
+		};
+
+		i2c0 {
+			i2c0_xfer: i2c0-xfer {
+				rockchip,pins =
+					<1 15 RK_FUNC_2 &pcfg_pull_none>,
+					<1 16 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		i2c1 {
+			i2c1_xfer: i2c1-xfer {
+				rockchip,pins =
+					<4 2 RK_FUNC_1 &pcfg_pull_none>,
+					<4 1 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2c2 {
+			i2c2_xfer: i2c2-xfer {
+				rockchip,pins =
+					<2 1 RK_FUNC_2 &pcfg_pull_none_12ma>,
+					<2 0 RK_FUNC_2 &pcfg_pull_none_12ma>;
+			};
+		};
+
+		i2c3 {
+			i2c3_xfer: i2c3-xfer {
+				rockchip,pins =
+					<4 17 RK_FUNC_1 &pcfg_pull_none>,
+					<4 16 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2c4 {
+			i2c4_xfer: i2c4-xfer {
+				rockchip,pins =
+					<1 12 RK_FUNC_1 &pcfg_pull_none>,
+					<1 11 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2c5 {
+			i2c5_xfer: i2c5-xfer {
+				rockchip,pins =
+					<3 11 RK_FUNC_2 &pcfg_pull_none>,
+					<3 10 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		i2c6 {
+			i2c6_xfer: i2c6-xfer {
+				rockchip,pins =
+					<2 10 RK_FUNC_2 &pcfg_pull_none>,
+					<2 9 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		i2c7 {
+			i2c7_xfer: i2c7-xfer {
+				rockchip,pins =
+					<2 8 RK_FUNC_2 &pcfg_pull_none>,
+					<2 7 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		i2c8 {
+			i2c8_xfer: i2c8-xfer {
+				rockchip,pins =
+					<1 21 RK_FUNC_1 &pcfg_pull_none>,
+					<1 20 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2s0 {
+			i2s0_8ch_bus: i2s0-8ch-bus {
+				rockchip,pins =
+					<3 24 RK_FUNC_1 &pcfg_pull_none>,
+					<3 25 RK_FUNC_1 &pcfg_pull_none>,
+					<3 26 RK_FUNC_1 &pcfg_pull_none>,
+					<3 27 RK_FUNC_1 &pcfg_pull_none>,
+					<3 28 RK_FUNC_1 &pcfg_pull_none>,
+					<3 29 RK_FUNC_1 &pcfg_pull_none>,
+					<3 30 RK_FUNC_1 &pcfg_pull_none>,
+					<3 31 RK_FUNC_1 &pcfg_pull_none>,
+					<4 0 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		i2s1 {
+			i2s1_2ch_bus: i2s1-2ch-bus {
+				rockchip,pins =
+					<4 3 RK_FUNC_1 &pcfg_pull_none>,
+					<4 4 RK_FUNC_1 &pcfg_pull_none>,
+					<4 5 RK_FUNC_1 &pcfg_pull_none>,
+					<4 6 RK_FUNC_1 &pcfg_pull_none>,
+					<4 7 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		spdif {
+			spdif_bus: spdif-bus {
+				rockchip,pins =
+					<4 21 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		spi0 {
+			spi0_clk: spi0-clk {
+				rockchip,pins =
+					<3 6 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi0_cs0: spi0-cs0 {
+				rockchip,pins =
+					<3 7 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi0_cs1: spi0-cs1 {
+				rockchip,pins =
+					<3 8 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi0_tx: spi0-tx {
+				rockchip,pins =
+					<3 5 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi0_rx: spi0-rx {
+				rockchip,pins =
+					<3 4 RK_FUNC_2 &pcfg_pull_up>;
+			};
+		};
+
+		spi1 {
+			spi1_clk: spi1-clk {
+				rockchip,pins =
+					<1 9 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi1_cs0: spi1-cs0 {
+				rockchip,pins =
+					<1 10 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi1_rx: spi1-rx {
+				rockchip,pins =
+					<1 7 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi1_tx: spi1-tx {
+				rockchip,pins =
+					<1 8 RK_FUNC_2 &pcfg_pull_up>;
+			};
+		};
+
+		spi2 {
+			spi2_clk: spi2-clk {
+				rockchip,pins =
+					<2 11 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi2_cs0: spi2-cs0 {
+				rockchip,pins =
+					<2 12 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi2_rx: spi2-rx {
+				rockchip,pins =
+					<2 9 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi2_tx: spi2-tx {
+				rockchip,pins =
+					<2 10 RK_FUNC_1 &pcfg_pull_up>;
+			};
+		};
+
+		spi3 {
+			spi3_clk: spi3-clk {
+				rockchip,pins =
+					<1 17 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi3_cs0: spi3-cs0 {
+				rockchip,pins =
+					<1 18 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi3_rx: spi3-rx {
+				rockchip,pins =
+					<1 15 RK_FUNC_1 &pcfg_pull_up>;
+			};
+			spi3_tx: spi3-tx {
+				rockchip,pins =
+					<1 16 RK_FUNC_1 &pcfg_pull_up>;
+			};
+		};
+
+		spi4 {
+			spi4_clk: spi4-clk {
+				rockchip,pins =
+					<3 2 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi4_cs0: spi4-cs0 {
+				rockchip,pins =
+					<3 3 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi4_rx: spi4-rx {
+				rockchip,pins =
+					<3 0 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi4_tx: spi4-tx {
+				rockchip,pins =
+					<3 1 RK_FUNC_2 &pcfg_pull_up>;
+			};
+		};
+
+		spi5 {
+			spi5_clk: spi5-clk {
+				rockchip,pins =
+					<2 22 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi5_cs0: spi5-cs0 {
+				rockchip,pins =
+					<2 23 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi5_rx: spi5-rx {
+				rockchip,pins =
+					<2 20 RK_FUNC_2 &pcfg_pull_up>;
+			};
+			spi5_tx: spi5-tx {
+				rockchip,pins =
+					<2 21 RK_FUNC_2 &pcfg_pull_up>;
+			};
+		};
+
+		uart0 {
+			uart0_xfer: uart0-xfer {
+				rockchip,pins =
+					<2 16 RK_FUNC_1 &pcfg_pull_up>,
+					<2 17 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart0_cts: uart0-cts {
+				rockchip,pins =
+					<2 18 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			uart0_rts: uart0-rts {
+				rockchip,pins =
+					<2 19 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		uart1 {
+			uart1_xfer: uart1-xfer {
+				rockchip,pins =
+					<3 12 RK_FUNC_2 &pcfg_pull_up>,
+					<3 13 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		uart2a {
+			uart2a_xfer: uart2a-xfer {
+				rockchip,pins =
+					<4 8 RK_FUNC_2 &pcfg_pull_up>,
+					<4 9 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		uart2b {
+			uart2b_xfer: uart2b-xfer {
+				rockchip,pins =
+					<4 16 RK_FUNC_2 &pcfg_pull_up>,
+					<4 17 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		uart2c {
+			uart2c_xfer: uart2c-xfer {
+				rockchip,pins =
+					<4 19 RK_FUNC_1 &pcfg_pull_up>,
+					<4 20 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		uart3 {
+			uart3_xfer: uart3-xfer {
+				rockchip,pins =
+					<3 14 RK_FUNC_2 &pcfg_pull_up>,
+					<3 15 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			uart3_cts: uart3-cts {
+				rockchip,pins =
+					<3 18 RK_FUNC_2 &pcfg_pull_none>;
+			};
+
+			uart3_rts: uart3-rts {
+				rockchip,pins =
+					<3 19 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		uart4 {
+			uart4_xfer: uart4-xfer {
+				rockchip,pins =
+					<1 7 RK_FUNC_1 &pcfg_pull_up>,
+					<1 8 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		uarthdcp {
+			uarthdcp_xfer: uarthdcp-xfer {
+				rockchip,pins =
+					<4 21 RK_FUNC_2 &pcfg_pull_up>,
+					<4 22 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		pwm0 {
+			pwm0_pin: pwm0-pin {
+				rockchip,pins =
+					<4 18 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			vop0_pwm_pin: vop0-pwm-pin {
+				rockchip,pins =
+					<4 18 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+
+		pwm1 {
+			pwm1_pin: pwm1-pin {
+				rockchip,pins =
+					<4 22 RK_FUNC_1 &pcfg_pull_none>;
+			};
+
+			vop1_pwm_pin: vop1-pwm-pin {
+				rockchip,pins =
+					<4 18 RK_FUNC_3 &pcfg_pull_none>;
+			};
+		};
+
+		pwm2 {
+			pwm2_pin: pwm2-pin {
+				rockchip,pins =
+					<1 19 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwm3a {
+			pwm3a_pin: pwm3a-pin {
+				rockchip,pins =
+					<0 6 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
+		pwm3b {
+			pwm3b_pin: pwm3b-pin {
+				rockchip,pins =
+					<1 14 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+	};
+};
diff --git a/arch/arm/dts/sun5i-a10s.dtsi b/arch/arm/dts/sun5i-a10s.dtsi
index bddd0de..a5f8855 100644
--- a/arch/arm/dts/sun5i-a10s.dtsi
+++ b/arch/arm/dts/sun5i-a10s.dtsi
@@ -241,6 +241,20 @@
 		allwinner,drive = <SUN4I_PINCTRL_30_MA>;
 		allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 	};
+
+	nand_cs2_pins_a: nand_cs@2 {
+		allwinner,pins = "PC17";
+		allwinner,function = "nand0";
+		allwinner,drive = <0>;
+		allwinner,pull = <0>;
+	};
+
+	nand_cs3_pins_a: nand_cs@3 {
+		allwinner,pins = "PC18";
+		allwinner,function = "nand0";
+		allwinner,drive = <0>;
+		allwinner,pull = <0>;
+	};
 };
 
 &sram_a {
diff --git a/arch/arm/dts/sun5i-a13-olinuxino.dts b/arch/arm/dts/sun5i-a13-olinuxino.dts
index b3c234c..30e069a 100644
--- a/arch/arm/dts/sun5i-a13-olinuxino.dts
+++ b/arch/arm/dts/sun5i-a13-olinuxino.dts
@@ -155,6 +155,21 @@
 	status = "okay";
 };
 
+&nfc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+	status = "okay";
+
+	nand@0 {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		reg = <0>;
+		allwinner,rb = <0>;
+		nand-ecc-mode = "hw";
+		allwinner,randomize;
+	};
+};
+
 &ohci0 {
 	status = "okay";
 };
diff --git a/arch/arm/dts/sun5i-r8-chip.dts b/arch/arm/dts/sun5i-r8-chip.dts
index 6ad19e2..b1b62d5 100644
--- a/arch/arm/dts/sun5i-r8-chip.dts
+++ b/arch/arm/dts/sun5i-r8-chip.dts
@@ -142,6 +142,21 @@
 	status = "okay";
 };
 
+&nfc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+	status = "okay";
+
+	nand@0 {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		reg = <0>;
+		allwinner,rb = <0>;
+		nand-ecc-mode = "hw";
+		nand-on-flash-bbt;
+	};
+};
+
 &ohci0 {
 	status = "okay";
 };
diff --git a/arch/arm/dts/sun5i.dtsi b/arch/arm/dts/sun5i.dtsi
index 59a9426..87e5353 100644
--- a/arch/arm/dts/sun5i.dtsi
+++ b/arch/arm/dts/sun5i.dtsi
@@ -356,6 +356,17 @@
 			#dma-cells = <2>;
 		};
 
+		nfc: nand@01c03000 {
+			compatible = "allwinner,sun4i-a10-nand";
+			reg = <0x01c03000 0x1000>;
+			interrupts = <37>;
+			clocks = <&ahb_gates 13>, <&nand_clk>;
+			clock-names = "ahb", "mod";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
 		spi0: spi@01c05000 {
 			compatible = "allwinner,sun4i-a10-spi";
 			reg = <0x01c05000 0x1000>;
@@ -548,6 +559,44 @@
 				allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
 			};
 
+			nand_pins_a: nand_base0@0 {
+				allwinner,pins = "PC0", "PC1", "PC2",
+						"PC5", "PC8", "PC9", "PC10",
+						"PC11", "PC12", "PC13", "PC14",
+						"PC15";
+				allwinner,function = "nand0";
+				allwinner,drive = <0>;
+				allwinner,pull = <0>;
+			};
+
+			nand_cs0_pins_a: nand_cs@0 {
+				allwinner,pins = "PC4";
+				allwinner,function = "nand0";
+				allwinner,drive = <0>;
+				allwinner,pull = <0>;
+			};
+
+			nand_cs1_pins_a: nand_cs@1 {
+				allwinner,pins = "PC3";
+				allwinner,function = "nand0";
+				allwinner,drive = <0>;
+				allwinner,pull = <0>;
+			};
+
+			nand_rb0_pins_a: nand_rb@0 {
+				allwinner,pins = "PC6";
+				allwinner,function = "nand0";
+				allwinner,drive = <0>;
+				allwinner,pull = <0>;
+			};
+
+			nand_rb1_pins_a: nand_rb@1 {
+				allwinner,pins = "PC7";
+				allwinner,function = "nand0";
+				allwinner,drive = <0>;
+				allwinner,pull = <0>;
+			};
+
 			uart3_pins_a: uart3@0 {
 				allwinner,pins = "PG9", "PG10";
 				allwinner,function = "uart3";
diff --git a/arch/arm/include/asm/arch-omap4/i2c.h b/arch/arm/include/asm/arch-omap4/i2c.h
index adc8eb2..463e979 100644
--- a/arch/arm/include/asm/arch-omap4/i2c.h
+++ b/arch/arm/include/asm/arch-omap4/i2c.h
@@ -14,9 +14,9 @@
 	unsigned short revnb_lo;	/* 0x00 */
 	unsigned short res1;
 	unsigned short revnb_hi;	/* 0x04 */
-	unsigned short res2[13];
-	unsigned short sysc;		/* 0x20 */
-	unsigned short res3;
+	unsigned short res2[5];
+	unsigned short sysc;		/* 0x10 */
+	unsigned short res3[9];
 	unsigned short irqstatus_raw;	/* 0x24 */
 	unsigned short res4;
 	unsigned short stat;		/* 0x28 */
diff --git a/arch/arm/include/asm/arch-omap5/i2c.h b/arch/arm/include/asm/arch-omap5/i2c.h
index d875cfe..2b55edf 100644
--- a/arch/arm/include/asm/arch-omap5/i2c.h
+++ b/arch/arm/include/asm/arch-omap5/i2c.h
@@ -14,9 +14,9 @@
 	unsigned short revnb_lo;	/* 0x00 */
 	unsigned short res1;
 	unsigned short revnb_hi;	/* 0x04 */
-	unsigned short res2[13];
-	unsigned short sysc;		/* 0x20 */
-	unsigned short res3;
+	unsigned short res2[5];
+	unsigned short sysc;		/* 0x10 */
+	unsigned short res3[9];
 	unsigned short irqstatus_raw;	/* 0x24 */
 	unsigned short res4;
 	unsigned short stat;		/* 0x28 */
diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h
index 317e512..21edbc2 100644
--- a/arch/arm/include/asm/arch-rockchip/clock.h
+++ b/arch/arm/include/asm/arch-rockchip/clock.h
@@ -65,6 +65,8 @@
 struct rk3288_cru;
 struct rk3288_grf;
 
-void rkclk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf);
+void rk3288_clk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf);
+
+int rockchip_get_clk(struct udevice **devp);
 
 #endif
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
index 0088bb9..d1c5ad0 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
@@ -269,6 +269,11 @@
 #define CCM_MBUS_CTRL_CLK_SRC_PLL5 0x2
 #define CCM_MBUS_CTRL_GATE (0x1 << 31)
 
+#define CCM_NAND_CTRL_M(x)		((x) - 1)
+#define CCM_NAND_CTRL_N(x)		((x) << 16)
+#define CCM_NAND_CTRL_OSCM24		(0x0 << 24)
+#define CCM_NAND_CTRL_PLL6		(0x1 << 24)
+#define CCM_NAND_CTRL_PLL5		(0x2 << 24)
 #define CCM_NAND_CTRL_ENABLE		(0x1 << 31)
 
 #define CCM_MMC_CTRL_M(x)		((x) - 1)
diff --git a/arch/arm/mach-keystone/init.c b/arch/arm/mach-keystone/init.c
index 3b6d5ef..6e5a1e1 100644
--- a/arch/arm/mach-keystone/init.c
+++ b/arch/arm/mach-keystone/init.c
@@ -101,9 +101,7 @@
 	msmc_share_all_segments(KS2_MSMC_SEGMENT_C6X_0);
 	msmc_share_all_segments(K2HKLE_MSMC_SEGMENT_ARM);
 	msmc_share_all_segments(K2HKLE_MSMC_SEGMENT_NETCP);
-#ifdef KS2_MSMC_SEGMENT_QM_PDSP
 	msmc_share_all_segments(K2HKLE_MSMC_SEGMENT_QM_PDSP);
-#endif
 	msmc_share_all_segments(K2HKLE_MSMC_SEGMENT_PCIE0);
 	msmc_share_all_segments(KS2_MSMC_SEGMENT_DEBUG);
 }
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
index c49cc19..1aac3c8 100644
--- a/arch/arm/mach-rockchip/Kconfig
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -1,7 +1,21 @@
 if ARCH_ROCKCHIP
 
+config ROCKCHIP_RK3036
+	bool "Support Rockchip RK3036"
+	select CPU_V7
+	select SUPPORT_SPL
+	select SPL
+	help
+	  The Rockchip RK3036 is a ARM-based SoC with a dual-core Cortex-A7
+	  including NEON and GPU, Mali-400 graphics, several DDR3 options
+	  and video codec support. Peripherals include Gigabit Ethernet,
+	  USB2 host and OTG, SDIO, I2S, UART, SPI, I2C and PWMs.
+
 config ROCKCHIP_RK3288
 	bool "Support Rockchip RK3288"
+	select CPU_V7
+	select SUPPORT_SPL
+	select SPL
 	help
 	  The Rockchip RK3288 is a ARM-based SoC with a quad-core Cortex-A17
 	  including NEON and GPU, 1MB L2 cache, Mali-T7 graphics, two
@@ -9,14 +23,26 @@
 	  and video codec support. Peripherals include Gigabit Ethernet,
 	  USB2 host and OTG, SDIO, I2S, UART,s, SPI, I2C and PWMs.
 
-config ROCKCHIP_RK3036
-	bool "Support Rockchip RK3036"
+config ROCKCHIP_RK3399
+	bool "Support Rockchip RK3399"
+	select ARM64
 	help
-	  The Rockchip RK3036 is a ARM-based SoC with a dual-core Cortex-A7
-	  including NEON and GPU, Mali-400 graphics, several DDR3 options
+	  The Rockchip RK3399 is a ARM-based SoC with a dual-core Cortex-A72
+	  and 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, UART, SPI, I2C and PWMs.
+	  USB2 host and OTG, SDIO, I2S, UARTs, SPI, I2C and PWMs.
 
-source "arch/arm/mach-rockchip/rk3288/Kconfig"
+config ROCKCHIP_SPL_BACK_TO_BROM
+	bool "SPL returns to bootrom"
+	default y if ROCKCHIP_RK3036
+	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.
+
 source "arch/arm/mach-rockchip/rk3036/Kconfig"
+source "arch/arm/mach-rockchip/rk3288/Kconfig"
+source "arch/arm/mach-rockchip/rk3399/Kconfig"
 endif
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index 55567cb..157d42f 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -5,11 +5,15 @@
 #
 
 ifdef CONFIG_SPL_BUILD
-obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288-board-spl.o
 obj-$(CONFIG_ROCKCHIP_RK3036) += rk3036-board-spl.o
+obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288-board-spl.o
+obj-$(CONFIG_ROCKCHIP_SPL_BACK_TO_BROM) += save_boot_param.o
 else
 obj-$(CONFIG_ROCKCHIP_RK3288) += board.o
 endif
+ifndef CONFIG_ARM64
 obj-y += rk_timer.o
-obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288/
+endif
 obj-$(CONFIG_ROCKCHIP_RK3036) += rk3036/
+obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288/
+obj-$(CONFIG_ROCKCHIP_RK3399) += rk3399/
diff --git a/arch/arm/mach-rockchip/board.c b/arch/arm/mach-rockchip/board.c
index 816540e..bec756d 100644
--- a/arch/arm/mach-rockchip/board.c
+++ b/arch/arm/mach-rockchip/board.c
@@ -10,12 +10,45 @@
 #include <ram.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
+#include <asm/arch/periph.h>
+#include <asm/gpio.h>
+#include <dm/pinctrl.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
 int board_init(void)
 {
+#ifdef CONFIG_ROCKCHIP_SPL_BACK_TO_BROM
+	struct udevice *pinctrl;
+	int ret;
+
+    /*
+     * We need to implement sdcard iomux here for the further
+     * initlization, 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)
@@ -52,6 +85,78 @@
 {
 }
 
+#if defined(CONFIG_USB_GADGET) && defined(CONFIG_USB_GADGET_DWC2_OTG)
+#include <usb.h>
+#include <usb/dwc2_udc.h>
+
+static struct dwc2_plat_otg_data rk3288_otg_data = {
+	.rx_fifo_sz	= 512,
+	.np_tx_fifo_sz	= 16,
+	.tx_fifo_sz	= 128,
+};
+
+int board_usb_init(int index, enum usb_init_type init)
+{
+	int node, phy_node;
+	const char *mode;
+	bool matched = false;
+	const void *blob = gd->fdt_blob;
+	u32 grf_phy_offset;
+
+	/* find the usb_otg node */
+	node = fdt_node_offset_by_compatible(blob, -1,
+					"rockchip,rk3288-usb");
+
+	while (node > 0) {
+		mode = fdt_getprop(blob, node, "dr_mode", NULL);
+		if (mode && strcmp(mode, "otg") == 0) {
+			matched = true;
+			break;
+		}
+
+		node = fdt_node_offset_by_compatible(blob, node,
+					"rockchip,rk3288-usb");
+	}
+	if (!matched) {
+		debug("Not found usb_otg device\n");
+		return -ENODEV;
+	}
+	rk3288_otg_data.regs_otg = fdtdec_get_addr(blob, node, "reg");
+
+	node = fdtdec_lookup_phandle(blob, node, "phys");
+	if (node <= 0) {
+		debug("Not found usb phy device\n");
+		return -ENODEV;
+	}
+
+	phy_node = fdt_parent_offset(blob, node);
+	if (phy_node <= 0) {
+		debug("Not found usb phy device\n");
+		return -ENODEV;
+	}
+
+	rk3288_otg_data.phy_of_node = phy_node;
+	grf_phy_offset = fdtdec_get_addr(blob, node, "reg");
+
+	/* find the grf node */
+	node = fdt_node_offset_by_compatible(blob, -1,
+					"rockchip,rk3288-grf");
+	if (node <= 0) {
+		debug("Not found grf device\n");
+		return -ENODEV;
+	}
+	rk3288_otg_data.regs_phy = grf_phy_offset +
+				fdtdec_get_addr(blob, node, "reg");
+
+	return dwc2_udc_probe(&rk3288_otg_data);
+}
+
+int board_usb_cleanup(int index, enum usb_init_type init)
+{
+	return 0;
+}
+#endif
+
 static int do_clock(cmd_tbl_t *cmdtp, int flag, int argc,
 		       char * const argv[])
 {
@@ -73,7 +178,7 @@
 	int ret, i;
 	struct udevice *dev;
 
-	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
+	ret = rockchip_get_clk(&dev);
 	if (ret) {
 		printf("clk-uclass not found\n");
 		return 0;
diff --git a/arch/arm/mach-rockchip/rk3036/Kconfig b/arch/arm/mach-rockchip/rk3036/Kconfig
index cc03808..f7562bd 100644
--- a/arch/arm/mach-rockchip/rk3036/Kconfig
+++ b/arch/arm/mach-rockchip/rk3036/Kconfig
@@ -15,7 +15,7 @@
 config ROCKCHIP_COMMON
 	bool "Support rk common fuction"
 
-source "board/evb_rk3036/evb_rk3036/Kconfig"
-source "board/kylin/kylin_rk3036/Kconfig"
+source "board/rockchip/evb_rk3036/Kconfig"
+source "board/rockchip/kylin_rk3036/Kconfig"
 
 endif
diff --git a/arch/arm/mach-rockchip/rk3036/Makefile b/arch/arm/mach-rockchip/rk3036/Makefile
index 97d299d..6095777 100644
--- a/arch/arm/mach-rockchip/rk3036/Makefile
+++ b/arch/arm/mach-rockchip/rk3036/Makefile
@@ -10,4 +10,3 @@
 endif
 
 obj-y += sdram_rk3036.o
-obj-y += save_boot_param.o
diff --git a/arch/arm/mach-rockchip/rk3288-board-spl.c b/arch/arm/mach-rockchip/rk3288-board-spl.c
index 123f58b..ed14023 100644
--- a/arch/arm/mach-rockchip/rk3288-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3288-board-spl.c
@@ -149,7 +149,7 @@
 	return 0;
 }
 #endif
-
+extern void back_to_bootrom(void);
 void board_init_f(ulong dummy)
 {
 	struct udevice *pinctrl;
@@ -187,7 +187,7 @@
 	rockchip_timer_init();
 	configure_l2ctlr();
 
-	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
+	ret = rockchip_get_clk(&dev);
 	if (ret) {
 		debug("CLK init failed: %d\n", ret);
 		return;
@@ -204,6 +204,9 @@
 		debug("DRAM init failed: %d\n", ret);
 		return;
 	}
+#ifdef CONFIG_ROCKCHIP_SPL_BACK_TO_BROM
+	back_to_bootrom();
+#endif
 }
 
 static int setup_led(void)
@@ -248,7 +251,8 @@
 	}
 #ifdef CONFIG_SPL_MMC_SUPPORT
 	if (!IS_ENABLED(CONFIG_TARGET_ROCK2) &&
-	    !IS_ENABLED(CONFIG_TARGET_FIREFLY_RK3288)) {
+	    !IS_ENABLED(CONFIG_TARGET_FIREFLY_RK3288) &&
+	    !IS_ENABLED(CONFIG_TARGET_EVB_RK3288)) {
 		ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_SDCARD);
 		if (ret) {
 			debug("%s: Failed to set up SD card\n", __func__);
diff --git a/arch/arm/mach-rockchip/rk3288/Kconfig b/arch/arm/mach-rockchip/rk3288/Kconfig
index 7215624..031dbfc 100644
--- a/arch/arm/mach-rockchip/rk3288/Kconfig
+++ b/arch/arm/mach-rockchip/rk3288/Kconfig
@@ -8,6 +8,14 @@
 	  also includes on-board eMMC and 1GB of SDRAM. Expansion connectors
 	  provide access to display pins, I2C, SPI, UART and GPIOs.
 
+config TARGET_EVB_RK3288
+	bool "Evb-RK3288"
+	help
+	  EVB-RK3288 is a RK3288-based development board with 2 USB ports,
+	  HDMI, VGA, micro-SD card, audio, WiFi  and Gigabit Ethernet, It
+	  also includes on-board eMMC and 2GB of SDRAM. Expansion connectors
+	  provide access to display pins, I2C, SPI, UART and GPIOs.
+
 config TARGET_CHROMEBOOK_JERRY
 	bool "Google/Rockchip Veyron-Jerry Chromebook"
 	help
@@ -45,4 +53,6 @@
 
 source "board/radxa/rock2/Kconfig"
 
+source "board/evb-rk3288/evb-rk3288/Kconfig"
+
 endif
diff --git a/arch/arm/mach-rockchip/rk3288/Makefile b/arch/arm/mach-rockchip/rk3288/Makefile
index 6f62375..82b00a1 100644
--- a/arch/arm/mach-rockchip/rk3288/Makefile
+++ b/arch/arm/mach-rockchip/rk3288/Makefile
@@ -4,6 +4,7 @@
 # SPDX-License-Identifier:      GPL-2.0+
 #
 
+obj-y += clk_rk3288.o
 obj-y += reset_rk3288.o
 obj-y += sdram_rk3288.o
 obj-y += syscon_rk3288.o
diff --git a/arch/arm/mach-rockchip/rk3288/clk_rk3288.c b/arch/arm/mach-rockchip/rk3288/clk_rk3288.c
new file mode 100644
index 0000000..2099e34
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3288/clk_rk3288.c
@@ -0,0 +1,17 @@
+/*
+ * 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>
+
+int rockchip_get_clk(struct udevice **devp)
+{
+	return uclass_get_device_by_driver(UCLASS_CLK,
+			DM_GET_DRIVER(rockchip_rk3288_cru), devp);
+}
diff --git a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
index b36b6af..cf9ef2e 100644
--- a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
+++ b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
@@ -575,14 +575,14 @@
 			&sdram_params->ch[chan];
 
 		sys_reg |= info->row_3_4 << SYS_REG_ROW_3_4_SHIFT(chan);
-		sys_reg |= chan << SYS_REG_CHINFO_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 ? 1 << SYS_REG_BK_SHIFT(chan) : 0;
+		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 |= info->bw << SYS_REG_BW_SHIFT(chan);
-		sys_reg |= info->dbw << SYS_REG_DBW_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);
 	}
@@ -734,13 +734,13 @@
 		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 = sys_reg & (1 << SYS_REG_BK_SHIFT(ch)) ? 3 : 0;
+		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 = (sys_reg >> SYS_REG_BW_SHIFT(ch)) &
-			SYS_REG_BW_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;
 
@@ -784,7 +784,7 @@
 		return ret;
 	udelay(100);/* Must wait for voltage to stabilize, 2mV/us */
 
-	rkclk_configure_cpu(priv->cru, priv->grf);
+	rk3288_clk_configure_cpu(priv->cru, priv->grf);
 
 	return 0;
 }
@@ -923,7 +923,7 @@
 	priv->chan[1].pctl = regmap_get_range(plat->map, 2);
 	priv->chan[1].publ = regmap_get_range(plat->map, 3);
 #endif
-	ret = uclass_get_device(UCLASS_CLK, 0, &dev_clk);
+	ret = rockchip_get_clk(&dev_clk);
 	if (ret)
 		return ret;
 	priv->ddr_clk.id = CLK_DDR;
diff --git a/arch/arm/mach-rockchip/rk3399/Kconfig b/arch/arm/mach-rockchip/rk3399/Kconfig
new file mode 100644
index 0000000..83bd04a
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3399/Kconfig
@@ -0,0 +1,23 @@
+if ROCKCHIP_RK3399
+
+choice
+	prompt "RK3399 board select"
+
+config TARGET_EVB_RK3399
+	bool "RK3399 evaluation board"
+	help
+	  RK3399evb is a evaluation board for Rockchp rk3399,
+	  with full function and phisical connectors support like type-C ports,
+	  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_rk3399/Kconfig"
+
+endif
diff --git a/arch/arm/mach-rockchip/rk3399/Makefile b/arch/arm/mach-rockchip/rk3399/Makefile
new file mode 100644
index 0000000..3f219ac
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3399/Makefile
@@ -0,0 +1,7 @@
+#
+# (C) Copyright 2016 Rockchip Electronics Co., Ltd
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y += rk3399.o
diff --git a/arch/arm/mach-rockchip/rk3399/rk3399.c b/arch/arm/mach-rockchip/rk3399/rk3399.c
new file mode 100644
index 0000000..b9d7629
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3399/rk3399.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/armv8/mmu.h>
+
+static struct mm_region rk3399_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 = rk3399_mem_map;
diff --git a/arch/arm/mach-rockchip/rk3036/save_boot_param.S b/arch/arm/mach-rockchip/save_boot_param.S
similarity index 89%
rename from arch/arm/mach-rockchip/rk3036/save_boot_param.S
rename to arch/arm/mach-rockchip/save_boot_param.S
index 778ec83..85b407b 100644
--- a/arch/arm/mach-rockchip/rk3036/save_boot_param.S
+++ b/arch/arm/mach-rockchip/save_boot_param.S
@@ -1,5 +1,5 @@
 /*
- * (C) Copyright 2015 Google, Inc
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
  *
  * SPDX-License-Identifier:     GPL-2.0+
  */
diff --git a/board/evb-rk3288/evb-rk3288/Kconfig b/board/evb-rk3288/evb-rk3288/Kconfig
new file mode 100644
index 0000000..b201acb
--- /dev/null
+++ b/board/evb-rk3288/evb-rk3288/Kconfig
@@ -0,0 +1,15 @@
+if TARGET_EVB_RK3288
+
+config SYS_BOARD
+	default "evb-rk3288"
+
+config SYS_VENDOR
+	default "evb-rk3288"
+
+config SYS_CONFIG_NAME
+	default "evb-rk3288"
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+	def_bool y
+
+endif
diff --git a/board/evb-rk3288/evb-rk3288/MAINTAINERS b/board/evb-rk3288/evb-rk3288/MAINTAINERS
new file mode 100644
index 0000000..222c254
--- /dev/null
+++ b/board/evb-rk3288/evb-rk3288/MAINTAINERS
@@ -0,0 +1,6 @@
+EVB-RK3288
+M:	Lin Huang <hl@rock-chips.com>
+S:	Maintained
+F:	board/evb-rk3288/evb-rk3288
+F:	include/configs/evb-rk3288.h
+F:	configs/evb-rk3288_defconfig
diff --git a/board/evb-rk3288/evb-rk3288/Makefile b/board/evb-rk3288/evb-rk3288/Makefile
new file mode 100644
index 0000000..c11b657
--- /dev/null
+++ b/board/evb-rk3288/evb-rk3288/Makefile
@@ -0,0 +1,7 @@
+#
+# (C) Copyright 2016 Rockchip Electronics Co., Ltd
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y	+= evb-rk3288.o
diff --git a/board/evb-rk3288/evb-rk3288/evb-rk3288.c b/board/evb-rk3288/evb-rk3288/evb-rk3288.c
new file mode 100644
index 0000000..a82f0ae
--- /dev/null
+++ b/board/evb-rk3288/evb-rk3288/evb-rk3288.c
@@ -0,0 +1,15 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <spl.h>
+
+void board_boot_order(u32 *spl_boot_list)
+{
+	/* eMMC prior to sdcard. */
+	spl_boot_list[0] = BOOT_DEVICE_MMC2;
+	spl_boot_list[1] = BOOT_DEVICE_MMC1;
+}
diff --git a/board/evb_rk3036/evb_rk3036/Kconfig b/board/rockchip/evb_rk3036/Kconfig
similarity index 89%
rename from board/evb_rk3036/evb_rk3036/Kconfig
rename to board/rockchip/evb_rk3036/Kconfig
index ae2a9eb..ef45f62 100644
--- a/board/evb_rk3036/evb_rk3036/Kconfig
+++ b/board/rockchip/evb_rk3036/Kconfig
@@ -4,7 +4,7 @@
 	default "evb_rk3036"
 
 config SYS_VENDOR
-	default "evb_rk3036"
+	default "rockchip"
 
 config SYS_CONFIG_NAME
 	default "evb_rk3036"
diff --git a/board/evb_rk3036/evb_rk3036/MAINTAINERS b/board/rockchip/evb_rk3036/MAINTAINERS
similarity index 100%
rename from board/evb_rk3036/evb_rk3036/MAINTAINERS
rename to board/rockchip/evb_rk3036/MAINTAINERS
diff --git a/board/evb_rk3036/evb_rk3036/Makefile b/board/rockchip/evb_rk3036/Makefile
similarity index 100%
rename from board/evb_rk3036/evb_rk3036/Makefile
rename to board/rockchip/evb_rk3036/Makefile
diff --git a/board/evb_rk3036/evb_rk3036/evb_rk3036.c b/board/rockchip/evb_rk3036/evb_rk3036.c
similarity index 100%
rename from board/evb_rk3036/evb_rk3036/evb_rk3036.c
rename to board/rockchip/evb_rk3036/evb_rk3036.c
diff --git a/board/rockchip/evb_rk3399/Kconfig b/board/rockchip/evb_rk3399/Kconfig
new file mode 100644
index 0000000..412b81c
--- /dev/null
+++ b/board/rockchip/evb_rk3399/Kconfig
@@ -0,0 +1,15 @@
+if TARGET_EVB_RK3399
+
+config SYS_BOARD
+	default "evb_rk3399"
+
+config SYS_VENDOR
+	default "rockchip"
+
+config SYS_CONFIG_NAME
+	default "evb_rk3399"
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+	def_bool y
+
+endif
diff --git a/board/rockchip/evb_rk3399/MAINTAINERS b/board/rockchip/evb_rk3399/MAINTAINERS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/board/rockchip/evb_rk3399/MAINTAINERS
diff --git a/board/rockchip/evb_rk3399/Makefile b/board/rockchip/evb_rk3399/Makefile
new file mode 100644
index 0000000..aaa51c2
--- /dev/null
+++ b/board/rockchip/evb_rk3399/Makefile
@@ -0,0 +1,7 @@
+#
+# (C) Copyright 2016 Rockchip Electronics Co., Ltd
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y	+= evb-rk3399.o
diff --git a/board/rockchip/evb_rk3399/README b/board/rockchip/evb_rk3399/README
new file mode 100644
index 0000000..fb8bb19
--- /dev/null
+++ b/board/rockchip/evb_rk3399/README
@@ -0,0 +1,73 @@
+Introduction
+============
+
+RK3399 key features we might use in U-Boot:
+* CPU: ARMv8 64bit Big-Little architecture,
+*      Big: dual-core Cortex-A72
+*      Little: quad-core Cortex-A53
+* IRAM: 200KB
+* DRAM: 4GB-128MB dual-channel
+* eMMC: support eMMC 5.0/5.1, suport HS400, HS200, DDR50
+* SD/MMC: support SD 3.0, MMC 4.51
+* USB: USB3.0 typc-C port *2 with dwc3 controller
+*      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 rk3399.
+
+Get the Source and prebuild binary
+==================================
+
+  > mkdir ~/evb_rk3399
+  > cd ~/evb_rk3399
+  > 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 the ATF
+===============
+
+  > cd arm-trusted-firmware
+  > make realclean
+  > make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3399 bl31
+
+Compile the U-Boot
+==================
+
+  > cd ../u-boot
+  > make CROSS_COMPILE=aarch64-linux-gnu- evb-rk3399_defconfig all
+
+Compile the rkflashtool
+=======================
+
+  > cd ../rkflashtool
+  > make
+
+Package the image for miniloader
+================================
+  > cd ..
+  > cp arm-trusted-firmware/build/rk3399/release/bl31.bin rkbin/rk33
+  > ./rkbin/tools/trust_merger rkbin/tools/RK3399TRUST.ini
+  > ./rkbin/tools/loaderimage --pack --uboot u-boot/u-boot-dtb.bin uboot.img
+  > mkdir image
+  > mv trust.img ./image/
+  > mv uboot.img ./image/rk3399evb-uboot.bin
+
+Flash the image
+===============
+Power on(or reset with RESET KEY) with MASKROM KEY preesed, and then:
+
+  > ./rkflashtool/rkflashloader rk3399evb
+
+You should be able to get U-Boot log message in console/UART2 now.
diff --git a/board/rockchip/evb_rk3399/evb-rk3399.c b/board/rockchip/evb_rk3399/evb-rk3399.c
new file mode 100644
index 0000000..dffacd0
--- /dev/null
+++ b/board/rockchip/evb_rk3399/evb-rk3399.c
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+#include <common.h>
+#include <asm/armv8/mmu.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)
+{
+	gd->bd->bi_dram[0].start = 0;
+	gd->bd->bi_dram[0].size = 0x80000000;
+}
diff --git a/board/kylin/kylin_rk3036/Kconfig b/board/rockchip/kylin_rk3036/Kconfig
similarity index 90%
rename from board/kylin/kylin_rk3036/Kconfig
rename to board/rockchip/kylin_rk3036/Kconfig
index 5d75c1f..8d35b4e 100644
--- a/board/kylin/kylin_rk3036/Kconfig
+++ b/board/rockchip/kylin_rk3036/Kconfig
@@ -4,7 +4,7 @@
 	default "kylin_rk3036"
 
 config SYS_VENDOR
-	default "kylin"
+	default "rockchip"
 
 config SYS_CONFIG_NAME
 	default "kylin_rk3036"
diff --git a/board/kylin/kylin_rk3036/MAINTAINERS b/board/rockchip/kylin_rk3036/MAINTAINERS
similarity index 100%
rename from board/kylin/kylin_rk3036/MAINTAINERS
rename to board/rockchip/kylin_rk3036/MAINTAINERS
diff --git a/board/kylin/kylin_rk3036/Makefile b/board/rockchip/kylin_rk3036/Makefile
similarity index 100%
rename from board/kylin/kylin_rk3036/Makefile
rename to board/rockchip/kylin_rk3036/Makefile
diff --git a/board/kylin/kylin_rk3036/kylin_rk3036.c b/board/rockchip/kylin_rk3036/kylin_rk3036.c
similarity index 100%
rename from board/kylin/kylin_rk3036/kylin_rk3036.c
rename to board/rockchip/kylin_rk3036/kylin_rk3036.c
diff --git a/board/sandbox/README.sandbox b/board/sandbox/README.sandbox
index 9fe3bf1..ed820d3 100644
--- a/board/sandbox/README.sandbox
+++ b/board/sandbox/README.sandbox
@@ -44,6 +44,9 @@
       make sandbox_defconfig all NO_SDL=1
       ./u-boot
 
+   If you are building on a 32-bit machine you may get errors from __ffs.h
+   about shifting more than the machine word size. Edit the config file
+   include/configs/sandbox.h and change CONFIG_SANDBOX_BITS_PER_LONG to 32.
 
 U-Boot will start on your computer, showing a sandbox emulation of the serial
 console:
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index f6e28b0..36cf963 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -136,7 +136,7 @@
 	return 0;
 }
 
-#if defined(CONFIG_NAND_SUNXI) && defined(CONFIG_SPL_BUILD)
+#if defined(CONFIG_NAND_SUNXI)
 static void nand_pinmux_setup(void)
 {
 	unsigned int pin;
@@ -173,6 +173,9 @@
 {
 	nand_pinmux_setup();
 	nand_clock_setup();
+#ifndef CONFIG_SPL_BUILD
+	sunxi_nand_init();
+#endif
 }
 #endif
 
diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index 011f62c..d66892e 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -290,6 +290,11 @@
 
 	/* Patch bootefi_image_path to the target file path */
 	memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str));
-	snprintf(devname, sizeof(devname), "%s", path);
+	if (strcmp(dev, "Net")) {
+		/* Add leading / to fs paths, because they're absolute */
+		snprintf(devname, sizeof(devname), "/%s", path);
+	} else {
+		snprintf(devname, sizeof(devname), "%s", path);
+	}
 	ascii2unicode(bootefi_image_path[0].str, devname);
 }
diff --git a/cmd/lzmadec.c b/cmd/lzmadec.c
index 1ad9ed6..c78df82 100644
--- a/cmd/lzmadec.c
+++ b/cmd/lzmadec.c
@@ -20,7 +20,7 @@
 static int do_lzmadec(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
 {
 	unsigned long src, dst;
-	unsigned long src_len = ~0UL, dst_len = ~0UL;
+	SizeT src_len = ~0UL, dst_len = ~0UL;
 	int ret;
 
 	switch (argc) {
@@ -40,7 +40,8 @@
 
 	if (ret != SZ_OK)
 		return 1;
-	printf("Uncompressed size: %ld = 0x%lX\n", src_len, src_len);
+	printf("Uncompressed size: %ld = %#lX\n", (ulong)src_len,
+	       (ulong)src_len);
 	setenv_hex("filesize", src_len);
 
 	return 0;
diff --git a/cmd/misc.c b/cmd/misc.c
index 39d8683..efcbb90 100644
--- a/cmd/misc.c
+++ b/cmd/misc.c
@@ -15,13 +15,31 @@
 static int do_sleep(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 	ulong start = get_timer(0);
+	ulong mdelay = 0;
 	ulong delay;
+	char *frpart;
 
 	if (argc != 2)
 		return CMD_RET_USAGE;
 
 	delay = simple_strtoul(argv[1], NULL, 10) * CONFIG_SYS_HZ;
 
+	frpart = strchr(argv[1], '.');
+
+	if (frpart) {
+		uint mult = CONFIG_SYS_HZ / 10;
+		for (frpart++; *frpart != '\0' && mult > 0; frpart++) {
+			if (*frpart < '0' || *frpart > '9') {
+				mdelay = 0;
+				break;
+			}
+			mdelay += (*frpart - '0') * mult;
+			mult /= 10;
+		}
+	}
+
+	delay += mdelay;
+
 	while (get_timer(start) < delay) {
 		if (ctrlc())
 			return (-1);
@@ -36,7 +54,8 @@
 	sleep ,    2,    1,     do_sleep,
 	"delay execution for some time",
 	"N\n"
-	"    - delay execution for N seconds (N is _decimal_ !!!)"
+	"    - delay execution for N seconds (N is _decimal_ and can be\n"
+	"      fractional)"
 );
 
 #ifdef CONFIG_CMD_TIMER
diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c
index 3f4f334..b9b160d 100644
--- a/cmd/mtdparts.c
+++ b/cmd/mtdparts.c
@@ -1493,7 +1493,7 @@
 			part = list_entry(pentry, struct part_info, link);
 
 			debug("spread_partitions: device = %s%d, partition %d ="
-				" (%s) 0x%08x@0x%08x\n",
+				" (%s) 0x%08llx@0x%08llx\n",
 				MTD_DEV_TYPE(dev->id->type), dev->id->num,
 				part_num, part->name, part->size,
 				part->offset);
@@ -2025,7 +2025,7 @@
 
 		if (!strcmp(&argv[1][3], ".spread")) {
 			spread_partition(mtd, p, &next_offset);
-			debug("increased %s to %d bytes\n", p->name, p->size);
+			debug("increased %s to %llu bytes\n", p->name, p->size);
 		}
 #endif
 
diff --git a/cmd/nand.c b/cmd/nand.c
index ffdeea4..e10349a 100644
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -306,7 +306,7 @@
 }
 
 static int raw_access(struct mtd_info *mtd, ulong addr, loff_t off,
-		      ulong count, int read)
+		      ulong count, int read, int no_verify)
 {
 	int ret = 0;
 
@@ -324,7 +324,7 @@
 			ret = mtd_read_oob(mtd, off, &ops);
 		} else {
 			ret = mtd_write_oob(mtd, off, &ops);
-			if (!ret)
+			if (!ret && !no_verify)
 				ret = nand_verify_page_oob(mtd, &ops, off);
 		}
 
@@ -546,6 +546,7 @@
 		ulong pagecount = 1;
 		int read;
 		int raw = 0;
+		int no_verify = 0;
 
 		if (argc < 4)
 			goto usage;
@@ -557,9 +558,12 @@
 
 		s = strchr(cmd, '.');
 
-		if (s && !strcmp(s, ".raw")) {
+		if (s && !strncmp(s, ".raw", 4)) {
 			raw = 1;
 
+			if (!strcmp(s, ".raw.noverify"))
+				no_verify = 1;
+
 			if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize,
 					MTD_DEV_TYPE_NAND,
 					nand_info[dev]->size))
@@ -633,7 +637,8 @@
 			else
 				ret = mtd_write_oob(mtd, off, &ops);
 		} else if (raw) {
-			ret = raw_access(mtd, addr, off, pagecount, read);
+			ret = raw_access(mtd, addr, off, pagecount, read,
+					 no_verify);
 		} else {
 			printf("Unknown nand command suffix '%s'.\n", s);
 			return 1;
@@ -786,7 +791,7 @@
 	"    read/write 'size' bytes starting at offset 'off'\n"
 	"    to/from memory address 'addr', skipping bad blocks.\n"
 	"nand read.raw - addr off|partition [count]\n"
-	"nand write.raw - addr off|partition [count]\n"
+	"nand write.raw[.noverify] - addr off|partition [count]\n"
 	"    Use read.raw/write.raw to avoid ECC and access the flash as-is.\n"
 #ifdef CONFIG_CMD_NAND_TRIMFFS
 	"nand write.trimffs - addr off|partition size\n"
diff --git a/common/image-fit.c b/common/image-fit.c
index 6f920da..73ad34e 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -1684,12 +1684,13 @@
 
 	bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL);
 	type_ok = fit_image_check_type(fit, noffset, image_type) ||
-		(image_type == IH_TYPE_KERNEL &&
-			fit_image_check_type(fit, noffset,
-					     IH_TYPE_KERNEL_NOLOAD));
+		  fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE) ||
+		  (image_type == IH_TYPE_KERNEL &&
+		   fit_image_check_type(fit, noffset, IH_TYPE_KERNEL_NOLOAD));
 
 	os_ok = image_type == IH_TYPE_FLATDT || IH_TYPE_FPGA ||
 		fit_image_check_os(fit, noffset, IH_OS_LINUX) ||
+		fit_image_check_os(fit, noffset, IH_OS_U_BOOT) ||
 		fit_image_check_os(fit, noffset, IH_OS_OPENRTOS);
 
 	/*
diff --git a/configs/am335x_boneblack_vboot_defconfig b/configs/am335x_boneblack_vboot_defconfig
index 903f518..c2f09cb 100644
--- a/configs/am335x_boneblack_vboot_defconfig
+++ b/configs/am335x_boneblack_vboot_defconfig
@@ -49,3 +49,4 @@
 CONFIG_G_DNL_MANUFACTURER="Texas Instruments"
 CONFIG_G_DNL_VENDOR_NUM=0x0451
 CONFIG_G_DNL_PRODUCT_NUM=0xd022
+CONFIG_DM_I2C=y
diff --git a/configs/am335x_evm_defconfig b/configs/am335x_evm_defconfig
index 696024c..6885230 100644
--- a/configs/am335x_evm_defconfig
+++ b/configs/am335x_evm_defconfig
@@ -49,3 +49,4 @@
 CONFIG_SPL_OF_LIBFDT=y
 CONFIG_SPL_LOAD_FIT=y
 CONFIG_OF_LIST="am335x-evm am335x-bone am335x-boneblack am335x-evmsk am335x-bonegreen am335x-icev2"
+CONFIG_DM_I2C=y
diff --git a/configs/am43xx_evm_defconfig b/configs/am43xx_evm_defconfig
index cb3de11..0eab4ad 100644
--- a/configs/am43xx_evm_defconfig
+++ b/configs/am43xx_evm_defconfig
@@ -54,3 +54,4 @@
 CONFIG_G_DNL_VENDOR_NUM=0x0403
 CONFIG_G_DNL_PRODUCT_NUM=0xbd00
 CONFIG_SPL_OF_LIBFDT=y
+CONFIG_DM_I2C=y
diff --git a/configs/am43xx_hs_evm_defconfig b/configs/am43xx_hs_evm_defconfig
index 68dfb6c..c8ce723 100644
--- a/configs/am43xx_hs_evm_defconfig
+++ b/configs/am43xx_hs_evm_defconfig
@@ -58,3 +58,4 @@
 CONFIG_G_DNL_VENDOR_NUM=0x0403
 CONFIG_G_DNL_PRODUCT_NUM=0xbd00
 CONFIG_SPL_OF_LIBFDT=y
+CONFIG_DM_I2C=y
diff --git a/configs/am57xx_evm_defconfig b/configs/am57xx_evm_defconfig
index c29a05a..8a8a4c9 100644
--- a/configs/am57xx_evm_defconfig
+++ b/configs/am57xx_evm_defconfig
@@ -40,3 +40,4 @@
 CONFIG_SPL_OF_LIBFDT=y
 CONFIG_SPL_LOAD_FIT=y
 CONFIG_OF_LIST="am57xx-beagle-x15 am572x-idk"
+CONFIG_DM_I2C=y
diff --git a/configs/am57xx_hs_evm_defconfig b/configs/am57xx_hs_evm_defconfig
index 01a4701..2ccb332 100644
--- a/configs/am57xx_hs_evm_defconfig
+++ b/configs/am57xx_hs_evm_defconfig
@@ -42,3 +42,4 @@
 CONFIG_SPL_LOAD_FIT=y
 CONFIG_SPL_FIT_IMAGE_POST_PROCESS=y
 CONFIG_OF_LIST="am57xx-beagle-x15"
+CONFIG_DM_I2C=y
diff --git a/configs/chromebook_jerry_defconfig b/configs/chromebook_jerry_defconfig
index d5bc515..fd5314a 100644
--- a/configs/chromebook_jerry_defconfig
+++ b/configs/chromebook_jerry_defconfig
@@ -53,7 +53,7 @@
 CONFIG_PINCTRL=y
 CONFIG_SPL_PINCTRL=y
 # CONFIG_SPL_PINCTRL_FULL is not set
-CONFIG_ROCKCHIP_PINCTRL=y
+CONFIG_ROCKCHIP_RK3288_PINCTRL=y
 CONFIG_DM_PMIC=y
 # CONFIG_SPL_PMIC_CHILDREN is not set
 CONFIG_PMIC_RK808=y
diff --git a/configs/dra7xx_evm_defconfig b/configs/dra7xx_evm_defconfig
index 756af63..1d27e52 100644
--- a/configs/dra7xx_evm_defconfig
+++ b/configs/dra7xx_evm_defconfig
@@ -57,3 +57,4 @@
 CONFIG_SPL_OF_LIBFDT=y
 CONFIG_SPL_LOAD_FIT=y
 CONFIG_OF_LIST="dra7-evm dra72-evm"
+CONFIG_DM_I2C=y
diff --git a/configs/dra7xx_hs_evm_defconfig b/configs/dra7xx_hs_evm_defconfig
index eb01f41..faf9cd5 100644
--- a/configs/dra7xx_hs_evm_defconfig
+++ b/configs/dra7xx_hs_evm_defconfig
@@ -60,3 +60,4 @@
 CONFIG_SPL_LOAD_FIT=y
 CONFIG_SPL_FIT_IMAGE_POST_PROCESS=y
 CONFIG_OF_LIST="dra7-evm dra72-evm"
+CONFIG_DM_I2C=y
diff --git a/configs/evb-rk3036_defconfig b/configs/evb-rk3036_defconfig
index 9894fff..2d5e5e0 100644
--- a/configs/evb-rk3036_defconfig
+++ b/configs/evb-rk3036_defconfig
@@ -32,7 +32,7 @@
 CONFIG_DM_MMC=y
 CONFIG_ROCKCHIP_DWMMC=y
 CONFIG_PINCTRL=y
-CONFIG_ROCKCHIP_3036_PINCTRL=y
+CONFIG_ROCKCHIP_RK3036_PINCTRL=y
 CONFIG_RAM=y
 # CONFIG_SPL_SERIAL_PRESENT is not set
 CONFIG_DEBUG_UART=y
diff --git a/configs/evb-rk3288_defconfig b/configs/evb-rk3288_defconfig
new file mode 100644
index 0000000..41cfedd
--- /dev/null
+++ b/configs/evb-rk3288_defconfig
@@ -0,0 +1,67 @@
+CONFIG_ARM=y
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_ROCKCHIP_RK3288=y
+CONFIG_TARGET_EVB_RK3288=y
+CONFIG_SPL_STACK_R_ADDR=0x80000
+CONFIG_DEFAULT_DEVICE_TREE="rk3288-evb"
+CONFIG_SPL_STACK_R=y
+CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x2000
+CONFIG_HUSH_PARSER=y
+CONFIG_CMD_BOOTZ=y
+# CONFIG_CMD_IMLS is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_SF=y
+CONFIG_CMD_SPI=y
+CONFIG_CMD_I2C=y
+CONFIG_CMD_GPIO=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_CACHE=y
+CONFIG_CMD_TIME=y
+CONFIG_CMD_PMIC=y
+CONFIG_CMD_REGULATOR=y
+CONFIG_CMD_EXT2=y
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=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_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_SYS_I2C_ROCKCHIP=y
+CONFIG_LED=y
+CONFIG_LED_GPIO=y
+CONFIG_SYSRESET=y
+CONFIG_DM_MMC=y
+CONFIG_ROCKCHIP_DWMMC=y
+CONFIG_PINCTRL=y
+# CONFIG_PINCTRL_FULL is not set
+CONFIG_SPL_PINCTRL=y
+# CONFIG_SPL_PINCTRL_FULL is not set
+CONFIG_ROCKCHIP_PINCTRL=y
+CONFIG_DM_PMIC=y
+CONFIG_PMIC_ACT8846=y
+CONFIG_DM_REGULATOR=y
+CONFIG_REGULATOR_ACT8846=y
+CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_DM_PWM=y
+CONFIG_PWM_ROCKCHIP=y
+CONFIG_RAM=y
+CONFIG_SPL_RAM=y
+CONFIG_DEBUG_UART=y
+CONFIG_DEBUG_UART_BASE=0xff690000
+CONFIG_DEBUG_UART_CLOCK=24000000
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_SYS_NS16550=y
+CONFIG_USE_PRIVATE_LIBGCC=y
+CONFIG_USE_TINY_PRINTF=y
+CONFIG_CMD_DHRYSTONE=y
+CONFIG_ERRNO_STR=y
diff --git a/configs/evb-rk3399_defconfig b/configs/evb-rk3399_defconfig
new file mode 100644
index 0000000..3f9b47e
--- /dev/null
+++ b/configs/evb-rk3399_defconfig
@@ -0,0 +1,33 @@
+CONFIG_ARM=y
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_ROCKCHIP_RK3399=y
+CONFIG_TARGET_EVB_RK3399=y
+CONFIG_DEFAULT_DEVICE_TREE="rk3399-evb"
+CONFIG_HUSH_PARSER=y
+CONFIG_CMD_BOOTZ=y
+# CONFIG_CMD_IMLS is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_SF=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_TIME=y
+CONFIG_CMD_GPT=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_FIT=y
+CONFIG_SYSRESET=y
+CONFIG_DM_MMC=y
+CONFIG_ROCKCHIP_SDHCI=y
+CONFIG_PINCTRL=y
+CONFIG_RAM=y
+CONFIG_SYS_NS16550=y
+CONFIG_DEBUG_UART=y
+CONFIG_DEBUG_UART_BASE=0xFF1A0000
+CONFIG_DEBUG_UART_CLOCK=24000000
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_USE_TINY_PRINTF=y
+CONFIG_ERRNO_STR=y
diff --git a/configs/firefly-rk3288_defconfig b/configs/firefly-rk3288_defconfig
index bdafc71..4122000 100644
--- a/configs/firefly-rk3288_defconfig
+++ b/configs/firefly-rk3288_defconfig
@@ -46,7 +46,7 @@
 CONFIG_PINCTRL=y
 CONFIG_SPL_PINCTRL=y
 # CONFIG_SPL_PINCTRL_FULL is not set
-CONFIG_ROCKCHIP_PINCTRL=y
+CONFIG_ROCKCHIP_RK3288_PINCTRL=y
 CONFIG_DM_PMIC=y
 # CONFIG_SPL_PMIC_CHILDREN is not set
 CONFIG_PMIC_ACT8846=y
diff --git a/configs/kylin-rk3036_defconfig b/configs/kylin-rk3036_defconfig
index 0ff6c6b..51196aa 100644
--- a/configs/kylin-rk3036_defconfig
+++ b/configs/kylin-rk3036_defconfig
@@ -32,7 +32,7 @@
 CONFIG_DM_MMC=y
 CONFIG_ROCKCHIP_DWMMC=y
 CONFIG_PINCTRL=y
-CONFIG_ROCKCHIP_3036_PINCTRL=y
+CONFIG_ROCKCHIP_RK3036_PINCTRL=y
 CONFIG_RAM=y
 CONFIG_USE_PRIVATE_LIBGCC=y
 CONFIG_CMD_DHRYSTONE=y
diff --git a/configs/rock2_defconfig b/configs/rock2_defconfig
index 3e16b80..3b6d7d9 100644
--- a/configs/rock2_defconfig
+++ b/configs/rock2_defconfig
@@ -44,7 +44,7 @@
 CONFIG_PINCTRL=y
 CONFIG_SPL_PINCTRL=y
 # CONFIG_SPL_PINCTRL_FULL is not set
-CONFIG_ROCKCHIP_PINCTRL=y
+CONFIG_ROCKCHIP_RK3288_PINCTRL=y
 CONFIG_DM_PMIC=y
 # CONFIG_SPL_PMIC_CHILDREN is not set
 CONFIG_PMIC_ACT8846=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 6a1874a..29e6d85 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -101,6 +101,7 @@
 CONFIG_PWRSEQ=y
 CONFIG_SPL_PWRSEQ=y
 CONFIG_SYSRESET=y
+CONFIG_I2C_EEPROM=y
 CONFIG_DM_MMC_OPS=y
 CONFIG_SANDBOX_MMC=y
 CONFIG_SPI_FLASH_SANDBOX=y
@@ -119,8 +120,8 @@
 CONFIG_PCI_SANDBOX=y
 CONFIG_PINCTRL=y
 CONFIG_PINCONF=y
-CONFIG_ROCKCHIP_PINCTRL=y
-CONFIG_ROCKCHIP_3036_PINCTRL=y
+CONFIG_ROCKCHIP_RK3288_PINCTRL=y
+CONFIG_ROCKCHIP_RK3036_PINCTRL=y
 CONFIG_PINCTRL_SANDBOX=y
 CONFIG_DM_PMIC=y
 CONFIG_PMIC_ACT8846=y
diff --git a/configs/sandbox_noblk_defconfig b/configs/sandbox_noblk_defconfig
index 60c7339..503845b 100644
--- a/configs/sandbox_noblk_defconfig
+++ b/configs/sandbox_noblk_defconfig
@@ -112,8 +112,8 @@
 CONFIG_PCI_SANDBOX=y
 CONFIG_PINCTRL=y
 CONFIG_PINCONF=y
-CONFIG_ROCKCHIP_PINCTRL=y
-CONFIG_ROCKCHIP_3036_PINCTRL=y
+CONFIG_ROCKCHIP_RK3288_PINCTRL=y
+CONFIG_ROCKCHIP_RK3036_PINCTRL=y
 CONFIG_PINCTRL_SANDBOX=y
 CONFIG_DM_PMIC=y
 CONFIG_PMIC_ACT8846=y
diff --git a/configs/thunderx_88xx_defconfig b/configs/thunderx_88xx_defconfig
index 4a8655f..28797f9 100644
--- a/configs/thunderx_88xx_defconfig
+++ b/configs/thunderx_88xx_defconfig
@@ -2,7 +2,6 @@
 CONFIG_TARGET_THUNDERX_88XX=y
 CONFIG_DM_SERIAL=y
 CONFIG_DEFAULT_DEVICE_TREE="thunderx-88xx"
-CONFIG_SYS_EXTRA_OPTIONS="ARM64"
 CONFIG_BOOTDELAY=5
 CONFIG_HUSH_PARSER=y
 CONFIG_SYS_PROMPT="ThunderX_88XX> "
diff --git a/disk/part_efi.c b/disk/part_efi.c
index 0af1e92..01f71be 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -886,9 +886,10 @@
 	count = le32_to_cpu(pgpt_head->num_partition_entries) *
 		le32_to_cpu(pgpt_head->sizeof_partition_entry);
 
-	debug("%s: count = %u * %u = %zu\n", __func__,
+	debug("%s: count = %u * %u = %lu\n", __func__,
 	      (u32) le32_to_cpu(pgpt_head->num_partition_entries),
-	      (u32) le32_to_cpu(pgpt_head->sizeof_partition_entry), count);
+	      (u32) le32_to_cpu(pgpt_head->sizeof_partition_entry),
+	      (ulong)count);
 
 	/* Allocate memory for PTE, remember to FREE */
 	if (count != 0) {
@@ -897,9 +898,8 @@
 	}
 
 	if (count == 0 || pte == NULL) {
-		printf("%s: ERROR: Can't allocate 0x%zX "
-		       "bytes for GPT Entries\n",
-			__func__, count);
+		printf("%s: ERROR: Can't allocate %#lX bytes for GPT Entries\n",
+		       __func__, (ulong)count);
 		return NULL;
 	}
 
diff --git a/doc/README.rockchip b/doc/README.rockchip
index e0572c8..c218a8b 100644
--- a/doc/README.rockchip
+++ b/doc/README.rockchip
@@ -36,11 +36,12 @@
 Building
 ========
 
-At present three RK3288 boards are supported:
+At present four RK3288 boards are supported:
 
    - Firefly RK3288 - use firefly-rk3288 configuration
    - Radxa Rock 2 - use rock2 configuration
    - Hisense Chromebook - use chromebook_jerry configuration
+   - EVB RK3288 - use evb-rk3288 configuration
 
 Two RK3036 board are supported:
 
@@ -119,6 +120,20 @@
    Hit any key to stop autoboot:  0
    =>
 
+The rockchip bootrom can load and boot an initial spl, then continue to
+load a second-level bootloader(ie. U-BOOT) as soon as it returns to bootrom.
+Therefore RK3288 has another loading sequence like RK3036. The option of
+U-Boot is controlled with this setting in U-Boot:
+
+	#define CONFIG_ROCKCHIP_SPL_BACK_TO_BROM
+
+You can create the image via the following operations:
+
+   ./firefly-rk3288/tools/mkimage -n rk3288 -T rksd -d \
+	firefly-rk3288/spl/u-boot-spl-dtb.bin out && \
+   cat firefly-rk3288/u-boot-dtb.bin >> out && \
+   sudo dd if=out of=/dev/sdc seek=64
+
 If you have an HDMI cable attached you should see a video console.
 
 For evb_rk3036 board:
@@ -129,6 +144,32 @@
 Note: rk3036 SDMMC and debug uart use the same iomux, so if you boot from SD, the
       debug uart must be disabled
 
+Using fastboot on rk3288
+========================
+- Define GPT partition layout like kylin_rk3036(see include/configs/kylin_rk3036.h)
+- Write GPT partition layout to mmc device which fastboot want to use it to
+store the image
+
+        => gpt write mmc 1 $partitions
+
+- Invoke fastboot command to prepare
+
+        => fastboot 1
+
+- Start fastboot request on PC
+
+        fastboot -i 0x2207 flash loader evb-rk3288/spl/u-boot-spl-dtb.bin
+
+You should see something like:
+
+        => fastboot 1
+        WARNING: unknown variable: partition-type:loader
+        Starting download of 357796 bytes
+        ..
+        downloading of 357796 bytes finished
+        Flashing Raw Image
+        ........ wrote 357888 bytes to 'loader'
+
 Booting from SPI
 ================
 
diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c
index 679f010..e00feb0 100644
--- a/drivers/clk/clk_rk3288.c
+++ b/drivers/clk/clk_rk3288.c
@@ -47,7 +47,7 @@
 	OUTPUT_MAX_HZ	= 2200U * 1000000,
 	OUTPUT_MIN_HZ	= 27500000,
 	FREF_MAX_HZ	= 2200U * 1000000,
-	FREF_MIN_HZ	= 269 * 1000000,
+	FREF_MIN_HZ	= 269 * 1000,
 };
 
 enum {
@@ -145,7 +145,7 @@
 	struct udevice *dev;
 	int ret;
 
-	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
+	ret = rockchip_get_clk(&dev);
 	if (ret)
 		return ERR_PTR(ret);
 
@@ -447,7 +447,7 @@
 }
 #endif
 
-void rkclk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf)
+void rk3288_clk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf)
 {
 	/* pll enter slow-mode */
 	rk_clrsetreg(&cru->cru_mode_con,
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index 1141ce1..de602ae 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -311,6 +311,26 @@
 }
 #endif
 
+int uclass_get_device_by_driver(enum uclass_id id,
+				const struct driver *find_drv,
+				struct udevice **devp)
+{
+	struct udevice *dev;
+	struct uclass *uc;
+	int ret;
+
+	ret = uclass_get(id, &uc);
+	if (ret)
+		return ret;
+
+	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+		if (dev->driver == find_drv)
+			return uclass_get_device_tail(dev, 0, devp);
+	}
+
+	return -ENODEV;
+}
+
 int uclass_get_device_tail(struct udevice *dev, int ret,
 				  struct udevice **devp)
 {
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 6e22bba..b3e8405 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -154,6 +154,13 @@
 	  Support for UniPhier FIFO-builtin I2C controller driver.
 	  This I2C controller is used on PH1-Pro4 or newer UniPhier SoCs.
 
+config SYS_I2C_MVTWSI
+	bool "Marvell I2C driver"
+	depends on DM_I2C
+	help
+	  Support for Marvell I2C controllers as used on the orion5x and
+	  kirkwood SoC families.
+
 source "drivers/i2c/muxes/Kconfig"
 
 endmenu
diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c
index 50b99ea..20b30ff 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/drivers/i2c/i2c-uclass.c
@@ -467,6 +467,7 @@
 	return ops->deblock(bus);
 }
 
+#if CONFIG_IS_ENABLED(OF_CONTROL)
 int i2c_chip_ofdata_to_platdata(const void *blob, int node,
 				struct dm_i2c_chip *chip)
 {
@@ -482,31 +483,44 @@
 
 	return 0;
 }
+#endif
 
 static int i2c_post_probe(struct udevice *dev)
 {
+#if CONFIG_IS_ENABLED(OF_CONTROL)
 	struct dm_i2c_bus *i2c = dev_get_uclass_priv(dev);
 
 	i2c->speed_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
 				     "clock-frequency", 100000);
 
 	return dm_i2c_set_bus_speed(dev, i2c->speed_hz);
+#else
+	return 0;
+#endif
 }
 
 static int i2c_post_bind(struct udevice *dev)
 {
+#if CONFIG_IS_ENABLED(OF_CONTROL)
 	/* Scan the bus for devices */
 	return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+#else
+	return 0;
+#endif
 }
 
 static int i2c_child_post_bind(struct udevice *dev)
 {
+#if CONFIG_IS_ENABLED(OF_CONTROL)
 	struct dm_i2c_chip *plat = dev_get_parent_platdata(dev);
 
 	if (dev->of_offset == -1)
 		return 0;
 
 	return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat);
+#else
+	return 0;
+#endif
 }
 
 UCLASS_DRIVER(i2c) = {
diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c
index bf44432..ab7481a 100644
--- a/drivers/i2c/mvtwsi.c
+++ b/drivers/i2c/mvtwsi.c
@@ -12,12 +12,19 @@
 #include <i2c.h>
 #include <asm/errno.h>
 #include <asm/io.h>
+#include <linux/compat.h>
+#ifdef CONFIG_DM_I2C
+#include <dm.h>
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
 
 /*
- * include a file that will provide CONFIG_I2C_MVTWSI_BASE*
- * and possibly other settings
+ * Include a file that will provide CONFIG_I2C_MVTWSI_BASE*, and possibly other
+ * settings
  */
 
+#ifndef CONFIG_DM_I2C
 #if defined(CONFIG_ORION5X)
 #include <asm/arch/orion5x.h>
 #elif (defined(CONFIG_KIRKWOOD) || defined(CONFIG_ARCH_MVEBU))
@@ -27,6 +34,7 @@
 #else
 #error Driver mvtwsi not supported by SoC or board
 #endif
+#endif /* CONFIG_DM_I2C */
 
 /*
  * TWSI register structure
@@ -51,8 +59,8 @@
 	u32 data;
 	u32 control;
 	union {
-		u32 status;	/* when reading */
-		u32 baudrate;	/* when writing */
+		u32 status;	/* When reading */
+		u32 baudrate;	/* When writing */
 	};
 	u32 xtnd_slave_addr;
 	u32 reserved[2];
@@ -61,20 +69,43 @@
 
 #endif
 
+#ifdef CONFIG_DM_I2C
+struct mvtwsi_i2c_dev {
+	/* TWSI Register base for the device */
+	struct mvtwsi_registers *base;
+	/* Number of the device (determined from cell-index property) */
+	int index;
+	/* The I2C slave address for the device */
+	u8 slaveadd;
+	/* The configured I2C speed in Hz */
+	uint speed;
+	/* The current length of a clock period (depending on speed) */
+	uint tick;
+};
+#endif /* CONFIG_DM_I2C */
+
 /*
- * Control register fields
+ * enum mvtwsi_ctrl_register_fields - Bit masks for flags in the control
+ * register
  */
-
-#define	MVTWSI_CONTROL_ACK	0x00000004
-#define	MVTWSI_CONTROL_IFLG	0x00000008
-#define	MVTWSI_CONTROL_STOP	0x00000010
-#define	MVTWSI_CONTROL_START	0x00000020
-#define	MVTWSI_CONTROL_TWSIEN	0x00000040
-#define	MVTWSI_CONTROL_INTEN	0x00000080
+enum mvtwsi_ctrl_register_fields {
+	/* Acknowledge bit */
+	MVTWSI_CONTROL_ACK	= 0x00000004,
+	/* Interrupt flag */
+	MVTWSI_CONTROL_IFLG	= 0x00000008,
+	/* Stop bit */
+	MVTWSI_CONTROL_STOP	= 0x00000010,
+	/* Start bit */
+	MVTWSI_CONTROL_START	= 0x00000020,
+	/* I2C enable */
+	MVTWSI_CONTROL_TWSIEN	= 0x00000040,
+	/* Interrupt enable */
+	MVTWSI_CONTROL_INTEN	= 0x00000080,
+};
 
 /*
- * On sun6i and newer IFLG is a write-clear bit which is cleared by writing 1,
- * on other platforms it is a normal r/w bit which is cleared by writing 0.
+ * On sun6i and newer, IFLG is a write-clear bit, which is cleared by writing 1;
+ * on other platforms, it is a normal r/w bit, which is cleared by writing 0.
  */
 
 #ifdef CONFIG_SUNXI_GEN_SUN6I
@@ -84,53 +115,95 @@
 #endif
 
 /*
- * Status register values -- only those expected in normal master
- * operation on non-10-bit-address devices; whatever status we don't
- * expect in nominal conditions (bus errors, arbitration losses,
- * missing ACKs...) we just pass back to the caller as an error
+ * enum mvstwsi_status_values - Possible values of I2C controller's status
+ * register
+ *
+ * Only those statuses expected in normal master operation on
+ * non-10-bit-address devices are specified.
+ *
+ * Every status that's unexpected during normal operation (bus errors,
+ * arbitration losses, missing ACKs...) is passed back to the caller as an error
  * code.
  */
-
-#define	MVTWSI_STATUS_START		0x08
-#define	MVTWSI_STATUS_REPEATED_START	0x10
-#define	MVTWSI_STATUS_ADDR_W_ACK	0x18
-#define	MVTWSI_STATUS_DATA_W_ACK	0x28
-#define	MVTWSI_STATUS_ADDR_R_ACK	0x40
-#define	MVTWSI_STATUS_ADDR_R_NAK	0x48
-#define	MVTWSI_STATUS_DATA_R_ACK	0x50
-#define	MVTWSI_STATUS_DATA_R_NAK	0x58
-#define	MVTWSI_STATUS_IDLE		0xF8
+enum mvstwsi_status_values {
+	/* START condition transmitted */
+	MVTWSI_STATUS_START		= 0x08,
+	/* Repeated START condition transmitted */
+	MVTWSI_STATUS_REPEATED_START	= 0x10,
+	/* Address + write bit transmitted, ACK received */
+	MVTWSI_STATUS_ADDR_W_ACK	= 0x18,
+	/* Data transmitted, ACK received */
+	MVTWSI_STATUS_DATA_W_ACK	= 0x28,
+	/* Address + read bit transmitted, ACK received */
+	MVTWSI_STATUS_ADDR_R_ACK	= 0x40,
+	/* Address + read bit transmitted, ACK not received */
+	MVTWSI_STATUS_ADDR_R_NAK	= 0x48,
+	/* Data received, ACK transmitted */
+	MVTWSI_STATUS_DATA_R_ACK	= 0x50,
+	/* Data received, ACK not transmitted */
+	MVTWSI_STATUS_DATA_R_NAK	= 0x58,
+	/* No relevant status */
+	MVTWSI_STATUS_IDLE		= 0xF8,
+};
 
 /*
- * MVTWSI controller base
+ * enum mvstwsi_ack_flags - Determine whether a read byte should be
+ * acknowledged or not.
  */
+enum mvtwsi_ack_flags {
+	/* Send NAK after received byte */
+	MVTWSI_READ_NAK = 0,
+	/* Send ACK after received byte */
+	MVTWSI_READ_ACK = 1,
+};
 
+/*
+ * calc_tick() - Calculate the duration of a clock cycle from the I2C speed
+ *
+ * @speed:	The speed in Hz to calculate the clock cycle duration for.
+ * @return The duration of a clock cycle in ns.
+ */
+inline uint calc_tick(uint speed)
+{
+	/* One tick = the duration of a period at the specified speed in ns (we
+	 * add 100 ns to be on the safe side) */
+	return (1000000000u / speed) + 100;
+}
+
+#ifndef CONFIG_DM_I2C
+
+/*
+ * twsi_get_base() - Get controller register base for specified adapter
+ *
+ * @adap:	Adapter to get the register base for.
+ * @return Register base for the specified adapter.
+ */
 static struct mvtwsi_registers *twsi_get_base(struct i2c_adapter *adap)
 {
 	switch (adap->hwadapnr) {
 #ifdef CONFIG_I2C_MVTWSI_BASE0
 	case 0:
-		return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE0;
+		return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE0;
 #endif
 #ifdef CONFIG_I2C_MVTWSI_BASE1
 	case 1:
-		return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE1;
+		return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE1;
 #endif
 #ifdef CONFIG_I2C_MVTWSI_BASE2
 	case 2:
-		return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE2;
+		return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE2;
 #endif
 #ifdef CONFIG_I2C_MVTWSI_BASE3
 	case 3:
-		return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE3;
+		return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE3;
 #endif
 #ifdef CONFIG_I2C_MVTWSI_BASE4
 	case 4:
-		return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE4;
+		return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE4;
 #endif
 #ifdef CONFIG_I2C_MVTWSI_BASE5
 	case 5:
-		return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE5;
+		return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE5;
 #endif
 	default:
 		printf("Missing mvtwsi controller %d base\n", adap->hwadapnr);
@@ -139,30 +212,48 @@
 
 	return NULL;
 }
+#endif
 
 /*
- * Returned statuses are 0 for success and nonzero otherwise.
- * Currently, cmd_i2c and cmd_eeprom do not interpret an error status.
- * Thus to ease debugging, the return status contains some debug info:
- * - bits 31..24 are error class: 1 is timeout, 2 is 'status mismatch'.
- * - bits 23..16 are the last value of the control register.
- * - bits 15..8 are the last value of the status register.
- * - bits 7..0 are the expected value of the status register.
+ * enum mvtwsi_error_class - types of I2C errors
  */
-
-#define MVTWSI_ERROR_WRONG_STATUS	0x01
-#define MVTWSI_ERROR_TIMEOUT		0x02
-
-#define MVTWSI_ERROR(ec, lc, ls, es) (((ec << 24) & 0xFF000000) | \
-	((lc << 16) & 0x00FF0000) | ((ls<<8) & 0x0000FF00) | (es & 0xFF))
+enum mvtwsi_error_class {
+	/* The controller returned a different status than expected */
+	MVTWSI_ERROR_WRONG_STATUS       = 0x01,
+	/* The controller timed out */
+	MVTWSI_ERROR_TIMEOUT            = 0x02,
+};
 
 /*
- * Wait for IFLG to raise, or return 'timeout'; then if status is as expected,
- * return 0 (ok) or return 'wrong status'.
+ * mvtwsi_error() - Build I2C return code from error information
+ *
+ * For debugging purposes, this function packs some information of an occurred
+ * error into a return code. These error codes are returned from I2C API
+ * functions (i2c_{read,write}, dm_i2c_{read,write}, etc.).
+ *
+ * @ec:		The error class of the error (enum mvtwsi_error_class).
+ * @lc:		The last value of the control register.
+ * @ls:		The last value of the status register.
+ * @es:		The expected value of the status register.
+ * @return The generated error code.
  */
-static int twsi_wait(struct i2c_adapter *adap, int expected_status)
+inline uint mvtwsi_error(uint ec, uint lc, uint ls, uint es)
 {
-	struct mvtwsi_registers *twsi = twsi_get_base(adap);
+	return ((ec << 24) & 0xFF000000)
+	       | ((lc << 16) & 0x00FF0000)
+	       | ((ls << 8) & 0x0000FF00)
+	       | (es & 0xFF);
+}
+
+/*
+ * twsi_wait() - Wait for I2C bus interrupt flag and check status, or time out.
+ *
+ * @return Zero if status is as expected, or a non-zero code if either a time
+ *	   out occurred, or the status was not the expected one.
+ */
+static int twsi_wait(struct mvtwsi_registers *twsi, int expected_status,
+		     uint tick)
+{
 	int control, status;
 	int timeout = 1000;
 
@@ -173,105 +264,140 @@
 			if (status == expected_status)
 				return 0;
 			else
-				return MVTWSI_ERROR(
+				return mvtwsi_error(
 					MVTWSI_ERROR_WRONG_STATUS,
 					control, status, expected_status);
 		}
-		udelay(10); /* one clock cycle at 100 kHz */
+		ndelay(tick); /* One clock cycle */
 	} while (timeout--);
 	status = readl(&twsi->status);
-	return MVTWSI_ERROR(
-		MVTWSI_ERROR_TIMEOUT, control, status, expected_status);
+	return mvtwsi_error(MVTWSI_ERROR_TIMEOUT, control, status,
+			    expected_status);
 }
 
 /*
- * Assert the START condition, either in a single I2C transaction
- * or inside back-to-back ones (repeated starts).
+ * twsi_start() - Assert a START condition on the bus.
+ *
+ * This function is used in both single I2C transactions and inside
+ * back-to-back transactions (repeated starts).
+ *
+ * @twsi:		The MVTWSI register structure to use.
+ * @expected_status:	The I2C bus status expected to be asserted after the
+ *			operation completion.
+ * @tick:		The duration of a clock cycle at the current I2C speed.
+ * @return Zero if status is as expected, or a non-zero code if either a time
+ *	   out occurred or the status was not the expected one.
  */
-static int twsi_start(struct i2c_adapter *adap, int expected_status, u8 *flags)
+static int twsi_start(struct mvtwsi_registers *twsi, int expected_status,
+		      uint tick)
 {
-	struct mvtwsi_registers *twsi = twsi_get_base(adap);
-
-	/* globally set TWSIEN in case it was not */
-	*flags |= MVTWSI_CONTROL_TWSIEN;
-	/* assert START */
-	writel(*flags | MVTWSI_CONTROL_START |
-				    MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
-	/* wait for controller to process START */
-	return twsi_wait(adap, expected_status);
+	/* Assert START */
+	writel(MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_START |
+	       MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
+	/* Wait for controller to process START */
+	return twsi_wait(twsi, expected_status, tick);
 }
 
 /*
- * Send a byte (i2c address or data).
+ * twsi_send() - Send a byte on the I2C bus.
+ *
+ * The byte may be part of an address byte or data.
+ *
+ * @twsi:		The MVTWSI register structure to use.
+ * @byte:		The byte to send.
+ * @expected_status:	The I2C bus status expected to be asserted after the
+ *			operation completion.
+ * @tick:		The duration of a clock cycle at the current I2C speed.
+ * @return Zero if status is as expected, or a non-zero code if either a time
+ *	   out occurred or the status was not the expected one.
  */
-static int twsi_send(struct i2c_adapter *adap, u8 byte, int expected_status,
-		     u8 *flags)
+static int twsi_send(struct mvtwsi_registers *twsi, u8 byte,
+		     int expected_status, uint tick)
 {
-	struct mvtwsi_registers *twsi = twsi_get_base(adap);
-
-	/* put byte in data register for sending */
+	/* Write byte to data register for sending */
 	writel(byte, &twsi->data);
-	/* clear any pending interrupt -- that'll cause sending */
-	writel(*flags | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
-	/* wait for controller to receive byte and check ACK */
-	return twsi_wait(adap, expected_status);
+	/* Clear any pending interrupt -- that will cause sending */
+	writel(MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_CLEAR_IFLG,
+	       &twsi->control);
+	/* Wait for controller to receive byte, and check ACK */
+	return twsi_wait(twsi, expected_status, tick);
 }
 
 /*
- * Receive a byte.
- * Global mvtwsi_control_flags variable says if we should ack or nak.
+ * twsi_recv() - Receive a byte on the I2C bus.
+ *
+ * The static variable mvtwsi_control_flags controls whether we ack or nak.
+ *
+ * @twsi:		The MVTWSI register structure to use.
+ * @byte:		The byte to send.
+ * @ack_flag:		Flag that determines whether the received byte should
+ *			be acknowledged by the controller or not (sent ACK/NAK).
+ * @tick:		The duration of a clock cycle at the current I2C speed.
+ * @return Zero if status is as expected, or a non-zero code if either a time
+ *	   out occurred or the status was not the expected one.
  */
-static int twsi_recv(struct i2c_adapter *adap, u8 *byte, u8 *flags)
+static int twsi_recv(struct mvtwsi_registers *twsi, u8 *byte, int ack_flag,
+		     uint tick)
 {
-	struct mvtwsi_registers *twsi = twsi_get_base(adap);
-	int expected_status, status;
+	int expected_status, status, control;
 
-	/* compute expected status based on ACK bit in global control flags */
-	if (*flags & MVTWSI_CONTROL_ACK)
-		expected_status = MVTWSI_STATUS_DATA_R_ACK;
-	else
-		expected_status = MVTWSI_STATUS_DATA_R_NAK;
-	/* acknowledge *previous state* and launch receive */
-	writel(*flags | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
-	/* wait for controller to receive byte and assert ACK or NAK */
-	status = twsi_wait(adap, expected_status);
-	/* if we did receive expected byte then store it */
+	/* Compute expected status based on passed ACK flag */
+	expected_status = ack_flag ? MVTWSI_STATUS_DATA_R_ACK :
+			  MVTWSI_STATUS_DATA_R_NAK;
+	/* Acknowledge *previous state*, and launch receive */
+	control = MVTWSI_CONTROL_TWSIEN;
+	control |= ack_flag == MVTWSI_READ_ACK ? MVTWSI_CONTROL_ACK : 0;
+	writel(control | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
+	/* Wait for controller to receive byte, and assert ACK or NAK */
+	status = twsi_wait(twsi, expected_status, tick);
+	/* If we did receive the expected byte, store it */
 	if (status == 0)
 		*byte = readl(&twsi->data);
-	/* return status */
 	return status;
 }
 
 /*
- * Assert the STOP condition.
- * This is also used to force the bus back in idle (SDA=SCL=1).
+ * twsi_stop() - Assert a STOP condition on the bus.
+ *
+ * This function is also used to force the bus back to idle state (SDA =
+ * SCL = 1).
+ *
+ * @twsi:	The MVTWSI register structure to use.
+ * @tick:	The duration of a clock cycle at the current I2C speed.
+ * @return Zero if the operation succeeded, or a non-zero code if a time out
+ *	   occurred.
  */
-static int twsi_stop(struct i2c_adapter *adap, int status)
+static int twsi_stop(struct mvtwsi_registers *twsi, uint tick)
 {
-	struct mvtwsi_registers *twsi = twsi_get_base(adap);
 	int control, stop_status;
+	int status = 0;
 	int timeout = 1000;
 
-	/* assert STOP */
+	/* Assert STOP */
 	control = MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_STOP;
 	writel(control | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
-	/* wait for IDLE; IFLG won't rise so twsi_wait() is no use. */
+	/* Wait for IDLE; IFLG won't rise, so we can't use twsi_wait() */
 	do {
 		stop_status = readl(&twsi->status);
 		if (stop_status == MVTWSI_STATUS_IDLE)
 			break;
-		udelay(10); /* one clock cycle at 100 kHz */
+		ndelay(tick); /* One clock cycle */
 	} while (timeout--);
 	control = readl(&twsi->control);
 	if (stop_status != MVTWSI_STATUS_IDLE)
-		if (status == 0)
-			status = MVTWSI_ERROR(
-				MVTWSI_ERROR_TIMEOUT,
-				control, status, MVTWSI_STATUS_IDLE);
+		status = mvtwsi_error(MVTWSI_ERROR_TIMEOUT,
+				      control, status, MVTWSI_STATUS_IDLE);
 	return status;
 }
 
-static unsigned int twsi_calc_freq(const int n, const int m)
+/*
+ * twsi_calc_freq() - Compute I2C frequency depending on m and n parameters.
+ *
+ * @n:		Parameter 'n' for the frequency calculation algorithm.
+ * @m:		Parameter 'm' for the frequency calculation algorithm.
+ * @return The I2C frequency corresponding to the passed m and n parameters.
+ */
+static uint twsi_calc_freq(const int n, const int m)
 {
 #ifdef CONFIG_SUNXI
 	return CONFIG_SYS_TCLK / (10 * (m + 1) * (1 << n));
@@ -281,176 +407,303 @@
 }
 
 /*
- * Reset controller.
- * Controller reset also resets the baud rate and slave address, so
- * they must be re-established afterwards.
+ * twsi_reset() - Reset the I2C controller.
+ *
+ * Resetting the controller also resets the baud rate and slave address, hence
+ * they must be re-established after the reset.
+ *
+ * @twsi:	The MVTWSI register structure to use.
  */
-static void twsi_reset(struct i2c_adapter *adap)
+static void twsi_reset(struct mvtwsi_registers *twsi)
 {
-	struct mvtwsi_registers *twsi = twsi_get_base(adap);
-
-	/* reset controller */
+	/* Reset controller */
 	writel(0, &twsi->soft_reset);
-	/* wait 2 ms -- this is what the Marvell LSP does */
+	/* Wait 2 ms -- this is what the Marvell LSP does */
 	udelay(20000);
 }
 
 /*
- * I2C init called by cmd_i2c when doing 'i2c reset'.
- * Sets baud to the highest possible value not exceeding requested one.
+ * __twsi_i2c_set_bus_speed() - Set the speed of the I2C controller.
+ *
+ * This function sets baud rate to the highest possible value that does not
+ * exceed the requested rate.
+ *
+ * @twsi:		The MVTWSI register structure to use.
+ * @requested_speed:	The desired frequency the controller should run at
+ *			in Hz.
+ * @return The actual frequency the controller was configured to.
  */
-static unsigned int twsi_i2c_set_bus_speed(struct i2c_adapter *adap,
-					   unsigned int requested_speed)
+static uint __twsi_i2c_set_bus_speed(struct mvtwsi_registers *twsi,
+				     uint requested_speed)
 {
-	struct mvtwsi_registers *twsi = twsi_get_base(adap);
-	unsigned int tmp_speed, highest_speed, n, m;
-	unsigned int baud = 0x44; /* baudrate at controller reset */
+	uint tmp_speed, highest_speed, n, m;
+	uint baud = 0x44; /* Baud rate after controller reset */
 
-	/* use actual speed to collect progressively higher values */
 	highest_speed = 0;
-	/* compute m, n setting for highest speed not above requested speed */
+	/* Successively try m, n combinations, and use the combination
+	 * resulting in the largest speed that's not above the requested
+	 * speed */
 	for (n = 0; n < 8; n++) {
 		for (m = 0; m < 16; m++) {
 			tmp_speed = twsi_calc_freq(n, m);
-			if ((tmp_speed <= requested_speed)
-			 && (tmp_speed > highest_speed)) {
+			if ((tmp_speed <= requested_speed) &&
+			    (tmp_speed > highest_speed)) {
 				highest_speed = tmp_speed;
 				baud = (m << 3) | n;
 			}
 		}
 	}
 	writel(baud, &twsi->baudrate);
-	return 0;
-}
 
-static void twsi_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
-{
-	struct mvtwsi_registers *twsi = twsi_get_base(adap);
-
-	/* reset controller */
-	twsi_reset(adap);
-	/* set speed */
-	twsi_i2c_set_bus_speed(adap, speed);
-	/* set slave address even though we don't use it */
-	writel(slaveadd, &twsi->slave_address);
-	writel(0, &twsi->xtnd_slave_addr);
-	/* assert STOP but don't care for the result */
-	(void) twsi_stop(adap, 0);
+	/* Wait for controller for one tick */
+#ifdef CONFIG_DM_I2C
+	ndelay(calc_tick(highest_speed));
+#else
+	ndelay(10000);
+#endif
+	return highest_speed;
 }
 
 /*
- * Begin I2C transaction with expected start status, at given address.
- * Common to i2c_probe, i2c_read and i2c_write.
- * Expected address status will derive from direction bit (bit 0) in addr.
+ * __twsi_i2c_init() - Initialize the I2C controller.
+ *
+ * @twsi:		The MVTWSI register structure to use.
+ * @speed:		The initial frequency the controller should run at
+ *			in Hz.
+ * @slaveadd:		The I2C address to be set for the I2C master.
+ * @actual_speed:	A output parameter that receives the actual frequency
+ *			in Hz the controller was set to by the function.
+ * @return Zero if the operation succeeded, or a non-zero code if a time out
+ *	   occurred.
  */
-static int i2c_begin(struct i2c_adapter *adap, int expected_start_status,
-		     u8 addr, u8 *flags)
+static void __twsi_i2c_init(struct mvtwsi_registers *twsi, int speed,
+			    int slaveadd, uint *actual_speed)
+{
+	/* Reset controller */
+	twsi_reset(twsi);
+	/* Set speed */
+	*actual_speed = __twsi_i2c_set_bus_speed(twsi, speed);
+	/* Set slave address; even though we don't use it */
+	writel(slaveadd, &twsi->slave_address);
+	writel(0, &twsi->xtnd_slave_addr);
+	/* Assert STOP, but don't care for the result */
+#ifdef CONFIG_DM_I2C
+	(void) twsi_stop(twsi, calc_tick(*actual_speed));
+#else
+	(void) twsi_stop(twsi, 10000);
+#endif
+}
+
+/*
+ * i2c_begin() - Start a I2C transaction.
+ *
+ * Begin a I2C transaction with a given expected start status and chip address.
+ * A START is asserted, and the address byte is sent to the I2C controller. The
+ * expected address status will be derived from the direction bit (bit 0) of
+ * the address byte.
+ *
+ * @twsi:			The MVTWSI register structure to use.
+ * @expected_start_status:	The I2C status the controller is expected to
+ *				assert after the address byte was sent.
+ * @addr:			The address byte to be sent.
+ * @tick:			The duration of a clock cycle at the current
+ *				I2C speed.
+ * @return Zero if the operation succeeded, or a non-zero code if a time out or
+ *	   unexpected I2C status occurred.
+ */
+static int i2c_begin(struct mvtwsi_registers *twsi, int expected_start_status,
+		     u8 addr, uint tick)
 {
 	int status, expected_addr_status;
 
-	/* compute expected address status from direction bit in addr */
-	if (addr & 1) /* reading */
+	/* Compute the expected address status from the direction bit in
+	 * the address byte */
+	if (addr & 1) /* Reading */
 		expected_addr_status = MVTWSI_STATUS_ADDR_R_ACK;
-	else /* writing */
+	else /* Writing */
 		expected_addr_status = MVTWSI_STATUS_ADDR_W_ACK;
-	/* assert START */
-	status = twsi_start(adap, expected_start_status, flags);
-	/* send out the address if the start went well */
+	/* Assert START */
+	status = twsi_start(twsi, expected_start_status, tick);
+	/* Send out the address if the start went well */
 	if (status == 0)
-		status = twsi_send(adap, addr, expected_addr_status,
-				   flags);
-	/* return ok or status of first failure to caller */
+		status = twsi_send(twsi, addr, expected_addr_status, tick);
+	/* Return 0, or the status of the first failure */
 	return status;
 }
 
 /*
- * I2C probe called by cmd_i2c when doing 'i2c probe'.
- * Begin read, nak data byte, end.
+ * __twsi_i2c_probe_chip() - Probe the given I2C chip address.
+ *
+ * This function begins a I2C read transaction, does a dummy read and NAKs; if
+ * the procedure succeeds, the chip is considered to be present.
+ *
+ * @twsi:	The MVTWSI register structure to use.
+ * @chip:	The chip address to probe.
+ * @tick:	The duration of a clock cycle at the current I2C speed.
+ * @return Zero if the operation succeeded, or a non-zero code if a time out or
+ *	   unexpected I2C status occurred.
  */
-static int twsi_i2c_probe(struct i2c_adapter *adap, uchar chip)
+static int __twsi_i2c_probe_chip(struct mvtwsi_registers *twsi, uchar chip,
+				 uint tick)
 {
 	u8 dummy_byte;
-	u8 flags = 0;
 	int status;
 
-	/* begin i2c read */
-	status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1) | 1, &flags);
-	/* dummy read was accepted: receive byte but NAK it. */
+	/* Begin i2c read */
+	status = i2c_begin(twsi, MVTWSI_STATUS_START, (chip << 1) | 1, tick);
+	/* Dummy read was accepted: receive byte, but NAK it. */
 	if (status == 0)
-		status = twsi_recv(adap, &dummy_byte, &flags);
+		status = twsi_recv(twsi, &dummy_byte, MVTWSI_READ_NAK, tick);
 	/* Stop transaction */
-	twsi_stop(adap, 0);
-	/* return 0 or status of first failure */
+	twsi_stop(twsi, tick);
+	/* Return 0, or the status of the first failure */
 	return status;
 }
 
 /*
- * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c
- * Begin write, send address byte(s), begin read, receive data bytes, end.
+ * __twsi_i2c_read() - Read data from a I2C chip.
  *
- * NOTE: some EEPROMS want a stop right before the second start, while
- * some will choke if it is there. Deciding which we should do is eeprom
- * stuff, not i2c, but at the moment the APIs won't let us put it in
- * cmd_eeprom, so we have to choose here, and for the moment that'll be
- * a repeated start without a preceding stop.
+ * This function begins a I2C write transaction, and transmits the address
+ * bytes; then begins a I2C read transaction, and receives the data bytes.
+ *
+ * NOTE: Some devices want a stop right before the second start, while some
+ * will choke if it is there. Since deciding this is not yet supported in
+ * higher level APIs, we need to make a decision here, and for the moment that
+ * will be a repeated start without a preceding stop.
+ *
+ * @twsi:	The MVTWSI register structure to use.
+ * @chip:	The chip address to read from.
+ * @addr:	The address bytes to send.
+ * @alen:	The length of the address bytes in bytes.
+ * @data:	The buffer to receive the data read from the chip (has to have
+ *		a size of at least 'length' bytes).
+ * @length:	The amount of data to be read from the chip in bytes.
+ * @tick:	The duration of a clock cycle at the current I2C speed.
+ * @return Zero if the operation succeeded, or a non-zero code if a time out or
+ *	   unexpected I2C status occurred.
  */
-static int twsi_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
-			int alen, uchar *data, int length)
+static int __twsi_i2c_read(struct mvtwsi_registers *twsi, uchar chip,
+			   u8 *addr, int alen, uchar *data, int length,
+			   uint tick)
 {
-	int status;
-	u8 flags = 0;
+	int status = 0;
+	int stop_status;
+	int expected_start = MVTWSI_STATUS_START;
 
-	/* begin i2c write to send the address bytes */
-	status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1), &flags);
-	/* send addr bytes */
-	while ((status == 0) && alen--)
-		status = twsi_send(adap, addr >> (8*alen),
-			MVTWSI_STATUS_DATA_W_ACK, &flags);
-	/* begin i2c read to receive eeprom data bytes */
-	if (status == 0)
-		status = i2c_begin(adap, MVTWSI_STATUS_REPEATED_START,
-				   (chip << 1) | 1, &flags);
-	/* prepare ACK if at least one byte must be received */
-	if (length > 0)
-		flags |= MVTWSI_CONTROL_ACK;
-	/* now receive actual bytes */
-	while ((status == 0) && length--) {
-		/* reset NAK if we if no more to read now */
-		if (length == 0)
-			flags &= ~MVTWSI_CONTROL_ACK;
-		/* read current byte */
-		status = twsi_recv(adap, data++, &flags);
+	if (alen > 0) {
+		/* Begin i2c write to send the address bytes */
+		status = i2c_begin(twsi, expected_start, (chip << 1), tick);
+		/* Send address bytes */
+		while ((status == 0) && alen--)
+			status = twsi_send(twsi, *(addr++),
+					   MVTWSI_STATUS_DATA_W_ACK, tick);
+		/* Send repeated STARTs after the initial START */
+		expected_start = MVTWSI_STATUS_REPEATED_START;
 	}
+	/* Begin i2c read to receive data bytes */
+	if (status == 0)
+		status = i2c_begin(twsi, expected_start, (chip << 1) | 1, tick);
+	/* Receive actual data bytes; set NAK if we if we have nothing more to
+	 * read */
+	while ((status == 0) && length--)
+		status = twsi_recv(twsi, data++,
+				   length > 0 ?
+				   MVTWSI_READ_ACK : MVTWSI_READ_NAK, tick);
 	/* Stop transaction */
-	status = twsi_stop(adap, status);
-	/* return 0 or status of first failure */
-	return status;
+	stop_status = twsi_stop(twsi, tick);
+	/* Return 0, or the status of the first failure */
+	return status != 0 ? status : stop_status;
 }
 
 /*
- * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c
- * Begin write, send address byte(s), send data bytes, end.
+ * __twsi_i2c_write() - Send data to a I2C chip.
+ *
+ * This function begins a I2C write transaction, and transmits the address
+ * bytes; then begins a new I2C write transaction, and sends the data bytes.
+ *
+ * @twsi:	The MVTWSI register structure to use.
+ * @chip:	The chip address to read from.
+ * @addr:	The address bytes to send.
+ * @alen:	The length of the address bytes in bytes.
+ * @data:	The buffer containing the data to be sent to the chip.
+ * @length:	The length of data to be sent to the chip in bytes.
+ * @tick:	The duration of a clock cycle at the current I2C speed.
+ * @return Zero if the operation succeeded, or a non-zero code if a time out or
+ *	   unexpected I2C status occurred.
  */
-static int twsi_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
-			int alen, uchar *data, int length)
+static int __twsi_i2c_write(struct mvtwsi_registers *twsi, uchar chip,
+			    u8 *addr, int alen, uchar *data, int length,
+			    uint tick)
 {
-	int status;
-	u8 flags = 0;
+	int status, stop_status;
 
-	/* begin i2c write to send the eeprom adress bytes then data bytes */
-	status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1), &flags);
-	/* send addr bytes */
-	while ((status == 0) && alen--)
-		status = twsi_send(adap, addr >> (8*alen),
-			MVTWSI_STATUS_DATA_W_ACK, &flags);
-	/* send data bytes */
+	/* Begin i2c write to send first the address bytes, then the
+	 * data bytes */
+	status = i2c_begin(twsi, MVTWSI_STATUS_START, (chip << 1), tick);
+	/* Send address bytes */
+	while ((status == 0) && (alen-- > 0))
+		status = twsi_send(twsi, *(addr++), MVTWSI_STATUS_DATA_W_ACK,
+				   tick);
+	/* Send data bytes */
 	while ((status == 0) && (length-- > 0))
-		status = twsi_send(adap, *(data++), MVTWSI_STATUS_DATA_W_ACK,
-				   &flags);
+		status = twsi_send(twsi, *(data++), MVTWSI_STATUS_DATA_W_ACK,
+				   tick);
 	/* Stop transaction */
-	status = twsi_stop(adap, status);
-	/* return 0 or status of first failure */
-	return status;
+	stop_status = twsi_stop(twsi, tick);
+	/* Return 0, or the status of the first failure */
+	return status != 0 ? status : stop_status;
+}
+
+#ifndef CONFIG_DM_I2C
+static void twsi_i2c_init(struct i2c_adapter *adap, int speed,
+			  int slaveadd)
+{
+	struct mvtwsi_registers *twsi = twsi_get_base(adap);
+	__twsi_i2c_init(twsi, speed, slaveadd, NULL);
+}
+
+static uint twsi_i2c_set_bus_speed(struct i2c_adapter *adap,
+				   uint requested_speed)
+{
+	struct mvtwsi_registers *twsi = twsi_get_base(adap);
+	__twsi_i2c_set_bus_speed(twsi, requested_speed);
+	return 0;
+}
+
+static int twsi_i2c_probe(struct i2c_adapter *adap, uchar chip)
+{
+	struct mvtwsi_registers *twsi = twsi_get_base(adap);
+	return __twsi_i2c_probe_chip(twsi, chip, 10000);
+}
+
+static int twsi_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
+			 int alen, uchar *data, int length)
+{
+	struct mvtwsi_registers *twsi = twsi_get_base(adap);
+	u8 addr_bytes[4];
+
+	addr_bytes[0] = (addr >> 0) & 0xFF;
+	addr_bytes[1] = (addr >> 8) & 0xFF;
+	addr_bytes[2] = (addr >> 16) & 0xFF;
+	addr_bytes[3] = (addr >> 24) & 0xFF;
+
+	return __twsi_i2c_read(twsi, chip, addr_bytes, alen, data, length,
+			       10000);
+}
+
+static int twsi_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
+			  int alen, uchar *data, int length)
+{
+	struct mvtwsi_registers *twsi = twsi_get_base(adap);
+	u8 addr_bytes[4];
+
+	addr_bytes[0] = (addr >> 0) & 0xFF;
+	addr_bytes[1] = (addr >> 8) & 0xFF;
+	addr_bytes[2] = (addr >> 16) & 0xFF;
+	addr_bytes[3] = (addr >> 24) & 0xFF;
+
+	return __twsi_i2c_write(twsi, chip, addr_bytes, alen, data, length,
+				10000);
 }
 
 #ifdef CONFIG_I2C_MVTWSI_BASE0
@@ -494,3 +747,99 @@
 			 CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 5)
 
 #endif
+#else /* CONFIG_DM_I2C */
+
+static int mvtwsi_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
+				 u32 chip_flags)
+{
+	struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
+	return __twsi_i2c_probe_chip(dev->base, chip_addr, dev->tick);
+}
+
+static int mvtwsi_i2c_set_bus_speed(struct udevice *bus, uint speed)
+{
+	struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
+
+	dev->speed = __twsi_i2c_set_bus_speed(dev->base, speed);
+	dev->tick = calc_tick(dev->speed);
+
+	return 0;
+}
+
+static int mvtwsi_i2c_ofdata_to_platdata(struct udevice *bus)
+{
+	struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
+
+	dev->base = dev_get_addr_ptr(bus);
+
+	if (!dev->base)
+		return -ENOMEM;
+
+	dev->index = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
+				    "cell-index", -1);
+	dev->slaveadd = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
+				       "u-boot,i2c-slave-addr", 0x0);
+	dev->speed = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
+				    "clock-frequency", 100000);
+	return 0;
+}
+
+static int mvtwsi_i2c_probe(struct udevice *bus)
+{
+	struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
+	uint actual_speed;
+
+	__twsi_i2c_init(dev->base, dev->speed, dev->slaveadd, &actual_speed);
+	dev->speed = actual_speed;
+	dev->tick = calc_tick(dev->speed);
+	return 0;
+}
+
+static int mvtwsi_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+	struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
+	struct i2c_msg *dmsg, *omsg, dummy;
+
+	memset(&dummy, 0, sizeof(struct i2c_msg));
+
+	/* We expect either two messages (one with an offset and one with the
+	 * actual data) or one message (just data or offset/data combined) */
+	if (nmsgs > 2 || nmsgs == 0) {
+		debug("%s: Only one or two messages are supported.", __func__);
+		return -1;
+	}
+
+	omsg = nmsgs == 1 ? &dummy : msg;
+	dmsg = nmsgs == 1 ? msg : msg + 1;
+
+	if (dmsg->flags & I2C_M_RD)
+		return __twsi_i2c_read(dev->base, dmsg->addr, omsg->buf,
+				       omsg->len, dmsg->buf, dmsg->len,
+				       dev->tick);
+	else
+		return __twsi_i2c_write(dev->base, dmsg->addr, omsg->buf,
+					omsg->len, dmsg->buf, dmsg->len,
+					dev->tick);
+}
+
+static const struct dm_i2c_ops mvtwsi_i2c_ops = {
+	.xfer		= mvtwsi_i2c_xfer,
+	.probe_chip	= mvtwsi_i2c_probe_chip,
+	.set_bus_speed	= mvtwsi_i2c_set_bus_speed,
+};
+
+static const struct udevice_id mvtwsi_i2c_ids[] = {
+	{ .compatible = "marvell,mv64xxx-i2c", },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(i2c_mvtwsi) = {
+	.name = "i2c_mvtwsi",
+	.id = UCLASS_I2C,
+	.of_match = mvtwsi_i2c_ids,
+	.probe = mvtwsi_i2c_probe,
+	.ofdata_to_platdata = mvtwsi_i2c_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct mvtwsi_i2c_dev),
+	.ops = &mvtwsi_i2c_ops,
+};
+#endif /* CONFIG_DM_I2C */
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index a7f3fb4..0006343 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -39,6 +39,7 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <i2c.h>
 
 #include <asm/arch/i2c.h>
@@ -53,10 +54,14 @@
 /* Absolutely safe for status update at 100 kHz I2C: */
 #define I2C_WAIT	200
 
-static int wait_for_bb(struct i2c_adapter *adap);
-static struct i2c *omap24_get_base(struct i2c_adapter *adap);
-static u16 wait_for_event(struct i2c_adapter *adap);
-static void flush_fifo(struct i2c_adapter *adap);
+struct omap_i2c {
+	struct udevice *clk;
+	struct i2c *regs;
+	unsigned int speed;
+	int waitdelay;
+	int clk_id;
+};
+
 static int omap24_i2c_findpsc(u32 *pscl, u32 *psch, uint speed)
 {
 	unsigned int sampleclk, prescaler;
@@ -90,9 +95,96 @@
 	}
 	return -1;
 }
-static uint omap24_i2c_setspeed(struct i2c_adapter *adap, uint speed)
+
+/*
+ * Wait for the bus to be free by checking the Bus Busy (BB)
+ * bit to become clear
+ */
+static int wait_for_bb(struct i2c *i2c_base, int waitdelay)
 {
-	struct i2c *i2c_base = omap24_get_base(adap);
+	int timeout = I2C_TIMEOUT;
+	u16 stat;
+
+	writew(0xFFFF, &i2c_base->stat);	/* clear current interrupts...*/
+#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
+	while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) {
+#else
+	/* Read RAW status */
+	while ((stat = readw(&i2c_base->irqstatus_raw) &
+		I2C_STAT_BB) && timeout--) {
+#endif
+		writew(stat, &i2c_base->stat);
+		udelay(waitdelay);
+	}
+
+	if (timeout <= 0) {
+		printf("Timed out in wait_for_bb: status=%04x\n",
+		       stat);
+		return 1;
+	}
+	writew(0xFFFF, &i2c_base->stat);	 /* clear delayed stuff*/
+	return 0;
+}
+
+/*
+ * Wait for the I2C controller to complete current action
+ * and update status
+ */
+static u16 wait_for_event(struct i2c *i2c_base, int waitdelay)
+{
+	u16 status;
+	int timeout = I2C_TIMEOUT;
+
+	do {
+		udelay(waitdelay);
+#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
+		status = readw(&i2c_base->stat);
+#else
+		/* Read RAW status */
+		status = readw(&i2c_base->irqstatus_raw);
+#endif
+	} while (!(status &
+		   (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
+		    I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
+		    I2C_STAT_AL)) && timeout--);
+
+	if (timeout <= 0) {
+		printf("Timed out in wait_for_event: status=%04x\n",
+		       status);
+		/*
+		 * If status is still 0 here, probably the bus pads have
+		 * not been configured for I2C, and/or pull-ups are missing.
+		 */
+		printf("Check if pads/pull-ups of bus are properly configured\n");
+		writew(0xFFFF, &i2c_base->stat);
+		status = 0;
+	}
+
+	return status;
+}
+
+static void flush_fifo(struct i2c *i2c_base)
+{
+	u16 stat;
+
+	/*
+	 * note: if you try and read data when its not there or ready
+	 * you get a bus error
+	 */
+	while (1) {
+		stat = readw(&i2c_base->stat);
+		if (stat == I2C_STAT_RRDY) {
+			readb(&i2c_base->data);
+			writew(I2C_STAT_RRDY, &i2c_base->stat);
+			udelay(1000);
+		} else
+			break;
+	}
+}
+
+static int __omap24_i2c_setspeed(struct i2c *i2c_base, uint speed,
+				 int *waitdelay)
+{
 	int psc, fsscll = 0, fssclh = 0;
 	int hsscll = 0, hssclh = 0;
 	u32 scll = 0, sclh = 0;
@@ -142,8 +234,7 @@
 		}
 	}
 
-	adap->speed	= speed;
-	adap->waitdelay = (10000000 / speed) * 2; /* wait for 20 clkperiods */
+	*waitdelay = (10000000 / speed) * 2; /* wait for 20 clkperiods */
 	writew(0, &i2c_base->con);
 	writew(psc, &i2c_base->psc);
 	writew(scll, &i2c_base->scll);
@@ -154,9 +245,8 @@
 	return 0;
 }
 
-static void omap24_i2c_deblock(struct i2c_adapter *adap)
+static void omap24_i2c_deblock(struct i2c *i2c_base)
 {
-	struct i2c *i2c_base = omap24_get_base(adap);
 	int i;
 	u16 systest;
 	u16 orgsystest;
@@ -200,9 +290,9 @@
 	writew(orgsystest, &i2c_base->systest);
 }
 
-static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+static void __omap24_i2c_init(struct i2c *i2c_base, int speed, int slaveadd,
+			      int *waitdelay)
 {
-	struct i2c *i2c_base = omap24_get_base(adap);
 	int timeout = I2C_TIMEOUT;
 	int deblock = 1;
 
@@ -224,7 +314,7 @@
 		udelay(1000);
 	}
 
-	if (0 != omap24_i2c_setspeed(adap, speed)) {
+	if (0 != __omap24_i2c_setspeed(i2c_base, speed, waitdelay)) {
 		printf("ERROR: failed to setup I2C bus-speed!\n");
 		return;
 	}
@@ -241,45 +331,24 @@
 	       I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c_base->ie);
 #endif
 	udelay(1000);
-	flush_fifo(adap);
+	flush_fifo(i2c_base);
 	writew(0xFFFF, &i2c_base->stat);
 
 	/* Handle possible failed I2C state */
-	if (wait_for_bb(adap))
+	if (wait_for_bb(i2c_base, *waitdelay))
 		if (deblock == 1) {
-			omap24_i2c_deblock(adap);
+			omap24_i2c_deblock(i2c_base);
 			deblock = 0;
 			goto retry;
 		}
 }
 
-static void flush_fifo(struct i2c_adapter *adap)
-{
-	struct i2c *i2c_base = omap24_get_base(adap);
-	u16 stat;
-
-	/*
-	 * note: if you try and read data when its not there or ready
-	 * you get a bus error
-	 */
-	while (1) {
-		stat = readw(&i2c_base->stat);
-		if (stat == I2C_STAT_RRDY) {
-			readb(&i2c_base->data);
-			writew(I2C_STAT_RRDY, &i2c_base->stat);
-			udelay(1000);
-		} else
-			break;
-	}
-}
-
 /*
  * i2c_probe: Use write access. Allows to identify addresses that are
  *            write-only (like the config register of dual-port EEPROMs)
  */
-static int omap24_i2c_probe(struct i2c_adapter *adap, uchar chip)
+static int __omap24_i2c_probe(struct i2c *i2c_base, int waitdelay, uchar chip)
 {
-	struct i2c *i2c_base = omap24_get_base(adap);
 	u16 status;
 	int res = 1; /* default = fail */
 
@@ -287,7 +356,7 @@
 		return res;
 
 	/* Wait until bus is free */
-	if (wait_for_bb(adap))
+	if (wait_for_bb(i2c_base, waitdelay))
 		return res;
 
 	/* No data transfer, slave addr only */
@@ -296,7 +365,7 @@
 	writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
 	       I2C_CON_STP, &i2c_base->con);
 
-	status = wait_for_event(adap);
+	status = wait_for_event(i2c_base, waitdelay);
 
 	if ((status & ~I2C_STAT_XRDY) == 0 || (status & I2C_STAT_AL)) {
 		/*
@@ -306,8 +375,8 @@
 		 * following 'if' section:
 		 */
 		if (status == I2C_STAT_XRDY)
-			printf("i2c_probe: pads on bus %d probably not configured (status=0x%x)\n",
-			       adap->hwadapnr, status);
+			printf("i2c_probe: pads on bus probably not configured (status=0x%x)\n",
+			       status);
 
 		goto pr_exit;
 	}
@@ -315,7 +384,7 @@
 	/* Check for ACK (!NAK) */
 	if (!(status & I2C_STAT_NACK)) {
 		res = 0;				/* Device found */
-		udelay(adap->waitdelay);/* Required by AM335X in SPL */
+		udelay(waitdelay);/* Required by AM335X in SPL */
 		/* Abort transfer (force idle state) */
 		writew(I2C_CON_MST | I2C_CON_TRX, &i2c_base->con); /* Reset */
 		udelay(1000);
@@ -323,7 +392,7 @@
 		       I2C_CON_STP, &i2c_base->con);		/* STP */
 	}
 pr_exit:
-	flush_fifo(adap);
+	flush_fifo(i2c_base);
 	writew(0xFFFF, &i2c_base->stat);
 	return res;
 }
@@ -341,10 +410,9 @@
  *           or that do not need a register address at all (such as some clock
  *           distributors).
  */
-static int omap24_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
-			   int alen, uchar *buffer, int len)
+static int __omap24_i2c_read(struct i2c *i2c_base, int waitdelay, uchar chip,
+			     uint addr, int alen, uchar *buffer, int len)
 {
-	struct i2c *i2c_base = omap24_get_base(adap);
 	int i2c_error = 0;
 	u16 status;
 
@@ -389,7 +457,7 @@
 #endif
 
 	/* Wait until bus not busy */
-	if (wait_for_bb(adap))
+	if (wait_for_bb(i2c_base, waitdelay))
 		return 1;
 
 	/* Zero, one or two bytes reg address (offset) */
@@ -410,12 +478,12 @@
 #endif
 		/* Send register offset */
 		while (1) {
-			status = wait_for_event(adap);
+			status = wait_for_event(i2c_base, waitdelay);
 			/* Try to identify bus that is not padconf'd for I2C */
 			if (status == I2C_STAT_XRDY) {
 				i2c_error = 2;
-				printf("i2c_read (addr phase): pads on bus %d probably not configured (status=0x%x)\n",
-				       adap->hwadapnr, status);
+				printf("i2c_read (addr phase): pads on bus probably not configured (status=0x%x)\n",
+				       status);
 				goto rd_exit;
 			}
 			if (status == 0 || (status & I2C_STAT_NACK)) {
@@ -450,7 +518,7 @@
 
 	/* Receive data */
 	while (1) {
-		status = wait_for_event(adap);
+		status = wait_for_event(i2c_base, waitdelay);
 		/*
 		 * Try to identify bus that is not padconf'd for I2C. This
 		 * state could be left over from previous transactions if
@@ -458,8 +526,8 @@
 		 */
 		if (status == I2C_STAT_XRDY) {
 			i2c_error = 2;
-			printf("i2c_read (data phase): pads on bus %d probably not configured (status=0x%x)\n",
-			       adap->hwadapnr, status);
+			printf("i2c_read (data phase): pads on bus probably not configured (status=0x%x)\n",
+			       status);
 			goto rd_exit;
 		}
 		if (status == 0 || (status & I2C_STAT_NACK)) {
@@ -477,16 +545,15 @@
 	}
 
 rd_exit:
-	flush_fifo(adap);
+	flush_fifo(i2c_base);
 	writew(0xFFFF, &i2c_base->stat);
 	return i2c_error;
 }
 
 /* i2c_write: Address (reg offset) may be 0, 1 or 2 bytes long. */
-static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
-			    int alen, uchar *buffer, int len)
+static int __omap24_i2c_write(struct i2c *i2c_base, int waitdelay, uchar chip,
+			      uint addr, int alen, uchar *buffer, int len)
 {
-	struct i2c *i2c_base = omap24_get_base(adap);
 	int i;
 	u16 status;
 	int i2c_error = 0;
@@ -536,7 +603,7 @@
 #endif
 
 	/* Wait until bus not busy */
-	if (wait_for_bb(adap))
+	if (wait_for_bb(i2c_base, waitdelay))
 		return 1;
 
 	/* Start address phase - will write regoffset + len bytes data */
@@ -549,12 +616,12 @@
 
 	while (alen) {
 		/* Must write reg offset (one or two bytes) */
-		status = wait_for_event(adap);
+		status = wait_for_event(i2c_base, waitdelay);
 		/* Try to identify bus that is not padconf'd for I2C */
 		if (status == I2C_STAT_XRDY) {
 			i2c_error = 2;
-			printf("i2c_write: pads on bus %d probably not configured (status=0x%x)\n",
-			       adap->hwadapnr, status);
+			printf("i2c_write: pads on bus probably not configured (status=0x%x)\n",
+			       status);
 			goto wr_exit;
 		}
 		if (status == 0 || (status & I2C_STAT_NACK)) {
@@ -576,7 +643,7 @@
 	}
 	/* Address phase is over, now write data */
 	for (i = 0; i < len; i++) {
-		status = wait_for_event(adap);
+		status = wait_for_event(i2c_base, waitdelay);
 		if (status == 0 || (status & I2C_STAT_NACK)) {
 			i2c_error = 1;
 			printf("i2c_write: error waiting for data ACK (status=0x%x)\n",
@@ -598,87 +665,22 @@
 	 * transferred on the bus.
 	 */
 	do {
-		status = wait_for_event(adap);
+		status = wait_for_event(i2c_base, waitdelay);
 	} while (!(status & I2C_STAT_ARDY) && timeout--);
 	if (timeout <= 0)
 		printf("i2c_write: timed out writig last byte!\n");
 
 wr_exit:
-	flush_fifo(adap);
+	flush_fifo(i2c_base);
 	writew(0xFFFF, &i2c_base->stat);
 	return i2c_error;
 }
 
+#ifndef CONFIG_DM_I2C
 /*
- * Wait for the bus to be free by checking the Bus Busy (BB)
- * bit to become clear
+ * The legacy I2C functions. These need to get removed once
+ * all users of this driver are converted to DM.
  */
-static int wait_for_bb(struct i2c_adapter *adap)
-{
-	struct i2c *i2c_base = omap24_get_base(adap);
-	int timeout = I2C_TIMEOUT;
-	u16 stat;
-
-	writew(0xFFFF, &i2c_base->stat);	/* clear current interrupts...*/
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
-	while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) {
-#else
-	/* Read RAW status */
-	while ((stat = readw(&i2c_base->irqstatus_raw) &
-		I2C_STAT_BB) && timeout--) {
-#endif
-		writew(stat, &i2c_base->stat);
-		udelay(adap->waitdelay);
-	}
-
-	if (timeout <= 0) {
-		printf("Timed out in wait_for_bb: status=%04x\n",
-		       stat);
-		return 1;
-	}
-	writew(0xFFFF, &i2c_base->stat);	 /* clear delayed stuff*/
-	return 0;
-}
-
-/*
- * Wait for the I2C controller to complete current action
- * and update status
- */
-static u16 wait_for_event(struct i2c_adapter *adap)
-{
-	struct i2c *i2c_base = omap24_get_base(adap);
-	u16 status;
-	int timeout = I2C_TIMEOUT;
-
-	do {
-		udelay(adap->waitdelay);
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
-		status = readw(&i2c_base->stat);
-#else
-		/* Read RAW status */
-		status = readw(&i2c_base->irqstatus_raw);
-#endif
-	} while (!(status &
-		   (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
-		    I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
-		    I2C_STAT_AL)) && timeout--);
-
-	if (timeout <= 0) {
-		printf("Timed out in wait_for_event: status=%04x\n",
-		       status);
-		/*
-		 * If status is still 0 here, probably the bus pads have
-		 * not been configured for I2C, and/or pull-ups are missing.
-		 */
-		printf("Check if pads/pull-ups of bus %d are properly configured\n",
-		       adap->hwadapnr);
-		writew(0xFFFF, &i2c_base->stat);
-		status = 0;
-	}
-
-	return status;
-}
-
 static struct i2c *omap24_get_base(struct i2c_adapter *adap)
 {
 	switch (adap->hwadapnr) {
@@ -710,6 +712,56 @@
 	return NULL;
 }
 
+
+static int omap24_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
+			   int alen, uchar *buffer, int len)
+{
+	struct i2c *i2c_base = omap24_get_base(adap);
+
+	return __omap24_i2c_read(i2c_base, adap->waitdelay, chip, addr,
+				 alen, buffer, len);
+}
+
+
+static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
+			    int alen, uchar *buffer, int len)
+{
+	struct i2c *i2c_base = omap24_get_base(adap);
+
+	return __omap24_i2c_write(i2c_base, adap->waitdelay, chip, addr,
+				  alen, buffer, len);
+}
+
+static uint omap24_i2c_setspeed(struct i2c_adapter *adap, uint speed)
+{
+	struct i2c *i2c_base = omap24_get_base(adap);
+	int ret;
+
+	ret = __omap24_i2c_setspeed(i2c_base, speed, &adap->waitdelay);
+	if (ret) {
+		error("%s: set i2c speed failed\n", __func__);
+		return ret;
+	}
+
+	adap->speed = speed;
+
+	return 0;
+}
+
+static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+{
+	struct i2c *i2c_base = omap24_get_base(adap);
+
+	return __omap24_i2c_init(i2c_base, speed, slaveadd, &adap->waitdelay);
+}
+
+static int omap24_i2c_probe(struct i2c_adapter *adap, uchar chip)
+{
+	struct i2c *i2c_base = omap24_get_base(adap);
+
+	return __omap24_i2c_probe(i2c_base, adap->waitdelay, chip);
+}
+
 #if !defined(CONFIG_SYS_OMAP24_I2C_SPEED1)
 #define CONFIG_SYS_OMAP24_I2C_SPEED1 CONFIG_SYS_OMAP24_I2C_SPEED
 #endif
@@ -769,3 +821,92 @@
 #endif
 #endif
 #endif
+
+#else /* CONFIG_DM_I2C */
+
+static int omap_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+	struct omap_i2c *priv = dev_get_priv(bus);
+	int ret;
+
+	debug("i2c_xfer: %d messages\n", nmsgs);
+	for (; nmsgs > 0; nmsgs--, msg++) {
+		debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
+		if (msg->flags & I2C_M_RD) {
+			ret = __omap24_i2c_read(priv->regs, priv->waitdelay,
+						msg->addr, 0, 0, msg->buf,
+						msg->len);
+		} else {
+			ret = __omap24_i2c_write(priv->regs, priv->waitdelay,
+						 msg->addr, 0, 0, msg->buf,
+						 msg->len);
+		}
+		if (ret) {
+			debug("i2c_write: error sending\n");
+			return -EREMOTEIO;
+		}
+	}
+
+	return 0;
+}
+
+static int omap_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+	struct omap_i2c *priv = dev_get_priv(bus);
+
+	priv->speed = speed;
+
+	return __omap24_i2c_setspeed(priv->regs, speed, &priv->waitdelay);
+}
+
+static int omap_i2c_probe_chip(struct udevice *bus, uint chip_addr,
+				     uint chip_flags)
+{
+	struct omap_i2c *priv = dev_get_priv(bus);
+
+	return __omap24_i2c_probe(priv->regs, priv->waitdelay, chip_addr);
+}
+
+static int omap_i2c_probe(struct udevice *bus)
+{
+	struct omap_i2c *priv = dev_get_priv(bus);
+
+	__omap24_i2c_init(priv->regs, priv->speed, 0, &priv->waitdelay);
+
+	return 0;
+}
+
+static int omap_i2c_ofdata_to_platdata(struct udevice *bus)
+{
+	struct omap_i2c *priv = dev_get_priv(bus);
+
+	priv->regs = map_physmem(dev_get_addr(bus), sizeof(void *),
+				 MAP_NOCACHE);
+	priv->speed = CONFIG_SYS_OMAP24_I2C_SPEED;
+
+	return 0;
+}
+
+static const struct dm_i2c_ops omap_i2c_ops = {
+	.xfer		= omap_i2c_xfer,
+	.probe_chip	= omap_i2c_probe_chip,
+	.set_bus_speed	= omap_i2c_set_bus_speed,
+};
+
+static const struct udevice_id omap_i2c_ids[] = {
+	{ .compatible = "ti,omap4-i2c" },
+	{ }
+};
+
+U_BOOT_DRIVER(i2c_omap) = {
+	.name	= "i2c_omap",
+	.id	= UCLASS_I2C,
+	.of_match = omap_i2c_ids,
+	.ofdata_to_platdata = omap_i2c_ofdata_to_platdata,
+	.probe	= omap_i2c_probe,
+	.priv_auto_alloc_size = sizeof(struct omap_i2c),
+	.ops	= &omap_i2c_ops,
+	.flags  = DM_FLAG_PRE_RELOC,
+};
+
+#endif /* CONFIG_DM_I2C */
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index e0adb9b..dc8f2b6 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -61,6 +61,12 @@
 	help
 	  Support for Arasan SDHCI host controller on Zynq/ZynqMP ARM SoCs platform
 
+config ROCKCHIP_SDHCI
+	bool "Arasan SDHCI controller for Rockchip support"
+	depends on DM_MMC && BLK && DM_MMC_OPS
+	help
+	  Support for Arasan SDHCI host controller on Rockchip ARM SoCs platform
+
 config MMC_UNIPHIER
 	bool "UniPhier SD/MMC Host Controller support"
 	depends on ARCH_UNIPHIER
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index b44a12e..18351fb 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -56,6 +56,7 @@
 obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o
 obj-$(CONFIG_MMC_UNIPHIER) += uniphier-sd.o
 obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
+obj-$(CONFIG_ROCKCHIP_SDHCI) += rockchip_sdhci.o
 
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c
new file mode 100644
index 0000000..023c29b
--- /dev/null
+++ b/drivers/mmc/rockchip_sdhci.c
@@ -0,0 +1,93 @@
+/*
+ * (C) Copyright 2016 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * Rockchip SD Host Controller Interface
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <libfdt.h>
+#include <malloc.h>
+#include <sdhci.h>
+
+/* 400KHz is max freq for card ID etc. Use that as min */
+#define EMMC_MIN_FREQ	400000
+
+struct rockchip_sdhc_plat {
+	struct mmc_config cfg;
+	struct mmc mmc;
+};
+
+struct rockchip_sdhc {
+	struct sdhci_host host;
+	void *base;
+};
+
+static int arasan_sdhci_probe(struct udevice *dev)
+{
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct rockchip_sdhc_plat *plat = dev_get_platdata(dev);
+	struct rockchip_sdhc *prv = dev_get_priv(dev);
+	struct sdhci_host *host = &prv->host;
+	int ret;
+	u32 caps;
+
+	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD;
+
+	caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+	ret = sdhci_setup_cfg(&plat->cfg, dev->name, host->bus_width,
+			caps, CONFIG_ROCKCHIP_SDHCI_MAX_FREQ, EMMC_MIN_FREQ,
+			host->version, host->quirks, 0);
+
+	host->mmc = &plat->mmc;
+	if (ret)
+		return ret;
+	host->mmc->priv = &prv->host;
+	host->mmc->dev = dev;
+	upriv->mmc = host->mmc;
+
+	return sdhci_probe(dev);
+}
+
+static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev)
+{
+	struct sdhci_host *host = dev_get_priv(dev);
+
+	host->name = dev->name;
+	host->ioaddr = dev_get_addr_ptr(dev);
+
+	return 0;
+}
+
+static int rockchip_sdhci_bind(struct udevice *dev)
+{
+	struct rockchip_sdhc_plat *plat = dev_get_platdata(dev);
+	int ret;
+
+	ret = sdhci_bind(dev, &plat->mmc, &plat->cfg);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct udevice_id arasan_sdhci_ids[] = {
+	{ .compatible = "arasan,sdhci-5.1" },
+	{ }
+};
+
+U_BOOT_DRIVER(arasan_sdhci_drv) = {
+	.name		= "arasan_sdhci",
+	.id		= UCLASS_MMC,
+	.of_match	= arasan_sdhci_ids,
+	.ofdata_to_platdata = arasan_sdhci_ofdata_to_platdata,
+	.ops		= &sdhci_ops,
+	.bind		= rockchip_sdhci_bind,
+	.probe		= arasan_sdhci_probe,
+	.priv_auto_alloc_size = sizeof(struct rockchip_sdhc),
+	.platdata_auto_alloc_size = sizeof(struct rockchip_sdhc_plat),
+};
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 8c46a2f..5ce7d6d 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -64,12 +64,14 @@
 	  PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
 
 config NAND_SUNXI
-	bool "Support for NAND on Allwinner SoCs in SPL"
+	bool "Support for NAND on Allwinner SoCs"
 	depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I
 	select SYS_NAND_SELF_INIT
 	---help---
-	Enable support for NAND. This option allows SPL to read from
-	sunxi NAND using DMA transfers.
+	Enable support for NAND. This option enables the standard and
+	SPL drivers.
+	The SPL driver only supports reading from the NAND using DMA
+	transfers.
 
 config NAND_ARASAN
 	bool "Configure Arasan Nand"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 837d397..1df9273 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -66,6 +66,7 @@
 obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
 obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
 obj-$(CONFIG_NAND_PLAT) += nand_plat.o
+obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
 
 else  # minimal SPL drivers
 
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 6897167..d1287bc 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -29,6 +29,9 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <common.h>
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+#include <fdtdec.h>
+#endif
 #include <malloc.h>
 #include <watchdog.h>
 #include <linux/err.h>
@@ -2411,7 +2414,7 @@
 		int cached = writelen > bytes && page != blockmask;
 		uint8_t *wbuf = buf;
 		int use_bufpoi;
-		int part_pagewr = (column || writelen < (mtd->writesize - 1));
+		int part_pagewr = (column || writelen < mtd->writesize);
 
 		if (part_pagewr)
 			use_bufpoi = 1;
@@ -3763,6 +3766,66 @@
 	return type;
 }
 
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+DECLARE_GLOBAL_DATA_PTR;
+
+static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, int node)
+{
+	int ret, ecc_mode = -1, ecc_strength, ecc_step;
+	const void *blob = gd->fdt_blob;
+	const char *str;
+
+	ret = fdtdec_get_int(blob, node, "nand-bus-width", -1);
+	if (ret == 16)
+		chip->options |= NAND_BUSWIDTH_16;
+
+	if (fdtdec_get_bool(blob, node, "nand-on-flash-bbt"))
+		chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+	str = fdt_getprop(blob, node, "nand-ecc-mode", NULL);
+	if (str) {
+		if (!strcmp(str, "none"))
+			ecc_mode = NAND_ECC_NONE;
+		else if (!strcmp(str, "soft"))
+			ecc_mode = NAND_ECC_SOFT;
+		else if (!strcmp(str, "hw"))
+			ecc_mode = NAND_ECC_HW;
+		else if (!strcmp(str, "hw_syndrome"))
+			ecc_mode = NAND_ECC_HW_SYNDROME;
+		else if (!strcmp(str, "hw_oob_first"))
+			ecc_mode = NAND_ECC_HW_OOB_FIRST;
+		else if (!strcmp(str, "soft_bch"))
+			ecc_mode = NAND_ECC_SOFT_BCH;
+	}
+
+
+	ecc_strength = fdtdec_get_int(blob, node, "nand-ecc-strength", -1);
+	ecc_step = fdtdec_get_int(blob, node, "nand-ecc-step-size", -1);
+
+	if ((ecc_step >= 0 && !(ecc_strength >= 0)) ||
+	    (!(ecc_step >= 0) && ecc_strength >= 0)) {
+		pr_err("must set both strength and step size in DT\n");
+		return -EINVAL;
+	}
+
+	if (ecc_mode >= 0)
+		chip->ecc.mode = ecc_mode;
+
+	if (ecc_strength >= 0)
+		chip->ecc.strength = ecc_strength;
+
+	if (ecc_step > 0)
+		chip->ecc.size = ecc_step;
+
+	return 0;
+}
+#else
+static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, int node)
+{
+	return 0;
+}
+#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
+
 /**
  * nand_scan_ident - [NAND Interface] Scan for the NAND device
  * @mtd: MTD device structure
@@ -3779,6 +3842,13 @@
 	int i, nand_maf_id, nand_dev_id;
 	struct nand_chip *chip = mtd_to_nand(mtd);
 	struct nand_flash_dev *type;
+	int ret;
+
+	if (chip->flash_node) {
+		ret = nand_dt_init(mtd, chip, chip->flash_node);
+		if (ret)
+			return ret;
+	}
 
 	/* Set the default functions */
 	nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 561d2cd..ce0a14e 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -62,6 +62,10 @@
 		{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
 		  SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
 		  NAND_ECC_INFO(40, SZ_1K), 4 },
+	{"H27QCG8T2E5R‐BCF 64G 3.3V 8-bit",
+		{ .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} },
+		  SZ_16K, SZ_8K, SZ_4M, NAND_NEED_SCRAMBLING, 6, 1664,
+		  NAND_ECC_INFO(56, SZ_1K), 1 },
 
 	LEGACY_ID_NAND("NAND 4MiB 5V 8-bit",   0x6B, 4, SZ_8K, SP_OPTIONS),
 	LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
new file mode 100644
index 0000000..c4e2cd7
--- /dev/null
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -0,0 +1,1845 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
+ * Copyright (C) 2015 Roy Spliet <r.spliet@ultimaker.com>
+ *
+ * Derived from:
+ *	https://github.com/yuq/sunxi-nfc-mtd
+ *	Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
+ *
+ *	https://github.com/hno/Allwinner-Info
+ *	Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
+ *
+ *	Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
+ *	Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <memalign.h>
+#include <nand.h>
+
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+#include <asm/gpio.h>
+#include <asm/arch/clock.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NFC_REG_CTL		0x0000
+#define NFC_REG_ST		0x0004
+#define NFC_REG_INT		0x0008
+#define NFC_REG_TIMING_CTL	0x000C
+#define NFC_REG_TIMING_CFG	0x0010
+#define NFC_REG_ADDR_LOW	0x0014
+#define NFC_REG_ADDR_HIGH	0x0018
+#define NFC_REG_SECTOR_NUM	0x001C
+#define NFC_REG_CNT		0x0020
+#define NFC_REG_CMD		0x0024
+#define NFC_REG_RCMD_SET	0x0028
+#define NFC_REG_WCMD_SET	0x002C
+#define NFC_REG_IO_DATA		0x0030
+#define NFC_REG_ECC_CTL		0x0034
+#define NFC_REG_ECC_ST		0x0038
+#define NFC_REG_DEBUG		0x003C
+#define NFC_REG_ECC_ERR_CNT(x)	((0x0040 + (x)) & ~0x3)
+#define NFC_REG_USER_DATA(x)	(0x0050 + ((x) * 4))
+#define NFC_REG_SPARE_AREA	0x00A0
+#define NFC_REG_PAT_ID		0x00A4
+#define NFC_RAM0_BASE		0x0400
+#define NFC_RAM1_BASE		0x0800
+
+/* define bit use in NFC_CTL */
+#define NFC_EN			BIT(0)
+#define NFC_RESET		BIT(1)
+#define NFC_BUS_WIDTH_MSK	BIT(2)
+#define NFC_BUS_WIDTH_8		(0 << 2)
+#define NFC_BUS_WIDTH_16	(1 << 2)
+#define NFC_RB_SEL_MSK		BIT(3)
+#define NFC_RB_SEL(x)		((x) << 3)
+#define NFC_CE_SEL_MSK		(0x7 << 24)
+#define NFC_CE_SEL(x)		((x) << 24)
+#define NFC_CE_CTL		BIT(6)
+#define NFC_PAGE_SHIFT_MSK	(0xf << 8)
+#define NFC_PAGE_SHIFT(x)	(((x) < 10 ? 0 : (x) - 10) << 8)
+#define NFC_SAM			BIT(12)
+#define NFC_RAM_METHOD		BIT(14)
+#define NFC_DEBUG_CTL		BIT(31)
+
+/* define bit use in NFC_ST */
+#define NFC_RB_B2R		BIT(0)
+#define NFC_CMD_INT_FLAG	BIT(1)
+#define NFC_DMA_INT_FLAG	BIT(2)
+#define NFC_CMD_FIFO_STATUS	BIT(3)
+#define NFC_STA			BIT(4)
+#define NFC_NATCH_INT_FLAG	BIT(5)
+#define NFC_RB_STATE(x)		BIT(x + 8)
+
+/* define bit use in NFC_INT */
+#define NFC_B2R_INT_ENABLE	BIT(0)
+#define NFC_CMD_INT_ENABLE	BIT(1)
+#define NFC_DMA_INT_ENABLE	BIT(2)
+#define NFC_INT_MASK		(NFC_B2R_INT_ENABLE | \
+				 NFC_CMD_INT_ENABLE | \
+				 NFC_DMA_INT_ENABLE)
+
+/* define bit use in NFC_TIMING_CTL */
+#define NFC_TIMING_CTL_EDO	BIT(8)
+
+/* define NFC_TIMING_CFG register layout */
+#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD)		\
+	(((tWB) & 0x3) | (((tADL) & 0x3) << 2) |		\
+	(((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) |		\
+	(((tCAD) & 0x7) << 8))
+
+/* define bit use in NFC_CMD */
+#define NFC_CMD_LOW_BYTE_MSK	0xff
+#define NFC_CMD_HIGH_BYTE_MSK	(0xff << 8)
+#define NFC_CMD(x)		(x)
+#define NFC_ADR_NUM_MSK		(0x7 << 16)
+#define NFC_ADR_NUM(x)		(((x) - 1) << 16)
+#define NFC_SEND_ADR		BIT(19)
+#define NFC_ACCESS_DIR		BIT(20)
+#define NFC_DATA_TRANS		BIT(21)
+#define NFC_SEND_CMD1		BIT(22)
+#define NFC_WAIT_FLAG		BIT(23)
+#define NFC_SEND_CMD2		BIT(24)
+#define NFC_SEQ			BIT(25)
+#define NFC_DATA_SWAP_METHOD	BIT(26)
+#define NFC_ROW_AUTO_INC	BIT(27)
+#define NFC_SEND_CMD3		BIT(28)
+#define NFC_SEND_CMD4		BIT(29)
+#define NFC_CMD_TYPE_MSK	(0x3 << 30)
+#define NFC_NORMAL_OP		(0 << 30)
+#define NFC_ECC_OP		(1 << 30)
+#define NFC_PAGE_OP		(2 << 30)
+
+/* define bit use in NFC_RCMD_SET */
+#define NFC_READ_CMD_MSK	0xff
+#define NFC_RND_READ_CMD0_MSK	(0xff << 8)
+#define NFC_RND_READ_CMD1_MSK	(0xff << 16)
+
+/* define bit use in NFC_WCMD_SET */
+#define NFC_PROGRAM_CMD_MSK	0xff
+#define NFC_RND_WRITE_CMD_MSK	(0xff << 8)
+#define NFC_READ_CMD0_MSK	(0xff << 16)
+#define NFC_READ_CMD1_MSK	(0xff << 24)
+
+/* define bit use in NFC_ECC_CTL */
+#define NFC_ECC_EN		BIT(0)
+#define NFC_ECC_PIPELINE	BIT(3)
+#define NFC_ECC_EXCEPTION	BIT(4)
+#define NFC_ECC_BLOCK_SIZE_MSK	BIT(5)
+#define NFC_ECC_BLOCK_512	(1 << 5)
+#define NFC_RANDOM_EN		BIT(9)
+#define NFC_RANDOM_DIRECTION	BIT(10)
+#define NFC_ECC_MODE_MSK	(0xf << 12)
+#define NFC_ECC_MODE(x)		((x) << 12)
+#define NFC_RANDOM_SEED_MSK	(0x7fff << 16)
+#define NFC_RANDOM_SEED(x)	((x) << 16)
+
+/* define bit use in NFC_ECC_ST */
+#define NFC_ECC_ERR(x)		BIT(x)
+#define NFC_ECC_PAT_FOUND(x)	BIT(x + 16)
+#define NFC_ECC_ERR_CNT(b, x)	(((x) >> ((b) * 8)) & 0xff)
+
+#define NFC_DEFAULT_TIMEOUT_MS	1000
+
+#define NFC_SRAM_SIZE		1024
+
+#define NFC_MAX_CS		7
+
+/*
+ * Ready/Busy detection type: describes the Ready/Busy detection modes
+ *
+ * @RB_NONE:	no external detection available, rely on STATUS command
+ *		and software timeouts
+ * @RB_NATIVE:	use sunxi NAND controller Ready/Busy support. The Ready/Busy
+ *		pin of the NAND flash chip must be connected to one of the
+ *		native NAND R/B pins (those which can be muxed to the NAND
+ *		Controller)
+ * @RB_GPIO:	use a simple GPIO to handle Ready/Busy status. The Ready/Busy
+ *		pin of the NAND flash chip must be connected to a GPIO capable
+ *		pin.
+ */
+enum sunxi_nand_rb_type {
+	RB_NONE,
+	RB_NATIVE,
+	RB_GPIO,
+};
+
+/*
+ * Ready/Busy structure: stores information related to Ready/Busy detection
+ *
+ * @type:	the Ready/Busy detection mode
+ * @info:	information related to the R/B detection mode. Either a gpio
+ *		id or a native R/B id (those supported by the NAND controller).
+ */
+struct sunxi_nand_rb {
+	enum sunxi_nand_rb_type type;
+	union {
+		struct gpio_desc gpio;
+		int nativeid;
+	} info;
+};
+
+/*
+ * Chip Select structure: stores information related to NAND Chip Select
+ *
+ * @cs:		the NAND CS id used to communicate with a NAND Chip
+ * @rb:		the Ready/Busy description
+ */
+struct sunxi_nand_chip_sel {
+	u8 cs;
+	struct sunxi_nand_rb rb;
+};
+
+/*
+ * sunxi HW ECC infos: stores information related to HW ECC support
+ *
+ * @mode:	the sunxi ECC mode field deduced from ECC requirements
+ * @layout:	the OOB layout depending on the ECC requirements and the
+ *		selected ECC mode
+ */
+struct sunxi_nand_hw_ecc {
+	int mode;
+	struct nand_ecclayout layout;
+};
+
+/*
+ * NAND chip structure: stores NAND chip device related information
+ *
+ * @node:		used to store NAND chips into a list
+ * @nand:		base NAND chip structure
+ * @mtd:		base MTD structure
+ * @clk_rate:		clk_rate required for this NAND chip
+ * @timing_cfg		TIMING_CFG register value for this NAND chip
+ * @selected:		current active CS
+ * @nsels:		number of CS lines required by the NAND chip
+ * @sels:		array of CS lines descriptions
+ */
+struct sunxi_nand_chip {
+	struct list_head node;
+	struct nand_chip nand;
+	unsigned long clk_rate;
+	u32 timing_cfg;
+	u32 timing_ctl;
+	int selected;
+	int addr_cycles;
+	u32 addr[2];
+	int cmd_cycles;
+	u8 cmd[2];
+	int nsels;
+	struct sunxi_nand_chip_sel sels[0];
+};
+
+static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
+{
+	return container_of(nand, struct sunxi_nand_chip, nand);
+}
+
+/*
+ * NAND Controller structure: stores sunxi NAND controller information
+ *
+ * @controller:		base controller structure
+ * @dev:		parent device (used to print error messages)
+ * @regs:		NAND controller registers
+ * @ahb_clk:		NAND Controller AHB clock
+ * @mod_clk:		NAND Controller mod clock
+ * @assigned_cs:	bitmask describing already assigned CS lines
+ * @clk_rate:		NAND controller current clock rate
+ * @chips:		a list containing all the NAND chips attached to
+ *			this NAND controller
+ * @complete:		a completion object used to wait for NAND
+ *			controller events
+ */
+struct sunxi_nfc {
+	struct nand_hw_control controller;
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *ahb_clk;
+	struct clk *mod_clk;
+	unsigned long assigned_cs;
+	unsigned long clk_rate;
+	struct list_head chips;
+};
+
+static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
+{
+	return container_of(ctrl, struct sunxi_nfc, controller);
+}
+
+static void sunxi_nfc_set_clk_rate(unsigned long hz)
+{
+	struct sunxi_ccm_reg *const ccm =
+	(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	int div_m, div_n;
+
+	div_m = (clock_get_pll6() + hz - 1) / hz;
+	for (div_n = 0; div_n < 3 && div_m > 16; div_n++) {
+		if (div_m % 2)
+			div_m++;
+		div_m >>= 1;
+	}
+	if (div_m > 16)
+		div_m = 16;
+
+	/* config mod clock */
+	writel(CCM_NAND_CTRL_ENABLE | CCM_NAND_CTRL_PLL6 |
+	       CCM_NAND_CTRL_N(div_n) | CCM_NAND_CTRL_M(div_m),
+	       &ccm->nand0_clk_cfg);
+
+	/* gate on nand clock */
+	setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0));
+#ifdef CONFIG_MACH_SUN9I
+	setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
+#else
+	setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
+#endif
+}
+
+static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
+			      unsigned int timeout_ms)
+{
+	unsigned int timeout_ticks;
+	u32 time_start, status;
+	int ret = -ETIMEDOUT;
+
+	if (!timeout_ms)
+		timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
+
+	timeout_ticks = (timeout_ms * CONFIG_SYS_HZ) / 1000;
+
+	time_start = get_timer(0);
+
+	do {
+		status = readl(nfc->regs + NFC_REG_ST);
+		if ((status & flags) == flags) {
+			ret = 0;
+			break;
+		}
+
+		udelay(1);
+	} while (get_timer(time_start) < timeout_ticks);
+
+	writel(status & flags, nfc->regs + NFC_REG_ST);
+
+	return ret;
+}
+
+static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
+{
+	unsigned long timeout = (CONFIG_SYS_HZ *
+				 NFC_DEFAULT_TIMEOUT_MS) / 1000;
+	u32 time_start;
+
+	time_start = get_timer(0);
+	do {
+		if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+			return 0;
+	} while (get_timer(time_start) < timeout);
+
+	dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
+	return -ETIMEDOUT;
+}
+
+static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
+{
+	unsigned long timeout = (CONFIG_SYS_HZ *
+				 NFC_DEFAULT_TIMEOUT_MS) / 1000;
+	u32 time_start;
+
+	writel(0, nfc->regs + NFC_REG_ECC_CTL);
+	writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
+
+	time_start = get_timer(0);
+	do {
+		if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET))
+			return 0;
+	} while (get_timer(time_start) < timeout);
+
+	dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
+	return -ETIMEDOUT;
+}
+
+static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	struct sunxi_nand_rb *rb;
+	unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20);
+	int ret;
+
+	if (sunxi_nand->selected < 0)
+		return 0;
+
+	rb = &sunxi_nand->sels[sunxi_nand->selected].rb;
+
+	switch (rb->type) {
+	case RB_NATIVE:
+		ret = !!(readl(nfc->regs + NFC_REG_ST) &
+			 NFC_RB_STATE(rb->info.nativeid));
+		if (ret)
+			break;
+
+		sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
+		ret = !!(readl(nfc->regs + NFC_REG_ST) &
+			 NFC_RB_STATE(rb->info.nativeid));
+		break;
+	case RB_GPIO:
+		ret = dm_gpio_get_value(&rb->info.gpio);
+		break;
+	case RB_NONE:
+	default:
+		ret = 0;
+		dev_err(nfc->dev, "cannot check R/B NAND status!\n");
+		break;
+	}
+
+	return ret;
+}
+
+static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	struct sunxi_nand_chip_sel *sel;
+	u32 ctl;
+
+	if (chip > 0 && chip >= sunxi_nand->nsels)
+		return;
+
+	if (chip == sunxi_nand->selected)
+		return;
+
+	ctl = readl(nfc->regs + NFC_REG_CTL) &
+	      ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);
+
+	if (chip >= 0) {
+		sel = &sunxi_nand->sels[chip];
+
+		ctl |= NFC_CE_SEL(sel->cs) | NFC_EN |
+		       NFC_PAGE_SHIFT(nand->page_shift - 10);
+		if (sel->rb.type == RB_NONE) {
+			nand->dev_ready = NULL;
+		} else {
+			nand->dev_ready = sunxi_nfc_dev_ready;
+			if (sel->rb.type == RB_NATIVE)
+				ctl |= NFC_RB_SEL(sel->rb.info.nativeid);
+		}
+
+		writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
+
+		if (nfc->clk_rate != sunxi_nand->clk_rate) {
+			sunxi_nfc_set_clk_rate(sunxi_nand->clk_rate);
+			nfc->clk_rate = sunxi_nand->clk_rate;
+		}
+	}
+
+	writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
+	writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
+	writel(ctl, nfc->regs + NFC_REG_CTL);
+
+	sunxi_nand->selected = chip;
+}
+
+static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	int ret;
+	int cnt;
+	int offs = 0;
+	u32 tmp;
+
+	while (len > offs) {
+		cnt = min(len - offs, NFC_SRAM_SIZE);
+
+		ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+		if (ret)
+			break;
+
+		writel(cnt, nfc->regs + NFC_REG_CNT);
+		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
+		writel(tmp, nfc->regs + NFC_REG_CMD);
+
+		ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+		if (ret)
+			break;
+
+		if (buf)
+			memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
+				      cnt);
+		offs += cnt;
+	}
+}
+
+static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+				int len)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	int ret;
+	int cnt;
+	int offs = 0;
+	u32 tmp;
+
+	while (len > offs) {
+		cnt = min(len - offs, NFC_SRAM_SIZE);
+
+		ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+		if (ret)
+			break;
+
+		writel(cnt, nfc->regs + NFC_REG_CNT);
+		memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
+		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+		      NFC_ACCESS_DIR;
+		writel(tmp, nfc->regs + NFC_REG_CMD);
+
+		ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+		if (ret)
+			break;
+
+		offs += cnt;
+	}
+}
+
+static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
+{
+	uint8_t ret;
+
+	sunxi_nfc_read_buf(mtd, &ret, 1);
+
+	return ret;
+}
+
+static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
+			       unsigned int ctrl)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	int ret;
+	u32 tmp;
+
+	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+	if (ret)
+		return;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		tmp = readl(nfc->regs + NFC_REG_CTL);
+		if (ctrl & NAND_NCE)
+			tmp |= NFC_CE_CTL;
+		else
+			tmp &= ~NFC_CE_CTL;
+		writel(tmp, nfc->regs + NFC_REG_CTL);
+	}
+
+	if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
+	    !(ctrl & (NAND_CLE | NAND_ALE))) {
+		u32 cmd = 0;
+
+		if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles)
+			return;
+
+		if (sunxi_nand->cmd_cycles--)
+			cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0];
+
+		if (sunxi_nand->cmd_cycles--) {
+			cmd |= NFC_SEND_CMD2;
+			writel(sunxi_nand->cmd[1],
+			       nfc->regs + NFC_REG_RCMD_SET);
+		}
+
+		sunxi_nand->cmd_cycles = 0;
+
+		if (sunxi_nand->addr_cycles) {
+			cmd |= NFC_SEND_ADR |
+			       NFC_ADR_NUM(sunxi_nand->addr_cycles);
+			writel(sunxi_nand->addr[0],
+			       nfc->regs + NFC_REG_ADDR_LOW);
+		}
+
+		if (sunxi_nand->addr_cycles > 4)
+			writel(sunxi_nand->addr[1],
+			       nfc->regs + NFC_REG_ADDR_HIGH);
+
+		writel(cmd, nfc->regs + NFC_REG_CMD);
+		sunxi_nand->addr[0] = 0;
+		sunxi_nand->addr[1] = 0;
+		sunxi_nand->addr_cycles = 0;
+		sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+	}
+
+	if (ctrl & NAND_CLE) {
+		sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat;
+	} else if (ctrl & NAND_ALE) {
+		sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |=
+				dat << ((sunxi_nand->addr_cycles % 4) * 8);
+		sunxi_nand->addr_cycles++;
+	}
+}
+
+/* These seed values have been extracted from Allwinner's BSP */
+static const u16 sunxi_nfc_randomizer_page_seeds[] = {
+	0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
+	0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
+	0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
+	0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
+	0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
+	0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
+	0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
+	0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
+	0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
+	0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
+	0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
+	0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
+	0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
+	0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
+	0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
+	0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
+};
+
+/*
+ * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
+ * have been generated using
+ * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
+ * the randomizer engine does internally before de/scrambling OOB data.
+ *
+ * Those tables are statically defined to avoid calculating randomizer state
+ * at runtime.
+ */
+static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
+	0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
+	0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
+	0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
+	0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
+	0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
+	0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
+	0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
+	0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
+	0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
+	0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
+	0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
+	0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
+	0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
+	0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
+	0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
+	0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
+};
+
+static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
+	0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
+	0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
+	0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
+	0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
+	0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
+	0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
+	0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
+	0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
+	0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
+	0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
+	0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
+	0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
+	0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
+	0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
+	0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
+	0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
+};
+
+static u16 sunxi_nfc_randomizer_step(u16 state, int count)
+{
+	state &= 0x7fff;
+
+	/*
+	 * This loop is just a simple implementation of a Fibonacci LFSR using
+	 * the x16 + x15 + 1 polynomial.
+	 */
+	while (count--)
+		state = ((state >> 1) |
+			 (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
+
+	return state;
+}
+
+static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc)
+{
+	const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
+	int mod = mtd->erasesize / mtd->writesize;
+
+	if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
+		mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
+
+	if (ecc) {
+		if (mtd->ecc_step_size == 512)
+			seeds = sunxi_nfc_randomizer_ecc512_seeds;
+		else
+			seeds = sunxi_nfc_randomizer_ecc1024_seeds;
+	}
+
+	return seeds[page % mod];
+}
+
+static void sunxi_nfc_randomizer_config(struct mtd_info *mtd,
+					int page, bool ecc)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+	u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+	u16 state;
+
+	if (!(nand->options & NAND_NEED_SCRAMBLING))
+		return;
+
+	ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+	state = sunxi_nfc_randomizer_state(mtd, page, ecc);
+	ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
+	writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+	if (!(nand->options & NAND_NEED_SCRAMBLING))
+		return;
+
+	writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
+	       nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+	if (!(nand->options & NAND_NEED_SCRAMBLING))
+		return;
+
+	writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+	       nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm)
+{
+	u16 state = sunxi_nfc_randomizer_state(mtd, page, true);
+
+	bbm[0] ^= state;
+	bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
+}
+
+static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd,
+					   const uint8_t *buf, int len,
+					   bool ecc, int page)
+{
+	sunxi_nfc_randomizer_config(mtd, page, ecc);
+	sunxi_nfc_randomizer_enable(mtd);
+	sunxi_nfc_write_buf(mtd, buf, len);
+	sunxi_nfc_randomizer_disable(mtd);
+}
+
+static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf,
+					  int len, bool ecc, int page)
+{
+	sunxi_nfc_randomizer_config(mtd, page, ecc);
+	sunxi_nfc_randomizer_enable(mtd);
+	sunxi_nfc_read_buf(mtd, buf, len);
+	sunxi_nfc_randomizer_disable(mtd);
+}
+
+static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+	struct sunxi_nand_hw_ecc *data = nand->ecc.priv;
+	u32 ecc_ctl;
+
+	ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+	ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
+		     NFC_ECC_BLOCK_SIZE_MSK);
+	ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION;
+
+	if (nand->ecc.size == 512)
+		ecc_ctl |= NFC_ECC_BLOCK_512;
+
+	writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+	writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
+	       nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
+{
+	buf[0] = user_data;
+	buf[1] = user_data >> 8;
+	buf[2] = user_data >> 16;
+	buf[3] = user_data >> 24;
+}
+
+static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
+				       u8 *data, int data_off,
+				       u8 *oob, int oob_off,
+				       int *cur_off,
+				       unsigned int *max_bitflips,
+				       bool bbm, int page)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+	struct nand_ecc_ctrl *ecc = &nand->ecc;
+	int raw_mode = 0;
+	u32 status;
+	int ret;
+
+	if (*cur_off != data_off)
+		nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
+
+	sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page);
+
+	if (data_off + ecc->size != oob_off)
+		nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
+
+	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+	if (ret)
+		return ret;
+
+	sunxi_nfc_randomizer_enable(mtd);
+	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
+	       nfc->regs + NFC_REG_CMD);
+
+	ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+	sunxi_nfc_randomizer_disable(mtd);
+	if (ret)
+		return ret;
+
+	*cur_off = oob_off + ecc->bytes + 4;
+
+	status = readl(nfc->regs + NFC_REG_ECC_ST);
+	if (status & NFC_ECC_PAT_FOUND(0)) {
+		u8 pattern = 0xff;
+
+		if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1)))
+			pattern = 0x0;
+
+		memset(data, pattern, ecc->size);
+		memset(oob, pattern, ecc->bytes + 4);
+
+		return 1;
+	}
+
+	ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)));
+
+	memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
+
+	nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
+	sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);
+
+	if (status & NFC_ECC_ERR(0)) {
+		/*
+		 * Re-read the data with the randomizer disabled to identify
+		 * bitflips in erased pages.
+		 */
+		if (nand->options & NAND_NEED_SCRAMBLING) {
+			nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
+			nand->read_buf(mtd, data, ecc->size);
+			nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
+			nand->read_buf(mtd, oob, ecc->bytes + 4);
+		}
+
+		ret = nand_check_erased_ecc_chunk(data,	ecc->size,
+						  oob, ecc->bytes + 4,
+						  NULL, 0, ecc->strength);
+		if (ret >= 0)
+			raw_mode = 1;
+	} else {
+		/*
+		 * The engine protects 4 bytes of OOB data per chunk.
+		 * Retrieve the corrected OOB bytes.
+		 */
+		sunxi_nfc_user_data_to_buf(readl(nfc->regs +
+						 NFC_REG_USER_DATA(0)),
+					   oob);
+
+		/* De-randomize the Bad Block Marker. */
+		if (bbm && nand->options & NAND_NEED_SCRAMBLING)
+			sunxi_nfc_randomize_bbm(mtd, page, oob);
+	}
+
+	if (ret < 0) {
+		mtd->ecc_stats.failed++;
+	} else {
+		mtd->ecc_stats.corrected += ret;
+		*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
+	}
+
+	return raw_mode;
+}
+
+static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
+					    u8 *oob, int *cur_off,
+					    bool randomize, int page)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct nand_ecc_ctrl *ecc = &nand->ecc;
+	int offset = ((ecc->bytes + 4) * ecc->steps);
+	int len = mtd->oobsize - offset;
+
+	if (len <= 0)
+		return;
+
+	if (*cur_off != offset)
+		nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
+			      offset + mtd->writesize, -1);
+
+	if (!randomize)
+		sunxi_nfc_read_buf(mtd, oob + offset, len);
+	else
+		sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
+					      false, page);
+
+	*cur_off = mtd->oobsize + mtd->writesize;
+}
+
+static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
+{
+	return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
+					const u8 *data, int data_off,
+					const u8 *oob, int oob_off,
+					int *cur_off, bool bbm,
+					int page)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+	struct nand_ecc_ctrl *ecc = &nand->ecc;
+	int ret;
+
+	if (data_off != *cur_off)
+		nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1);
+
+	sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
+
+	/* Fill OOB data in */
+	if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
+		u8 user_data[4];
+
+		memcpy(user_data, oob, 4);
+		sunxi_nfc_randomize_bbm(mtd, page, user_data);
+		writel(sunxi_nfc_buf_to_user_data(user_data),
+		       nfc->regs + NFC_REG_USER_DATA(0));
+	} else {
+		writel(sunxi_nfc_buf_to_user_data(oob),
+		       nfc->regs + NFC_REG_USER_DATA(0));
+	}
+
+	if (data_off + ecc->size != oob_off)
+		nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
+
+	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+	if (ret)
+		return ret;
+
+	sunxi_nfc_randomizer_enable(mtd);
+	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+	       NFC_ACCESS_DIR | NFC_ECC_OP,
+	       nfc->regs + NFC_REG_CMD);
+
+	ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+	sunxi_nfc_randomizer_disable(mtd);
+	if (ret)
+		return ret;
+
+	*cur_off = oob_off + ecc->bytes + 4;
+
+	return 0;
+}
+
+static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
+					     u8 *oob, int *cur_off,
+					     int page)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct nand_ecc_ctrl *ecc = &nand->ecc;
+	int offset = ((ecc->bytes + 4) * ecc->steps);
+	int len = mtd->oobsize - offset;
+
+	if (len <= 0)
+		return;
+
+	if (*cur_off != offset)
+		nand->cmdfunc(mtd, NAND_CMD_RNDIN,
+			      offset + mtd->writesize, -1);
+
+	sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page);
+
+	*cur_off = mtd->oobsize + mtd->writesize;
+}
+
+static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
+				      struct nand_chip *chip, uint8_t *buf,
+				      int oob_required, int page)
+{
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	unsigned int max_bitflips = 0;
+	int ret, i, cur_off = 0;
+	bool raw_mode = false;
+
+	sunxi_nfc_hw_ecc_enable(mtd);
+
+	for (i = 0; i < ecc->steps; i++) {
+		int data_off = i * ecc->size;
+		int oob_off = i * (ecc->bytes + 4);
+		u8 *data = buf + data_off;
+		u8 *oob = chip->oob_poi + oob_off;
+
+		ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
+						  oob_off + mtd->writesize,
+						  &cur_off, &max_bitflips,
+						  !i, page);
+		if (ret < 0)
+			return ret;
+		else if (ret)
+			raw_mode = true;
+	}
+
+	if (oob_required)
+		sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
+						!raw_mode, page);
+
+	sunxi_nfc_hw_ecc_disable(mtd);
+
+	return max_bitflips;
+}
+
+static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
+					 struct nand_chip *chip,
+					 uint32_t data_offs, uint32_t readlen,
+					 uint8_t *bufpoi, int page)
+{
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	int ret, i, cur_off = 0;
+	unsigned int max_bitflips = 0;
+
+	sunxi_nfc_hw_ecc_enable(mtd);
+
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+	for (i = data_offs / ecc->size;
+	     i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
+		int data_off = i * ecc->size;
+		int oob_off = i * (ecc->bytes + 4);
+		u8 *data = bufpoi + data_off;
+		u8 *oob = chip->oob_poi + oob_off;
+
+		ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off,
+			oob, oob_off + mtd->writesize,
+			&cur_off, &max_bitflips, !i, page);
+		if (ret < 0)
+			return ret;
+	}
+
+	sunxi_nfc_hw_ecc_disable(mtd);
+
+	return max_bitflips;
+}
+
+static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
+				       struct nand_chip *chip,
+				       const uint8_t *buf, int oob_required,
+				       int page)
+{
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	int ret, i, cur_off = 0;
+
+	sunxi_nfc_hw_ecc_enable(mtd);
+
+	for (i = 0; i < ecc->steps; i++) {
+		int data_off = i * ecc->size;
+		int oob_off = i * (ecc->bytes + 4);
+		const u8 *data = buf + data_off;
+		const u8 *oob = chip->oob_poi + oob_off;
+
+		ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
+						   oob_off + mtd->writesize,
+						   &cur_off, !i, page);
+		if (ret)
+			return ret;
+	}
+
+	if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
+		sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
+						 &cur_off, page);
+
+	sunxi_nfc_hw_ecc_disable(mtd);
+
+	return 0;
+}
+
+static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,
+					  struct nand_chip *chip,
+					  u32 data_offs, u32 data_len,
+					  const u8 *buf, int oob_required,
+					  int page)
+{
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	int ret, i, cur_off = 0;
+
+	sunxi_nfc_hw_ecc_enable(mtd);
+
+	for (i = data_offs / ecc->size;
+	     i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
+		int data_off = i * ecc->size;
+		int oob_off = i * (ecc->bytes + 4);
+		const u8 *data = buf + data_off;
+		const u8 *oob = chip->oob_poi + oob_off;
+
+		ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
+						   oob_off + mtd->writesize,
+						   &cur_off, !i, page);
+		if (ret)
+			return ret;
+	}
+
+	sunxi_nfc_hw_ecc_disable(mtd);
+
+	return 0;
+}
+
+static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
+					       struct nand_chip *chip,
+					       uint8_t *buf, int oob_required,
+					       int page)
+{
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	unsigned int max_bitflips = 0;
+	int ret, i, cur_off = 0;
+	bool raw_mode = false;
+
+	sunxi_nfc_hw_ecc_enable(mtd);
+
+	for (i = 0; i < ecc->steps; i++) {
+		int data_off = i * (ecc->size + ecc->bytes + 4);
+		int oob_off = data_off + ecc->size;
+		u8 *data = buf + (i * ecc->size);
+		u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
+
+		ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
+						  oob_off, &cur_off,
+						  &max_bitflips, !i, page);
+		if (ret < 0)
+			return ret;
+		else if (ret)
+			raw_mode = true;
+	}
+
+	if (oob_required)
+		sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
+						!raw_mode, page);
+
+	sunxi_nfc_hw_ecc_disable(mtd);
+
+	return max_bitflips;
+}
+
+static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
+						struct nand_chip *chip,
+						const uint8_t *buf,
+						int oob_required, int page)
+{
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	int ret, i, cur_off = 0;
+
+	sunxi_nfc_hw_ecc_enable(mtd);
+
+	for (i = 0; i < ecc->steps; i++) {
+		int data_off = i * (ecc->size + ecc->bytes + 4);
+		int oob_off = data_off + ecc->size;
+		const u8 *data = buf + (i * ecc->size);
+		const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
+
+		ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,
+						   oob, oob_off, &cur_off,
+						   false, page);
+		if (ret)
+			return ret;
+	}
+
+	if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
+		sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
+						 &cur_off, page);
+
+	sunxi_nfc_hw_ecc_disable(mtd);
+
+	return 0;
+}
+
+static const s32 tWB_lut[] = {6, 12, 16, 20};
+static const s32 tRHW_lut[] = {4, 8, 12, 20};
+
+static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
+		u32 clk_period)
+{
+	u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
+	int i;
+
+	for (i = 0; i < lut_size; i++) {
+		if (clk_cycles <= lut[i])
+			return i;
+	}
+
+	/* Doesn't fit */
+	return -EINVAL;
+}
+
+#define sunxi_nand_lookup_timing(l, p, c) \
+			_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
+
+static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
+				       const struct nand_sdr_timings *timings)
+{
+	u32 min_clk_period = 0;
+	s32 tWB, tADL, tWHR, tRHW, tCAD;
+
+	/* T1 <=> tCLS */
+	if (timings->tCLS_min > min_clk_period)
+		min_clk_period = timings->tCLS_min;
+
+	/* T2 <=> tCLH */
+	if (timings->tCLH_min > min_clk_period)
+		min_clk_period = timings->tCLH_min;
+
+	/* T3 <=> tCS */
+	if (timings->tCS_min > min_clk_period)
+		min_clk_period = timings->tCS_min;
+
+	/* T4 <=> tCH */
+	if (timings->tCH_min > min_clk_period)
+		min_clk_period = timings->tCH_min;
+
+	/* T5 <=> tWP */
+	if (timings->tWP_min > min_clk_period)
+		min_clk_period = timings->tWP_min;
+
+	/* T6 <=> tWH */
+	if (timings->tWH_min > min_clk_period)
+		min_clk_period = timings->tWH_min;
+
+	/* T7 <=> tALS */
+	if (timings->tALS_min > min_clk_period)
+		min_clk_period = timings->tALS_min;
+
+	/* T8 <=> tDS */
+	if (timings->tDS_min > min_clk_period)
+		min_clk_period = timings->tDS_min;
+
+	/* T9 <=> tDH */
+	if (timings->tDH_min > min_clk_period)
+		min_clk_period = timings->tDH_min;
+
+	/* T10 <=> tRR */
+	if (timings->tRR_min > (min_clk_period * 3))
+		min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
+
+	/* T11 <=> tALH */
+	if (timings->tALH_min > min_clk_period)
+		min_clk_period = timings->tALH_min;
+
+	/* T12 <=> tRP */
+	if (timings->tRP_min > min_clk_period)
+		min_clk_period = timings->tRP_min;
+
+	/* T13 <=> tREH */
+	if (timings->tREH_min > min_clk_period)
+		min_clk_period = timings->tREH_min;
+
+	/* T14 <=> tRC */
+	if (timings->tRC_min > (min_clk_period * 2))
+		min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
+
+	/* T15 <=> tWC */
+	if (timings->tWC_min > (min_clk_period * 2))
+		min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
+
+	/* T16 - T19 + tCAD */
+	tWB  = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
+					min_clk_period);
+	if (tWB < 0) {
+		dev_err(nfc->dev, "unsupported tWB\n");
+		return tWB;
+	}
+
+	tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
+	if (tADL > 3) {
+		dev_err(nfc->dev, "unsupported tADL\n");
+		return -EINVAL;
+	}
+
+	tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
+	if (tWHR > 3) {
+		dev_err(nfc->dev, "unsupported tWHR\n");
+		return -EINVAL;
+	}
+
+	tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
+					min_clk_period);
+	if (tRHW < 0) {
+		dev_err(nfc->dev, "unsupported tRHW\n");
+		return tRHW;
+	}
+
+	/*
+	 * TODO: according to ONFI specs this value only applies for DDR NAND,
+	 * but Allwinner seems to set this to 0x7. Mimic them for now.
+	 */
+	tCAD = 0x7;
+
+	/* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
+	chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
+
+	/*
+	 * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
+	 * output cycle timings shall be used if the host drives tRC less than
+	 * 30 ns.
+	 */
+	chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0;
+
+	/* Convert min_clk_period from picoseconds to nanoseconds */
+	min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
+
+	/*
+	 * Convert min_clk_period into a clk frequency, then get the
+	 * appropriate rate for the NAND controller IP given this formula
+	 * (specified in the datasheet):
+	 * nand clk_rate = min_clk_rate
+	 */
+	chip->clk_rate = 1000000000L / min_clk_period;
+
+	return 0;
+}
+
+static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip)
+{
+	struct mtd_info *mtd = nand_to_mtd(&chip->nand);
+	const struct nand_sdr_timings *timings;
+	int ret;
+	int mode;
+
+	mode = onfi_get_async_timing_mode(&chip->nand);
+	if (mode == ONFI_TIMING_MODE_UNKNOWN) {
+		mode = chip->nand.onfi_timing_mode_default;
+	} else {
+		uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
+		int i;
+
+		mode = fls(mode) - 1;
+		if (mode < 0)
+			mode = 0;
+
+		feature[0] = mode;
+		for (i = 0; i < chip->nsels; i++) {
+			chip->nand.select_chip(mtd, i);
+			ret = chip->nand.onfi_set_features(mtd,
+						&chip->nand,
+						ONFI_FEATURE_ADDR_TIMING_MODE,
+						feature);
+			chip->nand.select_chip(mtd, -1);
+			if (ret)
+				return ret;
+		}
+	}
+
+	timings = onfi_async_timing_mode_to_sdr_timings(mode);
+	if (IS_ERR(timings))
+		return PTR_ERR(timings);
+
+	return sunxi_nand_chip_set_timings(chip, timings);
+}
+
+static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
+					      struct nand_ecc_ctrl *ecc)
+{
+	static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
+	struct sunxi_nand_hw_ecc *data;
+	struct nand_ecclayout *layout;
+	int nsectors;
+	int ret;
+	int i;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (ecc->size != 512 && ecc->size != 1024)
+		return -EINVAL;
+
+	/* Prefer 1k ECC chunk over 512 ones */
+	if (ecc->size == 512 && mtd->writesize > 512) {
+		ecc->size = 1024;
+		ecc->strength *= 2;
+	}
+
+	/* Add ECC info retrieval from DT */
+	for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+		if (ecc->strength <= strengths[i])
+			break;
+	}
+
+	if (i >= ARRAY_SIZE(strengths)) {
+		dev_err(nfc->dev, "unsupported strength\n");
+		ret = -ENOTSUPP;
+		goto err;
+	}
+
+	data->mode = i;
+
+	/* HW ECC always request ECC bytes for 1024 bytes blocks */
+	ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
+
+	/* HW ECC always work with even numbers of ECC bytes */
+	ecc->bytes = ALIGN(ecc->bytes, 2);
+
+	layout = &data->layout;
+	nsectors = mtd->writesize / ecc->size;
+
+	if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	layout->eccbytes = (ecc->bytes * nsectors);
+
+	ecc->layout = layout;
+	ecc->priv = data;
+
+	return 0;
+
+err:
+	kfree(data);
+
+	return ret;
+}
+
+#ifndef __UBOOT__
+static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
+{
+	kfree(ecc->priv);
+}
+#endif /* __UBOOT__ */
+
+static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
+				       struct nand_ecc_ctrl *ecc)
+{
+	struct nand_ecclayout *layout;
+	int nsectors;
+	int i, j;
+	int ret;
+
+	ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc);
+	if (ret)
+		return ret;
+
+	ecc->read_page = sunxi_nfc_hw_ecc_read_page;
+	ecc->write_page = sunxi_nfc_hw_ecc_write_page;
+	ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
+	ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
+	layout = ecc->layout;
+	nsectors = mtd->writesize / ecc->size;
+
+	for (i = 0; i < nsectors; i++) {
+		if (i) {
+			layout->oobfree[i].offset =
+				layout->oobfree[i - 1].offset +
+				layout->oobfree[i - 1].length +
+				ecc->bytes;
+			layout->oobfree[i].length = 4;
+		} else {
+			/*
+			 * The first 2 bytes are used for BB markers, hence we
+			 * only have 2 bytes available in the first user data
+			 * section.
+			 */
+			layout->oobfree[i].length = 2;
+			layout->oobfree[i].offset = 2;
+		}
+
+		for (j = 0; j < ecc->bytes; j++)
+			layout->eccpos[(ecc->bytes * i) + j] =
+					layout->oobfree[i].offset +
+					layout->oobfree[i].length + j;
+	}
+
+	if (mtd->oobsize > (ecc->bytes + 4) * nsectors) {
+		layout->oobfree[nsectors].offset =
+				layout->oobfree[nsectors - 1].offset +
+				layout->oobfree[nsectors - 1].length +
+				ecc->bytes;
+		layout->oobfree[nsectors].length = mtd->oobsize -
+				((ecc->bytes + 4) * nsectors);
+	}
+
+	return 0;
+}
+
+static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
+						struct nand_ecc_ctrl *ecc)
+{
+	struct nand_ecclayout *layout;
+	int nsectors;
+	int i;
+	int ret;
+
+	ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc);
+	if (ret)
+		return ret;
+
+	ecc->prepad = 4;
+	ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
+	ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
+
+	layout = ecc->layout;
+	nsectors = mtd->writesize / ecc->size;
+
+	for (i = 0; i < (ecc->bytes * nsectors); i++)
+		layout->eccpos[i] = i;
+
+	layout->oobfree[0].length = mtd->oobsize - i;
+	layout->oobfree[0].offset = i;
+
+	return 0;
+}
+
+#ifndef __UBOOT__
+static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
+{
+	switch (ecc->mode) {
+	case NAND_ECC_HW:
+	case NAND_ECC_HW_SYNDROME:
+		sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc);
+		break;
+	case NAND_ECC_NONE:
+		kfree(ecc->layout);
+	default:
+		break;
+	}
+}
+#endif /* __UBOOT__ */
+
+static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	int ret;
+
+	if (!ecc->size) {
+		ecc->size = nand->ecc_step_ds;
+		ecc->strength = nand->ecc_strength_ds;
+	}
+
+	if (!ecc->size || !ecc->strength)
+		return -EINVAL;
+
+	switch (ecc->mode) {
+	case NAND_ECC_SOFT_BCH:
+		break;
+	case NAND_ECC_HW:
+		ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc);
+		if (ret)
+			return ret;
+		break;
+	case NAND_ECC_HW_SYNDROME:
+		ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc);
+		if (ret)
+			return ret;
+		break;
+	case NAND_ECC_NONE:
+		ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL);
+		if (!ecc->layout)
+			return -ENOMEM;
+		ecc->layout->oobfree[0].length = mtd->oobsize;
+	case NAND_ECC_SOFT:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
+{
+	const struct nand_sdr_timings *timings;
+	const void *blob = gd->fdt_blob;
+	struct sunxi_nand_chip *chip;
+	struct mtd_info *mtd;
+	struct nand_chip *nand;
+	int nsels;
+	int ret;
+	int i;
+	u32 cs[8], rb[8];
+
+	if (!fdt_getprop(blob, node, "reg", &nsels))
+		return -EINVAL;
+
+	nsels /= sizeof(u32);
+	if (!nsels || nsels > 8) {
+		dev_err(dev, "invalid reg property size\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(*chip) +
+		       (nsels * sizeof(struct sunxi_nand_chip_sel)),
+		       GFP_KERNEL);
+	if (!chip) {
+		dev_err(dev, "could not allocate chip\n");
+		return -ENOMEM;
+	}
+
+	chip->nsels = nsels;
+	chip->selected = -1;
+
+	for (i = 0; i < nsels; i++) {
+		cs[i] = -1;
+		rb[i] = -1;
+	}
+
+	ret = fdtdec_get_int_array(gd->fdt_blob, node, "reg", cs, nsels);
+	if (ret) {
+		dev_err(dev, "could not retrieve reg property: %d\n", ret);
+		return ret;
+	}
+
+	ret = fdtdec_get_int_array(gd->fdt_blob, node, "allwinner,rb", rb,
+				   nsels);
+	if (ret) {
+		dev_err(dev, "could not retrieve reg property: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0; i < nsels; i++) {
+		int tmp = cs[i];
+
+		if (tmp > NFC_MAX_CS) {
+			dev_err(dev,
+				"invalid reg value: %u (max CS = 7)\n",
+				tmp);
+			return -EINVAL;
+		}
+
+		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
+			dev_err(dev, "CS %d already assigned\n", tmp);
+			return -EINVAL;
+		}
+
+		chip->sels[i].cs = tmp;
+
+		tmp = rb[i];
+		if (tmp >= 0 && tmp < 2) {
+			chip->sels[i].rb.type = RB_NATIVE;
+			chip->sels[i].rb.info.nativeid = tmp;
+		} else {
+			ret = gpio_request_by_name_nodev(blob, node,
+						"rb-gpios", i,
+						&chip->sels[i].rb.info.gpio,
+						GPIOD_IS_IN);
+			if (ret)
+				chip->sels[i].rb.type = RB_GPIO;
+			else
+				chip->sels[i].rb.type = RB_NONE;
+		}
+	}
+
+	timings = onfi_async_timing_mode_to_sdr_timings(0);
+	if (IS_ERR(timings)) {
+		ret = PTR_ERR(timings);
+		dev_err(dev,
+			"could not retrieve timings for ONFI mode 0: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = sunxi_nand_chip_set_timings(chip, timings);
+	if (ret) {
+		dev_err(dev, "could not configure chip timings: %d\n", ret);
+		return ret;
+	}
+
+	nand = &chip->nand;
+	/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
+	nand->chip_delay = 200;
+	nand->controller = &nfc->controller;
+	/*
+	 * Set the ECC mode to the default value in case nothing is specified
+	 * in the DT.
+	 */
+	nand->ecc.mode = NAND_ECC_HW;
+	nand->flash_node = node;
+	nand->select_chip = sunxi_nfc_select_chip;
+	nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
+	nand->read_buf = sunxi_nfc_read_buf;
+	nand->write_buf = sunxi_nfc_write_buf;
+	nand->read_byte = sunxi_nfc_read_byte;
+
+	mtd = nand_to_mtd(nand);
+	ret = nand_scan_ident(mtd, nsels, NULL);
+	if (ret)
+		return ret;
+
+	if (nand->bbt_options & NAND_BBT_USE_FLASH)
+		nand->bbt_options |= NAND_BBT_NO_OOB;
+
+	if (nand->options & NAND_NEED_SCRAMBLING)
+		nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+	nand->options |= NAND_SUBPAGE_READ;
+
+	ret = sunxi_nand_chip_init_timings(chip);
+	if (ret) {
+		dev_err(dev, "could not configure chip timings: %d\n", ret);
+		return ret;
+	}
+
+	ret = sunxi_nand_ecc_init(mtd, &nand->ecc);
+	if (ret) {
+		dev_err(dev, "ECC init failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = nand_scan_tail(mtd);
+	if (ret) {
+		dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = nand_register(devnum, mtd);
+	if (ret) {
+		dev_err(dev, "failed to register mtd device: %d\n", ret);
+		return ret;
+	}
+
+	list_add_tail(&chip->node, &nfc->chips);
+
+	return 0;
+}
+
+static int sunxi_nand_chips_init(int node, struct sunxi_nfc *nfc)
+{
+	const void *blob = gd->fdt_blob;
+	int nand_node;
+	int ret, i = 0;
+
+	for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0;
+	     nand_node = fdt_next_subnode(blob, nand_node))
+		i++;
+
+	if (i > 8) {
+		dev_err(dev, "too many NAND chips: %d (max = 8)\n", i);
+		return -EINVAL;
+	}
+
+	i = 0;
+	for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0;
+	     nand_node = fdt_next_subnode(blob, nand_node)) {
+		ret = sunxi_nand_chip_init(nand_node, nfc, i++);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+#ifndef __UBOOT__
+static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
+{
+	struct sunxi_nand_chip *chip;
+
+	while (!list_empty(&nfc->chips)) {
+		chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip,
+					node);
+		nand_release(&chip->mtd);
+		sunxi_nand_ecc_cleanup(&chip->nand.ecc);
+		list_del(&chip->node);
+		kfree(chip);
+	}
+}
+#endif /* __UBOOT__ */
+
+void sunxi_nand_init(void)
+{
+	const void *blob = gd->fdt_blob;
+	struct sunxi_nfc *nfc;
+	fdt_addr_t regs;
+	int node;
+	int ret;
+
+	nfc = kzalloc(sizeof(*nfc), GFP_KERNEL);
+	if (!nfc)
+		return;
+
+	spin_lock_init(&nfc->controller.lock);
+	init_waitqueue_head(&nfc->controller.wq);
+	INIT_LIST_HEAD(&nfc->chips);
+
+	node = fdtdec_next_compatible(blob, 0, COMPAT_SUNXI_NAND);
+	if (node < 0) {
+		pr_err("unable to find nfc node in device tree\n");
+		goto err;
+	}
+
+	if (!fdtdec_get_is_enabled(blob, node)) {
+		pr_err("nfc disabled in device tree\n");
+		goto err;
+	}
+
+	regs = fdtdec_get_addr(blob, node, "reg");
+	if (regs == FDT_ADDR_T_NONE) {
+		pr_err("unable to find nfc address in device tree\n");
+		goto err;
+	}
+
+	nfc->regs = (void *)regs;
+
+	ret = sunxi_nfc_rst(nfc);
+	if (ret)
+		goto err;
+
+	ret = sunxi_nand_chips_init(node, nfc);
+	if (ret) {
+		dev_err(dev, "failed to init nand chips\n");
+		goto err;
+	}
+
+	return;
+
+err:
+	kfree(nfc);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Boris BREZILLON");
+MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 1785e3b..2972dba 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -123,16 +123,7 @@
 	  both the GPIO definitions and pin control functions for each
 	  available multiplex function.
 
-config ROCKCHIP_PINCTRL
-	bool "Rockchip pin control driver"
-	depends on DM
-	help
-	  Support pin multiplexing control on Rockchip 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_3036_PINCTRL
+config ROCKCHIP_RK3036_PINCTRL
 	bool "Rockchip rk3036 pin control driver"
 	depends on DM
 	help
@@ -141,6 +132,15 @@
 	  definitions and pin control functions for each available multiplex
 	  function.
 
+config ROCKCHIP_RK3288_PINCTRL
+	bool "Rockchip pin control driver"
+	depends on DM
+	help
+	  Support pin multiplexing control on Rockchip rk3288 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 PINCTRL_SANDBOX
 	bool "Sandbox pinctrl driver"
 	depends on SANDBOX
diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile
index 6fa7d00..64e9587 100644
--- a/drivers/pinctrl/rockchip/Makefile
+++ b/drivers/pinctrl/rockchip/Makefile
@@ -5,5 +5,5 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
-obj-$(CONFIG_ROCKCHIP_PINCTRL) += pinctrl_rk3288.o
-obj-$(CONFIG_ROCKCHIP_3036_PINCTRL) += pinctrl_rk3036.o
+obj-$(CONFIG_ROCKCHIP_RK3036_PINCTRL) += pinctrl_rk3036.o
+obj-$(CONFIG_ROCKCHIP_RK3288_PINCTRL) += pinctrl_rk3288.o
diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c
index a23278d..029927f 100644
--- a/drivers/usb/gadget/dwc2_udc_otg.c
+++ b/drivers/usb/gadget/dwc2_udc_otg.c
@@ -403,6 +403,7 @@
 	int i;
 	unsigned int uTemp = writel(CORE_SOFT_RESET, &reg->grstctl);
 	uint32_t dflt_gusbcfg;
+	uint32_t rx_fifo_sz, tx_fifo_sz, np_tx_fifo_sz;
 
 	debug("Reseting OTG controller\n");
 
@@ -467,18 +468,27 @@
 	/* 10. Unmask device IN EP common interrupts*/
 	writel(DIEPMSK_INIT, &reg->diepmsk);
 
+	rx_fifo_sz = RX_FIFO_SIZE;
+	np_tx_fifo_sz = NPTX_FIFO_SIZE;
+	tx_fifo_sz = PTX_FIFO_SIZE;
+
+	if (dev->pdata->rx_fifo_sz)
+		rx_fifo_sz = dev->pdata->rx_fifo_sz;
+	if (dev->pdata->np_tx_fifo_sz)
+		np_tx_fifo_sz = dev->pdata->np_tx_fifo_sz;
+	if (dev->pdata->tx_fifo_sz)
+		tx_fifo_sz = dev->pdata->tx_fifo_sz;
+
 	/* 11. Set Rx FIFO Size (in 32-bit words) */
-	writel(RX_FIFO_SIZE >> 2, &reg->grxfsiz);
+	writel(rx_fifo_sz, &reg->grxfsiz);
 
 	/* 12. Set Non Periodic Tx FIFO Size */
-	writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0,
+	writel((np_tx_fifo_sz << 16) | rx_fifo_sz,
 	       &reg->gnptxfsiz);
 
 	for (i = 1; i < DWC2_MAX_HW_ENDPOINTS; i++)
-		writel((PTX_FIFO_SIZE >> 2) << 16 |
-		       ((RX_FIFO_SIZE + NPTX_FIFO_SIZE +
-			 PTX_FIFO_SIZE*(i-1)) >> 2) << 0,
-		       &reg->dieptxf[i-1]);
+		writel((rx_fifo_sz + np_tx_fifo_sz + tx_fifo_sz*(i-1)) |
+			tx_fifo_sz << 16, &reg->dieptxf[i-1]);
 
 	/* Flush the RX FIFO */
 	writel(RX_FIFO_FLUSH, &reg->grstctl);
diff --git a/drivers/usb/gadget/dwc2_udc_otg_regs.h b/drivers/usb/gadget/dwc2_udc_otg_regs.h
index 78ec90e..c94396a 100644
--- a/drivers/usb/gadget/dwc2_udc_otg_regs.h
+++ b/drivers/usb/gadget/dwc2_udc_otg_regs.h
@@ -130,9 +130,9 @@
 #define HIGH_SPEED_CONTROL_PKT_SIZE	64
 #define HIGH_SPEED_BULK_PKT_SIZE	512
 
-#define RX_FIFO_SIZE			(1024*4)
-#define NPTX_FIFO_SIZE			(1024*4)
-#define PTX_FIFO_SIZE			(1536*1)
+#define RX_FIFO_SIZE			(1024)
+#define NPTX_FIFO_SIZE			(1024)
+#define PTX_FIFO_SIZE			(384)
 
 #define DEPCTL_TXFNUM_0		(0x0<<22)
 #define DEPCTL_TXFNUM_1		(0x1<<22)
diff --git a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
index 12f5c85..0d6d2fb 100644
--- a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
+++ b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
@@ -110,6 +110,9 @@
 
 	ctrl =  readl(&reg->out_endp[ep_num].doepctl);
 
+	invalidate_dcache_range((unsigned long) ep->dma_buf,
+				(unsigned long) ep->dma_buf + ep->len);
+
 	writel((unsigned int) ep->dma_buf, &reg->out_endp[ep_num].doepdma);
 	writel(DOEPT_SIZ_PKT_CNT(pktcnt) | DOEPT_SIZ_XFER_SIZE(length),
 	       &reg->out_endp[ep_num].doeptsiz);
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 93d147e..4e548c2 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -7,3 +7,4 @@
 
 obj-$(CONFIG_TWL4030_USB) += twl4030.o
 obj-$(CONFIG_OMAP_USB_PHY) += omap_usb_phy.o
+obj-$(CONFIG_ROCKCHIP_USB2_PHY) += rockchip_usb2_phy.o
diff --git a/drivers/usb/phy/rockchip_usb2_phy.c b/drivers/usb/phy/rockchip_usb2_phy.c
new file mode 100644
index 0000000..1958478
--- /dev/null
+++ b/drivers/usb/phy/rockchip_usb2_phy.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <libfdt.h>
+
+#include "../gadget/dwc2_udc_otg_priv.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define BIT_WRITEABLE_SHIFT	16
+
+struct usb2phy_reg {
+	unsigned int offset;
+	unsigned int bitend;
+	unsigned int bitstart;
+	unsigned int disable;
+	unsigned int enable;
+};
+
+/**
+ * struct rockchip_usb2_phy_cfg: usb-phy port configuration
+ * @port_reset: usb otg per-port reset register
+ * @soft_con: software control usb otg register
+ * @suspend: phy suspend register
+ */
+struct rockchip_usb2_phy_cfg {
+	struct usb2phy_reg port_reset;
+	struct usb2phy_reg soft_con;
+	struct usb2phy_reg suspend;
+};
+
+struct rockchip_usb2_phy_dt_id {
+	char		compatible[128];
+	const void	*data;
+};
+
+static const struct rockchip_usb2_phy_cfg rk3288_pdata = {
+	.port_reset     = {0x00, 12, 12, 0, 1},
+	.soft_con       = {0x08, 2, 2, 0, 1},
+	.suspend	= {0x0c, 5, 0, 0x01, 0x2A},
+};
+
+static struct rockchip_usb2_phy_dt_id rockchip_usb2_phy_dt_ids[] = {
+	{ .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
+	{}
+};
+
+static void property_enable(struct dwc2_plat_otg_data *pdata,
+				  const struct usb2phy_reg *reg, bool en)
+{
+	unsigned int val, mask, tmp;
+
+	tmp = en ? reg->enable : reg->disable;
+	mask = GENMASK(reg->bitend, reg->bitstart);
+	val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+	writel(val, pdata->regs_phy + reg->offset);
+}
+
+
+void otg_phy_init(struct dwc2_udc *dev)
+{
+	struct dwc2_plat_otg_data *pdata = dev->pdata;
+	struct rockchip_usb2_phy_cfg *phy_cfg = NULL;
+	struct rockchip_usb2_phy_dt_id *of_id;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(rockchip_usb2_phy_dt_ids); i++) {
+		of_id = &rockchip_usb2_phy_dt_ids[i];
+		if (fdt_node_check_compatible(gd->fdt_blob, pdata->phy_of_node,
+					      of_id->compatible) == 0) {
+			phy_cfg = (struct rockchip_usb2_phy_cfg *)of_id->data;
+			break;
+		}
+	}
+	if (!phy_cfg) {
+		debug("Can't find device platform data\n");
+
+		hang();
+		return;
+	}
+	pdata->priv = phy_cfg;
+	/* disable software control */
+	property_enable(pdata, &phy_cfg->soft_con, false);
+
+	/* reset otg port */
+	property_enable(pdata, &phy_cfg->port_reset, true);
+	mdelay(1);
+	property_enable(pdata, &phy_cfg->port_reset, false);
+	udelay(1);
+}
+
+void otg_phy_off(struct dwc2_udc *dev)
+{
+	struct dwc2_plat_otg_data *pdata = dev->pdata;
+	struct rockchip_usb2_phy_cfg *phy_cfg = pdata->priv;
+
+	/* enable software control */
+	property_enable(pdata, &phy_cfg->soft_con, true);
+	/* enter suspend */
+	property_enable(pdata, &phy_cfg->suspend, true);
+}
diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c
index cc26f19..c6d88d9 100644
--- a/drivers/video/rockchip/rk_vop.c
+++ b/drivers/video/rockchip/rk_vop.c
@@ -238,7 +238,7 @@
 		return ret;
 	}
 
-	ret = uclass_get_device(UCLASS_CLK, 0, &dev_clk);
+	ret = rockchip_get_clk(&dev_clk);
 	if (!ret) {
 		clk.id = DCLK_VOP0 + remote_vop_id;
 		ret = clk_request(dev_clk, &clk);
diff --git a/include/configs/evb-rk3288.h b/include/configs/evb-rk3288.h
new file mode 100644
index 0000000..342557f
--- /dev/null
+++ b/include/configs/evb-rk3288.h
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#define ROCKCHIP_DEVICE_SETTINGS
+#include <configs/rk3288_common.h>
+
+#define CONFIG_SPL_MMC_SUPPORT
+
+#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 CONFIG_SYS_WHITE_ON_BLACK
+#define CONFIG_CONSOLE_SCROLL_LINES		10
+
+#endif
diff --git a/include/configs/evb_rk3399.h b/include/configs/evb_rk3399.h
new file mode 100644
index 0000000..047850a
--- /dev/null
+++ b/include/configs/evb_rk3399.h
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __EVB_RK3399_H
+#define __EVB_RK3399_H
+
+#include <configs/rk3399_common.h>
+
+#define CONFIG_ENV_IS_IN_MMC
+#define CONFIG_SYS_MMC_ENV_DEV 0
+/*
+ * 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/rk3288_common.h b/include/configs/rk3288_common.h
index 8adc26f..2a36c17 100644
--- a/include/configs/rk3288_common.h
+++ b/include/configs/rk3288_common.h
@@ -33,7 +33,12 @@
 #define CONFIG_SYS_NS16550_MEM32
 #define CONFIG_SPL_BOARD_INIT
 
+#ifdef CONFIG_ROCKCHIP_SPL_BACK_TO_BROM
+/* Bootrom will load u-boot binary to 0x0 once return from SPL */
+#define CONFIG_SYS_TEXT_BASE		0x00000000
+#else
 #define CONFIG_SYS_TEXT_BASE		0x00100000
+#endif
 #define CONFIG_SYS_INIT_SP_ADDR		0x00100000
 #define CONFIG_SYS_LOAD_ADDR		0x00800800
 #define CONFIG_SPL_STACK		0xff718000
@@ -79,6 +84,32 @@
 #define CONFIG_SPI
 #define CONFIG_SF_DEFAULT_SPEED 20000000
 
+/* 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
+
+/* fastboot  */
+#define CONFIG_CMD_FASTBOOT
+#define CONFIG_USB_FUNCTION_FASTBOOT
+#define CONFIG_FASTBOOT_FLASH
+#define CONFIG_FASTBOOT_FLASH_MMC_DEV	1	/* eMMC */
+/* stroe safely fastboot buffer data to the middle of bank */
+#define CONFIG_FASTBOOT_BUF_ADDR	(CONFIG_SYS_SDRAM_BASE \
+					+ SDRAM_BANK_SIZE / 2)
+#define CONFIG_FASTBOOT_BUF_SIZE	0x08000000
+
+#define CONFIG_USB_GADGET_DOWNLOAD
+#define CONFIG_G_DNL_MANUFACTURER	"Rockchip"
+#define CONFIG_G_DNL_VENDOR_NUM		0x2207
+#define CONFIG_G_DNL_PRODUCT_NUM	0x320a
+
+/* Enable gpt partition table */
+#define CONFIG_CMD_GPT
+#define CONFIG_EFI_PARTITION
+
 #ifndef CONFIG_SPL_BUILD
 #include <config_distro_defaults.h>
 
diff --git a/include/configs/rk3399_common.h b/include/configs/rk3399_common.h
new file mode 100644
index 0000000..6ce1aa7
--- /dev/null
+++ b/include/configs/rk3399_common.h
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __CONFIG_RK3399_COMMON_H
+#define __CONFIG_RK3399_COMMON_H
+
+#define CONFIG_SYS_CACHELINE_SIZE	64
+
+#define CONFIG_SYS_NO_FLASH
+#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_DISPLAY_BOARDINFO
+
+#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_MMC
+#define CONFIG_GENERIC_MMC
+#define CONFIG_SDHCI
+#define CONFIG_BOUNCE_BUFFER
+#define CONFIG_ROCKCHIP_SDHCI_MAX_FREQ	200000000
+
+#define CONFIG_FAT_WRITE
+
+/* RAW SD card / eMMC locations. */
+#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR	256
+#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
+#include <config_distro_defaults.h>
+
+#define ENV_MEM_LAYOUT_SETTINGS \
+	"scriptaddr=0x00000000\0" \
+	"pxefile_addr_r=0x00100000\0" \
+	"fdt_addr_r=0x01f00000\0" \
+	"kernel_addr_r=0x02000000\0" \
+	"ramdisk_addr_r=0x04000000\0"
+
+/* First try to boot from SD (index 0), then eMMC (index 1) */
+#define BOOT_TARGET_DEVICES(func) \
+	func(MMC, mmc, 0) \
+	func(MMC, mmc, 1)
+
+#include <config_distro_bootcmd.h>
+#define CONFIG_EXTRA_ENV_SETTINGS \
+	BOOTENV
+
+#endif
+
+#endif
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 4de89f8..94e024b 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -27,7 +27,10 @@
 
 #define CONFIG_SYS_STDIO_DEREGISTER
 
-/* Number of bits in a C 'long' on this architecture */
+/*
+ * Number of bits in a C 'long' on this architecture. Set this to 32 when
+ * building on a 32-bit machine.
+ */
 #define CONFIG_SANDBOX_BITS_PER_LONG	64
 
 #define CONFIG_LMB
@@ -82,7 +85,6 @@
 #define CONFIG_CMD_SF_TEST
 
 #define CONFIG_I2C_EDID
-#define CONFIG_I2C_EEPROM
 
 /* Memory things - we don't really want a memory test */
 #define CONFIG_SYS_LOAD_ADDR		0x00000000
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
index 6358901..b9aa62b 100644
--- a/include/configs/sunxi-common.h
+++ b/include/configs/sunxi-common.h
@@ -134,7 +134,10 @@
 #define CONFIG_SERIAL_TAG
 
 #ifdef CONFIG_NAND_SUNXI
+#define CONFIG_SYS_NAND_MAX_ECCPOS 1664
 #define CONFIG_SPL_NAND_SUPPORT 1
+#define CONFIG_SYS_NAND_ONFI_DETECTION
+#define CONFIG_SYS_MAX_NAND_DEVICE 8
 #endif
 
 #ifdef CONFIG_SPL_SPI_SUNXI
diff --git a/include/configs/ti_armv7_common.h b/include/configs/ti_armv7_common.h
index ba7cf15..9f947ee 100644
--- a/include/configs/ti_armv7_common.h
+++ b/include/configs/ti_armv7_common.h
@@ -108,9 +108,25 @@
 /* Timer information. */
 #define CONFIG_SYS_PTV			2	/* Divisor: 2^(PTV+1) => 8 */
 
+/*
+ * Disable DM_* for SPL build and can be re-enabled after adding
+ * DM support in SPL
+ */
+#ifdef CONFIG_SPL_BUILD
+#undef CONFIG_DM_I2C
+#endif
+
 /* I2C IP block */
 #define CONFIG_I2C
+#ifndef CONFIG_DM_I2C
 #define CONFIG_SYS_I2C
+#else
+/*
+ * Enable CONFIG_DM_I2C_COMPAT temporarily until all the i2c client
+ * devices are adopted to DM
+ */
+#define CONFIG_DM_I2C_COMPAT
+#endif
 
 /* MMC/SD IP block */
 #define CONFIG_MMC
diff --git a/include/dm/device.h b/include/dm/device.h
index c825d47..705849b 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -207,6 +207,10 @@
 #define U_BOOT_DRIVER(__name)						\
 	ll_entry_declare(struct driver, __name, driver)
 
+/* Get a pointer to a given driver */
+#define DM_GET_DRIVER(__name)						\
+	ll_entry_get(struct driver, __name, driver)
+
 /**
  * dev_get_platdata() - Get the platform data for a device
  *
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
index fd368b6..84f05bc 100644
--- a/include/dm/uclass.h
+++ b/include/dm/uclass.h
@@ -38,6 +38,7 @@
 	struct list_head sibling_node;
 };
 
+struct driver;
 struct udevice;
 
 /* Members of this uclass sequence themselves with aliases */
@@ -194,6 +195,23 @@
 				 const char *name, struct udevice **devp);
 
 /**
+ * uclass_get_device_by_driver() - Get a uclass device for a driver
+ *
+ * This searches the devices in the uclass for one that uses the given
+ * driver. Use DM_GET_DRIVER(name) for the @drv argument, where 'name' is
+ * the driver name - as used in U_BOOT_DRIVER(name).
+ *
+ * The device is probed to activate it ready for use.
+ *
+ * @id: ID to look up
+ * @drv: Driver to look for
+ * @devp: Returns pointer to the first device with that driver
+ * @return 0 if OK, -ve on error
+ */
+int uclass_get_device_by_driver(enum uclass_id id, const struct driver *drv,
+				struct udevice **devp);
+
+/**
  * uclass_first_device() - Get the first device in a uclass
  *
  * The device returned is probed if necessary, and ready for use
diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h
new file mode 100644
index 0000000..0a86aec
--- /dev/null
+++ b/include/dt-bindings/clock/rk3399-cru.h
@@ -0,0 +1,746 @@
+/*
+ * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RK3399_H
+#define _DT_BINDINGS_CLK_ROCKCHIP_RK3399_H
+
+/* core clocks */
+#define PLL_APLLL			1
+#define PLL_APLLB			2
+#define PLL_DPLL			3
+#define PLL_CPLL			4
+#define PLL_GPLL			5
+#define PLL_NPLL			6
+#define PLL_VPLL			7
+#define ARMCLKL				8
+#define ARMCLKB				9
+
+/* sclk gates (special clocks) */
+#define SCLK_I2C1			65
+#define SCLK_I2C2			66
+#define SCLK_I2C3			67
+#define SCLK_I2C5			68
+#define SCLK_I2C6			69
+#define SCLK_I2C7			70
+#define SCLK_SPI0			71
+#define SCLK_SPI1			72
+#define SCLK_SPI2			73
+#define SCLK_SPI4			74
+#define SCLK_SPI5			75
+#define SCLK_SDMMC			76
+#define SCLK_SDIO			77
+#define SCLK_EMMC			78
+#define SCLK_TSADC			79
+#define SCLK_SARADC			80
+#define SCLK_UART0			81
+#define SCLK_UART1			82
+#define SCLK_UART2			83
+#define SCLK_UART3			84
+#define SCLK_SPDIF_8CH			85
+#define SCLK_I2S0_8CH			86
+#define SCLK_I2S1_8CH			87
+#define SCLK_I2S2_8CH			88
+#define SCLK_I2S_8CH_OUT		89
+#define SCLK_TIMER00			90
+#define SCLK_TIMER01			91
+#define SCLK_TIMER02			92
+#define SCLK_TIMER03			93
+#define SCLK_TIMER04			94
+#define SCLK_TIMER05			95
+#define SCLK_TIMER06			96
+#define SCLK_TIMER07			97
+#define SCLK_TIMER08			98
+#define SCLK_TIMER09			99
+#define SCLK_TIMER10			100
+#define SCLK_TIMER11			101
+#define SCLK_MACREF			102
+#define SCLK_MAC_RX			103
+#define SCLK_MAC_TX			104
+#define SCLK_MAC			105
+#define SCLK_MACREF_OUT			106
+#define SCLK_VOP0_PWM			107
+#define SCLK_VOP1_PWM			108
+#define SCLK_RGA_CORE			109
+#define SCLK_ISP0			110
+#define SCLK_ISP1			111
+#define SCLK_HDMI_CEC			112
+#define SCLK_HDMI_SFR			113
+#define SCLK_DP_CORE			114
+#define SCLK_PVTM_CORE_L		115
+#define SCLK_PVTM_CORE_B		116
+#define SCLK_PVTM_GPU			117
+#define SCLK_PVTM_DDR			118
+#define SCLK_MIPIDPHY_REF		119
+#define SCLK_MIPIDPHY_CFG		120
+#define SCLK_HSICPHY			121
+#define SCLK_USBPHY480M			122
+#define SCLK_USB2PHY0_REF		123
+#define SCLK_USB2PHY1_REF		124
+#define SCLK_UPHY0_TCPDPHY_REF		125
+#define SCLK_UPHY0_TCPDCORE		126
+#define SCLK_UPHY1_TCPDPHY_REF		127
+#define SCLK_UPHY1_TCPDCORE		128
+#define SCLK_USB3OTG0_REF		129
+#define SCLK_USB3OTG1_REF		130
+#define SCLK_USB3OTG0_SUSPEND		131
+#define SCLK_USB3OTG1_SUSPEND		132
+#define SCLK_CRYPTO0			133
+#define SCLK_CRYPTO1			134
+#define SCLK_CCI_TRACE			135
+#define SCLK_CS				136
+#define SCLK_CIF_OUT			137
+#define SCLK_PCIEPHY_REF		138
+#define SCLK_PCIE_CORE			139
+#define SCLK_M0_PERILP			140
+#define SCLK_M0_PERILP_DEC		141
+#define SCLK_CM0S			142
+#define SCLK_DBG_NOC			143
+#define SCLK_DBG_PD_CORE_B		144
+#define SCLK_DBG_PD_CORE_L		145
+#define SCLK_DFIMON0_TIMER		146
+#define SCLK_DFIMON1_TIMER		147
+#define SCLK_INTMEM0			148
+#define SCLK_INTMEM1			149
+#define SCLK_INTMEM2			150
+#define SCLK_INTMEM3			151
+#define SCLK_INTMEM4			152
+#define SCLK_INTMEM5			153
+#define SCLK_SDMMC_DRV			154
+#define SCLK_SDMMC_SAMPLE		155
+#define SCLK_SDIO_DRV			156
+#define SCLK_SDIO_SAMPLE		157
+#define SCLK_VDU_CORE			158
+#define SCLK_VDU_CA			159
+#define SCLK_PCIE_PM			160
+#define SCLK_SPDIF_REC_DPTX		161
+#define SCLK_DPHY_PLL			162
+#define SCLK_DPHY_TX0_CFG		163
+#define SCLK_DPHY_TX1RX1_CFG		164
+#define SCLK_DPHY_RX0_CFG		165
+#define SCLK_RMII_SRC			166
+#define SCLK_PCIEPHY_REF100M		167
+
+#define DCLK_VOP0			180
+#define DCLK_VOP1			181
+#define DCLK_VOP0_DIV			182
+#define DCLK_VOP1_DIV			183
+#define DCLK_M0_PERILP			184
+
+#define FCLK_CM0S			190
+
+/* aclk gates */
+#define ACLK_PERIHP			192
+#define ACLK_PERIHP_NOC			193
+#define ACLK_PERILP0			194
+#define ACLK_PERILP0_NOC		195
+#define ACLK_PERF_PCIE			196
+#define ACLK_PCIE			197
+#define ACLK_INTMEM			198
+#define ACLK_TZMA			199
+#define ACLK_DCF			200
+#define ACLK_CCI			201
+#define ACLK_CCI_NOC0			202
+#define ACLK_CCI_NOC1			203
+#define ACLK_CCI_GRF			204
+#define ACLK_CENTER			205
+#define ACLK_CENTER_MAIN_NOC		206
+#define ACLK_CENTER_PERI_NOC		207
+#define ACLK_GPU			208
+#define ACLK_PERF_GPU			209
+#define ACLK_GPU_GRF			210
+#define ACLK_DMAC0_PERILP		211
+#define ACLK_DMAC1_PERILP		212
+#define ACLK_GMAC			213
+#define ACLK_GMAC_NOC			214
+#define ACLK_PERF_GMAC			215
+#define ACLK_VOP0_NOC			216
+#define ACLK_VOP0			217
+#define ACLK_VOP1_NOC			218
+#define ACLK_VOP1			219
+#define ACLK_RGA			220
+#define ACLK_RGA_NOC			221
+#define ACLK_HDCP			222
+#define ACLK_HDCP_NOC			223
+#define ACLK_HDCP22			224
+#define ACLK_IEP			225
+#define ACLK_IEP_NOC			226
+#define ACLK_VIO			227
+#define ACLK_VIO_NOC			228
+#define ACLK_ISP0			229
+#define ACLK_ISP1			230
+#define ACLK_ISP0_NOC			231
+#define ACLK_ISP1_NOC			232
+#define ACLK_ISP0_WRAPPER		233
+#define ACLK_ISP1_WRAPPER		234
+#define ACLK_VCODEC			235
+#define ACLK_VCODEC_NOC			236
+#define ACLK_VDU			237
+#define ACLK_VDU_NOC			238
+#define ACLK_PERI			239
+#define ACLK_EMMC			240
+#define ACLK_EMMC_CORE			241
+#define ACLK_EMMC_NOC			242
+#define ACLK_EMMC_GRF			243
+#define ACLK_USB3			244
+#define ACLK_USB3_NOC			245
+#define ACLK_USB3OTG0			246
+#define ACLK_USB3OTG1			247
+#define ACLK_USB3_RKSOC_AXI_PERF	248
+#define ACLK_USB3_GRF			249
+#define ACLK_GIC			250
+#define ACLK_GIC_NOC			251
+#define ACLK_GIC_ADB400_CORE_L_2_GIC	252
+#define ACLK_GIC_ADB400_CORE_B_2_GIC	253
+#define ACLK_GIC_ADB400_GIC_2_CORE_L	254
+#define ACLK_GIC_ADB400_GIC_2_CORE_B	255
+#define ACLK_CORE_ADB400_CORE_L_2_CCI500 256
+#define ACLK_CORE_ADB400_CORE_B_2_CCI500 257
+#define ACLK_ADB400M_PD_CORE_L		258
+#define ACLK_ADB400M_PD_CORE_B		259
+#define ACLK_PERF_CORE_L		260
+#define ACLK_PERF_CORE_B		261
+#define ACLK_GIC_PRE			262
+#define ACLK_VOP0_PRE			263
+#define ACLK_VOP1_PRE			264
+
+/* pclk gates */
+#define PCLK_PERIHP			320
+#define PCLK_PERIHP_NOC			321
+#define PCLK_PERILP0			322
+#define PCLK_PERILP1			323
+#define PCLK_PERILP1_NOC		324
+#define PCLK_PERILP_SGRF		325
+#define PCLK_PERIHP_GRF			326
+#define PCLK_PCIE			327
+#define PCLK_SGRF			328
+#define PCLK_INTR_ARB			329
+#define PCLK_CENTER_MAIN_NOC		330
+#define PCLK_CIC			331
+#define PCLK_COREDBG_B			332
+#define PCLK_COREDBG_L			333
+#define PCLK_DBG_CXCS_PD_CORE_B		334
+#define PCLK_DCF			335
+#define PCLK_GPIO2			336
+#define PCLK_GPIO3			337
+#define PCLK_GPIO4			338
+#define PCLK_GRF			339
+#define PCLK_HSICPHY			340
+#define PCLK_I2C1			341
+#define PCLK_I2C2			342
+#define PCLK_I2C3			343
+#define PCLK_I2C5			344
+#define PCLK_I2C6			345
+#define PCLK_I2C7			346
+#define PCLK_SPI0			347
+#define PCLK_SPI1			348
+#define PCLK_SPI2			349
+#define PCLK_SPI4			350
+#define PCLK_SPI5			351
+#define PCLK_UART0			352
+#define PCLK_UART1			353
+#define PCLK_UART2			354
+#define PCLK_UART3			355
+#define PCLK_TSADC			356
+#define PCLK_SARADC			357
+#define PCLK_GMAC			358
+#define PCLK_GMAC_NOC			359
+#define PCLK_TIMER0			360
+#define PCLK_TIMER1			361
+#define PCLK_EDP			362
+#define PCLK_EDP_NOC			363
+#define PCLK_EDP_CTRL			364
+#define PCLK_VIO			365
+#define PCLK_VIO_NOC			366
+#define PCLK_VIO_GRF			367
+#define PCLK_MIPI_DSI0			368
+#define PCLK_MIPI_DSI1			369
+#define PCLK_HDCP			370
+#define PCLK_HDCP_NOC			371
+#define PCLK_HDMI_CTRL			372
+#define PCLK_DP_CTRL			373
+#define PCLK_HDCP22			374
+#define PCLK_GASKET			375
+#define PCLK_DDR			376
+#define PCLK_DDR_MON			377
+#define PCLK_DDR_SGRF			378
+#define PCLK_ISP1_WRAPPER		379
+#define PCLK_WDT			380
+#define PCLK_EFUSE1024NS		381
+#define PCLK_EFUSE1024S			382
+#define PCLK_PMU_INTR_ARB		383
+#define PCLK_MAILBOX0			384
+#define PCLK_USBPHY_MUX_G		385
+#define PCLK_UPHY0_TCPHY_G		386
+#define PCLK_UPHY0_TCPD_G		387
+#define PCLK_UPHY1_TCPHY_G		388
+#define PCLK_UPHY1_TCPD_G		389
+#define PCLK_ALIVE			390
+
+/* hclk gates */
+#define HCLK_PERIHP			448
+#define HCLK_PERILP0			449
+#define HCLK_PERILP1			450
+#define HCLK_PERILP0_NOC		451
+#define HCLK_PERILP1_NOC		452
+#define HCLK_M0_PERILP			453
+#define HCLK_M0_PERILP_NOC		454
+#define HCLK_AHB1TOM			455
+#define HCLK_HOST0			456
+#define HCLK_HOST0_ARB			457
+#define HCLK_HOST1			458
+#define HCLK_HOST1_ARB			459
+#define HCLK_HSIC			460
+#define HCLK_SD				461
+#define HCLK_SDMMC			462
+#define HCLK_SDMMC_NOC			463
+#define HCLK_M_CRYPTO0			464
+#define HCLK_M_CRYPTO1			465
+#define HCLK_S_CRYPTO0			466
+#define HCLK_S_CRYPTO1			467
+#define HCLK_I2S0_8CH			468
+#define HCLK_I2S1_8CH			469
+#define HCLK_I2S2_8CH			470
+#define HCLK_SPDIF			471
+#define HCLK_VOP0_NOC			472
+#define HCLK_VOP0			473
+#define HCLK_VOP1_NOC			474
+#define HCLK_VOP1			475
+#define HCLK_ROM			476
+#define HCLK_IEP			477
+#define HCLK_IEP_NOC			478
+#define HCLK_ISP0			479
+#define HCLK_ISP1			480
+#define HCLK_ISP0_NOC			481
+#define HCLK_ISP1_NOC			482
+#define HCLK_ISP0_WRAPPER		483
+#define HCLK_ISP1_WRAPPER		484
+#define HCLK_RGA			485
+#define HCLK_RGA_NOC			486
+#define HCLK_HDCP			487
+#define HCLK_HDCP_NOC			488
+#define HCLK_HDCP22			489
+#define HCLK_VCODEC			490
+#define HCLK_VCODEC_NOC			491
+#define HCLK_VDU			492
+#define HCLK_VDU_NOC			493
+#define HCLK_SDIO			494
+#define HCLK_SDIO_NOC			495
+#define HCLK_SDIOAUDIO_NOC		496
+
+#define CLK_NR_CLKS			(HCLK_SDIOAUDIO_NOC + 1)
+
+/* pmu-clocks indices */
+
+#define PLL_PPLL			1
+
+#define SCLK_32K_SUSPEND_PMU		2
+#define SCLK_SPI3_PMU			3
+#define SCLK_TIMER12_PMU		4
+#define SCLK_TIMER13_PMU		5
+#define SCLK_UART4_PMU			6
+#define SCLK_PVTM_PMU			7
+#define SCLK_WIFI_PMU			8
+#define SCLK_I2C0_PMU			9
+#define SCLK_I2C4_PMU			10
+#define SCLK_I2C8_PMU			11
+
+#define PCLK_SRC_PMU			19
+#define PCLK_PMU			20
+#define PCLK_PMUGRF_PMU			21
+#define PCLK_INTMEM1_PMU		22
+#define PCLK_GPIO0_PMU			23
+#define PCLK_GPIO1_PMU			24
+#define PCLK_SGRF_PMU			25
+#define PCLK_NOC_PMU			26
+#define PCLK_I2C0_PMU			27
+#define PCLK_I2C4_PMU			28
+#define PCLK_I2C8_PMU			29
+#define PCLK_RKPWM_PMU			30
+#define PCLK_SPI3_PMU			31
+#define PCLK_TIMER_PMU			32
+#define PCLK_MAILBOX_PMU		33
+#define PCLK_UART4_PMU			34
+#define PCLK_WDT_M0_PMU			35
+
+#define FCLK_CM0S_SRC_PMU		44
+#define FCLK_CM0S_PMU			45
+#define SCLK_CM0S_PMU			46
+#define HCLK_CM0S_PMU			47
+#define DCLK_CM0S_PMU			48
+#define PCLK_INTR_ARB_PMU		49
+#define HCLK_NOC_PMU			50
+
+#define CLKPMU_NR_CLKS			(HCLK_NOC_PMU + 1)
+
+/* soft-reset indices */
+
+/* cru_softrst_con0 */
+#define SRST_CORE_L0			0
+#define SRST_CORE_B0			1
+#define SRST_CORE_PO_L0			2
+#define SRST_CORE_PO_B0			3
+#define SRST_L2_L			4
+#define SRST_L2_B			5
+#define SRST_ADB_L			6
+#define SRST_ADB_B			7
+#define SRST_A_CCI			8
+#define SRST_A_CCIM0_NOC		9
+#define SRST_A_CCIM1_NOC		10
+#define SRST_DBG_NOC			11
+
+/* cru_softrst_con1 */
+#define SRST_CORE_L0_T			16
+#define SRST_CORE_L1			17
+#define SRST_CORE_L2			18
+#define SRST_CORE_L3			19
+#define SRST_CORE_PO_L0_T		20
+#define SRST_CORE_PO_L1			21
+#define SRST_CORE_PO_L2			22
+#define SRST_CORE_PO_L3			23
+#define SRST_A_ADB400_GIC2COREL		24
+#define SRST_A_ADB400_COREL2GIC		25
+#define SRST_P_DBG_L			26
+#define SRST_L2_L_T			28
+#define SRST_ADB_L_T			29
+#define SRST_A_RKPERF_L			30
+#define SRST_PVTM_CORE_L		31
+
+/* cru_softrst_con2 */
+#define SRST_CORE_B0_T			32
+#define SRST_CORE_B1			33
+#define SRST_CORE_PO_B0_T		36
+#define SRST_CORE_PO_B1			37
+#define SRST_A_ADB400_GIC2COREB		40
+#define SRST_A_ADB400_COREB2GIC		41
+#define SRST_P_DBG_B			42
+#define SRST_L2_B_T			43
+#define SRST_ADB_B_T			45
+#define SRST_A_RKPERF_B			46
+#define SRST_PVTM_CORE_B		47
+
+/* cru_softrst_con3 */
+#define SRST_A_CCI_T			50
+#define SRST_A_CCIM0_NOC_T		51
+#define SRST_A_CCIM1_NOC_T		52
+#define SRST_A_ADB400M_PD_CORE_B_T	53
+#define SRST_A_ADB400M_PD_CORE_L_T	54
+#define SRST_DBG_NOC_T			55
+#define SRST_DBG_CXCS			56
+#define SRST_CCI_TRACE			57
+#define SRST_P_CCI_GRF			58
+
+/* cru_softrst_con4 */
+#define SRST_A_CENTER_MAIN_NOC		64
+#define SRST_A_CENTER_PERI_NOC		65
+#define SRST_P_CENTER_MAIN		66
+#define SRST_P_DDRMON			67
+#define SRST_P_CIC			68
+#define SRST_P_CENTER_SGRF		69
+#define SRST_DDR0_MSCH			70
+#define SRST_DDRCFG0_MSCH		71
+#define SRST_DDR0			72
+#define SRST_DDRPHY0			73
+#define SRST_DDR1_MSCH			74
+#define SRST_DDRCFG1_MSCH		75
+#define SRST_DDR1			76
+#define SRST_DDRPHY1			77
+#define SRST_DDR_CIC			78
+#define SRST_PVTM_DDR			79
+
+/* cru_softrst_con5 */
+#define SRST_A_VCODEC_NOC		80
+#define SRST_A_VCODEC			81
+#define SRST_H_VCODEC_NOC		82
+#define SRST_H_VCODEC			83
+#define SRST_A_VDU_NOC			88
+#define SRST_A_VDU			89
+#define SRST_H_VDU_NOC			90
+#define SRST_H_VDU			91
+#define SRST_VDU_CORE			92
+#define SRST_VDU_CA			93
+
+/* cru_softrst_con6 */
+#define SRST_A_IEP_NOC			96
+#define SRST_A_VOP_IEP			97
+#define SRST_A_IEP			98
+#define SRST_H_IEP_NOC			99
+#define SRST_H_IEP			100
+#define SRST_A_RGA_NOC			102
+#define SRST_A_RGA			103
+#define SRST_H_RGA_NOC			104
+#define SRST_H_RGA			105
+#define SRST_RGA_CORE			106
+#define SRST_EMMC_NOC			108
+#define SRST_EMMC			109
+#define SRST_EMMC_GRF			110
+
+/* cru_softrst_con7 */
+#define SRST_A_PERIHP_NOC		112
+#define SRST_P_PERIHP_GRF		113
+#define SRST_H_PERIHP_NOC		114
+#define SRST_USBHOST0			115
+#define SRST_HOSTC0_AUX			116
+#define SRST_HOST0_ARB			117
+#define SRST_USBHOST1			118
+#define SRST_HOSTC1_AUX			119
+#define SRST_HOST1_ARB			120
+#define SRST_SDIO0			121
+#define SRST_SDMMC			122
+#define SRST_HSIC			123
+#define SRST_HSIC_AUX			124
+#define SRST_AHB1TOM			125
+#define SRST_P_PERIHP_NOC		126
+#define SRST_HSICPHY			127
+
+/* cru_softrst_con8 */
+#define SRST_A_PCIE			128
+#define SRST_P_PCIE			129
+#define SRST_PCIE_CORE			130
+#define SRST_PCIE_MGMT			131
+#define SRST_PCIE_MGMT_STICKY		132
+#define SRST_PCIE_PIPE			133
+#define SRST_PCIE_PM			134
+#define SRST_PCIEPHY			135
+#define SRST_A_GMAC_NOC			136
+#define SRST_A_GMAC			137
+#define SRST_P_GMAC_NOC			138
+#define SRST_P_GMAC_GRF			140
+#define SRST_HSICPHY_POR		142
+#define SRST_HSICPHY_UTMI		143
+
+/* cru_softrst_con9 */
+#define SRST_USB2PHY0_POR		144
+#define SRST_USB2PHY0_UTMI_PORT0	145
+#define SRST_USB2PHY0_UTMI_PORT1	146
+#define SRST_USB2PHY0_EHCIPHY		147
+#define SRST_UPHY0_PIPE_L00		148
+#define SRST_UPHY0			149
+#define SRST_UPHY0_TCPDPWRUP		150
+#define SRST_USB2PHY1_POR		152
+#define SRST_USB2PHY1_UTMI_PORT0	153
+#define SRST_USB2PHY1_UTMI_PORT1	154
+#define SRST_USB2PHY1_EHCIPHY		155
+#define SRST_UPHY1_PIPE_L00		156
+#define SRST_UPHY1			157
+#define SRST_UPHY1_TCPDPWRUP		158
+
+/* cru_softrst_con10 */
+#define SRST_A_PERILP0_NOC		160
+#define SRST_A_DCF			161
+#define SRST_GIC500			162
+#define SRST_DMAC0_PERILP0		163
+#define SRST_DMAC1_PERILP0		164
+#define SRST_TZMA			165
+#define SRST_INTMEM			166
+#define SRST_ADB400_MST0		167
+#define SRST_ADB400_MST1		168
+#define SRST_ADB400_SLV0		169
+#define SRST_ADB400_SLV1		170
+#define SRST_H_PERILP0			171
+#define SRST_H_PERILP0_NOC		172
+#define SRST_ROM			173
+#define SRST_CRYPTO_S			174
+#define SRST_CRYPTO_M			175
+
+/* cru_softrst_con11 */
+#define SRST_P_DCF			176
+#define SRST_CM0S_NOC			177
+#define SRST_CM0S			178
+#define SRST_CM0S_DBG			179
+#define SRST_CM0S_PO			180
+#define SRST_CRYPTO			181
+#define SRST_P_PERILP1_SGRF		182
+#define SRST_P_PERILP1_GRF		183
+#define SRST_CRYPTO1_S			184
+#define SRST_CRYPTO1_M			185
+#define SRST_CRYPTO1			186
+#define SRST_GIC_NOC			188
+#define SRST_SD_NOC			189
+#define SRST_SDIOAUDIO_BRG		190
+
+/* cru_softrst_con12 */
+#define SRST_H_PERILP1			192
+#define SRST_H_PERILP1_NOC		193
+#define SRST_H_I2S0_8CH			194
+#define SRST_H_I2S1_8CH			195
+#define SRST_H_I2S2_8CH			196
+#define SRST_H_SPDIF_8CH		197
+#define SRST_P_PERILP1_NOC		198
+#define SRST_P_EFUSE_1024		199
+#define SRST_P_EFUSE_1024S		200
+#define SRST_P_I2C0			201
+#define SRST_P_I2C1			202
+#define SRST_P_I2C2			203
+#define SRST_P_I2C3			204
+#define SRST_P_I2C4			205
+#define SRST_P_I2C5			206
+#define SRST_P_MAILBOX0			207
+
+/* cru_softrst_con13 */
+#define SRST_P_UART0			208
+#define SRST_P_UART1			209
+#define SRST_P_UART2			210
+#define SRST_P_UART3			211
+#define SRST_P_SARADC			212
+#define SRST_P_TSADC			213
+#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_SPI0			219
+#define SRST_SPI1			220
+#define SRST_SPI2			221
+#define SRST_SPI3			222
+#define SRST_SPI4			223
+
+/* cru_softrst_con14 */
+#define SRST_I2S0_8CH			224
+#define SRST_I2S1_8CH			225
+#define SRST_I2S2_8CH			226
+#define SRST_SPDIF_8CH			227
+#define SRST_UART0			228
+#define SRST_UART1			229
+#define SRST_UART2			230
+#define SRST_UART3			231
+#define SRST_TSADC			232
+#define SRST_I2C0			233
+#define SRST_I2C1			234
+#define SRST_I2C2			235
+#define SRST_I2C3			236
+#define SRST_I2C4			237
+#define SRST_I2C5			238
+#define SRST_SDIOAUDIO_NOC		239
+
+/* cru_softrst_con15 */
+#define SRST_A_VIO_NOC			240
+#define SRST_A_HDCP_NOC			241
+#define SRST_A_HDCP			242
+#define SRST_H_HDCP_NOC			243
+#define SRST_H_HDCP			244
+#define SRST_P_HDCP_NOC			245
+#define SRST_P_HDCP			246
+#define SRST_P_HDMI_CTRL		247
+#define SRST_P_DP_CTRL			248
+#define SRST_S_DP_CTRL			249
+#define SRST_C_DP_CTRL			250
+#define SRST_P_MIPI_DSI0		251
+#define SRST_P_MIPI_DSI1		252
+#define SRST_DP_CORE			253
+#define SRST_DP_I2S			254
+
+/* cru_softrst_con16 */
+#define SRST_GASKET			256
+#define SRST_VIO_GRF			258
+#define SRST_DPTX_SPDIF_REC		259
+#define SRST_HDMI_CTRL			260
+#define SRST_HDCP_CTRL			261
+#define SRST_A_ISP0_NOC			262
+#define SRST_A_ISP1_NOC			263
+#define SRST_H_ISP0_NOC			266
+#define SRST_H_ISP1_NOC			267
+#define SRST_H_ISP0			268
+#define SRST_H_ISP1			269
+#define SRST_ISP0			270
+#define SRST_ISP1			271
+
+/* cru_softrst_con17 */
+#define SRST_A_VOP0_NOC			272
+#define SRST_A_VOP1_NOC			273
+#define SRST_A_VOP0			274
+#define SRST_A_VOP1			275
+#define SRST_H_VOP0_NOC			276
+#define SRST_H_VOP1_NOC			277
+#define SRST_H_VOP0			278
+#define SRST_H_VOP1			279
+#define SRST_D_VOP0			280
+#define SRST_D_VOP1			281
+#define SRST_VOP0_PWM			282
+#define SRST_VOP1_PWM			283
+#define SRST_P_EDP_NOC			284
+#define SRST_P_EDP_CTRL			285
+
+/* cru_softrst_con18 */
+#define SRST_A_GPU			288
+#define SRST_A_GPU_NOC			289
+#define SRST_A_GPU_GRF			290
+#define SRST_PVTM_GPU			291
+#define SRST_A_USB3_NOC			292
+#define SRST_A_USB3_OTG0		293
+#define SRST_A_USB3_OTG1		294
+#define SRST_A_USB3_GRF			295
+#define SRST_PMU			296
+
+/* cru_softrst_con19 */
+#define SRST_P_TIMER0_5			304
+#define SRST_TIMER0			305
+#define SRST_TIMER1			306
+#define SRST_TIMER2			307
+#define SRST_TIMER3			308
+#define SRST_TIMER4			309
+#define SRST_TIMER5			310
+#define SRST_P_TIMER6_11		311
+#define SRST_TIMER6			312
+#define SRST_TIMER7			313
+#define SRST_TIMER8			314
+#define SRST_TIMER9			315
+#define SRST_TIMER10			316
+#define SRST_TIMER11			317
+#define SRST_P_INTR_ARB_PMU		318
+#define SRST_P_ALIVE_SGRF		319
+
+/* cru_softrst_con20 */
+#define SRST_P_GPIO2			320
+#define SRST_P_GPIO3			321
+#define SRST_P_GPIO4			322
+#define SRST_P_GRF			323
+#define SRST_P_ALIVE_NOC		324
+#define SRST_P_WDT0			325
+#define SRST_P_WDT1			326
+#define SRST_P_INTR_ARB			327
+#define SRST_P_UPHY0_DPTX		328
+#define SRST_P_UPHY0_APB		330
+#define SRST_P_UPHY0_TCPHY		332
+#define SRST_P_UPHY1_TCPHY		333
+#define SRST_P_UPHY0_TCPDCTRL		334
+#define SRST_P_UPHY1_TCPDCTRL		335
+
+/* pmu soft-reset indices */
+
+/* pmu_cru_softrst_con0 */
+#define SRST_P_NOC			0
+#define SRST_P_INTMEM			1
+#define SRST_H_CM0S			2
+#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_TIMER_0_1		8
+#define SRST_P_TIMER_0			9
+#define SRST_P_TIMER_1			10
+#define SRST_P_UART4			11
+#define SRST_UART4			12
+#define SRST_P_WDT			13
+
+/* pmu_cru_softrst_con1 */
+#define SRST_P_I2C6			16
+#define SRST_P_I2C7			17
+#define SRST_P_I2C8			18
+#define SRST_P_MAILBOX			19
+#define SRST_P_RKPWM			20
+#define SRST_P_PMUGRF			21
+#define SRST_P_SGRF			22
+#define SRST_P_GPIO0			23
+#define SRST_P_GPIO1			24
+#define SRST_P_CRU			25
+#define SRST_P_INTR			26
+#define SRST_PVTM			27
+#define SRST_I2C6			28
+#define SRST_I2C7			29
+#define SRST_I2C8			30
+
+#endif
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 151c590..70ea0bf 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -155,6 +155,7 @@
 	COMPAT_INTEL_BAYTRAIL_FSP,	/* Intel Bay Trail FSP */
 	COMPAT_INTEL_BAYTRAIL_FSP_MDP,	/* Intel FSP memory-down params */
 	COMPAT_INTEL_IVYBRIDGE_FSP,	/* Intel Ivy Bridge FSP */
+	COMPAT_SUNXI_NAND,		/* SUNXI NAND controller */
 
 	COMPAT_COUNT,
 };
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index cf20674..779eea0 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -500,5 +500,10 @@
 int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off,
 		     loff_t *size, loff_t *maxsize, int devtype,
 		     uint64_t chipsize);
+
+/* drivers/mtd/mtdcore.c */
+void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
+			  const uint64_t length, uint64_t *len_incl_bad,
+			  int *truncated);
 #endif
 #endif /* __MTD_MTD_H__ */
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index b5a02c3..87d72db 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -48,7 +48,7 @@
  * is supported now. If you add a chip with bigger oobsize/page
  * adjust this accordingly.
  */
-#define NAND_MAX_OOBSIZE       1216
+#define NAND_MAX_OOBSIZE       1664
 #define NAND_MAX_PAGESIZE      16384
 
 /*
@@ -590,6 +590,7 @@
  *			flash device
  * @IO_ADDR_W:		[BOARDSPECIFIC] address to write the 8 I/O lines of the
  *			flash device.
+ * @flash_node:		[BOARDSPECIFIC] device node describing this instance
  * @read_byte:		[REPLACEABLE] read one byte from the chip
  * @read_word:		[REPLACEABLE] read one word from the chip
  * @write_byte:		[REPLACEABLE] write a single byte to the chip on the
@@ -689,6 +690,8 @@
 	void __iomem *IO_ADDR_R;
 	void __iomem *IO_ADDR_W;
 
+	int flash_node;
+
 	uint8_t (*read_byte)(struct mtd_info *mtd);
 	u16 (*read_word)(struct mtd_info *mtd);
 	void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
diff --git a/include/nand.h b/include/nand.h
index 627b217..b6eb223 100644
--- a/include/nand.h
+++ b/include/nand.h
@@ -142,3 +142,6 @@
 int get_nand_env_oob(struct mtd_info *mtd, unsigned long *result);
 #endif
 int spl_nand_erase_one(int block, int page);
+
+/* platform specific init functions */
+void sunxi_nand_init(void);
diff --git a/include/usb/dwc2_udc.h b/include/usb/dwc2_udc.h
index 302e9a3..7324d8a 100644
--- a/include/usb/dwc2_udc.h
+++ b/include/usb/dwc2_udc.h
@@ -12,12 +12,17 @@
 #define PHY0_SLEEP              (1 << 5)
 
 struct dwc2_plat_otg_data {
+	void		*priv;
+	int		phy_of_node;
 	int		(*phy_control)(int on);
 	unsigned int	regs_phy;
 	unsigned int	regs_otg;
 	unsigned int    usb_phy_ctrl;
 	unsigned int    usb_flags;
 	unsigned int	usb_gusbcfg;
+	unsigned int	rx_fifo_sz;
+	unsigned int	np_tx_fifo_sz;
+	unsigned int	tx_fifo_sz;
 };
 
 int dwc2_udc_probe(struct dwc2_plat_otg_data *pdata);
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index c2bcbde..462a24f 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -65,6 +65,7 @@
 	COMPAT(INTEL_BAYTRAIL_FSP, "intel,baytrail-fsp"),
 	COMPAT(INTEL_BAYTRAIL_FSP_MDP, "intel,baytrail-fsp-mdp"),
 	COMPAT(INTEL_IVYBRIDGE_FSP, "intel,ivybridge-fsp"),
+	COMPAT(COMPAT_SUNXI_NAND, "allwinner,sun4i-a10-nand"),
 };
 
 const char *fdtdec_get_compatible(enum fdt_compat_id id)
diff --git a/lib/hashtable.c b/lib/hashtable.c
index 435e2a6..4e52b36 100644
--- a/lib/hashtable.c
+++ b/lib/hashtable.c
@@ -602,8 +602,8 @@
 		return (-1);
 	}
 
-	debug("EXPORT  table = %p, htab.size = %d, htab.filled = %d, "
-		"size = %zu\n", htab, htab->size, htab->filled, size);
+	debug("EXPORT  table = %p, htab.size = %d, htab.filled = %d, size = %lu\n",
+	      htab, htab->size, htab->filled, (ulong)size);
 	/*
 	 * Pass 1:
 	 * search used entries,
@@ -657,8 +657,8 @@
 	/* Check if the user supplied buffer size is sufficient */
 	if (size) {
 		if (size < totlen + 1) {	/* provided buffer too small */
-			printf("Env export buffer too small: %zu, "
-				"but need %zu\n", size, totlen + 1);
+			printf("Env export buffer too small: %lu, but need %lu\n",
+			       (ulong)size, (ulong)totlen + 1);
 			__set_errno(ENOMEM);
 			return (-1);
 		}
@@ -790,7 +790,7 @@
 
 	/* we allocate new space to make sure we can write to the array */
 	if ((data = malloc(size + 1)) == NULL) {
-		debug("himport_r: can't malloc %zu bytes\n", size + 1);
+		debug("himport_r: can't malloc %lu bytes\n", (ulong)size + 1);
 		__set_errno(ENOMEM);
 		return 0;
 	}
diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c
index d4a1a5e..c26f741 100644
--- a/lib/rsa/rsa-sign.c
+++ b/lib/rsa/rsa-sign.c
@@ -420,8 +420,10 @@
 		BN_rshift(num, num, 32); /*  N = N/B */
 	}
 
-	/* We try signing with successively increasing size values, so this
-	 * might fail several times */
+	/*
+	 * We try signing with successively increasing size values, so this
+	 * might fail several times
+	 */
 	ret = fdt_setprop(blob, noffset, prop_name, buf, size);
 	if (ret)
 		return -FDT_ERR_NOSPACE;
diff --git a/tools/dtoc/fdt_fallback.py b/tools/dtoc/fdt_fallback.py
index 14decf3..9ed11e4 100644
--- a/tools/dtoc/fdt_fallback.py
+++ b/tools/dtoc/fdt_fallback.py
@@ -71,6 +71,12 @@
         if type(newprop.value) == list and type(self.value) != list:
             self.value = newprop.value
 
+        if type(self.value) == list and len(newprop.value) > len(self.value):
+            val = fdt_util.GetEmpty(self.type)
+            while len(self.value) < len(newprop.value):
+                self.value.append(val)
+
+
 class Node:
     """A device tree node
 
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c
index 34ceebd..6b0dcaa 100644
--- a/tools/env/fw_env.c
+++ b/tools/env/fw_env.c
@@ -661,8 +661,8 @@
 
 		if (badblock) {
 #ifdef DEBUG
-			fprintf (stderr, "Bad block at 0x%llx, "
-				 "skipping\n", *blockstart);
+			fprintf (stderr, "Bad block at 0x%llx, skipping\n",
+				(unsigned long long) *blockstart);
 #endif
 			return badblock;
 		}
@@ -749,7 +749,8 @@
 		}
 #ifdef DEBUG
 		fprintf(stderr, "Read 0x%x bytes at 0x%llx on %s\n",
-			 rc, blockstart + block_seek, DEVNAME(dev));
+			rc, (unsigned long long) blockstart + block_seek,
+			DEVNAME(dev));
 #endif
 		processed += readlen;
 		readlen = min (blocklen, count - processed);
@@ -847,8 +848,9 @@
 		if (block_seek + count != write_total) {
 			if (block_seek != 0)
 				fprintf(stderr, " and ");
-			fprintf(stderr, "0x%lx - 0x%x",
-				block_seek + count, write_total - 1);
+			fprintf(stderr, "0x%lx - 0x%lx",
+				(unsigned long) block_seek + count,
+				(unsigned long) write_total - 1);
 		}
 		fprintf(stderr, "\n");
 #endif
@@ -911,8 +913,9 @@
 		}
 
 #ifdef DEBUG
-		fprintf(stderr, "Write 0x%x bytes at 0x%llx\n", erasesize,
-			blockstart);
+		fprintf(stderr, "Write 0x%llx bytes at 0x%llx\n",
+			(unsigned long long) erasesize,
+			(unsigned long long) blockstart);
 #endif
 		if (write (fd, data + processed, erasesize) != erasesize) {
 			fprintf (stderr, "Write error on %s: %s\n",
diff --git a/tools/image-host.c b/tools/image-host.c
index 399ec94..1104695 100644
--- a/tools/image-host.c
+++ b/tools/image-host.c
@@ -238,11 +238,16 @@
 	/* Get keyname again, as FDT has changed and invalidated our pointer */
 	info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL);
 
-	ret = info.algo->add_verify_data(&info, keydest);
+	if (keydest)
+		ret = info.algo->add_verify_data(&info, keydest);
+	else
+		return -1;
 
-	/* Write the public key into the supplied FDT file; this might fail
+	/*
+	 * Write the public key into the supplied FDT file; this might fail
 	 * several times, since we try signing with successively increasing
-	 * size values */
+	 * size values
+	 */
 	if (keydest && ret)
 		return ret;
 
diff --git a/tools/rkcommon.c b/tools/rkcommon.c
index 72621fd..0a072aa 100644
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -56,6 +56,7 @@
 static struct spl_info spl_infos[] = {
 	{ "rk3036", "RK30", 0x1000 },
 	{ "rk3288", "RK32", 0x8000 },
+	{ "rk3399", "RK33", 0x20000 },
 };
 
 static unsigned char rc4_key[16] = {