diff options
Diffstat (limited to 'drivers/mmc/mmc.c')
-rw-r--r-- | drivers/mmc/mmc.c | 82 |
1 files changed, 78 insertions, 4 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b5f6a10d3..c327e71d2 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -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; @@ -392,7 +422,7 @@ static int mmc_send_op_cond(void) ret = mmc_reset_to_idle(); if (ret != 0) { return ret; - }; + } for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) { ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE | @@ -425,7 +455,7 @@ static int mmc_enumerate(unsigned int clk, unsigned int bus_width) ret = mmc_reset_to_idle(); if (ret != 0) { return ret; - }; + } if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { ret = mmc_send_op_cond(); @@ -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) |