diff options
Diffstat (limited to 'drivers/nxp/sd/sd_mmc.c')
-rw-r--r-- | drivers/nxp/sd/sd_mmc.c | 1496 |
1 files changed, 1496 insertions, 0 deletions
diff --git a/drivers/nxp/sd/sd_mmc.c b/drivers/nxp/sd/sd_mmc.c new file mode 100644 index 0000000..f7f48e7 --- /dev/null +++ b/drivers/nxp/sd/sd_mmc.c @@ -0,0 +1,1496 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + * + */ + +#include <endian.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/io/io_block.h> +#include "nxp_timer.h" +#include "sd_mmc.h" +#include <utils.h> +#include <utils_def.h> + + +/* Private structure for MMC driver data */ +static struct mmc mmc_drv_data; + +#ifndef NXP_POLICY_OTA +/* + * For NXP_POLICY_OTA, SD needs to do R/W on OCRAM. OCRAM is secure memory at + * default. SD can only do non-secure DMA. Configuring SD to work in PIO mode + * instead of DMA mode will make SD R/W on OCRAM available. + */ +/* To debug without dma comment this MACRO */ +#define NXP_SD_DMA_CAPABILITY +#endif +#define SD_TIMEOUT 1000 /* ms */ +#define SD_TIMEOUT_HIGH 20000 /* ms */ +#define SD_BLOCK_TIMEOUT 8 /* ms */ + +#define ERROR_ESDHC_CARD_DETECT_FAIL -1 +#define ERROR_ESDHC_UNUSABLE_CARD -2 +#define ERROR_ESDHC_COMMUNICATION_ERROR -3 +#define ERROR_ESDHC_BLOCK_LENGTH -4 +#define ERROR_ESDHC_DMA_ERROR -5 +#define ERROR_ESDHC_BUSY -6 + +/*************************************************************** + * Function : set_speed + * Arguments : mmc - Pointer to mmc struct + * clock - Clock Value to be set + * Return : void + * Description : Calculates the value of SDCLKFS and DVS to be set + * for getting the required clock assuming the base_clk + * as a fixed value (MAX_PLATFORM_CLOCK) + *****************************************************************/ +static void set_speed(struct mmc *mmc, uint32_t clock) +{ + /* sdhc_clk = (base clock) / [(SDCLKFS × 2) × (DVS +1)] */ + + uint32_t dvs = 1U; + uint32_t sdclkfs = 2U; + /* TBD - Change this to actual platform clock by reading via RCW */ + uint32_t base_clk = MAX_PLATFORM_CLOCK; + + if (base_clk / 16 > clock) { + for (sdclkfs = 2U; sdclkfs < 256U; sdclkfs *= 2U) { + if ((base_clk / sdclkfs) <= (clock * 16)) { + break; + } + } + } + + for (dvs = 1U; dvs <= 16U; dvs++) { + if ((base_clk / (dvs * sdclkfs)) <= clock) { + break; + } + } + + sdclkfs >>= 1U; + dvs -= 1U; + + esdhc_out32(&mmc->esdhc_regs->sysctl, + (ESDHC_SYSCTL_DTOCV(TIMEOUT_COUNTER_SDCLK_2_27) | + ESDHC_SYSCTL_SDCLKFS(sdclkfs) | ESDHC_SYSCTL_DVS(dvs) | + ESDHC_SYSCTL_SDCLKEN)); +} + +/*************************************************************************** + * Function : esdhc_init + * Arguments : mmc - Pointer to mmc struct + * card_detect - flag to indicate if card insert needs + * to be detected or not. For SDHC2 controller, Card detect + * is not present, so this field will be false + * Return : SUCCESS or Error Code + * Description : 1. Set Initial Clock Speed + * 2. Card Detect if not eMMC + * 3. Enable Controller Clock + * 4. Send 80 ticks for card to power up + * 5. Set LE mode and Bus Width as 1 bit. + ***************************************************************************/ +static int esdhc_init(struct mmc *mmc, bool card_detect) +{ + uint32_t val; + uint64_t start_time; + + /* Reset the entire host controller */ + val = esdhc_in32(&mmc->esdhc_regs->sysctl) | ESDHC_SYSCTL_RSTA; + esdhc_out32(&mmc->esdhc_regs->sysctl, val); + + /* Wait until the controller is available */ + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + val = esdhc_in32(&mmc->esdhc_regs->sysctl) & ESDHC_SYSCTL_RSTA; + if (val == 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->sysctl) & + (ESDHC_SYSCTL_RSTA); + if (val != 0U) { + ERROR("SD Reset failed\n"); + return ERROR_ESDHC_BUSY; + } + + /* Set initial clock speed */ + set_speed(mmc, CARD_IDENTIFICATION_FREQ); + + if (card_detect) { + /* Check CINS in prsstat register */ + val = esdhc_in32(&mmc->esdhc_regs->prsstat) & + ESDHC_PRSSTAT_CINS; + if (val == 0) { + ERROR("CINS not set in prsstat\n"); + return ERROR_ESDHC_CARD_DETECT_FAIL; + } + } + + /* Enable controller clock */ + val = esdhc_in32(&mmc->esdhc_regs->sysctl) | ESDHC_SYSCTL_SDCLKEN; + esdhc_out32(&mmc->esdhc_regs->sysctl, val); + + /* Send 80 clock ticks for the card to power up */ + val = esdhc_in32(&mmc->esdhc_regs->sysctl) | ESDHC_SYSCTL_INITA; + esdhc_out32(&mmc->esdhc_regs->sysctl, val); + + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT) { + val = esdhc_in32(&mmc->esdhc_regs->sysctl) & ESDHC_SYSCTL_INITA; + if (val != 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->sysctl) & ESDHC_SYSCTL_INITA; + if (val == 0U) { + ERROR("Failed to power up the card\n"); + return ERROR_ESDHC_CARD_DETECT_FAIL; + } + + INFO("Card detected successfully\n"); + + val = esdhc_in32(&mmc->esdhc_regs->proctl); + val = val | (ESDHC_PROCTL_EMODE_LE | ESDHC_PROCTL_DTW_1BIT); + + /* Set little endian mode, set bus width as 1-bit */ + esdhc_out32(&mmc->esdhc_regs->proctl, val); + + /* Enable cache snooping for DMA transactions */ + val = esdhc_in32(&mmc->esdhc_regs->ctl) | ESDHC_DCR_SNOOP; + esdhc_out32(&mmc->esdhc_regs->ctl, val); + + return 0; +} + +/*************************************************************************** + * Function : esdhc_send_cmd + * Arguments : mmc - Pointer to mmc struct + * cmd - Command Number + * args - Command Args + * Return : SUCCESS is 0, or Error Code ( < 0) + * Description : Updates the eSDHC registers cmdargs and xfertype + ***************************************************************************/ +static int esdhc_send_cmd(struct mmc *mmc, uint32_t cmd, uint32_t args) +{ + uint32_t val; + uint64_t start_time; + uint32_t xfertyp = 0; + + esdhc_out32(&mmc->esdhc_regs->irqstat, ESDHC_IRQSTAT_CLEAR_ALL); + + /* Wait for the command line & data line to be free */ + /* (poll the CIHB,CDIHB bit of the present state register) */ + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + val = esdhc_in32(&mmc->esdhc_regs->prsstat) & + (ESDHC_PRSSTAT_CIHB | ESDHC_PRSSTAT_CDIHB); + if (val == 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->prsstat) & + (ESDHC_PRSSTAT_CIHB | ESDHC_PRSSTAT_CDIHB); + if (val != 0U) { + ERROR("SD send cmd: Command Line or Data Line Busy cmd = %x\n", + cmd); + return ERROR_ESDHC_BUSY; + } + + if (cmd == CMD2 || cmd == CMD9) { + xfertyp |= ESDHC_XFERTYP_RSPTYP_136; + } else if (cmd == CMD7 || (cmd == CMD6 && mmc->card.type == MMC_CARD)) { + xfertyp |= ESDHC_XFERTYP_RSPTYP_48_BUSY; + } else if (cmd != CMD0) { + xfertyp |= ESDHC_XFERTYP_RSPTYP_48; + } + + if (cmd == CMD2 || cmd == CMD9) { + xfertyp |= ESDHC_XFERTYP_CCCEN; /* Command index check enable */ + } else if ((cmd != CMD0) && (cmd != ACMD41) && (cmd != CMD1)) { + xfertyp = xfertyp | ESDHC_XFERTYP_CCCEN | ESDHC_XFERTYP_CICEN; + } + + if ((cmd == CMD8 || cmd == CMD14 || cmd == CMD19) && + mmc->card.type == MMC_CARD) { + xfertyp |= ESDHC_XFERTYP_DPSEL; + if (cmd != CMD19) { + xfertyp |= ESDHC_XFERTYP_DTDSEL; + } + } + + if (cmd == CMD6 || cmd == CMD17 || cmd == CMD18 || cmd == CMD24 || + cmd == ACMD51) { + if (!(mmc->card.type == MMC_CARD && cmd == CMD6)) { + if (cmd == CMD24) { + xfertyp |= ESDHC_XFERTYP_DPSEL; + } else { + xfertyp |= (ESDHC_XFERTYP_DPSEL | + ESDHC_XFERTYP_DTDSEL); + } + } + + if (cmd == CMD18) { + xfertyp |= ESDHC_XFERTYP_BCEN; + if (mmc->dma_support != 0) { + /* Set BCEN of XFERTYP */ + xfertyp |= ESDHC_XFERTYP_DMAEN; + } + } + + if ((cmd == CMD17 || cmd == CMD24) && (mmc->dma_support != 0)) { + xfertyp |= ESDHC_XFERTYP_DMAEN; + } + } + + xfertyp |= ((cmd & 0x3F) << 24); + esdhc_out32(&mmc->esdhc_regs->cmdarg, args); + esdhc_out32(&mmc->esdhc_regs->xfertyp, xfertyp); + +#ifdef NXP_SD_DEBUG + INFO("cmd = %d\n", cmd); + INFO("args = %x\n", args); + INFO("xfertyp: = %x\n", xfertyp); +#endif + return 0; +} + +/*************************************************************************** + * Function : esdhc_wait_response + * Arguments : mmc - Pointer to mmc struct + * response - Value updated + * Return : SUCCESS - Response Received + * COMMUNICATION_ERROR - Command not Complete + * COMMAND_ERROR - CIE, CCE or CEBE error + * RESP_TIMEOUT - CTOE error + * Description : Checks for successful command completion. + * Clears the CC bit at the end. + ***************************************************************************/ +static int esdhc_wait_response(struct mmc *mmc, uint32_t *response) +{ + uint32_t val; + uint64_t start_time; + uint32_t status = 0U; + + /* Wait for the command to complete */ + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + val = esdhc_in32(&mmc->esdhc_regs->irqstat) & ESDHC_IRQSTAT_CC; + if (val != 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->irqstat) & ESDHC_IRQSTAT_CC; + if (val == 0U) { + ERROR("%s:IRQSTAT Cmd not complete(CC not set)\n", __func__); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + status = esdhc_in32(&mmc->esdhc_regs->irqstat); + + /* Check whether the interrupt is a CRC, CTOE or CIE error */ + if ((status & (ESDHC_IRQSTAT_CIE | ESDHC_IRQSTAT_CEBE | + ESDHC_IRQSTAT_CCE)) != 0) { + ERROR("%s: IRQSTAT CRC, CEBE or CIE error = %x\n", + __func__, status); + return COMMAND_ERROR; + } + + if ((status & ESDHC_IRQSTAT_CTOE) != 0) { + INFO("%s: IRQSTAT CTOE set = %x\n", __func__, status); + return RESP_TIMEOUT; + } + + if ((status & ESDHC_IRQSTAT_DMAE) != 0) { + ERROR("%s: IRQSTAT DMAE set = %x\n", __func__, status); + return ERROR_ESDHC_DMA_ERROR; + } + + if (response != NULL) { + /* Get response values from eSDHC CMDRSPx registers. */ + response[0] = esdhc_in32(&mmc->esdhc_regs->cmdrsp[0]); + response[1] = esdhc_in32(&mmc->esdhc_regs->cmdrsp[1]); + response[2] = esdhc_in32(&mmc->esdhc_regs->cmdrsp[2]); + response[3] = esdhc_in32(&mmc->esdhc_regs->cmdrsp[3]); +#ifdef NXP_SD_DEBUG + INFO("Resp R1 R2 R3 R4\n"); + INFO("Resp R1 = %x\n", response[0]); + INFO("R2 = %x\n", response[1]); + INFO("R3 = %x\n", response[2]); + INFO("R4 = %x\n", response[3]); + INFO("\n"); +#endif + } + + /* Clear the CC bit - w1c */ + val = esdhc_in32(&mmc->esdhc_regs->irqstat) | ESDHC_IRQSTAT_CC; + esdhc_out32(&mmc->esdhc_regs->irqstat, val); + + return 0; +} + +/*************************************************************************** + * Function : mmc_switch_to_high_frquency + * Arguments : mmc - Pointer to mmc struct + * Return : SUCCESS or Error Code + * Description : mmc card bellow ver 4.0 does not support high speed + * freq = 20 MHz + * Send CMD6 (CMD_SWITCH_FUNC) With args 0x03B90100 + * Send CMD13 (CMD_SEND_STATUS) + * if SWITCH Error, freq = 26 MHz + * if no error, freq = 52 MHz + ***************************************************************************/ +static int mmc_switch_to_high_frquency(struct mmc *mmc) +{ + int error; + uint32_t response[4]; + uint64_t start_time; + + mmc->card.bus_freq = MMC_SS_20MHZ; + /* mmc card bellow ver 4.0 does not support high speed */ + if (mmc->card.version < MMC_CARD_VERSION_4_X) { + return 0; + } + + /* send switch cmd to change the card to High speed */ + error = esdhc_send_cmd(mmc, CMD_SWITCH_FUNC, SET_EXT_CSD_HS_TIMING); + if (error != 0) { + return error; + } + error = esdhc_wait_response(mmc, response); + if (error != 0) { + return error; + } + + start_time = get_timer_val(0); + do { + /* check the status for which error */ + error = esdhc_send_cmd(mmc, + CMD_SEND_STATUS, mmc->card.rca << 16); + if (error != 0) { + return error; + } + + error = esdhc_wait_response(mmc, response); + if (error != 0) { + return error; + } + } while (((response[0] & SWITCH_ERROR) != 0) && + (get_timer_val(start_time) < SD_TIMEOUT)); + + /* Check for the present state of card */ + if ((response[0] & SWITCH_ERROR) != 0) { + mmc->card.bus_freq = MMC_HS_26MHZ; + } else { + mmc->card.bus_freq = MMC_HS_52MHZ; + } + + return 0; +} + +/*************************************************************************** + * Function : esdhc_set_data_attributes + * Arguments : mmc - Pointer to mmc struct + * blkcnt + * blklen + * Return : SUCCESS or Error Code + * Description : Set block attributes and watermark level register + ***************************************************************************/ +static int esdhc_set_data_attributes(struct mmc *mmc, uint32_t *dest_ptr, + uint32_t blkcnt, uint32_t blklen) +{ + uint32_t val; + uint64_t start_time; + uint32_t wml; + uint32_t wl; + uint32_t dst = (uint32_t)((uint64_t)(dest_ptr)); + + /* set blkattr when no transactions are executing */ + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + val = esdhc_in32(&mmc->esdhc_regs->prsstat) & ESDHC_PRSSTAT_DLA; + if (val == 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->prsstat) & ESDHC_PRSSTAT_DLA; + if (val != 0U) { + ERROR("%s: Data line active.Can't set attribute\n", __func__); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + wml = esdhc_in32(&mmc->esdhc_regs->wml); + wml &= ~(ESDHC_WML_WR_BRST_MASK | ESDHC_WML_RD_BRST_MASK | + ESDHC_WML_RD_WML_MASK | ESDHC_WML_WR_WML_MASK); + + if ((mmc->dma_support != 0) && (dest_ptr != NULL)) { + /* Set burst length to 128 bytes */ + esdhc_out32(&mmc->esdhc_regs->wml, + wml | ESDHC_WML_WR_BRST(BURST_128_BYTES)); + esdhc_out32(&mmc->esdhc_regs->wml, + wml | ESDHC_WML_RD_BRST(BURST_128_BYTES)); + + /* Set DMA System Destination Address */ + esdhc_out32(&mmc->esdhc_regs->dsaddr, dst); + } else { + wl = (blklen >= BLOCK_LEN_512) ? + WML_512_BYTES : ((blklen + 3) / 4); + /* Set 'Read Water Mark Level' register */ + esdhc_out32(&mmc->esdhc_regs->wml, wml | ESDHC_WML_RD_WML(wl)); + } + + /* Configure block Attributes register */ + esdhc_out32(&mmc->esdhc_regs->blkattr, + ESDHC_BLKATTR_BLKCNT(blkcnt) | ESDHC_BLKATTR_BLKSZE(blklen)); + + mmc->block_len = blklen; + + return 0; +} + +/*************************************************************************** + * Function : esdhc_read_data_nodma + * Arguments : mmc - Pointer to mmc struct + * dest_ptr - Bufffer where read data is to be copied + * len - Length of Data to be read + * Return : SUCCESS or Error Code + * Description : Read data from the sdhc buffer without using DMA + * and using polling mode + ***************************************************************************/ +static int esdhc_read_data_nodma(struct mmc *mmc, void *dest_ptr, uint32_t len) +{ + uint32_t i = 0U; + uint32_t status; + uint32_t num_blocks; + uint32_t *dst = (uint32_t *)dest_ptr; + uint32_t val; + uint64_t start_time; + + num_blocks = len / mmc->block_len; + + while ((num_blocks--) != 0U) { + + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + val = esdhc_in32(&mmc->esdhc_regs->prsstat) & + ESDHC_PRSSTAT_BREN; + if (val != 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->prsstat) + & ESDHC_PRSSTAT_BREN; + if (val == 0U) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + for (i = 0U, status = esdhc_in32(&mmc->esdhc_regs->irqstat); + i < mmc->block_len / 4; i++, dst++) { + /* get data from data port */ + val = mmio_read_32( + (uintptr_t)&mmc->esdhc_regs->datport); + esdhc_out32(dst, val); + /* Increment destination pointer */ + status = esdhc_in32(&mmc->esdhc_regs->irqstat); + } + /* Check whether the interrupt is an DTOE/DCE/DEBE */ + if ((status & (ESDHC_IRQSTAT_DTOE | ESDHC_IRQSTAT_DCE | + ESDHC_IRQSTAT_DEBE)) != 0) { + ERROR("SD read error - DTOE, DCE, DEBE bit set = %x\n", + status); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + } + + /* Wait for TC */ + + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + val = esdhc_in32(&mmc->esdhc_regs->irqstat) & ESDHC_IRQSTAT_TC; + if (val != 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->irqstat) & ESDHC_IRQSTAT_TC; + if (val == 0U) { + ERROR("SD read timeout: Transfer bit not set in IRQSTAT\n"); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + return 0; +} + +/*************************************************************************** + * Function : esdhc_write_data_nodma + * Arguments : mmc - Pointer to mmc struct + * src_ptr - Buffer where data is copied from + * len - Length of Data to be written + * Return : SUCCESS or Error Code + * Description : Write data to the sdhc buffer without using DMA + * and using polling mode + ***************************************************************************/ +static int esdhc_write_data_nodma(struct mmc *mmc, void *src_ptr, uint32_t len) +{ + uint32_t i = 0U; + uint32_t status; + uint32_t num_blocks; + uint32_t *src = (uint32_t *)src_ptr; + uint32_t val; + uint64_t start_time; + + num_blocks = len / mmc->block_len; + + while ((num_blocks--) != 0U) { + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + val = esdhc_in32(&mmc->esdhc_regs->prsstat) & + ESDHC_PRSSTAT_BWEN; + if (val != 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->prsstat) & + ESDHC_PRSSTAT_BWEN; + if (val == 0U) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + for (i = 0U, status = esdhc_in32(&mmc->esdhc_regs->irqstat); + i < mmc->block_len / 4; i++, src++) { + val = esdhc_in32(src); + /* put data to data port */ + mmio_write_32((uintptr_t)&mmc->esdhc_regs->datport, + val); + /* Increment source pointer */ + status = esdhc_in32(&mmc->esdhc_regs->irqstat); + } + /* Check whether the interrupt is an DTOE/DCE/DEBE */ + if ((status & (ESDHC_IRQSTAT_DTOE | ESDHC_IRQSTAT_DCE | + ESDHC_IRQSTAT_DEBE)) != 0) { + ERROR("SD write error - DTOE, DCE, DEBE bit set = %x\n", + status); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + } + + /* Wait for TC */ + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + val = esdhc_in32(&mmc->esdhc_regs->irqstat) & ESDHC_IRQSTAT_TC; + if (val != 0U) { + break; + } + } + + val = esdhc_in32(&mmc->esdhc_regs->irqstat) & ESDHC_IRQSTAT_TC; + if (val == 0U) { + ERROR("SD write timeout: Transfer bit not set in IRQSTAT\n"); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + return 0; +} + +/*************************************************************************** + * Function : esdhc_read_data_dma + * Arguments : mmc - Pointer to mmc struct + * len - Length of Data to be read + * Return : SUCCESS or Error Code + * Description : Read data from the sd card using DMA. + ***************************************************************************/ +static int esdhc_read_data_dma(struct mmc *mmc, uint32_t len) +{ + uint32_t status; + uint32_t tblk; + uint64_t start_time; + + tblk = SD_BLOCK_TIMEOUT * (len / mmc->block_len); + + start_time = get_timer_val(0); + + /* poll till TC is set */ + do { + status = esdhc_in32(&mmc->esdhc_regs->irqstat); + + if ((status & (ESDHC_IRQSTAT_DEBE | ESDHC_IRQSTAT_DCE + | ESDHC_IRQSTAT_DTOE)) != 0) { + ERROR("SD read error - DTOE, DCE, DEBE bit set = %x\n", + status); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + if ((status & ESDHC_IRQSTAT_DMAE) != 0) { + ERROR("SD read error - DMA error = %x\n", status); + return ERROR_ESDHC_DMA_ERROR; + } + + } while (((status & ESDHC_IRQSTAT_TC) == 0) && + ((esdhc_in32(&mmc->esdhc_regs->prsstat) & ESDHC_PRSSTAT_DLA) != 0) && + (get_timer_val(start_time) < SD_TIMEOUT_HIGH + tblk)); + + if (get_timer_val(start_time) > SD_TIMEOUT_HIGH + tblk) { + ERROR("SD read DMA timeout\n"); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + return 0; +} + +/*************************************************************************** + * Function : esdhc_write_data_dma + * Arguments : mmc - Pointer to mmc struct + * len - Length of Data to be written + * Return : SUCCESS or Error Code + * Description : Write data to the sd card using DMA. + ***************************************************************************/ +static int esdhc_write_data_dma(struct mmc *mmc, uint32_t len) +{ + uint32_t status; + uint32_t tblk; + uint64_t start_time; + + tblk = SD_BLOCK_TIMEOUT * (len / mmc->block_len); + + start_time = get_timer_val(0); + + /* poll till TC is set */ + do { + status = esdhc_in32(&mmc->esdhc_regs->irqstat); + + if ((status & (ESDHC_IRQSTAT_DEBE | ESDHC_IRQSTAT_DCE + | ESDHC_IRQSTAT_DTOE)) != 0) { + ERROR("SD write error - DTOE, DCE, DEBE bit set = %x\n", + status); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + if ((status & ESDHC_IRQSTAT_DMAE) != 0) { + ERROR("SD write error - DMA error = %x\n", status); + return ERROR_ESDHC_DMA_ERROR; + } + } while (((status & ESDHC_IRQSTAT_TC) == 0) && + ((esdhc_in32(&mmc->esdhc_regs->prsstat) & ESDHC_PRSSTAT_DLA) != 0) && + (get_timer_val(start_time) < SD_TIMEOUT_HIGH + tblk)); + + if (get_timer_val(start_time) > SD_TIMEOUT_HIGH + tblk) { + ERROR("SD write DMA timeout\n"); + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + return 0; +} + +/*************************************************************************** + * Function : esdhc_read_data + * Arguments : mmc - Pointer to mmc struct + * dest_ptr - Bufffer where read data is to be copied + * len - Length of Data to be read + * Return : SUCCESS or Error Code + * Description : Calls esdhc_read_data_nodma and clear interrupt status + ***************************************************************************/ +int esdhc_read_data(struct mmc *mmc, void *dest_ptr, uint32_t len) +{ + int ret; + + if (mmc->dma_support && len > 64) { + ret = esdhc_read_data_dma(mmc, len); + } else { + ret = esdhc_read_data_nodma(mmc, dest_ptr, len); + } + + /* clear interrupt status */ + esdhc_out32(&mmc->esdhc_regs->irqstat, ESDHC_IRQSTAT_CLEAR_ALL); + + return ret; +} + +/*************************************************************************** + * Function : esdhc_write_data + * Arguments : mmc - Pointer to mmc struct + * src_ptr - Buffer where data is copied from + * len - Length of Data to be written + * Return : SUCCESS or Error Code + * Description : Calls esdhc_write_data_nodma and clear interrupt status + ***************************************************************************/ +int esdhc_write_data(struct mmc *mmc, void *src_ptr, uint32_t len) +{ + int ret; + + if (mmc->dma_support && len > 64) { + ret = esdhc_write_data_dma(mmc, len); + } else { + ret = esdhc_write_data_nodma(mmc, src_ptr, len); + } + + /* clear interrupt status */ + esdhc_out32(&mmc->esdhc_regs->irqstat, ESDHC_IRQSTAT_CLEAR_ALL); + + return ret; +} + +/*************************************************************************** + * Function : sd_switch_to_high_freq + * Arguments : mmc - Pointer to mmc struct + * Return : SUCCESS or Error Code + * Description : 1. Send ACMD51 (CMD_SEND_SCR) + * 2. Read the SCR to check if card supports higher freq + * 3. check version from SCR + * 4. If SD 1.0, return (no Switch) freq = 25 MHz. + * 5. Send CMD6 (CMD_SWITCH_FUNC) with args 0x00FFFFF1 to + * check the status of switch func + * 6. Send CMD6 (CMD_SWITCH_FUNC) With args 0x80FFFFF1 to + * switch to high frequency = 50 Mhz + ***************************************************************************/ +static int sd_switch_to_high_freq(struct mmc *mmc) +{ + int err; + uint8_t scr[8]; + uint8_t status[64]; + uint32_t response[4]; + uint32_t version; + uint32_t count; + uint32_t sd_versions[] = {SD_CARD_VERSION_1_0, SD_CARD_VERSION_1_10, + SD_CARD_VERSION_2_0}; + + mmc->card.bus_freq = SD_SS_25MHZ; + /* Send Application command */ + err = esdhc_send_cmd(mmc, CMD_APP_CMD, mmc->card.rca << 16); + if (err != 0) { + return err; + } + + err = esdhc_wait_response(mmc, response); + if (err != 0) { + return err; + } + + esdhc_set_data_attributes(mmc, NULL, 1, 8); + /* Read the SCR to find out if this card supports higher speeds */ + err = esdhc_send_cmd(mmc, CMD_SEND_SCR, mmc->card.rca << 16); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, response); + if (err != 0) { + return err; + } + + /* read 8 bytes of scr data */ + err = esdhc_read_data(mmc, scr, 8U); + if (err != 0) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + /* check version from SCR */ + version = scr[0] & U(0xF); + if (version <= 2U) { + mmc->card.version = sd_versions[version]; + } else { + mmc->card.version = SD_CARD_VERSION_2_0; + } + + /* does not support switch func */ + if (mmc->card.version == SD_CARD_VERSION_1_0) { + return 0; + } + + /* read 64 bytes of status */ + esdhc_set_data_attributes(mmc, NULL, 1U, 64U); + + /* check the status of switch func */ + for (count = 0U; count < 4U; count++) { + err = esdhc_send_cmd(mmc, CMD_SWITCH_FUNC, + SD_SWITCH_FUNC_CHECK_MODE); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, response); + if (err != 0) { + return err; + } + /* read 64 bytes of scr data */ + err = esdhc_read_data(mmc, status, 64U); + if (err != 0) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + if ((status[29] & SD_SWITCH_FUNC_HIGH_SPEED) == 0) { + break; + } + } + + if ((status[13] & SD_SWITCH_FUNC_HIGH_SPEED) == 0) { + return 0; + } + + /* SWITCH */ + esdhc_set_data_attributes(mmc, NULL, 1, 64); + err = esdhc_send_cmd(mmc, CMD_SWITCH_FUNC, SD_SWITCH_FUNC_SWITCH_MODE); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, response); + if (err != 0) { + return err; + } + + err = esdhc_read_data(mmc, status, 64U); + if (err != 0) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + if ((status[16]) == U(0x01)) { + mmc->card.bus_freq = SD_HS_50MHZ; + } + + return 0; +} + +/*************************************************************************** + * Function : change_state_to_transfer_state + * Arguments : mmc - Pointer to mmc struct + * Return : SUCCESS or Error Code + * Description : 1. Send CMD7 (CMD_SELECT_CARD) to toggles the card + * between stand-by and transfer state + * 2. Send CMD13 (CMD_SEND_STATUS) to check state as + * Transfer State + ***************************************************************************/ +static int change_state_to_transfer_state(struct mmc *mmc) +{ + int error = 0; + uint32_t response[4]; + uint64_t start_time; + + /* Command CMD_SELECT_CARD/CMD7 toggles the card between stand-by + * and transfer states + */ + error = esdhc_send_cmd(mmc, CMD_SELECT_CARD, mmc->card.rca << 16); + if (error != 0) { + return error; + } + error = esdhc_wait_response(mmc, response); + if (error != 0) { + return error; + } + + start_time = get_timer_val(0); + while (get_timer_val(start_time) < SD_TIMEOUT_HIGH) { + /* send CMD13 to check card status */ + error = esdhc_send_cmd(mmc, + CMD_SEND_STATUS, mmc->card.rca << 16); + if (error != 0) { + return error; + } + error = esdhc_wait_response(mmc, response); + if ((error != 0) || ((response[0] & R1_ERROR) != 0)) { + return error; + } + + /* Check for the present state of card */ + if (((response[0] >> 9U) & U(0xF)) == STATE_TRAN) { + break; + } + } + if (((response[0] >> 9U) & U(0xF)) == STATE_TRAN) { + return 0; + } else { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } +} + +/*************************************************************************** + * Function : get_cid_rca_csd + * Arguments : mmc - Pointer to mmc struct + * Return : SUCCESS or Error Code + * Description : 1. Send CMD2 (CMD_ALL_SEND_CID) + * 2. get RCA for SD cards, set rca for mmc cards + * Send CMD3 (CMD_SEND_RELATIVE_ADDR) + * 3. Send CMD9 (CMD_SEND_CSD) + * 4. Get MMC Version from CSD + ***************************************************************************/ +static int get_cid_rca_csd(struct mmc *mmc) +{ + int err; + uint32_t version; + uint32_t response[4]; + uint32_t mmc_version[] = {MMC_CARD_VERSION_1_2, MMC_CARD_VERSION_1_4, + MMC_CARD_VERSION_2_X, MMC_CARD_VERSION_3_X, + MMC_CARD_VERSION_4_X}; + + err = esdhc_send_cmd(mmc, CMD_ALL_SEND_CID, 0); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, response); + if (err != 0) { + return err; + } + + /* get RCA for SD cards, set rca for mmc cards */ + mmc->card.rca = SD_MMC_CARD_RCA; + + /* send RCA cmd */ + err = esdhc_send_cmd(mmc, CMD_SEND_RELATIVE_ADDR, mmc->card.rca << 16); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, response); + if (err != 0) { + return err; + } + + /* for SD, get the the RCA */ + if (mmc->card.type == SD_CARD) { + mmc->card.rca = (response[0] >> 16) & 0xFFFF; + } + + /* Get the CSD (card specific data) from card. */ + err = esdhc_send_cmd(mmc, CMD_SEND_CSD, mmc->card.rca << 16); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, response); + if (err != 0) { + return err; + } + + version = (response[3] >> 18U) & U(0xF); + if (mmc->card.type == MMC_CARD) { + if (version <= MMC_CARD_VERSION_4_X) { + mmc->card.version = mmc_version[version]; + } else { + mmc->card.version = MMC_CARD_VERSION_4_X; + } + } + + mmc->card.block_len = 1 << ((response[2] >> 8) & 0xF); + + if (mmc->card.block_len > BLOCK_LEN_512) { + mmc->card.block_len = BLOCK_LEN_512; + } + + return 0; +} + +/*************************************************************************** + * Function : identify_mmc_card + * Arguments : mmc - Pointer to mmc struct + * Return : SUCCESS or Error Code + * Description : 1. Send Reset Command + * 2. Send CMD1 with args to set voltage range and Sector + * Mode. (Voltage Args = 0xFF8) + * 3. Check the OCR Response + ***************************************************************************/ +static int identify_mmc_card(struct mmc *mmc) +{ + uint64_t start_time; + uint32_t resp[4]; + int ret; + uint32_t args; + + /* card reset */ + ret = esdhc_send_cmd(mmc, CMD_GO_IDLE_STATE, 0U); + if (ret != 0) { + return ret; + } + ret = esdhc_wait_response(mmc, resp); + if (ret != 0) { + return ret; + } + + /* Send CMD1 to get the ocr value repeatedly till the card */ + /* busy is clear. timeout = 20sec */ + + start_time = get_timer_val(0); + do { + /* set the bits for the voltage ranges supported by host */ + args = mmc->voltages_caps | MMC_OCR_SECTOR_MODE; + ret = esdhc_send_cmd(mmc, CMD_MMC_SEND_OP_COND, args); + if (ret != 0) { + return ret; + } + ret = esdhc_wait_response(mmc, resp); + if (ret != 0) { + return ERROR_ESDHC_UNUSABLE_CARD; + } + } while (((resp[0] & MMC_OCR_BUSY) == 0U) && + (get_timer_val(start_time) < SD_TIMEOUT_HIGH)); + + if (get_timer_val(start_time) > SD_TIMEOUT_HIGH) { + return ERROR_ESDHC_UNUSABLE_CARD; + } + + if ((resp[0] & MMC_OCR_CCS) == MMC_OCR_CCS) { + mmc->card.is_high_capacity = 1; + } + + return MMC_CARD; +} + +/*************************************************************************** + * Function : check_for_sd_card + * Arguments : mmc - Pointer to mmc struct + * Return : SUCCESS or Error Code + * Description : 1. Send Reset Command + * 2. Send CMD8 with pattern 0xAA (to check for SD 2.0) + * 3. Send ACMD41 with args to set voltage range and HCS + * HCS is set only for SD Card > 2.0 + * Voltage Caps = 0xFF8 + * 4. Check the OCR Response + ***************************************************************************/ +static int check_for_sd_card(struct mmc *mmc) +{ + uint64_t start_time; + uint32_t args; + int ret; + uint32_t resp[4]; + + /* Send reset command */ + ret = esdhc_send_cmd(mmc, CMD_GO_IDLE_STATE, 0U); + if (ret != 0) { + return ret; + } + ret = esdhc_wait_response(mmc, resp); + if (ret != 0) { + return ret; + } + + /* send CMD8 with pattern 0xAA */ + args = MMC_VDD_HIGH_VOLTAGE | 0xAA; + ret = esdhc_send_cmd(mmc, CMD_SEND_IF_COND, args); + if (ret != 0) { + return ret; + } + ret = esdhc_wait_response(mmc, resp); + if (ret == RESP_TIMEOUT) { /* sd ver 1.x or not sd */ + mmc->card.is_high_capacity = 0; + } else if ((resp[0] & U(0xFF)) == U(0xAA)) { /* ver 2.0 or later */ + mmc->card.version = SD_CARD_VERSION_2_0; + } else { + return NOT_SD_CARD; + } + /* Send Application command-55 to get the ocr value repeatedly till + * the card busy is clear. timeout = 20sec + */ + + start_time = get_timer_val(0); + do { + ret = esdhc_send_cmd(mmc, CMD_APP_CMD, 0U); + if (ret != 0) { + return ret; + } + ret = esdhc_wait_response(mmc, resp); + if (ret == COMMAND_ERROR) { + return ERROR_ESDHC_UNUSABLE_CARD; + } + + /* set the bits for the voltage ranges supported by host */ + args = mmc->voltages_caps; + if (mmc->card.version == SD_CARD_VERSION_2_0) { + args |= SD_OCR_HCS; + } + + /* Send ACMD41 to set voltage range */ + ret = esdhc_send_cmd(mmc, CMD_SD_SEND_OP_COND, args); + if (ret != 0) { + return ret; + } + ret = esdhc_wait_response(mmc, resp); + if (ret == COMMAND_ERROR) { + return ERROR_ESDHC_UNUSABLE_CARD; + } else if (ret == RESP_TIMEOUT) { + return NOT_SD_CARD; + } + } while (((resp[0] & MMC_OCR_BUSY) == 0U) && + (get_timer_val(start_time) < SD_TIMEOUT_HIGH)); + + if (get_timer_val(start_time) > SD_TIMEOUT_HIGH) { + INFO("SD_TIMEOUT_HIGH\n"); + return ERROR_ESDHC_UNUSABLE_CARD; + } + + /* bit set in card capacity status */ + if ((resp[0] & MMC_OCR_CCS) == MMC_OCR_CCS) { + mmc->card.is_high_capacity = 1; + } + + return SD_CARD; +} + +/*************************************************************************** + * Function : esdhc_emmc_init + * Arguments : mmc - Pointer to mmc struct + * src_emmc - Flag to Indicate SRC as emmc + * Return : SUCCESS or Error Code (< 0) + * Description : Base Function called from sd_mmc_init or emmc_init + ***************************************************************************/ +int esdhc_emmc_init(struct mmc *mmc, bool card_detect) +{ + int error = 0; + int ret = 0; + + error = esdhc_init(mmc, card_detect); + if (error != 0) { + return error; + } + + mmc->card.bus_freq = CARD_IDENTIFICATION_FREQ; + mmc->card.rca = 0; + mmc->card.is_high_capacity = 0; + mmc->card.type = ERROR_ESDHC_UNUSABLE_CARD; + + /* Set Voltage caps as FF8 i.e all supported */ + /* high voltage bits 2.7 - 3.6 */ + mmc->voltages_caps = MMC_OCR_VDD_FF8; + +#ifdef NXP_SD_DMA_CAPABILITY + /* Getting host DMA capabilities. */ + mmc->dma_support = esdhc_in32(&mmc->esdhc_regs->hostcapblt) & + ESDHC_HOSTCAPBLT_DMAS; +#else + mmc->dma_support = 0; +#endif + + ret = NOT_SD_CARD; + /* If SRC is not EMMC, check for SD or MMC */ + ret = check_for_sd_card(mmc); + switch (ret) { + case SD_CARD: + mmc->card.type = SD_CARD; + break; + + case NOT_SD_CARD: + /* try for MMC card */ + if (identify_mmc_card(mmc) == MMC_CARD) { + mmc->card.type = MMC_CARD; + } else { + return ERROR_ESDHC_UNUSABLE_CARD; + } + break; + + default: + return ERROR_ESDHC_UNUSABLE_CARD; + } + + /* get CID, RCA and CSD. For MMC, set the rca */ + error = get_cid_rca_csd(mmc); + if (error != 0) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + /* change state to Transfer mode */ + error = change_state_to_transfer_state(mmc); + if (error != 0) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + /* change to high frequency if supported */ + if (mmc->card.type == SD_CARD) { + error = sd_switch_to_high_freq(mmc); + } else { + error = mmc_switch_to_high_frquency(mmc); + } + if (error != 0) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + /* mmc: 20000000, 26000000, 52000000 */ + /* sd: 25000000, 50000000 */ + set_speed(mmc, mmc->card.bus_freq); + + INFO("init done:\n"); + return 0; +} + +/*************************************************************************** + * Function : sd_mmc_init + * Arguments : mmc - Pointer to mmc struct + * Return : SUCCESS or Error Code + * Description : Base Function called via hal_init for SD/MMC + * initialization + ***************************************************************************/ +int sd_mmc_init(uintptr_t nxp_esdhc_addr, bool card_detect) +{ + struct mmc *mmc = NULL; + int ret; + + mmc = &mmc_drv_data; + memset(mmc, 0, sizeof(struct mmc)); + mmc->esdhc_regs = (struct esdhc_regs *)nxp_esdhc_addr; + + INFO("esdhc_emmc_init\n"); + ret = esdhc_emmc_init(mmc, card_detect); + return ret; +} + +/*************************************************************************** + * Function : esdhc_read_block + * Arguments : mmc - Pointer to mmc struct + * dst - Destination Pointer + * block - Block Number + * Return : SUCCESS or Error Code + * Description : Read a Single block to Destination Pointer + * 1. Send CMD16 (CMD_SET_BLOCKLEN) with args as blocklen + * 2. Send CMD17 (CMD_READ_SINGLE_BLOCK) with args offset + ***************************************************************************/ +static int esdhc_read_block(struct mmc *mmc, void *dst, uint32_t block) +{ + uint32_t offset; + int err; + + /* send cmd16 to set the block size. */ + err = esdhc_send_cmd(mmc, CMD_SET_BLOCKLEN, mmc->card.block_len); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, NULL); + if (err != 0) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + if (mmc->card.is_high_capacity != 0) { + offset = block; + } else { + offset = block * mmc->card.block_len; + } + + esdhc_set_data_attributes(mmc, dst, 1, mmc->card.block_len); + err = esdhc_send_cmd(mmc, CMD_READ_SINGLE_BLOCK, offset); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, NULL); + if (err != 0) { + return err; + } + + err = esdhc_read_data(mmc, dst, mmc->card.block_len); + + return err; +} + +/*************************************************************************** + * Function : esdhc_write_block + * Arguments : mmc - Pointer to mmc struct + * src - Source Pointer + * block - Block Number + * Return : SUCCESS or Error Code + * Description : Write a Single block from Source Pointer + * 1. Send CMD16 (CMD_SET_BLOCKLEN) with args as blocklen + * 2. Send CMD24 (CMD_WRITE_SINGLE_BLOCK) with args offset + ***************************************************************************/ +static int esdhc_write_block(struct mmc *mmc, void *src, uint32_t block) +{ + uint32_t offset; + int err; + + /* send cmd16 to set the block size. */ + err = esdhc_send_cmd(mmc, CMD_SET_BLOCKLEN, mmc->card.block_len); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, NULL); + if (err != 0) { + return ERROR_ESDHC_COMMUNICATION_ERROR; + } + + if (mmc->card.is_high_capacity != 0) { + offset = block; + } else { + offset = block * mmc->card.block_len; + } + + esdhc_set_data_attributes(mmc, src, 1, mmc->card.block_len); + err = esdhc_send_cmd(mmc, CMD_WRITE_SINGLE_BLOCK, offset); + if (err != 0) { + return err; + } + err = esdhc_wait_response(mmc, NULL); + if (err != 0) { + return err; + } + + err = esdhc_write_data(mmc, src, mmc->card.block_len); + + return err; +} + +/*************************************************************************** + * Function : esdhc_read + * Arguments : src_offset - offset on sd/mmc to read from. Should be block + * size aligned + * dst - Destination Pointer + * size - Length of Data ( Multiple of block size) + * Return : SUCCESS or Error Code + * Description : Calls esdhc_read_block repeatedly for reading the + * data. + ***************************************************************************/ +int esdhc_read(struct mmc *mmc, uint32_t src_offset, uintptr_t dst, size_t size) +{ + int error = 0; + uint32_t blk, num_blocks; + uint8_t *buff = (uint8_t *)dst; + +#ifdef NXP_SD_DEBUG + INFO("sd mmc read\n"); + INFO("src = %x, dst = %lxsize = %lu\n", src_offset, dst, size); +#endif + + /* check for size */ + if (size == 0) { + return 0; + } + + if ((size % mmc->card.block_len) != 0) { + ERROR("Size is not block aligned\n"); + return -1; + } + + if ((src_offset % mmc->card.block_len) != 0) { + ERROR("Size is not block aligned\n"); + return -1; + } + + /* start block */ + blk = src_offset / mmc->card.block_len; +#ifdef NXP_SD_DEBUG + INFO("blk = %x\n", blk); +#endif + + /* Number of blocks to be read */ + num_blocks = size / mmc->card.block_len; + + while (num_blocks) { + error = esdhc_read_block(mmc, buff, blk); + if (error != 0) { + ERROR("Read error = %x\n", error); + return error; + } + + buff = buff + mmc->card.block_len; + blk++; + num_blocks--; + } + + INFO("sd-mmc read done.\n"); + return error; +} + +/*************************************************************************** + * Function : esdhc_write + * Arguments : src - Source Pointer + * dst_offset - offset on sd/mmc to write to. Should be block + * size aligned + * size - Length of Data (Multiple of block size) + * Return : SUCCESS or Error Code + * Description : Calls esdhc_write_block repeatedly for writing the + * data. + ***************************************************************************/ +int esdhc_write(struct mmc *mmc, uintptr_t src, uint32_t dst_offset, + size_t size) +{ + int error = 0; + uint32_t blk, num_blocks; + uint8_t *buff = (uint8_t *)src; + +#ifdef NXP_SD_DEBUG + INFO("sd mmc write\n"); + INFO("src = %x, dst = %lxsize = %lu\n", src, dst_offset, size); +#endif + + /* check for size */ + if (size == 0) { + return 0; + } + + if ((size % mmc->card.block_len) != 0) { + ERROR("Size is not block aligned\n"); + return -1; + } + + if ((dst_offset % mmc->card.block_len) != 0) { + ERROR("Size is not block aligned\n"); + return -1; + } + + /* start block */ + blk = dst_offset / mmc->card.block_len; +#ifdef NXP_SD_DEBUG + INFO("blk = %x\n", blk); +#endif + + /* Number of blocks to be written */ + num_blocks = size / mmc->card.block_len; + + while (num_blocks != 0U) { + error = esdhc_write_block(mmc, buff, blk); + if (error != 0U) { + ERROR("Write error = %x\n", error); + return error; + } + + buff = buff + mmc->card.block_len; + blk++; + num_blocks--; + } + + INFO("sd-mmc write done.\n"); + return error; +} + +static size_t ls_sd_emmc_read(int lba, uintptr_t buf, size_t size) +{ + struct mmc *mmc = NULL; + int ret; + + mmc = &mmc_drv_data; + lba *= BLOCK_LEN_512; + ret = esdhc_read(mmc, lba, buf, size); + return ret ? 0 : size; +} + +static struct io_block_dev_spec ls_emmc_dev_spec = { + .buffer = { + .offset = 0, + .length = 0, + }, + .ops = { + .read = ls_sd_emmc_read, + }, + .block_size = BLOCK_LEN_512, +}; + +int sd_emmc_init(uintptr_t *block_dev_spec, + uintptr_t nxp_esdhc_addr, + size_t nxp_sd_block_offset, + size_t nxp_sd_block_size, + bool card_detect) +{ + int ret; + + ret = sd_mmc_init(nxp_esdhc_addr, card_detect); + if (ret != 0) { + return ret; + } + + ls_emmc_dev_spec.buffer.offset = nxp_sd_block_offset; + ls_emmc_dev_spec.buffer.length = nxp_sd_block_size; + *block_dev_spec = (uintptr_t)&ls_emmc_dev_spec; + + return 0; +} |