Merge git://git.denx.de/u-boot-rockchip
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 94ad805..ca386de 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1130,6 +1130,7 @@
 	select DM_USB if USB
 	select DM_PWM
 	select DM_REGULATOR
+	select ENABLE_ARM_SOC_BOOT0_HOOK
 	imply CMD_FASTBOOT
 	imply FASTBOOT
 	imply FAT_WRITE
diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S
index 7b84a7a..95a0b52 100644
--- a/arch/arm/cpu/armv7/start.S
+++ b/arch/arm/cpu/armv7/start.S
@@ -31,6 +31,7 @@
 
 	.globl	reset
 	.globl	save_boot_params_ret
+	.type   save_boot_params_ret,%function
 #ifdef CONFIG_ARMV7_LPAE
 	.global	switch_to_hypervisor_ret
 #endif
diff --git a/arch/arm/include/asm/arch-bcm235xx/boot0.h b/arch/arm/include/asm/arch-bcm235xx/boot0.h
index a747bd3..28f05ba 100644
--- a/arch/arm/include/asm/arch-bcm235xx/boot0.h
+++ b/arch/arm/include/asm/arch-bcm235xx/boot0.h
@@ -5,5 +5,7 @@
  */
 
 /* BOOT0 header information */
+_start:
+	ARM_VECTORS
 	.word	0xbabeface
 	.word	_end - _start
diff --git a/arch/arm/include/asm/arch-bcm281xx/boot0.h b/arch/arm/include/asm/arch-bcm281xx/boot0.h
index a747bd3..28f05ba 100644
--- a/arch/arm/include/asm/arch-bcm281xx/boot0.h
+++ b/arch/arm/include/asm/arch-bcm281xx/boot0.h
@@ -5,5 +5,7 @@
  */
 
 /* BOOT0 header information */
+_start:
+	ARM_VECTORS
 	.word	0xbabeface
 	.word	_end - _start
diff --git a/arch/arm/include/asm/arch-rockchip/boot0.h b/arch/arm/include/asm/arch-rockchip/boot0.h
index 72d264b..af3a733 100644
--- a/arch/arm/include/asm/arch-rockchip/boot0.h
+++ b/arch/arm/include/asm/arch-rockchip/boot0.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2017 Theobroma Systems Design und Consulting GmbH
  *
@@ -7,26 +6,49 @@
 
 /*
  * Execution starts on the instruction following this 4-byte header
- * (containing the magic 'RK33').
+ * (containing the magic 'RK30', 'RK31', 'RK32' or 'RK33').  This
+ * magic constant will be written into the final image by the rkimage
+ * tool, but we need to reserve space for it here.
  *
  * To make life easier for everyone, we build the SPL binary with
  * space for this 4-byte header already included in the binary.
  */
-
 #ifdef CONFIG_SPL_BUILD
 	/*
 	 * We need to add 4 bytes of space for the 'RK33' at the
 	 * beginning of the executable.	 However, as we want to keep
 	 * this generic and make it applicable to builds that are like
 	 * the RK3368 (TPL needs this, SPL doesn't) or the RK3399 (no
-	 * TPL, but extra space needed in the SPL), we simply repeat
-	 * the 'b reset' with the expectation that the first one will
-	 * be overwritten, if this is the first stage contained in the
-	 * final image created with mkimage)...
+	 * TPL, but extra space needed in the SPL), we simply insert
+	 * a branch-to-next-instruction-word with the expectation that
+	 * the first one may be overwritten, if this is the first stage
+	 * contained in the final image created with mkimage)...
 	 */
-	b reset	 /* may be overwritten --- should be 'nop' or a 'b reset' */
+	b 1f	 /* if overwritten, entry-address is at the next word */
+1:
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_EARLYRETURN_TO_BROM)
+	adr     r3, entry_counter
+	ldr	r0, [r3]
+	cmp	r0, #1           /* check if entry_counter == 1 */
+	beq	reset            /* regular bootup */
+	add     r0, #1
+	str	r0, [r3]         /* increment the entry_counter in memory */
+	mov     r0, #0           /* return 0 to the BROM to signal 'OK' */
+	bx	lr               /* return control to the BROM */
+entry_counter:
+	.word   0
 #endif
 	b reset
+#if !defined(CONFIG_ARM64)
+	/*
+	 * For armv7, the addr '_start' will used as vector start address
+	 * and write to VBAR register, which needs to aligned to 0x20.
+	 */
+	.align(5), 0x0
+_start:
+	ARM_VECTORS
+#endif
 
 #if defined(CONFIG_ROCKCHIP_RK3399) && defined(CONFIG_SPL_BUILD)
 	.space CONFIG_ROCKCHIP_SPL_RESERVE_IRAM	/* space for the ATF data */
diff --git a/arch/arm/include/asm/arch-rockchip/boot_mode.h b/arch/arm/include/asm/arch-rockchip/boot_mode.h
index bd65f60..6b2a610 100644
--- a/arch/arm/include/asm/arch-rockchip/boot_mode.h
+++ b/arch/arm/include/asm/arch-rockchip/boot_mode.h
@@ -15,5 +15,11 @@
 #define BOOT_CHARGING		(REBOOT_FLAG + 11)
 /* enter usb mass storage mode */
 #define BOOT_UMS		(REBOOT_FLAG + 12)
+/* enter bootrom download mode */
+#define BOOT_BROM_DOWNLOAD	0xEF08A53C
+
+#ifndef __ASSEMBLY__
+int setup_boot_mode(void);
+#endif
 
 #endif
diff --git a/arch/arm/include/asm/arch-rockchip/bootrom.h b/arch/arm/include/asm/arch-rockchip/bootrom.h
index 169cc5e..103b799 100644
--- a/arch/arm/include/asm/arch-rockchip/bootrom.h
+++ b/arch/arm/include/asm/arch-rockchip/bootrom.h
@@ -1,5 +1,6 @@
 /*
  * (C) Copyright 2017 Heiko Stuebner <heiko@sntech.de>
+ * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
  *
  * SPDX-License-Identifier:	GPL-2.0
  */
@@ -14,15 +15,30 @@
 extern u32 SAVE_SP_ADDR;
 
 /**
- * Hand control back to the bootrom to load another
- * boot stage.
+ * back_to_bootrom() - return to bootrom (for TPL/SPL), passing a
+ *                     result code
+ *
+ * Transfer control back to the Rockchip BROM, restoring necessary
+ * register context and passing a command/result code to the BROM
+ * to instruct its next actions (e.g. continue boot sequence, enter
+ * download mode, ...).
+ *
+ * This function does not return.
+ *
+ * @brom_cmd: indicates how the bootrom should continue the boot
+ *            sequence (e.g. load the next stage)
  */
-void back_to_bootrom(void);
+enum rockchip_bootrom_cmd {
+	/*
+	 * These can not start at 0, as 0 has a special meaning
+	 * for setjmp().
+	 */
 
-/**
- * Assembler component for the above (do not call this directly)
- */
-void _back_to_bootrom_s(void);
+	BROM_BOOT_NEXTSTAGE = 1,  /* continue boot-sequence */
+	BROM_BOOT_ENTER_DNL,      /* have BROM enter download-mode */
+};
+
+void back_to_bootrom(enum rockchip_bootrom_cmd brom_cmd);
 
 /**
  * Boot-device identifiers as used by the BROM
diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h
index 641df58..736b260 100644
--- a/arch/arm/include/asm/arch-rockchip/clock.h
+++ b/arch/arm/include/asm/arch-rockchip/clock.h
@@ -39,6 +39,11 @@
 	return clk_id - 1;
 }
 
+struct sysreset_reg {
+	unsigned int glb_srst_fst_value;
+	unsigned int glb_srst_snd_value;
+};
+
 /**
  * clk_get_divisor() - Calculate the required clock divisior
  *
diff --git a/arch/arm/include/asm/setjmp.h b/arch/arm/include/asm/setjmp.h
index c3399a7..517beeb 100644
--- a/arch/arm/include/asm/setjmp.h
+++ b/arch/arm/include/asm/setjmp.h
@@ -1,6 +1,6 @@
 /*
- * (C) Copyright 2016
- * Alexander Graf <agraf@suse.de>
+ * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
+ * (C) Copyright 2016 Alexander Graf <agraf@suse.de>
  *
  * SPDX-License-Identifier:	GPL-2.0+
  */
@@ -8,89 +8,21 @@
 #ifndef _SETJMP_H_
 #define _SETJMP_H_	1
 
+/*
+ * This really should be opaque, but the EFI implementation wrongly
+ * assumes that a 'struct jmp_buf_data' is defined.
+ */
 struct jmp_buf_data {
-	ulong target;
-	ulong regs[5];
-	int ret;
-};
-
-typedef struct jmp_buf_data jmp_buf[1];
-
-static inline int setjmp(jmp_buf jmp)
-{
-	jmp->ret = 0;
-
-#ifdef CONFIG_ARM64
-	asm volatile(
-		"adr x1, jmp_target\n"
-		"str x1, %0\n"
-		"stp x26, x27, %1\n"
-		"stp x28, x29, %2\n"
-		"mov x1, sp\n"
-		"str x1, %3\n"
-		"jmp_target: "
-		: "=m" (jmp->target), "=m" (jmp->regs[0]),
-		  "=m" (jmp->regs[2]), "=m" (jmp->regs[4])
-		:
-		: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
-		  "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
-		  "x16", "x17", "x18", "x19", "x20", "x21", "x22",
-		  "x23", "x24", "x25", /* x26, x27, x28, x29, sp */
-		  "x30", "cc", "memory");
+#if defined(__aarch64__)
+	u64  regs[13];
 #else
-	asm volatile(
-#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
-		".align 2\n"
-		"adr r0, jmp_target\n"
-		"add r0, r0, $1\n"
-#else
-		"adr r0, jmp_target\n"
-#endif
-		"mov r1, %0\n"
-		"mov r2, sp\n"
-		"stm r1!, {r0, r2, r4, r5, r6, r7}\n"
-		".align 2\n"
-		"jmp_target: \n"
-		:
-		: "l" (&jmp->target)
-		: "r0", "r1", "r2", "r3", /* "r4", "r5", "r6", "r7", */
-		  "r8", "r9", "r10", "r11", /* sp, */ "ip", "lr",
-		  "cc", "memory");
+	u32  regs[10];  /* r4-r9, sl, fp, sp, lr */
 #endif
-
-	return jmp->ret;
-}
-
-static inline __noreturn void longjmp(jmp_buf jmp, int ret)
-{
-	jmp->ret = ret;
-
-#ifdef CONFIG_ARM64
-	asm volatile(
-		"ldr x0, %0\n"
-		"ldr x1, %3\n"
-		"mov sp, x1\n"
-		"ldp x26, x27, %1\n"
-		"ldp x28, x25, %2\n"
-		"mov x29, x25\n"
-		"br x0\n"
-		:
-		: "m" (jmp->target), "m" (jmp->regs[0]), "m" (jmp->regs[2]),
-		  "m" (jmp->regs[4])
-		: "x0", "x1", "x25", "x26", "x27", "x28");
-#else
-	asm volatile(
-		"mov r1, %0\n"
-		"ldm r1!, {r0, r2, r4, r5, r6, r7}\n"
-		"mov sp, r2\n"
-		"bx r0\n"
-		:
-		: "l" (&jmp->target)
-		: "r1");
-#endif
+};
 
-	while (1) { }
-}
+typedef struct jmp_buf_data jmp_buf[1];
 
+int setjmp(jmp_buf jmp);
+void longjmp(jmp_buf jmp, int ret);
 
 #endif /* _SETJMP_H_ */
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index 1d7d4f3..4f043ca 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -332,37 +332,6 @@
 
 #ifndef __ASSEMBLY__
 
-/**
- * save_boot_params() - Save boot parameters before starting reset sequence
- *
- * If you provide this function it will be called immediately U-Boot starts,
- * both for SPL and U-Boot proper.
- *
- * All registers are unchanged from U-Boot entry. No registers need be
- * preserved.
- *
- * This is not a normal C function. There is no stack. Return by branching to
- * save_boot_params_ret.
- *
- * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3);
- */
-
-/**
- * save_boot_params_ret() - Return from save_boot_params()
- *
- * If you provide save_boot_params(), then you should jump back to this
- * function when done. Try to preserve all registers.
- *
- * If your implementation of save_boot_params() is in C then it is acceptable
- * to simply call save_boot_params_ret() at the end of your function. Since
- * there is no link register set up, you cannot just exit the function. U-Boot
- * will return to the (initialised) value of lr, and likely crash/hang.
- *
- * If your implementation of save_boot_params() is in assembler then you
- * should use 'b' or 'bx' to return to save_boot_params_ret.
- */
-void save_boot_params_ret(void);
-
 #ifdef CONFIG_ARMV7_LPAE
 void switch_to_hypervisor_ret(void);
 #endif
@@ -556,6 +525,37 @@
 
 #ifndef __ASSEMBLY__
 /**
+ * save_boot_params() - Save boot parameters before starting reset sequence
+ *
+ * If you provide this function it will be called immediately U-Boot starts,
+ * both for SPL and U-Boot proper.
+ *
+ * All registers are unchanged from U-Boot entry. No registers need be
+ * preserved.
+ *
+ * This is not a normal C function. There is no stack. Return by branching to
+ * save_boot_params_ret.
+ *
+ * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3);
+ */
+
+/**
+ * save_boot_params_ret() - Return from save_boot_params()
+ *
+ * If you provide save_boot_params(), then you should jump back to this
+ * function when done. Try to preserve all registers.
+ *
+ * If your implementation of save_boot_params() is in C then it is acceptable
+ * to simply call save_boot_params_ret() at the end of your function. Since
+ * there is no link register set up, you cannot just exit the function. U-Boot
+ * will return to the (initialised) value of lr, and likely crash/hang.
+ *
+ * If your implementation of save_boot_params() is in assembler then you
+ * should use 'b' or 'bx' to return to save_boot_params_ret.
+ */
+void save_boot_params_ret(void);
+
+/**
  * Change the cache settings for a region.
  *
  * \param start		start address of memory region to change
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index 6e1c436..abffa10 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -17,6 +17,12 @@
 obj-y	+= vectors.o crt0.o
 endif
 
+ifdef CONFIG_ARM64
+obj-y   += setjmp_aarch64.o
+else
+obj-y   += setjmp.o
+endif
+
 ifndef CONFIG_SPL_BUILD
 ifdef CONFIG_ARM64
 obj-y	+= relocate_64.o
diff --git a/arch/arm/lib/setjmp.S b/arch/arm/lib/setjmp.S
new file mode 100644
index 0000000..6746e5e
--- /dev/null
+++ b/arch/arm/lib/setjmp.S
@@ -0,0 +1,37 @@
+/*
+ * (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/assembler.h>
+#include <linux/linkage.h>
+
+.pushsection .text.setjmp, "ax"
+ENTRY(setjmp)
+	/*
+	 * A subroutine must preserve the contents of the registers
+	 * r4-r8, r10, r11 (v1-v5, v7 and v8) and SP (and r9 in PCS
+	 * variants that designate r9 as v6).
+	 */
+	mov  ip, sp
+	stm  a1, {v1-v8, ip, lr}
+	mov  a1, #0
+	bx   lr
+ENDPROC(setjmp)
+.popsection
+
+.pushsection .text.longjmp, "ax"
+ENTRY(longjmp)
+	ldm  a1, {v1-v8, ip, lr}
+	mov  sp, ip
+	mov  a1, a2
+	/* If we were passed a return value of zero, return one instead */
+	cmp  a1, #0
+	bne  1f
+	mov  a1, #1
+1:
+	bx   lr
+ENDPROC(longjmp)
+.popsection
diff --git a/arch/arm/lib/setjmp_aarch64.S b/arch/arm/lib/setjmp_aarch64.S
new file mode 100644
index 0000000..b68edb8
--- /dev/null
+++ b/arch/arm/lib/setjmp_aarch64.S
@@ -0,0 +1,42 @@
+/*
+ * (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/macro.h>
+#include <linux/linkage.h>
+
+.pushsection .text.setjmp, "ax"
+ENTRY(setjmp)
+	/* Preserve all callee-saved registers and the SP */
+	stp  x19, x20, [x0,#0]
+	stp  x21, x22, [x0,#16]
+	stp  x23, x24, [x0,#32]
+	stp  x25, x26, [x0,#48]
+	stp  x27, x28, [x0,#64]
+	stp  x29, x30, [x0,#80]
+	mov  x2, sp
+	str  x2, [x0, #96]
+	mov  x0, #0
+	ret
+ENDPROC(setjmp)
+.popsection
+
+.pushsection .text.longjmp, "ax"
+ENTRY(longjmp)
+	ldp  x19, x20, [x0,#0]
+	ldp  x21, x22, [x0,#16]
+	ldp  x23, x24, [x0,#32]
+	ldp  x25, x26, [x0,#48]
+	ldp  x27, x28, [x0,#64]
+	ldp  x29, x30, [x0,#80]
+	ldr  x2, [x0,#96]
+	mov  sp, x2
+	/* Move the return value in place, but return 1 if passed 0. */
+	adds x0, xzr, x1
+	csinc x0, x0, xzr, ne
+	ret
+ENDPROC(longjmp)
+.popsection
diff --git a/arch/arm/lib/vectors.S b/arch/arm/lib/vectors.S
index 1019091..9cb0d2e 100644
--- a/arch/arm/lib/vectors.S
+++ b/arch/arm/lib/vectors.S
@@ -16,6 +16,22 @@
 #include <config.h>
 
 /*
+ * A macro to allow insertion of an ARM exception vector either
+ * for the non-boot0 case or by a boot0-header.
+ */
+        .macro ARM_VECTORS
+	b	reset
+	ldr	pc, _undefined_instruction
+	ldr	pc, _software_interrupt
+	ldr	pc, _prefetch_abort
+	ldr	pc, _data_abort
+	ldr	pc, _not_used
+	ldr	pc, _irq
+	ldr	pc, _fiq
+	.endm
+
+
+/*
  *************************************************************************
  *
  * Symbol _start is referenced elsewhere, so make it global
@@ -35,6 +51,23 @@
 
 	.section ".vectors", "ax"
 
+#if defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK)
+/*
+ * Various SoCs need something special and SoC-specific up front in
+ * order to boot, allow them to set that in their boot0.h file and then
+ * use it here.
+ *
+ * To allow a boot0 hook to insert a 'special' sequence after the vector
+ * table (e.g. for the socfpga), the presence of a boot0 hook supresses
+ * the below vector table and assumes that the vector table is filled in
+ * by the boot0 hook.  The requirements for a boot0 hook thus are:
+ *   (1) defines '_start:' as appropriate
+ *   (2) inserts the vector table using ARM_VECTORS as appropriate
+ */
+#include <asm/arch/boot0.h>
+
+#else
+
 /*
  *************************************************************************
  *
@@ -46,28 +79,11 @@
  */
 
 _start:
-
 #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
 	.word	CONFIG_SYS_DV_NOR_BOOT_CFG
 #endif
-
-	b	reset
-	ldr	pc, _undefined_instruction
-	ldr	pc, _software_interrupt
-	ldr	pc, _prefetch_abort
-	ldr	pc, _data_abort
-	ldr	pc, _not_used
-	ldr	pc, _irq
-	ldr	pc, _fiq
-
-#ifdef CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK
-/*
- * Various SoCs need something special and SoC-specific up front in
- * order to boot, allow them to set that in their boot0.h file and then
- * use it here.
- */
-#include <asm/arch/boot0.h>
-#endif
+	ARM_VECTORS
+#endif /* !defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK) */
 
 /*
  *************************************************************************
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
index d9b25d5..a26736a 100644
--- a/arch/arm/mach-rockchip/Kconfig
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -16,9 +16,14 @@
 	select CPU_V7
 	select SPL_BOARD_INIT if SPL
 	select SUPPORT_SPL
-	select SUPPORT_TPL
 	select SPL
-	select TPL
+	select SPL_CLK
+	select SPL_PINCTRL
+	select SPL_REGMAP
+	select SPL_SYSCON
+	select SPL_RAM
+	select SPL_DRIVERS_MISC_SUPPORT
+	select SPL_ROCKCHIP_EARLYRETURN_TO_BROM
 	select BOARD_LATE_INIT
 	select ROCKCHIP_BROM_HELPER
 	help
@@ -74,7 +79,6 @@
 	imply SPL_SEPARATE_BSS
 	imply SPL_SERIAL_SUPPORT
 	imply TPL_SERIAL_SUPPORT
-	select ENABLE_ARM_SOC_BOOT0_HOOK
 	select DEBUG_UART_BOARD_INIT
 	select SYS_NS16550
 	help
@@ -112,8 +116,9 @@
 	select SPL_SEPARATE_BSS
 	select SPL_SERIAL_SUPPORT
 	select SPL_DRIVERS_MISC_SUPPORT
-	select ENABLE_ARM_SOC_BOOT0_HOOK
 	select DEBUG_UART_BOARD_INIT
+	select BOARD_LATE_INIT
+	select ROCKCHIP_BROM_HELPER
 	help
 	  The Rockchip RK3399 is a ARM-based SoC with a dual-core Cortex-A72
 	  and quad-core Cortex-A53.
@@ -149,6 +154,20 @@
           SPL will return to the boot rom, which will then load the U-Boot
           binary to keep going on.
 
+config ROCKCHIP_BOOT_MODE_REG
+	hex "Rockchip boot mode flag register address"
+	default 0x200081c8 if ROCKCHIP_RK3036
+	default 0x20004040 if ROCKCHIP_RK3188
+	default 0x110005c8 if ROCKCHIP_RK322X
+	default 0xff730094 if ROCKCHIP_RK3288
+	default 0xff738200 if ROCKCHIP_RK3368
+	default 0xff320300 if ROCKCHIP_RK3399
+	default 0x10300580 if ROCKCHIP_RV1108
+	default 0
+	help
+	  The Soc will enter to different boot mode(defined in asm/arch/boot_mode.h)
+	  according to the value from this register.
+
 config ROCKCHIP_SPL_RESERVE_IRAM
 	hex "Size of IRAM reserved in SPL"
 	default 0x4000
@@ -160,6 +179,34 @@
 config ROCKCHIP_BROM_HELPER
 	bool
 
+config SPL_ROCKCHIP_EARLYRETURN_TO_BROM
+        bool "SPL requires early-return (for RK3188-style BROM) to BROM"
+	depends on SPL && ENABLE_ARM_SOC_BOOT0_HOOK
+	help
+	  Some Rockchip BROM variants (e.g. on the RK3188) load the
+	  first stage in segments and enter multiple times. E.g. on
+	  the RK3188, the first 1KB of the first stage are loaded
+	  first and entered; after returning to the BROM, the
+	  remainder of the first stage is loaded, but the BROM
+	  re-enters at the same address/to the same code as previously.
+
+	  This enables support code in the BOOT0 hook for the SPL stage
+	  to allow multiple entries.
+
+config TPL_ROCKCHIP_EARLYRETURN_TO_BROM
+        bool "TPL requires early-return (for RK3188-style BROM) to BROM"
+	depends on TPL && ENABLE_ARM_SOC_BOOT0_HOOK
+	help
+	  Some Rockchip BROM variants (e.g. on the RK3188) load the
+	  first stage in segments and enter multiple times. E.g. on
+	  the RK3188, the first 1KB of the first stage are loaded
+	  first and entered; after returning to the BROM, the
+	  remainder of the first stage is loaded, but the BROM
+	  re-enters at the same address/to the same code as previously.
+
+	  This enables support code in the BOOT0 hook for the TPL stage
+	  to allow multiple entries.
+
 config SPL_MMC_SUPPORT
 	default y if !SPL_ROCKCHIP_BACK_TO_BROM
 
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index daafc8d..2127f2b 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -8,10 +8,9 @@
 # this may have entered from ATF with the stack-pointer pointing to
 # inaccessible/protected memory (and the bootrom-helper assumes that
 # the stack-pointer is valid before switching to the U-Boot stack).
-obj-spl-$(CONFIG_ROCKCHIP_BROM_HELPER) += bootrom.o save_boot_param.o
-obj-tpl-$(CONFIG_ROCKCHIP_BROM_HELPER) += bootrom.o save_boot_param.o
+obj-spl-$(CONFIG_ROCKCHIP_BROM_HELPER) += bootrom.o
+obj-tpl-$(CONFIG_ROCKCHIP_BROM_HELPER) += bootrom.o
 
-obj-tpl-$(CONFIG_ROCKCHIP_RK3188) += rk3188-board-tpl.o
 obj-tpl-$(CONFIG_ROCKCHIP_RK3288) += rk3288-board-tpl.o
 obj-tpl-$(CONFIG_ROCKCHIP_RK3368) += rk3368-board-tpl.o
 
@@ -23,10 +22,16 @@
 obj-spl-$(CONFIG_ROCKCHIP_RK3399) += rk3399-board-spl.o spl-boot-order.o
 
 ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TPL_BUILD),)
+
+ifneq ($(CONFIG_ROCKCHIP_BOOT_MODE_REG),0)
+obj-y += boot_mode.o
+endif
+
 obj-$(CONFIG_ROCKCHIP_RK3188) += rk3188-board.o
 obj-$(CONFIG_ROCKCHIP_RK322X) += rk322x-board.o
 obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288-board.o
 obj-$(CONFIG_ROCKCHIP_RK3036) += rk3036-board.o
+obj-$(CONFIG_ROCKCHIP_RK3399) += rk3399-board.o
 endif
 
 obj-$(CONFIG_$(SPL_TPL_)RAM) += sdram_common.o
diff --git a/arch/arm/mach-rockchip/boot_mode.c b/arch/arm/mach-rockchip/boot_mode.c
new file mode 100644
index 0000000..942849f
--- /dev/null
+++ b/arch/arm/mach-rockchip/boot_mode.c
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 2016 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <adc.h>
+#include <asm/io.h>
+#include <asm/arch/boot_mode.h>
+
+void set_back_to_bootrom_dnl_flag(void)
+{
+	writel(BOOT_BROM_DOWNLOAD, CONFIG_ROCKCHIP_BOOT_MODE_REG);
+}
+
+/*
+ * detect download key status by adc, most rockchip
+ * based boards use adc sample the download key status,
+ * but there are also some use gpio. So it's better to
+ * make this a weak function that can be override by
+ * some special boards.
+ */
+#define KEY_DOWN_MIN_VAL	0
+#define KEY_DOWN_MAX_VAL	30
+
+__weak int rockchip_dnl_key_pressed(void)
+{
+	unsigned int val;
+
+	if (adc_channel_single_shot("saradc", 1, &val)) {
+		pr_err("%s: adc_channel_single_shot fail!\n", __func__);
+		return false;
+	}
+
+	if ((val >= KEY_DOWN_MIN_VAL) && (val <= KEY_DOWN_MAX_VAL))
+		return true;
+	else
+		return false;
+}
+
+void rockchip_dnl_mode_check(void)
+{
+	if (rockchip_dnl_key_pressed()) {
+		printf("download key pressed, entering download mode...");
+		set_back_to_bootrom_dnl_flag();
+		do_reset(NULL, 0, 0, NULL);
+	}
+}
+
+int setup_boot_mode(void)
+{
+	void *reg = (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG;
+	int boot_mode = readl(reg);
+
+	rockchip_dnl_mode_check();
+
+	boot_mode = readl(reg);
+	debug("%s: boot mode 0x%08x\n", __func__, boot_mode);
+
+	/* Clear boot mode */
+	writel(BOOT_NORMAL, reg);
+
+	switch (boot_mode) {
+	case BOOT_FASTBOOT:
+		debug("%s: enter fastboot!\n", __func__);
+		env_set("preboot", "setenv preboot; fastboot usb0");
+		break;
+	case BOOT_UMS:
+		debug("%s: enter UMS!\n", __func__);
+		env_set("preboot", "setenv preboot; ums mmc 0");
+		break;
+	}
+
+	return 0;
+}
diff --git a/arch/arm/mach-rockchip/bootrom.c b/arch/arm/mach-rockchip/bootrom.c
index 8380e4e..bef2a90 100644
--- a/arch/arm/mach-rockchip/bootrom.c
+++ b/arch/arm/mach-rockchip/bootrom.c
@@ -6,11 +6,104 @@
 
 #include <common.h>
 #include <asm/arch/bootrom.h>
+#include <asm/arch/boot_mode.h>
+#include <asm/io.h>
+#include <asm/setjmp.h>
+#include <asm/system.h>
 
-void back_to_bootrom(void)
+/*
+ * Force the jmp_buf to the data-section, as .bss will not be valid
+ * when save_boot_params is invoked.
+ */
+static jmp_buf brom_ctx __section(".data");
+
+static void _back_to_bootrom(enum rockchip_bootrom_cmd brom_cmd)
+{
+	longjmp(brom_ctx, brom_cmd);
+}
+
+void back_to_bootrom(enum rockchip_bootrom_cmd brom_cmd)
 {
 #if CONFIG_IS_ENABLED(LIBCOMMON_SUPPORT)
 	puts("Returning to boot ROM...\n");
 #endif
+	_back_to_bootrom(brom_cmd);
+}
+
+/*
+ * we back to bootrom download mode if get a
+ * BOOT_BROM_DOWNLOAD flag in boot mode register
+ *
+ * note: the boot mode register is configured by
+ * application(next stage bootloader, kernel, etc),
+ * and the bootrom never check this register, so we need
+ * to check it and back to bootrom at very early bootstage(before
+ * some basic configurations(such as interrupts) been
+ * changed by TPL/SPL, as the bootrom download operation
+ * relys on many default settings(such as interrupts) by
+ * it's self.
+ */
+static bool check_back_to_brom_dnl_flag(void)
+{
+	u32 boot_mode;
+
+	if (CONFIG_ROCKCHIP_BOOT_MODE_REG) {
+		boot_mode = readl(CONFIG_ROCKCHIP_BOOT_MODE_REG);
+		if (boot_mode == BOOT_BROM_DOWNLOAD) {
+			writel(0, CONFIG_ROCKCHIP_BOOT_MODE_REG);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * All Rockchip BROM implementations enter with a valid stack-pointer,
+ * so this can safely be implemented in C (providing a single
+ * implementation both for ARMv7 and AArch64).
+ */
+int save_boot_params(void)
+{
+	int  ret = setjmp(brom_ctx);
+
+	switch (ret) {
+	case 0:
+		if (check_back_to_brom_dnl_flag())
+			_back_to_bootrom(BROM_BOOT_ENTER_DNL);
+		/*
+		 * This is the initial pass through this function
+		 * (i.e. saving the context), setjmp just setup up the
+		 * brom_ctx: transfer back into the startup-code at
+		 * 'save_boot_params_ret' and let the compiler know
+		 * that this will not return.
+		 */
+		save_boot_params_ret();
+		while (true)
+			/* does not return */;
+		break;
+
+	case BROM_BOOT_NEXTSTAGE:
+		/*
+		 * To instruct the BROM to boot the next stage, we
+		 * need to return 0 to it: i.e. we need to rewrite
+		 * the return code once more.
+		 */
+		ret = 0;
+		break;
+	case BROM_BOOT_ENTER_DNL:
+		/*
+		 * A non-zero return value will instruct the BROM enter
+		 * download mode.
+		 */
+		ret = 1;
+		break;
+	default:
+#if CONFIG_IS_ENABLED(LIBCOMMON_SUPPORT)
+		puts("FATAL: unexpected command to back_to_bootrom()\n");
+#endif
-	_back_to_bootrom_s();
+		hang();
+	};
+
+	return ret;
 }
diff --git a/arch/arm/mach-rockchip/rk3036-board-spl.c b/arch/arm/mach-rockchip/rk3036-board-spl.c
index 9458201..550e3a1 100644
--- a/arch/arm/mach-rockchip/rk3036-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3036-board-spl.c
@@ -40,7 +40,7 @@
 	sdram_init();
 
 	/* return to maskrom */
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 }
 
 /* Place Holders */
diff --git a/arch/arm/mach-rockchip/rk3036-board.c b/arch/arm/mach-rockchip/rk3036-board.c
index a3457f3..a5d2571 100644
--- a/arch/arm/mach-rockchip/rk3036-board.c
+++ b/arch/arm/mach-rockchip/rk3036-board.c
@@ -19,30 +19,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#define GRF_BASE	0x20008000
-
-static void setup_boot_mode(void)
-{
-	struct rk3036_grf *const grf = (void *)GRF_BASE;
-	int boot_mode = readl(&grf->os_reg[4]);
-
-	debug("boot mode %x.\n", boot_mode);
-
-	/* Clear boot mode */
-	writel(BOOT_NORMAL, &grf->os_reg[4]);
-
-	switch (boot_mode) {
-	case BOOT_FASTBOOT:
-		printf("enter fastboot!\n");
-		env_set("preboot", "setenv preboot; fastboot usb0");
-		break;
-	case BOOT_UMS:
-		printf("enter UMS!\n");
-		env_set("preboot", "setenv preboot; ums mmc 0");
-		break;
-	}
-}
-
 __weak int rk_board_late_init(void)
 {
 	return 0;
diff --git a/arch/arm/mach-rockchip/rk3188-board-spl.c b/arch/arm/mach-rockchip/rk3188-board-spl.c
index 406207e..8e3b8ae 100644
--- a/arch/arm/mach-rockchip/rk3188-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3188-board-spl.c
@@ -101,7 +101,6 @@
 void board_init_f(ulong dummy)
 {
 	struct udevice *pinctrl, *dev;
-	struct rk3188_pmu *pmu;
 	int ret;
 
 	/* Example code showing how to enable the debug UART on RK3188 */
@@ -145,15 +144,6 @@
 		return;
 	}
 
-	/*
-	 * Recover the bootrom's stackpointer.
-	 * For whatever reason needs to run after rockchip_get_clk.
-	 */
-	pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU);
-	if (IS_ERR(pmu))
-		pr_err("pmu syscon returned %ld\n", PTR_ERR(pmu));
-	SAVE_SP_ADDR = readl(&pmu->sys_reg[2]);
-
 	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
 	if (ret) {
 		debug("Pinctrl init failed: %d\n", ret);
@@ -168,7 +158,7 @@
 
 	setup_arm_clock();
 #if CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM) && !defined(CONFIG_SPL_BOARD_INIT)
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 #endif
 }
 
@@ -229,7 +219,7 @@
 
 	preloader_console_init();
 #if CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM)
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 #endif
 	return;
 
diff --git a/arch/arm/mach-rockchip/rk3188-board-tpl.c b/arch/arm/mach-rockchip/rk3188-board-tpl.c
deleted file mode 100644
index b458ef6..0000000
--- a/arch/arm/mach-rockchip/rk3188-board-tpl.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * (C) Copyright 2015 Google, Inc
- *
- * SPDX-License-Identifier:     GPL-2.0+
- */
-
-#include <common.h>
-#include <debug_uart.h>
-#include <spl.h>
-#include <asm/io.h>
-#include <asm/arch/bootrom.h>
-#include <asm/arch/pmu_rk3188.h>
-
-DECLARE_GLOBAL_DATA_PTR;
-
-/* track how often we were entered */
-static int rk3188_num_entries __attribute__ ((section(".data")));
-
-#define PMU_BASE	0x20004000
-#define SPL_ENTRY	0x10080C00
-
-static void jump_to_spl(void)
-{
-	typedef void __noreturn (*image_entry_noargs_t)(void);
-
-	struct rk3188_pmu * const pmu = (void *)PMU_BASE;
-	image_entry_noargs_t tpl_entry =
-		(image_entry_noargs_t)(unsigned long)SPL_ENTRY;
-
-	/* Store the SAVE_SP_ADDR in a location shared with SPL. */
-	writel(SAVE_SP_ADDR, &pmu->sys_reg[2]);
-	tpl_entry();
-}
-
-void board_init_f(ulong dummy)
-{
-	/* Example code showing how to enable the debug UART on RK3188 */
-#ifdef EARLY_UART
-#include <asm/arch/grf_rk3188.h>
-	/* Enable early UART on the RK3188 */
-#define GRF_BASE	0x20008000
-	struct rk3188_grf * const grf = (void *)GRF_BASE;
-
-	rk_clrsetreg(&grf->gpio1b_iomux,
-		     GPIO1B1_MASK << GPIO1B1_SHIFT |
-		     GPIO1B0_MASK << GPIO1B0_SHIFT,
-		     GPIO1B1_UART2_SOUT << GPIO1B1_SHIFT |
-		     GPIO1B0_UART2_SIN << GPIO1B0_SHIFT);
-	/*
-	 * Debug UART can be used from here if required:
-	 *
-	 * debug_uart_init();
-	 * printch('a');
-	 * printhex8(0x1234);
-	 * printascii("string");
-	 */
-	debug_uart_init();
-
-	printch('t');
-	printch('p');
-	printch('l');
-	printch('-');
-	printch(rk3188_num_entries + 1 + '0');
-	printch('\n');
-#endif
-
-	rk3188_num_entries++;
-
-	if (rk3188_num_entries == 1) {
-		/*
-		 * The original loader did some very basic integrity
-		 * checking at this point, but the remaining few bytes
-		 * could be used for any improvement making sense
-		 * really early on.
-		 */
-
-		back_to_bootrom();
-	} else {
-		/*
-		 * TPL part of the loader should now wait for us
-		 * at offset 0xC00 in the sram. Should never return
-		 * from there.
-		 */
-		jump_to_spl();
-	}
-}
diff --git a/arch/arm/mach-rockchip/rk3188-board.c b/arch/arm/mach-rockchip/rk3188-board.c
index 96859a5..916d18f 100644
--- a/arch/arm/mach-rockchip/rk3188-board.c
+++ b/arch/arm/mach-rockchip/rk3188-board.c
@@ -24,6 +24,7 @@
 {
 	struct rk3188_grf *grf;
 
+	setup_boot_mode();
 	grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
 	if (IS_ERR(grf)) {
 		pr_err("grf syscon returned %ld\n", PTR_ERR(grf));
diff --git a/arch/arm/mach-rockchip/rk322x-board-spl.c b/arch/arm/mach-rockchip/rk322x-board-spl.c
index 4ddb8ba..35f4f97 100644
--- a/arch/arm/mach-rockchip/rk322x-board-spl.c
+++ b/arch/arm/mach-rockchip/rk322x-board-spl.c
@@ -76,6 +76,6 @@
 	/* Disable the ddr secure region setting to make it non-secure */
 	rk_clrreg(SGRF_DDR_CON0, 0x4000);
 #if defined(CONFIG_ROCKCHIP_SPL_BACK_TO_BROM) && !defined(CONFIG_SPL_BOARD_INIT)
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 #endif
 }
diff --git a/arch/arm/mach-rockchip/rk322x-board.c b/arch/arm/mach-rockchip/rk322x-board.c
index d443114..e71847d 100644
--- a/arch/arm/mach-rockchip/rk322x-board.c
+++ b/arch/arm/mach-rockchip/rk322x-board.c
@@ -16,30 +16,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#define GRF_BASE	0x11000000
-
-static void setup_boot_mode(void)
-{
-	struct rk322x_grf *const grf = (void *)GRF_BASE;
-	int boot_mode = readl(&grf->os_reg[0]);
-
-	debug("boot mode %x.\n", boot_mode);
-
-	/* Clear boot mode */
-	writel(BOOT_NORMAL, &grf->os_reg[0]);
-
-	switch (boot_mode) {
-	case BOOT_FASTBOOT:
-		printf("enter fastboot!\n");
-		env_set("preboot", "setenv preboot; fastboot usb0");
-		break;
-	case BOOT_UMS:
-		printf("enter UMS!\n");
-		env_set("preboot", "setenv preboot; ums mmc 0");
-		break;
-	}
-}
-
 __weak int rk_board_late_init(void)
 {
 	return 0;
diff --git a/arch/arm/mach-rockchip/rk3288-board-spl.c b/arch/arm/mach-rockchip/rk3288-board-spl.c
index 7b7fd5a..f64a548 100644
--- a/arch/arm/mach-rockchip/rk3288-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3288-board-spl.c
@@ -216,7 +216,7 @@
 #endif
 
 #if CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM) && !defined(CONFIG_SPL_BOARD_INIT)
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 #endif
 }
 
@@ -283,7 +283,7 @@
 
 	preloader_console_init();
 #if CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM)
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 #endif
 	return;
 err:
diff --git a/arch/arm/mach-rockchip/rk3288-board-tpl.c b/arch/arm/mach-rockchip/rk3288-board-tpl.c
index 3d08b5b..150beea 100644
--- a/arch/arm/mach-rockchip/rk3288-board-tpl.c
+++ b/arch/arm/mach-rockchip/rk3288-board-tpl.c
@@ -69,7 +69,7 @@
 
 void board_return_to_bootrom(void)
 {
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 }
 
 u32 spl_boot_device(void)
diff --git a/arch/arm/mach-rockchip/rk3288-board.c b/arch/arm/mach-rockchip/rk3288-board.c
index 278bb40..1c53cca 100644
--- a/arch/arm/mach-rockchip/rk3288-board.c
+++ b/arch/arm/mach-rockchip/rk3288-board.c
@@ -23,31 +23,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#define PMU_BASE	0xff730000
-
-static void setup_boot_mode(void)
-{
-	struct rk3288_pmu *const pmu = (void *)PMU_BASE;
-	int boot_mode = readl(&pmu->sys_reg[0]);
-
-	debug("boot mode %x.\n", boot_mode);
-
-	/* Clear boot mode */
-	writel(BOOT_NORMAL, &pmu->sys_reg[0]);
-
-	switch (boot_mode) {
-	case BOOT_FASTBOOT:
-		printf("enter fastboot!\n");
-		env_set("preboot", "setenv preboot; fastboot usb0");
-		break;
-	case BOOT_UMS:
-		printf("enter UMS!\n");
-		env_set("preboot", "setenv preboot; if mmc dev 0;"
-		       "then ums mmc 0; else ums mmc 1;fi");
-		break;
-	}
-}
-
 __weak int rk_board_late_init(void)
 {
 	return 0;
diff --git a/arch/arm/mach-rockchip/rk3368-board-tpl.c b/arch/arm/mach-rockchip/rk3368-board-tpl.c
index b3e6ffa..60d5aea 100644
--- a/arch/arm/mach-rockchip/rk3368-board-tpl.c
+++ b/arch/arm/mach-rockchip/rk3368-board-tpl.c
@@ -148,7 +148,7 @@
 
 void board_return_to_bootrom(void)
 {
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 }
 
 u32 spl_boot_device(void)
diff --git a/arch/arm/mach-rockchip/rk3399-board-spl.c b/arch/arm/mach-rockchip/rk3399-board-spl.c
index 9c20f56..b96903e 100644
--- a/arch/arm/mach-rockchip/rk3399-board-spl.c
+++ b/arch/arm/mach-rockchip/rk3399-board-spl.c
@@ -23,7 +23,7 @@
 
 void board_return_to_bootrom(void)
 {
-	back_to_bootrom();
+	back_to_bootrom(BROM_BOOT_NEXTSTAGE);
 }
 
 static const char * const boot_devices[BROM_LAST_BOOTSOURCE + 1] = {
diff --git a/arch/arm/mach-rockchip/rk3399-board.c b/arch/arm/mach-rockchip/rk3399-board.c
new file mode 100644
index 0000000..9293843
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3399-board.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2017 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/boot_mode.h>
+
+int board_late_init(void)
+{
+	setup_boot_mode();
+	return 0;
+}
diff --git a/arch/arm/mach-rockchip/save_boot_param.S b/arch/arm/mach-rockchip/save_boot_param.S
deleted file mode 100644
index 50fce20..0000000
--- a/arch/arm/mach-rockchip/save_boot_param.S
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * (C) Copyright 2016 Rockchip Electronics Co., Ltd
- * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
- *
- * SPDX-License-Identifier:     GPL-2.0+
- */
-
-#include <linux/linkage.h>
-
-#if defined(CONFIG_ARM64)
-.globl	SAVE_SP_ADDR
-SAVE_SP_ADDR:
-	.quad 0
-
-ENTRY(save_boot_params)
-	sub	sp, sp, #0x60
-	stp	x29, x30, [sp, #0x50]
-	stp	x27, x28, [sp, #0x40]
-	stp	x25, x26, [sp, #0x30]
-	stp	x23, x24, [sp, #0x20]
-	stp	x21, x22, [sp, #0x10]
-	stp	x19, x20, [sp, #0]
-	ldr	x8, =SAVE_SP_ADDR
-	mov	x9, sp
-	str	x9, [x8]
-	b	save_boot_params_ret  /* back to my caller */
-ENDPROC(save_boot_params)
-
-.globl _back_to_bootrom_s
-ENTRY(_back_to_bootrom_s)
-	ldr	x0, =SAVE_SP_ADDR
-	ldr	x0, [x0]
-	mov	sp, x0
-	ldp	x29, x30, [sp, #0x50]
-	ldp	x27, x28, [sp, #0x40]
-	ldp	x25, x26, [sp, #0x30]
-	ldp	x23, x24, [sp, #0x20]
-	ldp	x21, x22, [sp, #0x10]
-	ldp	x19, x20, [sp]
-	add	sp, sp, #0x60
-	mov	x0, xzr
-	ret
-ENDPROC(_back_to_bootrom_s)
-#else
-.globl	SAVE_SP_ADDR
-SAVE_SP_ADDR:
-	.word 0
-
-/*
- * void save_boot_params
- *
- * Save sp, lr, r1~r12
- */
-ENTRY(save_boot_params)
-	push	{r1-r12, lr}
-	ldr	r0, =SAVE_SP_ADDR
-	str	sp, [r0]
-	b	save_boot_params_ret		@ back to my caller
-ENDPROC(save_boot_params)
-
-
-.globl _back_to_bootrom_s
-ENTRY(_back_to_bootrom_s)
-	ldr	r0, =SAVE_SP_ADDR
-	ldr	sp, [r0]
-	mov	r0, #0
-	pop	{r1-r12, pc}
-ENDPROC(_back_to_bootrom_s)
-#endif
diff --git a/arch/arm/mach-socfpga/include/mach/boot0.h b/arch/arm/mach-socfpga/include/mach/boot0.h
index 22d9e7f..d6b9435 100644
--- a/arch/arm/mach-socfpga/include/mach/boot0.h
+++ b/arch/arm/mach-socfpga/include/mach/boot0.h
@@ -7,6 +7,9 @@
 #ifndef __BOOT0_H
 #define __BOOT0_H
 
+_start:
+	ARM_VECTORS
+
 #ifdef CONFIG_SPL_BUILD
 	.balignl 64,0xf33db33f;
 
diff --git a/doc/README.rockchip b/doc/README.rockchip
index da99f30..597f068 100644
--- a/doc/README.rockchip
+++ b/doc/README.rockchip
@@ -176,17 +176,17 @@
 
 The bootrom of rk3188 expects to find a small 1kb loader which returns
 control to the bootrom, after which it will load the real loader, which
-can then be up to 29kb in size and does the regular ddr init.
+can then be up to 29kb in size and does the regular ddr init.  This is
+handled by a single image (built as the SPL stage) that tests whether
+it is handled for the first or second time via code executed from the
+boot0-hook.
 
 Additionally the rk3188 requires everything the bootrom loads to be
 rc4-encrypted. Except for the very first stage the bootrom always reads
 and decodes 2kb pages, so files should be sized accordingly.
 
 # copy tpl, pad to 1020 bytes and append spl
-cat tpl/u-boot-tpl.bin > tplspl.bin
-truncate -s 1020 tplspl.bin
-cat spl/u-boot-spl.bin >> tplspl.bin
-tools/mkimage -n rk3188 -T rksd -d tplspl.bin out
+tools/mkimage -n rk3188 -T rksd -d spl/u-boot-spl.bin out
 
 # truncate, encode and append u-boot.bin
 truncate -s %2048 u-boot.bin
diff --git a/drivers/clk/rockchip/clk_rk3036.c b/drivers/clk/rockchip/clk_rk3036.c
index 83f4ae6..280ebb9 100644
--- a/drivers/clk/rockchip/clk_rk3036.c
+++ b/drivers/clk/rockchip/clk_rk3036.c
@@ -330,11 +330,22 @@
 static int rk3036_clk_bind(struct udevice *dev)
 {
 	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
 
 	/* The reset driver does not have a device node, so bind it here */
-	ret = device_bind_driver(gd->dm_root, "rk3036_sysreset", "reset", &dev);
-	if (ret)
-		debug("Warning: No RK3036 reset driver: ret=%d\n", ret);
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rk3036_cru,
+						    cru_glb_srst_fst_value);
+		priv->glb_srst_snd_value = offsetof(struct rk3036_cru,
+						    cru_glb_srst_snd_value);
+		sys_child->priv = priv;
+	}
 
 	return 0;
 }
diff --git a/drivers/clk/rockchip/clk_rk3188.c b/drivers/clk/rockchip/clk_rk3188.c
index 8c2c9bc..fca6899 100644
--- a/drivers/clk/rockchip/clk_rk3188.c
+++ b/drivers/clk/rockchip/clk_rk3188.c
@@ -573,11 +573,22 @@
 static int rk3188_clk_bind(struct udevice *dev)
 {
 	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
 
 	/* The reset driver does not have a device node, so bind it here */
-	ret = device_bind_driver(gd->dm_root, "rk3188_sysreset", "reset", &dev);
-	if (ret)
-		debug("Warning: No rk3188 reset driver: ret=%d\n", ret);
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rk3188_cru,
+						    cru_glb_srst_fst_value);
+		priv->glb_srst_snd_value = offsetof(struct rk3188_cru,
+						    cru_glb_srst_snd_value);
+		sys_child->priv = priv;
+	}
 
 	return 0;
 }
diff --git a/drivers/clk/rockchip/clk_rk322x.c b/drivers/clk/rockchip/clk_rk322x.c
index e87267d..ff52b55 100644
--- a/drivers/clk/rockchip/clk_rk322x.c
+++ b/drivers/clk/rockchip/clk_rk322x.c
@@ -385,11 +385,22 @@
 static int rk322x_clk_bind(struct udevice *dev)
 {
 	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
 
 	/* The reset driver does not have a device node, so bind it here */
-	ret = device_bind_driver(gd->dm_root, "rk322x_sysreset", "reset", &dev);
-	if (ret)
-		debug("Warning: No RK322x reset driver: ret=%d\n", ret);
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rk322x_cru,
+						    cru_glb_srst_fst_value);
+		priv->glb_srst_snd_value = offsetof(struct rk322x_cru,
+						    cru_glb_srst_snd_value);
+		sys_child->priv = priv;
+	}
 
 	return 0;
 }
diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c
index a133810..ac53239 100644
--- a/drivers/clk/rockchip/clk_rk3288.c
+++ b/drivers/clk/rockchip/clk_rk3288.c
@@ -859,11 +859,22 @@
 static int rk3288_clk_bind(struct udevice *dev)
 {
 	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
 
 	/* The reset driver does not have a device node, so bind it here */
-	ret = device_bind_driver(gd->dm_root, "rk3288_sysreset", "reset", &dev);
-	if (ret)
-		debug("Warning: No RK3288 reset driver: ret=%d\n", ret);
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rk3288_cru,
+						    cru_glb_srst_fst_value);
+		priv->glb_srst_snd_value = offsetof(struct rk3288_cru,
+						    cru_glb_srst_snd_value);
+		sys_child->priv = priv;
+	}
 
 	return 0;
 }
diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c
index 540d910..4d522a7 100644
--- a/drivers/clk/rockchip/clk_rk3328.c
+++ b/drivers/clk/rockchip/clk_rk3328.c
@@ -597,11 +597,22 @@
 static int rk3328_clk_bind(struct udevice *dev)
 {
 	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
 
 	/* The reset driver does not have a device node, so bind it here */
-	ret = device_bind_driver(gd->dm_root, "rk3328_sysreset", "reset", &dev);
-	if (ret)
-		printf("Warning: No RK3328 reset driver: ret=%d\n", ret);
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rk3328_cru,
+						    glb_srst_fst_value);
+		priv->glb_srst_snd_value = offsetof(struct rk3328_cru,
+						    glb_srst_snd_value);
+		sys_child->priv = priv;
+	}
 
 	return ret;
 }
diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c
index 3661769..bfeef39 100644
--- a/drivers/clk/rockchip/clk_rk3368.c
+++ b/drivers/clk/rockchip/clk_rk3368.c
@@ -526,11 +526,22 @@
 static int rk3368_clk_bind(struct udevice *dev)
 {
 	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
 
 	/* The reset driver does not have a device node, so bind it here */
-	ret = device_bind_driver(gd->dm_root, "rk3368_sysreset", "reset", &dev);
-	if (ret)
-		pr_err("bind RK3368 reset driver failed: ret=%d\n", ret);
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rk3368_cru,
+						    glb_srst_fst_val);
+		priv->glb_srst_snd_value = offsetof(struct rk3368_cru,
+						    glb_srst_snd_val);
+		sys_child->priv = priv;
+	}
 
 	return ret;
 }
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index 6f85a38..e21d056 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -1033,11 +1033,22 @@
 static int rk3399_clk_bind(struct udevice *dev)
 {
 	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
 
 	/* The reset driver does not have a device node, so bind it here */
-	ret = device_bind_driver(gd->dm_root, "rk3399_sysreset", "reset", &dev);
-	if (ret)
-		printf("Warning: No RK3399 reset driver: ret=%d\n", ret);
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rk3399_cru,
+						    glb_srst_fst_value);
+		priv->glb_srst_snd_value = offsetof(struct rk3399_cru,
+						    glb_srst_snd_value);
+		sys_child->priv = priv;
+	}
 
 	return 0;
 }
diff --git a/drivers/clk/rockchip/clk_rv1108.c b/drivers/clk/rockchip/clk_rv1108.c
index 55741c3..a119548 100644
--- a/drivers/clk/rockchip/clk_rv1108.c
+++ b/drivers/clk/rockchip/clk_rv1108.c
@@ -223,11 +223,22 @@
 static int rv1108_clk_bind(struct udevice *dev)
 {
 	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
 
 	/* The reset driver does not have a device node, so bind it here */
-	ret = device_bind_driver(gd->dm_root, "rv1108_sysreset", "reset", &dev);
-	if (ret)
-		pr_err("No Rv1108 reset driver: ret=%d\n", ret);
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rv1108_cru,
+						    glb_srst_fst_val);
+		priv->glb_srst_snd_value = offsetof(struct rv1108_cru,
+						    glb_srst_snd_val);
+		sys_child->priv = priv;
+	}
 
 	return 0;
 }
diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c
index b18db74..03d3fa6 100644
--- a/drivers/spi/rk_spi.c
+++ b/drivers/spi/rk_spi.c
@@ -94,7 +94,7 @@
 	 */
 	if (clk_div > 0xfffe) {
 		clk_div = 0xfffe;
-		debug("%s: can't divide down to %d hz (actual will be %d hz)\n",
+		debug("%s: can't divide down to %d Hz (actual will be %d Hz)\n",
 		      __func__, speed, priv->input_rate / clk_div);
 	}
 
diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile
index 476d361..2e9598e 100644
--- a/drivers/sysreset/Makefile
+++ b/drivers/sysreset/Makefile
@@ -9,16 +9,7 @@
 obj-$(CONFIG_SYSRESET_SYSCON) += sysreset_syscon.o
 obj-$(CONFIG_SYSRESET_WATCHDOG) += sysreset_watchdog.o
 
-ifndef CONFIG_SPL_BUILD
-obj-$(CONFIG_ROCKCHIP_RK3036) += sysreset_rk3036.o
-endif
-obj-$(CONFIG_ROCKCHIP_RK3188) += sysreset_rk3188.o
-obj-$(CONFIG_ROCKCHIP_RK322X) += sysreset_rk322x.o
-obj-$(CONFIG_ROCKCHIP_RK3288) += sysreset_rk3288.o
-obj-$(CONFIG_ROCKCHIP_RK3328) += sysreset_rk3328.o
-obj-$(CONFIG_ROCKCHIP_RK3368) += sysreset_rk3368.o
-obj-$(CONFIG_ROCKCHIP_RK3399) += sysreset_rk3399.o
-obj-$(CONFIG_ROCKCHIP_RV1108) += sysreset_rv1108.o
+obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o
 obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o
 obj-$(CONFIG_ARCH_SNAPDRAGON) += sysreset_snapdragon.o
 obj-$(CONFIG_ARCH_STI) += sysreset_sti.o
diff --git a/drivers/sysreset/sysreset_rk3036.c b/drivers/sysreset/sysreset_rk3036.c
deleted file mode 100644
index b3d2113..0000000
--- a/drivers/sysreset/sysreset_rk3036.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * (C) Copyright 2015 Rockchip Electronics Co., Ltd
- *
- * SPDX-License-Identifier:     GPL-2.0+
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <sysreset.h>
-#include <asm/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cru_rk3036.h>
-#include <asm/arch/hardware.h>
-#include <linux/err.h>
-
-int rk3036_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
-	struct rk3036_cru *cru = rockchip_get_cru();
-
-	if (IS_ERR(cru))
-		return PTR_ERR(cru);
-	switch (type) {
-	case SYSRESET_WARM:
-		writel(0xeca8, &cru->cru_glb_srst_snd_value);
-		break;
-	case SYSRESET_COLD:
-		writel(0xfdb9, &cru->cru_glb_srst_fst_value);
-		break;
-	default:
-		return -EPROTONOSUPPORT;
-	}
-
-	return -EINPROGRESS;
-}
-
-static struct sysreset_ops rk3036_sysreset = {
-	.request	= rk3036_sysreset_request,
-};
-
-U_BOOT_DRIVER(sysreset_rk3036) = {
-	.name	= "rk3036_sysreset",
-	.id	= UCLASS_SYSRESET,
-	.ops	= &rk3036_sysreset,
-};
diff --git a/drivers/sysreset/sysreset_rk3188.c b/drivers/sysreset/sysreset_rk3188.c
deleted file mode 100644
index 053a634..0000000
--- a/drivers/sysreset/sysreset_rk3188.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * (C) Copyright 2015 Google, Inc
- *
- * SPDX-License-Identifier:	GPL-2.0
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <syscon.h>
-#include <sysreset.h>
-#include <asm/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cru_rk3188.h>
-#include <asm/arch/grf_rk3188.h>
-#include <asm/arch/hardware.h>
-#include <linux/err.h>
-
-int rk3188_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
-	struct rk3188_cru *cru = rockchip_get_cru();
-	struct rk3188_grf *grf;
-
-	if (IS_ERR(cru))
-		return PTR_ERR(cru);
-	switch (type) {
-	case SYSRESET_WARM:
-		grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
-		if (IS_ERR(grf))
-			return -EPROTONOSUPPORT;
-
-		/*
-		 * warm-reset keeps the remap value,
-		 * so make sure it's disabled.
-		 */
-		rk_clrsetreg(&grf->soc_con0,
-			NOC_REMAP_MASK << NOC_REMAP_SHIFT,
-			0 << NOC_REMAP_SHIFT);
-
-		rk_clrreg(&cru->cru_mode_con, 0xffff);
-		writel(0xeca8, &cru->cru_glb_srst_snd_value);
-		break;
-	case SYSRESET_COLD:
-		rk_clrreg(&cru->cru_mode_con, 0xffff);
-		writel(0xfdb9, &cru->cru_glb_srst_fst_value);
-		break;
-	default:
-		return -EPROTONOSUPPORT;
-	}
-
-	return -EINPROGRESS;
-}
-
-static struct sysreset_ops rk3188_sysreset = {
-	.request	= rk3188_sysreset_request,
-};
-
-U_BOOT_DRIVER(sysreset_rk3188) = {
-	.name	= "rk3188_sysreset",
-	.id	= UCLASS_SYSRESET,
-	.ops	= &rk3188_sysreset,
-};
diff --git a/drivers/sysreset/sysreset_rk322x.c b/drivers/sysreset/sysreset_rk322x.c
deleted file mode 100644
index 5fce79b..0000000
--- a/drivers/sysreset/sysreset_rk322x.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * (C) Copyright 2017 Rockchip Electronics Co., Ltd
- *
- * SPDX-License-Identifier:     GPL-2.0+
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <sysreset.h>
-#include <asm/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cru_rk322x.h>
-#include <asm/arch/hardware.h>
-#include <linux/err.h>
-
-int rk322x_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
-	struct rk322x_cru *cru = rockchip_get_cru();
-
-	if (IS_ERR(cru))
-		return PTR_ERR(cru);
-	switch (type) {
-	case SYSRESET_WARM:
-		writel(0xeca8, &cru->cru_glb_srst_snd_value);
-		break;
-	case SYSRESET_COLD:
-		writel(0xfdb9, &cru->cru_glb_srst_fst_value);
-		break;
-	default:
-		return -EPROTONOSUPPORT;
-	}
-
-	return -EINPROGRESS;
-}
-
-static struct sysreset_ops rk322x_sysreset = {
-	.request	= rk322x_sysreset_request,
-};
-
-U_BOOT_DRIVER(sysreset_rk322x) = {
-	.name	= "rk322x_sysreset",
-	.id	= UCLASS_SYSRESET,
-	.ops	= &rk322x_sysreset,
-};
diff --git a/drivers/sysreset/sysreset_rk3288.c b/drivers/sysreset/sysreset_rk3288.c
deleted file mode 100644
index 0aad1c2..0000000
--- a/drivers/sysreset/sysreset_rk3288.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * (C) Copyright 2015 Google, Inc
- *
- * SPDX-License-Identifier:	GPL-2.0
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <sysreset.h>
-#include <asm/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cru_rk3288.h>
-#include <asm/arch/hardware.h>
-#include <linux/err.h>
-
-int rk3288_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
-	struct rk3288_cru *cru = rockchip_get_cru();
-
-	if (IS_ERR(cru))
-		return PTR_ERR(cru);
-	switch (type) {
-	case SYSRESET_WARM:
-		rk_clrreg(&cru->cru_mode_con, 0xffff);
-		writel(0xeca8, &cru->cru_glb_srst_snd_value);
-		break;
-	case SYSRESET_COLD:
-		rk_clrreg(&cru->cru_mode_con, 0xffff);
-		writel(0xfdb9, &cru->cru_glb_srst_fst_value);
-		break;
-	default:
-		return -EPROTONOSUPPORT;
-	}
-
-	return -EINPROGRESS;
-}
-
-static struct sysreset_ops rk3288_sysreset = {
-	.request	= rk3288_sysreset_request,
-};
-
-U_BOOT_DRIVER(sysreset_rk3288) = {
-	.name	= "rk3288_sysreset",
-	.id	= UCLASS_SYSRESET,
-	.ops	= &rk3288_sysreset,
-};
diff --git a/drivers/sysreset/sysreset_rk3328.c b/drivers/sysreset/sysreset_rk3328.c
deleted file mode 100644
index 7b9af09..0000000
--- a/drivers/sysreset/sysreset_rk3328.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * (C) Copyright 2016 Rockchip Electronics Co., Ltd
- *
- * SPDX-License-Identifier:	GPL-2.0
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <sysreset.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cru_rk3328.h>
-#include <asm/arch/hardware.h>
-#include <asm/io.h>
-#include <linux/err.h>
-
-int rk3328_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
-	struct rk3328_cru *cru = rockchip_get_cru();
-
-	if (IS_ERR(cru))
-		return PTR_ERR(cru);
-	switch (type) {
-	case SYSRESET_WARM:
-		writel(0xeca8, &cru->glb_srst_snd_value);
-		break;
-	case SYSRESET_COLD:
-		writel(0xfdb9, &cru->glb_srst_fst_value);
-		break;
-	default:
-		return -EPROTONOSUPPORT;
-	}
-
-	return -EINPROGRESS;
-}
-
-static struct sysreset_ops rk3328_sysreset = {
-	.request	= rk3328_sysreset_request,
-};
-
-U_BOOT_DRIVER(sysreset_rk3328) = {
-	.name	= "rk3328_sysreset",
-	.id	= UCLASS_SYSRESET,
-	.ops	= &rk3328_sysreset,
-};
diff --git a/drivers/sysreset/sysreset_rk3368.c b/drivers/sysreset/sysreset_rk3368.c
deleted file mode 100644
index de62921..0000000
--- a/drivers/sysreset/sysreset_rk3368.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * (C) Copyright Rockchip Electronics Co., Ltd
- *
- * SPDX-License-Identifier:	GPL-2.0
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <sysreset.h>
-#include <asm/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cru_rk3368.h>
-#include <asm/arch/hardware.h>
-#include <linux/err.h>
-
-static void rk3368_pll_enter_slow_mode(struct rk3368_cru *cru)
-{
-	struct rk3368_pll *pll;
-	int i;
-
-	for (i = 0; i < 6; i++) {
-		pll = &cru->pll[i];
-		rk_clrreg(&pll->con3, PLL_MODE_MASK);
-	}
-}
-
-static int rk3368_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
-	struct rk3368_cru *cru = rockchip_get_cru();
-
-	if (IS_ERR(cru))
-		return PTR_ERR(cru);
-	switch (type) {
-	case SYSRESET_WARM:
-		rk3368_pll_enter_slow_mode(cru);
-		rk_clrsetreg(&cru->glb_rst_con, PMU_GLB_SRST_CTRL_MASK,
-			     PMU_RST_BY_SND_GLB_SRST << PMU_GLB_SRST_CTRL_SHIFT);
-		writel(0xeca8, &cru->glb_srst_snd_val);
-		break;
-	case SYSRESET_COLD:
-		rk3368_pll_enter_slow_mode(cru);
-		rk_clrsetreg(&cru->glb_rst_con, PMU_GLB_SRST_CTRL_MASK,
-			     PMU_RST_BY_FST_GLB_SRST << PMU_GLB_SRST_CTRL_SHIFT);
-		writel(0xfdb9, &cru->glb_srst_fst_val);
-		break;
-	default:
-		return -EPROTONOSUPPORT;
-	}
-
-	return -EINPROGRESS;
-}
-
-static struct sysreset_ops rk3368_sysreset = {
-	.request	= rk3368_sysreset_request,
-};
-
-U_BOOT_DRIVER(sysreset_rk3368) = {
-	.name	= "rk3368_sysreset",
-	.id	= UCLASS_SYSRESET,
-	.ops	= &rk3368_sysreset,
-};
diff --git a/drivers/sysreset/sysreset_rk3399.c b/drivers/sysreset/sysreset_rk3399.c
deleted file mode 100644
index 9a55546..0000000
--- a/drivers/sysreset/sysreset_rk3399.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * (C) Copyright 2016 Rockchip Electronics Co., Ltd
- *
- * SPDX-License-Identifier:	GPL-2.0
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <sysreset.h>
-#include <asm/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cru_rk3399.h>
-#include <asm/arch/hardware.h>
-#include <linux/err.h>
-
-int rk3399_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
-	struct rk3399_cru *cru = rockchip_get_cru();
-
-	if (IS_ERR(cru))
-		return PTR_ERR(cru);
-	switch (type) {
-	case SYSRESET_WARM:
-		writel(0xeca8, &cru->glb_srst_snd_value);
-		break;
-	case SYSRESET_COLD:
-		writel(0xfdb9, &cru->glb_srst_fst_value);
-		break;
-	default:
-		return -EPROTONOSUPPORT;
-	}
-
-	return -EINPROGRESS;
-}
-
-static struct sysreset_ops rk3399_sysreset = {
-	.request	= rk3399_sysreset_request,
-};
-
-U_BOOT_DRIVER(sysreset_rk3399) = {
-	.name	= "rk3399_sysreset",
-	.id	= UCLASS_SYSRESET,
-	.ops	= &rk3399_sysreset,
-};
diff --git a/drivers/sysreset/sysreset_rockchip.c b/drivers/sysreset/sysreset_rockchip.c
new file mode 100644
index 0000000..894975f
--- /dev/null
+++ b/drivers/sysreset/sysreset_rockchip.c
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 2017 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <sysreset.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3328.h>
+#include <asm/arch/hardware.h>
+#include <linux/err.h>
+
+int rockchip_sysreset_request(struct udevice *dev, enum sysreset_t type)
+{
+	struct sysreset_reg *offset = dev_get_priv(dev);
+	unsigned long cru_base = (unsigned long)rockchip_get_cru();
+
+	if (IS_ERR_VALUE(cru_base))
+		return (int)cru_base;
+
+	switch (type) {
+	case SYSRESET_WARM:
+		writel(0xeca8, cru_base + offset->glb_srst_snd_value);
+		break;
+	case SYSRESET_COLD:
+		writel(0xfdb9, cru_base + offset->glb_srst_fst_value);
+		break;
+	default:
+		return -EPROTONOSUPPORT;
+	}
+
+	return -EINPROGRESS;
+}
+
+static struct sysreset_ops rockchip_sysreset = {
+	.request	= rockchip_sysreset_request,
+};
+
+U_BOOT_DRIVER(sysreset_rockchip) = {
+	.name	= "rockchip_sysreset",
+	.id	= UCLASS_SYSRESET,
+	.ops	= &rockchip_sysreset,
+};
diff --git a/drivers/sysreset/sysreset_rv1108.c b/drivers/sysreset/sysreset_rv1108.c
deleted file mode 100644
index 9d8e9f7..0000000
--- a/drivers/sysreset/sysreset_rv1108.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * (C) Copyright 2015 Rockchip Electronics Co., Ltd
- * Author: Andy Yan <andy.yan@rock-chips.com>
- * SPDX-License-Identifier:     GPL-2.0+
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <sysreset.h>
-#include <asm/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cru_rv1108.h>
-#include <asm/arch/hardware.h>
-#include <linux/err.h>
-
-int rv1108_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
-	struct rv1108_cru *cru = rockchip_get_cru();
-
-	if (IS_ERR(cru))
-		return PTR_ERR(cru);
-
-	switch (type) {
-	case SYSRESET_WARM:
-		writel(0xeca8, &cru->glb_srst_snd_val);
-		break;
-	case SYSRESET_COLD:
-		writel(0xfdb9, &cru->glb_srst_fst_val);
-		break;
-	default:
-		return -EPROTONOSUPPORT;
-	}
-
-	return -EINPROGRESS;
-}
-
-static struct sysreset_ops rv1108_sysreset = {
-	.request	= rv1108_sysreset_request,
-};
-
-U_BOOT_DRIVER(sysreset_rv1108) = {
-	.name	= "rv1108_sysreset",
-	.id	= UCLASS_SYSRESET,
-	.ops	= &rv1108_sysreset,
-};
diff --git a/include/configs/evb_px5.h b/include/configs/evb_px5.h
index d008539..4160187 100644
--- a/include/configs/evb_px5.h
+++ b/include/configs/evb_px5.h
@@ -9,8 +9,6 @@
 
 #include <configs/rk3368_common.h>
 
-#define CONFIG_ENV_SIZE			0x2000
-
 #define CONFIG_CONSOLE_SCROLL_LINES	10
 
 #endif
diff --git a/include/configs/geekbox.h b/include/configs/geekbox.h
index 7a707cb..bb2ef9a 100644
--- a/include/configs/geekbox.h
+++ b/include/configs/geekbox.h
@@ -9,8 +9,6 @@
 
 #include <configs/rk3368_common.h>
 
-#define CONFIG_ENV_SIZE			0x2000
-
 #define CONFIG_CONSOLE_SCROLL_LINES		10
 
 #endif
diff --git a/include/configs/rk3036_common.h b/include/configs/rk3036_common.h
index 4ed8f5a..3a2bf33 100644
--- a/include/configs/rk3036_common.h
+++ b/include/configs/rk3036_common.h
@@ -25,7 +25,7 @@
 #define CONFIG_SYS_INIT_SP_ADDR		0x60100000
 #define CONFIG_SYS_LOAD_ADDR		0x60800800
 #define CONFIG_SPL_STACK		0x10081fff
-#define CONFIG_SPL_TEXT_BASE		0x10081004
+#define CONFIG_SPL_TEXT_BASE		0x10081000
 
 #define CONFIG_ROCKCHIP_MAX_INIT_SIZE	(4 << 10)
 #define CONFIG_ROCKCHIP_CHIP_TAG	"RK30"
diff --git a/include/configs/rk3188_common.h b/include/configs/rk3188_common.h
index cfa5364..0cb0762 100644
--- a/include/configs/rk3188_common.h
+++ b/include/configs/rk3188_common.h
@@ -36,23 +36,11 @@
 #define CONFIG_ROCKCHIP_MAX_INIT_SIZE	(0x8000 - 0x800)
 #define CONFIG_ROCKCHIP_CHIP_TAG	"RK31"
 
-#ifdef CONFIG_TPL_BUILD
-#define CONFIG_SPL_TEXT_BASE		0x10080804
-/* tpl size 1kb - 4byte RK31 header */
-#define CONFIG_SPL_MAX_SIZE		(0x400 - 0x4)
-#elif defined(CONFIG_SPL_BUILD)
-/* spl size 32kb sram - 2kb bootrom - 1kb spl */
-#define CONFIG_SPL_MAX_SIZE		(0x8000 - 0xC00)
-#define CONFIG_SPL_TEXT_BASE		0x10080C00
+#define CONFIG_SPL_TEXT_BASE		0x10080800
+/* spl size 32kb sram - 2kb bootrom */
+#define CONFIG_SPL_MAX_SIZE		(0x8000 - 0x800)
 #define CONFIG_SPL_FRAMEWORK		1
-#define CONFIG_SPL_CLK			1
-#define CONFIG_SPL_PINCTRL		1
-#define CONFIG_SPL_REGMAP		1
-#define CONFIG_SPL_SYSCON		1
-#define CONFIG_SPL_RAM			1
-#define CONFIG_SPL_DRIVERS_MISC_SUPPORT	1
 #define CONFIG_ROCKCHIP_SERIAL		1
-#endif
 
 #define CONFIG_SPL_STACK		0x10087fff
 
diff --git a/include/configs/rk3288_common.h b/include/configs/rk3288_common.h
index 2b8f618..e2f070f 100644
--- a/include/configs/rk3288_common.h
+++ b/include/configs/rk3288_common.h
@@ -34,7 +34,7 @@
 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_TPL_BOOTROM_SUPPORT)
 # define CONFIG_SPL_TEXT_BASE		0x0
 #else
-# define CONFIG_SPL_TEXT_BASE		0xff704004
+# define CONFIG_SPL_TEXT_BASE		0xff704000
 #endif
 
 /* MMC/SD IP block */
diff --git a/include/configs/rv1108_common.h b/include/configs/rv1108_common.h
index 5ee4559..549839d 100644
--- a/include/configs/rv1108_common.h
+++ b/include/configs/rv1108_common.h
@@ -9,7 +9,6 @@
 #include <asm/arch/hardware.h>
 #include "rockchip-common.h"
 
-#define CONFIG_ENV_SIZE			0x2000
 #define CONFIG_SYS_MALLOC_LEN		(32 << 20)
 #define CONFIG_SYS_CBSIZE		1024
 #define CONFIG_SKIP_LOWLEVEL_INIT
diff --git a/tools/rkcommon.c b/tools/rkcommon.c
index 1a24e16..aed2b95 100644
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -58,9 +58,6 @@
  * @spl_hdr:		Boot ROM requires a 4-bytes spl header
  * @spl_size:		Spl size(include extra 4-bytes spl header)
  * @spl_rc4:		RC4 encode the SPL binary (same key as header)
- * @spl_boot0:          A new-style (ARM_SOC_BOOT0_HOOK) image that should
- *                      have the boot magic (e.g. 'RK33') written to its first
- *                      word.
  */
 
 struct spl_info {
@@ -68,19 +65,18 @@
 	const char *spl_hdr;
 	const uint32_t spl_size;
 	const bool spl_rc4;
-	const bool spl_boot0;
 };
 
 static struct spl_info spl_infos[] = {
-	{ "rk3036", "RK30", 0x1000, false, false },
-	{ "rk3128", "RK31", 0x1800, false, false },
-	{ "rk3188", "RK31", 0x8000 - 0x800, true, false },
-	{ "rk322x", "RK32", 0x8000 - 0x1000, false, false },
-	{ "rk3288", "RK32", 0x8000, false, false },
-	{ "rk3328", "RK32", 0x8000 - 0x1000, false, false },
-	{ "rk3368", "RK33", 0x8000 - 0x1000, false, true },
-	{ "rk3399", "RK33", 0x30000 - 0x2000, false, true },
-	{ "rv1108", "RK11", 0x1800, false, false},
+	{ "rk3036", "RK30", 0x1000, false },
+	{ "rk3128", "RK31", 0x1800, false },
+	{ "rk3188", "RK31", 0x8000 - 0x800, true },
+	{ "rk322x", "RK32", 0x8000 - 0x1000, false },
+	{ "rk3288", "RK32", 0x8000, false },
+	{ "rk3328", "RK32", 0x8000 - 0x1000, false },
+	{ "rk3368", "RK33", 0x8000 - 0x1000, false },
+	{ "rk3399", "RK33", 0x30000 - 0x2000, false },
+	{ "rv1108", "RK11", 0x1800, false },
 };
 
 static unsigned char rc4_key[16] = {
@@ -158,16 +154,6 @@
 	return info->spl_rc4;
 }
 
-bool rkcommon_spl_is_boot0(struct image_tool_params *params)
-{
-	struct spl_info *info = rkcommon_get_spl_info(params->imagename);
-
-	/*
-	 * info would not be NULL, because of we checked params before.
-	 */
-	return info->spl_boot0;
-}
-
 static void rkcommon_set_header0(void *buf, uint file_size,
 				 struct image_tool_params *params)
 {
@@ -366,15 +352,12 @@
 	 * have the first 4 bytes reserved for the spl_name).  Reserving
 	 * these 4 bytes is done using the BOOT0_HOOK infrastructure.
 	 *
-	 * Depending on this, the header is either 0x800 (if this is a
-	 * 'boot0'-style payload, which has reserved 4 bytes at the
-	 * beginning for the 'spl_name' and expects us to overwrite
-	 * its first 4 bytes) or 0x804 bytes in length.
+	 * The header is always at 0x800 (as we now use a payload
+	 * prepadded using the boot0 hook for all targets): the first
+	 * 4 bytes of these images can safely be overwritten using the
+	 * boot magic.
 	 */
-	if (rkcommon_spl_is_boot0(params))
-		tparams->header_size = RK_SPL_HDR_START;
-	else
-		tparams->header_size = RK_SPL_HDR_START + 4;
+	tparams->header_size = RK_SPL_HDR_START;
 
 	/* Allocate, clear and install the header */
 	tparams->hdr = malloc(tparams->header_size);