| /* |
| * 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 below 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 below 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 - Buffer 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 - Buffer 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; |
| } |