Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 140 additions & 77 deletions bsp/stm32/libraries/HAL_Drivers/drivers/drv_sdmmc.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
Expand All @@ -8,6 +8,7 @@
* 2020-05-23 liuduanfei first version
* 2020-08-25 wanghaijing add sdmmmc2
* 2023-03-26 wdfk-prog Distinguish between SDMMC and SDIO drivers
* 2024-08-05 wdfk-prog Optimize SDMMC timeout handling and clock divider
*/
#include "board.h"

Expand All @@ -27,7 +28,6 @@
#endif /* DRV_DEBUG */
#include <rtdbg.h>

static struct stm32_sdio_class sdio_obj;
static struct rt_mmcsd_host *host1;
static struct rt_mmcsd_host *host2;

Expand Down Expand Up @@ -55,6 +55,11 @@ struct rthw_sdio
rt_align(SDIO_ALIGN_LEN)
static rt_uint8_t cache_buf[SDIO_BUFF_SIZE];

static rt_uint32_t stm32_sdio_clk_get(void)
{
return SDIO_CLOCK_FREQ;
}

/**
* @brief This function get order from sdio.
* @param data
Expand Down Expand Up @@ -154,10 +159,6 @@ static void rthw_sdio_wait_completed(struct rthw_sdio *sdio)
return;
}

if (sdio->pkg == RT_NULL)
{
return;
}
/* Get Card Specific Data */
cmd->resp[0] = hsd->RESP1;
if (resp_type(cmd) == RESP_R2)
Expand Down Expand Up @@ -221,6 +222,38 @@ static void rthw_sdio_wait_completed(struct rthw_sdio *sdio)
}
}

/**
* @brief Calculate timeout for SDMMC data transfer.
*
* @note The timeout is estimated based on transfer size:
* timeout = 100 ms + 2 ms per 512 bytes.
* The result is clamped to the range [100 ms, 5000 ms].
*
* @param data Pointer to transfer data descriptor.
*
* @return Timeout in RT-Thread tick units.
*/
static rt_tick_t stm32_sdmmc_calc_timeout(struct mmcsd_data *data)
{
rt_size_t len;
rt_uint32_t timeout_ms;

if (data == RT_NULL)
return rt_tick_from_millisecond(100);

len = data->blks * data->blksize;

/* base timeout + size-based timeout */
timeout_ms = 100 + (len / 512) * 2;

if (timeout_ms < 100)
timeout_ms = 100;
if (timeout_ms > 5000)
timeout_ms = 5000;

return rt_tick_from_millisecond(timeout_ms);
}

/**
* @brief This function send command.
* @param sdio rthw_sdio
Expand Down Expand Up @@ -262,7 +295,7 @@ static void rthw_sdio_send_command(struct rthw_sdio *sdio, struct sdio_pkg *pkg)
/* data pre configuration */
if (data != RT_NULL)
{
SCB_CleanInvalidateDCache();
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, cache_buf, data->blks * data->blksize);

reg_cmd |= SDMMC_CMD_CMDTRANS;
__HAL_SD_DISABLE_IT(&sdio->sdio_des.hw_sdio, SDMMC_MASK_CMDRENDIE | SDMMC_MASK_CMDSENTIE);
Expand Down Expand Up @@ -292,14 +325,19 @@ static void rthw_sdio_send_command(struct rthw_sdio *sdio, struct sdio_pkg *pkg)
/* Waiting for data to be sent to completion */
if (data != RT_NULL)
{
volatile rt_uint32_t count = SDIO_TX_RX_COMPLETE_TIMEOUT_LOOPS;
rt_tick_t start = rt_tick_get();
rt_tick_t timeout = stm32_sdmmc_calc_timeout(data);

while (count && (hsd->STA & SDMMC_STA_DPSMACT))
while (hsd->STA & SDMMC_STA_DPSMACT)
{
count--;
if ((rt_tick_get() - start) >= timeout)
{
cmd->err = -RT_ETIMEOUT;
break;
}
}

if ((count == 0) || (hsd->STA & SDIO_ERRORS))
if ((hsd->STA & SDIO_ERRORS) != 0)
{
cmd->err = -RT_ERROR;
}
Expand All @@ -311,7 +349,7 @@ static void rthw_sdio_send_command(struct rthw_sdio *sdio, struct sdio_pkg *pkg)
if (data->flags & DATA_DIR_READ)
{
rt_memcpy(data->buf, cache_buf, data->blks * data->blksize);
SCB_CleanInvalidateDCache();
rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, cache_buf, data->blks * data->blksize);
}
}
}
Expand Down Expand Up @@ -378,6 +416,33 @@ static void rthw_sdio_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *
SDMMC_InitTypeDef Init = {0};
rt_uint32_t sdmmc_clk = sdio->sdio_des.clk_get();

rt_bool_t stop_flag = RT_TRUE;

switch (io_cfg->power_mode & 0X03)
{
case MMCSD_POWER_OFF:
/* Set Power State to OFF */
(void)SDMMC_PowerState_OFF(hsd->Instance);
stop_flag = RT_TRUE;
break;
case MMCSD_POWER_UP:
/* In F4 series chips, 0X01 is reserved bit and has no practical effect.
For F7 series chips, 0X01 is power-on after power-off,The SDMMC disables the function and the card clock stops.
For H7 series chips, 0X03 is the power-on function.
*/
stop_flag = RT_TRUE;
break;
case MMCSD_POWER_ON:
stop_flag = RT_FALSE;
break;
default:
LOG_W("unknown power mode %d", io_cfg->power_mode);
stop_flag = RT_FALSE;
break;
}

if(stop_flag == RT_TRUE) return;

if (sdmmc_clk < 400 * 1000)
{
LOG_E("The clock rate is too low! rata:%d", sdmmc_clk);
Expand Down Expand Up @@ -422,91 +487,88 @@ static void rthw_sdio_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *
Init.BusWide = SDMMC_BUS_WIDE_1B;
}
Init.HardwareFlowControl = hsd->Init.HardwareFlowControl;
/* Check if user Clock div < Normal speed 25Mhz, no change in Clockdiv */
if (hsd->Init.ClockDiv >= (sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ)))
{
Init.ClockDiv = hsd->Init.ClockDiv;
}
//CARD_ULTRA_HIGH_SPEED :UHS-I SD Card <50Mo/s for SDR50, DDR5 Cards and <104Mo/s for SDR104, Spec version 3.01
else if (MMCSD_TIMING_UHS_SDR50 <= io_cfg->timing && io_cfg->timing <= MMCSD_TIMING_UHS_DDR50)
{
/* UltraHigh speed SD card,user Clock div */
Init.ClockDiv = hsd->Init.ClockDiv;
}
//CARD_HIGH_SPEED: High Speed Card <25Mo/s , Spec version 2.00
else if (io_cfg->timing == MMCSD_TIMING_SD_HS)
/* Keep user clock divider if current setting is already <= normal speed */
if (clk != SD_INIT_FREQ)
{
/* High speed SD card, Max Frequency = 50Mhz */
if (hsd->Init.ClockDiv == 0U)
if (hsd->Init.ClockDiv >= (sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ)))
{
if (sdmmc_clk > SD_HIGH_SPEED_FREQ)
{
Init.ClockDiv = sdmmc_clk / (2U * SD_HIGH_SPEED_FREQ);
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
}
Init.ClockDiv = hsd->Init.ClockDiv;
}
else
//CARD_ULTRA_HIGH_SPEED :UHS-I SD Card <50Mo/s for SDR50, DDR5 Cards and <104Mo/s for SDR104, Spec version 3.01
else if (MMCSD_TIMING_UHS_SDR50 <= io_cfg->timing && io_cfg->timing <= MMCSD_TIMING_UHS_DDR50)
{
/* UltraHigh speed SD card,user Clock div */
Init.ClockDiv = hsd->Init.ClockDiv;
}
//CARD_HIGH_SPEED: High Speed Card <25Mo/s , Spec version 2.00
else if (io_cfg->timing == MMCSD_TIMING_SD_HS)
{
if ((sdmmc_clk/(2U * hsd->Init.ClockDiv)) > SD_HIGH_SPEED_FREQ)
/* High speed SD card, Max Frequency = 50Mhz */
if (hsd->Init.ClockDiv == 0U)
{
Init.ClockDiv = sdmmc_clk / (2U * SD_HIGH_SPEED_FREQ);
if (sdmmc_clk > SD_HIGH_SPEED_FREQ)
{
Init.ClockDiv = sdmmc_clk / (2U * SD_HIGH_SPEED_FREQ);
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
}
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
if ((sdmmc_clk/(2U * hsd->Init.ClockDiv)) > SD_HIGH_SPEED_FREQ)
{
Init.ClockDiv = sdmmc_clk / (2U * SD_HIGH_SPEED_FREQ);
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
}
}
}
}
//CARD_NORMAL_SPEED: Normal Speed Card <12.5Mo/s , Spec Version 1.01
else if (io_cfg->timing == MMCSD_TIMING_LEGACY)
{
/* No High speed SD card, Max Frequency = 25Mhz */
if (hsd->Init.ClockDiv == 0U)
//CARD_NORMAL_SPEED: Normal Speed Card <12.5Mo/s , Spec Version 1.01
else if (io_cfg->timing == MMCSD_TIMING_LEGACY)
{
if (sdmmc_clk > SD_NORMAL_SPEED_FREQ)
/* No High speed SD card, Max Frequency = 25Mhz */
if (hsd->Init.ClockDiv == 0U)
{
Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ);
if (sdmmc_clk > SD_NORMAL_SPEED_FREQ)
{
Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ);
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
}
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
if ((sdmmc_clk/(2U * hsd->Init.ClockDiv)) > SD_NORMAL_SPEED_FREQ)
{
Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ);
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
}
}
}
else
{
if ((sdmmc_clk/(2U * hsd->Init.ClockDiv)) > SD_NORMAL_SPEED_FREQ)
{
Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ);
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
}
Init.ClockDiv = hsd->Init.ClockDiv;
}
}
else
{
Init.ClockDiv = hsd->Init.ClockDiv;
}
(void)SDMMC_Init(hsd->Instance, Init);
}
switch ((io_cfg->power_mode)&0X03)
if((io_cfg->power_mode & 0X03) == MMCSD_POWER_ON)
{
case MMCSD_POWER_OFF:
/* Set Power State to OFF */
(void)SDMMC_PowerState_OFF(hsd->Instance);
break;
case MMCSD_POWER_UP:
/* In F4 series chips, 0X01 is reserved bit and has no practical effect.
For F7 series chips, 0X01 is power-on after power-off,The SDMMC disables the function and the card clock stops.
For H7 series chips, 0X03 is the power-on function.
*/
case MMCSD_POWER_ON:
/* Set Power State to ON */
(void)SDMMC_PowerState_ON(hsd->Instance);
break;
default:
LOG_W("unknown power mode %d", io_cfg->power_mode);
break;
}
}

Expand Down Expand Up @@ -578,15 +640,15 @@ struct rt_mmcsd_host *sdio_host_create(struct stm32_sdio_des *sdio_des)

if (sdio_des == RT_NULL)
{
LOG_E("L:%d F:%s",(sdio_des == RT_NULL ? "sdio_des is NULL" : ""));
LOG_E("sdio_des is NULL");
return RT_NULL;
}

sdio = rt_malloc(sizeof(struct rthw_sdio));

if (sdio == RT_NULL)
{
LOG_E("L:%d F:%s malloc rthw_sdio fail");
LOG_E("malloc rthw_sdio fail");
return RT_NULL;
}

Expand All @@ -596,12 +658,13 @@ struct rt_mmcsd_host *sdio_host_create(struct stm32_sdio_des *sdio_des)

if (host == RT_NULL)
{
LOG_E("L:%d F:%s mmcsd alloc host fail");
LOG_E("mmcsd alloc host fail");
rt_free(sdio);
return RT_NULL;
}

rt_memcpy(&sdio->sdio_des, sdio_des, sizeof(struct stm32_sdio_des));
sdio->sdio_des.clk_get = (sdio_des->clk_get == RT_NULL ? stm32_sdio_clk_get : sdio_des->clk_get);
#ifdef BSP_USING_SDIO1
if(sdio_des->hw_sdio.Instance == SDMMC1)
{
Expand Down Expand Up @@ -630,14 +693,14 @@ struct rt_mmcsd_host *sdio_host_create(struct stm32_sdio_des *sdio_des)
#endif
host->max_seg_size = SDIO_BUFF_SIZE;
host->max_dma_segs = 1;
host->max_blk_size = 512;
host->max_blk_count = 512;
host->max_blk_size = BLOCKSIZE;
host->max_blk_count = BLOCKSIZE;

/* link up host and sdio */
sdio->host = host;
host->private_data = sdio;

rthw_sdio_irq_update(host, 1);
rthw_sdio_irq_update(host, RT_TRUE);

/* ready to change */
mmcsd_change(host);
Expand Down
Loading
Loading