Files

614 lines
18 KiB
C

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