| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <boot_device.h> |
| #include <console/console.h> |
| #include <soc/alternate_cbfs.h> |
| #include <soc/power.h> |
| #include <soc/spi.h> |
| #include <symbols.h> |
| |
| /* This allows USB A-A firmware upload from a compatible host in four parts: |
| * The first two are the bare BL1 and the coreboot boot block, which are just |
| * written to their respective loading addresses. These transfers are initiated |
| * by the IROM / BL1, so this code has nothing to do with them. |
| * |
| * The third transfer is a valid CBFS image that contains only the romstage, |
| * and must be small enough to fit into the PRE_RAM CBFS cache in |
| * IRAM. It is loaded when this function gets called in the boot block, and |
| * the normal CBFS code extracts the romstage from it. |
| * |
| * The fourth transfer is also a CBFS image, but can be of arbitrary size and |
| * should contain all available stages/payloads/etc. It is loaded when this |
| * function is called a second time at the end of the romstage, and copied to |
| * the romstage/ramstage CBFS cache in DRAM. It will reside there for the |
| * rest of the firmware's lifetime and all subsequent stages can just directly |
| * reference it there. |
| */ |
| static int usb_cbfs_open(void) |
| { |
| if (!ENV_ROMSTAGE_OR_BEFORE) |
| return 0; |
| |
| static int first_run = 1; |
| int (*irom_load_usb)(void) = *irom_load_image_from_usb_ptr; |
| |
| if (!first_run) |
| return 0; |
| |
| if (!irom_load_usb()) { |
| printk(BIOS_EMERG, "Unable to load CBFS image via USB!\n"); |
| return -1; |
| } |
| |
| /* |
| * We need to trust the host/irom to copy the image to our |
| * _cbfs_cache address... there is no way to control or even |
| * check the transfer size or target address from our side. |
| */ |
| |
| printk(BIOS_DEBUG, "USB A-A transfer successful, CBFS image should now" |
| " be at %p\n", _cbfs_cache); |
| first_run = 0; |
| return 0; |
| } |
| |
| /* |
| * SDMMC works very similar to USB A-A: we copy the CBFS image into memory |
| * and read it from there. While SDMMC would also allow direct block by block |
| * on-demand reading, we might run into problems if we call back into the IROM |
| * in very late boot stages (e.g. after initializing/changing MMC clocks)... so |
| * this seems like a safer approach. It also makes it easy to pass our image |
| * down to payloads. |
| */ |
| static int sdmmc_cbfs_open(void) |
| { |
| if (!ENV_ROMSTAGE_OR_BEFORE) |
| return 0; |
| |
| /* |
| * In the bootblock, we just copy the small part that fits in the buffer |
| * and hope that it's enough (since the romstage is currently always the |
| * first component in the image, this should work out). In the romstage, |
| * we copy until our cache is full (currently 12M) to avoid the pain of |
| * figuring out the true image size from in here. Since this is mainly a |
| * developer/debug boot mode, those shortcomings should be bearable. |
| */ |
| const u32 count = REGION_SIZE(cbfs_cache) / 512; |
| static int first_run = 1; |
| int (*irom_load_sdmmc)(u32 start, u32 count, void *dst) = |
| *irom_sdmmc_read_blocks_ptr; |
| |
| if (!first_run) |
| return 0; |
| |
| if (!irom_load_sdmmc(1, count, _cbfs_cache)) { |
| printk(BIOS_EMERG, "Unable to load CBFS image from SDMMC!\n"); |
| return -1; |
| } |
| |
| printk(BIOS_DEBUG, "SDMMC read successful, CBFS image should now be" |
| " at %p\n", _cbfs_cache); |
| first_run = 0; |
| return 0; |
| } |
| |
| static const struct mem_region_device alternate_rdev = |
| MEM_REGION_DEV_RO_INIT(_cbfs_cache, REGION_SIZE(cbfs_cache)); |
| |
| const struct region_device *boot_device_ro(void) |
| { |
| if (*iram_secondary_base == SECONDARY_BASE_BOOT_USB) |
| return &alternate_rdev.rdev; |
| |
| switch (exynos_power->om_stat & OM_STAT_MASK) { |
| case OM_STAT_SDMMC: |
| return &alternate_rdev.rdev; |
| case OM_STAT_SPI: |
| return exynos_spi_boot_device(); |
| default: |
| printk(BIOS_EMERG, "Exynos OM_STAT value 0x%x not supported!\n", |
| exynos_power->om_stat); |
| return NULL; |
| } |
| } |
| |
| void boot_device_init(void) |
| { |
| if (*iram_secondary_base == SECONDARY_BASE_BOOT_USB) { |
| printk(BIOS_DEBUG, "Using Exynos alternate boot mode USB A-A\n"); |
| usb_cbfs_open(); |
| return; |
| } |
| |
| switch (exynos_power->om_stat & OM_STAT_MASK) { |
| case OM_STAT_SDMMC: |
| printk(BIOS_DEBUG, "Using Exynos alternate boot mode SDMMC\n"); |
| sdmmc_cbfs_open(); |
| break; |
| case OM_STAT_SPI: |
| exynos_init_spi_boot_device(); |
| break; |
| default: |
| printk(BIOS_EMERG, "Exynos OM_STAT value 0x%x not supported!\n", |
| exynos_power->om_stat); |
| } |
| } |