/* Copyright (C) 2023 Alif Semiconductor - All Rights Reserved. * Use, distribution and modification of this code is permitted under the * terms stated in the Alif Semiconductor Software License Agreement * * You should have received a copy of the Alif Semiconductor Software * License Agreement with this file. If not, please write to: * contact@alifsemi.com, or visit: https://alifsemi.com/license * */ /**************************************************************************//** * @file sd_host.c * @author Deepak Kumar * @email deepak@alifsemi.com * @version V0.0.1 * @date 28-Nov-2022 * @brief SD Host Controller Driver APIs. * @bug None. * @Note None ******************************************************************************/ /* Includes ------------------------------------------------------------------*/ #include "sd_host.h" #include "sd.h" #ifdef SDMMC_PRINTF_DEBUG #include "stdio.h" #endif extern sd_handle_t Hsd; static adma2_desc_t adma_desc_tbl[32] __attribute__((section("sd_dma_buf"))) __attribute__((aligned(512))); static volatile uint16_t nis,eis,cc; #ifdef SDMMC_IRQ_MODE void SDMMC_IRQHandler(void){ /* get the current interrupt status */ eis = Hsd.regs->SDMMC_ERROR_INT_STAT_R; nis = Hsd.regs->SDMMC_NORMAL_INT_STAT_R; /* clear the current interrupt status */ while(Hsd.regs->SDMMC_ERROR_INT_STAT_R) { eis = Hsd.regs->SDMMC_ERROR_INT_STAT_R; Hsd.regs->SDMMC_ERROR_INT_STAT_R = eis; } while(Hsd.regs->SDMMC_NORMAL_INT_STAT_R) { nis = Hsd.regs->SDMMC_NORMAL_INT_STAT_R; Hsd.regs->SDMMC_NORMAL_INT_STAT_R = nis; } if(eis) hc_reset(&Hsd, (uint8_t)(SDMMC_SW_RST_DAT_Msk | SDMMC_SW_RST_CMD_Msk)); switch(nis){ case SDMMC_INTR_CC_Msk: cc = 1; break; case SDMMC_INTR_TC_Msk: case (SDMMC_INTR_CC_Msk | SDMMC_INTR_TC_Msk): cc = 1; if(Hsd.sd_param.app_callback) Hsd.sd_param.app_callback(SDMMC_HC_STATUS_OK); break; } } void SDMMC_WAKEUP_IRQHandler(void){ } #endif /** \fn static uint8_t get_cmd_rsp_type(uint8_t Cmd) \brief get expected response of given command. \param[in] cmd - input cmd index \return expected response type. */ static uint8_t hc_get_cmd_rsp_type(uint8_t Cmd){ uint8_t RetVal=0; switch(Cmd){ case CMD0: RetVal = SDMMC_RESP_NONE; break; case CMD1: RetVal = SDMMC_RESP_R48; break; case CMD2: RetVal = SDMMC_RESP_R136; break; case CMD3: RetVal = SDMMC_RESP_R48; break; case CMD5: RetVal = SDMMC_RESP_R48; break; case CMD6: RetVal = SDMMC_RESP_R48; break; case ACMD6: RetVal = SDMMC_RESP_R48; break; case CMD7: RetVal = SDMMC_RESP_R48; break; case CMD8: RetVal = SDMMC_RESP_R48; break; case CMD9: RetVal = SDMMC_RESP_R136; break; case CMD11: case CMD13: case CMD16: RetVal = SDMMC_RESP_R48; break; case CMD17: case CMD18: RetVal = SDMMC_CMD_R_DATA_PRES_SEL_Msk | SDMMC_RESP_R48; break; case CMD23: RetVal = SDMMC_RESP_R48; break; case CMD24: case CMD25: RetVal = SDMMC_CMD_R_DATA_PRES_SEL_Msk | SDMMC_RESP_R48;; break; case CMD41: RetVal = SDMMC_RESP_R48; break; case CMD52: RetVal = SDMMC_RESP_R1; break; case CMD53: RetVal = SDMMC_RESP_R48; break; case CMD55: RetVal = SDMMC_RESP_R48; break; default: RetVal = SDMMC_RESP_NONE; break; } return RetVal; } /** \fn SDMMC_HC_STATUS hc_send_cmd(sd_handle_t *pHsd, sd_cmd_t *pCmd) \brief Configure Host controller to send SD command \param[in] pHsd - Global SD Handle pointer \param[in] pcmd - command info pointer \return Host controller driver API status. */ SDMMC_HC_STATUS hc_send_cmd(sd_handle_t *pHsd, sd_cmd_t *pCmd){ uint16_t cmd, timeout_cnt = SDMMC_MAX_TIMEOUT_16; uint8_t rsp_type, retry_cnt=1; rsp_type = hc_get_cmd_rsp_type(pCmd->cmdidx); cmd = (pCmd->cmdidx << SDMMC_CMD_IDX_Pos) | ( rsp_type ); RETRY: pHsd->regs->SDMMC_ERROR_INT_STAT_R = SDMMC_ERROR_INTR_ALL_Msk; pHsd->regs->SDMMC_NORMAL_INT_STAT_R = SDMMC_NORM_INTR_ALL_Msk; cc = 0; hc_check_bus_idle(pHsd); pHsd->regs->SDMMC_XFER_MODE_R = 0; if(pCmd->data_present){ pHsd->regs->SDMMC_XFER_MODE_R = pCmd->xfer_mode; } pHsd->regs->SDMMC_ARGUMENT_R = pCmd->arg; pHsd->regs->SDMMC_CMD_R = cmd; #ifndef SDMMC_IRQ_MODE cc = pHsd->regs->SDMMC_NORMAL_INT_STAT_R & SDMMC_INTR_CC_Msk; #endif while( timeout_cnt-- && (!cc) ); #ifdef SDMMC_PRINTF_DEBUG printf("CMD: 0x%04x, ARG: 0x%08x XFER: 0x%04x RSP01: 0x%08x RSP23: 0x%08x RSP45: 0x%08x RSP67: 0x%08x PSTATE: 0x%08x cc:%d\n", cmd,pCmd->arg,pCmd->xfer_mode,pHsd->regs->SDMMC_RESP01_R,pHsd->regs->SDMMC_RESP23_R, pHsd->regs->SDMMC_RESP45_R,pHsd->regs->SDMMC_RESP67_R,pHsd->regs->SDMMC_PSTATE_REG, cc); #endif if( (timeout_cnt == SDMMC_MAX_TIMEOUT_16) || eis ){ if(!retry_cnt--) return SDMMC_HC_STATUS_ERR; else goto RETRY; } return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_reset(sd_handle_t *pHsd, uint8_t reset_val) \brief software reset of the controller \param[in] Global SD Handle pointer \param[in] Reset Value \return Host controller driver status */ SDMMC_HC_STATUS hc_reset(sd_handle_t *pHsd, uint8_t reset_val){ uint8_t curr_reset_val = 0; uint16_t timeout = SDMMC_MAX_TIMEOUT_16; /* Reset the HC lines */ pHsd->regs->SDMMC_SW_RST_R = reset_val; do{ curr_reset_val = pHsd->regs->SDMMC_SW_RST_R; sys_busy_loop_us(100); }while((curr_reset_val == 1) && timeout--); return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_set_tout(sd_handle_t *pHsd, uint8_t ToutVal) \brief software reset of the controller \param[in] Global SD Handle pointer \param[in] Timeout Value \return Host controller driver status */ void hc_set_tout(sd_handle_t *pHsd, uint8_t tout){ /* Set the timeout value */ pHsd->regs->SDMMC_TOUT_CTRL_R = tout; return; } /** \fn SDMMC_HC_STATUS hc_power_cycle(sd_handle_t *pHsd) \brief software reset and power cycle of the controller \param[in] Global SD Handle pointer \return Host controller driver status */ void hc_power_cycle(sd_handle_t *pHsd){ pHsd->regs->SDMMC_PWR_CTRL_R ^= SDMMC_PC_BUS_PWR_VDD1_Msk; //disable vdd1 sys_busy_loop_us(1000); pHsd->regs->SDMMC_PWR_CTRL_R |= SDMMC_PC_BUS_PWR_VDD1_Msk; //enable vdd1 sys_busy_loop_us(1000); pHsd->regs->SDMMC_CLK_CTRL_R |= SDMMC_CLK_EN_Msk; pHsd->regs->SDMMC_NORMAL_INT_STAT_R = SDMMC_NORM_INTR_ALL_Msk; pHsd->regs->SDMMC_ERROR_INT_STAT_R = SDMMC_ERROR_INTR_ALL_Msk; return; } /** \fn SDMMC_HC_STATUS hc_set_bus_power(sd_handle_t *pHsd, uint8_t PwrVal){ \brief Set required sd bus power supply \param[in] Global SD Handle pointer \param[in] required bus voltage equivalent register value as per host controller data sheet \return Host controller driver status */ void hc_set_bus_power(sd_handle_t *pHsd, uint8_t PwrVal){ /* Disable clock */ pHsd->regs->SDMMC_PWR_CTRL_R = 0U; /* If selected power is zero, return from here */ if (PwrVal == 0U){ return; } /* set the required voltage level */ pHsd->regs->SDMMC_PWR_CTRL_R = PwrVal; /* .2ms delay after power on/off */ sys_busy_loop_us(2000); return; } /** \fn SDMMC_HC_STATUS hc_set_clk_freq(sd_handle_t *pHsd, uint32_t clk_freq) \brief Sets required clock \param[in] Global SD Handle pointer \param[in] required bus clock equivalent register value as per host controller data sheet \return Host controller driver status */ SDMMC_HC_STATUS hc_set_clk_freq(sd_handle_t *pHsd, uint16_t clk_freq){ uint16_t reg; /* Disable clock */ pHsd->regs->SDMMC_CLK_CTRL_R = 0; /* If selected frequency is zero, return from here */ if (clk_freq == 0U){ return SDMMC_HC_STATUS_OK; } pHsd->regs->SDMMC_CLK_CTRL_R = clk_freq; sys_busy_loop_us(1000); /* SD clk stability */ do{ reg = pHsd->regs->SDMMC_CLK_CTRL_R; sys_busy_loop_us(1); }while((reg & SDMMC_INTERNAL_CLK_STABLE_Msk) == 0); return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_config_dma(sd_handle_t *pHsd,uint8_t dmaMask) \brief Host Controller DMA configuration \param[in] Global SD Handle pointer \param[in] dmaMask - Host ctrl 1 register value \return Host controller driver status */ SDMMC_HC_STATUS hc_config_dma(sd_handle_t *pHsd, uint8_t dmaMask){ /* Host Version 4 Param */ pHsd->regs->SDMMC_HOST_CTRL2_R = SDMMC_HOST_CTRL2_ASYNC_INT_EN_Msk | SDMMC_HOST_CTRL2_VER4_EN_Msk | SDMMC_HOST_CTRL2_CMD23_EN_Msk; pHsd->regs->SDMMC_HOST_CTRL1_R = dmaMask; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_set_bus_width(sd_handle_t *pHsd, uint8_t buswidth) \brief Configure required bit for communication for Host and Card \param[in] pHsd - Global SD Handle pointer \param[in] buswidth - number of Data lines for data transfer \return Host controller driver status */ SDMMC_HC_STATUS hc_set_bus_width(sd_handle_t *pHsd, uint8_t buswidth){ uint8_t regs; uint32_t status; hc_get_card_status(pHsd, &status); if(status != SD_CARD_STATE_TRAN) return SDMMC_HC_STATUS_INV_STATE; /* SD Must be TRAN state to change Bus width */ regs = pHsd->regs->SDMMC_HOST_CTRL1_R; if(buswidth == SDMMC_1_BIT_WIDTH_Msk) regs = regs | SDMMC_1_BIT_WIDTH_Msk; else if(buswidth == SDMMC_4_BIT_WIDTH_Msk) regs = regs | SDMMC_4_BIT_WIDTH_Msk | SDMMC_HOST_CTRL1_HIGH_SPEED_MODE_EN; else regs = regs | SDMMC_8_BIT_WIDTH_Msk | SDMMC_HOST_CTRL1_HIGH_SPEED_MODE_EN; pHsd->regs->SDMMC_HOST_CTRL1_R = regs; /* just to indicate Card that the next cmd is APP CMD if not MMC/eMMC Card */ if(pHsd->sd_card.cardtype != SDMMC_CARD_MMC){ pHsd->sd_cmd.cmdidx = CMD55; pHsd->sd_cmd.arg = pHsd->sd_card.relcardadd; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } } /* Send ACMD6 to change Bus width */ pHsd->sd_cmd.cmdidx = CMD6; pHsd->sd_cmd.arg = 0x2; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } return SDMMC_HC_STATUS_OK; } /** \fn void hc_config_interrupt(sd_handle_t *pHsd) \brief Configure Error and interrupt in Host Controller \param[in] pHsd - Global SD Handle pointer \return none */ void hc_config_interrupt(sd_handle_t *pHsd){ /* Enable all interrupt status except card interrupt initially */ pHsd->regs->SDMMC_NORMAL_INT_STAT_EN_R = SDMMC_NORM_INTR_ALL_Msk & (~SDMMC_INTR_CARD_Msk); pHsd->regs->SDMMC_ERROR_INT_STAT_EN_R = SDMMC_ERROR_INTR_ALL_Msk; /* Disable all interrupt signals by default. */ pHsd->regs->SDMMC_NORMAL_INT_SIGNAL_EN_R = 0x0U; pHsd->regs->SDMMC_ERROR_INT_SIGNAL_EN_R = 0x0U; } /** \fn SDMMC_HC_STATUS hc_identify_card(sd_handle_t *pHsd) \brief Indentify the inserted cards \param[in] Global SD Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_identify_card(sd_handle_t *pHsd){ uint32_t isCardPresent = 0, timeout = 0xFFFFU; SDMMC_HC_STATUS status; hc_reset(pHsd, (uint8_t)(SDMMC_SW_RST_DAT_Msk | SDMMC_SW_RST_CMD_Msk)); // reset CMD and DATA Line #ifdef SDMMC_PRINTF_DEBUG printf("Waiting for SD Card to be inserted...\n"); #endif /* Card Insertion and Removal State and Signal Enable */ pHsd->regs->SDMMC_NORMAL_INT_SIGNAL_EN_R = SDMMC_INTR_CC_Msk | SDMMC_INTR_TC_Msk | SDMMC_INTR_DMA_Msk | SDMMC_INTR_BWR_Msk | SDMMC_INTR_BRR_Msk | SDMMC_INTR_CARD_INSRT_Msk | SDMMC_INTR_CARD_REM_Msk; pHsd->regs->SDMMC_NORMAL_INT_STAT_EN_R = SDMMC_INTR_CC_Msk | SDMMC_INTR_TC_Msk | SDMMC_INTR_DMA_Msk | SDMMC_INTR_BWR_Msk | SDMMC_INTR_BRR_Msk | SDMMC_INTR_CARD_INSRT_Msk | SDMMC_INTR_CARD_REM_Msk; pHsd->regs->SDMMC_ERROR_INT_STAT_EN_R = SDMMC_ERROR_INTR_ALL_Msk; do{ isCardPresent = pHsd->regs->SDMMC_PSTATE_REG; isCardPresent = (isCardPresent & SDMMC_CARD_INSRT_Msk); pHsd->sd_card.iscardpresent = isCardPresent; #ifdef SDMMC_PRINTF_DEBUG printf("SD Card Detected...\n"); #endif sys_busy_loop_us(1); }while(timeout-- && (!isCardPresent)); status = SDMMC_HC_STATUS_OK; if(timeout == SDMMC_MAX_TIMEOUT_16){ pHsd->error_code = SD_DRV_STATUS_TIMEOUT_ERR; status = SDMMC_HC_STATUS_ERR; } pHsd->regs->SDMMC_NORMAL_INT_SIGNAL_EN_R = SDMMC_INTR_CC_Msk | SDMMC_INTR_TC_Msk | SDMMC_INTR_DMA_Msk | SDMMC_INTR_BWR_Msk | SDMMC_INTR_BRR_Msk | SDMMC_INTR_CARD_INSRT_Msk | SDMMC_INTR_CARD_REM_Msk; pHsd->regs->SDMMC_NORMAL_INT_STAT_EN_R = SDMMC_INTR_CC_Msk | SDMMC_INTR_TC_Msk | SDMMC_INTR_DMA_Msk | SDMMC_INTR_BWR_Msk | SDMMC_INTR_BRR_Msk | SDMMC_INTR_CARD_INSRT_Msk | SDMMC_INTR_CARD_REM_Msk; return status; } /** \fn SDMMC_HC_STATUS hc_get_card_ifcond(sd_handle_t *pHsd) \brief Get Card Interface condition \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_card_ifcond(sd_handle_t *pHsd){ uint32_t resp_ifcond; /* Change the Card State from Idle to Identification */ pHsd->state = SD_CARD_STATE_IDENT; pHsd->sd_cmd.cmdidx = CMD8; pHsd->sd_cmd.arg = SDMMC_CMD8_VOL_PATTERN; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } resp_ifcond = pHsd->regs->SDMMC_RESP01_R; if(resp_ifcond != SDMMC_CMD8_VOL_PATTERN){ /* SD Version 1 Card */ pHsd->sd_card.cardversion = SDMMC_CARD_SDSC; pHsd->sd_card.cardtype = SDMMC_CARD_SDSC; pHsd->sd_card.f8flag = 1; }else{ /* SD Version 2 or Later Card */ pHsd->sd_card.cardversion = SDMMC_CARD_SDHC; pHsd->sd_card.cardtype = SDMMC_CARD_SDHC; pHsd->sd_card.f8flag = 0; } return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_emmc_card_opcond(sd_handle_t *pHsd) \brief Get eMMC Card operating voltage condition \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_emmc_card_opcond(sd_handle_t *pHsd){ uint32_t resp_OPcond; uint32_t timeout = 0xFFFFU; uint32_t switch1v8 = 0; uint32_t ocr; hc_power_cycle(pHsd); hc_reset(pHsd, SDMMC_SW_RST_CMD_Msk | SDMMC_SW_RST_DAT_Msk); hc_go_idle(pHsd); hc_get_card_ifcond(pHsd); do{ pHsd->sd_cmd.cmdidx = CMD1; pHsd->sd_cmd.arg = (SDMMC_CMD41_HCS | SDMMC_CMD41_3V3);// | SD_OCR_S18R); if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } resp_OPcond = pHsd->regs->SDMMC_RESP01_R; switch1v8 = resp_OPcond & SDMMC_CMD41_S18A; resp_OPcond = pHsd->regs->SDMMC_RESP01_R; resp_OPcond = resp_OPcond & SDMMC_OCR_READY; }while( (!resp_OPcond) && (timeout--)); if(timeout == SDMMC_MAX_TIMEOUT_32) return SDMMC_HC_STATUS_ERR; /* UHS-I Specific Initializations */ if(switch1v8) hc_switch_1v8(pHsd); return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_card_opcond(sd_handle_t *pHsd) \brief Get Card operating voltage condition \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_card_opcond(sd_handle_t *pHsd){ uint32_t resp_OPcond; uint32_t timeout = 0xFFFFU; uint32_t switch1v8 = 0; uint32_t ocr; uint32_t sdio_func_number = 0; /* Get the SD/SDIO card operating condition */ if(hc_get_io_opcond(pHsd, 0, &resp_OPcond) == SDMMC_HC_STATUS_OK){ /* IO functionality found in this card */ sdio_func_number = (resp_OPcond & CMD5_RESP_NIOF_PRES_Msk) >> CMD5_RESP_NIOF_PRES_Pos; ocr = (resp_OPcond & CMD5_RESP_OCR_3V3_Msk); if(sdio_func_number && ocr){ hc_get_io_opcond(pHsd, ocr, &resp_OPcond); } if(resp_OPcond & CMD5_RESP_IO_READY_Msk){ /* update the global instance */ pHsd->sd_card.sdio.sdio_opcd.func_number = sdio_func_number; pHsd->sd_card.sdio_mode = 1; } }else{ /* Memory */ hc_power_cycle(pHsd); hc_reset(pHsd, SDMMC_SW_RST_CMD_Msk | SDMMC_SW_RST_DAT_Msk); hc_go_idle(pHsd); hc_get_card_ifcond(pHsd); do{ /* just to indicate SD Card that the next cmd is APP CMD */ pHsd->sd_cmd.cmdidx = CMD55; pHsd->sd_cmd.arg = 0x0; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } pHsd->sd_cmd.cmdidx = CMD41; pHsd->sd_cmd.arg = (SDMMC_CMD41_HCS | SDMMC_CMD41_3V3);// | SD_OCR_S18R); if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } resp_OPcond = pHsd->regs->SDMMC_RESP01_R; switch1v8 = resp_OPcond & SDMMC_CMD41_S18A; resp_OPcond = pHsd->regs->SDMMC_RESP01_R; resp_OPcond = resp_OPcond & SDMMC_OCR_READY; }while( (!resp_OPcond) && (timeout--)); } if(timeout == SDMMC_MAX_TIMEOUT_32) return SDMMC_HC_STATUS_ERR; if(!(pHsd->regs->SDMMC_RESP01_R & SDMMC_CMD41_HCS)) { pHsd->sd_card.cardversion = SDMMC_CARD_SDSC; pHsd->sd_card.cardtype = SDMMC_CARD_SDSC; } /* UHS-I Specific Initializations */ if(switch1v8) hc_switch_1v8(pHsd); return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_card_cid(sd_handle_t *pHsd) \brief Get Card Identification information \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_card_cid(sd_handle_t *pHsd){ pHsd->sd_cmd.cmdidx = CMD2; pHsd->sd_cmd.arg = 0x0; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } pHsd->cid[0] = pHsd->regs->SDMMC_RESP01_R; pHsd->cid[1] = pHsd->regs->SDMMC_RESP23_R; pHsd->cid[2] = pHsd->regs->SDMMC_RESP45_R; pHsd->cid[3] = pHsd->regs->SDMMC_RESP67_R; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_card_csd(sd_handle_t *pHsd) \brief Get Card Specific Data \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_card_csd(sd_handle_t *pHsd){ uint32_t blk_len; uint32_t device_size; uint32_t mult, c_size; pHsd->sd_cmd.cmdidx = CMD9; pHsd->sd_cmd.arg = pHsd->sd_card.relcardadd; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } /* update the global instance */ pHsd->csd[0] = pHsd->regs->SDMMC_RESP01_R; pHsd->csd[1] = pHsd->regs->SDMMC_RESP23_R; pHsd->csd[2] = pHsd->regs->SDMMC_RESP45_R; pHsd->csd[3] = pHsd->regs->SDMMC_RESP67_R; if(((pHsd->csd[3] & CSD_STRUCT_Msk) >> 22U) == 0U){ blk_len = (uint32_t)1U << ((uint32_t)(pHsd->csd[2] & READ_BLK_LEN_Msk) >> 8U); mult = (uint32_t)1U << ((uint32_t)((pHsd->csd[1] & C_SIZE_MULT_Msk) >> 7U) + (uint32_t)2U); device_size = (pHsd->csd[1] & C_SIZE_LOWER_Msk) >> 22U; device_size |= (pHsd->csd[2] & C_SIZE_UPPER_Msk) << 10U; device_size = (device_size + 1U) * mult; device_size = device_size * blk_len; pHsd->sd_card.sectorcount = (device_size/SDMMC_BLK_SIZE_512_Msk); pHsd->sd_card.sectorsize = blk_len; pHsd->sd_card.logblocksize = blk_len; }else if(((pHsd->csd[3] & CSD_STRUCT_Msk) >> 22U) == 1U){ c_size = ((pHsd->csd[1] & CSD_V2_C_SIZE_Msk) >> 8U); if(c_size >= 0xFFFF) pHsd->sd_card.cardtype = SDMMC_CARD_SDXC; pHsd->sd_card.sectorcount = (((pHsd->csd[1] & CSD_V2_C_SIZE_Msk) >> 8U) + 1U) * 1024U; pHsd->sd_card.card_class = (uint16_t)((pHsd->csd[2] & CSD_CCC_Msk) >> CSD_CCC_SHIFT); pHsd->sd_card.sectorsize = 512; pHsd->sd_card.logblocksize = 512; }else{ return SDMMC_HC_STATUS_ERR; } return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_card_scr(sd_handle_t *pHsd) \brief Get SCR \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_card_scr(sd_handle_t *pHsd){ /* just to indicate SD Card that the next cmd is APP CMD */ pHsd->sd_cmd.cmdidx = CMD55; pHsd->sd_cmd.arg = 0x0; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } pHsd->sd_cmd.cmdidx = CMD51; pHsd->sd_cmd.arg = 0; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } /* update the global instance */ pHsd->scr[0] = pHsd->regs->SDMMC_RESP01_R; pHsd->scr[1] = pHsd->regs->SDMMC_RESP23_R; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_switch_1v8(sd_handle_t *pHsd) \brief switch to 1.8v signaling \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_switch_1v8(sd_handle_t *pHsd){ uint16_t clk; uint8_t power_level; #ifdef SDMMC_PRINTF_DEBUG printf("Switching to 1.8v...\n"); #endif pHsd->sd_cmd.cmdidx = CMD11; pHsd->sd_cmd.arg = 0; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } clk = pHsd->regs->SDMMC_CLK_CTRL_R; clk &= ~(SDMMC_CLK_EN_Msk | SDMMC_INTERNAL_CLK_EN_Msk); /* Disable clock */ hc_set_clk_freq(pHsd, clk); pHsd->regs->SDMMC_HOST_CTRL1_R |= 6; pHsd->regs->SDMMC_HOST_CTRL2_R |= SDMMC_HOST_CTRL2_SIGNALING_EN_Msk; power_level = SDMMC_PC_BUS_VSEL_1V8_Msk | SDMMC_PC_BUS_PWR_VDD1_Msk; hc_set_bus_power(pHsd, power_level); /* Delay 5ms after switching to 1.8v */ sys_busy_loop_us(5000); clk |= (SDMMC_CLK_EN_Msk | SDMMC_INTERNAL_CLK_EN_Msk); /* Enable clock */ hc_set_clk_freq(pHsd, clk); sys_busy_loop_us(1000); return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_go_idle(sd_handle_t *pHsd) \brief put the card in idle mode \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_go_idle(sd_handle_t *pHsd){ pHsd->sd_cmd.cmdidx = CMD0; pHsd->sd_cmd.arg = 0; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } pHsd->state = SD_CARD_STATE_IDLE; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_sel_card(sd_handle_t *pHsd, uint32_t rca) \brief Select a card for further operation \param[in] Global sd Handle pointer \param[in] Card Relative Address \return Host controller driver status */ SDMMC_HC_STATUS hc_sel_card(sd_handle_t *pHsd, uint32_t rca){ /* Select the card to transition to transfer state */ pHsd->sd_cmd.cmdidx = CMD7; pHsd->sd_cmd.arg = rca; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } /* Change the Card State from Ready to Tran */ pHsd->state = SD_CARD_STATE_TRAN; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_set_blk_size(sd_handle_t *pHsd, uint32_t BlkSize) \brief Set the Block Size \param[in] Global sd Handle pointer \param[in] Block size \return Host controller driver status */ SDMMC_HC_STATUS hc_set_blk_size(sd_handle_t *pHsd, uint32_t blk_size){ if(pHsd->regs->SDMMC_BLOCKSIZE_R != blk_size){ /* set the block size */ pHsd->sd_cmd.cmdidx = CMD16; pHsd->sd_cmd.arg = blk_size; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } pHsd->regs->SDMMC_BLOCKSIZE_R = blk_size; } return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_rca(sd_handle_t *pHsd, uint32_t *prca) \brief Get the Card Relative Address \param[in] Global sd Handle pointer \param[in] RCA destination pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_rca(sd_handle_t *pHsd, uint32_t *prca){ /* Get the card Relative Addr */ pHsd->sd_cmd.cmdidx = CMD3; pHsd->sd_cmd.arg = 0x0; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } *prca = pHsd->regs->SDMMC_RESP01_R & SDMMC_RCA_Msk; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_capabilities(sd_handle_t *pHsd, uint32_t *phcaps) \brief Get the Host Controller Capabilities \param[in] Global sd Handle pointer \param[in] Capabilities pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_capabilities(sd_handle_t *pHsd, uint32_t *phcaps){ /* Get the card Relative Addr */ *phcaps = pHsd->regs->SDMMC_CAPABILITIES1_R; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_response1(sd_handle_t *pHsd) \brief Get the Card Response1 \param[in] Global sd Handle pointer \return response */ uint32_t hc_get_response1(sd_handle_t *pHsd){ /* Get the card Response1 */ return pHsd->regs->SDMMC_RESP01_R; } /** \fn SDMMC_HC_STATUS hc_get_response2(sd_handle_t *pHsd) \brief Get the Card Response2 \param[in] Global sd Handle pointer \return response */ uint32_t hc_get_response2(sd_handle_t *pHsd){ /* Get the card Response2 */ return pHsd->regs->SDMMC_RESP23_R; } /** \fn SDMMC_HC_STATUS hc_get_response3(sd_handle_t *pHsd) \brief Get the Card Response3 \param[in] Global sd Handle pointer \return response */ uint32_t hc_get_response3(sd_handle_t *pHsd){ /* Get the card Response3 */ return pHsd->regs->SDMMC_RESP45_R; } /** \fn SDMMC_HC_STATUS hc_get_response4(sd_handle_t *pHsd) \brief Get the Card Response1 \param[in] Global sd Handle pointer \return response */ uint32_t hc_get_response4(sd_handle_t *pHsd){ /* Get the card Response4 */ return pHsd->regs->SDMMC_RESP67_R; } /** \fn SDMMC_HC_STATUS hc_get_card_status(sd_handle_t *pHsd, uint32_t *pstatus) \brief Get the Card Status \param[in] Global sd Handle pointer \param[in] status pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_card_status(sd_handle_t *pHsd, uint32_t *pstatus){ uint32_t status; /* Check current card status */ pHsd->sd_cmd.cmdidx = CMD13; pHsd->sd_cmd.arg = pHsd->sd_card.relcardadd; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } status = hc_get_response1(pHsd); *pstatus = (status & SDMMC_STATUS_Msk) >> SDMMC_STATUS_Pos; #ifdef SDMMC_PRINTF_DEBUG printf("Card Status: %x\n",status); #endif return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_set_blk_cnt(sd_handle_t *pHsd, uint32_t BlkCnt) \brief Set the Block Count \param[in] Global sd Handle pointer \param[in] Block Count \return Host controller driver status */ SDMMC_HC_STATUS hc_set_blk_cnt(sd_handle_t *pHsd, uint32_t BlkCnt){ pHsd->sd_cmd.cmdidx = CMD23; pHsd->sd_cmd.arg = BlkCnt; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } pHsd->regs->SDMMC_BLOCKCOUNT_R = BlkCnt; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_dma_config(sd_handle_t *pHsd, uint32_t buff, uint32_t sector, uint16_t BlkCnt){ \brief Setup read parameter and start reading sector \param[in] Global sd Handle pointer \param[in] destination buffer \param[in] sector number to read \param[in] Block Count \return Host controller driver status */ SDMMC_HC_STATUS hc_dma_config(sd_handle_t *pHsd, uint32_t buff, uint32_t sector, uint16_t blk_cnt){ ARG_UNUSED(sector); if((pHsd->sd_card.cardtype != SDMMC_CARD_SDSC)){ hc_set_blk_cnt(pHsd, blk_cnt); } /* Configure DMA buffer */ if(pHsd->sd_param.dma_mode == SDMMC_HOST_CTRL1_ADMA2_MODE){ uint32_t desc_num = 0; uint32_t total_desc = 0, bytes_per_desc; bytes_per_desc = blk_cnt * SDMMC_BLK_SIZE_512_Msk; if(bytes_per_desc > SDMMC_ADMA2_DESC_MAX_LEN){ total_desc = bytes_per_desc / SDMMC_ADMA2_DESC_MAX_LEN; if(bytes_per_desc % SDMMC_ADMA2_DESC_MAX_LEN) total_desc += 1; } do{ adma_desc_tbl[desc_num].addr = buff + (desc_num * SDMMC_ADMA2_DESC_MAX_LEN); adma_desc_tbl[desc_num].len = bytes_per_desc; adma_desc_tbl[desc_num].attr = SDMMC_ADMA2_DESC_TRAN | SDMMC_ADMA2_DESC_VALID; }while(desc_num++ < total_desc); adma_desc_tbl[desc_num - 1].attr |= SDMMC_ADMA2_DESC_END; #ifdef SDMMC_PRINTF_DEBUG printf("ADMA Desc: 0x%x, addr: 0x%x, Len: 0x%x, Attr: 0x%x\n",(uint32_t)&adma_desc_tbl[0],adma_desc_tbl[0].addr,adma_desc_tbl[0].len,adma_desc_tbl[0].attr); #endif RTSS_CleanDCache_by_Addr(&adma_desc_tbl[0], sizeof(adma_desc_tbl)); pHsd->regs->SDMMC_ADMA_SA_LOW_R = (uint32_t)LocalToGlobal((&adma_desc_tbl[0])); } else pHsd->regs->SDMMC_ADMA_SA_LOW_R = (uint32_t)LocalToGlobal((const volatile void*)buff); return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_read_setup(sd_handle_t *pHsd, uint32_t buff, uint32_t sector, uint16_t BlkCnt){ \brief Setup read parameter and start reading sector \param[in] Global sd Handle pointer \param[in] destination buffer \param[in] sector number to read \param[in] Block Count \return Host controller driver status */ SDMMC_HC_STATUS hc_read_setup(sd_handle_t *pHsd, uint32_t buff, uint32_t sector, uint16_t BlkCnt){ hc_dma_config(pHsd, buff, sector, BlkCnt); pHsd->sd_cmd.arg = sector; if(pHsd->sd_card.cardtype == SDMMC_CARD_SDSC) pHsd->sd_cmd.arg <<= 9; pHsd->sd_cmd.data_present = 1; if(BlkCnt == 1){ pHsd->sd_cmd.cmdidx = CMD17; pHsd->sd_cmd.xfer_mode = SDMMC_XFER_MODE_DATA_XFER_RD_Msk | SDMMC_XFER_MODE_DMA_EN_Msk; }else{ pHsd->sd_cmd.cmdidx = CMD18; pHsd->sd_cmd.xfer_mode = SDMMC_XFER_MODE_DATA_XFER_RD_Msk | SDMMC_XFER_MODE_MULTI_BLK_SEL_Msk | (SDMMC_XFER_MODE_AUTO_CMD12 << SDMMC_XFER_MODE_AUTO_CMD_EN_Pos) | SDMMC_XFER_MODE_DMA_EN_Msk; if(pHsd->sd_param.dma_mode == SDMMC_HOST_CTRL1_SDMA_MODE) pHsd->sd_cmd.xfer_mode |= SDMMC_XFER_MODE_BLK_CNT_Msk; } pHsd->regs->SDMMC_HOST_CTRL1_R |= 1; //led caution on if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } pHsd->sd_cmd.data_present = 0; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_write_setup(sd_handle_t *pHsd, uint32_t buff, uint32_t sector, uint16_t BlkCnt) \brief Setup write parameter and start writing sector \param[in] Global sd Handle pointer \param[in] source buffer \param[in] sector number to write \param[in] Block Count \return Host controller driver status */ SDMMC_HC_STATUS hc_write_setup(sd_handle_t *pHsd, uint32_t buff, uint32_t sector, uint16_t BlkCnt){ hc_dma_config(pHsd, buff, sector, BlkCnt); pHsd->sd_cmd.arg = sector; if(pHsd->sd_card.cardtype == SDMMC_CARD_SDSC) pHsd->sd_cmd.arg <<= 9; pHsd->sd_cmd.data_present = 1; if(BlkCnt == 1){ pHsd->sd_cmd.cmdidx = CMD24; pHsd->sd_cmd.xfer_mode = SDMMC_XFER_MODE_DMA_EN_Msk; }else{ pHsd->sd_cmd.cmdidx = CMD25; pHsd->sd_cmd.xfer_mode = SDMMC_XFER_MODE_DATA_XFER_WR_Msk | SDMMC_XFER_MODE_MULTI_BLK_SEL_Msk | SDMMC_XFER_MODE_BLK_CNT_Msk | SDMMC_XFER_MODE_DMA_EN_Msk; if(pHsd->sd_param.dma_mode == SDMMC_HOST_CTRL1_SDMA_MODE) pHsd->sd_cmd.xfer_mode |= SDMMC_XFER_MODE_BLK_CNT_Msk; } pHsd->regs->SDMMC_HOST_CTRL1_R |= SDMMC_HOST_CTRL1_LED_ON; //led caution on if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ sd_error_handler(); } pHsd->sd_cmd.data_present = 0; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_check_xfer_done(sd_handle_t *pHsd, uint32_t timeout_cnt) \brief Check for transfer complete \param[in] Global sd Handle pointer \param[in] timeout Count \return Host controller driver status */ SDMMC_HC_STATUS hc_check_xfer_done(sd_handle_t *pHsd, uint32_t timeout_cnt){ uint32_t pstate = 0, dma_irq=0, xfer_comp=0; /* check for transfer active state */ while(timeout_cnt){ #ifndef SDMMC_IRQ_MODE nis = pHsd->regs->SDMMC_NORMAL_INT_STAT_R; #endif dma_irq = nis; pstate = pHsd->regs->SDMMC_PSTATE_REG; pstate = pstate & XFER_ACTIVE_Msk; xfer_comp = dma_irq & NORMAL_INT_STAT_XFER_COMPLETE_Msk; if(xfer_comp && !pstate) break; sys_busy_loop_us(1); timeout_cnt--; } pHsd->regs->SDMMC_HOST_CTRL1_R ^= SDMMC_HOST_CTRL1_LED_ON; //led caution off #ifdef SDMMC_PRINTF_SD_STATE_DEBUG printf("PSTATE REG:0x%08x\tAUTO_CMD_STAT:0x%04hx\tERROR_INT_STAT_R:0x%x\n", pHsd->regs->SDMMC_PSTATE_REG,pHsd->regs->SDMMC_AUTO_CMD_STAT_R, (uint8_t)pHsd->regs->SDMMC_ERROR_INT_STAT_R); #endif if(!timeout_cnt){ return SDMMC_HC_STATUS_ERR; } return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_check_bus_idle(sd_handle_t *pHsd) \brief Check the CMD line is idle or nor \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_check_bus_idle(sd_handle_t *pHsd){ uint32_t pstate, cmdinhibit, datinhibit, timeout = 0x100000; do{ pstate = pHsd->regs->SDMMC_PSTATE_REG; cmdinhibit = pstate & SDMMC_CMD_INHIBIT_Msk; datinhibit = pstate & SDMMC_DAT_INHIBIT_Msk; if(!timeout--) return SDMMC_HC_STATUS_ERR; }while(cmdinhibit || (pHsd->sd_cmd.data_present && datinhibit)); return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_io_reset(sd_handle_t *pHsd) \brief Reset the IO/Combo Card \param[in] Global sd Handle pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_io_reset(sd_handle_t *pHsd){ pHsd->sd_cmd.cmdidx = SDIO_RW_DIRECT; pHsd->sd_cmd.arg = 0; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ return SDMMC_HC_STATUS_ERR; } return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_get_io_opcond(sd_handle_t *pHsd, uint32_t *) \brief Reset the IO/Combo Card \param[in] Global sd Handle pointer \param[in] ocr response destination pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_get_io_opcond(sd_handle_t *pHsd, uint32_t ocr, uint32_t *rocr){ pHsd->sd_cmd.cmdidx = SDIO_SEND_OP_COND; pHsd->sd_cmd.arg = ocr; if(hc_send_cmd(pHsd, &pHsd->sd_cmd) != SDMMC_HC_STATUS_OK){ return SDMMC_HC_STATUS_ERR; } *rocr = pHsd->regs->SDMMC_RESP01_R; if(*rocr == SDMMC_CMD8_VOL_PATTERN) return SDMMC_HC_STATUS_ERR; return SDMMC_HC_STATUS_OK; } /** \fn SDMMC_HC_STATUS hc_io_rw_direct(sd_handle_t *pHsd, uint32_t *) \brief Reset the IO/Combo Card \param[in] Global sd Handle pointer \param[in] ocr response destination pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_io_rw_direct(sd_handle_t *pHsd, uint32_t rwFlag, uint32_t fn, uint32_t addr, uint8_t writeData, uint8_t *readPtr){ SDMMC_HC_STATUS status = SDMMC_HC_STATUS_OK; sd_cmd_t sdio_cmd; uint32_t resp; if( (fn > SDIO_MAX_FUNCTION) || (addr & ~(SDIO_MAX_CIA_ADDR))){ return SDMMC_HC_STATUS_ERR; } sdio_cmd.xfer_mode = SDMMC_XFER_MODE_DATA_XFER_RD_Msk | SDMMC_XFER_MODE_DMA_EN_Msk; sdio_cmd.cmdidx = SDIO_RW_DIRECT; sdio_cmd.arg = rwFlag ? SDIO_RW_FLAG_Msk : 0x00000000; sdio_cmd.arg |= fn << SDIO_FN_Pos; sdio_cmd.arg |= (rwFlag && readPtr) ? SDIO_RAW_FLAG_Msk : 0x00000000; sdio_cmd.arg |= addr << SDIO_REG_ADDR_Pos; sdio_cmd.arg |= rwFlag ? writeData : 0x00000000; if(hc_send_cmd(pHsd, &sdio_cmd) != SDMMC_HC_STATUS_OK){ return SDMMC_HC_STATUS_ERR; } resp = pHsd->regs->SDMMC_RESP01_R; if( (resp & SDIO_R5_ERROR) || (resp & SDIO_R5_FUNCTION_NUMBER) || (resp & SDIO_R5_OUT_OF_RANGE)) { return SDMMC_HC_STATUS_ERR; } if(readPtr){ *readPtr = resp & 0xFF; } return status; } /** \fn SDMMC_HC_STATUS hc_io_rw_extended(sd_handle_t *pHsd, uint32_t *) \brief Reset the IO/Combo Card \param[in] Global sd Handle pointer \param[in] ocr response destination pointer \return Host controller driver status */ SDMMC_HC_STATUS hc_io_rw_extended(sd_handle_t *pHsd, uint32_t rwFlag, uint32_t fn, uint32_t addr, uint32_t incr_addr, uint8_t *buf, uint32_t BlkCnt, uint32_t blkSize){ SDMMC_HC_STATUS status = SDMMC_HC_STATUS_OK; sd_cmd_t sdio_cmd; if( (fn > SDIO_MAX_FUNCTION) || (addr & ~(SDIO_MAX_CIA_ADDR))){ status = SDMMC_HC_STATUS_ERR; goto exit; } /* Configure DMA */ pHsd->regs->SDMMC_ADMA_SA_LOW_R = (uint32_t)LocalToGlobal((const volatile void *)buf); sdio_cmd.cmdidx = SDIO_RW_EXTENDED; sdio_cmd.arg = rwFlag ? SDIO_RW_FLAG_Msk : 0x00000000; sdio_cmd.arg |= fn << SDIO_FN_Pos; sdio_cmd.arg |= incr_addr ? SDIO_RW_EXT_INCR_ADDR_Msk : 0x00000000; sdio_cmd.arg |= addr << SDIO_REG_ADDR_Pos; sdio_cmd.xfer_mode = (rwFlag ? SDMMC_XFER_MODE_DATA_XFER_WR_Msk : SDMMC_XFER_MODE_DATA_XFER_RD_Msk) | SDMMC_XFER_MODE_DMA_EN_Msk; if(BlkCnt){ sdio_cmd.arg |= SDIO_RW_EXT_BLK_MODE_Msk | BlkCnt; sdio_cmd.xfer_mode |= SDMMC_XFER_MODE_MULTI_BLK_SEL_Msk; pHsd->regs->SDMMC_BLOCKCOUNT_R = BlkCnt; }else{ sdio_cmd.arg |= (blkSize == 512) ? 0 : blkSize; pHsd->regs->SDMMC_BLOCKCOUNT_R = 1; } exit: return status; }