diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:13:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:13:47 +0000 |
commit | 102b0d2daa97dae68d3eed54d8fe37a9cc38a892 (patch) | |
tree | bcf648efac40ca6139842707f0eba5a4496a6dd2 /drivers/brcm/emmc/emmc_pboot_hal_memory_drv.c | |
parent | Initial commit. (diff) | |
download | arm-trusted-firmware-upstream.tar.xz arm-trusted-firmware-upstream.zip |
Adding upstream version 2.8.0+dfsg.upstream/2.8.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/brcm/emmc/emmc_pboot_hal_memory_drv.c')
-rw-r--r-- | drivers/brcm/emmc/emmc_pboot_hal_memory_drv.c | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/drivers/brcm/emmc/emmc_pboot_hal_memory_drv.c b/drivers/brcm/emmc/emmc_pboot_hal_memory_drv.c new file mode 100644 index 0000000..68f93e7 --- /dev/null +++ b/drivers/brcm/emmc/emmc_pboot_hal_memory_drv.c @@ -0,0 +1,621 @@ +/* + * Copyright (c) 2016 - 2020, Broadcom + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <string.h> + +#include <emmc_api.h> +#include <cmn_plat_util.h> + +#define MAX_CMD_RETRY 10 + +#if EMMC_USE_DMA +#define USE_DMA 1 +#else +#define USE_DMA 0 +#endif + +struct emmc_global_buffer emmc_global_buf; +struct emmc_global_buffer *emmc_global_buf_ptr = &emmc_global_buf; + +struct emmc_global_vars emmc_global_vars; +struct emmc_global_vars *emmc_global_vars_ptr = &emmc_global_vars; + +static struct sd_handle *sdio_gethandle(void); +static uint32_t sdio_idle(struct sd_handle *p_sdhandle); + +static uint32_t sdio_read(struct sd_handle *p_sdhandle, + uintptr_t mem_addr, + uintptr_t storage_addr, + size_t storage_size, + size_t bytes_to_read); + +#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE +static uint32_t sdio_write(struct sd_handle *p_sdhandle, + uintptr_t mem_addr, + uintptr_t data_addr, + size_t bytes_to_write); +#endif + +static struct sd_handle *sdio_init(void); +static int32_t bcm_emmc_card_ready_state(struct sd_handle *p_sdhandle); + +static void init_globals(void) +{ + memset((void *)emmc_global_buf_ptr, 0, sizeof(*emmc_global_buf_ptr)); + memset((void *)emmc_global_vars_ptr, 0, sizeof(*emmc_global_vars_ptr)); +} + +/* + * This function is used to change partition + */ +uint32_t emmc_partition_select(uint32_t partition) +{ + int rc; + struct sd_handle *sd_handle = sdio_gethandle(); + + if (sd_handle->device == 0) { + EMMC_TRACE("eMMC init is not done"); + return 0; + } + + switch (partition) { + case EMMC_BOOT_PARTITION1: + rc = set_boot_config(sd_handle, + SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_BOOT1); + EMMC_TRACE( + "Change to Boot Partition 1 result:%d (0 means SD_OK)\n", + rc); + break; + + case EMMC_BOOT_PARTITION2: + rc = set_boot_config(sd_handle, + SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_BOOT2); + EMMC_TRACE( + "Change to Boot Partition 2 result:%d (0 means SD_OK)\n", + rc); + break; + + case EMMC_USE_CURRENT_PARTITION: + rc = SD_OK; + EMMC_TRACE("Stay on current partition"); + break; + + case EMMC_USER_AREA: + default: + rc = set_boot_config(sd_handle, + SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_USER); + EMMC_TRACE("Change to User area result:%d (0 means SD_OK)\n", + rc); + break; + + } + return (rc == SD_OK); +} + +/* + * Initialize emmc controller for eMMC + * Returns 0 on fail condition + */ +uint32_t bcm_emmc_init(bool card_rdy_only) +{ + struct sd_handle *p_sdhandle; + uint32_t result = 0; + + EMMC_TRACE("Enter emmc_controller_init()\n"); + + /* If eMMC is already initialized, skip init */ + if (emmc_global_vars_ptr->init_done) + return 1; + + init_globals(); + + p_sdhandle = sdio_init(); + + if (p_sdhandle == NULL) { + ERROR("eMMC init failed"); + return result; + } + + if (card_rdy_only) { + /* Put the card in Ready state, Not complete init */ + result = bcm_emmc_card_ready_state(p_sdhandle); + return !result; + } + + if (sdio_idle(p_sdhandle) == EMMC_BOOT_OK) { + set_config(p_sdhandle, SD_NORMAL_SPEED, MAX_CMD_RETRY, USE_DMA, + SD_DMA_BOUNDARY_256K, EMMC_BLOCK_SIZE, + EMMC_WFE_RETRY); + + if (!select_blk_sz(p_sdhandle, + p_sdhandle->device->cfg.blockSize)) { + emmc_global_vars_ptr->init_done = 1; + result = 1; + } else { + ERROR("Select Block Size failed\n"); + } + } else { + ERROR("eMMC init failed"); + } + + /* Initialization is failed, so deinit HW setting */ + if (result == 0) + emmc_deinit(); + + return result; +} + +/* + * Function to de-init SDIO controller for eMMC + */ +void emmc_deinit(void) +{ + emmc_global_vars_ptr->init_done = 0; + emmc_global_vars_ptr->sdHandle.card = 0; + emmc_global_vars_ptr->sdHandle.device = 0; +} + +/* + * Read eMMC memory + * Returns read_size + */ +uint32_t emmc_read(uintptr_t mem_addr, uintptr_t storage_addr, + size_t storage_size, size_t bytes_to_read) +{ + struct sd_handle *sd_handle = sdio_gethandle(); + + if (sd_handle->device == 0) { + EMMC_TRACE("eMMC init is not done"); + return 0; + } + + return sdio_read(sdio_gethandle(), mem_addr, storage_addr, + storage_size, bytes_to_read); +} + +#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE +#define EXT_CSD_ERASE_GRP_SIZE 224 + +static int emmc_block_erase(uintptr_t mem_addr, size_t blocks) +{ + struct sd_handle *sd_handle = sdio_gethandle(); + + if (sd_handle->device == 0) { + ERROR("eMMC init is not done"); + return -1; + } + + return erase_card(sdio_gethandle(), mem_addr, blocks); +} + +int emmc_erase(uintptr_t mem_addr, size_t num_of_blocks, uint32_t partition) +{ + int err = 0; + size_t block_count = 0, blocks = 0; + size_t erase_group = 0; + + erase_group = + emmc_global_buf_ptr->u.Ext_CSD_storage[EXT_CSD_ERASE_GRP_SIZE]*1024; + + INFO("eMMC Erase Group Size=0x%lx\n", erase_group); + + emmc_partition_select(partition); + + while (block_count < num_of_blocks) { + blocks = ((num_of_blocks - block_count) > erase_group) ? + erase_group : (num_of_blocks - block_count); + err = emmc_block_erase(mem_addr + block_count, blocks); + if (err) + break; + + block_count += blocks; + } + + if (err == 0) + INFO("eMMC Erase of partition %d successful\n", partition); + else + ERROR("eMMC Erase of partition %d Failed(%i)\n", partition, err); + + return err; +} +#endif + +#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE +/* + * Write to eMMC memory + * Returns written_size + */ +uint32_t emmc_write(uintptr_t mem_addr, uintptr_t data_addr, + size_t bytes_to_write) +{ + struct sd_handle *sd_handle = sdio_gethandle(); + + if (sd_handle->device == 0) { + EMMC_TRACE("eMMC init is not done"); + return 0; + } + + return sdio_write(sd_handle, mem_addr, data_addr, bytes_to_write); +} +#endif + +/* + * Send SDIO Cmd + * Return 0 for pass condition + */ +uint32_t send_sdio_cmd(uint32_t cmdIndex, uint32_t argument, + uint32_t options, struct sd_resp *resp) +{ + struct sd_handle *sd_handle = sdio_gethandle(); + + if (sd_handle->device == 0) { + EMMC_TRACE("eMMC init is not done"); + return 1; + } + + return send_cmd(sd_handle, cmdIndex, argument, options, resp); +} + + +/* + * This function return SDIO handle + */ +struct sd_handle *sdio_gethandle(void) +{ + return &emmc_global_vars_ptr->sdHandle; +} + +/* + * Initialize SDIO controller + */ +struct sd_handle *sdio_init(void) +{ + uint32_t SDIO_base; + struct sd_handle *p_sdhandle = &emmc_global_vars_ptr->sdHandle; + + SDIO_base = EMMC_CTRL_REGS_BASE_ADDR; + + if (SDIO_base == SDIO0_EMMCSDXC_SYSADDR) + EMMC_TRACE(" ---> for SDIO 0 Controller\n\n"); + + memset(p_sdhandle, 0, sizeof(struct sd_handle)); + + p_sdhandle->device = &emmc_global_vars_ptr->sdDevice; + p_sdhandle->card = &emmc_global_vars_ptr->sdCard; + + memset(p_sdhandle->device, 0, sizeof(struct sd_dev)); + memset(p_sdhandle->card, 0, sizeof(struct sd_card_info)); + + if (chal_sd_start((CHAL_HANDLE *) p_sdhandle->device, + SD_PIO_MODE, SDIO_base, SDIO_base) != SD_OK) + return NULL; + + set_config(p_sdhandle, SD_NORMAL_SPEED, MAX_CMD_RETRY, SD_DMA_OFF, + SD_DMA_BOUNDARY_4K, EMMC_BLOCK_SIZE, EMMC_WFE_RETRY); + + return &emmc_global_vars_ptr->sdHandle; +} + +uint32_t sdio_idle(struct sd_handle *p_sdhandle) +{ + reset_card(p_sdhandle); + + SD_US_DELAY(1000); + + if (init_card(p_sdhandle, SD_CARD_DETECT_MMC) != SD_OK) { + reset_card(p_sdhandle); + reset_host_ctrl(p_sdhandle); + return EMMC_BOOT_NO_CARD; + } + + return EMMC_BOOT_OK; +} + +/* + * This function read eMMC + */ +uint32_t sdio_read(struct sd_handle *p_sdhandle, + uintptr_t mem_addr, + uintptr_t storage_addr, + size_t storage_size, size_t bytes_to_read) +{ + uint32_t offset = 0, blockAddr, readLen = 0, rdCount; + uint32_t remSize, manual_copy_size; + uint8_t *outputBuf = (uint8_t *) storage_addr; + const size_t blockSize = p_sdhandle->device->cfg.blockSize; + + VERBOSE("EMMC READ: dst=0x%lx, src=0x%lx, size=0x%lx\n", + storage_addr, mem_addr, bytes_to_read); + + if (storage_size < bytes_to_read) + /* Don't have sufficient storage to complete the operation */ + return 0; + + /* Range check non high capacity memory */ + if ((p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) == 0) { + if (mem_addr > 0x80000000) + return 0; + } + + /* High capacity card use block address mode */ + if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) { + blockAddr = (uint32_t) (mem_addr / blockSize); + offset = (uint32_t) (mem_addr - (blockAddr * blockSize)); + } else { + blockAddr = (uint32_t) (mem_addr / blockSize) * blockSize; + offset = (uint32_t) (mem_addr - blockAddr); + } + + remSize = bytes_to_read; + + rdCount = 0; + + /* Process first unaligned block of MAX_READ_LENGTH */ + if (offset > 0) { + if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf, + blockAddr, SD_MAX_READ_LENGTH)) { + + if (remSize < (blockSize - offset)) { + rdCount += remSize; + manual_copy_size = remSize; + remSize = 0; /* read is done */ + } else { + remSize -= (blockSize - offset); + rdCount += (blockSize - offset); + manual_copy_size = blockSize - offset; + } + + /* Check for overflow */ + if (manual_copy_size > storage_size || + (((uintptr_t)outputBuf + manual_copy_size) > + (storage_addr + storage_size))) { + ERROR("EMMC READ: Overflow 1\n"); + return 0; + } + + memcpy(outputBuf, + (void *)((uintptr_t) + (emmc_global_buf_ptr->u.tempbuf + offset)), + manual_copy_size); + + /* Update Physical address */ + outputBuf += manual_copy_size; + + if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) + blockAddr++; + else + blockAddr += blockSize; + } else { + return 0; + } + } + + while (remSize >= blockSize) { + + if (remSize >= SD_MAX_BLK_TRANSFER_LENGTH) + readLen = SD_MAX_BLK_TRANSFER_LENGTH; + else + readLen = (remSize / blockSize) * blockSize; + + /* Check for overflow */ + if ((rdCount + readLen) > storage_size || + (((uintptr_t) outputBuf + readLen) > + (storage_addr + storage_size))) { + ERROR("EMMC READ: Overflow\n"); + return 0; + } + + if (!read_block(p_sdhandle, outputBuf, blockAddr, readLen)) { + if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) + blockAddr += (readLen / blockSize); + else + blockAddr += readLen; + + remSize -= readLen; + rdCount += readLen; + + /* Update Physical address */ + outputBuf += readLen; + } else { + return 0; + } + } + + /* process the last unaligned block reading */ + if (remSize > 0) { + if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf, + blockAddr, SD_MAX_READ_LENGTH)) { + + rdCount += remSize; + /* Check for overflow */ + if (rdCount > storage_size || + (((uintptr_t) outputBuf + remSize) > + (storage_addr + storage_size))) { + ERROR("EMMC READ: Overflow\n"); + return 0; + } + + memcpy(outputBuf, + emmc_global_buf_ptr->u.tempbuf, remSize); + + /* Update Physical address */ + outputBuf += remSize; + } else { + rdCount = 0; + } + } + + return rdCount; +} + +#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE +static uint32_t sdio_write(struct sd_handle *p_sdhandle, uintptr_t mem_addr, + uintptr_t data_addr, size_t bytes_to_write) +{ + + uint32_t offset, blockAddr, writeLen, wtCount = 0; + uint32_t remSize, manual_copy_size = 0; + + uint8_t *inputBuf = (uint8_t *)data_addr; + + /* range check non high capacity memory */ + if ((p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) == 0) { + if (mem_addr > 0x80000000) + return 0; + } + + /* the high capacity card use block address mode */ + if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) { + blockAddr = + (uint32_t)(mem_addr / p_sdhandle->device->cfg.blockSize); + offset = + (uint32_t)(mem_addr - + blockAddr * p_sdhandle->device->cfg.blockSize); + } else { + blockAddr = + ((uint32_t)mem_addr / p_sdhandle->device->cfg.blockSize) * + p_sdhandle->device->cfg.blockSize; + offset = (uint32_t) mem_addr - blockAddr; + } + + remSize = bytes_to_write; + + wtCount = 0; + + /* process first unaligned block */ + if (offset > 0) { + if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf, + blockAddr, p_sdhandle->device->cfg.blockSize)) { + + if (remSize < + (p_sdhandle->device->cfg.blockSize - offset)) + manual_copy_size = remSize; + else + manual_copy_size = + p_sdhandle->device->cfg.blockSize - offset; + + memcpy((void *)((uintptr_t) + (emmc_global_buf_ptr->u.tempbuf + offset)), + inputBuf, + manual_copy_size); + + /* Update Physical address */ + + if (!write_block(p_sdhandle, + emmc_global_buf_ptr->u.tempbuf, + blockAddr, + p_sdhandle->device->cfg.blockSize)) { + + if (remSize < + (p_sdhandle->device->cfg.blockSize - + offset)) { + wtCount += remSize; + manual_copy_size = remSize; + remSize = 0; /* read is done */ + } else { + remSize -= + (p_sdhandle->device->cfg.blockSize - + offset); + wtCount += + (p_sdhandle->device->cfg.blockSize - + offset); + manual_copy_size = + p_sdhandle->device->cfg.blockSize - + offset; + } + + inputBuf += manual_copy_size; + + if (p_sdhandle->device->ctrl.ocr & + SD_CARD_HIGH_CAPACITY) + blockAddr++; + else + blockAddr += + p_sdhandle->device->cfg.blockSize; + } else + return 0; + } else { + return 0; + } + } + + /* process block writing */ + while (remSize >= p_sdhandle->device->cfg.blockSize) { + if (remSize >= SD_MAX_READ_LENGTH) { + writeLen = SD_MAX_READ_LENGTH; + } else { + writeLen = + (remSize / p_sdhandle->device->cfg.blockSize) * + p_sdhandle->device->cfg.blockSize; + } + + if (!write_block(p_sdhandle, inputBuf, blockAddr, writeLen)) { + if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) + blockAddr += + (writeLen / + p_sdhandle->device->cfg.blockSize); + else + blockAddr += writeLen; + + remSize -= writeLen; + wtCount += writeLen; + inputBuf += writeLen; + } else { + return 0; + } + } + + /* process the last unaligned block reading */ + if (remSize > 0) { + if (!read_block(p_sdhandle, + emmc_global_buf_ptr->u.tempbuf, + blockAddr, p_sdhandle->device->cfg.blockSize)) { + + memcpy(emmc_global_buf_ptr->u.tempbuf, + inputBuf, remSize); + + /* Update Physical address */ + + if (!write_block(p_sdhandle, + emmc_global_buf_ptr->u.tempbuf, + blockAddr, + p_sdhandle->device->cfg.blockSize)) { + wtCount += remSize; + inputBuf += remSize; + } else { + return 0; + } + } else { + wtCount = 0; + } + } + + return wtCount; +} +#endif + +/* + * Function to put the card in Ready state by sending CMD0 and CMD1 + */ +static int32_t bcm_emmc_card_ready_state(struct sd_handle *p_sdhandle) +{ + int32_t result = 0; + uint32_t argument = MMC_CMD_IDLE_RESET_ARG; /* Exit from Boot mode */ + + if (p_sdhandle) { + send_sdio_cmd(SD_CMD_GO_IDLE_STATE, argument, 0, NULL); + + result = reset_card(p_sdhandle); + if (result != SD_OK) { + EMMC_TRACE("eMMC Reset error\n"); + return SD_RESET_ERROR; + } + SD_US_DELAY(2000); + result = mmc_cmd1(p_sdhandle); + } + + return result; +} |