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_csl_sdcard.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_csl_sdcard.c')
-rw-r--r-- | drivers/brcm/emmc/emmc_csl_sdcard.c | 1089 |
1 files changed, 1089 insertions, 0 deletions
diff --git a/drivers/brcm/emmc/emmc_csl_sdcard.c b/drivers/brcm/emmc/emmc_csl_sdcard.c new file mode 100644 index 0000000..9e2c618 --- /dev/null +++ b/drivers/brcm/emmc/emmc_csl_sdcard.c @@ -0,0 +1,1089 @@ +/* + * Copyright (c) 2016 - 2020, Broadcom + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <inttypes.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <arch_helpers.h> +#include <lib/mmio.h> + +#include "bcm_emmc.h" +#include "emmc_chal_types.h" +#include "emmc_csl_sdprot.h" +#include "emmc_chal_sd.h" +#include "emmc_csl_sdcmd.h" +#include "emmc_csl_sd.h" +#include "emmc_pboot_hal_memory_drv.h" + +#define SD_CARD_BUSY 0x80000000 +#define SD_CARD_RETRY_LIMIT 1000 +#define SD_CARD_HIGH_SPEED_PS 13 +#define SD_CHK_HIGH_SPEED_MODE 0x00FFFFF1 +#define SD_SET_HIGH_SPEED_MODE 0x80FFFFF1 +#define SD_MMC_ENABLE_HIGH_SPEED 0x03b90100 //0x03b90103 +#define SD_MMC_8BIT_MODE 0x03b70200 +#define SD_MMC_4BIT_MODE 0x03b70100 +#define SD_MMC_1BIT_MODE 0x03b70000 + +#define SD_MMC_BOOT_8BIT_MODE 0x03b10200 +#define SD_MMC_BOOT_4BIT_MODE 0x03b10100 +#define SD_MMC_BOOT_1BIT_MODE 0x03b10000 +#define SDIO_HW_EMMC_EXT_CSD_BOOT_CNF 0X03B30000 + +#ifdef USE_EMMC_FIP_TOC_CACHE +/* + * Cache size mirrors the size of the global eMMC temp buffer + * which is used for non-image body reads such as headers, ToC etc. + */ +#define CACHE_SIZE ((EMMC_BLOCK_SIZE) * 2) +#define PARTITION_BLOCK_ADDR ((PLAT_FIP_ATTEMPT_OFFSET)/(EMMC_BLOCK_SIZE)) + +static uint32_t cached_partition_block; +static uint8_t cached_block[CACHE_SIZE]; +#endif + +static int set_card_data_width(struct sd_handle *handle, int width); +static int abort_err(struct sd_handle *handle); +static int err_recovery(struct sd_handle *handle, uint32_t errors); +static int xfer_data(struct sd_handle *handle, uint32_t mode, uint32_t addr, + uint32_t length, uint8_t *base); + +int set_boot_config(struct sd_handle *handle, uint32_t config) +{ + return mmc_cmd6(handle, SDIO_HW_EMMC_EXT_CSD_BOOT_CNF | config); +} + +void process_csd_mmc_speed(struct sd_handle *handle, uint32_t csd_mmc_speed) +{ + uint32_t div_ctrl_setting; + + /* CSD field TRAN_SPEED: + * Bits [2:0] 0 = 100 KHz + * 1 = 1 MHz + * 2 = 10 MHz + * 3 = 100 MHz + * 4...7 Reserved. + * Bits [6:3] 0 = Reserved + * 1 = 1.0 + * 2 = 1.2 + * 3 = 1.3 + * 4 = 1.5 + * 5 = 2.0 + * 6 = 2.6 + * 7 = 3.0 + * 8 = 3.5 + * 9 = 4.0 + * A = 4.5 + * B = 5.2 + * C = 5.5 + * D = 6.0 + * E = 7.0 + * F = 8.0 + * For cards supporting version 4.0, 4.1, and 4.2 of the standard, + * the value shall be 20 MHz (0x2A). + * For cards supporting version 4.3 , the value shall be 26 MHz (0x32) + */ + + switch (csd_mmc_speed & 0x7F) { + case 0x2A: + EMMC_TRACE("Speeding up eMMC clock to 20MHz\n"); + div_ctrl_setting = + chal_sd_freq_2_div_ctrl_setting(20 * 1000 * 1000); + break; + case 0x32: + EMMC_TRACE("Speeding up eMMC clock to 26MHz\n"); + div_ctrl_setting = + chal_sd_freq_2_div_ctrl_setting(26 * 1000 * 1000); + break; + default: + /* Unknown */ + return; + } + + chal_sd_set_clock((CHAL_HANDLE *) handle->device, div_ctrl_setting, 0); + + chal_sd_set_clock((CHAL_HANDLE *) handle->device, div_ctrl_setting, 1); + + SD_US_DELAY(1000); +} + + +/* + * The function changes SD/SDIO/MMC card data width if + * the card support configurable data width. The host controller + * and the card has to be in the same bus data width. + */ +int set_card_data_width(struct sd_handle *handle, int width) +{ + uint32_t data_width = 0; + int is_valid_arg = 1; + int rc = SD_FAIL; + char *bitwidth_str = " "; + char *result_str = "failed"; + + switch (width) { +#ifdef DRIVER_EMMC_ENABLE_DATA_WIDTH_8BIT + case SD_BUS_DATA_WIDTH_8BIT: + data_width = SD_MMC_8BIT_MODE; +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + bitwidth_str = "8_BIT"; +#endif + break; +#endif + case SD_BUS_DATA_WIDTH_4BIT: + data_width = SD_MMC_4BIT_MODE; +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + bitwidth_str = "4_BIT"; +#endif + break; + + case SD_BUS_DATA_WIDTH_1BIT: + data_width = SD_MMC_1BIT_MODE; +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + bitwidth_str = "1_BIT"; +#endif + break; + + default: + is_valid_arg = 0; +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + bitwidth_str = "unknown"; +#endif + break; + } + + if (is_valid_arg) { + rc = mmc_cmd6(handle, data_width); + if (rc == SD_OK) { +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + result_str = "succeeded"; +#endif + chal_sd_config_bus_width((CHAL_HANDLE *) handle->device, + width); + } else { +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + result_str = "failed"; +#endif + } + } else { + rc = SD_FAIL; +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + result_str = "ignored"; +#endif + } + + VERBOSE("SDIO Data Width(%s) %s.\n", bitwidth_str, result_str); + + return rc; +} + + +/* + * Error handling routine. Does abort data + * transmission if error is found. + */ +static int abort_err(struct sd_handle *handle) +{ + uint32_t present, options, event, rel = 0; + struct sd_resp cmdRsp; + + handle->device->ctrl.argReg = 0; + handle->device->ctrl.cmdIndex = SD_CMD_STOP_TRANSMISSION; + + options = (SD_CMD_STOP_TRANSMISSION << 24) | + (SD_CMDR_RSP_TYPE_R1b_5b << SD_CMDR_RSP_TYPE_S) | + SD4_EMMC_TOP_CMD_CRC_EN_MASK | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK; + + chal_sd_send_cmd((CHAL_HANDLE *) handle->device, + handle->device->ctrl.cmdIndex, + handle->device->ctrl.argReg, options); + + event = wait_for_event(handle, + SD4_EMMC_TOP_INTR_CMDDONE_MASK | + SD_ERR_INTERRUPTS, + handle->device->cfg.wfe_retry); + + if (event & SD_CMD_ERROR_INT) { + rel = SD_ERROR_NON_RECOVERABLE; + } else { + if (event & SD_DAT_TIMEOUT) { + return SD_ERROR_NON_RECOVERABLE; + } + + chal_sd_get_response((CHAL_HANDLE *) handle->device, + (uint32_t *)&cmdRsp); + + process_cmd_response(handle, handle->device->ctrl.cmdIndex, + cmdRsp.data.r2.rsp1, cmdRsp.data.r2.rsp2, + cmdRsp.data.r2.rsp3, cmdRsp.data.r2.rsp4, + &cmdRsp); + + SD_US_DELAY(2000); + + present = + chal_sd_get_present_status((CHAL_HANDLE *) handle->device); + + if ((present & 0x00F00000) == 0x00F00000) + rel = SD_ERROR_RECOVERABLE; + else + rel = SD_ERROR_NON_RECOVERABLE; + } + + return rel; +} + + +/* + * The function handles real data transmission on both DMA and + * none DMA mode, In None DMA mode the data transfer starts + * when the command is sent to the card, data has to be written + * into the host contollers buffer at this time one block + * at a time. + * In DMA mode, the real data transfer is done by the DMA engine + * and this functions just waits for the data transfer to complete. + * + */ +int process_data_xfer(struct sd_handle *handle, uint8_t *buffer, uint32_t addr, + uint32_t length, int dir) +{ + if (dir == SD_XFER_HOST_TO_CARD) { +#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE + if (handle->device->cfg.dma == SD_DMA_OFF) { + /* + * In NON DMA mode, the real data xfer starts from here + */ + if (write_buffer(handle, length, buffer)) + return SD_WRITE_ERROR; + } else { + wait_for_event(handle, + SD4_EMMC_TOP_INTR_TXDONE_MASK | + SD_ERR_INTERRUPTS, + handle->device->cfg.wfe_retry); + + if (handle->device->ctrl.cmdStatus == SD_OK) + return SD_OK; + + check_error(handle, handle->device->ctrl.cmdStatus); + return SD_WRITE_ERROR; + } +#else + return SD_WRITE_ERROR; +#endif + } else { /* SD_XFER_CARD_TO_HOST */ + + if (handle->device->cfg.dma == SD_DMA_OFF) { + /* In NON DMA mode, the real data + * transfer starts from here + */ + if (read_buffer(handle, length, buffer)) + return SD_READ_ERROR; + + } else { /* for DMA mode */ + + /* + * once the data transmission is done + * copy data to the host buffer. + */ + wait_for_event(handle, + SD4_EMMC_TOP_INTR_TXDONE_MASK | + SD_ERR_INTERRUPTS, + handle->device->cfg.wfe_retry); + + if (handle->device->ctrl.cmdStatus == SD_OK) + return SD_OK; + + check_error(handle, handle->device->ctrl.cmdStatus); + return SD_READ_ERROR; + } + } + return SD_OK; +} + + +/* + * The function sets block size for the next SD/SDIO/MMC + * card read/write command. + */ +int select_blk_sz(struct sd_handle *handle, uint16_t size) +{ + return sd_cmd16(handle, size); +} + + +/* + * The function initalizes the SD/SDIO/MMC/CEATA and detects + * the card according to the flag of detection. + * Once this function is called, the card is put into ready state + * so application can do data transfer to and from the card. + */ +int init_card(struct sd_handle *handle, int detection) +{ + /* + * After Reset, eMMC comes up in 1 Bit Data Width by default. + * Set host side to match. + */ + chal_sd_config_bus_width((CHAL_HANDLE *) handle->device, + SD_BUS_DATA_WIDTH_1BIT); + +#ifdef USE_EMMC_FIP_TOC_CACHE + cached_partition_block = 0; +#endif + handle->device->ctrl.present = 0; /* init card present to be no card */ + + init_mmc_card(handle); + + handle->device->ctrl.present = 1; /* card is detected */ + + /* switch the data width back */ + if (handle->card->type != SD_CARD_MMC) + return SD_FAIL; + + /* + * Dynamically set Data Width to highest supported value. + * Try different data width settings (highest to lowest). + * Verify each setting by reading EXT_CSD and comparing + * against the EXT_CSD contents previously read in call to + * init_mmc_card() earlier. Stop at first verified data width + * setting. + */ + { +#define EXT_CSD_PROPERTIES_SECTION_START_INDEX 192 +#define EXT_CSD_PROPERTIES_SECTION_END_INDEX 511 + uint8_t buffer[EXT_CSD_SIZE]; +#ifdef DRIVER_EMMC_ENABLE_DATA_WIDTH_8BIT + /* Try 8 Bit Data Width */ + chal_sd_config_bus_width((CHAL_HANDLE *) handle->device, + SD_BUS_DATA_WIDTH_8BIT); + if ((!set_card_data_width(handle, SD_BUS_DATA_WIDTH_8BIT)) && + (!mmc_cmd8(handle, buffer)) && + (!memcmp(&buffer[EXT_CSD_PROPERTIES_SECTION_START_INDEX], + &(emmc_global_buf_ptr->u.Ext_CSD_storage[EXT_CSD_PROPERTIES_SECTION_START_INDEX]), + EXT_CSD_PROPERTIES_SECTION_END_INDEX - EXT_CSD_PROPERTIES_SECTION_START_INDEX + 1))) + + return SD_OK; +#endif + /* Fall back to 4 Bit Data Width */ + chal_sd_config_bus_width((CHAL_HANDLE *) handle->device, + SD_BUS_DATA_WIDTH_4BIT); + if ((!set_card_data_width(handle, SD_BUS_DATA_WIDTH_4BIT)) && + (!mmc_cmd8(handle, buffer)) && + (!memcmp(&buffer[EXT_CSD_PROPERTIES_SECTION_START_INDEX], + &(emmc_global_buf_ptr->u.Ext_CSD_storage[EXT_CSD_PROPERTIES_SECTION_START_INDEX]), + EXT_CSD_PROPERTIES_SECTION_END_INDEX - EXT_CSD_PROPERTIES_SECTION_START_INDEX + 1))) + + return SD_OK; + + /* Fall back to 1 Bit Data Width */ + chal_sd_config_bus_width((CHAL_HANDLE *) handle->device, + SD_BUS_DATA_WIDTH_1BIT); + /* Just use 1 Bit Data Width then. */ + if (!set_card_data_width(handle, SD_BUS_DATA_WIDTH_1BIT)) + return SD_OK; + + } + return SD_CARD_INIT_ERROR; +} + + +/* + * The function handles MMC/CEATA card initalization. + */ +int init_mmc_card(struct sd_handle *handle) +{ + uint32_t ocr = 0, newOcr, rc, limit = 0; + uint32_t cmd1_option = 0x40300000; + uint32_t sec_count; + + handle->card->type = SD_CARD_MMC; + + do { + SD_US_DELAY(1000); + newOcr = 0; + ocr = 0; + rc = sd_cmd1(handle, cmd1_option, &newOcr); + limit++; + + if (rc == SD_OK) + ocr = newOcr; + + } while (((ocr & SD_CARD_BUSY) == 0) && (limit < SD_CARD_RETRY_LIMIT)); + + if (limit >= SD_CARD_RETRY_LIMIT) { + handle->card->type = SD_CARD_UNKNOWN; + EMMC_TRACE("CMD1 Timeout: Device is not ready\n"); + return SD_CARD_UNKNOWN; + } + + /* Save the ocr register */ + handle->device->ctrl.ocr = ocr; + + /* Ready State */ + rc = sd_cmd2(handle); + if (rc != SD_OK) { + handle->card->type = SD_CARD_UNKNOWN; + return SD_CARD_UNKNOWN; + } + + rc = sd_cmd3(handle); + if (rc != SD_OK) { + handle->card->type = SD_CARD_UNKNOWN; + return SD_CARD_UNKNOWN; + } + /* read CSD */ + rc = sd_cmd9(handle, &emmc_global_vars_ptr->cardData); + if (rc != SD_OK) { + handle->card->type = SD_CARD_UNKNOWN; + return SD_CARD_UNKNOWN; + } + + /* Increase clock frequency according to what the card advertises */ + EMMC_TRACE("From CSD... cardData.csd.mmc.speed = 0x%X\n", + emmc_global_vars_ptr->cardData.csd.mmc.speed); + process_csd_mmc_speed(handle, + emmc_global_vars_ptr->cardData.csd.mmc.speed); + + /* goto transfer mode */ + rc = sd_cmd7(handle, handle->device->ctrl.rca); + if (rc != SD_OK) { + handle->card->type = SD_CARD_UNKNOWN; + return SD_CARD_UNKNOWN; + } + + rc = mmc_cmd8(handle, emmc_global_buf_ptr->u.Ext_CSD_storage); + if (rc == SD_OK) { + /* calcul real capacity */ + sec_count = emmc_global_buf_ptr->u.Ext_CSD_storage[212] | + emmc_global_buf_ptr->u.Ext_CSD_storage[213] << 8 | + emmc_global_buf_ptr->u.Ext_CSD_storage[214] << 16 | + emmc_global_buf_ptr->u.Ext_CSD_storage[215] << 24; + + EMMC_TRACE("Device density = %ldMBytes\n", + handle->card->size / (1024 * 1024)); + + if (sec_count > 0) { + handle->card->size = (uint64_t)sec_count * 512; + + EMMC_TRACE("Updated Device density = %ldMBytes\n", + handle->card->size / (1024 * 1024)); + } + + if (sec_count > (2u * 1024 * 1024 * 1024) / 512) { + handle->device->ctrl.ocr |= SD_CARD_HIGH_CAPACITY; + handle->device->cfg.blockSize = 512; + } + + if (handle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) + EMMC_TRACE("Sector addressing\n"); + else + EMMC_TRACE("Byte addressing\n"); + + EMMC_TRACE("Ext_CSD_storage[162]: 0x%02X Ext_CSD_storage[179]: 0x%02X\n", + emmc_global_buf_ptr->u.Ext_CSD_storage[162], + emmc_global_buf_ptr->u.Ext_CSD_storage[179]); + } + + return handle->card->type; +} + + +/* + * The function send reset command to the card. + * The card will be in ready status after the reset. + */ +int reset_card(struct sd_handle *handle) +{ + int res = SD_OK; + + /* on reset, card's RCA should return to 0 */ + handle->device->ctrl.rca = 0; + + res = sd_cmd0(handle); + + if (res != SD_OK) + return SD_RESET_ERROR; + + return res; +} + + +/* + * The function sends command to the card and starts + * data transmission. + */ +static int xfer_data(struct sd_handle *handle, + uint32_t mode, + uint32_t addr, uint32_t length, uint8_t *base) +{ + int rc = SD_OK; + + VERBOSE("XFER: dest: 0x%" PRIx64 ", addr: 0x%x, size: 0x%x bytes\n", + (uint64_t)base, addr, length); + + if ((length / handle->device->cfg.blockSize) > 1) { + if (mode == SD_OP_READ) { + inv_dcache_range((uintptr_t)base, (uint64_t)length); + rc = sd_cmd18(handle, addr, length, base); + } else { +#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE + flush_dcache_range((uintptr_t)base, (uint64_t)length); + rc = sd_cmd25(handle, addr, length, base); +#else + rc = SD_DATA_XFER_ERROR; +#endif + } + } else { + if (mode == SD_OP_READ) { + inv_dcache_range((uintptr_t)base, (uint64_t)length); + rc = sd_cmd17(handle, addr, + handle->device->cfg.blockSize, base); + } else { +#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE + flush_dcache_range((uintptr_t)base, (uint64_t)length); + rc = sd_cmd24(handle, addr, + handle->device->cfg.blockSize, base); +#else + rc = SD_DATA_XFER_ERROR; +#endif + } + } + + if (rc != SD_OK) + return SD_DATA_XFER_ERROR; + + return SD_OK; +} + +#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE +int erase_card(struct sd_handle *handle, uint32_t addr, uint32_t blocks) +{ + uint32_t end_addr; + + INFO("ERASE: addr: 0x%x, num of sectors: 0x%x\n", addr, blocks); + + if (sd_cmd35(handle, addr) != SD_OK) + return SD_FAIL; + + end_addr = addr + blocks - 1; + if (sd_cmd36(handle, end_addr) != SD_OK) + return SD_FAIL; + + if (sd_cmd38(handle) != SD_OK) + return SD_FAIL; + + return SD_OK; +} +#endif + +/* + * The function reads block data from a card. + */ +#ifdef USE_EMMC_FIP_TOC_CACHE +int read_block(struct sd_handle *handle, + uint8_t *dst, uint32_t addr, uint32_t len) +{ + int rel = SD_OK; + + /* + * Avoid doing repeated reads of the partition block + * by caching. + */ + if (cached_partition_block && + addr == PARTITION_BLOCK_ADDR && + len == CACHE_SIZE) { + memcpy(dst, cached_block, len); + } else { + rel = xfer_data(handle, SD_OP_READ, addr, len, dst); + + if (len == CACHE_SIZE && addr == PARTITION_BLOCK_ADDR) { + cached_partition_block = 1; + memcpy(cached_block, dst, len); + } + } + + return rel; +} +#else +int read_block(struct sd_handle *handle, + uint8_t *dst, uint32_t addr, uint32_t len) +{ + return xfer_data(handle, SD_OP_READ, addr, len, dst); +} +#endif + +#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE + +/* + * The function writes block data to a card. + */ +int write_block(struct sd_handle *handle, + uint8_t *src, uint32_t addr, uint32_t len) +{ + int rel = SD_OK; + + /* + * Current HC has problem to get response of cmd16 after cmd12, + * the delay is necessary to sure the next cmd16 will not be timed out. + * The delay has to be at least 4 ms. + * The code removed cmd16 and use cmd13 to get card status before + * sending cmd18 or cmd25 to make sure the card is ready and thus + * no need to have delay here. + */ + + rel = xfer_data(handle, SD_OP_WRITE, addr, len, src); + + EMMC_TRACE("wr_blk addr:0x%08X src:0x%08X len:0x%08X result:%d\n", + addr, src, len, rel); + + return rel; +} + + +/* + * The function is called to write one block data directly to + * a card's data buffer. + * it is used in Non-DMA mode for card data transmission. + */ +int write_buffer(struct sd_handle *handle, uint32_t length, uint8_t *data) +{ + uint32_t rem, blockSize, event; + uint8_t *pData = data; + + blockSize = handle->device->cfg.blockSize; + rem = length; + + if (rem == 0) + return SD_OK; + + while (rem > 0) { + + event = wait_for_event(handle, + SD4_EMMC_TOP_INTR_BWRDY_MASK | + SD_ERR_INTERRUPTS, + handle->device->cfg.wfe_retry); + + if (handle->device->ctrl.cmdStatus) { + check_error(handle, handle->device->ctrl.cmdStatus); + return SD_WRITE_ERROR; + } + + if (rem >= blockSize) + chal_sd_write_buffer((CHAL_HANDLE *) handle->device, + blockSize, pData); + else + chal_sd_write_buffer((CHAL_HANDLE *) handle->device, + rem, pData); + + if (rem > blockSize) { + rem -= blockSize; + pData += blockSize; + } else { + pData += rem; + rem = 0; + } + } + + if ((event & SD4_EMMC_TOP_INTR_TXDONE_MASK) != + SD4_EMMC_TOP_INTR_TXDONE_MASK) { + event = wait_for_event(handle, + SD4_EMMC_TOP_INTR_TXDONE_MASK | + SD_ERR_INTERRUPTS, + handle->device->cfg.wfe_retry); + + if (handle->device->ctrl.cmdStatus != SD_OK) { + check_error(handle, handle->device->ctrl.cmdStatus); + return SD_WRITE_ERROR; + } + } else { + handle->device->ctrl.eventList &= ~SD4_EMMC_TOP_INTR_TXDONE_MASK; + } + + return SD_OK; +} +#endif /* INCLUDE_EMMC_DRIVER_WRITE_CODE */ + + +/* + * The function is called to read maximal one block data + * directly from a card + * It is used in Non-DMA mode for card data transmission. + */ +int read_buffer(struct sd_handle *handle, uint32_t length, uint8_t *data) +{ + uint32_t rem, blockSize, event = 0; + uint8_t *pData = data; + + blockSize = handle->device->cfg.blockSize; + rem = length; + + if (rem == 0) + return SD_OK; + + while (rem > 0) { + event = wait_for_event(handle, + SD4_EMMC_TOP_INTR_BRRDY_MASK | + SD_ERR_INTERRUPTS, + handle->device->cfg.wfe_retry); + + if (handle->device->ctrl.cmdStatus) { + check_error(handle, handle->device->ctrl.cmdStatus); + return SD_READ_ERROR; + } + + if (rem >= blockSize) + chal_sd_read_buffer((CHAL_HANDLE *) handle->device, + blockSize, pData); + else + chal_sd_read_buffer((CHAL_HANDLE *) handle->device, rem, + pData); + + if (rem > blockSize) { + rem -= blockSize; + pData += blockSize; + } else { + pData += rem; + rem = 0; + } + } + + /* In case, there are extra data in the SD FIFO, just dump them. */ + chal_sd_dump_fifo((CHAL_HANDLE *) handle->device); + + if ((event & SD4_EMMC_TOP_INTR_TXDONE_MASK) != + SD4_EMMC_TOP_INTR_TXDONE_MASK) { + event = wait_for_event(handle, SD4_EMMC_TOP_INTR_TXDONE_MASK, + handle->device->cfg.wfe_retry); + + if (handle->device->ctrl.cmdStatus) { + check_error(handle, handle->device->ctrl.cmdStatus); + return SD_READ_ERROR; + } + } else { + handle->device->ctrl.eventList &= ~SD4_EMMC_TOP_INTR_TXDONE_MASK; + } + + return SD_OK; +} + + +/* + * Error handling routine. + * The function just reset the DAT + * and CMD line if an error occures during data transmission. + */ +int check_error(struct sd_handle *handle, uint32_t ints) +{ + uint32_t rel; + + chal_sd_set_irq_signal((CHAL_HANDLE *) handle->device, + SD_ERR_INTERRUPTS, 0); + + if (ints & SD4_EMMC_TOP_INTR_CMDERROR_MASK) { + + chal_sd_reset_line((CHAL_HANDLE *) handle->device, + SD4_EMMC_TOP_CTRL1_CMDRST_MASK); + rel = abort_err(handle); + + chal_sd_reset_line((CHAL_HANDLE *) handle->device, + SD4_EMMC_TOP_CTRL1_DATRST_MASK); + chal_sd_set_irq_signal((CHAL_HANDLE *) handle->device, + SD_ERR_INTERRUPTS, 1); + + return (rel == SD_ERROR_NON_RECOVERABLE) ? + SD_ERROR_NON_RECOVERABLE : SD_ERROR_RECOVERABLE; + } else { + rel = err_recovery(handle, ints); + } + + chal_sd_set_irq_signal((CHAL_HANDLE *) handle->device, + SD_ERR_INTERRUPTS, 1); + + return rel; +} + + +/* + * Error recovery routine. + * Try to recover from the error. + */ +static int err_recovery(struct sd_handle *handle, uint32_t errors) +{ + uint32_t rel = 0; + + /* + * In case of timeout error, the cmd line and data line maybe + * still active or stuck at atcitve so it is needed to reset + * either data line or cmd line to make sure a new cmd can be sent. + */ + + if (errors & SD_CMD_ERROR_INT) + chal_sd_reset_line((CHAL_HANDLE *) handle->device, + SD4_EMMC_TOP_CTRL1_CMDRST_MASK); + + if (errors & SD_DAT_ERROR_INT) + chal_sd_reset_line((CHAL_HANDLE *) handle->device, + SD4_EMMC_TOP_CTRL1_DATRST_MASK); + + /* Abort transaction by sending out stop command */ + if ((handle->device->ctrl.cmdIndex == 18) || + (handle->device->ctrl.cmdIndex == 25)) + rel = abort_err(handle); + + return rel; +} + + +/* + * The function is called to read one block data directly from a card. + * It is used in Non-DMA mode for card data transmission. + */ +int process_cmd_response(struct sd_handle *handle, + uint32_t cmdIndex, + uint32_t rsp0, + uint32_t rsp1, + uint32_t rsp2, uint32_t rsp3, struct sd_resp *resp) +{ + int result = SD_OK; + + /* R6 */ + uint32_t rca = (rsp0 >> 16) & 0xffff; + uint32_t cardStatus = rsp0; + + /* R4 */ + uint32_t cBit = (rsp0 >> 31) & 0x1; + uint32_t funcs = (rsp0 >> 28) & 0x7; + uint32_t memPresent = (rsp0 >> 27) & 0x1; + + resp->r1 = 0x3f; + resp->cardStatus = cardStatus; + + if (cmdIndex == SD_CMD_IO_SEND_OP_COND) { + resp->data.r4.cardReady = cBit; + resp->data.r4.funcs = funcs; + resp->data.r4.memPresent = memPresent; + resp->data.r4.ocr = cardStatus; + } + + if (cmdIndex == SD_CMD_MMC_SET_RCA) { + resp->data.r6.rca = rca; + resp->data.r6.cardStatus = cardStatus & 0xFFFF; + } + + if (cmdIndex == SD_CMD_SELECT_DESELECT_CARD) { + resp->data.r7.rca = rca; + } + + if (cmdIndex == SD_CMD_IO_RW_DIRECT) { + if (((rsp0 >> 16) & 0xffff) != 0) + result = SD_CMD_ERR_INVALID_RESPONSE; + + resp->data.r5.data = rsp0 & 0xff; + } + + if (cmdIndex == SD_CMD_IO_RW_EXTENDED) { + if (((rsp0 >> 16) & 0xffff) != 0) + result = SD_CMD_ERR_INVALID_RESPONSE; + + resp->data.r5.data = rsp0 & 0xff; + } + + if (cmdIndex == SD_ACMD_SD_SEND_OP_COND || + cmdIndex == SD_CMD_SEND_OPCOND) + resp->data.r3.ocr = cardStatus; + + if (cmdIndex == SD_CMD_SEND_CSD || + cmdIndex == SD_CMD_SEND_CID || + cmdIndex == SD_CMD_ALL_SEND_CID) { + resp->data.r2.rsp4 = rsp3; + resp->data.r2.rsp3 = rsp2; + resp->data.r2.rsp2 = rsp1; + resp->data.r2.rsp1 = rsp0; + } + + if ((cmdIndex == SD_CMD_READ_EXT_CSD) && + (handle->card->type == SD_CARD_SD)) { + if ((resp->cardStatus & 0xAA) != 0xAA) { + result = SD_CMD_ERR_INVALID_RESPONSE; + } + } + + return result; +} + + +/* + * The function sets DMA buffer and data length, process + * block size and the number of blocks to be transferred. + * It returns the DMA buffer address. + * It copies dma data from user buffer to the DMA buffer + * if the operation is to write data to the SD card. + */ +void data_xfer_setup(struct sd_handle *handle, uint8_t *data, uint32_t length, + int dir) +{ + chal_sd_setup_xfer((CHAL_HANDLE *)handle->device, data, length, dir); +} + + +/* + * The function does soft reset the host SD controller. After + * the function call all host controller's register are reset + * to default vallue; + * + * Note This function only resets the host controller it does not + * reset the controller's handler. + */ +int reset_host_ctrl(struct sd_handle *handle) +{ + chal_sd_stop(); + + return SD_OK; +} + +static void pstate_log(struct sd_handle *handle) +{ + ERROR("PSTATE: 0x%x\n", mmio_read_32 + (handle->device->ctrl.sdRegBaseAddr + + SD4_EMMC_TOP_PSTATE_SD4_OFFSET)); + ERROR("ERRSTAT: 0x%x\n", mmio_read_32 + (handle->device->ctrl.sdRegBaseAddr + + SD4_EMMC_TOP_ERRSTAT_OFFSET)); +} + +/* + * The function waits for one or a group of interrupts specified + * by mask. The function returns if any one the interrupt status + * is set. If interrupt mode is not enabled then it will poll + * the interrupt status register until a interrupt status is set + * an error interrupt happens. If interrupt mode is enabled then + * this function should be called after the interrupt + * is received by ISR routine. + */ +uint32_t wait_for_event(struct sd_handle *handle, + uint32_t mask, uint32_t retry) +{ + uint32_t regval, cmd12, time = 0; + + handle->device->ctrl.cmdStatus = 0; /* no error */ + EMMC_TRACE("%s %d mask:0x%x timeout:%d irq_status:0x%x\n", + __func__, __LINE__, mask, retry, + chal_sd_get_irq_status((CHAL_HANDLE *)handle->device)); + + /* Polling mode */ + do { + regval = chal_sd_get_irq_status((CHAL_HANDLE *)handle->device); + + if (regval & SD4_EMMC_TOP_INTR_DMAIRQ_MASK) { + chal_sd_set_dma_addr((CHAL_HANDLE *)handle->device, + (uintptr_t) + chal_sd_get_dma_addr((CHAL_HANDLE *) + handle->device)); + chal_sd_clear_irq((CHAL_HANDLE *)handle->device, + SD4_EMMC_TOP_INTR_DMAIRQ_MASK); + } + + if (time++ > retry) { + ERROR("EMMC: No response (cmd%d) after %dus.\n", + handle->device->ctrl.cmdIndex, + time * EMMC_WFE_RETRY_DELAY_US); + handle->device->ctrl.cmdStatus = SD_CMD_MISSING; + pstate_log(handle); + ERROR("EMMC: INT[0x%x]\n", regval); + break; + } + + if (regval & SD4_EMMC_TOP_INTR_CTOERR_MASK) { + ERROR("EMMC: Cmd%d timeout INT[0x%x]\n", + handle->device->ctrl.cmdIndex, regval); + handle->device->ctrl.cmdStatus = + SD4_EMMC_TOP_INTR_CTOERR_MASK; + pstate_log(handle); + break; + } + if (regval & SD_CMD_ERROR_FLAGS) { + ERROR("EMMC: Cmd%d error INT[0x%x]\n", + handle->device->ctrl.cmdIndex, regval); + handle->device->ctrl.cmdStatus = SD_CMD_ERROR_FLAGS; + pstate_log(handle); + break; + } + + cmd12 = chal_sd_get_atuo12_error((CHAL_HANDLE *)handle->device); + if (cmd12) { + ERROR("EMMC: Cmd%d auto cmd12 err:0x%x\n", + handle->device->ctrl.cmdIndex, cmd12); + handle->device->ctrl.cmdStatus = cmd12; + pstate_log(handle); + break; + } + + if (SD_DATA_ERROR_FLAGS & regval) { + ERROR("EMMC: Data for cmd%d error, INT[0x%x]\n", + handle->device->ctrl.cmdIndex, regval); + handle->device->ctrl.cmdStatus = + (SD_DATA_ERROR_FLAGS & regval); + pstate_log(handle); + break; + } + + if ((regval & mask) == 0) + udelay(EMMC_WFE_RETRY_DELAY_US); + + } while ((regval & mask) == 0); + + /* clear the interrupt since it is processed */ + chal_sd_clear_irq((CHAL_HANDLE *)handle->device, (regval & mask)); + + return (regval & mask); +} + +int32_t set_config(struct sd_handle *handle, uint32_t speed, uint32_t retry, + uint32_t dma, uint32_t dmaBound, uint32_t blkSize, + uint32_t wfe_retry) +{ + int32_t rel = 0; + + if (handle == NULL) + return SD_FAIL; + + handle->device->cfg.wfe_retry = wfe_retry; + + rel = chal_sd_config((CHAL_HANDLE *)handle->device, speed, retry, + dmaBound, blkSize, dma); + return rel; + +} + +int mmc_cmd1(struct sd_handle *handle) +{ + uint32_t newOcr, res; + uint32_t cmd1_option = MMC_OCR_OP_VOLT | MMC_OCR_SECTOR_ACCESS_MODE; + + /* + * After Reset, eMMC comes up in 1 Bit Data Width by default. + * Set host side to match. + */ + chal_sd_config_bus_width((CHAL_HANDLE *) handle->device, + SD_BUS_DATA_WIDTH_1BIT); + +#ifdef USE_EMMC_FIP_TOC_CACHE + cached_partition_block = 0; +#endif + handle->device->ctrl.present = 0; /* init card present to be no card */ + + handle->card->type = SD_CARD_MMC; + + res = sd_cmd1(handle, cmd1_option, &newOcr); + + if (res != SD_OK) { + EMMC_TRACE("CMD1 Timeout: Device is not ready\n"); + res = SD_CARD_UNKNOWN; + } + return res; +} |