diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e288d47ed..c327e71d2 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -105,6 +105,36 @@ static int mmc_device_state(void) return MMC_GET_STATE(resp_data[0]); } +static int mmc_send_part_switch_cmd(unsigned int part_config) +{ + int ret; + unsigned int part_time = 0; + + ret = mmc_send_cmd(MMC_CMD(6), + EXTCSD_WRITE_BYTES | + EXTCSD_CMD(CMD_EXTCSD_PARTITION_CONFIG) | + EXTCSD_VALUE(part_config) | + EXTCSD_CMD_SET_NORMAL, + MMC_RESPONSE_R1B, NULL); + if (ret != 0) { + return ret; + } + + /* Partition switch timing is in 10ms units */ + part_time = mmc_ext_csd[CMD_EXTCSD_PART_SWITCH_TIME] * 10; + + mdelay(part_time); + + do { + ret = mmc_device_state(); + if (ret < 0) { + return ret; + } + } while (ret == MMC_STATE_PRG); + + return 0; +} + static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value) { int ret; @@ -668,7 +698,7 @@ static inline void mmc_rpmb_enable(void) { mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG, PART_CFG_BOOT_PARTITION1_ENABLE | - PART_CFG_PARTITION1_ACCESS); + PART_CFG_BOOT_PARTITION1_ACCESS); } static inline void mmc_rpmb_disable(void) @@ -710,6 +740,50 @@ size_t mmc_rpmb_erase_blocks(int lba, size_t size) return size_erased; } +static int mmc_part_switch(unsigned int part_type) +{ + uint8_t part_config = mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG]; + + part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; + part_config |= part_type; + + return mmc_send_part_switch_cmd(part_config); +} + +static unsigned char mmc_current_boot_part(void) +{ + return PART_CFG_CURRENT_BOOT_PARTITION(mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG]); +} + +size_t mmc_boot_part_read_blocks(int lba, uintptr_t buf, size_t size) +{ + size_t size_read; + int ret; + unsigned char current_boot_part = mmc_current_boot_part(); + + if (current_boot_part != 1U && + current_boot_part != 2U) { + ERROR("Got unexpected value for active boot partition, %u\n", current_boot_part); + return 0; + } + + ret = mmc_part_switch(current_boot_part); + if (ret < 0) { + ERROR("Failed to switch to boot partition, %d\n", ret); + return 0; + } + + size_read = mmc_read_blocks(lba, buf, size); + + ret = mmc_part_switch(0); + if (ret < 0) { + ERROR("Failed to switch back to user partition, %d\n", ret); + return 0; + } + + return size_read; +} + int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk, unsigned int width, unsigned int flags, struct mmc_device_info *device_info) diff --git a/drivers/st/io/io_mmc.c b/drivers/st/io/io_mmc.c index 0ed71540c..2bf88e6f7 100644 --- a/drivers/st/io/io_mmc.c +++ b/drivers/st/io/io_mmc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -29,6 +29,7 @@ static int mmc_dev_close(io_dev_info_t *dev_info); static io_type_t device_type_mmc(void); static signed long long seek_offset; +static size_t (*_read_blocks)(int lba, uintptr_t buf, size_t size); static const io_dev_connector_t mmc_dev_connector = { .dev_open = mmc_dev_open @@ -60,9 +61,15 @@ static io_type_t device_type_mmc(void) /* Open a connection to the mmc device */ static int mmc_dev_open(const uintptr_t init_params, io_dev_info_t **dev_info) { + struct io_mmc_dev_spec *device_spec = + (struct io_mmc_dev_spec *)init_params; + assert(dev_info != NULL); *dev_info = (io_dev_info_t *)&mmc_dev_info; + _read_blocks = !device_spec->use_boot_part ? + mmc_read_blocks : mmc_boot_part_read_blocks; + return 0; } @@ -100,8 +107,8 @@ static int mmc_block_read(io_entity_t *entity, uintptr_t buffer, uint8_t retries; for (retries = 0U; retries < 3U; retries++) { - *length_read = mmc_read_blocks(seek_offset / MMC_BLOCK_SIZE, - buffer, length); + *length_read = _read_blocks(seek_offset / MMC_BLOCK_SIZE, + buffer, length); if (*length_read == length) { return 0; diff --git a/include/drivers/mmc.h b/include/drivers/mmc.h index 7611f019a..834a80f4a 100644 --- a/include/drivers/mmc.h +++ b/include/drivers/mmc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -60,10 +60,16 @@ #define CMD_EXTCSD_PARTITION_CONFIG 179 #define CMD_EXTCSD_BUS_WIDTH 183 #define CMD_EXTCSD_HS_TIMING 185 +#define CMD_EXTCSD_PART_SWITCH_TIME 199 #define CMD_EXTCSD_SEC_CNT 212 +#define EXT_CSD_PART_CONFIG_ACC_MASK GENMASK(2, 0) #define PART_CFG_BOOT_PARTITION1_ENABLE (U(1) << 3) -#define PART_CFG_PARTITION1_ACCESS (U(1) << 0) +#define PART_CFG_BOOT_PARTITION1_ACCESS (U(1) << 0) +#define PART_CFG_BOOT_PART_EN_MASK GENMASK(5, 3) +#define PART_CFG_BOOT_PART_EN_SHIFT 3 +#define PART_CFG_CURRENT_BOOT_PARTITION(x) (((x) & PART_CFG_BOOT_PART_EN_MASK) >> \ + PART_CFG_BOOT_PART_EN_SHIFT) /* Values in EXT CSD register */ #define MMC_BUS_WIDTH_1 U(0) @@ -230,6 +236,7 @@ size_t mmc_erase_blocks(int lba, size_t size); size_t mmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size); size_t mmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size); size_t mmc_rpmb_erase_blocks(int lba, size_t size); +size_t mmc_boot_part_read_blocks(int lba, uintptr_t buf, size_t size); int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk, unsigned int width, unsigned int flags, struct mmc_device_info *device_info); diff --git a/include/drivers/st/io_mmc.h b/include/drivers/st/io_mmc.h index b35b4b5d0..6179e89e2 100644 --- a/include/drivers/st/io_mmc.h +++ b/include/drivers/st/io_mmc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,6 +9,10 @@ #include +struct io_mmc_dev_spec { + bool use_boot_part; +}; + int register_io_dev_mmc(const io_dev_connector_t **dev_con); #endif /* IO_MMC_H */ diff --git a/plat/st/common/bl2_io_storage.c b/plat/st/common/bl2_io_storage.c index a25643b02..0a18d991e 100644 --- a/plat/st/common/bl2_io_storage.c +++ b/plat/st/common/bl2_io_storage.c @@ -62,6 +62,30 @@ static const io_block_dev_spec_t mmc_block_dev_spec = { .block_size = MMC_BLOCK_SIZE, }; +#if STM32MP_EMMC_BOOT +static io_block_spec_t emmc_boot_ssbl_block_spec = { + .offset = PLAT_EMMC_BOOT_SSBL_OFFSET, + .length = MMC_BLOCK_SIZE, /* We are interested only in first 4 bytes */ +}; + +static const io_block_dev_spec_t mmc_block_dev_boot_part_spec = { + /* It's used as temp buffer in block driver */ + .buffer = { + .offset = (size_t)&block_buffer, + .length = MMC_BLOCK_SIZE, + }, + .ops = { + .read = mmc_boot_part_read_blocks, + .write = NULL, + }, + .block_size = MMC_BLOCK_SIZE, +}; +#endif + +static struct io_mmc_dev_spec mmc_device_spec = { + .use_boot_part = false, +}; + static const io_dev_connector_t *mmc_dev_con; #endif /* STM32MP_SDMMC || STM32MP_EMMC */ @@ -236,6 +260,38 @@ static int open_storage(const uintptr_t spec) return io_dev_init(storage_dev_handle, 0); } +#if STM32MP_EMMC_BOOT +static uint32_t get_boot_part_ssbl_header(void) +{ + uint32_t magic = 0; + int io_result; + size_t bytes_read; + + io_result = register_io_dev_block(&mmc_dev_con); + if (io_result != 0) { + panic(); + } + + io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_block_dev_boot_part_spec, + &storage_dev_handle); + assert(io_result == 0); + + io_result = io_open(storage_dev_handle, (uintptr_t) &emmc_boot_ssbl_block_spec, + &image_dev_handle); + assert(io_result == 0); + + io_result = io_read(image_dev_handle, (uintptr_t) &magic, sizeof(magic), + &bytes_read); + assert(io_result == 0); + assert(bytes_read == sizeof(magic)); + + io_result = io_dev_close(storage_dev_handle); + assert(io_result == 0); + + return magic; +} +#endif + static void print_boot_device(boot_api_context_t *boot_context) { switch (boot_context->boot_interface_selected) { @@ -273,7 +329,8 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, uint8_t idx; struct stm32image_part_info *part; struct stm32_sdmmc2_params params; - const partition_entry_t *entry; + const partition_entry_t *entry __unused; + uint32_t magic __unused; zeromem(¶ms, sizeof(struct stm32_sdmmc2_params)); @@ -305,6 +362,26 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, panic(); } + stm32image_dev_info_spec.device_size = + stm32_sdmmc2_mmc_get_device_size(); + +#if STM32MP_EMMC_BOOT + magic = get_boot_part_ssbl_header(); + + if (magic == BOOT_API_IMAGE_HEADER_MAGIC_NB) { + VERBOSE("%s, header found, jump to emmc load\n", __func__); + idx = IMG_IDX_BL33; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = PLAT_EMMC_BOOT_SSBL_OFFSET; + part->bkp_offset = 0U; + mmc_device_spec.use_boot_part = true; + + goto emmc_boot; + } else { + WARN("%s: Can't find STM32 header on a boot partition\n", __func__); + } +#endif + /* Open MMC as a block device to read GPT table */ io_result = register_io_dev_block(&mmc_dev_con); if (io_result != 0) { @@ -320,9 +397,6 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, io_result = io_dev_close(storage_dev_handle); assert(io_result == 0); - stm32image_dev_info_spec.device_size = - stm32_sdmmc2_mmc_get_device_size(); - for (idx = 0U; idx < IMG_IDX_NUM; idx++) { part = &stm32image_dev_info_spec.part_info[idx]; entry = get_partition_entry(part->name); @@ -335,6 +409,9 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, part->bkp_offset = 0U; } +#if STM32MP_EMMC_BOOT +emmc_boot: +#endif /* * Re-open MMC with io_mmc, for better perfs compared to * io_block. @@ -342,7 +419,8 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, io_result = register_io_dev_mmc(&mmc_dev_con); assert(io_result == 0); - io_result = io_dev_open(mmc_dev_con, 0, &storage_dev_handle); + io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_device_spec, + &storage_dev_handle); assert(io_result == 0); io_result = register_io_dev_stm32image(&stm32image_dev_con); diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h index 9a2f54a14..2d7d36945 100644 --- a/plat/st/stm32mp1/include/platform_def.h +++ b/plat/st/stm32mp1/include/platform_def.h @@ -88,6 +88,12 @@ */ #define PLAT_STM32MP_NS_IMAGE_OFFSET BL33_BASE +/* + * SSBL offset in case it's stored in eMMC boot partition. + * We can fix it to 256K because TF-A size can't be bigger than SRAM + */ +#define PLAT_EMMC_BOOT_SSBL_OFFSET U(0x40000) + /******************************************************************************* * DTB specific defines. ******************************************************************************/ diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index 50fb1b77b..128dbc4ac 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -42,6 +42,7 @@ STM32MP_SDMMC ?= 0 STM32MP_RAW_NAND ?= 0 STM32MP_SPI_NAND ?= 0 STM32MP_SPI_NOR ?= 0 +STM32MP_EMMC_BOOT ?= 0 ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC} ${STM32MP_RAW_NAND} \ ${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),) @@ -77,6 +78,7 @@ $(eval $(call assert_booleans,\ STM32MP_RAW_NAND \ STM32MP_SPI_NAND \ STM32MP_SPI_NOR \ + STM32MP_EMMC_BOOT \ PLAT_XLAT_TABLES_DYNAMIC \ ))) @@ -93,6 +95,7 @@ $(eval $(call add_defines,\ STM32MP_RAW_NAND \ STM32MP_SPI_NAND \ STM32MP_SPI_NOR \ + STM32MP_EMMC_BOOT \ PLAT_XLAT_TABLES_DYNAMIC \ STM32_TF_A_COPIES \ PLAT_PARTITION_MAX_ENTRIES \