diff --git a/common/Kconfig b/common/Kconfig
index a3094d984fd2e88c6131ea96d9f6c88e1739661d..c49199bb1a66fc421a75a20eb056496e2abb19b5 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -18,6 +18,15 @@ config BOOTSTAGE
 	  Calls to show_boot_progress() will also result in log entries but
 	  these will not have names.
 
+config SPL_BOOTSTAGE
+	bool "Boot timing and reported in SPL"
+	depends on BOOTSTAGE
+	help
+	  Enable recording of boot time in SPL. To make this visible to U-Boot
+	  proper, enable BOOTSTAGE_STASH as well. This will stash the timing
+	  information when SPL finishes and load it when U-Boot proper starts
+	  up.
+
 config BOOTSTAGE_REPORT
 	bool "Display a detailed boot timing report before booting the OS"
 	depends on BOOTSTAGE
diff --git a/common/Makefile b/common/Makefile
index c7c8ea42c6ece400cbf5f232938b81771cda2d4f..539cf98e19f35b8742454d1188e4110e12affcba 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -65,7 +65,6 @@ obj-$(CONFIG_USB_STORAGE) += usb_storage.o
 endif
 
 # others
-obj-$(CONFIG_BOOTSTAGE) += bootstage.o
 obj-$(CONFIG_CONSOLE_MUX) += iomux.o
 obj-$(CONFIG_MTD_NOR_FLASH) += flash.o
 obj-$(CONFIG_CMD_KGDB) += kgdb.o kgdb_stubs.o
@@ -89,6 +88,8 @@ obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o
 
 endif # !CONFIG_SPL_BUILD
 
+obj-$(CONFIG_$(SPL_)BOOTSTAGE) += bootstage.o
+
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_SPL_DFU_SUPPORT) += dfu.o
 obj-$(CONFIG_SPL_DFU_SUPPORT) += cli_hush.o
diff --git a/common/board_f.c b/common/board_f.c
index a8fc28cf4b2413c7f8f600743c9426870ff7022e..46e52849fb80f9a5a12a1b8ed0182d6fc02e9b75 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -707,11 +707,26 @@ static int jump_to_copy(void)
 /* Record the board_init_f() bootstage (after arch_cpu_init()) */
 static int initf_bootstage(void)
 {
+#if defined(CONFIG_SPL_BOOTSTAGE) && defined(CONFIG_BOOTSTAGE_STASH)
+	bool from_spl = true;
+#else
+	bool from_spl = false;
+#endif
 	int ret;
 
-	ret = bootstage_init(true);
+	ret = bootstage_init(!from_spl);
 	if (ret)
 		return ret;
+	if (from_spl) {
+		const void *stash = map_sysmem(CONFIG_BOOTSTAGE_STASH_ADDR,
+					       CONFIG_BOOTSTAGE_STASH_SIZE);
+
+		ret = bootstage_unstash(stash, CONFIG_BOOTSTAGE_STASH_SIZE);
+		if (ret && ret != -ENOENT) {
+			debug("Failed to unstash bootstage: err=%d\n", ret);
+			return ret;
+		}
+	}
 
 	bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");
 
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 0a49766f21422c25c2756969bbc41b70b9c9c580..f493a3ad495422a31e490f9d4f12a29d4ba876b4 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -232,6 +232,13 @@ static int spl_common_init(bool setup_malloc)
 		gd->malloc_ptr = 0;
 	}
 #endif
+	ret = bootstage_init(true);
+	if (ret) {
+		debug("%s: Failed to set up bootstage: ret=%d\n", __func__,
+		      ret);
+		return ret;
+	}
+	bootstage_mark_name(BOOTSTAGE_ID_START_SPL, "spl");
 	if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
 		ret = fdtdec_setup();
 		if (ret) {
@@ -240,8 +247,10 @@ static int spl_common_init(bool setup_malloc)
 		}
 	}
 	if (IS_ENABLED(CONFIG_SPL_DM)) {
+		bootstage_start(BOOTSTATE_ID_ACCUM_DM_SPL, "dm_spl");
 		/* With CONFIG_SPL_OF_PLATDATA, bring in all devices */
 		ret = dm_init_and_scan(!CONFIG_IS_ENABLED(OF_PLATDATA));
+		bootstage_accum(BOOTSTATE_ID_ACCUM_DM_SPL);
 		if (ret) {
 			debug("dm_init_and_scan() returned error %d\n", ret);
 			return ret;
@@ -421,6 +430,15 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
 	}
 
 	debug("loaded - jumping to U-Boot...\n");
+#ifdef CONFIG_BOOTSTAGE_STASH
+	int ret;
+
+	bootstage_mark_name(BOOTSTAGE_ID_END_SPL, "end_spl");
+	ret = bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR,
+			      CONFIG_BOOTSTAGE_STASH_SIZE);
+	if (ret)
+		debug("Failed to stash bootstage: err=%d\n", ret);
+#endif
 	spl_board_prepare_for_boot();
 	jump_to_image_no_args(&spl_image);
 }
diff --git a/include/bootstage.h b/include/bootstage.h
index 5f4fffaffbde436f4d69a3555b3fa045df78e9e1..c972027ffc624cd3748b2429b25b660a068bbebd 100644
--- a/include/bootstage.h
+++ b/include/bootstage.h
@@ -177,6 +177,7 @@ enum bootstage_id {
 	 */
 	BOOTSTAGE_ID_AWAKE,
 	BOOTSTAGE_ID_START_SPL,
+	BOOTSTAGE_ID_END_SPL,
 	BOOTSTAGE_ID_START_UBOOT_F,
 	BOOTSTAGE_ID_START_UBOOT_R,
 	BOOTSTAGE_ID_USB_START,
@@ -200,6 +201,7 @@ enum bootstage_id {
 	BOOTSTAGE_ID_ACCUM_SPI,
 	BOOTSTAGE_ID_ACCUM_DECOMP,
 	BOOTSTAGE_ID_FPGA_INIT,
+	BOOTSTATE_ID_ACCUM_DM_SPL,
 	BOOTSTATE_ID_ACCUM_DM_F,
 	BOOTSTATE_ID_ACCUM_DM_R,
 
@@ -228,8 +230,14 @@ ulong timer_get_boot_us(void);
 void show_boot_progress(int val);
 #endif
 
-#if defined(CONFIG_BOOTSTAGE) && !defined(CONFIG_SPL_BUILD) && \
-	!defined(USE_HOSTCC)
+#if !defined(USE_HOSTCC)
+#if CONFIG_IS_ENABLED(BOOTSTAGE)
+#define ENABLE_BOOTSTAGE
+#endif
+#endif
+
+#ifdef ENABLE_BOOTSTAGE
+
 /* This is the full bootstage implementation */
 
 /**
@@ -420,7 +428,8 @@ static inline int bootstage_init(bool first)
 {
 	return 0;
 }
-#endif /* CONFIG_BOOTSTAGE */
+
+#endif /* ENABLE_BOOTSTAGE */
 
 /* Helper macro for adding a bootstage to a line of code */
 #define BOOTSTAGE_MARKER()	\