[PATCH 1/3] ARM: imx: Support detection of ECSPI recovery mode on i.MX6

Frieder Schrempf <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <[email protected]>
From: Frieder Schrempf <[email protected]>

The i.MX6 allows to boot from a SPI NOR as a fallback if the primary
boot fails. Parse the ROM log in order to detect this and return
the proper boot device in spl_boot_device(). Otherwise the primary
boot device is returned even if it was not used.

To make imx6_is_ecspi_recovery_boot() usable in U-Boot proper,
add them in arch/arm/mach-imx/init.c.

This was tested on i.MX6S and the implementation covers other
i.MX6 variants based on the documentation from NXP (AN12853).

Assisted-by: Claude:claude-opus-5
Signed-off-by: Frieder Schrempf <[email protected]>
---
 arch/arm/include/asm/mach-imx/sys_proto.h |  1 +
 arch/arm/mach-imx/init.c                  | 75 +++++++++++++++++++++++
 arch/arm/mach-imx/spl.c                   |  7 +++
 3 files changed, 83 insertions(+)

diff --git a/arch/arm/include/asm/mach-imx/sys_proto.h b/arch/arm/include/asm/mach-imx/sys_proto.h
index d25c08f8fe7..06b5636c7a0 100644
--- a/arch/arm/include/asm/mach-imx/sys_proto.h
+++ b/arch/arm/include/asm/mach-imx/sys_proto.h
@@ -159,6 +159,7 @@ enum imx6_bmode {
 };
 
 u32 imx6_src_get_boot_mode(void);
+bool imx6_is_ecspi_recovery_boot(void);
 void gpr_init(void);
 
 #endif /* CONFIG_MX6 */
diff --git a/arch/arm/mach-imx/init.c b/arch/arm/mach-imx/init.c
index ce3eb4b0b8a..4193b350e03 100644
--- a/arch/arm/mach-imx/init.c
+++ b/arch/arm/mach-imx/init.c
@@ -137,4 +137,79 @@ u32 imx6_src_get_boot_mode(void)
 	else
 		return readl(&src_base->sbmr1);
 }
+
+/*
+ * The boot ROM records what it did in an event log buffer in OCRAM. The
+ * address of that buffer is stored at a fixed, SoC specific location inside
+ * the ROM, see AN12853 "i.MX ROMs Log Events". i.MX6 uses the version 0 log
+ * format, where an entry consists of a 32-bit word holding a 24-bit event
+ * ID, optionally followed by up to three parameter words.
+ */
+#define IMX6_ROM_LOG_ID_MASK		GENMASK(23, 0)
+#define IMX6_ROM_LOG_MAX_ENTRIES	128
+
+static const u32 *imx6_get_rom_log(void)
+{
+	uintptr_t log;
+	ulong addr;
+
+	if (is_mx6dq() || is_mx6dqp() || is_mx6sl())
+		addr = 0xd4;
+	else if (is_mx6sdl())
+		addr = 0xd8;
+	else if (is_mx6sx() || is_mx6sll() || is_mx6ul() || is_mx6ull())
+		addr = 0x1e0;
+	else
+		return NULL;
+
+	log = readl(addr);
+
+	/* The log buffer lives in OCRAM, ignore an invalid pointer */
+	if (log < IRAM_BASE_ADDR || log >= IRAM_BASE_ADDR + IRAM_SIZE ||
+	    log & 0x3)
+		return NULL;
+
+	return (const u32 *)log;
+}
+
+/*
+ * If BOOT_CFG4[6] is fused, the boot ROM does not fall back to the serial
+ * downloader right away when the primary boot device fails, but first tries
+ * a recovery boot from the serial ROM selected by BOOT_CFG4[2:0]. Neither
+ * SRC_SBMR1 nor SRC_SBMR2 reflect that this happened, so ask the ROM event
+ * log instead.
+ */
+bool imx6_is_ecspi_recovery_boot(void)
+{
+	const u32 *log = imx6_get_rom_log();
+	u32 event_id;
+	int i;
+
+	if (!log)
+		return false;
+
+	for (i = 0; i < IMX6_ROM_LOG_MAX_ENTRIES; i++) {
+		event_id = log[i] & IMX6_ROM_LOG_ID_MASK;
+
+		switch (event_id) {
+		case 0x000000: /* End of list */
+			return false;
+		/* Recovery boot from ECSPI NOR device */
+		case 0x061004:
+			return true;
+		/* Log entries with 1 parameter, skip 1 */
+		case 0x080000: /* Start to read data from boot device */
+		case 0x090000: /* Image authentication result */
+		case 0x0a0000: /* Start to execute the plugin program */
+		case 0x0b0000: /* Jump to the boot image soon */
+		case 0x0d0000: /* Jump to the SDP boot image soon */
+			i += 1;
+			continue;
+		default:
+			continue;
+		}
+	}
+
+	return false;
+}
 #endif
diff --git a/arch/arm/mach-imx/spl.c b/arch/arm/mach-imx/spl.c
index 1029c1e4e85..fbe5172d385 100644
--- a/arch/arm/mach-imx/spl.c
+++ b/arch/arm/mach-imx/spl.c
@@ -93,6 +93,13 @@ u32 spl_boot_device(void)
 	if (is_usbotg_phy_active())
 		return BOOT_DEVICE_BOARD;
 
+	/*
+	 * Likewise the boot ROM may have done a recovery boot from a serial
+	 * ROM on ECSPI after the primary boot device failed.
+	 */
+	if (imx6_is_ecspi_recovery_boot())
+		return BOOT_DEVICE_SPI;
+
 	/* BOOT_CFG1[7:4] - see IMX6DQRM Table 8-8 */
 	switch ((reg & IMX6_BMODE_MASK) >> IMX6_BMODE_SHIFT) {
 	 /* EIM: See 8.5.1, Table 8-9 */
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.