/* ****************************************************************************** * @file driver_sd_mmc.c * @author FreqChip Firmware Team * @version V1.0.0 * @date 2022 * @brief Multi-Media card application HAL module driver. ****************************************************************************** * @attention * * Copyright (c) 2022 FreqChip. * All rights reserved. ****************************************************************************** */ #include "fr30xx.h" /* Private function prototypes -----------------------------------------------*/ static uint32_t __eMMC_PowerON(MMC_HandleTypeDef *hmmc); static uint32_t __eMMC_InitCard(MMC_HandleTypeDef *hmmc); static void __MMCCard_GetCardCSD(MMC_HandleTypeDef *hmmc); static void __MMCCard_GetCardCID(MMC_HandleTypeDef *hmmc); /************************************************************************************ * @fn eMMC_Init * * @brief Initializes the Embedded Multi Media Card. * * @param hmmc: Pointer to MMC handle */ uint32_t eMMC_Init(MMC_HandleTypeDef *hmmc) { uint32_t lu32_ErrState = INT_NO_ERR; /* eMMC class initialization */ SD_SDMMCClass_Init(hmmc->SDx); /* Identify card operating voltage */ lu32_ErrState = __eMMC_PowerON(hmmc); if (lu32_ErrState) { return lu32_ErrState; } /* Card initialization */ lu32_ErrState = __eMMC_InitCard(hmmc); if (lu32_ErrState) { return lu32_ErrState; } /* Get the card information from the CID register */ __MMCCard_GetCardCID(hmmc); /* Get the card information from the CSD register */ __MMCCard_GetCardCSD(hmmc); return lu32_ErrState; } /************************************************************************************ * @fn eMMC_BusWidth_Select * * @brief SDCard 1bit/4bit/8bit bus width select. * * @param hmmc: Pointer to MMC handle. * fu32_BusWidth: bus width. can select MMC_BUS_WIDTH_1BIT. * MMC_BUS_WIDTH_4BIT. * MMC_BUS_WIDTH_8BIT. */ uint32_t eMMC_BusWidth_Select(MMC_HandleTypeDef *hmmc, uint32_t fu32_BusWidth) { uint32_t lu32_ErrState = INT_NO_ERR; uint32_t fu32_Argument; /* Access */ fu32_Argument = MMC_CMD6_ACCESS_WRITE_BYTE; /* Index */ fu32_Argument |= MMC_EX_CSD_INDEX_BUS_WIDTH << 16; /* Value */ fu32_Argument |= fu32_BusWidth << 8; /* Send CMD6 SWITCH */ lu32_ErrState = MMC_CMD_Switch(hmmc->SDx, fu32_Argument); if (lu32_ErrState) { return lu32_ErrState; } else { /* Data line is in use */ while(__SD_GET_PRESENT_STATE(hmmc->SDx) & PreState_DAT_LINE_MASK); /* Host set bus width */ if (fu32_BusWidth == MMC_BUS_WIDTH_8BIT) { /* Enable Bus width 8bit */ __SD_8BIT_WIDTH_ENABLE(hmmc->SDx); } else { /* Disable Bus width 8bit */ __SD_8BIT_WIDTH_DISABLE(hmmc->SDx); if (fu32_BusWidth == MMC_BUS_WIDTH_4BIT) __SD_DATA_WIDTH_4BIT(hmmc->SDx); else __SD_DATA_WIDTH_1BIT(hmmc->SDx); } } return lu32_ErrState; } /************************************************************************************ * @fn eMMC_ReadBolcks * * @brief Reads block(s) from a specified address in a eMMC. * The Data transfer by polling mode. * * @param hsd: Pointer to SD handle. * fp_Data: pointer to the received dat buffer. * fu32_BlockAddr: Block Start Address. * fu16_BlockNum: Number of SD blocks to read. */ uint32_t eMMC_ReadBolcks(MMC_HandleTypeDef *hmmc, uint32_t *fp_Data, uint32_t fu32_BlockAddr, uint16_t fu16_BlockNum) { int i; uint32_t lu32_ErrState = INT_NO_ERR; if (fu16_BlockNum == 0) return E1_NUM_ERR; if (fu16_BlockNum == 1) { /* SEND CMD17 READ_SINGLI_BLOCK */ lu32_ErrState = MMC_CMD_ReadSingleBlock(hmmc->SDx, fu32_BlockAddr); if (lu32_ErrState) return lu32_ErrState; /* Wait for Buffer Read Ready Int */ while(!(__SD_GET_INT_STATUS(hmmc->SDx) & INT_BUFFER_READ_READY)); /* Clear Buffer_Read_Ready */ __SD_CLR_INT_STATUS(hmmc->SDx, INT_BUFFER_READ_READY); /* Read one block */ for (i = 0; i < BLOCKSIZE / 4; i++) { *fp_Data++ = __SD_GET_BUFFERDATA(hmmc->SDx); } } else { /* SEND CMD23 SET_BLOCK_COUNT */ lu32_ErrState = MMC_CMD_SetBlockCount(hmmc->SDx, fu16_BlockNum); if (lu32_ErrState) return lu32_ErrState; /* Set block count */ __SD_SET_BLOCK_COUNT(hmmc->SDx, fu16_BlockNum); /* SEND CMD18 READ_MULTIPLE_BLOCK */ lu32_ErrState = MMC_CMD_ReadMultiBlock(hmmc->SDx, fu32_BlockAddr); if (lu32_ErrState) return lu32_ErrState; while (fu16_BlockNum--) { /* Wait for Buffer_Read_Ready */ while(!(__SD_GET_INT_STATUS(hmmc->SDx) & INT_BUFFER_READ_READY)); /* Clear Buffer_Read_Ready */ __SD_CLR_INT_STATUS(hmmc->SDx, INT_BUFFER_READ_READY); /* Read one block */ for (i = 0; i < BLOCKSIZE / 4; i++) { *fp_Data++ = __SD_GET_BUFFERDATA(hmmc->SDx); } } } /* wait for transfer complete or any errors occur */ while(!(__SD_GET_INT_STATUS(hmmc->SDx) & (INT_TRANSFER_COMPLETE | INT_ERR_MASK))); /* Any errors occur */ if (__SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK) { lu32_ErrState = __SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK; } /* clear interrupt status */ __SD_CLR_ALL_INT_STATUS(hmmc->SDx); return lu32_ErrState; } /************************************************************************************ * @fn eMMC_WriteBolcks * * @brief Write block(s) from a specified address in a eMMC. * The Data transfer by polling mode. * * @param hsd: Pointer to SD handle. * fp_Data: pointer to the received dat buffer. * fu32_BlockAddr: Block Start Address. * fu16_BlockNum: Number of SD blocks to write. */ uint32_t eMMC_WriteBolcks(MMC_HandleTypeDef *hmmc, uint32_t *fp_Data, uint32_t fu32_BlockAddr, uint16_t fu16_BlockNum) { int i; uint32_t lu32_ErrState = INT_NO_ERR; if (fu16_BlockNum == 0) return E1_NUM_ERR; if (fu16_BlockNum == 1) { /* SEND CMD24 WRITE_BLOCK */ lu32_ErrState = MMC_CMD_WriteSingleBlock(hmmc->SDx, fu32_BlockAddr); if (lu32_ErrState) return lu32_ErrState; /* Wait for buffer write enable Int */ while(!(__SD_GET_PRESENT_STATE(hmmc->SDx) & PreState_BUFFER_WRITE_EN_MASK)); /* write one block */ for (i = 0; i < BLOCKSIZE / 4; i++) { __SD_SET_BUFFERDATA(hmmc->SDx, *fp_Data++); } } else { /* SEND CMD23 SET_BLOCK_COUNT */ lu32_ErrState = MMC_CMD_SetBlockCount(hmmc->SDx, fu16_BlockNum); if (lu32_ErrState) return lu32_ErrState; /* Set block count */ __SD_SET_BLOCK_COUNT(hmmc->SDx, fu16_BlockNum); /* SEND CMD25 WRITE_MULTIPLE_BLOCK */ lu32_ErrState = MMC_CMD_WriteMultiBlock(hmmc->SDx, fu32_BlockAddr); if (lu32_ErrState) return lu32_ErrState; /* First block transfer check the PresentState register */ /* Wait for Buffer_Write_Enable */ while(!(__SD_GET_PRESENT_STATE(hmmc->SDx) & PreState_BUFFER_WRITE_EN_MASK)); /* write one block */ for (i = 0; i < BLOCKSIZE / 4; i++) { __SD_SET_BUFFERDATA(hmmc->SDx, *fp_Data++); } fu16_BlockNum--; /* Other block transfer check the StatusInt register */ while(fu16_BlockNum--) { /* Wait for Buffer_Write_Ready */ while(!(__SD_GET_INT_STATUS(hmmc->SDx) & INT_BUFFER_WRITE_READY)); /* Clear Buffer_Write_Ready */ __SD_CLR_INT_STATUS(hmmc->SDx, INT_BUFFER_WRITE_READY); /* write one block */ for (i = 0; i < BLOCKSIZE / 4; i++) { __SD_SET_BUFFERDATA(hmmc->SDx, *fp_Data++); } } } /* wait for transfer complete or any errors occur */ while(!(__SD_GET_INT_STATUS(hmmc->SDx) & (INT_TRANSFER_COMPLETE | INT_ERR_MASK))); /* Any errors occur */ if (__SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK) { lu32_ErrState = __SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK; } /* clear interrupt status */ __SD_CLR_ALL_INT_STATUS(hmmc->SDx); return lu32_ErrState; } /************************************************************************************ * @fn eMMC_ReadBolcks_SDMA * * @brief Reads block(s) from a specified address in a eMMC. * The Data transfer by SDMA(Simple DMA) mode. * * @param hsd: Pointer to SD handle. * fp_Data: pointer to the received dat buffer. * fu32_BlockAddr: Block Start Address. * fu16_BlockNum: Number of SD blocks to read. */ uint32_t eMMC_ReadBolcks_SDMA(MMC_HandleTypeDef *hmmc, uint32_t *fp_Data, uint32_t fu32_BlockAddr, uint16_t fu16_BlockNum) { int i; uint32_t lu32_ErrState = INT_NO_ERR; bool lb_TrfComplete = true; uint32_t lu32_Addr = (uint32_t)fp_Data; if (fu16_BlockNum == 0) return E1_NUM_ERR; /* SEND CMD23 SET_BLOCK_COUNT */ lu32_ErrState = MMC_CMD_SetBlockCount(hmmc->SDx, fu16_BlockNum); if (lu32_ErrState) return lu32_ErrState; /* set SDMA system Address */ __SD_SET_SDMA_SYSTEM_ADDR(hmmc->SDx, (uint32_t)fp_Data); /* Set block count */ __SD_SET_BLOCK_COUNT(hmmc->SDx, fu16_BlockNum); /* SEND CMD18 READ_MULTIPLE_BLOCK */ lu32_ErrState = MMC_CMD_ReadBlock_SDMA(hmmc->SDx, fu32_BlockAddr); if (lu32_ErrState) return lu32_ErrState; /* Address alignment */ lu32_Addr &= SDMA_ADDR_ALIGN_MASK; /* wait for transfer complete or any errors occur */ while (lb_TrfComplete) { if (__SD_GET_INT_STATUS(hmmc->SDx) & (INT_TRANSFER_COMPLETE | INT_ERR_MASK)) { lb_TrfComplete = false; } if (__SD_GET_INT_STATUS(hmmc->SDx) & INT_DMA_INT) { __SD_CLR_INT_STATUS(hmmc->SDx, INT_DMA_INT); /* SDMA system Address alignment increases */ lu32_Addr += SDMA_ADDR_UNIT; /* Update SDMA system Address */ __SD_SET_SDMA_SYSTEM_ADDR(hmmc->SDx, (uint32_t)lu32_Addr); } } /* Any errors occur */ if (__SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK) { lu32_ErrState = __SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK; } /* clear interrupt status */ __SD_CLR_ALL_INT_STATUS(hmmc->SDx); return lu32_ErrState; } /************************************************************************************ * @fn eMMC_WriteBolcks_SDMA * * @brief Write block(s) from a specified address in a eMMC. * The Data transfer by SDMA(Simple DMA) mode. * * @param hsd: Pointer to SD handle. * fp_Data: pointer to the received dat buffer. * fu32_BlockAddr: Block Start Address. * fu16_BlockNum: Number of SD blocks to write. */ uint32_t eMMC_WriteBolcks_SDMA(MMC_HandleTypeDef *hmmc, uint32_t *fp_Data, uint32_t fu32_BlockAddr, uint16_t fu16_BlockNum) { int i; uint32_t lu32_ErrState = INT_NO_ERR; bool lb_TrfComplete = true; uint32_t lu32_Addr = (uint32_t)fp_Data; if (fu16_BlockNum == 0) return E1_NUM_ERR; /* SEND CMD23 SET_BLOCK_COUNT */ lu32_ErrState = MMC_CMD_SetBlockCount(hmmc->SDx, fu16_BlockNum); if (lu32_ErrState) return lu32_ErrState; /* set SDMA system Address */ __SD_SET_SDMA_SYSTEM_ADDR(hmmc->SDx, (uint32_t)fp_Data); /* Set block count */ __SD_SET_BLOCK_COUNT(hmmc->SDx, fu16_BlockNum); /* SEND CMD25 WRITE_MULTIPLE_BLOCK */ lu32_ErrState = MMC_CMD_WriteBlock_SDMA(hmmc->SDx, fu32_BlockAddr); if (lu32_ErrState) return lu32_ErrState; /* Address alignment */ lu32_Addr &= SDMA_ADDR_ALIGN_MASK; /* wait for transfer complete or any errors occur */ while (lb_TrfComplete) { if (__SD_GET_INT_STATUS(hmmc->SDx) & (INT_TRANSFER_COMPLETE | INT_ERR_MASK)) { lb_TrfComplete = false; } if (__SD_GET_INT_STATUS(hmmc->SDx) & INT_DMA_INT) { __SD_CLR_INT_STATUS(hmmc->SDx, INT_DMA_INT); /* SDMA system Address alignment increases */ lu32_Addr += SDMA_ADDR_UNIT; /* Update SDMA system Address */ __SD_SET_SDMA_SYSTEM_ADDR(hmmc->SDx, (uint32_t)lu32_Addr); } } /* Any errors occur */ if (__SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK) { lu32_ErrState = __SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK; } /* clear interrupt status */ __SD_CLR_ALL_INT_STATUS(hmmc->SDx); return lu32_ErrState; } /************************************************************************************ * @fn MMCCard_GetCardCSD * * @brief Get the card information from the CSD register. */ static void __MMCCard_GetCardCSD(MMC_HandleTypeDef *hmmc) { hmmc->CSDInfo = (MMC_CardCSDTypeDef *)hmmc->CSD; /* Card memory capacity, unit KByte */ hmmc->CardInfo.MemoryCapacity = hmmc->ExCSDInfo.SEC_COUNT / 2; /* Card command class */ hmmc->CardInfo.Class = hmmc->CSDInfo->CCC; } /************************************************************************************ * @fn __MMCCard_GetCardCID * * @brief Get the card information from the CID register. */ static void __MMCCard_GetCardCID(MMC_HandleTypeDef *hmmc) { hmmc->CIDInfo = (MMC_CardCIDTypeDef *)hmmc->CID; } /************************************************************************************ * @fn __eMMC_PowerON * * @brief Enquires cards about their operating voltage and configures clock ontrols. */ static uint32_t __eMMC_PowerON(MMC_HandleTypeDef *hmmc) { uint32_t lu32_ErrState = INT_NO_ERR; bool lb_BusyStatus = false; /* CMD0: GO_IDLE_STATE */ lu32_ErrState = MMC_CMD_GoIdleState(hmmc->SDx); if(lu32_ErrState) { return lu32_ErrState; } while (lb_BusyStatus == false) { /* SEND CMD1 SEND_OP_COND with eMMC_DUAL_VOLTAGE_RANGE(0xC0FF8080U) as argument */ lu32_ErrState = MMC_CMD_SendOperCondition(hmmc->SDx, eMMC_DUAL_VOLTAGE_RANGE, &hmmc->OCR); if(lu32_ErrState) { return lu32_ErrState; } lb_BusyStatus = hmmc->OCR & OCR_BUSY ? true : false; } hmmc->CardInfo.CardType = 0; /* After power routine and command returns valid voltage */ if (hmmc->OCR & eMMC_DUAL_VOLTAGE_CARD) { /* Voltage range of the card is within 1.70V ~ 1.95V or 2.7V ~ 3.6V */ hmmc->CardInfo.CardType = eMMC_DUAL_VOLTAGE_CARD; } if (hmmc->OCR & eMMC_CAPACITY_HIGHER_2G) { /* Capacity > 2G, sector mode */ hmmc->CardInfo.CardType |= eMMC_CAPACITY_HIGHER_2G; } return lu32_ErrState; } /************************************************************************************ * @fn __eMMC_InitCard * * @brief Initializes the sd card. */ static uint32_t __eMMC_InitCard(MMC_HandleTypeDef *hmmc) { uint32_t lu32_ErrState = INT_NO_ERR; uint32_t lu32_Response, lu32_TempValue; /* CMD2: ALL_SEND_CID */ lu32_ErrState = MMC_CMD_AllSendCID(hmmc->SDx, hmmc->CID); if (lu32_ErrState) { return lu32_ErrState; } /* CMD3: SET_RELATIVE_ADDR */ hmmc->RCA = 0xF1170000; lu32_ErrState = MMC_CMD_SetRelAddr(hmmc->SDx, hmmc->RCA); if (lu32_ErrState) { return lu32_ErrState; } /* After CMD3, Bus select push-pull mode */ __SD_MMC_OD_PP(hmmc->SDx, 1); /* CMD9: SEND_CSD */ lu32_ErrState = MMC_CMD_SendCSD(hmmc->SDx, hmmc->RCA, hmmc->CSD); if (lu32_ErrState) { return lu32_ErrState; } /* CMD7: SEL_DESEL_CARD */ lu32_ErrState = MMC_CMD_SelectCard(hmmc->SDx, hmmc->RCA); if (lu32_ErrState) { return lu32_ErrState; } /* Wait to enter transfer state */ uint32_t lu32_Timeout = 100; while (lu32_Timeout--) { /* CMD13: SEND_STATUS */ lu32_ErrState = SD_CMD_SendStatus(hmmc->SDx, hmmc->RCA, &lu32_Response); if (lu32_ErrState) { return lu32_ErrState; } if (lu32_Response & CARD_CURRENT_STATE_TRAN) break; } /* CMD8: SEND_EXT_CSD */ lu32_ErrState = MMC_CMD_SendExtendedCSD(hmmc->SDx); if (lu32_ErrState) { return lu32_ErrState; } /* Wait for Buffer Read Ready Int */ while(!(__SD_GET_INT_STATUS(hmmc->SDx) & INT_BUFFER_READ_READY)); /* Clear Buffer_Read_Ready */ __SD_CLR_INT_STATUS(hmmc->SDx, INT_BUFFER_READ_READY); /* Read one block */ for (int i = 0; i < BLOCKSIZE / 4; i++) { lu32_TempValue = __SD_GET_BUFFERDATA(hmmc->SDx); /* Extract Parameter */ switch (i) { case MMC_EX_CSD_INDEX_SEC_COUNT0 / 4: { /* Byte 212,213,214,215 */ hmmc->ExCSDInfo.SEC_COUNT = lu32_TempValue; }break; case MMC_EX_CSD_INDEX_BUS_WIDTH / 4: { /* Byte 183 */ hmmc->ExCSDInfo.BUS_WIDTH = lu32_TempValue >> 24 & 0xFF; }break; case MMC_EX_CSD_INDEX_HS_TIMING / 4: { /* Byte 185 */ hmmc->ExCSDInfo.HS_TIMING = lu32_TempValue >> 8 & 0xFF; }break; case MMC_EX_CSD_INDEX_EXT_CSD_REV / 4: { /* Byte 192 */ hmmc->ExCSDInfo.EXT_CSD_REV = lu32_TempValue & 0xFF; }break; case MMC_EX_CSD_INDEX_DEVICE_TYPE / 4: { /* Byte 196 */ hmmc->ExCSDInfo.DEVICE_TYPE = lu32_TempValue & 0xFF; }break; default: break; } } /* wait for transfer complete or any errors occur */ while(!(__SD_GET_INT_STATUS(hmmc->SDx) & (INT_TRANSFER_COMPLETE | INT_ERR_MASK))); /* Any errors occur */ if (__SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK) { lu32_ErrState = __SD_GET_INT_STATUS(hmmc->SDx) & INT_ERR_MASK; } /* clear interrupt status */ __SD_CLR_ALL_INT_STATUS(hmmc->SDx); /* Update bus clock speed */ __SD_CLOCK_DIV_LOW_8BIT(hmmc->SDx, (hmmc->Init.ClockDiv / 2)); return lu32_ErrState; }