mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #19540 from gschorcht/cpu/stm32/periph/sdmmc
cpu/stm32/periph: add low-level SDMMC peripheral driver
This commit is contained in:
commit
7c2f091b39
@ -24,6 +24,7 @@ config BOARD_STM32F746G_DISCO
|
||||
select HAS_PERIPH_RTT
|
||||
select HAS_PERIPH_SPI
|
||||
select HAS_PERIPH_TIMER
|
||||
select HAS_PERIPH_SDMMC
|
||||
select HAS_PERIPH_UART
|
||||
select HAS_PERIPH_USBDEV
|
||||
select HAS_PERIPH_USBDEV_HS
|
||||
@ -37,7 +38,7 @@ config BOARD_STM32F746G_DISCO
|
||||
select HAVE_SAUL_GPIO
|
||||
select HAVE_STM32_ETH
|
||||
select HAVE_FT5X06
|
||||
|
||||
select HAVE_MTD_SDMMC_DEFAULT
|
||||
|
||||
config CLOCK_HSE
|
||||
default 25000000
|
||||
|
@ -17,3 +17,14 @@ endif
|
||||
ifneq (,$(filter periph_fmc,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_fmc_16bit
|
||||
endif
|
||||
|
||||
# default to using fatfs on SD card
|
||||
ifneq (,$(filter vfs_default,$(USEMODULE)))
|
||||
USEMODULE += fatfs_vfs
|
||||
USEMODULE += mtd
|
||||
endif
|
||||
|
||||
ifneq (,$(filter mtd,$(USEMODULE)))
|
||||
USEMODULE += mtd_sdmmc_default
|
||||
USEMODULE += periph_sdmmc
|
||||
endif
|
||||
|
@ -32,6 +32,7 @@ Current hardware support:
|
||||
| User microphones | - | |
|
||||
| External Quad-SPI Flash | - | |
|
||||
| External SDRAM | X | |
|
||||
| SD Card Interface | x | SDMMC1 on PC8..PC13 and PD2 |
|
||||
|
||||
## Flashing the device
|
||||
|
||||
|
@ -8,6 +8,7 @@ FEATURES_PROVIDED += periph_i2c
|
||||
FEATURES_PROVIDED += periph_ltdc
|
||||
FEATURES_PROVIDED += periph_rtc
|
||||
FEATURES_PROVIDED += periph_rtt
|
||||
FEATURES_PROVIDED += periph_sdmmc
|
||||
FEATURES_PROVIDED += periph_spi
|
||||
FEATURES_PROVIDED += periph_timer
|
||||
FEATURES_PROVIDED += periph_uart
|
||||
|
@ -61,7 +61,7 @@ static const dma_conf_t dma_config[] = {
|
||||
{ .stream = 6 }, /* DMA1 Stream 6 - USART2_TX */
|
||||
{ .stream = 3 }, /* DMA1 Stream 3 - SPI2_RX */
|
||||
{ .stream = 4 }, /* DMA1 Stream 4 - SPI2_TX */
|
||||
{ .stream = 11 }, /* DMA2 Stream 3 - SPI4_RX */
|
||||
{ .stream = 11 }, /* DMA2 Stream 3 - SPI4_RX Ch5 / SDMMC1 Ch 4 */
|
||||
{ .stream = 12 }, /* DMA2 Stream 4 - SPI4_TX */
|
||||
{ .stream = 8 }, /* DMA2 Stream 0 - ETH_TX */
|
||||
};
|
||||
@ -260,7 +260,8 @@ static const ltdc_conf_t ltdc_config = {
|
||||
/* values below come from STM32CubeF7 code and differ from the typical
|
||||
* values mentioned in the RK043FN48H datasheet. Both sets of values work
|
||||
* with the display.
|
||||
* See the discussion in https://community.st.com/s/question/0D50X0000BOvdWP/how-to-set-displays-parameters-
|
||||
* See the discussion in
|
||||
* https://community.st.com/s/question/0D50X0000BOvdWP/how-to-set-displays-parameters-
|
||||
*/
|
||||
.hsync = 41,
|
||||
.vsync = 10,
|
||||
@ -430,6 +431,43 @@ static const fmc_bank_conf_t fmc_bank_config[] = {
|
||||
#define FMC_BANK_NUMOF ARRAY_SIZE(fmc_bank_config)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SDIO/SDMMC configuration
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief SDIO/SDMMC static configuration struct
|
||||
*/
|
||||
static const sdmmc_conf_t sdmmc_config[] = {
|
||||
{
|
||||
.dev = SDMMC1,
|
||||
.bus = APB2,
|
||||
.rcc_mask = RCC_APB2ENR_SDMMC1EN,
|
||||
.cd = GPIO_PIN(PORT_C, 13),
|
||||
.cd_active = 0, /* CD pin is LOW active */
|
||||
.cd_mode = GPIO_IN_PU, /* Pull-up R12 not soldered by default */
|
||||
.clk = { GPIO_PIN(PORT_C, 12), GPIO_AF12 },
|
||||
.cmd = { GPIO_PIN(PORT_D, 2), GPIO_AF12 },
|
||||
.dat0 = { GPIO_PIN(PORT_C, 8), GPIO_AF12 },
|
||||
.dat1 = { GPIO_PIN(PORT_C, 9), GPIO_AF12 },
|
||||
.dat2 = { GPIO_PIN(PORT_C, 10), GPIO_AF12 },
|
||||
.dat3 = { GPIO_PIN(PORT_C, 11), GPIO_AF12 },
|
||||
#ifdef MODULE_PERIPH_DMA
|
||||
.dma = 5,
|
||||
.dma_chan = 4,
|
||||
#endif
|
||||
.irqn = SDMMC1_IRQn
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Number of configured SDIO/SDMMC peripherals
|
||||
*/
|
||||
#define SDMMC_CONFIG_NUMOF 1
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -22,6 +22,7 @@ config BOARD_STM32F7508_DK
|
||||
select HAS_PERIPH_LTDC
|
||||
select HAS_PERIPH_RTC
|
||||
select HAS_PERIPH_RTT
|
||||
select HAS_PERIPH_SDMMC
|
||||
select HAS_PERIPH_SPI
|
||||
select HAS_PERIPH_TIMER
|
||||
select HAS_PERIPH_UART
|
||||
@ -34,9 +35,10 @@ config BOARD_STM32F7508_DK
|
||||
select BOARD_HAS_HSE
|
||||
select BOARD_HAS_LSE
|
||||
|
||||
select HAVE_FT5X06
|
||||
select HAVE_MTD_SDMMC_DEFAULT
|
||||
select HAVE_SAUL_GPIO
|
||||
select HAVE_STM32_ETH
|
||||
select HAVE_FT5X06
|
||||
|
||||
config CLOCK_HSE
|
||||
default 25000000
|
||||
|
@ -25,6 +25,7 @@ config BOARD_STM32L496G_DISCO
|
||||
select HAS_PERIPH_RTC
|
||||
select HAS_PERIPH_RTT
|
||||
select HAS_PERIPH_PWM
|
||||
select HAS_PERIPH_SDMMC
|
||||
select HAS_PERIPH_SPI
|
||||
select HAS_PERIPH_SPI_STMOD
|
||||
select HAS_PERIPH_TIMER
|
||||
@ -43,6 +44,7 @@ config BOARD_STM32L496G_DISCO
|
||||
|
||||
select HAVE_SAUL_GPIO
|
||||
select HAVE_FT5X06
|
||||
select HAVE_MTD_SDMMC_DEFAULT
|
||||
select HAVE_ST7789
|
||||
select HAVE_LCD_PARALLEL_16BIT if MODULE_ST7789
|
||||
select HAVE_LCD_PARALLEL_LL_MCU if MODULE_ST7789
|
||||
|
@ -31,3 +31,14 @@ ifneq (,$(filter st7789,$(USEMODULE)))
|
||||
USEMODULE += lcd_parallel_ll_mcu
|
||||
FEATURES_REQUIRED += periph_fmc_nor_sram
|
||||
endif
|
||||
|
||||
# default to using fatfs on SD card
|
||||
ifneq (,$(filter vfs_default,$(USEMODULE)))
|
||||
USEMODULE += fatfs_vfs
|
||||
USEMODULE += mtd
|
||||
endif
|
||||
|
||||
ifneq (,$(filter mtd,$(USEMODULE)))
|
||||
USEMODULE += mtd_sdmmc_default
|
||||
USEMODULE += periph_sdmmc
|
||||
endif
|
||||
|
@ -13,6 +13,7 @@ FEATURES_PROVIDED += periph_lpuart
|
||||
FEATURES_PROVIDED += periph_rtc
|
||||
FEATURES_PROVIDED += periph_rtt
|
||||
FEATURES_PROVIDED += periph_pwm
|
||||
FEATURES_PROVIDED += periph_sdmmc
|
||||
FEATURES_PROVIDED += periph_spi
|
||||
FEATURES_PROVIDED += periph_spi_stmod
|
||||
FEATURES_PROVIDED += periph_timer
|
||||
|
@ -38,7 +38,7 @@ The main features of this board are:
|
||||
| SAI audio codec | - | |
|
||||
| External PSRAM | x | Connected to FMC peripheral |
|
||||
| External Quad-SPI Flash | - | QSPI peripheral is not yet supported |
|
||||
| SD Card Interface | - | |
|
||||
| SD Card Interface | - | SDMMC1 on PC8..PC13/PD2 |
|
||||
|
||||
## Board Configuration (sorted by peripheral):
|
||||
|
||||
|
@ -106,7 +106,7 @@ static const adc_conf_t adc_config[] = {
|
||||
{ .pin = GPIO_PIN(PORT_C, 0), .dev = 1, .chan = 13 }, /* A5, ADC12_IN13, SB28 closed */
|
||||
{ .pin = GPIO_UNDEF, .dev = 0, .chan = 0 }, /* V_REFINT, ADC1_IN0 */
|
||||
{ .pin = GPIO_UNDEF, .dev = 0, .chan = 18 }, /* V_BAT, ADC1_IN18 */
|
||||
#ifndef MODULE_PERIPH_DAC
|
||||
#if !MODULE_PERIPH_DAC
|
||||
{ .pin = GPIO_PIN(PORT_A, 4), .dev = 0, .chan = 9 }, /* STMOD+_ADC, ADC12_IN9 */
|
||||
#else
|
||||
{ .pin = GPIO_UNDEF, .dev = 1, .chan = 17 }, /* DAC1, ADC2_IN17 */
|
||||
@ -153,7 +153,7 @@ static const adc_conf_t adc_config[] = {
|
||||
*/
|
||||
static const dac_conf_t dac_config[] = {
|
||||
{ GPIO_PIN(PORT_A, 4), .chan = 0 }, /* STMod+_ADC pin */
|
||||
#ifndef MODULE_PERIPH_SPI
|
||||
#if !MODULE_PERIPH_SPI
|
||||
{ GPIO_PIN(PORT_A, 5), .chan = 1 }, /* Arduino D13, conflicts with SPI_DEV(0) */
|
||||
#endif
|
||||
};
|
||||
@ -397,6 +397,41 @@ static const pwm_conf_t pwm_config[] = {
|
||||
#define PWM_NUMOF ARRAY_SIZE(pwm_config)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SDIO/SDMMC configuration
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief SDIO/SDMMC static configuration struct
|
||||
*/
|
||||
static const sdmmc_conf_t sdmmc_config[] = {
|
||||
{
|
||||
.dev = SDMMC1,
|
||||
.bus = APB2,
|
||||
.rcc_mask = RCC_APB2ENR_SDMMC1EN,
|
||||
.cd = GPIO_UNDEF, /* CD is connected to MFX GPIO8 */
|
||||
.clk = { GPIO_PIN(PORT_C, 12), GPIO_AF12 },
|
||||
.cmd = { GPIO_PIN(PORT_D, 2), GPIO_AF12 },
|
||||
.dat0 = { GPIO_PIN(PORT_C, 8), GPIO_AF12 },
|
||||
.dat1 = { GPIO_PIN(PORT_C, 9), GPIO_AF12 },
|
||||
.dat2 = { GPIO_PIN(PORT_C, 10), GPIO_AF12 },
|
||||
.dat3 = { GPIO_PIN(PORT_C, 11), GPIO_AF12 },
|
||||
#if MODULE_PERIPH_DMA
|
||||
.dma = 6,
|
||||
.dma_chan = 7,
|
||||
#endif
|
||||
.irqn = SDMMC1_IRQn
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Number of configured SDIO/SDMMC peripherals
|
||||
*/
|
||||
#define SDMMC_CONFIG_NUMOF 1
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SPI configuration
|
||||
*
|
||||
@ -422,14 +457,14 @@ static const spi_conf_t spi_config[] = {
|
||||
.cs_af = GPIO_AF5,
|
||||
.rccmask = RCC_APB2ENR_SPI1EN,
|
||||
.apbbus = APB2,
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
#if MODULE_PERIPH_DMA
|
||||
.rx_dma = 0, /* DMA1 Channel 2 */
|
||||
.rx_dma_chan = 1, /* CxS = 1 */
|
||||
.tx_dma = 1, /* DMA1 Channel 3 */
|
||||
.tx_dma_chan = 1, /* CxS = 1 */
|
||||
#endif
|
||||
},
|
||||
#if IS_USED(MODULE_PERIPH_SPI_STMOD)
|
||||
#if MODULE_PERIPH_SPI_STMOD
|
||||
{ /* Pmod/STMod+ connector if solder bridges SB4, SB5, SB9 are closed */
|
||||
.dev = SPI2,
|
||||
.mosi_pin = GPIO_PIN(PORT_B, 15),
|
||||
@ -442,7 +477,7 @@ static const spi_conf_t spi_config[] = {
|
||||
.cs_af = GPIO_AF5,
|
||||
.rccmask = RCC_APB1ENR1_SPI2EN,
|
||||
.apbbus = APB1,
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
#if MODULE_PERIPH_DMA
|
||||
.rx_dma = 2, /* DMA1 Channel 4 */
|
||||
.rx_dma_chan = 1, /* CxS = 1 */
|
||||
.tx_dma = 3, /* DMA1 Channel 5 */
|
||||
@ -503,13 +538,13 @@ static const uart_conf_t uart_config[] = {
|
||||
.tx_af = GPIO_AF7,
|
||||
.bus = APB1,
|
||||
.irqn = USART2_IRQn,
|
||||
#if IS_USED(MODULE_PERIPH_UART_HW_FC)
|
||||
#if MODULE_PERIPH_UART_HW_FC
|
||||
.cts_pin = GPIO_UNDEF, /* CTS is not connected */
|
||||
.rts_pin = GPIO_UNDEF, /* RTS is not connected */
|
||||
#endif
|
||||
.type = STM32_USART,
|
||||
.clk_src = 0, /* Use APB clock */
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
#if MODULE_PERIPH_DMA
|
||||
.dma = 4, /* DMA1 Channel 7 */
|
||||
.dma_chan = 2, /* CxS = 2 */
|
||||
#endif
|
||||
@ -523,19 +558,19 @@ static const uart_conf_t uart_config[] = {
|
||||
.tx_af = GPIO_AF8,
|
||||
.bus = APB12,
|
||||
.irqn = LPUART1_IRQn,
|
||||
#if IS_USED(MODULE_PERIPH_UART_HW_FC)
|
||||
#if MODULE_PERIPH_UART_HW_FC
|
||||
.cts_pin = GPIO_UNDEF, /* CTS is not connected */
|
||||
.rts_pin = GPIO_UNDEF, /* RTS is not connected */
|
||||
#endif
|
||||
.type = STM32_LPUART,
|
||||
.clk_src = 0, /* Use APB clock */
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
#if MODULE_PERIPH_DMA
|
||||
.dma = 5, /* DMA2 Channel 6 */
|
||||
.dma_chan = 4, /* CxS = 4 */
|
||||
#endif
|
||||
},
|
||||
|
||||
#if !IS_USED(MODULE_PERIPH_SPI_STMOD)
|
||||
#if !MODULE_PERIPH_SPI_STMOD
|
||||
{ /* Pmod/STMod+ connector if solder bridges SB6, SB7, SB8 are closed (default) */
|
||||
.dev = USART1,
|
||||
.rcc_mask = RCC_APB2ENR_USART1EN,
|
||||
@ -545,7 +580,7 @@ static const uart_conf_t uart_config[] = {
|
||||
.tx_af = GPIO_AF7,
|
||||
.bus = APB2,
|
||||
.irqn = USART1_IRQn,
|
||||
#if IS_USED(MODULE_PERIPH_UART_HW_FC)
|
||||
#if MODULE_PERIPH_UART_HW_FC
|
||||
.cts_pin = GPIO_PIN(PORT_G, 11),
|
||||
.rts_pin = GPIO_PIN(PORT_G, 12),
|
||||
.cts_af = GPIO_AF7,
|
||||
@ -553,12 +588,12 @@ static const uart_conf_t uart_config[] = {
|
||||
#endif
|
||||
.type = STM32_USART,
|
||||
.clk_src = 0, /* Use APB clock */
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
#if MODULE_PERIPH_DMA
|
||||
.dma = 2, /* DMA1 Channel 4 */
|
||||
.dma_chan = 2, /* CxS = 2 */
|
||||
#endif
|
||||
},
|
||||
#endif /* !IS_USED(MODULE_PERIPH_SPI_STMOD) */
|
||||
#endif /* !MODULE_PERIPH_SPI_STMOD */
|
||||
};
|
||||
|
||||
#define UART_0_ISR (isr_usart2)
|
||||
|
@ -87,4 +87,10 @@ ifneq (,$(filter periph_fmc_%,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_fmc
|
||||
endif
|
||||
|
||||
ifneq (,$(filter periph_sdmmc,$(FEATURES_USED)))
|
||||
FEATURES_REQUIRED += periph_gpio_irq
|
||||
FEATURES_REQUIRED += periph_sdmmc_clk
|
||||
FEATURES_OPTIONAL += periph_dma
|
||||
endif
|
||||
|
||||
include $(RIOTCPU)/cortexm_common/Makefile.dep
|
||||
|
@ -82,6 +82,13 @@ ifneq (,$(filter $(CPU_FAM),f2 f4 f7 g4 l0 l4 l5 u5 wb))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter $(CPU_FAM),f2 f4 f7 l4))
|
||||
FEATURES_PROVIDED += periph_sdmmc_auto_clk
|
||||
FEATURES_PROVIDED += periph_sdmmc_clk
|
||||
FEATURES_PROVIDED += periph_sdmmc_hs
|
||||
FEATURES_PROVIDED += periph_sdmmc_mmc
|
||||
endif
|
||||
|
||||
ifneq (,$(filter $(CPU_FAM),f2 f4 f7 g4 l1 l4 mp1))
|
||||
FEATURES_PROVIDED += cortexm_mpu
|
||||
endif
|
||||
|
@ -44,7 +44,8 @@ extern "C" {
|
||||
#endif
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLL_N
|
||||
#if IS_USED(MODULE_PERIPH_USBDEV_CLK) && defined(CPU_LINE_STM32F411xE)
|
||||
#if (IS_USED(MODULE_PERIPH_USBDEV_CLK) || IS_USED(MODULE_PERIPH_SDMMC_CLK)) && \
|
||||
defined(CPU_LINE_STM32F411xE)
|
||||
#if IS_ACTIVE(CONFIG_BOARD_HAS_HSE) && (CONFIG_CLOCK_HSE == MHZ(8))
|
||||
#define CONFIG_CLOCK_PLL_N (96)
|
||||
#elif IS_ACTIVE(CONFIG_BOARD_HAS_HSE) && (CONFIG_CLOCK_HSE == MHZ(25))
|
||||
@ -60,7 +61,7 @@ extern "C" {
|
||||
#else
|
||||
#define CONFIG_CLOCK_PLL_N (50)
|
||||
#endif
|
||||
#endif /* MODULE_PERIPH_USBDEV_CLK */
|
||||
#endif /* MODULE_PERIPH_USBDEV_CLK || MODULE_PERIPH_SDMMC_CLK */
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLL_P
|
||||
#define CONFIG_CLOCK_PLL_P (2)
|
||||
|
@ -46,7 +46,7 @@ extern "C" {
|
||||
#endif
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLL_N
|
||||
#if IS_USED(MODULE_PERIPH_USBDEV_CLK) && \
|
||||
#if (IS_USED(MODULE_PERIPH_USBDEV_CLK) || IS_USED(MODULE_PERIPH_SDMMC_CLK)) && \
|
||||
(defined(CPU_LINE_STM32F405xx) || defined(CPU_LINE_STM32F407xx) || \
|
||||
defined(CPU_LINE_STM32F415xx) || defined(CPU_LINE_STM32F417xx) || \
|
||||
defined(CPU_LINE_STM32F427xx) || defined(CPU_LINE_STM32F429xx) || \
|
||||
@ -68,13 +68,13 @@ extern "C" {
|
||||
#else
|
||||
#define CONFIG_CLOCK_PLL_N (90)
|
||||
#endif
|
||||
#endif /* MODULE_PERIPH_USBDEV_CLK */
|
||||
#endif /* MODULE_PERIPH_USBDEV_CLK || MODULE_PERIPH_SDMMC_CLK */
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLL_P
|
||||
#define CONFIG_CLOCK_PLL_P (2)
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLL_Q
|
||||
#if IS_USED(MODULE_PERIPH_USBDEV_CLK) && \
|
||||
#if (IS_USED(MODULE_PERIPH_USBDEV_CLK) || IS_USED(MODULE_PERIPH_SDMMC_CLK)) && \
|
||||
(defined(CPU_LINE_STM32F405xx) || defined(CPU_LINE_STM32F407xx) || \
|
||||
defined(CPU_LINE_STM32F415xx) || defined(CPU_LINE_STM32F417xx) || \
|
||||
defined(CPU_LINE_STM32F427xx) || defined(CPU_LINE_STM32F429xx) || \
|
||||
|
@ -69,6 +69,26 @@ typedef enum {
|
||||
DMA_MEM_TO_MEM = 2, /**< Memory to memory */
|
||||
} dma_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Burst Transfer modes for F2/F4/F7
|
||||
*/
|
||||
typedef enum {
|
||||
DMA_BURST_SINGLE = 0, /**< single transfer */
|
||||
DMA_BURST_INCR4 = 1, /**< incremental burst of 4 beats */
|
||||
DMA_BURST_INCR8 = 2, /**< incremental burst of 8 beats */
|
||||
DMA_BURST_INCR16 = 3, /**< incremental burst of 16 beats */
|
||||
} dma_burst_t;
|
||||
|
||||
/**
|
||||
* @brief Threshold selection in FIFO mode for F2/F4F7
|
||||
*/
|
||||
typedef enum {
|
||||
DMA_FIFO_FULL_1_4 = 0, /**< 1/4 full FIFO */
|
||||
DMA_FIFO_FULL_1_2 = 1, /**< 1/2 full FIFO */
|
||||
DMA_FIFO_FULL_3_4 = 2, /**< 3/4 full FIFO */
|
||||
DMA_FIFO_FULL = 3, /**< Full FIFO */
|
||||
} dma_fifo_thresh_t;
|
||||
|
||||
/**
|
||||
* @brief DMA channel/trigger configuration for DMA peripherals without
|
||||
* channel/trigger filtering such as the stm32f1 and stm32f3.
|
||||
@ -211,6 +231,29 @@ int dma_configure(dma_t dma, int chan, const volatile void *src, volatile void *
|
||||
void dma_setup(dma_t dma, int chan, void *periph_addr, dma_mode_t mode,
|
||||
uint8_t width, bool inc_periph);
|
||||
|
||||
/**
|
||||
* @brief Low level extended initial DMA stream configuration for F2/F4/F7
|
||||
*
|
||||
* The function configures additional features of the DMA stream for F2/F4/F7.
|
||||
* It is supposed to be used after @ref dma_setup and before @ref dma_prepare.
|
||||
*
|
||||
* @note This function is only implemented for F2/F4/F7. For other families
|
||||
* it is only a dummy. It is not used by @ref dma_configure or the
|
||||
* convenience function @ref dma_transfer.
|
||||
*
|
||||
* @warn The combination of FIFO threshold and the memory burst transfer
|
||||
* has to be valid.
|
||||
*
|
||||
* @param[in] dma Logical DMA stream
|
||||
* @param[in] pburst Peripeheral burst transfer configuration
|
||||
* @param[in] mburst Memory burst transfer configuration
|
||||
* @param[in] fifo FIFO mode enable
|
||||
* @param[in] thresh FIFO threshold
|
||||
* @param[in] pfctrl Peripheral used as flow controller
|
||||
*/
|
||||
void dma_setup_ext(dma_t dma, dma_burst_t pburst, dma_burst_t mburst,
|
||||
bool fifo, dma_fifo_thresh_t thresh, bool pfctrl);
|
||||
|
||||
/**
|
||||
* @brief Low level DMA transfer configuration
|
||||
*
|
||||
|
100
cpu/stm32/include/periph/cpu_sdmmc.h
Normal file
100
cpu/stm32/include/periph/cpu_sdmmc.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief CPU specific definitions for SDIO/SDMMC for the STM32 family
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*/
|
||||
|
||||
#ifndef PERIPH_CPU_SDMMC_H
|
||||
#define PERIPH_CPU_SDMMC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "periph/cpu_dma.h"
|
||||
#include "periph/cpu_gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Declare the types for SDIO/SDMMC only if the STM32 has SDIO/SDMMC peripheral */
|
||||
#if defined(SDMMC_POWER_PWRCTRL) || defined(SDIO_POWER_PWRCTRL)
|
||||
|
||||
/* For F1, F2, F4 and L1 the SDMMC interface is called SDIO, define used
|
||||
* symbols for source code compatibility */
|
||||
#if !defined(SDMMC1) && !DOXYGEN
|
||||
#define SDMMC_TypeDef SDIO_TypeDef
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SDIO/SDMMC buffers alignment because of STM32 DMA/FIFO restrictions
|
||||
*/
|
||||
#define SDMMC_CPU_DMA_ALIGNMENT 4
|
||||
|
||||
/**
|
||||
* @brief SDIO/SDMMC buffer instantiation requirement for STM32
|
||||
*/
|
||||
#define SDMMC_CPU_DMA_REQUIREMENTS __attribute__((aligned(SDMMC_CPU_DMA_ALIGNMENT)))
|
||||
|
||||
/**
|
||||
* @brief SDIO/SDMMC pin structure for STM32
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_t pin; /**< GPIO pin */
|
||||
#ifndef CPU_FAM_STM32F1
|
||||
gpio_af_t af; /**< GPIO alternate function */
|
||||
#endif
|
||||
} sdmmc_pin_t;
|
||||
|
||||
/**
|
||||
* @brief SDIO/SDMCC peripheral configuration for STM32
|
||||
*
|
||||
* To use 1-bit bus width, define `dat1` to `dat3` as `GPIO_UNDEF`. 8-bit bus
|
||||
* width with `dat4` to `dat7` is only available if the board provides
|
||||
* feature `periph_sdmmc_8bit`.
|
||||
*/
|
||||
typedef struct {
|
||||
SDMMC_TypeDef *dev; /**< SDIO/SDMMC device */
|
||||
uint8_t bus; /**< APB/AHB bus used for SDIO/SDMMC peripheral */
|
||||
uint32_t rcc_mask; /**< Bit mask in clock enable register */
|
||||
gpio_t cd; /**< Card Detect pin (GPIO_UNDEF if not used) */
|
||||
int cd_active; /**< Card Detect pin active level */
|
||||
gpio_mode_t cd_mode; /**< Card Detect pin mode */
|
||||
sdmmc_pin_t clk; /**< Clock pin */
|
||||
sdmmc_pin_t cmd; /**< Command/Response pin */
|
||||
sdmmc_pin_t dat0; /**< Data Line Bit 0 pin */
|
||||
sdmmc_pin_t dat1; /**< Data Line Bit 1 pin */
|
||||
sdmmc_pin_t dat2; /**< Data Line Bit 2 pin */
|
||||
sdmmc_pin_t dat3; /**< Data Line Bit 3 pin */
|
||||
#if IS_USED(MODULE_PERIPH_SDMMC_8BIT)
|
||||
sdmmc_pin_t dat4; /**< Data Line Bit 4 pin */
|
||||
sdmmc_pin_t dat5; /**< Data Line Bit 5 pin */
|
||||
sdmmc_pin_t dat6; /**< Data Line Bit 6 pin */
|
||||
sdmmc_pin_t dat7; /**< Data Line Bit 7 pin */
|
||||
#endif
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
dma_t dma; /**< Logical DMA stream used for SDIO/SDMMC */
|
||||
uint8_t dma_chan; /**< DMA channel used for SDIO/SDMMC */
|
||||
#endif
|
||||
uint8_t irqn; /**< SDIO/SDMMC interrupt number */
|
||||
} sdmmc_conf_t;
|
||||
|
||||
#endif /* defined(SDMMC_POWER_PWRCTRL) || defined(SDIO_POWER_PWRCTRL) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PERIPH_CPU_SDMMC_H */
|
||||
/** @} */
|
@ -70,6 +70,7 @@
|
||||
#include "periph/cpu_pm.h"
|
||||
#include "periph/cpu_pwm.h"
|
||||
#include "periph/cpu_qdec.h"
|
||||
#include "periph/cpu_sdmmc.h"
|
||||
#include "periph/cpu_spi.h"
|
||||
#include "periph/cpu_timer.h"
|
||||
#include "periph/cpu_uart.h"
|
||||
|
@ -19,6 +19,10 @@ config CPU_FAM_F2
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW
|
||||
select HAS_PERIPH_HWRNG
|
||||
select HAS_PERIPH_RTC_MEM
|
||||
select HAS_PERIPH_SDMMC_AUTO_CLK
|
||||
select HAS_PERIPH_SDMMC_CLK
|
||||
select HAS_PERIPH_SDMMC_HS
|
||||
select HAS_PERIPH_SDMMC_MMC
|
||||
select HAS_PERIPH_VBAT
|
||||
select HAS_PERIPH_WDT
|
||||
select HAS_BOOTLOADER_STM32
|
||||
|
@ -17,6 +17,10 @@ config CPU_FAM_F4
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW
|
||||
select HAS_PERIPH_RTC_MEM
|
||||
select HAS_PERIPH_SDMMC_AUTO_CLK
|
||||
select HAS_PERIPH_SDMMC_CLK
|
||||
select HAS_PERIPH_SDMMC_HS
|
||||
select HAS_PERIPH_SDMMC_MMC
|
||||
select HAS_PERIPH_VBAT
|
||||
select HAS_PERIPH_WDT
|
||||
select HAS_BOOTLOADER_STM32
|
||||
|
@ -19,6 +19,10 @@ config CPU_FAM_F7
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW
|
||||
select HAS_PERIPH_HWRNG
|
||||
select HAS_PERIPH_RTC_MEM
|
||||
select HAS_PERIPH_SDMMC_AUTO_CLK
|
||||
select HAS_PERIPH_SDMMC_CLK
|
||||
select HAS_PERIPH_SDMMC_HS
|
||||
select HAS_PERIPH_SDMMC_MMC
|
||||
select HAS_PERIPH_VBAT
|
||||
select HAS_PERIPH_WDT
|
||||
select HAS_BOOTLOADER_STM32
|
||||
|
@ -20,6 +20,10 @@ config CPU_FAM_L4
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW
|
||||
select HAS_PERIPH_HWRNG
|
||||
select HAS_PERIPH_RTC_MEM
|
||||
select HAS_PERIPH_SDMMC_AUTO_CLK
|
||||
select HAS_PERIPH_SDMMC_CLK
|
||||
select HAS_PERIPH_SDMMC_HS
|
||||
select HAS_PERIPH_SDMMC_MMC
|
||||
select HAS_PERIPH_VBAT
|
||||
select HAS_PERIPH_WDT
|
||||
select HAS_BOOTLOADER_STM32
|
||||
|
@ -34,6 +34,9 @@ config MODULE_PERIPH
|
||||
select MODULE_USBDEV_SYNOPSYS_DWC2 if MODULE_PERIPH_USBDEV && CPU_LINE_STM32L4S5XX
|
||||
select MODULE_USBDEV_SYNOPSYS_DWC2 if MODULE_PERIPH_USBDEV && CPU_LINE_STM32L4S7XX
|
||||
select MODULE_USBDEV_SYNOPSYS_DWC2 if MODULE_PERIPH_USBDEV && CPU_LINE_STM32L4S9XX
|
||||
# Special SDMMC dependencies
|
||||
select MODULE_PERIPH_GPIO_IRQ if MODULE_PERIPH_SDMMC
|
||||
select MODULE_PERIPH_DMA if MODULE_PERIPH_SDMMC && HAS_PERIPH_DMA
|
||||
help
|
||||
stm32 common peripheral code.
|
||||
|
||||
@ -64,3 +67,13 @@ config MODULE_PERIPH_LTDC
|
||||
depends on HAS_PERIPH_LTDC
|
||||
help
|
||||
STM32 LCD-TFT Display controller
|
||||
|
||||
config MODULE_PERIPH_SDMMC_CLK
|
||||
bool
|
||||
depends on HAS_PERIPH_SDMMC_CLK
|
||||
default y if MODULE_PERIPH_SDMMC
|
||||
|
||||
config MODULE_PERIPH_INIT_SDMMC_CLK
|
||||
bool
|
||||
depends on MODULE_PERIPH_SDMMC_CLK
|
||||
default y if MODULE_PERIPH_INIT
|
||||
|
@ -416,6 +416,78 @@ void dma_prepare(dma_t dma, void *mem, size_t len, bool incr_mem)
|
||||
dma_ctx[dma].len = len;
|
||||
}
|
||||
|
||||
void dma_setup_ext(dma_t dma, dma_burst_t pburst, dma_burst_t mburst,
|
||||
bool fifo, dma_fifo_thresh_t thresh, bool pfctrl)
|
||||
{
|
||||
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
||||
STM32_DMA_Stream_Type *stream = dma_ctx[dma].stream;
|
||||
|
||||
/* configuraition can be done only if DMA stream is disabled */
|
||||
assert((stream->CR & DMA_EN) == 0);
|
||||
|
||||
/* FIFO configuration if enabled */
|
||||
if (fifo) {
|
||||
uint8_t width = (stream->CR & DMA_SxCR_MSIZE_Msk) >> DMA_SxCR_MSIZE_Pos;
|
||||
|
||||
/* check valid combinations of MSIZE, MBURST and FIFO threshold level */
|
||||
switch (width) {
|
||||
case DMA_DATA_WIDTH_BYTE:
|
||||
switch (thresh) {
|
||||
case DMA_FIFO_FULL_1_4:
|
||||
/* fall through */
|
||||
case DMA_FIFO_FULL_3_4:
|
||||
assert(mburst == DMA_BURST_INCR4);
|
||||
break;
|
||||
case DMA_FIFO_FULL_1_2:
|
||||
assert((mburst == DMA_BURST_INCR4) || (mburst == DMA_BURST_INCR8));
|
||||
break;
|
||||
case DMA_FIFO_FULL: /* all mburst values are valid */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case DMA_DATA_WIDTH_HALF_WORD:
|
||||
switch (thresh) {
|
||||
case DMA_FIFO_FULL_1_2:
|
||||
assert(mburst == DMA_BURST_INCR4);
|
||||
break;
|
||||
case DMA_FIFO_FULL:
|
||||
assert((mburst == DMA_BURST_INCR4) || (mburst == DMA_BURST_INCR8));
|
||||
break;
|
||||
default:
|
||||
assert(false); /* all other combinations are invalid) */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case DMA_DATA_WIDTH_WORD:
|
||||
assert((thresh == DMA_FIFO_FULL) && (mburst == DMA_BURST_INCR4));
|
||||
break;
|
||||
}
|
||||
|
||||
stream->FCR = (fifo << DMA_SxFCR_DMDIS_Pos) |
|
||||
(thresh << DMA_SxFCR_FTH_Pos);
|
||||
}
|
||||
else {
|
||||
stream->FCR = 0;
|
||||
}
|
||||
|
||||
stream->CR &= ~(DMA_SxCR_PFCTRL | DMA_SxCR_MBURST | DMA_SxCR_PBURST);
|
||||
stream->CR |= pfctrl ? DMA_SxCR_PFCTRL : 0;
|
||||
stream->CR |= (mburst << DMA_SxCR_MBURST_Pos);
|
||||
stream->CR |= (pburst << DMA_SxCR_PBURST_Pos);
|
||||
|
||||
#else
|
||||
(void)dma;
|
||||
(void)pburst;
|
||||
(void)pburst;
|
||||
(void)mburst;
|
||||
(void)fifo;
|
||||
(void)thresh;
|
||||
(void)pfctrl;
|
||||
#endif
|
||||
}
|
||||
|
||||
int dma_configure(dma_t dma, int chan, const volatile void *src, volatile void *dst, size_t len,
|
||||
dma_mode_t mode, uint8_t flags)
|
||||
{
|
||||
|
858
cpu/stm32/periph/sdmmc.c
Normal file
858
cpu/stm32/periph/sdmmc.c
Normal file
@ -0,0 +1,858 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level SDIO/SD/MMC peripheral driver interface for STM32
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "bitarithm.h"
|
||||
#include "container.h"
|
||||
#include "log.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#include "sdmmc/sdmmc.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
/* millisecond timer definitions dependent on active ztimer backend */
|
||||
#if IS_USED(MODULE_ZTIMER_MSEC)
|
||||
#define _ZTIMER_SLEEP_MS(n) ztimer_sleep(ZTIMER_MSEC, n)
|
||||
#elif IS_USED(MODULE_ZTIMER_USEC)
|
||||
#define _ZTIMER_SLEEP_MS(n) ztimer_sleep(ZTIMER_USEC, n * US_PER_MS)
|
||||
#else
|
||||
#error "Either ztimer_msec or ztimer_usec is needed"
|
||||
#endif
|
||||
|
||||
#define SDMMC_IRQ_PRIO (1)
|
||||
|
||||
/* command related static interrupts (same as MASK and ICR bits) */
|
||||
#define SDMMC_INT_STATIC_CMD (SDMMC_STA_CMDSENT | SDMMC_STA_CMDREND | \
|
||||
SDMMC_STA_CCRCFAIL | SDMMC_STA_CTIMEOUT)
|
||||
|
||||
/* data transfer related static interrupts (same as MASK and ICR bits) */
|
||||
#define SDMMC_INT_STATIC_DATA (SDMMC_STA_DCRCFAIL | SDMMC_STA_DTIMEOUT | \
|
||||
SDMMC_STA_DATAEND | SDMMC_STA_DBCKEND | \
|
||||
SDMMC_STA_TXUNDERR | SDMMC_STA_RXOVERR | \
|
||||
SDMMC_STA_SDIOIT | SDMMC_STA_STBITERR)
|
||||
|
||||
/* all static interrupts */
|
||||
#define SDMMC_INT_STATIC (SDMMC_INT_STATIC_CMD | SDMMC_INT_STATIC_DATA)
|
||||
|
||||
/* all static error interrupts (same as MASK and ICR bits) */
|
||||
#define SDMMC_INT_STATIC_ERROR (SDMMC_STA_CCRCFAIL | SDMMC_STA_CTIMEOUT \
|
||||
SDMMC_STA_DCRCFAIL | SDMMC_STA_DTIMEOUT | \
|
||||
SDMMC_STA_TXUNDERR | SDMMC_STA_RXOVERR)
|
||||
|
||||
#define SDMMC_CMD_WAITRESP_SHORT (SDMMC_CMD_WAITRESP_0)
|
||||
#define SDMMC_CMD_WAITRESP_LONG (SDMMC_CMD_WAITRESP_1 | SDMMC_CMD_WAITRESP_0)
|
||||
|
||||
#define SDMMC_CLKCR_CLKDIV_400KHZ (MHZ(48) / KHZ(400) - 2) /* SD clock 400 kHz */
|
||||
#define SDMMC_CLKCR_CLKDIV_1MHZ (MHZ(48) / MHZ(1) - 2) /* SD clock 4 MHz (debug) */
|
||||
#define SDMMC_CLKCR_CLKDIV_4MHZ (MHZ(48) / MHZ(4) - 2) /* SD clock 4 MHz (debug) */
|
||||
#define SDMMC_CLKCR_CLKDIV_8MHZ (MHZ(48) / MHZ(8) - 2) /* SD clock 8 MHz (debug) */
|
||||
#define SDMMC_CLKCR_CLKDIV_16MHZ (MHZ(48) / MHZ(16) - 2) /* SD clock 16 MHz (debug) */
|
||||
#define SDMMC_CLKCR_CLKDIV_20MHZ (MHZ(48) / MHZ(20) - 2) /* SD clock 20 MHz (MMC) */
|
||||
#define SDMMC_CLKCR_CLKDIV_25MHZ (MHZ(48) / MHZ(24) - 2) /* SD clock 24 MHz (max) */
|
||||
|
||||
/* limit the Default and High Speed clock rates for debugging */
|
||||
#if CONFIG_SDMMC_CLK_MAX_400KHZ
|
||||
#define CONFIG_SDMMC_CLK_MAX SDMMC_CLKCR_CLKDIV_400KHZ
|
||||
#elif CONFIG_SDMMC_CLK_MAX_1MHZ
|
||||
#define CONFIG_SDMMC_CLK_MAX SDMMC_CLKCR_CLKDIV_1MHZ
|
||||
#elif CONFIG_SDMMC_CLK_MAX_4MHZ
|
||||
#define CONFIG_SDMMC_CLK_MAX SDMMC_CLKCR_CLKDIV_4MHZ
|
||||
#elif CONFIG_SDMMC_CLK_MAX_8MHZ
|
||||
#define CONFIG_SDMMC_CLK_MAX SDMMC_CLKCR_CLKDIV_8MHZ
|
||||
#elif CONFIG_SDMMC_CLK_MAX_16MHZ
|
||||
#define CONFIG_SDMMC_CLK_MAX SDMMC_CLKCR_CLKDIV_16MHZ
|
||||
#endif
|
||||
|
||||
/* for F1, F2, F4 and L1 the SDMMC interface is called SDIO, defined the
|
||||
* corresponding symbols for compatibility */
|
||||
#if !defined(SDMMC1)
|
||||
|
||||
#define SDMMC1_IRQn SDIO_IRQn
|
||||
|
||||
#define SDMMC_POWER_PWRCTRL_Pos SDIO_POWER_PWRCTRL_Pos
|
||||
|
||||
#define SDMMC_CMD_CMDINDEX_Pos SDIO_CMD_CMDINDEX_Pos
|
||||
#define SDMMC_CMD_CMDINDEX SDIO_CMD_CMDINDEX
|
||||
#define SDMMC_CMD_WAITRESP_Pos SDIO_CMD_WAITRESP_Pos
|
||||
#define SDMMC_CMD_WAITRESP_0 SDIO_CMD_WAITRESP_0
|
||||
#define SDMMC_CMD_WAITRESP_1 SDIO_CMD_WAITRESP_1
|
||||
#define SDMMC_CMD_CPSMEN SDIO_CMD_CPSMEN
|
||||
|
||||
#define SDMMC_DCTRL_DBLOCKSIZE_Pos SDIO_DCTRL_DBLOCKSIZE_Pos
|
||||
#define SDMMC_DCTRL_DTDIR SDIO_DCTRL_DTDIR
|
||||
#define SDMMC_DCTRL_DTEN SDIO_DCTRL_DTEN
|
||||
#define SDMMC_DCTRL_DMAEN SDIO_DCTRL_DMAEN
|
||||
|
||||
#define SDMMC_STA_SDIOIT SDIO_STA_SDIOIT
|
||||
#define SDMMC_STA_CMDSENT SDIO_STA_CMDSENT
|
||||
#define SDMMC_STA_CMDREND SDIO_STA_CMDREND
|
||||
#define SDMMC_STA_CCRCFAIL SDIO_STA_CCRCFAIL
|
||||
#define SDMMC_STA_CTIMEOUT SDIO_STA_CTIMEOUT
|
||||
#define SDMMC_STA_DCRCFAIL SDIO_STA_DCRCFAIL
|
||||
#define SDMMC_STA_DTIMEOUT SDIO_STA_DTIMEOUT
|
||||
#define SDMMC_STA_DATAEND SDIO_STA_DATAEND
|
||||
#define SDMMC_STA_DBCKEND SDIO_STA_DBCKEND
|
||||
#define SDMMC_STA_TXUNDERR SDIO_STA_TXUNDERR
|
||||
#define SDMMC_STA_TXFIFOE SDIO_STA_TXFIFOE
|
||||
#define SDMMC_STA_TXFIFOF SDIO_STA_TXFIFOF
|
||||
#define SDMMC_STA_TXFIFOHE SDIO_STA_TXFIFOHE
|
||||
#define SDMMC_STA_TXACT SDIO_STA_TXACT
|
||||
#define SDMMC_STA_RXOVERR SDIO_STA_RXOVERR
|
||||
#define SDMMC_STA_RXFIFOE SDIO_STA_TXFIFOE
|
||||
#define SDMMC_STA_RXFIFOF SDIO_STA_TXFIFOF
|
||||
#define SDMMC_STA_RXFIFOHF SDIO_STA_RXFIFOHF
|
||||
#define SDMMC_STA_RXDAVL SDIO_STA_RXDAVL
|
||||
#define SDMMC_STA_RXACT SDIO_STA_RXACT
|
||||
|
||||
#ifdef SDIO_STA_STBITERR
|
||||
#define SDMMC_STA_STBITERR SDIO_STA_STBITERR
|
||||
#endif
|
||||
#ifdef SDIO_MASK_STBITERRIE
|
||||
#define SDMMC_MASK_STBITERRIE SDIO_MASK_STBITERRIE
|
||||
#endif
|
||||
|
||||
#define SDMMC_MASK_CMDSENTIE SDIO_MASK_CMDSENTIE
|
||||
#define SDMMC_MASK_CMDRENDIE SDIO_MASK_CMDRENDIE
|
||||
#define SDMMC_MASK_CCRCFAILIE SDIO_MASK_CCRCFAILIE
|
||||
#define SDMMC_MASK_CTIMEOUTIE SDIO_MASK_CTIMEOUTIE
|
||||
#define SDMMC_MASK_DCRCFAILIE SDIO_MASK_DCRCFAILIE
|
||||
#define SDMMC_MASK_DTIMEOUTIE SDIO_MASK_DTIMEOUTIE
|
||||
#define SDMMC_MASK_DATAENDIE SDIO_MASK_DATAENDIE
|
||||
#define SDMMC_MASK_DBCKENDIE SDIO_MASK_DBCKENDIE
|
||||
#define SDMMC_MASK_TXUNDERRIE SDIO_MASK_TXUNDERRIE
|
||||
#define SDMMC_MASK_TXFIFOEIE SDIO_MASK_TXFIFOEIE
|
||||
#define SDMMC_MASK_TXFIFOFIE SDIO_MASK_TXFIFOFIE
|
||||
#define SDMMC_MASK_TXFIFOHEIE SDIO_MASK_TXFIFOHEIE
|
||||
#define SDMMC_MASK_RXOVERRIE SDIO_MASK_RXOVERRIE
|
||||
#define SDMMC_MASK_RXFIFOHFIE SDIO_MASK_RXFIFOHFIE
|
||||
|
||||
#define SDMMC_ICR_CCRCFAILC SDIO_ICR_CCRCFAILC
|
||||
#define SDMMC_ICR_DCRCFAILC SDIO_ICR_DCRCFAILC
|
||||
#define SDMMC_ICR_CTIMEOUTC SDIO_ICR_CTIMEOUTC
|
||||
#define SDMMC_ICR_DTIMEOUTC SDIO_ICR_DTIMEOUTC
|
||||
#define SDMMC_ICR_TXUNDERRC SDIO_ICR_TXUNDERRC
|
||||
#define SDMMC_ICR_RXOVERRC SDIO_ICR_RXOVERRC
|
||||
#define SDMMC_ICR_CMDRENDC SDIO_ICR_CMDRENDC
|
||||
#define SDMMC_ICR_CMDSENTC SDIO_ICR_CMDSENTC
|
||||
#define SDMMC_ICR_DATAENDC SDIO_ICR_DATAENDC
|
||||
#define SDMMC_ICR_DBCKENDC SDIO_ICR_DBCKENDC
|
||||
#define SDMMC_ICR_SDIOITC SDIO_ICR_SDIOITC
|
||||
|
||||
#define SDMMC_CLKCR_NEGEDGE SDIO_CLKCR_NEGEDGE
|
||||
#define SDMMC_CLKCR_CLKEN SDIO_CLKCR_CLKEN
|
||||
#define SDMMC_CLKCR_PWRSAV SDIO_CLKCR_PWRSAV
|
||||
#define SDMMC_CLKCR_BYPASS_Msk SDIO_CLKCR_BYPASS_Msk
|
||||
#define SDMMC_CLKCR_CLKDIV_Msk SDIO_CLKCR_CLKDIV_Msk
|
||||
#define SDMMC_CLKCR_WIDBUS_Msk SDIO_CLKCR_WIDBUS_Msk
|
||||
#define SDMMC_CLKCR_WIDBUS_Pos SDIO_CLKCR_WIDBUS_Pos
|
||||
|
||||
#define isr_sdmmc1 isr_sdio
|
||||
|
||||
#endif /* !defined(SDMMC1) */
|
||||
|
||||
#ifndef SDMMC_STA_STBITERR
|
||||
#define SDMMC_STA_STBITERR (1UL << 9)
|
||||
#endif
|
||||
|
||||
#define SDMMC_CLKCR_WIDBUS_1BIT (0UL << SDMMC_CLKCR_WIDBUS_Pos)
|
||||
#define SDMMC_CLKCR_WIDBUS_4BIT (1UL << SDMMC_CLKCR_WIDBUS_Pos)
|
||||
#define SDMMC_CLKCR_WIDBUS_8BIT (2UL << SDMMC_CLKCR_WIDBUS_Pos)
|
||||
|
||||
/* Physical Layer Simplified Specification Version 9.00,
|
||||
* 4.6.2 Read, Write and Erase Timeout Conditions
|
||||
* Maximum values are used here */
|
||||
#define SDMMC_DATA_R_TIMEOUT (MHZ(24) / 10) /**< Data read timeout 100ms */
|
||||
#define SDMMC_DATA_W_TIMEOUT (MHZ(24) / 4) /**< Date write timeout 250ms */
|
||||
|
||||
enum {
|
||||
SDMMC_POWER_PWRCTRL_OFF = 0b00,
|
||||
SDMMC_POWER_PWRCTRL_ON = 0b11,
|
||||
};
|
||||
|
||||
#include "board.h"
|
||||
|
||||
/* forward declaration of _driver */
|
||||
static const sdmmc_driver_t _driver;
|
||||
|
||||
typedef struct {
|
||||
sdmmc_dev_t sdmmc_dev; /**< Inherited sdmmc_dev_t struct */
|
||||
const sdmmc_conf_t *config; /**< SDIO/SD/MMC peripheral config */
|
||||
uint32_t irq_status; /**< Interrupt status */
|
||||
mutex_t irq_wait; /**< Wait for an enabled interrupt */
|
||||
} stm32_sdmmc_dev_t;
|
||||
|
||||
/* driver related */
|
||||
static stm32_sdmmc_dev_t _sdmmc_devs[] = {
|
||||
{
|
||||
.sdmmc_dev = {
|
||||
.driver = &_driver,
|
||||
},
|
||||
.config = &sdmmc_config[0],
|
||||
.irq_wait = MUTEX_INIT_LOCKED,
|
||||
},
|
||||
#if SDMMC_CONFIG_NUMOF > 1
|
||||
{
|
||||
.sdmmc_dev = {
|
||||
.driver = &_driver,
|
||||
},
|
||||
.config = &sdmmc_config[1],
|
||||
.irq_wait = MUTEX_INIT_LOCKED,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/* sanity check of configuration */
|
||||
static_assert(SDMMC_CONFIG_NUMOF == ARRAY_SIZE(sdmmc_config),
|
||||
"SDMMC_CONFIG_NUMOF and the number of elements in sdmmc_config differ");
|
||||
static_assert(SDMMC_CONFIG_NUMOF == ARRAY_SIZE(_sdmmc_devs),
|
||||
"SDMMC_CONFIG_NUMOF and the number of elements in sdmmc_devs differ");
|
||||
|
||||
/* check that the number of devices does not exhaust the number of available devices */
|
||||
#if defined(SDMMC2)
|
||||
static_assert(SDMMC_CONFIG_NUMOF < 3, "MCU supports only 2 SDIO/SD/MMC interfaces");
|
||||
#else
|
||||
static_assert(SDMMC_CONFIG_NUMOF < 2, "MCU supports only 1 SDIO/SD/MMC interface");
|
||||
#endif
|
||||
|
||||
XFA_CONST(sdmmc_devs, 0) sdmmc_dev_t * const _sdmmc_1 = (sdmmc_dev_t * const)&_sdmmc_devs[0];
|
||||
#if SDMMC_CONFIG_NUMOF > 1
|
||||
XFA_CONST(sdmmc_devs, 0) sdmmc_dev_t * const _sdmmc_2 = (sdmmc_dev_t * const)&_sdmmc_devs[1];
|
||||
#endif
|
||||
|
||||
static inline bool _use_dma(const sdmmc_conf_t *conf)
|
||||
{
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
/* TODO SDMMC_IDMA_IDMAEN */
|
||||
# ifdef SDMMC_IDMA_IDMAEN
|
||||
/* The SDMMC of the L4+ and some L5 MCUs have an internal DMA controller
|
||||
* (IDMA) instead of using the DMA periphery. This internal DMA requires
|
||||
* separate handling. */
|
||||
return false;
|
||||
# else
|
||||
return (conf->dma != DMA_STREAM_UNDEF);
|
||||
# endif
|
||||
#else /* IS_USED(MODULE_PERIPH_DMA) */
|
||||
(void)conf;
|
||||
return false;
|
||||
#endif /* IS_USED(MODULE_PERIPH_DMA) */
|
||||
}
|
||||
|
||||
static void _isr(SDMMC_TypeDef *dev);
|
||||
static void _isr_cd_pin(void *arg);
|
||||
|
||||
static void _config_sdmmc(sdmmc_dev_t *sdmmc_dev)
|
||||
{
|
||||
stm32_sdmmc_dev_t *dev = container_of(sdmmc_dev, stm32_sdmmc_dev_t, sdmmc_dev);
|
||||
const sdmmc_conf_t *conf = dev->config;
|
||||
|
||||
#if !defined(CPU_FAM_STM32F1)
|
||||
/* reset SDIO/SD/MMC */
|
||||
/* TODO: reset for STM32F1, STM32F1 is using AHB */
|
||||
|
||||
/* APB2 or AHB2 is used for SDIO/SD/MMC peripheral, bit mask for the reset
|
||||
* register is always the same as for the enable register */
|
||||
if (conf->bus == APB2) {
|
||||
RCC->APB2RSTR |= conf->rcc_mask;
|
||||
RCC->APB2RSTR &= ~conf->rcc_mask;
|
||||
}
|
||||
else if (conf->bus == AHB2) {
|
||||
RCC->AHB2RSTR |= conf->rcc_mask;
|
||||
RCC->AHB2RSTR &= ~conf->rcc_mask;
|
||||
}
|
||||
else {
|
||||
/* should not happen */
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
/* power on SDIO/SD/MMC interface */
|
||||
conf->dev->POWER = (SDMMC_POWER_PWRCTRL_ON << SDMMC_POWER_PWRCTRL_Pos);
|
||||
conf->dev->CLKCR = SDMMC_CLKCR_WIDBUS_1BIT | SDMMC_CLKCR_CLKDIV_400KHZ |
|
||||
SDMMC_CLKCR_CLKEN | SDMMC_CLKCR_PWRSAV;
|
||||
}
|
||||
|
||||
static void _init_pins(sdmmc_dev_t *sdmmc_dev)
|
||||
{
|
||||
stm32_sdmmc_dev_t *dev = container_of(sdmmc_dev, stm32_sdmmc_dev_t, sdmmc_dev);
|
||||
const sdmmc_conf_t *conf = dev->config;
|
||||
|
||||
/* configure GPIOs of the SDIO/SD/MMC interface, at minimum 1 data line */
|
||||
assert(gpio_is_valid(conf->clk.pin));
|
||||
assert(gpio_is_valid(conf->cmd.pin));
|
||||
assert(gpio_is_valid(conf->dat0.pin));
|
||||
|
||||
sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_1BIT;
|
||||
gpio_init(conf->clk.pin, GPIO_OUT);
|
||||
gpio_init(conf->cmd.pin, GPIO_OUT);
|
||||
gpio_init(conf->dat0.pin, GPIO_OUT);
|
||||
|
||||
if (gpio_is_valid(conf->dat1.pin) && gpio_is_valid(conf->dat2.pin) &&
|
||||
gpio_is_valid(conf->dat3.pin)) {
|
||||
sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_4BIT;
|
||||
gpio_init(conf->dat1.pin, GPIO_OUT);
|
||||
gpio_init(conf->dat2.pin, GPIO_OUT);
|
||||
gpio_init(conf->dat3.pin, GPIO_OUT);
|
||||
}
|
||||
|
||||
#if !defined(CPU_FAM_STM32F1)
|
||||
gpio_init_af(conf->cmd.pin, conf->cmd.af);
|
||||
gpio_init_af(conf->clk.pin, conf->clk.af);
|
||||
gpio_init_af(conf->dat0.pin, conf->dat0.af);
|
||||
|
||||
if (sdmmc_dev->bus_width == SDMMC_BUS_WIDTH_4BIT) {
|
||||
gpio_init_af(conf->dat1.pin, conf->dat1.af);
|
||||
gpio_init_af(conf->dat2.pin, conf->dat2.af);
|
||||
gpio_init_af(conf->dat3.pin, conf->dat3.af);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IS_USED(MODULE_PERIPH_SDMMC_8BIT)
|
||||
if (gpio_is_valid(conf->dat4.pin) && gpio_is_valid(conf->dat5.pin) &&
|
||||
gpio_is_valid(conf->dat6.pin) && gpio_is_valid(conf->dat7.pin)) {
|
||||
sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_8BIT;
|
||||
gpio_init(conf->dat4.pin, GPIO_OUT);
|
||||
gpio_init(conf->dat5.pin, GPIO_OUT);
|
||||
gpio_init(conf->dat6.pin, GPIO_OUT);
|
||||
gpio_init(conf->dat7.pin, GPIO_OUT);
|
||||
#if !defined(CPU_FAM_STM32F1)
|
||||
gpio_init_af(conf->dat4.pin, conf->dat0.af);
|
||||
gpio_init_af(conf->dat5.pin, conf->dat1.af);
|
||||
gpio_init_af(conf->dat6.pin, conf->dat2.af);
|
||||
gpio_init_af(conf->dat7.pin, conf->dat3.af);
|
||||
#endif /* !defined(CPU_FAM_STM32F1) */
|
||||
}
|
||||
#endif /* IS_USED(MODULE_PERIPH_SDMMC_8BIT) */
|
||||
|
||||
if (gpio_is_valid(conf->cd)) {
|
||||
gpio_init_int(conf->cd, conf->cd_mode, GPIO_BOTH, _isr_cd_pin, sdmmc_dev);
|
||||
sdmmc_dev->present = gpio_read(conf->cd) == conf->cd_active;
|
||||
}
|
||||
else {
|
||||
sdmmc_dev->present = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void _init(sdmmc_dev_t *sdmmc_dev)
|
||||
{
|
||||
stm32_sdmmc_dev_t *dev = container_of(sdmmc_dev, stm32_sdmmc_dev_t, sdmmc_dev);
|
||||
assert(dev);
|
||||
|
||||
const sdmmc_conf_t *conf = dev->config;
|
||||
assert(conf);
|
||||
|
||||
/* initialize GPIOs */
|
||||
_init_pins(sdmmc_dev);
|
||||
|
||||
#if defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \
|
||||
defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L4)
|
||||
/* TODO: init_clock (use RCC_DCKCFGR2_SDMMCxSEL to select system clock)
|
||||
* PLL clock with 48 MHz is used by default, use module periph_usbdev_clk */
|
||||
#else
|
||||
#error "STM32 family not supported yet"
|
||||
#endif
|
||||
|
||||
/* enable peripheral clock */
|
||||
periph_clk_en(conf->bus, conf->rcc_mask);
|
||||
|
||||
/* reset and configure the peripheral */
|
||||
_config_sdmmc(sdmmc_dev);
|
||||
|
||||
#if defined(SDIO)
|
||||
assert (conf->dev == SDIO);
|
||||
NVIC_SetPriority(SDIO_IRQn, SDMMC_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(SDIO_IRQn);
|
||||
#else /* defined(SDIO) */
|
||||
#if defined(SDMMC1)
|
||||
if (conf->dev == SDMMC1) {
|
||||
NVIC_SetPriority(SDMMC1_IRQn, SDMMC_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(SDMMC1_IRQn);
|
||||
}
|
||||
#endif
|
||||
#if defined(SDMMC2)
|
||||
else if (conf->dev == SDMMC2) {
|
||||
NVIC_SetPriority(SDMMC2_IRQn, SDMMC_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(SDMMC2_IRQn);
|
||||
}
|
||||
#endif
|
||||
#endif /* defined(SDIO) */
|
||||
}
|
||||
|
||||
static int _send_cmd(sdmmc_dev_t *sdmmc_dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
|
||||
sdmmc_resp_t resp_type, uint32_t *resp)
|
||||
{
|
||||
/* to ensure that `sdmmc_send_acmd` is used for application specific commands */
|
||||
assert((cmd_idx & SDMMC_ACMD_PREFIX) == 0);
|
||||
|
||||
stm32_sdmmc_dev_t *dev = container_of(sdmmc_dev, stm32_sdmmc_dev_t, sdmmc_dev);
|
||||
assert(dev);
|
||||
|
||||
const sdmmc_conf_t *conf = dev->config;
|
||||
assert(conf);
|
||||
|
||||
SDMMC_TypeDef *sdmmc = conf->dev;
|
||||
|
||||
/* prepare the CMD register value, it has to be written as in one access */
|
||||
uint32_t cmd = (cmd_idx & SDMMC_CMD_CMDINDEX) << SDMMC_CMD_CMDINDEX_Pos;
|
||||
|
||||
switch (resp_type) {
|
||||
case SDMMC_NO_R:
|
||||
break;
|
||||
case SDMMC_R2:
|
||||
cmd |= SDMMC_CMD_WAITRESP_LONG;
|
||||
break;
|
||||
default:
|
||||
cmd |= SDMMC_CMD_WAITRESP_SHORT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* write ARG and enable Command Path State Machine */
|
||||
sdmmc->ARG = arg;
|
||||
sdmmc->CMD = cmd | SDMMC_CMD_CPSMEN;
|
||||
|
||||
/* clear and enable command related interrupts */
|
||||
sdmmc->ICR &= ~SDMMC_INT_STATIC_CMD;
|
||||
sdmmc->MASK &= ~SDMMC_INT_STATIC_CMD;
|
||||
sdmmc->MASK |= SDMMC_MASK_CTIMEOUTIE;
|
||||
sdmmc->MASK |= (resp_type == SDMMC_NO_R) ? SDMMC_MASK_CMDSENTIE
|
||||
: SDMMC_MASK_CMDRENDIE |
|
||||
SDMMC_MASK_CCRCFAILIE;
|
||||
|
||||
/* wait for command sent or response received */
|
||||
mutex_lock(&dev->irq_wait);
|
||||
|
||||
/* disable and clear command related static interrupts*/
|
||||
sdmmc->MASK &= ~SDMMC_INT_STATIC_CMD;
|
||||
sdmmc->ICR &= ~SDMMC_INT_STATIC_CMD;
|
||||
|
||||
/* disable the Command Path State Machine */
|
||||
sdmmc->CMD &= ~SDMMC_CMD_CPSMEN;
|
||||
|
||||
if (dev->irq_status & SDMMC_STA_CTIMEOUT) {
|
||||
DEBUG("[sdmmc] Timeout error\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
else if ((dev->irq_status & SDMMC_STA_CCRCFAIL) && (resp_type != SDMMC_R3)) {
|
||||
DEBUG("[sdmmc] CRC error in response\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if ((resp_type == SDMMC_R1) || (resp_type == SDMMC_R1B)) {
|
||||
dev->sdmmc_dev.status = sdmmc->RESP1;
|
||||
}
|
||||
|
||||
if (resp) {
|
||||
if (resp_type != SDMMC_NO_R) {
|
||||
resp[0] = sdmmc->RESP1;
|
||||
}
|
||||
if (resp_type == SDMMC_R2) {
|
||||
resp[1] = sdmmc->RESP2;
|
||||
resp[2] = sdmmc->RESP3;
|
||||
resp[3] = sdmmc->RESP4;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _set_bus_width(sdmmc_dev_t *sdmmc_dev, sdmmc_bus_width_t width)
|
||||
{
|
||||
stm32_sdmmc_dev_t *dev = container_of(sdmmc_dev, stm32_sdmmc_dev_t, sdmmc_dev);
|
||||
|
||||
assert(dev);
|
||||
assert(dev->config);
|
||||
|
||||
SDMMC_TypeDef *sdmmc = dev->config->dev;
|
||||
|
||||
/* Note: bits have to be cleared and written as a single write operation */
|
||||
switch (width) {
|
||||
case SDMMC_BUS_WIDTH_1BIT:
|
||||
sdmmc->CLKCR = (sdmmc->CLKCR & ~SDMMC_CLKCR_WIDBUS_Msk) |
|
||||
SDMMC_CLKCR_WIDBUS_1BIT;
|
||||
break;
|
||||
case SDMMC_BUS_WIDTH_4BIT:
|
||||
sdmmc->CLKCR = (sdmmc->CLKCR & ~SDMMC_CLKCR_WIDBUS_Msk) |
|
||||
SDMMC_CLKCR_WIDBUS_4BIT;
|
||||
break;
|
||||
case SDMMC_BUS_WIDTH_8BIT:
|
||||
sdmmc->CLKCR = (sdmmc->CLKCR & ~SDMMC_CLKCR_WIDBUS_Msk) |
|
||||
SDMMC_CLKCR_WIDBUS_8BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* prevent to write again data to this register within three SDIOCLK
|
||||
* clock periods plus two PCLK2 clock periods */
|
||||
_ZTIMER_SLEEP_MS(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _set_clock_rate(sdmmc_dev_t *sdmmc_dev, sdmmc_clock_rate_t rate)
|
||||
{
|
||||
stm32_sdmmc_dev_t *dev = container_of(sdmmc_dev, stm32_sdmmc_dev_t, sdmmc_dev);
|
||||
|
||||
assert(dev);
|
||||
assert(dev->config);
|
||||
|
||||
SDMMC_TypeDef *sdmmc = dev->config->dev;
|
||||
|
||||
/* Note: bits have to be cleared and written as a single write operation */
|
||||
switch (rate) {
|
||||
case SDMMC_CLK_400K:
|
||||
sdmmc->CLKCR = (sdmmc->CLKCR & ~(SDMMC_CLKCR_CLKDIV_Msk | SDMMC_CLKCR_BYPASS_Msk)) |
|
||||
SDMMC_CLKCR_CLKDIV_400KHZ;
|
||||
break;
|
||||
#ifdef CONFIG_SDMMC_CLK_MAX
|
||||
default:
|
||||
/* limit the Default and High Speed clock rates for debugging */
|
||||
sdmmc->CLKCR = (sdmmc->CLKCR & ~(SDMMC_CLKCR_CLKDIV_Msk | SDMMC_CLKCR_BYPASS_Msk)) |
|
||||
CONFIG_SDMMC_CLK_MAX;
|
||||
#else
|
||||
case SDMMC_CLK_20M:
|
||||
sdmmc->CLKCR = (sdmmc->CLKCR & ~(SDMMC_CLKCR_CLKDIV_Msk | SDMMC_CLKCR_BYPASS_Msk)) |
|
||||
SDMMC_CLKCR_CLKDIV_20MHZ;
|
||||
break;
|
||||
case SDMMC_CLK_25M:
|
||||
case SDMMC_CLK_26M:
|
||||
sdmmc->CLKCR = (sdmmc->CLKCR & ~(SDMMC_CLKCR_CLKDIV_Msk | SDMMC_CLKCR_BYPASS_Msk)) |
|
||||
SDMMC_CLKCR_CLKDIV_25MHZ;
|
||||
break;
|
||||
case SDMMC_CLK_52M:
|
||||
case SDMMC_CLK_50M:
|
||||
sdmmc->CLKCR = (sdmmc->CLKCR & ~(SDMMC_CLKCR_CLKDIV_Msk | SDMMC_CLKCR_BYPASS_Msk)) |
|
||||
SDMMC_CLKCR_BYPASS_Msk;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* prevent to write again data to this register within three SDIOCLK
|
||||
* clock cycles plus two PCLK2 clock cycles */
|
||||
_ZTIMER_SLEEP_MS(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _xfer_prepare(sdmmc_dev_t *sdmmc_dev, sdmmc_xfer_desc_t *xfer)
|
||||
{
|
||||
stm32_sdmmc_dev_t *dev = container_of(sdmmc_dev, stm32_sdmmc_dev_t, sdmmc_dev);
|
||||
|
||||
assert(dev);
|
||||
assert(dev->config);
|
||||
|
||||
SDMMC_TypeDef *sdmmc = dev->config->dev;
|
||||
|
||||
/* SDIO/SD/MMC uses 32-bit words */
|
||||
/* TODO: at the moment only 32-bit words supported */
|
||||
assert((xfer->block_size % sizeof(uint32_t)) == 0);
|
||||
|
||||
/* stop the Data Path State Machine and set the block size */
|
||||
sdmmc->DCTRL = 0;
|
||||
|
||||
/* configure data transfer */
|
||||
sdmmc->DTIMER = xfer->write ? SDMMC_DATA_W_TIMEOUT : SDMMC_DATA_R_TIMEOUT;
|
||||
sdmmc->DLEN = xfer->block_size * xfer->block_num;
|
||||
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
const sdmmc_conf_t *conf = dev->config;
|
||||
|
||||
if (_use_dma(conf)) {
|
||||
dma_acquire(conf->dma);
|
||||
dma_setup(conf->dma, conf->dma_chan, (uint32_t*)&(conf->dev->FIFO),
|
||||
xfer->write ? DMA_MEM_TO_PERIPH : DMA_PERIPH_TO_MEM,
|
||||
DMA_DATA_WIDTH_WORD, false);
|
||||
/* additional configuration needed for F2/F4/F7 */
|
||||
dma_setup_ext(conf->dma, DMA_BURST_INCR4, DMA_BURST_INCR4,
|
||||
true, DMA_FIFO_FULL, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _xfer_execute(sdmmc_dev_t *sdmmc_dev, sdmmc_xfer_desc_t *xfer,
|
||||
const void *data_wr, void *data_rd,
|
||||
uint16_t *done)
|
||||
{
|
||||
assert(xfer);
|
||||
assert((xfer->write && data_wr) || (!xfer->write && data_rd));
|
||||
|
||||
/* check the alignment required for the buffers */
|
||||
assert(HAS_ALIGNMENT_OF(data_wr, SDMMC_CPU_DMA_ALIGNMENT));
|
||||
assert(HAS_ALIGNMENT_OF(data_rd, SDMMC_CPU_DMA_ALIGNMENT));
|
||||
|
||||
stm32_sdmmc_dev_t *dev = container_of(sdmmc_dev, stm32_sdmmc_dev_t, sdmmc_dev);
|
||||
assert(dev);
|
||||
|
||||
const sdmmc_conf_t *conf = dev->config;
|
||||
assert(conf);
|
||||
|
||||
SDMMC_TypeDef *sdmmc = conf->dev;
|
||||
|
||||
uint32_t *data_to_read = data_rd;
|
||||
const uint32_t *data_to_write = data_wr;
|
||||
|
||||
/* clear data transfer related static interrupt flags */
|
||||
sdmmc->ICR = SDMMC_INT_STATIC_DATA;
|
||||
|
||||
/* enable used data interrupts */
|
||||
uint32_t status_mask = SDMMC_MASK_DCRCFAILIE | SDMMC_MASK_DTIMEOUTIE;
|
||||
status_mask |= xfer->write ? SDMMC_MASK_TXUNDERRIE : SDMMC_MASK_RXOVERRIE;
|
||||
status_mask |= (xfer->block_num == 1) ? SDMMC_MASK_DBCKENDIE
|
||||
: SDMMC_MASK_DATAENDIE;
|
||||
#if defined(SDMMC_MASK_STBITERRIE)
|
||||
/* Some CMSIS headers define the STBITERR enable and clear flag but not
|
||||
* the status flag. For such MCU models, the STBITERR flag is not documented
|
||||
* in the RM but marked as reserved. */
|
||||
status_mask |= SDMMC_MASK_STBITERRIE;
|
||||
#endif
|
||||
|
||||
sdmmc->MASK |= status_mask;
|
||||
|
||||
/* configure data transfer block size, read direction and enable the transfer */
|
||||
uint32_t dctrl = 0;
|
||||
dctrl |= bitarithm_msb(xfer->block_size) << SDMMC_DCTRL_DBLOCKSIZE_Pos;
|
||||
dctrl |= !xfer->write ? SDMMC_DCTRL_DTDIR : 0;
|
||||
|
||||
/* TODO: at the moment only 32-bit word transfers supported */
|
||||
assert((xfer->block_size % sizeof(uint32_t)) == 0);
|
||||
uint32_t fifo_words = sdmmc->DLEN >> 2; /* 32-bit words to read or write */
|
||||
|
||||
/* read data blocks from or write data blocks to the card */
|
||||
if (_use_dma(conf)) {
|
||||
#if IS_USED(MODULE_PERIPH_DMA)
|
||||
dma_prepare(conf->dma,
|
||||
(xfer->write) ? (void *)data_to_write : data_to_read,
|
||||
fifo_words, 1);
|
||||
dma_start(conf->dma);
|
||||
|
||||
/* TODO SDMMC_IDMA_IDMAEN
|
||||
* The SDMMC of the L4+ and some L5 MCUs have an internal DMA controller
|
||||
* (IDMA) instead of using the DMA periphery. This internal DMA requires
|
||||
* separate handling. Since `_use_dma` just returns `false` if
|
||||
* `SDMMC_IDMA_IDMAEN` is defined, this part of the code isn't used and
|
||||
* should be optimized out by the compiler. */
|
||||
|
||||
dctrl |= SDMMC_DCTRL_DMAEN;
|
||||
sdmmc->DCTRL = dctrl | SDMMC_DCTRL_DTEN;
|
||||
|
||||
/* wait for DBCKEND, DATAEND or any error interrupt */
|
||||
mutex_lock(&dev->irq_wait);
|
||||
|
||||
/* stop and release DMA */
|
||||
dma_stop(conf->dma);
|
||||
dma_release(conf->dma);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* start the Data Path State Machine */
|
||||
sdmmc->DCTRL = dctrl | SDMMC_DCTRL_DTEN;
|
||||
|
||||
do {
|
||||
if (xfer->write) {
|
||||
/* Fill the TX FIFO as long as TXFIFOF (TX FIFO Full) is not set
|
||||
* and further data have to be written. */
|
||||
while (((sdmmc->STA & SDMMC_STA_TXFIFOF) == 0) && fifo_words) {
|
||||
sdmmc->FIFO = *data_to_write++;
|
||||
fifo_words--;
|
||||
}
|
||||
/* Once the FIFO is full and more data needs to be sent, wait
|
||||
* for TXFIFOHE (TX FIFO Half Empty), i.e. there are at least
|
||||
* 8 32-bit words available in the FIFO again. TXFIFOHE is
|
||||
* dynamic and can't be cleared. It is therefore disabled in
|
||||
* ISR to prevent endless triggering. */
|
||||
if (fifo_words) {
|
||||
sdmmc->MASK |= status_mask | SDMMC_MASK_TXFIFOHEIE;
|
||||
}
|
||||
/* wait for TXFIFOHE or any other status interrupt */
|
||||
mutex_lock(&dev->irq_wait);
|
||||
}
|
||||
else {
|
||||
/* If further data have to be read, read all available data
|
||||
* from the RX FIFO (as long as RXDAVL is set) at once */
|
||||
while ((sdmmc->STA & SDMMC_STA_RXDAVL) && fifo_words) {
|
||||
*data_to_read++ = sdmmc->FIFO;
|
||||
fifo_words--;
|
||||
}
|
||||
/* If there are still at least 8 32-bit words to be read, wait
|
||||
* until at least 8 32-bit words have been received in the FIFO.
|
||||
* RXFIFOHF interrupt is dynamic and can't be cleared. It is
|
||||
* therefore disabled in ISR to prevent endless triggering. */
|
||||
if (fifo_words >= 8) {
|
||||
sdmmc->MASK |= SDMMC_MASK_RXFIFOHFIE;
|
||||
}
|
||||
/* wait for RXFIFOHF or any other status interrupt */
|
||||
mutex_lock(&dev->irq_wait);
|
||||
}
|
||||
} while (!(dev->irq_status & status_mask));
|
||||
|
||||
if (!xfer->write) {
|
||||
/* read remaining data from RX FIFO if there are any */
|
||||
while (sdmmc->STA & SDMMC_STA_RXDAVL) {
|
||||
*data_to_read++ = sdmmc->FIFO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* disable the Data Path State Machine */
|
||||
sdmmc->DCTRL = 0;
|
||||
|
||||
/* disable and clear all data transfer related static interrupts */
|
||||
sdmmc->MASK &= ~SDMMC_INT_STATIC_DATA;
|
||||
sdmmc->ICR = SDMMC_INT_STATIC_DATA;
|
||||
|
||||
if (done) {
|
||||
*done = xfer->block_num - (sdmmc->DCOUNT / xfer->block_size);
|
||||
}
|
||||
|
||||
if (dev->irq_status & SDMMC_STA_DTIMEOUT) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
else if (dev->irq_status & SDMMC_STA_DCRCFAIL) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
else if (dev->irq_status & SDMMC_STA_RXOVERR) {
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
else if (dev->irq_status & SDMMC_STA_TXUNDERR) {
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
#if defined(SDMMC_STA_STBITERR)
|
||||
else if (dev->irq_status & SDMMC_STA_STBITERR) {
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _xfer_finish(sdmmc_dev_t *sdmmc_dev, sdmmc_xfer_desc_t *xfer)
|
||||
{
|
||||
(void)sdmmc_dev;
|
||||
(void)xfer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _isr(SDMMC_TypeDef *dev)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
/* determine the index of the device that triggers the IRQ */
|
||||
for (i = 0; i < ARRAY_SIZE(_sdmmc_devs); i++) {
|
||||
if (_sdmmc_devs[i].config->dev == dev) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(i < ARRAY_SIZE(_sdmmc_devs));
|
||||
stm32_sdmmc_dev_t *sdmmc_dev = &_sdmmc_devs[i];
|
||||
|
||||
if ((dev->STA & SDMMC_STA_TXFIFOHE) && (dev->MASK & SDMMC_MASK_TXFIFOHEIE)) {
|
||||
/* Write transfer is waiting for free FIFO space signaled by RXFIFOF.
|
||||
* Since TXFIFOHE is dynamic and can't be cleared, it has to be
|
||||
* disabled here to prevent endless triggering. */
|
||||
sdmmc_dev->irq_status = dev->STA;
|
||||
dev->MASK &= ~SDMMC_MASK_TXFIFOHEIE;
|
||||
mutex_unlock(&sdmmc_dev->irq_wait);
|
||||
}
|
||||
else if ((dev->STA & SDMMC_STA_RXFIFOHF) && (dev->MASK & SDMMC_MASK_RXFIFOHFIE)) {
|
||||
/* Read transfer is waiting for available data signaled by RXFIFOF.
|
||||
* Since RXFIFOHF is dynamic and can't be cleared, it has to be
|
||||
* disabled here to prevent endless triggering. */
|
||||
sdmmc_dev->irq_status = dev->STA;
|
||||
dev->MASK &= ~SDMMC_MASK_RXFIFOHFIE;
|
||||
mutex_unlock(&sdmmc_dev->irq_wait);
|
||||
}
|
||||
else if (dev->STA & SDMMC_INT_STATIC_DATA) {
|
||||
/* data transfer is in progress and has to be handled. */
|
||||
sdmmc_dev->irq_status = dev->STA;
|
||||
dev->ICR = SDMMC_INT_STATIC_DATA;
|
||||
mutex_unlock(&sdmmc_dev->irq_wait);
|
||||
}
|
||||
else if (dev->STA & SDMMC_INT_STATIC_CMD) {
|
||||
/* Send command is in progress and has to be handled. */
|
||||
sdmmc_dev->irq_status = dev->STA;
|
||||
dev->ICR = SDMMC_INT_STATIC_CMD;
|
||||
mutex_unlock(&sdmmc_dev->irq_wait);
|
||||
}
|
||||
|
||||
#ifdef MODULE_CORTEXM_COMMON
|
||||
cortexm_isr_end();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(SDIO)
|
||||
void isr_sdio(void)
|
||||
{
|
||||
_isr(SDIO);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SDMMC1)
|
||||
void isr_sdmmc1(void)
|
||||
{
|
||||
_isr(SDMMC1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SDMMC2)
|
||||
void _isr_sdmmc2(void)
|
||||
{
|
||||
_isr(SDMMC2);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _isr_cd_pin(void *arg)
|
||||
{
|
||||
stm32_sdmmc_dev_t *dev = arg;
|
||||
assert(dev);
|
||||
|
||||
const sdmmc_conf_t *conf = dev->config;
|
||||
assert(conf);
|
||||
|
||||
sdmmc_dev_t *sdmmc_dev = &dev->sdmmc_dev;
|
||||
|
||||
sdmmc_dev->present = gpio_read(conf->cd) == conf->cd_active;
|
||||
sdmmc_dev->init_done = false;
|
||||
|
||||
if (sdmmc_dev->event_cb) {
|
||||
sdmmc_dev->event_cb(sdmmc_dev,
|
||||
sdmmc_dev->present ? SDMMC_EVENT_CARD_INSERTED
|
||||
: SDMMC_EVENT_CARD_REMOVED);
|
||||
}
|
||||
}
|
||||
|
||||
static const sdmmc_driver_t _driver = {
|
||||
.init = _init,
|
||||
.card_init = NULL, /* no own card init function */
|
||||
.send_cmd = _send_cmd,
|
||||
.set_bus_width = _set_bus_width,
|
||||
.set_clock_rate = _set_clock_rate,
|
||||
.xfer_prepare = _xfer_prepare,
|
||||
.xfer_execute = _xfer_execute,
|
||||
.xfer_finish = _xfer_finish,
|
||||
};
|
@ -54,38 +54,43 @@
|
||||
from PLLI2S or PLLSAI */
|
||||
|
||||
/* Determine if PLL is required, even if not used as SYSCLK
|
||||
This is the case when USB is used in application and PLLQ is configured to
|
||||
output 48MHz */
|
||||
#if IS_USED(MODULE_PERIPH_USBDEV_CLK) && (CLOCK_PLLQ == MHZ(48))
|
||||
This is the case when USB/SDIO/SDMMC is used in application and PLLQ is
|
||||
configured to output 48MHz */
|
||||
#if (IS_USED(MODULE_PERIPH_USBDEV_CLK) || IS_USED(MODULE_PERIPH_SDMMC_CLK)) && \
|
||||
(CLOCK_PLLQ == MHZ(48))
|
||||
#define CLOCK_REQUIRE_PLLQ 1
|
||||
#else
|
||||
#define CLOCK_REQUIRE_PLLQ 0
|
||||
#endif
|
||||
|
||||
/* PLLI2S can only be used for USB with F412/F413/F423 lines
|
||||
/* PLLI2S can only be used for USB/SDIO/SDMMC with F412/F413/F423 lines
|
||||
PLLI2S is only enabled if no suitable 48MHz clock source can be generated with PLLQ */
|
||||
#if (defined(CPU_LINE_STM32F412Cx) || defined(CPU_LINE_STM32F412Rx) || \
|
||||
defined(CPU_LINE_STM32F412Vx) || defined(CPU_LINE_STM32F412Zx) || \
|
||||
defined(CPU_LINE_STM32F413xx) || defined(CPU_LINE_STM32F423xx)) && \
|
||||
IS_USED(MODULE_PERIPH_USBDEV_CLK) && !IS_ACTIVE(CLOCK_REQUIRE_PLLQ)
|
||||
(IS_USED(MODULE_PERIPH_USBDEV_CLK) || IS_USED(MODULE_PERIPH_SDMMC_CLK)) && \
|
||||
!IS_ACTIVE(CLOCK_REQUIRE_PLLQ)
|
||||
#define CLOCK_REQUIRE_PLLI2SR 1
|
||||
#else
|
||||
/* Disable PLLI2S if USB is not required or is required but PLLQ cannot generate 48MHz clock */
|
||||
/* Disable PLLI2S if USB/SDIO/SDMMC is not required or is required but PLLQ
|
||||
* cannot generate 48MHz clock */
|
||||
#define CLOCK_REQUIRE_PLLI2SR 0
|
||||
#endif
|
||||
|
||||
/* PLLSAI can only be used for USB with F446/469/479 lines and F7
|
||||
/* PLLSAI can only be used for USB/SDIO/SDMMC with F446/469/479 lines and F7
|
||||
PLLSAI is only enabled if no suitable 48MHz clock source can be generated with PLLQ */
|
||||
#if (defined(CPU_LINE_STM32F446xx) || defined(CPU_LINE_STM32F469xx) || \
|
||||
defined(CPU_LINE_STM32F479xx) || defined(CPU_FAM_STM32F7)) && \
|
||||
IS_USED(MODULE_PERIPH_USBDEV_CLK) && !IS_ACTIVE(CLOCK_REQUIRE_PLLQ)
|
||||
(IS_USED(MODULE_PERIPH_USBDEV_CLK) || IS_USED(MODULE_PERIPH_SDMMC_CLK)) && \
|
||||
!IS_ACTIVE(CLOCK_REQUIRE_PLLQ)
|
||||
#define CLOCK_REQUIRE_PLLSAIP 1
|
||||
#else
|
||||
/* Disable PLLSAI if USB is not required or is required but PLLQ cannot generate 48MHz clock */
|
||||
/* Disable PLLSAI if USB/SDIO/SDMMC is not required or is required but PLLQ
|
||||
* cannot generate 48MHz clock */
|
||||
#define CLOCK_REQUIRE_PLLSAIP 0
|
||||
#endif
|
||||
|
||||
#if IS_USED(MODULE_PERIPH_USBDEV_CLK) && \
|
||||
#if (IS_USED(MODULE_PERIPH_USBDEV_CLK) || IS_USED(MODULE_PERIPH_SDMMC_CLK)) && \
|
||||
!(IS_ACTIVE(CLOCK_REQUIRE_PLLQ) || \
|
||||
IS_ACTIVE(CLOCK_REQUIRE_PLLI2SR) || \
|
||||
IS_ACTIVE(CLOCK_REQUIRE_PLLSAIP))
|
||||
@ -123,8 +128,8 @@
|
||||
#define CONFIG_CLOCK_PLLI2S_P (8) /* SPDIF-Rx clock, 48MHz by default */
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLLI2S_Q
|
||||
#define CONFIG_CLOCK_PLLI2S_Q (8) /* Alternative 48MHz clock (USB) and/or MCO2 PLLI2S */
|
||||
#endif
|
||||
#define CONFIG_CLOCK_PLLI2S_Q (8) /* Alternative 48MHz clock (USB/SDIO/SDMMC) */
|
||||
#endif /* and/or MCO2 PLLI2S */
|
||||
#ifndef CONFIG_CLOCK_PLLI2S_R
|
||||
#define CONFIG_CLOCK_PLLI2S_R (8) /* I2S clock, 48MHz by default */
|
||||
#endif
|
||||
@ -176,7 +181,7 @@
|
||||
#endif
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLLSAI_P
|
||||
#define CONFIG_CLOCK_PLLSAI_P (8) /* Alternative 48MHz clock (USB) */
|
||||
#define CONFIG_CLOCK_PLLSAI_P (8) /* Alternative 48MHz clock (USB/SDIO/SDMMC) */
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLLSAI_Q
|
||||
#define CONFIG_CLOCK_PLLSAI_Q (8) /* SAI clock, 48MHz by default */
|
||||
|
@ -350,8 +350,9 @@
|
||||
#define CLOCK48MHZ_SELECT (0)
|
||||
#endif
|
||||
|
||||
/* periph_hwrng and periph_usbdev require a 48MHz clock source */
|
||||
#if IS_USED(MODULE_PERIPH_HWRNG) || IS_USED(MODULE_PERIPH_USBDEV_CLK)
|
||||
/* periph_hwrng, periph_usbdev and periph_sdmmc require a 48MHz clock source */
|
||||
#if IS_USED(MODULE_PERIPH_HWRNG) || IS_USED(MODULE_PERIPH_USBDEV_CLK) || \
|
||||
IS_USED(MODULE_PERIPH_SDMMC_CLK)
|
||||
#if !IS_ACTIVE(CLOCK48MHZ_USE_PLLQ) && !IS_ACTIVE(CLOCK48MHZ_USE_MSI) && \
|
||||
!IS_ACTIVE(CLOCK48MHZ_USE_HSI48)
|
||||
#error "No 48MHz clock source available, HWRNG cannot work"
|
||||
|
Loading…
Reference in New Issue
Block a user