From d91f4385897ddd4c769925207eb8c9a647a4d9e4 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 28 Apr 2023 10:17:46 +0200 Subject: [PATCH 1/5] cpu/stm32/periph/dma: dma_setup_ext for extended configuration The function configures additional features of the DMA stream for F2/F4/F7. `dma_setup_ext` added to configure F2/F4/F7 specific additional features like `MBURST`, `PBURST`, `FIFO` and Peripheral flow controller. It is supposed to be used after `dma_setup` and `dma_prepare`. --- cpu/stm32/include/periph/cpu_dma.h | 43 ++++++++++++++++++ cpu/stm32/periph/dma.c | 72 ++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/cpu/stm32/include/periph/cpu_dma.h b/cpu/stm32/include/periph/cpu_dma.h index f9cfd118f8d2..4ac8991d1a39 100644 --- a/cpu/stm32/include/periph/cpu_dma.h +++ b/cpu/stm32/include/periph/cpu_dma.h @@ -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 * diff --git a/cpu/stm32/periph/dma.c b/cpu/stm32/periph/dma.c index 2d720e806a63..0b4370aed474 100644 --- a/cpu/stm32/periph/dma.c +++ b/cpu/stm32/periph/dma.c @@ -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) { From 337a63ecb5c1971992a0688988fbd0c4086833ec Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 13:52:58 +0200 Subject: [PATCH 2/5] cpu/stm32/periph: add SDMMC support for F2/F4/F7/L4 --- cpu/stm32/Makefile.dep | 6 + cpu/stm32/Makefile.features | 7 + .../clk/f2f4f7/cfg_clock_default_100.h | 5 +- .../clk/f2f4f7/cfg_clock_default_180.h | 6 +- cpu/stm32/include/periph/cpu_sdmmc.h | 100 ++ cpu/stm32/include/periph_cpu.h | 1 + cpu/stm32/kconfigs/f2/Kconfig | 4 + cpu/stm32/kconfigs/f4/Kconfig | 4 + cpu/stm32/kconfigs/f7/Kconfig | 4 + cpu/stm32/kconfigs/l4/Kconfig | 4 + cpu/stm32/periph/Kconfig | 13 + cpu/stm32/periph/sdmmc.c | 858 ++++++++++++++++++ cpu/stm32/stmclk/stmclk_f2f4f7.c | 31 +- cpu/stm32/stmclk/stmclk_l4wx.c | 5 +- 14 files changed, 1028 insertions(+), 20 deletions(-) create mode 100644 cpu/stm32/include/periph/cpu_sdmmc.h create mode 100644 cpu/stm32/periph/sdmmc.c diff --git a/cpu/stm32/Makefile.dep b/cpu/stm32/Makefile.dep index b1aca8e5be9a..c11dc0472131 100644 --- a/cpu/stm32/Makefile.dep +++ b/cpu/stm32/Makefile.dep @@ -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 diff --git a/cpu/stm32/Makefile.features b/cpu/stm32/Makefile.features index aa93e3cef57f..4fb6ab240b26 100644 --- a/cpu/stm32/Makefile.features +++ b/cpu/stm32/Makefile.features @@ -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 diff --git a/cpu/stm32/include/clk/f2f4f7/cfg_clock_default_100.h b/cpu/stm32/include/clk/f2f4f7/cfg_clock_default_100.h index 400b37fa29b5..b6cb75273dd8 100644 --- a/cpu/stm32/include/clk/f2f4f7/cfg_clock_default_100.h +++ b/cpu/stm32/include/clk/f2f4f7/cfg_clock_default_100.h @@ -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) diff --git a/cpu/stm32/include/clk/f2f4f7/cfg_clock_default_180.h b/cpu/stm32/include/clk/f2f4f7/cfg_clock_default_180.h index de4bec1e1936..e443a1effe22 100644 --- a/cpu/stm32/include/clk/f2f4f7/cfg_clock_default_180.h +++ b/cpu/stm32/include/clk/f2f4f7/cfg_clock_default_180.h @@ -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) || \ diff --git a/cpu/stm32/include/periph/cpu_sdmmc.h b/cpu/stm32/include/periph/cpu_sdmmc.h new file mode 100644 index 000000000000..4048c55655b6 --- /dev/null +++ b/cpu/stm32/include/periph/cpu_sdmmc.h @@ -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 + */ + +#ifndef PERIPH_CPU_SDMMC_H +#define PERIPH_CPU_SDMMC_H + +#include + +#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 */ +/** @} */ diff --git a/cpu/stm32/include/periph_cpu.h b/cpu/stm32/include/periph_cpu.h index 67d9a06ec19a..959ddcca6da7 100644 --- a/cpu/stm32/include/periph_cpu.h +++ b/cpu/stm32/include/periph_cpu.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" diff --git a/cpu/stm32/kconfigs/f2/Kconfig b/cpu/stm32/kconfigs/f2/Kconfig index fce3e13483a2..e3ecddea5835 100644 --- a/cpu/stm32/kconfigs/f2/Kconfig +++ b/cpu/stm32/kconfigs/f2/Kconfig @@ -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 diff --git a/cpu/stm32/kconfigs/f4/Kconfig b/cpu/stm32/kconfigs/f4/Kconfig index 4ee8d84927ac..fc72cecea9c5 100644 --- a/cpu/stm32/kconfigs/f4/Kconfig +++ b/cpu/stm32/kconfigs/f4/Kconfig @@ -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 diff --git a/cpu/stm32/kconfigs/f7/Kconfig b/cpu/stm32/kconfigs/f7/Kconfig index 8b07ec2ecae4..bff462e03422 100644 --- a/cpu/stm32/kconfigs/f7/Kconfig +++ b/cpu/stm32/kconfigs/f7/Kconfig @@ -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 diff --git a/cpu/stm32/kconfigs/l4/Kconfig b/cpu/stm32/kconfigs/l4/Kconfig index cd89902fc78c..437a852c61b4 100644 --- a/cpu/stm32/kconfigs/l4/Kconfig +++ b/cpu/stm32/kconfigs/l4/Kconfig @@ -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 diff --git a/cpu/stm32/periph/Kconfig b/cpu/stm32/periph/Kconfig index a15f74f45d2d..9ea741c7859a 100644 --- a/cpu/stm32/periph/Kconfig +++ b/cpu/stm32/periph/Kconfig @@ -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 diff --git a/cpu/stm32/periph/sdmmc.c b/cpu/stm32/periph/sdmmc.c new file mode 100644 index 000000000000..f2c12c2e14f9 --- /dev/null +++ b/cpu/stm32/periph/sdmmc.c @@ -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 + * + * @} + */ + +#include +#include +#include +#include + +#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, +}; diff --git a/cpu/stm32/stmclk/stmclk_f2f4f7.c b/cpu/stm32/stmclk/stmclk_f2f4f7.c index 9459fffcf020..50dee71dc956 100644 --- a/cpu/stm32/stmclk/stmclk_f2f4f7.c +++ b/cpu/stm32/stmclk/stmclk_f2f4f7.c @@ -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 */ diff --git a/cpu/stm32/stmclk/stmclk_l4wx.c b/cpu/stm32/stmclk/stmclk_l4wx.c index b58eaa3b1ddd..9262d8d65a60 100644 --- a/cpu/stm32/stmclk/stmclk_l4wx.c +++ b/cpu/stm32/stmclk/stmclk_l4wx.c @@ -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" From ae4ba633d939107cc647a9ed68dca4cf943a10b6 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 5 May 2023 14:20:25 +0200 Subject: [PATCH 3/5] boards/stm32f746g-disco: add SD Card interface support --- boards/stm32f746g-disco/Kconfig | 3 +- boards/stm32f746g-disco/Makefile.dep | 11 +++++ boards/stm32f746g-disco/doc.txt | 1 + boards/stm32f746g-disco/features-shared.mk | 1 + boards/stm32f746g-disco/include/periph_conf.h | 42 ++++++++++++++++++- 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/boards/stm32f746g-disco/Kconfig b/boards/stm32f746g-disco/Kconfig index 0b40bb655152..31bc92645e05 100644 --- a/boards/stm32f746g-disco/Kconfig +++ b/boards/stm32f746g-disco/Kconfig @@ -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 diff --git a/boards/stm32f746g-disco/Makefile.dep b/boards/stm32f746g-disco/Makefile.dep index fe69ca75f91d..f0cfb7a11a26 100644 --- a/boards/stm32f746g-disco/Makefile.dep +++ b/boards/stm32f746g-disco/Makefile.dep @@ -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 diff --git a/boards/stm32f746g-disco/doc.txt b/boards/stm32f746g-disco/doc.txt index 3ccb239c32f1..b984be636c60 100644 --- a/boards/stm32f746g-disco/doc.txt +++ b/boards/stm32f746g-disco/doc.txt @@ -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 diff --git a/boards/stm32f746g-disco/features-shared.mk b/boards/stm32f746g-disco/features-shared.mk index 27ce5d56deab..d025e3fd61f3 100644 --- a/boards/stm32f746g-disco/features-shared.mk +++ b/boards/stm32f746g-disco/features-shared.mk @@ -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 diff --git a/boards/stm32f746g-disco/include/periph_conf.h b/boards/stm32f746g-disco/include/periph_conf.h index 3660893f5a59..a1ecb2267b5d 100644 --- a/boards/stm32f746g-disco/include/periph_conf.h +++ b/boards/stm32f746g-disco/include/periph_conf.h @@ -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 From 2c2e70fb17b8a1ccc72da2137be8cebe8a4abbd9 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 13:43:36 +0200 Subject: [PATCH 4/5] boards/stm32l496g-disco: add SDMMC support --- boards/stm32l496g-disco/Kconfig | 2 + boards/stm32l496g-disco/Makefile.dep | 11 ++++ boards/stm32l496g-disco/Makefile.features | 1 + boards/stm32l496g-disco/doc.txt | 2 +- boards/stm32l496g-disco/include/periph_conf.h | 61 +++++++++++++++---- 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/boards/stm32l496g-disco/Kconfig b/boards/stm32l496g-disco/Kconfig index e8db1dc0242f..118af8730b05 100644 --- a/boards/stm32l496g-disco/Kconfig +++ b/boards/stm32l496g-disco/Kconfig @@ -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 diff --git a/boards/stm32l496g-disco/Makefile.dep b/boards/stm32l496g-disco/Makefile.dep index f231d8bf46e1..dde15b3432bd 100644 --- a/boards/stm32l496g-disco/Makefile.dep +++ b/boards/stm32l496g-disco/Makefile.dep @@ -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 diff --git a/boards/stm32l496g-disco/Makefile.features b/boards/stm32l496g-disco/Makefile.features index c284ab673f17..be265f41dc66 100644 --- a/boards/stm32l496g-disco/Makefile.features +++ b/boards/stm32l496g-disco/Makefile.features @@ -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 diff --git a/boards/stm32l496g-disco/doc.txt b/boards/stm32l496g-disco/doc.txt index 1449687af614..d4530b96d957 100644 --- a/boards/stm32l496g-disco/doc.txt +++ b/boards/stm32l496g-disco/doc.txt @@ -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): diff --git a/boards/stm32l496g-disco/include/periph_conf.h b/boards/stm32l496g-disco/include/periph_conf.h index 0e6d5a5e6f16..d7acd91958db 100644 --- a/boards/stm32l496g-disco/include/periph_conf.h +++ b/boards/stm32l496g-disco/include/periph_conf.h @@ -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) From b3f3e16771547a2e50976eb2914028118685c767 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 22 Dec 2023 12:01:48 +0100 Subject: [PATCH 5/5] boards/stm32f7508-dk: add SDMMC support --- boards/stm32f7508-dk/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/boards/stm32f7508-dk/Kconfig b/boards/stm32f7508-dk/Kconfig index 775528664a7d..95f26a1abead 100644 --- a/boards/stm32f7508-dk/Kconfig +++ b/boards/stm32f7508-dk/Kconfig @@ -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