/* * Copyright 2021 NXP * * SPDX-License-Identifier: BSD-3-Clause * * */ #include #include #include #include #include #include #include #include "nxp_timer.h" #include "sd_mmc.h" #include #include /* 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; }