summaryrefslogtreecommitdiffstats
path: root/drivers/brcm/emmc/emmc_csl_sdcard.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/brcm/emmc/emmc_csl_sdcard.c1089
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;
+}