/* * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* [CTRL][TYPE] */ static const enum bootsource locations[4][4] = { { /* CTRL = WEIM */ BOOTSOURCE_NOR, BOOTSOURCE_UNKNOWN, BOOTSOURCE_ONENAND, BOOTSOURCE_UNKNOWN, }, { /* CTRL == NAND */ BOOTSOURCE_NAND, BOOTSOURCE_NAND, BOOTSOURCE_NAND, BOOTSOURCE_NAND, }, { /* CTRL == ATA, (imx35 only) */ BOOTSOURCE_UNKNOWN, BOOTSOURCE_UNKNOWN, /* might be p-ata */ BOOTSOURCE_UNKNOWN, BOOTSOURCE_UNKNOWN, }, { /* CTRL == expansion */ BOOTSOURCE_MMC, /* note imx25 could also be: movinand, ce-ata */ BOOTSOURCE_UNKNOWN, BOOTSOURCE_I2C, BOOTSOURCE_SPI, } }; /* * Saves the boot source media into the $bootsource environment variable * * This information is useful for barebox init scripts as we can then easily * use a kernel image stored on the same media that we launch barebox with * (for example). * * imx25 and imx35 can boot into barebox from several media such as * nand, nor, mmc/sd cards, serial roms. "mmc" is used to represent several * sources as its impossible to distinguish between them. * * Some sources such as serial roms can themselves have 3 different boot * possibilities (i2c1, i2c2 etc). It is assumed that any board will * only be using one of these at any one time. * * Note also that I suspect that the boot source pins are only sampled at * power up. */ static enum bootsource imx25_35_boot_source(unsigned int ctrl, unsigned int type) { enum bootsource src; src = locations[ctrl][type]; return src; } void imx25_get_boot_source(enum bootsource *src, int *instance) { void __iomem *ccm_base = IOMEM(MX25_CCM_BASE_ADDR); uint32_t val; val = readl(ccm_base + MX25_CCM_RCSR); *src = imx25_35_boot_source((val >> MX25_CCM_RCSR_MEM_CTRL_SHIFT) & 0x3, (val >> MX25_CCM_RCSR_MEM_TYPE_SHIFT) & 0x3); } void imx25_boot_save_loc(void) { enum bootsource src = BOOTSOURCE_UNKNOWN; int instance = BOOTSOURCE_INSTANCE_UNKNOWN; imx25_get_boot_source(&src, &instance); bootsource_set(src); bootsource_set_instance(instance); } void imx35_get_boot_source(enum bootsource *src, int *instance) { void __iomem *ccm_base = IOMEM(MX35_CCM_BASE_ADDR); uint32_t val; val = readl(ccm_base + MX35_CCM_RCSR); *src = imx25_35_boot_source((val >> MX35_CCM_RCSR_MEM_CTRL_SHIFT) & 0x3, (val >> MX35_CCM_RCSR_MEM_TYPE_SHIFT) & 0x3); } void imx35_boot_save_loc(void) { enum bootsource src = BOOTSOURCE_UNKNOWN; int instance = BOOTSOURCE_INSTANCE_UNKNOWN; imx35_get_boot_source(&src, &instance); bootsource_set(src); bootsource_set_instance(instance); } #define IMX27_SYSCTRL_GPCR 0x18 #define IMX27_GPCR_BOOT_SHIFT 16 #define IMX27_GPCR_BOOT_MASK (0xf << IMX27_GPCR_BOOT_SHIFT) #define IMX27_GPCR_BOOT_UART_USB 0 #define IMX27_GPCR_BOOT_8BIT_NAND_2k 2 #define IMX27_GPCR_BOOT_16BIT_NAND_2k 3 #define IMX27_GPCR_BOOT_16BIT_NAND_512 4 #define IMX27_GPCR_BOOT_16BIT_CS0 5 #define IMX27_GPCR_BOOT_32BIT_CS0 6 #define IMX27_GPCR_BOOT_8BIT_NAND_512 7 void imx27_get_boot_source(enum bootsource *src, int *instance) { void __iomem *sysctrl_base = IOMEM(MX27_SYSCTRL_BASE_ADDR); uint32_t val; val = readl(sysctrl_base + IMX27_SYSCTRL_GPCR); val &= IMX27_GPCR_BOOT_MASK; val >>= IMX27_GPCR_BOOT_SHIFT; switch (val) { case IMX27_GPCR_BOOT_UART_USB: *src = BOOTSOURCE_SERIAL; break; case IMX27_GPCR_BOOT_8BIT_NAND_2k: case IMX27_GPCR_BOOT_16BIT_NAND_2k: case IMX27_GPCR_BOOT_16BIT_NAND_512: case IMX27_GPCR_BOOT_8BIT_NAND_512: *src = BOOTSOURCE_NAND; break; default: *src = BOOTSOURCE_NOR; break; } } void imx27_boot_save_loc(void) { enum bootsource src = BOOTSOURCE_UNKNOWN; int instance = BOOTSOURCE_INSTANCE_UNKNOWN; imx27_get_boot_source(&src, &instance); bootsource_set(src); bootsource_set_instance(instance); } #define IMX51_SRC_SBMR 0x4 #define IMX51_SBMR_BT_MEM_TYPE_SHIFT 7 #define IMX51_SBMR_BT_MEM_CTL_SHIFT 0 #define IMX51_SBMR_BMOD_SHIFT 14 void imx51_get_boot_source(enum bootsource *src, int *instance) { void __iomem *src_base = IOMEM(MX51_SRC_BASE_ADDR); uint32_t reg; unsigned int ctrl, type; reg = readl(src_base + IMX51_SRC_SBMR); switch ((reg >> IMX51_SBMR_BMOD_SHIFT) & 0x3) { case 0: case 2: /* internal boot */ ctrl = (reg >> IMX51_SBMR_BT_MEM_CTL_SHIFT) & 0x3; type = (reg >> IMX51_SBMR_BT_MEM_TYPE_SHIFT) & 0x3; *src = locations[ctrl][type]; break; case 1: /* reserved */ *src = BOOTSOURCE_UNKNOWN; break; case 3: *src = BOOTSOURCE_SERIAL; break; } } void imx51_boot_save_loc(void) { enum bootsource src = BOOTSOURCE_UNKNOWN; int instance = BOOTSOURCE_INSTANCE_UNKNOWN; imx51_get_boot_source(&src, &instance); bootsource_set(src); bootsource_set_instance(instance); } #define IMX53_SRC_SBMR 0x4 #define SRC_SBMR_BMOD GENMASK(25, 24) static unsigned int imx53_get_bmod(uint32_t r) { return FIELD_GET(SRC_SBMR_BMOD, r); } void imx53_get_boot_source(enum bootsource *src, int *instance) { void __iomem *src_base = IOMEM(MX53_SRC_BASE_ADDR); uint32_t cfg1 = readl(src_base + IMX53_SRC_SBMR); if (imx53_get_bmod(cfg1) == 0x3) { *src = BOOTSOURCE_USB; *instance = 0; return; } switch ((cfg1 & 0xff) >> 4) { case 2: *src = BOOTSOURCE_HD; break; case 3: if (cfg1 & (1 << 3)) *src = BOOTSOURCE_SPI; else *src = BOOTSOURCE_I2C; break; case 4: case 5: case 6: case 7: *src = BOOTSOURCE_MMC; break; default: break; } if (cfg1 & (1 << 7)) *src = BOOTSOURCE_NAND; switch (*src) { case BOOTSOURCE_MMC: case BOOTSOURCE_SPI: case BOOTSOURCE_I2C: *instance = (cfg1 >> 20) & 0x3; break; default: *instance = 0; break; } } void imx53_boot_save_loc(void) { enum bootsource src = BOOTSOURCE_UNKNOWN; int instance = BOOTSOURCE_INSTANCE_UNKNOWN; imx53_get_boot_source(&src, &instance); bootsource_set(src); bootsource_set_instance(instance); } #define IMX6_SRC_SBMR1 0x04 #define IMX6_SRC_SBMR2 0x1c #define IMX6_BMOD_SERIAL 0b01 #define IMX6_BMOD_RESERVED 0b11 #define IMX6_BMOD_FUSES 0b00 #define BT_FUSE_SEL BIT(4) static bool imx6_bootsource_reserved(uint32_t sbmr2) { return imx53_get_bmod(sbmr2) == IMX6_BMOD_RESERVED; } static bool imx6_bootsource_serial(uint32_t sbmr2) { return imx53_get_bmod(sbmr2) == IMX6_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 */ (imx53_get_bmod(sbmr2) == IMX6_BMOD_FUSES && !(sbmr2 & BT_FUSE_SEL)); } 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 boot_cfg_4_2_0; if (imx6_bootsource_reserved(sbmr2)) return; if (imx6_bootsource_serial(sbmr2)) { *src = BOOTSOURCE_SERIAL; return; } /* BOOT_CFG1[7:4] */ switch ((sbmr1 >> 4) & 0xf) { case 2: *src = BOOTSOURCE_HD; break; case 3: /* BOOT_CFG4[2:0] */ boot_cfg_4_2_0 = (sbmr1 >> 24) & 0x7; if (boot_cfg_4_2_0 > 4) { *src = BOOTSOURCE_I2C; *instance = boot_cfg_4_2_0 - 5; } else { *src = BOOTSOURCE_SPI; *instance = boot_cfg_4_2_0; } break; case 4: case 5: case 6: case 7: *src = BOOTSOURCE_MMC; /* BOOT_CFG2[4:3] */ *instance = (sbmr1 >> 11) & 0x3; break; default: break; } /* BOOT_CFG1[7:0] */ if (sbmr1 & (1 << 7)) *src = BOOTSOURCE_NAND; return; } void imx6_boot_save_loc(void) { enum bootsource src = BOOTSOURCE_UNKNOWN; int instance = BOOTSOURCE_INSTANCE_UNKNOWN; imx6_get_boot_source(&src, &instance); bootsource_set(src); bootsource_set_instance(instance); } #define IMX7_SRC_SBMR1 0x58 #define IMX7_SRC_SBMR2 0x70 #define IMX_BOOT_SW_INFO_POINTER_ADDR 0x000001E8 #define IMX_BOOT_SW_INFO_BDT_SD 0x1 struct imx_boot_sw_info { uint8_t reserved_1; uint8_t boot_device_instance; uint8_t boot_device_type; uint8_t reserved_2; uint32_t frequency_hz[4]; /* Various frequencies (ARM, AXI, * DDR, etc.). Not used */ uint32_t reserved_3[3]; } __packed; void imx7_get_boot_source(enum bootsource *src, int *instance) { void __iomem *src_base = IOMEM(MX7_SRC_BASE_ADDR); uint32_t sbmr1 = readl(src_base + IMX7_SRC_SBMR1); uint32_t sbmr2 = readl(src_base + IMX7_SRC_SBMR2); if (imx6_bootsource_reserved(sbmr2)) return; if (imx6_bootsource_serial(sbmr2)) { /* * On i.MX7 ROM code will try to bood from uSDHC1 * before entering serial mode. It doesn't seem to be * reflected in the contents of SBMR1 in any way when * that happens, so we check "Boot_SW_Info" structure * (per 6.6.14 Boot information for software) to see * if that really happened. * * FIXME: This behaviour can be inhibited by * DISABLE_SDMMC_MFG, but location of that fuse * doesn't seem to be documented anywhere. Once that * is known it should be taken into account here. */ const struct imx_boot_sw_info *info; info = (const void *)readl(IMX_BOOT_SW_INFO_POINTER_ADDR); if (info->boot_device_type == IMX_BOOT_SW_INFO_BDT_SD) { *src = BOOTSOURCE_MMC; /* * We are expecting to only ever boot from * uSDHC1 here */ WARN_ON(*instance = info->boot_device_instance); return; } *src = BOOTSOURCE_SERIAL; return; } switch ((sbmr1 >> 12) & 0xf) { case 1: case 2: *src = BOOTSOURCE_MMC; *instance = (sbmr1 >> 10 & 0x3); break; case 3: *src = BOOTSOURCE_NAND; break; case 4: *src = BOOTSOURCE_SPI_NOR, *instance = (sbmr1 >> 9 & 0x7); break; case 6: *src = BOOTSOURCE_SPI; /* Really: qspi */ break; case 5: *src = BOOTSOURCE_NOR; break; default: break; } /* BOOT_CFG1[7:0] */ if (sbmr1 & (1 << 7)) *src = BOOTSOURCE_NAND; return; } void imx7_boot_save_loc(void) { enum bootsource src = BOOTSOURCE_UNKNOWN; int instance = BOOTSOURCE_INSTANCE_UNKNOWN; imx7_get_boot_source(&src, &instance); bootsource_set(src); bootsource_set_instance(instance); }