You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1089 lines
28 KiB
1089 lines
28 KiB
/*
|
|
* 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;
|
|
}
|
|
|