summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/boot.c')
-rw-r--r--arch/arm/mach-imx/boot.c220
1 files changed, 176 insertions, 44 deletions
diff --git a/arch/arm/mach-imx/boot.c b/arch/arm/mach-imx/boot.c
index 7bce1c710c..3fea22d05f 100644
--- a/arch/arm/mach-imx/boot.c
+++ b/arch/arm/mach-imx/boot.c
@@ -1,15 +1,4 @@
-/*
- * 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-or-later
#include <common.h>
#include <bootsource.h>
@@ -19,19 +8,23 @@
#include <magicvar.h>
#include <io.h>
-#include <mach/generic.h>
-#include <mach/imx25-regs.h>
-#include <mach/imx27-regs.h>
-#include <mach/imx35-regs.h>
-#include <mach/imx51-regs.h>
-#include <mach/imx53-regs.h>
-#include <mach/imx6-regs.h>
-#include <mach/imx7-regs.h>
-#include <mach/imx8mm-regs.h>
-#include <mach/imx8mq-regs.h>
-#include <mach/vf610-regs.h>
-#include <mach/imx8mq.h>
-
+#include <mach/imx/clock-imx6.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/imx25-regs.h>
+#include <mach/imx/imx27-regs.h>
+#include <mach/imx/imx35-regs.h>
+#include <mach/imx/imx51-regs.h>
+#include <mach/imx/imx53-regs.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/imx7-regs.h>
+#include <mach/imx/imx8mm-regs.h>
+#include <mach/imx/imx8mp-regs.h>
+#include <mach/imx/imx8mq-regs.h>
+#include <mach/imx/vf610-regs.h>
+#include <mach/imx/imx8mq.h>
+#include <mach/imx/imx6.h>
+
+#include <soc/fsl/fsl_udc.h>
static void
imx_boot_save_loc(void (*get_boot_source)(enum bootsource *, int *))
@@ -41,8 +34,7 @@ imx_boot_save_loc(void (*get_boot_source)(enum bootsource *, int *))
get_boot_source(&src, &instance);
- bootsource_set(src);
- bootsource_set_instance(instance);
+ bootsource_set(src, instance);
}
@@ -210,7 +202,8 @@ void imx51_boot_save_loc(void)
}
#define IMX53_SRC_SBMR 0x4
-#define SRC_SBMR_BMOD GENMASK(25, 24)
+#define IMX53_SRC_SBMR_BMOD GENMASK(25, 24)
+#define IMX8MP_SRC_SBMR_BMOD GENMASK(27, 24)
#define IMX53_BMOD_SERIAL 0b11
#define __BOOT_CFG(n, m, l) GENMASK((m) + ((n) - 1) * 8, \
@@ -242,7 +235,17 @@ __MAKE_BOOT_CFG_BITS(4)
static unsigned int imx53_get_bmod(uint32_t r)
{
- return FIELD_GET(SRC_SBMR_BMOD, r);
+ return FIELD_GET(IMX53_SRC_SBMR_BMOD, r);
+}
+
+static unsigned int imx8mp_get_bmod(uint32_t r)
+{
+ return FIELD_GET(IMX8MP_SRC_SBMR_BMOD, r);
+}
+
+static unsigned int imx8mm_get_bcfg(uint32_t r)
+{
+ return FIELD_GET(BOOT_CFG2(6, 4), r);
}
static int imx53_bootsource_internal(uint32_t r)
@@ -292,6 +295,8 @@ void imx53_get_boot_source(enum bootsource *src, int *instance)
default:
if (imx53_bootsource_nand(cfg1))
*src = BOOTSOURCE_NAND;
+ else
+ *src = BOOTSOURCE_UNKNOWN;
break;
}
@@ -314,16 +319,21 @@ void imx53_boot_save_loc(void)
imx53_get_boot_source(&src, &instance);
- bootsource_set(src);
- bootsource_set_instance(instance);
+ bootsource_set(src, instance);
}
#define IMX6_SRC_SBMR1 0x04
#define IMX6_SRC_SBMR2 0x1c
+#define IMX6_SRC_GPR9 0x40
+#define IMX6_SRC_GPR10 0x44
#define IMX6_BMOD_SERIAL 0b01
#define IMX6_BMOD_RESERVED 0b11
+#define IMX8MM_BCFG_FSPI 0b100
+#define IMX8MP_BMOD_FUSES 0b0000
+#define IMX8MP_BMOD_SERIAL 0b0001
#define IMX6_BMOD_FUSES 0b00
#define BT_FUSE_SEL BIT(4)
+#define GPR10_BOOT_FROM_GPR9 BIT(28)
static bool imx6_bootsource_reserved(uint32_t sbmr2)
{
@@ -342,6 +352,30 @@ static bool imx6_bootsource_serial(uint32_t sbmr2)
!(sbmr2 & BT_FUSE_SEL));
}
+static bool imx8mp_bootsource_serial(uint32_t sbmr2)
+{
+ return imx8mp_get_bmod(sbmr2) == IMX8MP_BMOD_SERIAL ||
+ /*
+ * If boot from fuses is selected and fuses are not
+ * programmed by setting BT_FUSE_SEL, ROM code will
+ * fallback to serial mode
+ */
+ (imx8mp_get_bmod(sbmr2) == IMX8MP_BMOD_FUSES &&
+ !(sbmr2 & BT_FUSE_SEL));
+}
+
+static bool imx8mm_bootsource_qspi(uint32_t sbmr1)
+{
+ return imx8mm_get_bcfg(sbmr1) == IMX8MM_BCFG_FSPI;
+}
+
+static bool imx6_bootsource_serial_forced(uint32_t bootmode)
+{
+ if (cpu_mx6_is_mx6ul() || cpu_mx6_is_mx6ull())
+ return bootmode == 2;
+ return bootmode == 1;
+}
+
static int __imx6_bootsource_serial_rom(uint32_t r)
{
return FIELD_GET(BOOT_CFG4(2, 0), r);
@@ -388,38 +422,81 @@ static int imx6_boot_instance_mmc(uint32_t r)
return FIELD_GET(BOOT_CFG2(4, 3), r);
}
+static u32 imx6_get_src_boot_mode(void __iomem *src_base)
+{
+ if (readl(src_base + IMX6_SRC_GPR10) & GPR10_BOOT_FROM_GPR9)
+ return readl(src_base + IMX6_SRC_GPR9);
+
+ return readl(src_base + IMX6_SRC_SBMR1);
+}
+
+static inline bool imx6_usboh3_clk_active(void)
+{
+ return (readl(MXC_CCM_CCGR6) & 0x3) == 0x3;
+}
+
void imx6_get_boot_source(enum bootsource *src, int *instance)
{
void __iomem *src_base = IOMEM(MX6_SRC_BASE_ADDR);
- uint32_t sbmr1 = readl(src_base + IMX6_SRC_SBMR1);
uint32_t sbmr2 = readl(src_base + IMX6_SRC_SBMR2);
+ uint32_t bootmode, bootsrc;
+
+ bootmode = imx6_get_src_boot_mode(src_base);
if (imx6_bootsource_reserved(sbmr2))
return;
- if (imx6_bootsource_serial(sbmr2)) {
+ bootsrc = imx53_bootsource_internal(bootmode);
+
+ /*
+ * imx6_bootsource_serial() can't detect cases where the boot ROM
+ * decided to use the serial downloader as a fall back (primary
+ * boot source failed).
+ *
+ * Infer that the boot ROM used the USB serial downloader by
+ * checking whether both the UDC and the clock enabling access
+ * to its MMIO region are currently active...
+ * This assumes:
+ * - On fresh boots, PBL doesn't itself start a stopped UDC
+ * - In barebox proper, boot source is saved before the UDC driver
+ * may enable the UDC
+ */
+
+ if (imx6_usboh3_clk_active() &&
+ is_chipidea_udc_running(IOMEM(MX6_OTG_BASE_ADDR))) {
*src = BOOTSOURCE_SERIAL;
return;
}
- switch (imx53_bootsource_internal(sbmr1)) {
- case 2:
+ if (imx6_bootsource_serial(sbmr2) ||
+ imx6_bootsource_serial_forced(bootsrc)) {
+ *src = BOOTSOURCE_SERIAL;
+ return;
+ }
+
+ switch (bootsrc) {
+ case 1: /* only reachable for i.MX6UL(L) */
+ *src = BOOTSOURCE_SPI; /* Really: qspi */
+ return;
+ case 2: /* unreachable for i.MX6UL(L) */
*src = BOOTSOURCE_HD;
break;
case 3:
- *src = imx6_bootsource_serial_rom(sbmr1);
- *instance = imx6_boot_instance_serial_rom(sbmr1);
+ *src = imx6_bootsource_serial_rom(bootmode);
+ *instance = imx6_boot_instance_serial_rom(bootmode);
break;
case 4:
case 5:
case 6:
case 7:
*src = BOOTSOURCE_MMC;
- *instance = imx6_boot_instance_mmc(sbmr1);
+ *instance = imx6_boot_instance_mmc(bootmode);
break;
default:
- if (imx53_bootsource_nand(sbmr1))
+ if (imx53_bootsource_nand(bootmode))
*src = BOOTSOURCE_NAND;
+ else
+ *src = BOOTSOURCE_UNKNOWN;
break;
}
}
@@ -451,11 +528,6 @@ static void __imx7_get_boot_source(enum bootsource *src, int *instance,
{
const struct imx_boot_sw_info *info;
- if (imx6_bootsource_serial(sbmr2)) {
- *src = BOOTSOURCE_SERIAL;
- return;
- }
-
info = (const void *)(unsigned long)
readl(boot_sw_info_pointer_addr);
@@ -474,11 +546,17 @@ static void __imx7_get_boot_source(enum bootsource *src, int *instance,
break;
case 4:
*src = BOOTSOURCE_SPI; /* Really: qspi */
+ *instance = info->boot_device_instance;
break;
case 5:
*src = BOOTSOURCE_NOR;
break;
+ case 14: /* observed on i.MX8MP for USB "serial" booting */
+ case 15: /* observed on i.MX8MM for USB "serial" booting */
+ *src = BOOTSOURCE_SERIAL;
+ break;
default:
+ *src = BOOTSOURCE_UNKNOWN;
break;
}
}
@@ -488,6 +566,11 @@ void imx7_get_boot_source(enum bootsource *src, int *instance)
void __iomem *src_base = IOMEM(MX7_SRC_BASE_ADDR);
uint32_t sbmr2 = readl(src_base + 0x70);
+ if (imx6_bootsource_serial(sbmr2)) {
+ *src = BOOTSOURCE_SERIAL;
+ return;
+ }
+
__imx7_get_boot_source(src, instance, IMX7_BOOT_SW_INFO_POINTER_ADDR,
sbmr2);
}
@@ -582,6 +665,8 @@ void vf610_get_boot_source(enum bootsource *src, int *instance)
default:
if (imx53_bootsource_nand(sbmr1))
*src = BOOTSOURCE_NAND;
+ else
+ *src = BOOTSOURCE_UNKNOWN;
break;
}
}
@@ -601,6 +686,11 @@ void imx8mq_get_boot_source(enum bootsource *src, int *instance)
IMX8M_BOOT_SW_INFO_POINTER_ADDR_A0 :
IMX8M_BOOT_SW_INFO_POINTER_ADDR_B0;
+ if (imx6_bootsource_serial(sbmr2)) {
+ *src = BOOTSOURCE_SERIAL;
+ return;
+ }
+
__imx7_get_boot_source(src, instance, addr, sbmr2);
}
@@ -613,14 +703,56 @@ void imx8mm_get_boot_source(enum bootsource *src, int *instance)
{
unsigned long addr;
void __iomem *src_base = IOMEM(MX8MM_SRC_BASE_ADDR);
+ uint32_t sbmr1 = readl(src_base + 0x58);
uint32_t sbmr2 = readl(src_base + 0x70);
+ if (imx6_bootsource_serial(sbmr2)) {
+ *src = BOOTSOURCE_SERIAL;
+ return;
+ }
+
addr = IMX8M_BOOT_SW_INFO_POINTER_ADDR_A0;
__imx7_get_boot_source(src, instance, addr, sbmr2);
+
+ if (*src != BOOTSOURCE_UNKNOWN)
+ return;
+
+ if (imx8mm_bootsource_qspi(sbmr1)) {
+ *src = BOOTSOURCE_SPI; /* Really: qspi */
+ *instance = 0;
+ return;
+ }
}
void imx8mm_boot_save_loc(void)
{
imx_boot_save_loc(imx8mm_get_boot_source);
}
+
+void imx8mp_get_boot_source(enum bootsource *src, int *instance)
+{
+ unsigned long addr;
+ void __iomem *src_base = IOMEM(MX8MP_SRC_BASE_ADDR);
+ uint32_t sbmr2 = readl(src_base + 0x70);
+
+ if (imx8mp_bootsource_serial(sbmr2)) {
+ *src = BOOTSOURCE_SERIAL;
+ return;
+ }
+
+ addr = IMX8M_BOOT_SW_INFO_POINTER_ADDR_A0;
+
+ __imx7_get_boot_source(src, instance, addr, sbmr2);
+}
+
+void imx8mp_boot_save_loc(void)
+{
+ imx_boot_save_loc(imx8mp_get_boot_source);
+}
+
+void imx8mn_get_boot_source(enum bootsource *src, int *instance)
+ __alias(imx8mp_get_boot_source);
+
+void imx8mn_boot_save_loc(void)
+ __alias(imx8mp_boot_save_loc);