GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
+621
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <emmc_api.h>
|
||||
#include <cmn_plat_util.h>
|
||||
|
||||
#define MAX_CMD_RETRY 10
|
||||
|
||||
#if EMMC_USE_DMA
|
||||
#define USE_DMA 1
|
||||
#else
|
||||
#define USE_DMA 0
|
||||
#endif
|
||||
|
||||
struct emmc_global_buffer emmc_global_buf;
|
||||
struct emmc_global_buffer *emmc_global_buf_ptr = &emmc_global_buf;
|
||||
|
||||
struct emmc_global_vars emmc_global_vars;
|
||||
struct emmc_global_vars *emmc_global_vars_ptr = &emmc_global_vars;
|
||||
|
||||
static struct sd_handle *sdio_gethandle(void);
|
||||
static uint32_t sdio_idle(struct sd_handle *p_sdhandle);
|
||||
|
||||
static uint32_t sdio_read(struct sd_handle *p_sdhandle,
|
||||
uintptr_t mem_addr,
|
||||
uintptr_t storage_addr,
|
||||
size_t storage_size,
|
||||
size_t bytes_to_read);
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
|
||||
static uint32_t sdio_write(struct sd_handle *p_sdhandle,
|
||||
uintptr_t mem_addr,
|
||||
uintptr_t data_addr,
|
||||
size_t bytes_to_write);
|
||||
#endif
|
||||
|
||||
static struct sd_handle *sdio_init(void);
|
||||
static int32_t bcm_emmc_card_ready_state(struct sd_handle *p_sdhandle);
|
||||
|
||||
static void init_globals(void)
|
||||
{
|
||||
memset((void *)emmc_global_buf_ptr, 0, sizeof(*emmc_global_buf_ptr));
|
||||
memset((void *)emmc_global_vars_ptr, 0, sizeof(*emmc_global_vars_ptr));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is used to change partition
|
||||
*/
|
||||
uint32_t emmc_partition_select(uint32_t partition)
|
||||
{
|
||||
int rc;
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
EMMC_TRACE("eMMC init is not done");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (partition) {
|
||||
case EMMC_BOOT_PARTITION1:
|
||||
rc = set_boot_config(sd_handle,
|
||||
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_BOOT1);
|
||||
EMMC_TRACE(
|
||||
"Change to Boot Partition 1 result:%d (0 means SD_OK)\n",
|
||||
rc);
|
||||
break;
|
||||
|
||||
case EMMC_BOOT_PARTITION2:
|
||||
rc = set_boot_config(sd_handle,
|
||||
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_BOOT2);
|
||||
EMMC_TRACE(
|
||||
"Change to Boot Partition 2 result:%d (0 means SD_OK)\n",
|
||||
rc);
|
||||
break;
|
||||
|
||||
case EMMC_USE_CURRENT_PARTITION:
|
||||
rc = SD_OK;
|
||||
EMMC_TRACE("Stay on current partition");
|
||||
break;
|
||||
|
||||
case EMMC_USER_AREA:
|
||||
default:
|
||||
rc = set_boot_config(sd_handle,
|
||||
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_USER);
|
||||
EMMC_TRACE("Change to User area result:%d (0 means SD_OK)\n",
|
||||
rc);
|
||||
break;
|
||||
|
||||
}
|
||||
return (rc == SD_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize emmc controller for eMMC
|
||||
* Returns 0 on fail condition
|
||||
*/
|
||||
uint32_t bcm_emmc_init(bool card_rdy_only)
|
||||
{
|
||||
struct sd_handle *p_sdhandle;
|
||||
uint32_t result = 0;
|
||||
|
||||
EMMC_TRACE("Enter emmc_controller_init()\n");
|
||||
|
||||
/* If eMMC is already initialized, skip init */
|
||||
if (emmc_global_vars_ptr->init_done)
|
||||
return 1;
|
||||
|
||||
init_globals();
|
||||
|
||||
p_sdhandle = sdio_init();
|
||||
|
||||
if (p_sdhandle == NULL) {
|
||||
ERROR("eMMC init failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (card_rdy_only) {
|
||||
/* Put the card in Ready state, Not complete init */
|
||||
result = bcm_emmc_card_ready_state(p_sdhandle);
|
||||
return !result;
|
||||
}
|
||||
|
||||
if (sdio_idle(p_sdhandle) == EMMC_BOOT_OK) {
|
||||
set_config(p_sdhandle, SD_NORMAL_SPEED, MAX_CMD_RETRY, USE_DMA,
|
||||
SD_DMA_BOUNDARY_256K, EMMC_BLOCK_SIZE,
|
||||
EMMC_WFE_RETRY);
|
||||
|
||||
if (!select_blk_sz(p_sdhandle,
|
||||
p_sdhandle->device->cfg.blockSize)) {
|
||||
emmc_global_vars_ptr->init_done = 1;
|
||||
result = 1;
|
||||
} else {
|
||||
ERROR("Select Block Size failed\n");
|
||||
}
|
||||
} else {
|
||||
ERROR("eMMC init failed");
|
||||
}
|
||||
|
||||
/* Initialization is failed, so deinit HW setting */
|
||||
if (result == 0)
|
||||
emmc_deinit();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to de-init SDIO controller for eMMC
|
||||
*/
|
||||
void emmc_deinit(void)
|
||||
{
|
||||
emmc_global_vars_ptr->init_done = 0;
|
||||
emmc_global_vars_ptr->sdHandle.card = 0;
|
||||
emmc_global_vars_ptr->sdHandle.device = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read eMMC memory
|
||||
* Returns read_size
|
||||
*/
|
||||
uint32_t emmc_read(uintptr_t mem_addr, uintptr_t storage_addr,
|
||||
size_t storage_size, size_t bytes_to_read)
|
||||
{
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
EMMC_TRACE("eMMC init is not done");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sdio_read(sdio_gethandle(), mem_addr, storage_addr,
|
||||
storage_size, bytes_to_read);
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE
|
||||
#define EXT_CSD_ERASE_GRP_SIZE 224
|
||||
|
||||
static int emmc_block_erase(uintptr_t mem_addr, size_t blocks)
|
||||
{
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
ERROR("eMMC init is not done");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return erase_card(sdio_gethandle(), mem_addr, blocks);
|
||||
}
|
||||
|
||||
int emmc_erase(uintptr_t mem_addr, size_t num_of_blocks, uint32_t partition)
|
||||
{
|
||||
int err = 0;
|
||||
size_t block_count = 0, blocks = 0;
|
||||
size_t erase_group = 0;
|
||||
|
||||
erase_group =
|
||||
emmc_global_buf_ptr->u.Ext_CSD_storage[EXT_CSD_ERASE_GRP_SIZE]*1024;
|
||||
|
||||
INFO("eMMC Erase Group Size=0x%lx\n", erase_group);
|
||||
|
||||
emmc_partition_select(partition);
|
||||
|
||||
while (block_count < num_of_blocks) {
|
||||
blocks = ((num_of_blocks - block_count) > erase_group) ?
|
||||
erase_group : (num_of_blocks - block_count);
|
||||
err = emmc_block_erase(mem_addr + block_count, blocks);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
block_count += blocks;
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
INFO("eMMC Erase of partition %d successful\n", partition);
|
||||
else
|
||||
ERROR("eMMC Erase of partition %d Failed(%i)\n", partition, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
|
||||
/*
|
||||
* Write to eMMC memory
|
||||
* Returns written_size
|
||||
*/
|
||||
uint32_t emmc_write(uintptr_t mem_addr, uintptr_t data_addr,
|
||||
size_t bytes_to_write)
|
||||
{
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
EMMC_TRACE("eMMC init is not done");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sdio_write(sd_handle, mem_addr, data_addr, bytes_to_write);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Send SDIO Cmd
|
||||
* Return 0 for pass condition
|
||||
*/
|
||||
uint32_t send_sdio_cmd(uint32_t cmdIndex, uint32_t argument,
|
||||
uint32_t options, struct sd_resp *resp)
|
||||
{
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
EMMC_TRACE("eMMC init is not done");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return send_cmd(sd_handle, cmdIndex, argument, options, resp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function return SDIO handle
|
||||
*/
|
||||
struct sd_handle *sdio_gethandle(void)
|
||||
{
|
||||
return &emmc_global_vars_ptr->sdHandle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize SDIO controller
|
||||
*/
|
||||
struct sd_handle *sdio_init(void)
|
||||
{
|
||||
uint32_t SDIO_base;
|
||||
struct sd_handle *p_sdhandle = &emmc_global_vars_ptr->sdHandle;
|
||||
|
||||
SDIO_base = EMMC_CTRL_REGS_BASE_ADDR;
|
||||
|
||||
if (SDIO_base == SDIO0_EMMCSDXC_SYSADDR)
|
||||
EMMC_TRACE(" ---> for SDIO 0 Controller\n\n");
|
||||
|
||||
memset(p_sdhandle, 0, sizeof(struct sd_handle));
|
||||
|
||||
p_sdhandle->device = &emmc_global_vars_ptr->sdDevice;
|
||||
p_sdhandle->card = &emmc_global_vars_ptr->sdCard;
|
||||
|
||||
memset(p_sdhandle->device, 0, sizeof(struct sd_dev));
|
||||
memset(p_sdhandle->card, 0, sizeof(struct sd_card_info));
|
||||
|
||||
if (chal_sd_start((CHAL_HANDLE *) p_sdhandle->device,
|
||||
SD_PIO_MODE, SDIO_base, SDIO_base) != SD_OK)
|
||||
return NULL;
|
||||
|
||||
set_config(p_sdhandle, SD_NORMAL_SPEED, MAX_CMD_RETRY, SD_DMA_OFF,
|
||||
SD_DMA_BOUNDARY_4K, EMMC_BLOCK_SIZE, EMMC_WFE_RETRY);
|
||||
|
||||
return &emmc_global_vars_ptr->sdHandle;
|
||||
}
|
||||
|
||||
uint32_t sdio_idle(struct sd_handle *p_sdhandle)
|
||||
{
|
||||
reset_card(p_sdhandle);
|
||||
|
||||
SD_US_DELAY(1000);
|
||||
|
||||
if (init_card(p_sdhandle, SD_CARD_DETECT_MMC) != SD_OK) {
|
||||
reset_card(p_sdhandle);
|
||||
reset_host_ctrl(p_sdhandle);
|
||||
return EMMC_BOOT_NO_CARD;
|
||||
}
|
||||
|
||||
return EMMC_BOOT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function read eMMC
|
||||
*/
|
||||
uint32_t sdio_read(struct sd_handle *p_sdhandle,
|
||||
uintptr_t mem_addr,
|
||||
uintptr_t storage_addr,
|
||||
size_t storage_size, size_t bytes_to_read)
|
||||
{
|
||||
uint32_t offset = 0, blockAddr, readLen = 0, rdCount;
|
||||
uint32_t remSize, manual_copy_size;
|
||||
uint8_t *outputBuf = (uint8_t *) storage_addr;
|
||||
const size_t blockSize = p_sdhandle->device->cfg.blockSize;
|
||||
|
||||
VERBOSE("EMMC READ: dst=0x%lx, src=0x%lx, size=0x%lx\n",
|
||||
storage_addr, mem_addr, bytes_to_read);
|
||||
|
||||
if (storage_size < bytes_to_read)
|
||||
/* Don't have sufficient storage to complete the operation */
|
||||
return 0;
|
||||
|
||||
/* Range check non high capacity memory */
|
||||
if ((p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) == 0) {
|
||||
if (mem_addr > 0x80000000)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* High capacity card use block address mode */
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) {
|
||||
blockAddr = (uint32_t) (mem_addr / blockSize);
|
||||
offset = (uint32_t) (mem_addr - (blockAddr * blockSize));
|
||||
} else {
|
||||
blockAddr = (uint32_t) (mem_addr / blockSize) * blockSize;
|
||||
offset = (uint32_t) (mem_addr - blockAddr);
|
||||
}
|
||||
|
||||
remSize = bytes_to_read;
|
||||
|
||||
rdCount = 0;
|
||||
|
||||
/* Process first unaligned block of MAX_READ_LENGTH */
|
||||
if (offset > 0) {
|
||||
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr, SD_MAX_READ_LENGTH)) {
|
||||
|
||||
if (remSize < (blockSize - offset)) {
|
||||
rdCount += remSize;
|
||||
manual_copy_size = remSize;
|
||||
remSize = 0; /* read is done */
|
||||
} else {
|
||||
remSize -= (blockSize - offset);
|
||||
rdCount += (blockSize - offset);
|
||||
manual_copy_size = blockSize - offset;
|
||||
}
|
||||
|
||||
/* Check for overflow */
|
||||
if (manual_copy_size > storage_size ||
|
||||
(((uintptr_t)outputBuf + manual_copy_size) >
|
||||
(storage_addr + storage_size))) {
|
||||
ERROR("EMMC READ: Overflow 1\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(outputBuf,
|
||||
(void *)((uintptr_t)
|
||||
(emmc_global_buf_ptr->u.tempbuf + offset)),
|
||||
manual_copy_size);
|
||||
|
||||
/* Update Physical address */
|
||||
outputBuf += manual_copy_size;
|
||||
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
|
||||
blockAddr++;
|
||||
else
|
||||
blockAddr += blockSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (remSize >= blockSize) {
|
||||
|
||||
if (remSize >= SD_MAX_BLK_TRANSFER_LENGTH)
|
||||
readLen = SD_MAX_BLK_TRANSFER_LENGTH;
|
||||
else
|
||||
readLen = (remSize / blockSize) * blockSize;
|
||||
|
||||
/* Check for overflow */
|
||||
if ((rdCount + readLen) > storage_size ||
|
||||
(((uintptr_t) outputBuf + readLen) >
|
||||
(storage_addr + storage_size))) {
|
||||
ERROR("EMMC READ: Overflow\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!read_block(p_sdhandle, outputBuf, blockAddr, readLen)) {
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
|
||||
blockAddr += (readLen / blockSize);
|
||||
else
|
||||
blockAddr += readLen;
|
||||
|
||||
remSize -= readLen;
|
||||
rdCount += readLen;
|
||||
|
||||
/* Update Physical address */
|
||||
outputBuf += readLen;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* process the last unaligned block reading */
|
||||
if (remSize > 0) {
|
||||
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr, SD_MAX_READ_LENGTH)) {
|
||||
|
||||
rdCount += remSize;
|
||||
/* Check for overflow */
|
||||
if (rdCount > storage_size ||
|
||||
(((uintptr_t) outputBuf + remSize) >
|
||||
(storage_addr + storage_size))) {
|
||||
ERROR("EMMC READ: Overflow\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(outputBuf,
|
||||
emmc_global_buf_ptr->u.tempbuf, remSize);
|
||||
|
||||
/* Update Physical address */
|
||||
outputBuf += remSize;
|
||||
} else {
|
||||
rdCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rdCount;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
|
||||
static uint32_t sdio_write(struct sd_handle *p_sdhandle, uintptr_t mem_addr,
|
||||
uintptr_t data_addr, size_t bytes_to_write)
|
||||
{
|
||||
|
||||
uint32_t offset, blockAddr, writeLen, wtCount = 0;
|
||||
uint32_t remSize, manual_copy_size = 0;
|
||||
|
||||
uint8_t *inputBuf = (uint8_t *)data_addr;
|
||||
|
||||
/* range check non high capacity memory */
|
||||
if ((p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) == 0) {
|
||||
if (mem_addr > 0x80000000)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the high capacity card use block address mode */
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) {
|
||||
blockAddr =
|
||||
(uint32_t)(mem_addr / p_sdhandle->device->cfg.blockSize);
|
||||
offset =
|
||||
(uint32_t)(mem_addr -
|
||||
blockAddr * p_sdhandle->device->cfg.blockSize);
|
||||
} else {
|
||||
blockAddr =
|
||||
((uint32_t)mem_addr / p_sdhandle->device->cfg.blockSize) *
|
||||
p_sdhandle->device->cfg.blockSize;
|
||||
offset = (uint32_t) mem_addr - blockAddr;
|
||||
}
|
||||
|
||||
remSize = bytes_to_write;
|
||||
|
||||
wtCount = 0;
|
||||
|
||||
/* process first unaligned block */
|
||||
if (offset > 0) {
|
||||
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr, p_sdhandle->device->cfg.blockSize)) {
|
||||
|
||||
if (remSize <
|
||||
(p_sdhandle->device->cfg.blockSize - offset))
|
||||
manual_copy_size = remSize;
|
||||
else
|
||||
manual_copy_size =
|
||||
p_sdhandle->device->cfg.blockSize - offset;
|
||||
|
||||
memcpy((void *)((uintptr_t)
|
||||
(emmc_global_buf_ptr->u.tempbuf + offset)),
|
||||
inputBuf,
|
||||
manual_copy_size);
|
||||
|
||||
/* Update Physical address */
|
||||
|
||||
if (!write_block(p_sdhandle,
|
||||
emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr,
|
||||
p_sdhandle->device->cfg.blockSize)) {
|
||||
|
||||
if (remSize <
|
||||
(p_sdhandle->device->cfg.blockSize -
|
||||
offset)) {
|
||||
wtCount += remSize;
|
||||
manual_copy_size = remSize;
|
||||
remSize = 0; /* read is done */
|
||||
} else {
|
||||
remSize -=
|
||||
(p_sdhandle->device->cfg.blockSize -
|
||||
offset);
|
||||
wtCount +=
|
||||
(p_sdhandle->device->cfg.blockSize -
|
||||
offset);
|
||||
manual_copy_size =
|
||||
p_sdhandle->device->cfg.blockSize -
|
||||
offset;
|
||||
}
|
||||
|
||||
inputBuf += manual_copy_size;
|
||||
|
||||
if (p_sdhandle->device->ctrl.ocr &
|
||||
SD_CARD_HIGH_CAPACITY)
|
||||
blockAddr++;
|
||||
else
|
||||
blockAddr +=
|
||||
p_sdhandle->device->cfg.blockSize;
|
||||
} else
|
||||
return 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* process block writing */
|
||||
while (remSize >= p_sdhandle->device->cfg.blockSize) {
|
||||
if (remSize >= SD_MAX_READ_LENGTH) {
|
||||
writeLen = SD_MAX_READ_LENGTH;
|
||||
} else {
|
||||
writeLen =
|
||||
(remSize / p_sdhandle->device->cfg.blockSize) *
|
||||
p_sdhandle->device->cfg.blockSize;
|
||||
}
|
||||
|
||||
if (!write_block(p_sdhandle, inputBuf, blockAddr, writeLen)) {
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
|
||||
blockAddr +=
|
||||
(writeLen /
|
||||
p_sdhandle->device->cfg.blockSize);
|
||||
else
|
||||
blockAddr += writeLen;
|
||||
|
||||
remSize -= writeLen;
|
||||
wtCount += writeLen;
|
||||
inputBuf += writeLen;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* process the last unaligned block reading */
|
||||
if (remSize > 0) {
|
||||
if (!read_block(p_sdhandle,
|
||||
emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr, p_sdhandle->device->cfg.blockSize)) {
|
||||
|
||||
memcpy(emmc_global_buf_ptr->u.tempbuf,
|
||||
inputBuf, remSize);
|
||||
|
||||
/* Update Physical address */
|
||||
|
||||
if (!write_block(p_sdhandle,
|
||||
emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr,
|
||||
p_sdhandle->device->cfg.blockSize)) {
|
||||
wtCount += remSize;
|
||||
inputBuf += remSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
wtCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return wtCount;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Function to put the card in Ready state by sending CMD0 and CMD1
|
||||
*/
|
||||
static int32_t bcm_emmc_card_ready_state(struct sd_handle *p_sdhandle)
|
||||
{
|
||||
int32_t result = 0;
|
||||
uint32_t argument = MMC_CMD_IDLE_RESET_ARG; /* Exit from Boot mode */
|
||||
|
||||
if (p_sdhandle) {
|
||||
send_sdio_cmd(SD_CMD_GO_IDLE_STATE, argument, 0, NULL);
|
||||
|
||||
result = reset_card(p_sdhandle);
|
||||
if (result != SD_OK) {
|
||||
EMMC_TRACE("eMMC Reset error\n");
|
||||
return SD_RESET_ERROR;
|
||||
}
|
||||
SD_US_DELAY(2000);
|
||||
result = mmc_cmd1(p_sdhandle);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user