diff options
Diffstat (limited to 'drivers/brcm/emmc/emmc_csl_sdcmd.c')
-rw-r--r-- | drivers/brcm/emmc/emmc_csl_sdcmd.c | 842 |
1 files changed, 842 insertions, 0 deletions
diff --git a/drivers/brcm/emmc/emmc_csl_sdcmd.c b/drivers/brcm/emmc/emmc_csl_sdcmd.c new file mode 100644 index 0000000..c62886c --- /dev/null +++ b/drivers/brcm/emmc/emmc_csl_sdcmd.c @@ -0,0 +1,842 @@ +/* + * Copyright (c) 2016 - 2020, Broadcom + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdlib.h> +#include <stddef.h> + +#include "bcm_emmc.h" +#include "emmc_chal_types.h" +#include "emmc_chal_sd.h" +#include "emmc_csl_sdprot.h" +#include "emmc_csl_sdcmd.h" +#include "emmc_csl_sd.h" +#include "emmc_chal_sd.h" +#include "emmc_pboot_hal_memory_drv.h" + +int sd_cmd0(struct sd_handle *handle) +{ + int res; + uint32_t argument = 0x0; /* Go to IDLE state. */ + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_GO_IDLE_STATE, argument, 0, NULL); + + if (res == SD_OK) { + /* Clear all other interrupts */ + chal_sd_clear_irq((void *)handle->device, 0xffffffff); + } + + return res; +} + +int sd_cmd1(struct sd_handle *handle, uint32_t ocr, uint32_t *ocr_output) +{ + int res; + uint32_t options; + struct sd_resp resp; + + options = SD_CMDR_RSP_TYPE_R3_4 << SD_CMDR_RSP_TYPE_S; + + if (ocr_output == NULL) { + EMMC_TRACE("Invalid args\n"); + return SD_FAIL; + } + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_SEND_OPCOND, ocr, options, &resp); + + if (res == SD_OK) + *ocr_output = resp.data.r3.ocr; + + return res; +} + +int sd_cmd2(struct sd_handle *handle) +{ + uint32_t options; + struct sd_resp resp; + + /* send cmd and parse result */ + options = SD_CMDR_RSP_TYPE_R2 << SD_CMDR_RSP_TYPE_S; + + return send_cmd(handle, SD_CMD_ALL_SEND_CID, 0, options, &resp); +} + +int sd_cmd3(struct sd_handle *handle) +{ + int res; + uint32_t options = 0; + uint32_t argument; + struct sd_resp resp; + + /* use non zero and non 0x1 value for rca */ + handle->device->ctrl.rca = 0x5; + argument = handle->device->ctrl.rca << SD_CMD7_ARG_RCA_SHIFT; + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK | + SD4_EMMC_TOP_CMD_CRC_EN_MASK; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_MMC_SET_RCA, argument, options, &resp); + + if (res != SD_OK) + handle->device->ctrl.rca = 0; + + return res; +} + +int sd_cmd7(struct sd_handle *handle, uint32_t rca) +{ + int res; + uint32_t argument, options; + struct sd_resp resp; + + argument = (rca << SD_CMD7_ARG_RCA_SHIFT); + + /* + * Response to CMD7 is: + * R1 while selectiing from Stand-By State to Transfer State + * R1b while selecting from Disconnected State to Programming State. + * + * In this driver, we only issue a CMD7 once, to go to transfer mode + * during init_mmc_card(). + */ + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK | + SD4_EMMC_TOP_CMD_CRC_EN_MASK; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_SELECT_DESELECT_CARD, argument, options, + &resp); + + if (res == SD_OK) + /* Clear all other interrupts */ + chal_sd_clear_irq((void *)handle->device, 0xffffffff); + + return res; +} + + +/* + * CMD8 Get CSD_EXT + */ +int mmc_cmd8(struct sd_handle *handle, uint8_t *extCsdReg) +{ + uint32_t res, options; + struct sd_resp resp; + + data_xfer_setup(handle, extCsdReg, CEATA_EXT_CSDBLOCK_SIZE, + SD_XFER_CARD_TO_HOST); + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_DTDS_MASK | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK | SD4_EMMC_TOP_CMD_CRC_EN_MASK; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_READ_EXT_CSD, 0, options, &resp); + + if (res == SD_OK) + res = process_data_xfer(handle, extCsdReg, 0, + CEATA_EXT_CSDBLOCK_SIZE, + SD_XFER_CARD_TO_HOST); + + return res; +} + +int sd_cmd9(struct sd_handle *handle, struct sd_card_data *card) +{ + int res; + uint32_t argument, options, iBlkNum, multiFactor = 1; + uint32_t maxReadBlockLen = 1, maxWriteBlockLen = 1; + struct sd_resp resp; + + argument = handle->device->ctrl.rca << SD_CMD7_ARG_RCA_SHIFT; + + options = SD_CMDR_RSP_TYPE_R2 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_CRC_EN_MASK; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_SEND_CSD, argument, options, &resp); + + if (res != SD_OK) + return res; + + if (handle->card->type == SD_CARD_MMC) { + card->csd.mmc.structure = (resp.data.r2.rsp4 >> 22) & 0x3; + card->csd.mmc.csdSpecVer = (resp.data.r2.rsp4 >> 18) & 0x0f; + card->csd.mmc.taac = (resp.data.r2.rsp4 >> 8) & 0xff; + card->csd.mmc.nsac = resp.data.r2.rsp4 & 0xff; + card->csd.mmc.speed = resp.data.r2.rsp3 >> 24; + card->csd.mmc.classes = (resp.data.r2.rsp3 >> 12) & 0xfff; + card->csd.mmc.rdBlkLen = (resp.data.r2.rsp3 >> 8) & 0xf; + card->csd.mmc.rdBlkPartial = (resp.data.r2.rsp3 >> 7) & 0x01; + card->csd.mmc.wrBlkMisalign = (resp.data.r2.rsp3 >> 6) & 0x1; + card->csd.mmc.rdBlkMisalign = (resp.data.r2.rsp3 >> 5) & 0x1; + card->csd.mmc.dsr = (resp.data.r2.rsp2 >> 4) & 0x01; + card->csd.mmc.size = + ((resp.data.r2.rsp3 & 0x3) << 10) + + ((resp.data.r2.rsp2 >> 22) & 0x3ff); + card->csd.mmc.vddRdCurrMin = (resp.data.r2.rsp2 >> 19) & 0x7; + card->csd.mmc.vddRdCurrMax = (resp.data.r2.rsp2 >> 16) & 0x7; + card->csd.mmc.vddWrCurrMin = (resp.data.r2.rsp2 >> 13) & 0x7; + card->csd.mmc.vddWrCurrMax = (resp.data.r2.rsp2 >> 10) & 0x7; + card->csd.mmc.devSizeMulti = (resp.data.r2.rsp2 >> 7) & 0x7; + card->csd.mmc.eraseGrpSize = (resp.data.r2.rsp2 >> 2) & 0x1f; + card->csd.mmc.eraseGrpSizeMulti = + ((resp.data.r2.rsp2 & 0x3) << 3) + + ((resp.data.r2.rsp1 >> 29) & 0x7); + card->csd.mmc.wrProtGroupSize = + ((resp.data.r2.rsp1 >> 24) & 0x1f); + card->csd.mmc.wrProtGroupEnable = + (resp.data.r2.rsp1 >> 23) & 0x1; + card->csd.mmc.manuDefEcc = (resp.data.r2.rsp1 >> 21) & 0x3; + card->csd.mmc.wrSpeedFactor = (resp.data.r2.rsp1 >> 18) & 0x7; + card->csd.mmc.wrBlkLen = (resp.data.r2.rsp1 >> 14) & 0xf; + card->csd.mmc.wrBlkPartial = (resp.data.r2.rsp1 >> 13) & 0x1; + card->csd.mmc.protAppl = (resp.data.r2.rsp1 >> 8) & 0x1; + card->csd.mmc.copyFlag = (resp.data.r2.rsp1 >> 7) & 0x1; + card->csd.mmc.permWrProt = (resp.data.r2.rsp1 >> 6) & 0x1; + card->csd.mmc.tmpWrProt = (resp.data.r2.rsp1 >> 5) & 0x1; + card->csd.mmc.fileFormat = (resp.data.r2.rsp1 >> 4) & 0x03; + card->csd.mmc.eccCode = resp.data.r2.rsp1 & 0x03; + maxReadBlockLen <<= card->csd.mmc.rdBlkLen; + maxWriteBlockLen <<= card->csd.mmc.wrBlkLen; + + iBlkNum = card->csd.mmc.size + 1; + multiFactor = (1 << (card->csd.mmc.devSizeMulti + 2)); + + handle->card->size = + iBlkNum * multiFactor * (1 << card->csd.mmc.rdBlkLen); + } + + handle->card->maxRdBlkLen = maxReadBlockLen; + handle->card->maxWtBlkLen = maxWriteBlockLen; + + if (handle->card->size < 0xA00000) { + /* + * 10MB Too small size mean, cmd9 response is wrong, + * Use default value 1G + */ + handle->card->size = 0x40000000; + handle->card->maxRdBlkLen = 512; + handle->card->maxWtBlkLen = 512; + } + + if ((handle->card->maxRdBlkLen > 512) || + (handle->card->maxWtBlkLen > 512)) { + handle->card->maxRdBlkLen = 512; + handle->card->maxWtBlkLen = 512; + } else if ((handle->card->maxRdBlkLen == 0) || + (handle->card->maxWtBlkLen == 0)) { + handle->card->maxRdBlkLen = 512; + handle->card->maxWtBlkLen = 512; + } + + handle->device->cfg.blockSize = handle->card->maxRdBlkLen; + + return res; +} + +int sd_cmd13(struct sd_handle *handle, uint32_t *status) +{ + int res; + uint32_t argument, options; + struct sd_resp resp; + + argument = handle->device->ctrl.rca << SD_CMD7_ARG_RCA_SHIFT; + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK | + SD4_EMMC_TOP_CMD_CRC_EN_MASK; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_SEND_STATUS, argument, options, &resp); + + if (res == SD_OK) { + *status = resp.cardStatus; + } + + return res; +} + +int sd_cmd16(struct sd_handle *handle, uint32_t length) +{ + int res; + uint32_t argument, options, ntry; + struct sd_resp resp; + + argument = length; + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_CRC_EN_MASK | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK; + + ntry = 0; + do { + res = sd_cmd13(handle, &resp.cardStatus); + if (res != SD_OK) { + EMMC_TRACE( + "cmd13 failed before cmd16: rca 0x%0x, return %d, response 0x%0x\n", + handle->device->ctrl.rca, res, resp.cardStatus); + return res; + } + + if (resp.cardStatus & 0x100) + break; + + EMMC_TRACE("cmd13 rsp:0x%08x before cmd16\n", resp.cardStatus); + + if (ntry > handle->device->cfg.retryLimit) { + EMMC_TRACE("cmd13 retry reach limit %d\n", + handle->device->cfg.retryLimit); + return SD_CMD_TIMEOUT; + } + + ntry++; + EMMC_TRACE("cmd13 retry %d\n", ntry); + + SD_US_DELAY(1000); + + } while (1); + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_SET_BLOCKLEN, argument, options, &resp); + + return res; +} + +int sd_cmd17(struct sd_handle *handle, + uint32_t addr, uint32_t len, uint8_t *buffer) +{ + int res; + uint32_t argument, options, ntry; + struct sd_resp resp; + + ntry = 0; + do { + res = sd_cmd13(handle, &resp.cardStatus); + if (res != SD_OK) { + EMMC_TRACE( + "cmd 13 failed before cmd17: rca 0x%0x, return %d, response 0x%0x\n", + handle->device->ctrl.rca, res, resp.cardStatus); + return res; + } + + if (resp.cardStatus & 0x100) + break; + + EMMC_TRACE("cmd13 rsp:0x%08x before cmd17\n", resp.cardStatus); + + if (ntry > handle->device->cfg.retryLimit) { + EMMC_TRACE("cmd13 retry reach limit %d\n", + handle->device->cfg.retryLimit); + return SD_CMD_TIMEOUT; + } + + ntry++; + EMMC_TRACE("cmd13 retry %d\n", ntry); + + SD_US_DELAY(1000); + + } while (1); + + data_xfer_setup(handle, buffer, len, SD_XFER_CARD_TO_HOST); + + /* send cmd and parse result */ + argument = addr; + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_DTDS_MASK | + SD4_EMMC_TOP_CMD_CRC_EN_MASK | SD4_EMMC_TOP_CMD_CCHK_EN_MASK; + + res = send_cmd(handle, SD_CMD_READ_SINGLE_BLOCK, argument, options, + &resp); + + if (res != SD_OK) + return res; + + res = process_data_xfer(handle, buffer, addr, len, SD_XFER_CARD_TO_HOST); + + return res; +} + +int sd_cmd18(struct sd_handle *handle, + uint32_t addr, uint32_t len, uint8_t *buffer) +{ + int res; + uint32_t argument, options, ntry; + struct sd_resp resp; + + ntry = 0; + do { + res = sd_cmd13(handle, &resp.cardStatus); + if (res != SD_OK) { + EMMC_TRACE( + "cmd 13 failed before cmd18: rca 0x%0x, return %d, response 0x%0x\n", + handle->device->ctrl.rca, res, resp.cardStatus); + return res; + } + + if (resp.cardStatus & 0x100) + break; + + EMMC_TRACE("cmd13 rsp:0x%08x before cmd18\n", resp.cardStatus); + + if (ntry > handle->device->cfg.retryLimit) { + EMMC_TRACE("cmd13 retry reach limit %d\n", + handle->device->cfg.retryLimit); + return SD_CMD_TIMEOUT; + } + + ntry++; + EMMC_TRACE("cmd13 retry %d\n", ntry); + + SD_US_DELAY(1000); + } while (1); + + data_xfer_setup(handle, buffer, len, SD_XFER_CARD_TO_HOST); + + argument = addr; + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_DTDS_MASK | + SD4_EMMC_TOP_CMD_MSBS_MASK | SD4_EMMC_TOP_CMD_CCHK_EN_MASK | + SD4_EMMC_TOP_CMD_BCEN_MASK | SD4_EMMC_TOP_CMD_CRC_EN_MASK | + BIT(SD4_EMMC_TOP_CMD_ACMDEN_SHIFT); + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_READ_MULTIPLE_BLOCK, argument, options, + &resp); + + if (res != SD_OK) + return res; + + res = process_data_xfer(handle, buffer, addr, len, SD_XFER_CARD_TO_HOST); + + return res; +} + +#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE +static int card_sts_resp(struct sd_handle *handle, uint32_t *status) +{ + int res; + uint32_t ntry = 0; + + do { + res = sd_cmd13(handle, status); + if (res != SD_OK) { + EMMC_TRACE( + "cmd 13 failed before cmd35: rca 0x%0x, return %d\n", + handle->device->ctrl.rca, res); + return res; + } + + if (*status & 0x100) + break; + + EMMC_TRACE("cmd13 rsp:0x%08x before cmd35\n", resp.cardStatus); + + if (ntry > handle->device->cfg.retryLimit) { + EMMC_TRACE("cmd13 retry reach limit %d\n", + handle->device->cfg.retryLimit); + return SD_CMD_TIMEOUT; + } + + ntry++; + EMMC_TRACE("cmd13 retry %d\n", ntry); + + SD_US_DELAY(1000); + } while (1); + + return SD_OK; +} + +int sd_cmd35(struct sd_handle *handle, uint32_t start) +{ + int res; + uint32_t argument, options; + struct sd_resp resp; + + res = card_sts_resp(handle, &resp.cardStatus); + if (res != SD_OK) + return res; + + argument = start; + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_CRC_EN_MASK | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_ERASE_GROUP_START, + argument, options, &resp); + + if (res != SD_OK) + return res; + + return res; +} + +int sd_cmd36(struct sd_handle *handle, uint32_t end) +{ + int res; + uint32_t argument, options; + struct sd_resp resp; + + res = card_sts_resp(handle, &resp.cardStatus); + if (res != SD_OK) + return res; + + argument = end; + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_CRC_EN_MASK | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_ERASE_GROUP_END, + argument, options, &resp); + + if (res != SD_OK) + return res; + + return res; +} + +int sd_cmd38(struct sd_handle *handle) +{ + int res; + uint32_t argument, options; + struct sd_resp resp; + + res = card_sts_resp(handle, &resp.cardStatus); + if (res != SD_OK) + return res; + + argument = 0; + + options = (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; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_ERASE, argument, options, &resp); + + if (res != SD_OK) + return res; + + return res; +} +#endif + +#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE + +int sd_cmd24(struct sd_handle *handle, + uint32_t addr, uint32_t len, uint8_t *buffer) +{ + int res; + uint32_t argument, options, ntry; + struct sd_resp resp; + + ntry = 0; + do { + res = sd_cmd13(handle, &resp.cardStatus); + if (res != SD_OK) { + EMMC_TRACE( + "cmd 13 failed before cmd24: rca 0x%0x, return %d, response 0x%0x\n", + handle->device->ctrl.rca, res, &resp.cardStatus); + return res; + } + + if (resp.cardStatus & 0x100) + break; + + EMMC_TRACE("cmd13 rsp:0x%08x before cmd24\n", resp.cardStatus); + + if (ntry > handle->device->cfg.retryLimit) { + EMMC_TRACE("cmd13 retry reach limit %d\n", + handle->device->cfg.retryLimit); + return SD_CMD_TIMEOUT; + } + + ntry++; + EMMC_TRACE("cmd13 retry %d\n", ntry); + + SD_US_DELAY(1000); + + } while (1); + + data_xfer_setup(handle, buffer, len, SD_XFER_HOST_TO_CARD); + + argument = addr; + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_CRC_EN_MASK | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK; + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_WRITE_BLOCK, argument, options, &resp); + + if (res != SD_OK) + return res; + + res = process_data_xfer(handle, buffer, addr, len, SD_XFER_HOST_TO_CARD); + + return res; +} + +int sd_cmd25(struct sd_handle *handle, + uint32_t addr, uint32_t len, uint8_t *buffer) +{ + int res = SD_OK; + uint32_t argument, options, ntry; + struct sd_resp resp; + + ntry = 0; + do { + res = sd_cmd13(handle, &resp.cardStatus); + if (res != SD_OK) { + EMMC_TRACE( + "cmd 13 failed before cmd25: rca 0x%0x, return %d, response 0x%0x\n", + handle->device->ctrl.rca, res, &resp.cardStatus); + return res; + } + + if (resp.cardStatus & 0x100) + break; + + EMMC_TRACE("cmd13 rsp:0x%08x before cmd25\n", resp.cardStatus); + + if (ntry > handle->device->cfg.retryLimit) { + EMMC_TRACE("cmd13 retry reach limit %d\n", + handle->device->cfg.retryLimit); + return SD_CMD_TIMEOUT; + } + + ntry++; + EMMC_TRACE("cmd13 retry %d\n", ntry); + + SD_US_DELAY(1000); + } while (1); + + data_xfer_setup(handle, buffer, len, SD_XFER_HOST_TO_CARD); + + argument = addr; + + options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_MSBS_MASK | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK | SD4_EMMC_TOP_CMD_BCEN_MASK | + SD4_EMMC_TOP_CMD_CRC_EN_MASK | + BIT(SD4_EMMC_TOP_CMD_ACMDEN_SHIFT); + + /* send cmd and parse result */ + res = send_cmd(handle, SD_CMD_WRITE_MULTIPLE_BLOCK, + argument, options, &resp); + + if (res != SD_OK) + return res; + + res = process_data_xfer(handle, buffer, addr, len, SD_XFER_HOST_TO_CARD); + + return res; +} +#endif /* INCLUDE_EMMC_DRIVER_WRITE_CODE */ + +int mmc_cmd6(struct sd_handle *handle, uint32_t argument) +{ + int res; + uint32_t options; + struct sd_resp resp; + + options = SD_CMDR_RSP_TYPE_R1b_5b << SD_CMDR_RSP_TYPE_S | + SD4_EMMC_TOP_CMD_CCHK_EN_MASK | SD4_EMMC_TOP_CMD_CRC_EN_MASK; + + EMMC_TRACE("Sending CMD6 with argument 0x%X\n", argument); + + /* send cmd and parse result */ + res = send_cmd(handle, SD_ACMD_SET_BUS_WIDTH, argument, options, &resp); + + /* + * For R1b type response: + * controller issues a COMMAND COMPLETE interrupt when the R1 + * response is received, + * then controller monitors DAT0 for busy status, + * controller issues a TRANSFER COMPLETE interrupt when busy signal + * clears. + */ + wait_for_event(handle, + SD4_EMMC_TOP_INTR_TXDONE_MASK | SD_ERR_INTERRUPTS, + handle->device->cfg.wfe_retry); + + if (res == SD_OK) { + /* Check result of Cmd6 using Cmd13 to check card status */ + + /* Check status using Cmd13 */ + res = sd_cmd13(handle, &resp.cardStatus); + + if (res == SD_OK) { + /* Check bit 7 (SWITCH_ERROR) in card status */ + if ((resp.cardStatus & 0x80) != 0) { + EMMC_TRACE("cmd6 failed: SWITCH_ERROR\n"); + res = SD_FAIL; + } + } else { + EMMC_TRACE("cmd13 failed after cmd6: "); + EMMC_TRACE("rca 0x%0x, return %d, response 0x%0x\n", + handle->device->ctrl.rca, res, resp.cardStatus); + } + } + + return res; +} + + +#define SD_BUSY_CHECK 0x00203000 +#define DAT0_LEVEL_MASK 0x100000 /* bit20 in PSTATE */ +#define DEV_BUSY_TIMEOUT 600000 /* 60 Sec : 600000 * 100us */ + +int send_cmd(struct sd_handle *handle, uint32_t cmdIndex, uint32_t argument, + uint32_t options, struct sd_resp *resp) +{ + int status = SD_OK; + uint32_t event = 0, present, timeout = 0, retry = 0, mask = 3; + uint32_t temp_resp[4]; + + if (handle == NULL) { + EMMC_TRACE("Invalid handle for cmd%d\n", cmdIndex); + return SD_INVALID_HANDLE; + } + + mask = (SD_BUSY_CHECK & options) ? 3 : 1; + +RETRY_WRITE_CMD: + do { + /* Make sure it is ok to send command */ + present = + chal_sd_get_present_status((CHAL_HANDLE *) handle->device); + timeout++; + + if (present & mask) + SD_US_DELAY(1000); + else + break; + + } while (timeout < EMMC_BUSY_CMD_TIMEOUT_MS); + + if (timeout >= EMMC_BUSY_CMD_TIMEOUT_MS) { + status = SD_CMD_MISSING; + EMMC_TRACE("cmd%d timedout %dms\n", cmdIndex, timeout); + } + + /* Reset both DAT and CMD line if only of them are stuck */ + if (present & mask) + check_error(handle, SD4_EMMC_TOP_INTR_CMDERROR_MASK); + + handle->device->ctrl.argReg = argument; + chal_sd_send_cmd((CHAL_HANDLE *) handle->device, cmdIndex, + handle->device->ctrl.argReg, options); + + handle->device->ctrl.cmdIndex = cmdIndex; + + event = wait_for_event(handle, + (SD4_EMMC_TOP_INTR_CMDDONE_MASK | + SD_ERR_INTERRUPTS), + handle->device->cfg.wfe_retry); + + if (handle->device->ctrl.cmdStatus == SD_CMD_MISSING) { + retry++; + + if (retry >= handle->device->cfg.retryLimit) { + status = SD_CMD_MISSING; + EMMC_TRACE("cmd%d retry reaches the limit %d\n", + cmdIndex, retry); + } else { + /* reset both DAT & CMD line if one of them is stuck */ + present = chal_sd_get_present_status((CHAL_HANDLE *) + handle->device); + + if (present & mask) + check_error(handle, + SD4_EMMC_TOP_INTR_CMDERROR_MASK); + + EMMC_TRACE("cmd%d retry %d PSTATE[0x%08x]\n", + cmdIndex, retry, + chal_sd_get_present_status((CHAL_HANDLE *) + handle->device)); + goto RETRY_WRITE_CMD; + } + } + + if (handle->device->ctrl.cmdStatus == SD_OK) { + if (resp != NULL) { + status = + chal_sd_get_response((CHAL_HANDLE *) handle->device, + temp_resp); + process_cmd_response(handle, + handle->device->ctrl.cmdIndex, + temp_resp[0], temp_resp[1], + temp_resp[2], temp_resp[3], resp); + } + + /* Check Device busy after CMD */ + if ((cmdIndex == 5) || (cmdIndex == 6) || (cmdIndex == 7) || + (cmdIndex == 28) || (cmdIndex == 29) || (cmdIndex == 38)) { + + timeout = 0; + do { + present = + chal_sd_get_present_status((CHAL_HANDLE *) + handle->device); + + timeout++; + + /* Dat[0]:bit20 low means device busy */ + if ((present & DAT0_LEVEL_MASK) == 0) { + EMMC_TRACE("Device busy: "); + EMMC_TRACE( + "cmd%d arg:0x%08x: PSTATE[0x%08x]\n", + cmdIndex, argument, present); + SD_US_DELAY(100); + } else { + break; + } + } while (timeout < DEV_BUSY_TIMEOUT); + } + } else if (handle->device->ctrl.cmdStatus && + handle->device->ctrl.cmdStatus != SD_CMD_MISSING) { + retry++; + status = check_error(handle, handle->device->ctrl.cmdStatus); + + EMMC_TRACE( + "cmd%d error: cmdStatus:0x%08x error_status:0x%08x\n", + cmdIndex, handle->device->ctrl.cmdStatus, status); + + if ((handle->device->ctrl.cmdIndex == 1) || + (handle->device->ctrl.cmdIndex == 5)) { + status = event; + } else if ((handle->device->ctrl.cmdIndex == 7) || + (handle->device->ctrl.cmdIndex == 41)) { + status = event; + } else if ((status == SD_ERROR_RECOVERABLE) && + (retry < handle->device->cfg.retryLimit)) { + EMMC_TRACE("cmd%d recoverable error ", cmdIndex); + EMMC_TRACE("retry %d PSTATE[0x%08x].\n", retry, + chal_sd_get_present_status((CHAL_HANDLE *) + handle->device)); + goto RETRY_WRITE_CMD; + } else { + EMMC_TRACE("cmd%d retry reaches the limit %d\n", + cmdIndex, retry); + status = event; + } + } + + handle->device->ctrl.blkReg = 0; + /* clear error status for next command */ + handle->device->ctrl.cmdStatus = 0; + + return status; +} |