diff --git a/boards/common/esp32x/include/board_common.h b/boards/common/esp32x/include/board_common.h
index 30b98def2c70..b39c1d03b95d 100644
--- a/boards/common/esp32x/include/board_common.h
+++ b/boards/common/esp32x/include/board_common.h
@@ -7,7 +7,7 @@
*/
/**
- * @ingroup boards_common
+ * @ingroup boards_common_esp32x
* @brief Board definitions that are common for all ESP32x boards.
*
* This file contains board configurations that are valid for all ESP32.
@@ -103,17 +103,30 @@ extern "C" {
#if MODULE_MTD_SDCARD_DEFAULT || DOXYGEN
#define MTD_1 mtd_dev_get(1) /**< MTD device for the SD Card */
+#elif MODULE_MTD_SDMMC_DEFAULT
+#define MTD_1 mtd_dev_get(1) /**< MTD device for the SD/MMC Card */
#endif /* MODULE_MTD_SDCARD_DEFAULT || DOXYGEN */
/**
- * @brief MTD offset for SD Card interfaces
+ * @brief Default MTD offset for SPI SD Card interfaces
*
- * MTD_1 is used for SD Card.
+ * mtd1 is used for SPI SD Cards by default if module `mtd_sdcard_default`
+ * is used.
*/
#ifndef CONFIG_SDCARD_GENERIC_MTD_OFFSET
#define CONFIG_SDCARD_GENERIC_MTD_OFFSET 1
#endif
+/**
+ * @brief Default MTD offset for SD/MMC interfaces
+ *
+ * mtd1 is used for SD/MMCs by default if module `mtd_sdmmc_default`
+ * is used.
+ */
+#ifndef CONFIG_SDMMC_GENERIC_MTD_OFFSET
+#define CONFIG_SDMMC_GENERIC_MTD_OFFSET 1
+#endif
+
/** @} */
#endif /* MODULE_MTD || DOXYGEN */
diff --git a/boards/common/esp32x/include/periph_conf_common.h b/boards/common/esp32x/include/periph_conf_common.h
index 2f7bfe6bfab5..0d56301e4885 100644
--- a/boards/common/esp32x/include/periph_conf_common.h
+++ b/boards/common/esp32x/include/periph_conf_common.h
@@ -338,6 +338,10 @@ static const spi_conf_t spi_config[] = {
* @note SPI_NUMOF definition must not be changed.
*/
#define SPI_NUMOF ARRAY_SIZE(spi_config)
+
+#if IS_USED(MODULE_PERIPH_SPI)
+static_assert(SPI_NUMOF != 0, "No SPI devices defined");
+#endif
/** @} */
/**
diff --git a/boards/esp32-mh-et-live-minikit/Makefile.dep b/boards/esp32-mh-et-live-minikit/Makefile.dep
index 29d6c99ba8ee..9b22e1fdcbfd 100644
--- a/boards/esp32-mh-et-live-minikit/Makefile.dep
+++ b/boards/esp32-mh-et-live-minikit/Makefile.dep
@@ -1 +1,13 @@
+# if the sdcard_spi module is enabled, the SD Card Shield is used
+ifneq (,$(filter sdcard_spi,$(USEMODULE)))
+ # default to using fatfs on SD card
+ ifneq (,$(filter vfs_default,$(USEMODULE)))
+ USEMODULE += fatfs_vfs
+ USEMODULE += mtd
+ endif
+ ifneq (,$(filter mtd,$(USEMODULE)))
+ USEMODULE += mtd_sdcard_default
+ endif
+endif
+
include $(RIOTBOARD)/common/esp32/Makefile.dep
diff --git a/boards/esp32-mh-et-live-minikit/doc.txt b/boards/esp32-mh-et-live-minikit/doc.txt
index 8deca0fc66f5..7a9deb65f851 100644
--- a/boards/esp32-mh-et-live-minikit/doc.txt
+++ b/boards/esp32-mh-et-live-minikit/doc.txt
@@ -31,7 +31,7 @@ interesting development kit as it uses in the stackable Wemos D1 Mini format.
Thus, all [shields for Wemos D1 mini](https://docs.wemos.cc/en/latest/d1_mini_shield/index.html)
for ESP8266 can also be used with ESP32. Examples for such shields are:
-- Micro SD-Card Shield
+- Micro SD-Card Shield (enable module `sdcard_spi` to use it)
- MRF24J40 IEEE 802.15.4 radio Shield
- Button Shield
- RGB LED Shield
diff --git a/boards/esp32-olimex-evb/Kconfig b/boards/esp32-olimex-evb/Kconfig
index 7b52efabd3f7..e3758545c4bd 100644
--- a/boards/esp32-olimex-evb/Kconfig
+++ b/boards/esp32-olimex-evb/Kconfig
@@ -17,8 +17,11 @@ config BOARD_ESP32_OLIMEX_EVB
select HAS_PERIPH_ADC if USEMODULE_OLIMEX_ESP32_GATEWAY
select HAS_PERIPH_I2C
select HAS_PERIPH_PWM
+ select HAS_PERIPH_SDMMC
select HAS_PERIPH_SPI
select HAS_PERIPH_CAN
select HAS_PERIPH_IR
+ select HAVE_MTD_SDMMC_DEFAULT
+
source "$(RIOTBOARD)/common/esp32/Kconfig"
diff --git a/boards/esp32-olimex-evb/Makefile.dep b/boards/esp32-olimex-evb/Makefile.dep
index 1b7c4590a759..bd77f00ea246 100644
--- a/boards/esp32-olimex-evb/Makefile.dep
+++ b/boards/esp32-olimex-evb/Makefile.dep
@@ -4,3 +4,13 @@ include $(RIOTBOARD)/common/esp32/Makefile.dep
ifneq (,$(filter netdev_default,$(USEMODULE)))
USEMODULE += esp_eth
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
+endif
diff --git a/boards/esp32-olimex-evb/Makefile.features b/boards/esp32-olimex-evb/Makefile.features
index 7db04be96b69..29a36ef6fa0a 100644
--- a/boards/esp32-olimex-evb/Makefile.features
+++ b/boards/esp32-olimex-evb/Makefile.features
@@ -3,13 +3,20 @@ CPU_MODEL = esp32-wroom_32
# common board and CPU features
include $(RIOTBOARD)/common/esp32/Makefile.features
-# additional features provided by the board (no ADC and no DAC)
ifneq (,$(filter olimex_esp32_gateway,$(USEMODULE)))
+ # additional features provided by Olimex ESP32 Gateway
FEATURES_PROVIDED += periph_adc
+else
+ # SPI interface is not available on Olimex ESP32 Gateway
+ FEATURES_PROVIDED += periph_spi
endif
+
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
-FEATURES_PROVIDED += periph_spi
+FEATURES_PROVIDED += periph_sdmmc
+
+FEATURES_CONFLICT += periph_sdmmc:periph_spi
+FEATURES_CONFLICT_MSG += "SD/MMC and SPI cannot be used at the same time on this board."
# unique features of the board
FEATURES_PROVIDED += esp_eth # Ethernet MAC (EMAC)
diff --git a/boards/esp32-olimex-evb/doc.txt b/boards/esp32-olimex-evb/doc.txt
index 4456a24aa230..c2d01521b159 100644
--- a/boards/esp32-olimex-evb/doc.txt
+++ b/boards/esp32-olimex-evb/doc.txt
@@ -92,15 +92,15 @@ overridden by \ref esp32_application_specific_configurations
Pin | Configuration\n ESP32-EVB | Configuration\n ESP32-GATEWAY | Remarks / Prerequisites | Configuration
:------|:------------------|:-----------------|-|-|
-GPIO13 | I2C_DEV(0):SDA | SDCARD_CS | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_i2c_interfaces "I2C Interfaces"
+GPIO13 | I2C_DEV(0):SDA | SDMMC_DEV(0):DAT3 | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_i2c_interfaces "I2C Interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
GPIO16 | I2C_DEV(0):SCL | I2C_DEV(0):SCL | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_i2c_interfaces "I2C Interfaces"
-GPIO14 | SPI_DEV(0):CLK | SDCARD_CLK | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces"
-GPIO2 | SPI_DEV(0):MISO | SDCARD_MISO | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces"
-GPIO15 | SPI_DEV(0):MOSI | SDCARD_MOSI | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces"
+GPIO14 | SPI_DEV(0):CLK, SDMMC_DEV(0):CLK | SDMMC_DEV(0):CLK | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
+GPIO2 | SPI_DEV(0):MISO, SDMMC_DEV(0):DAT0 | SDMMC_DEV(0):DAT0 | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
+GPIO15 | SPI_DEV(0):MOSI, SDMMC_DEV(0):CMD | SDMMC_DEV(0):CMD | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
GPIO17 | SPI_DEV(0):CS0 | I2C_DEV(0):SDA | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces"
GPIO1 | UART_DEV(0):TxD | UART_DEV(0):TxD | Console (cannot be changed) | \ref esp32_uart_interfaces "UART interfaces"
GPIO3 | UART_DEV(0):RxD | UART_DEV(0):RxD | Console (cannot be changed) | \ref esp32_uart_interfaces "UART interfaces"
-GPIO4 | UART_DEV(1):TxD | N/A | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_uart_interfaces "UART interfaces"
+GPIO4 | UART_DEV(1):TxD | SDMMC_DEV(0):DAT1 | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_uart_interfaces "UART interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
GPIO36 | UART_DEV(1):RxD | ADC_LINE(2) | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_uart_interfaces "UART interfaces"
GPIO32 | Relais 1 | ADC_LINE(0) | | \ref esp32_adc_channels "ADC Channels"
GPIO33 | Relais 2 | LED0 | | |
@@ -109,7 +109,7 @@ GPIO9 | PWM_DEV(0):0 | PWM_DEV(0):0 | | \ref esp32_pwm_channels "PWM C
GPIO10 | PWM_DEV(0):1 | PWM_DEV(0):1 | | \ref esp32_pwm_channels "PWM Channels"
GPIO5 | CAN_DEV(0):TX | | | \ref esp32_can_interfaces "CAN Interfaces"
GPIO35 | CAN_DEV(0):RX | ADC_LINE(1) | | \ref esp32_adc_channels "ADC Channels"
-GPIO12 | IR_DEV(0):TX | N/A | IR is not yet supported | |
+GPIO12 | IR_DEV(0):TX | SDMMC_DEV(0):DAT2 | IR is not yet supported | \ref esp32_sdmmc_interfaces "SDMMC Interfaces" |
GPIO39 | IR_DEV(0):RX | ADC_LINE(3) | IR is not yet supported | \ref esp32_adc_channels "ADC Channels"
GPIO18 | EMAC_SMI:MDIO | EMAC_SMI:MDIO | LAN interface | \ref esp32_ethernet_network_interface "Ethernet MAC"
GPIO23 | EMAC_SMI:MDC | EMAC_SMI:MDC | LAN interface | \ref esp32_ethernet_network_interface "Ethernet MAC"
diff --git a/boards/esp32-olimex-evb/include/periph_conf.h b/boards/esp32-olimex-evb/include/periph_conf.h
index 6d921cf739ea..32e1a2b7c972 100644
--- a/boards/esp32-olimex-evb/include/periph_conf.h
+++ b/boards/esp32-olimex-evb/include/periph_conf.h
@@ -36,6 +36,8 @@
#include
+#include "periph_cpu.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -139,6 +141,29 @@ extern "C" {
/** @} */
+/**
+ * @name SD/MMC host controller configuration
+ * @{
+ */
+
+/** SDMMC devices */
+static const sdmmc_conf_t sdmmc_config[] = {
+ {
+ .slot = SDMMC_SLOT_1,
+ .cd = GPIO_UNDEF,
+ .wp = GPIO_UNDEF,
+#if MODULE_OLIMEX_ESP32_GATEWAY
+ .bus_width = 4,
+#else
+ .bus_width = 1,
+#endif
+ },
+};
+
+/** Number of configured SDMMC devices */
+#define SDMMC_CONFIG_NUMOF 1
+/** @} */
+
/**
* @name SPI configuration
* @{
@@ -147,12 +172,7 @@ extern "C" {
* @brief HSPI is used as SPI_DEV(0)
*
* It is available at the [UEXT] connector on Olimex ESP32-EVB.
- *
- * Although the SD card interface of the Olimex ESP32-EVB is also available at
- * the `SPI_DEV(0)` interface, it does not have a CS signal. Therefore,
- * it cannot be used in SPI mode with the `sdcard_spi` module. Olimex
- * ESP32-GATEWAY uses the integrated SD card interface with another GPIO for
- * the CS signal.
+ * If the SD Card/MMC interface is used, the SPI interface is not available.
*
* @note The GPIOs listed in the configuration are first initialized as SPI
* signals when the corresponding SPI interface is used for the first time
@@ -160,6 +180,8 @@ extern "C" {
* function. That is, they are not allocated as SPI signals before and can
* be used for other purposes as long as the SPI interface is not used.
*/
+#if !MODULE_PERIPH_SDMMC && !MODULE_OLIMEX_ESP32_GATEWAY
+
#ifndef SPI0_CTRL
#define SPI0_CTRL HSPI
#endif
@@ -173,14 +195,11 @@ extern "C" {
#ifndef SPI0_MOSI
#define SPI0_MOSI GPIO15 /**< MOSI [UEXT] / SD Card interface] */
#endif
-
#ifndef SPI0_CS0
-#ifndef MODULE_OLIMEX_ESP32_GATEWAY
#define SPI0_CS0 GPIO17 /**< CS0 [UEXT] */
-#else /* MODULE_OLIMEX_ESP32_GATEWAY */
-#define SPI0_CS0 GPIO13 /**< CS0 SD Card interface */
-#endif /* MODULE_OLIMEX_ESP32_GATEWAY */
-#endif /* SPI0_CS0 */
+#endif
+
+#endif /* !MODULE_PERIPH_SDMMC && !MODULE_OLIMEX_ESP32_GATEWAY */
/** @} */
@@ -201,12 +220,16 @@ extern "C" {
#define UART0_TXD GPIO1 /**< direct I/O pin for UART_DEV(0), can't be changed */
#define UART0_RXD GPIO3 /**< direct I/O pin for UART_DEV(0), can't be changed */
+#if !MODULE_OLIMEX_ESP32_GATEWAY
+
#ifndef UART1_TXD
#define UART1_TXD GPIO4 /**< UART_DEV(1) TxD */
#endif
#ifndef UART1_RXD
#define UART1_RXD GPIO36 /**< UART_DEV(1) RxD */
#endif
+
+#endif /* !MODULE_OLIMEX_ESP32_GATEWAY */
/** @} */
#ifdef __cplusplus
diff --git a/boards/esp32-wrover-kit/Kconfig b/boards/esp32-wrover-kit/Kconfig
index e19495d7efe0..c9e7748ae721 100644
--- a/boards/esp32-wrover-kit/Kconfig
+++ b/boards/esp32-wrover-kit/Kconfig
@@ -18,11 +18,13 @@ config BOARD_ESP32_WROVER_KIT
select HAS_PERIPH_ADC
select HAS_PERIPH_I2C
select HAS_PERIPH_PWM
+ select HAS_PERIPH_SDMMC
select HAS_PERIPH_SPI
select HAS_SDCARD_SPI
select HAVE_ILI9341
- select HAVE_MTD_SDCARD_DEFAULT
+ select HAVE_MTD_SDMMC_DEFAULT if !MODULE_SDCARD_SPI
+
select MODULE_FATFS_VFS if MODULE_VFS_DEFAULT
source "$(RIOTBOARD)/common/esp32/Kconfig"
diff --git a/boards/esp32-wrover-kit/Makefile.dep b/boards/esp32-wrover-kit/Makefile.dep
index 11982c736c00..5c610ad04811 100644
--- a/boards/esp32-wrover-kit/Makefile.dep
+++ b/boards/esp32-wrover-kit/Makefile.dep
@@ -5,14 +5,17 @@ endif
# Sets up configuration for openocd
USEMODULE += esp_jtag
-ifneq (,$(filter mtd,$(USEMODULE)))
- USEMODULE += mtd_sdcard_default
-endif
-
# default to using fatfs on SD card
ifneq (,$(filter vfs_default,$(USEMODULE)))
USEMODULE += fatfs_vfs
USEMODULE += mtd
endif
+ifneq (,$(filter mtd,$(USEMODULE)))
+ ifeq (,$(filter sdcard_spi,$(USEMODULE)))
+ # use mtd_sdmmc_default if sdcard_spi isn't explicitly enabled
+ USEMODULE += mtd_sdmmc_default
+ endif
+endif
+
include $(RIOTBOARD)/common/esp32/Makefile.dep
diff --git a/boards/esp32-wrover-kit/Makefile.features b/boards/esp32-wrover-kit/Makefile.features
index c93ac66276b4..2f8181395a28 100644
--- a/boards/esp32-wrover-kit/Makefile.features
+++ b/boards/esp32-wrover-kit/Makefile.features
@@ -7,6 +7,7 @@ include $(RIOTBOARD)/common/esp32/Makefile.features
FEATURES_PROVIDED += periph_adc
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
+FEATURES_PROVIDED += periph_sdmmc
FEATURES_PROVIDED += periph_spi
# unique features provided by the board
diff --git a/boards/esp32-wrover-kit/doc.txt b/boards/esp32-wrover-kit/doc.txt
index b74766eb9b7a..22bb6593427c 100644
--- a/boards/esp32-wrover-kit/doc.txt
+++ b/boards/esp32-wrover-kit/doc.txt
@@ -83,7 +83,7 @@ configuration can be overridden by
These abbreviations are used in subsequent tables:
-*SDC* = SD-Card interface is used (module **sdcard_spi** is enabled)\n
+*SDC* = SD-Card interface is used (module **periph_sdmmc** is enabled)\n
*CAM* = Camera is plugged in/used
@@ -95,12 +95,18 @@ These abbreviations are used in subsequent tables:
| `ADC_LINE(2)` | `GPIO36` | `GPIO36` | - | - | `CAMERA_D4` | \ref esp32_adc_channels |
| `ADC_LINE(3)` | `GPIO39` | `GPIO39` | - | - | `CAMERA_D5` | \ref esp32_adc_channels |
| `PWM_DEV(0):0 / LED0` | `GPIO0` | `GPIO0` | - | - | `LED_RED` / `CAMERA_RESET` | \ref esp32_pwm_channels |
-| `PWM_DEV(0):2 / LED2` | `GPIO4` | `GPIO4` | - | - | `LED_BLUE` / `CAMERA_D0` | \ref esp32_pwm_channels |
+| `PWM_DEV(0):1 / LED2` | `GPIO4` | `GPIO4` | - | - | `LED_BLUE` / `CAMERA_D0` | \ref esp32_pwm_channels |
| `LED1` | `GPIO2` | `GPIO2` | `GPIO2` | `GPIO2` | `LED_GREEN` | |
| `I2C_DEV(0):SCL` | `GPIO27` | `GPIO27` | `GPIO27` | `GPIO27` | `CAMERA_SIO_C` | \ref esp32_i2c_interfaces |
-| `I2C_DEV(0):SDA` | `GPIO26` | `GPIO26` | `GPIO26` | `GPIO27` | `CAMERA_SIO_D` | \ref esp32_i2c_interfaces |
+| `I2C_DEV(0):SDA` | `GPIO26` | `GPIO26` | `GPIO26` | `GPIO26` | `CAMERA_SIO_D` | \ref esp32_i2c_interfaces |
| `UART_DEV(0):TX` | `GPIO1` | `GPIO1` | `GPIO1` | `GPIO1` | | \ref esp32_uart_interfaces |
| `UART_DEV(0):RX` | `GPIO3` | `GPIO3` | `GPIO3` | `GPIO3` | | \ref esp32_uart_interfaces |
+| `SDMMC_DEV(0):CLK` | `GPIO14` | `GPIO14` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
+| `SDMMC_DEV(0):CMD` | `GPIO15` | `GPIO` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
+| `SDMMC_DEV(0):DAT0` | `GPIO2` | `GPIO2` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
+| `SDMMC_DEV(0):DAT1` | `GPIO4` | `GPIO4` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
+| `SDMMC_DEV(0):DAT2` | `GPIO12` | `GPIO` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
+| `SDMMC_DEV(0):DAT3` | `GPIO13` | `GPIO` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
| `SPI_DEV(0):SCK` | `GPIO14` | `GPIO14` | `GPIO14` | `GPIO14` | HSPI: SD-Card / Peripherals | \ref esp32_spi_interfaces |
| `SPI_DEV(0):MOSI` | `GPIO15` | `GPIO15` | `GPIO15` | `GPIO15` | HSPI: SD-Card / Peripherals | \ref esp32_spi_interfaces |
| `SPI_DEV(0):CS0` | `GPIO13` | `GPIO13` | `GPIO13` | `GPIO13` | HSPI: SD-Card CS | \ref esp32_spi_interfaces |
@@ -133,17 +139,18 @@ These abbreviations are used in subsequent tables:
-Following table shows the default board configuration sorted by GPIOs.
+Following table shows the default board configuration sorted by GPIOs depending
+on used hardware.
-| Pin | None | SDC | CAM | SDC+CAM | Remarks |
+| Pin | None | SDC 4-bit | CAM | SDC 1-bit + CAM | Remarks |
|:-------|:-----------------------|:--------------------|:--------------------|:---------------------------|:-----|
| GPIO0 | PWM_DEV(0):0 / LED0 | PWM_DEV(0):0 / LED0 | CAMERA_RESET | CAMERA_RESET | |
| GPIO1 | UART_DEV(0):TX | UART_DEV(0):TX | UART_DEV(0):TX | UART_DEV(0):TX | |
-| GPIO2 | SPI_DEV(0):MISO / LED1 | SPI_DEV(0):MISO | SPI_DEV(0):MISO | SPI_DEV(0):MISO | HSPI |
+| GPIO2 | SPI_DEV(0):MISO / LED1 | SDMMC_DEV(0):DAT0 | SPI_DEV(0):MISO | SDMMC_DEV(0):DAT0 | HSPI |
| GPIO3 | UART_DEV(0):RX | UART_DEV(0):RX | UART_DEV(0):RX | UART_DEV(0):RX | |
-| GPIO4 | PWM_DEV(0):1 / LED2 | PWM_DEV(0):1 / LED2 | CAMERA_D0 | CAMERA_D0 | |
+| GPIO4 | PWM_DEV(0):1 / LED2 | SDMMC_DEV(0):DAT1 | CAMERA_D0 | CAMERA_D0 | |
| GPIO5 | LCD LED | LCD_LED | CAMERA_D1 | CAMERA_D1 | |
| GPIO6 | Flash CLK | Flash CLK | Flash CLK | Flash CLK | |
| GPIO7 | Flash SD0 | Flash SD0 | Flash SD0 | Flash SD0 | |
@@ -151,10 +158,10 @@ Following table shows the default board configuration sorted by GPIOs.
| GPIO9 | | | | | |
| GPIO10 | | | | | |
| GPIO11 | Flash CMD | Flash CMD | Flash CMD | Flash CMD | |
-| GPIO12 | | | | | |
-| GPIO13 | SPI_DEV(0):CS0 | SPI_DEV(0):CS0 | SPI_DEV(0):CS0 | SPI_DEV(0):CS0 | HSPI / SD-Card CS |
-| GPIO14 | SPI_DEV(0):SCK | SPI_DEV(0):SCK | SPI_DEV(0):SCK | SPI_DEV(0):SCK | HSPI |
-| GPIO15 | SPI_DEV(0):MOSI | SPI_DEV(0):MOSI | SPI_DEV(0):MOSI | SPI_DEV(0):MOSI | HSPI |
+| GPIO12 | | SDMMC_DEV(0):DAT2 | | | |
+| GPIO13 | SPI_DEV(0):CS0 | SDMMC_DEV(0):DAT3 | SPI_DEV(0):CS0 | | HSPI / SPI SD-Card CS |
+| GPIO14 | SPI_DEV(0):SCK | SDMMC_DEV(0):CLK | SPI_DEV(0):SCK | | HSPI |
+| GPIO15 | SPI_DEV(0):MOSI | SDMMC_DEV(0):CMD | SPI_DEV(0):MOSI | | HSPI |
| GPIO16 | N/A | N/A | N/A | N/A | see below |
| GPIO17 | N/A | N/A | N/A | N/A | see below |
| GPIO18 | LCD_RESET | LCD_RESET | LCD_RESET | CAMERA_D2 | |
diff --git a/boards/esp32-wrover-kit/include/periph_conf.h b/boards/esp32-wrover-kit/include/periph_conf.h
index 2899d47dfa80..9ba9cb2d608f 100644
--- a/boards/esp32-wrover-kit/include/periph_conf.h
+++ b/boards/esp32-wrover-kit/include/periph_conf.h
@@ -46,6 +46,7 @@
#define PERIPH_CONF_H
#include
+#include "periph_cpu.h"
#ifdef __cplusplus
extern "C" {
@@ -109,10 +110,6 @@
*
* LEDs are used as PWM channels for device PWM_DEV(0).
*
- * @note As long as the according PWM device is not initialized with function
- * pwm_init, the GPIOs declared for this device can be used for other
- * purposes.
- *
* @note As long as the according PWM device is not initialized with
* the `pwm_init`, the GPIOs declared for this device can be used
* for other purposes.
@@ -120,8 +117,10 @@
* @{
*/
#ifndef PWM0_GPIOS
-#if !MODULE_ESP32_WROVER_KIT_CAMERA || DOXYGEN
+#if (!MODULE_ESP32_WROVER_KIT_CAMERA && !MODULE_PERIPH_SDMMC) || DOXYGEN
#define PWM0_GPIOS { GPIO0, GPIO4 } /**< only available when camera is not connected */
+#elif !MODULE_ESP32_WROVER_KIT_CAMERA
+#define PWM0_GPIOS { GPIO0 }
#else
#define PWM0_GPIOS { }
#endif
@@ -129,6 +128,33 @@
/** @} */
+/**
+ * @name SD/MMC host controller configuration
+ *
+ * @warning If the camera is plugged in, the SD Card has to be used in
+ * 1-bit mode.
+ * @{
+ */
+
+/** SDMMC devices */
+static const sdmmc_conf_t sdmmc_config[] = {
+ {
+ .slot = SDMMC_SLOT_1,
+ .cd = GPIO21,
+ .wp = GPIO_UNDEF,
+#if MODULE_ESP32_WROVER_KIT_CAMERA
+ /* if camera used, only DAT0 is available */
+ .bus_width = 1,
+#else
+ .bus_width = 4,
+#endif
+ },
+};
+
+/** Number of configured SDMMC devices */
+#define SDMMC_CONFIG_NUMOF 1
+/** @} */
+
/**
* @name SPI configuration
*
@@ -137,6 +163,12 @@
* HSPI is always available and therefore used as SPI_DEV(0)
* VSPI is only available when the camera is not plugged.
*
+ * @warning In order not to change the index of the SPI devices depending on
+ * the different hardware configuration options including the camera,
+ * SPI_DEV(0) is also defined in case of using the SD/MMC host
+ * controller, by default but cannot be used once an SD card is
+ * inserted. Use SPI_DEV(1) instead in this case.
+ *
* @{
*/
diff --git a/cpu/esp32/Kconfig.esp32 b/cpu/esp32/Kconfig.esp32
index cd2ef71806c7..c5da0ee870e6 100644
--- a/cpu/esp32/Kconfig.esp32
+++ b/cpu/esp32/Kconfig.esp32
@@ -14,6 +14,9 @@ config CPU_FAM_ESP32
select HAS_ESP_BLE
select HAS_ESP_BLE_ESP32
select HAS_PUF_SRAM
+ select HAS_PERIPH_SDMMC_AUTO_CLK
+ select HAS_PERIPH_SDMMC_AUTO_CMD12
+ select HAS_PERIPH_SDMMC_MMC
config CPU_FAM
default "esp32" if CPU_FAM_ESP32
diff --git a/cpu/esp32/Kconfig.esp32s3 b/cpu/esp32/Kconfig.esp32s3
index a0787460b907..0af20b4e9616 100644
--- a/cpu/esp32/Kconfig.esp32s3
+++ b/cpu/esp32/Kconfig.esp32s3
@@ -15,6 +15,10 @@ config CPU_FAM_ESP32S3
select HAS_BLE_PHY_2MBIT
select HAS_ESP_BLE
select HAS_ESP_BLE_ESP32C3
+ select HAS_PERIPH_SDMMC_AUTO_CLK
+ select HAS_PERIPH_SDMMC_AUTO_CMD12
+ select HAS_PERIPH_SDMMC_HS
+ select HAS_PERIPH_SDMMC_MMC
select MODULE_USBDEV_SYNOPSYS_DWC2 if MODULE_PERIPH_USBDEV
select MODULE_USB_BOARD_RESET if MODULE_USBUS_CDC_ACM || MODULE_TINYUSB_CLASS_CDC
diff --git a/cpu/esp32/Kconfig.esp32x b/cpu/esp32/Kconfig.esp32x
index 60f735cf6ecd..bbee9fd493e1 100644
--- a/cpu/esp32/Kconfig.esp32x
+++ b/cpu/esp32/Kconfig.esp32x
@@ -26,6 +26,7 @@ config CPU_COMMON_ESP32X
select PACKAGE_ESP32_SDK if TEST_KCONFIG
+ select MODULE_PERIPH_GPIO_IRQ if MODULE_PERIPH_SDMMC
select MODULE_PERIPH_RTT if HAS_PERIPH_RTT && MODULE_PM_LAYERED
select MODULE_PS if MODULE_SHELL
select MODULE_PTHREAD if MODULE_CPP
diff --git a/cpu/esp32/Makefile.dep b/cpu/esp32/Makefile.dep
index ca2556901e5e..9d4b62ab470a 100644
--- a/cpu/esp32/Makefile.dep
+++ b/cpu/esp32/Makefile.dep
@@ -105,6 +105,11 @@ ifneq (,$(filter periph_i2c,$(USEMODULE)))
endif
endif
+ifneq (,$(filter periph_sdmmc,$(USEMODULE)))
+ USEMODULE += esp_idf_sdmmc
+ USEMODULE += periph_gpio_irq
+endif
+
ifneq (,$(filter esp_spi_ram,$(USEMODULE)))
FEATURES_REQUIRED += esp_spi_ram
FEATURES_OPTIONAL += esp_spi_oct
diff --git a/cpu/esp32/Makefile.features b/cpu/esp32/Makefile.features
index 1e87ecbcc817..dcc8dba6be9b 100644
--- a/cpu/esp32/Makefile.features
+++ b/cpu/esp32/Makefile.features
@@ -46,6 +46,15 @@ else ifneq (,$(filter esp32c3 esp32s3,$(CPU_FAM)))
FEATURES_PROVIDED += esp_ble_esp32c3
endif
+ifneq (,$(filter esp32 esp32s3,$(CPU_FAM)))
+ FEATURES_PROVIDED += periph_sdmmc_auto_clk
+ FEATURES_PROVIDED += periph_sdmmc_auto_cmd12
+ FEATURES_PROVIDED += periph_sdmmc_mmc
+ ifeq (esp32s3,$(CPU_FAM))
+ FEATURES_PROVIDED += periph_sdmmc_hs
+ endif
+endif
+
ifneq (,$(filter esp32-wrover% esp32s2%r2 esp32s3%r2 esp32s3%r8 esp32s3%r8v,$(CPU_MODEL)))
FEATURES_PROVIDED += esp_spi_ram
ifneq (,$(filter esp32s3%r8 esp32s3%r8v,$(CPU_MODEL)))
diff --git a/cpu/esp32/doc.txt b/cpu/esp32/doc.txt
index c1225a13521e..66c5439586f1 100644
--- a/cpu/esp32/doc.txt
+++ b/cpu/esp32/doc.txt
@@ -55,13 +55,14 @@ This document describes the RIOT implementation for supported variants
3. [DAC Channels](#esp32_dac_channels)
4. [I2C Interfaces](#esp32_i2c_interfaces)
5. [PWM Channels](#esp32_pwm_channels)
- 6. [SPI Interfaces](#esp32_spi_interfaces)
- 7. [Timers](#esp32_timers)
- 8. [RTT Implementation](#esp32_rtt_counter)
- 9. [UART Interfaces](#esp32_uart_interfaces)
- 10. [CAN Interfaces](#esp32_can_interfaces)
- 11. [Power Management](#esp32_power_management)
- 12. [Other Peripherals](#esp32_other_peripherals)
+ 6. [SDMMC Interfaces](#esp32_sdmmc_interfaces)
+ 8. [SPI Interfaces](#esp32_spi_interfaces)
+ 9. [Timers](#esp32_timers)
+ 10. [RTT Implementation](#esp32_rtt_counter)
+ 12. [UART Interfaces](#esp32_uart_interfaces)
+ 13. [CAN Interfaces](#esp32_can_interfaces)
+ 14. [Power Management](#esp32_power_management)
+ 15. [Other Peripherals](#esp32_other_peripherals)
7. [Special On-board Peripherals](#esp32_special_on_board_peripherals)
1. [SPI RAM Modules](#esp32_spi_ram)
2. [SPIFFS Device](#esp32_spiffs_device)
@@ -212,13 +213,14 @@ The key features of ESP32 are:
| Flash | 512 KiB ... 16 MiB | yes |
| Frequency | 240 MHz, 160 MHz, 80 MHz | yes |
| Power Consumption | 68 mA @ 240 MHz
44 mA @ 160 MHz (34 mA @ 160 MHz single core)
31 mA @ 80 MHz (25 mA @ 80 MHz single core)
800 uA in light sleep mode
10 uA in deep sleep mode | yes
yes
yes
yes
yes |
-| Timers | 4 x 64 bit | yes |
-| ADCs | 2 x SAR-ADC with up to 18 x 12 bit channels total | yes |
-| DACs | 2 x DAC with 8 bit | yes |
-| GPIOs | 34 (6 are only inputs, 18 are RTC GPIOs) | yes |
-| I2Cs | 2 | yes |
-| SPIs | 4 | yes (2) |
-| UARTs | 3 | yes |
+| Timer | 4 x 64 bit | yes |
+| ADC | 2 x SAR-ADC with up to 18 x 12 bit channels total | yes |
+| DAC | 2 x DAC with 8 bit | yes |
+| GPIO | 34 (6 are only inputs, 18 are RTC GPIOs) | yes |
+| I2C | 2 | yes |
+| SDMMC | 2 | yes |
+| SPI | 4 | yes (2) |
+| UART | 3 | yes |
| WiFi | IEEE 802.11 b/g/n built in | yes |
| Bluetooth | v4.2 BR/EDR and BLE | yes |
| Ethernet | MAC interface with dedicated DMA and IEEE 1588 support | yes |
@@ -248,13 +250,13 @@ The key features of ESP32-C3 are:
| Flash | 8 MiB | yes |
| Frequency | 160 MHz, 80 MHz | yes |
| Power Consumption | 20 mA @ 240 MHz
15 mA @ 80 MHz
130 uA in light sleep mode
5 uA in deep sleep mode | yes
yes
yes
yes |
-| Timers | 2 x 54 bit | yes |
-| ADCs | 2 x SAR-ADC with up to 6 x 12 bit channels total | yes |
-| DACs | - | - |
-| GPIOs | 22 | yes |
-| I2Cs | 1 | yes |
-| SPIs | 3 | yes (1) |
-| UARTs | 2 | yes |
+| Timer | 2 x 54 bit | yes |
+| ADC | 2 x SAR-ADC with up to 6 x 12 bit channels total | yes |
+| DAC | - | - |
+| GPIO | 22 | yes |
+| I2C | 1 | yes |
+| SPI | 3 | yes (1) |
+| UART | 2 | yes |
| WiFi | IEEE 802.11 b/g/n built in | yes |
| Bluetooth | Bluetooth 5 (LE) | yes |
| Ethernet | - | - |
@@ -284,13 +286,13 @@ The key features of ESP32-S2 are:
| Flash | 512 KiB ... 32 MiB Dual/Quad/Octal SPI (external or internal) | yes |
| Frequency | 240 MHz, 160 MHz, 80 MHz | yes |
| Power Consumption | 66 mA @ 240 MHz
50 mA @ 160 MHz (40 mA @ 160 MHz single core)
33 mA @ 80 MHz (28 mA @ 80 MHz single core)
19 mA @ 40 MHz (16 mA @ 40 MHz single core)
240 uA in light sleep mode
8 uA in deep sleep mode | yes
yes
yes
yes
yes
yes |
-| Timers | 4 x 54 bit | yes |
-| ADCs | 2 x SAR-ADC with up to 20 x 13 bit channels total | yes |
-| DACs | 2 x DAC with 8 bit | - |
-| GPIOs | 43 (22 are RTC GPIOs) | yes |
-| I2Cs | 2 | yes |
-| SPIs | 4 | yes (2) |
-| UARTs | 2 | yes |
+| Timer | 4 x 54 bit | yes |
+| ADC | 2 x SAR-ADC with up to 20 x 13 bit channels total | yes |
+| DAC | 2 x DAC with 8 bit | - |
+| GPIO | 43 (22 are RTC GPIOs) | yes |
+| I2C | 2 | yes |
+| SPI | 4 | yes (2) |
+| UART | 2 | yes |
| WiFi | IEEE 802.11 b/g/n built in | yes |
| Bluetooth | - | - |
| Ethernet | - | - |
@@ -320,13 +322,14 @@ The key features of ESP32-S3 are:
| Flash | 512 KiB ... 32 MiB Dual/Quad/Octal SPI (external or internal) | yes |
| Frequency | 240 MHz, 160 MHz, 80 MHz | yes |
| Power Consumption | 66 mA @ 240 MHz
50 mA @ 160 MHz (40 mA @ 160 MHz single core)
33 mA @ 80 MHz (28 mA @ 80 MHz single core)
19 mA @ 40 MHz (16 mA @ 40 MHz single core)
240 uA in light sleep mode
8 uA in deep sleep mode | yes
yes
yes
yes
yes
yes |
-| Timers | 4 x 54 bit | yes |
-| ADCs | 2 x SAR-ADC with up to 20 x 12 bit channels total | yes |
-| DACs | - | - |
-| GPIOs | 45 (22 are RTC GPIOs) | yes |
-| I2Cs | 2 | yes |
-| SPIs | 4 | yes (2) |
-| UARTs | 3 | yes |
+| Timer | 4 x 54 bit | yes |
+| ADC | 2 x SAR-ADC with up to 20 x 12 bit channels total | yes |
+| DAC | - | - |
+| GPIO | 45 (22 are RTC GPIOs) | yes |
+| I2C | 2 | yes |
+| SDMMC | 2 | yes |
+| SPI | 4 | yes (2) |
+| UART | 3 | yes |
| WiFi | IEEE 802.11 b/g/n built in | yes |
| Bluetooth | Bluetooth 5 (LE) | yes |
| Ethernet | - | - |
@@ -1012,6 +1015,62 @@ definition of `PWM0_GPIOS`, `PWM1_GPIOS`, `PWM2_GPIOS` and `PWM3_GPIOS`.
[Back to table of contents](#esp32_toc)
+## SDMMC Interfaces {#esp32_sdmmc_interfaces}
+
+ESP32 and ESP32-S3 variants integrate a SD/MMC host controller which supports
+two slots for
+
+- SD Memory Cards,
+- SDIO Cards and
+- MMC/eMMCs.
+
+The SD/MMC host controller on the ESP32 variant
+
+- supports 1-bit, 4-bit and 8-bit data bus width for slot 0 (@ref SDMMC_SLOT_0),
+- supports 1-bit and 4-bit data bus width for slot 1 (@ref SDMMC_SLOT_1),
+- uses direct I/O for SD/MMC signals so that the GPIOs used for the slots are
+ therefore fixed.
+
+The SD/MMC host controller on the ESP32-S3 variant
+- supports 1-, 4- and 8-bit data bus width for both slots
+- uses the GPIO matrix to connect the SD/MMC signals to the GPIOs so that
+ all pins are configurable.
+
+@note Since the GPIOs are fixed on the ESP32 variant and the same GPIOs are
+used for slot 0 and the flash, slot 0 cannot be used on ESP32 variant.
+
+The board-specific configuration is realized by defining the array
+@ref sdmmc_config
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+static const sdmmc_conf_t sdmmc_config[] = {
+ {
+ .slot = SDMMC_SLOT_1,
+ .cd = GPIO_UNDEF,
+ .wp = GPIO_UNDEF,
+ ...
+ },
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+and the macro `SDMMC_NUMOF`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+#define SDMMC_NUMOF 1
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+where the value of @ref SDMMC_NUMOF must correspond to the number of elements
+in @ref sdmmc_config.
+
+While for the ESP32 variant it is sufficient to define the data bus width, used
+GPIOs have to be defined in the configuration for the ESP32-S3 variant instead.
+For details of ESP32x variant specific configuration, see:
+
+- \ref esp32_sdmmc_interfaces_esp32 "ESP32"
+- \ref esp32_sdmmc_interfaces_esp32s3 "ESP32-S3"
+
+If the board supports a Card Detect pin or a Write Protect pin, the
+corresponding GPIOs have to be defined in @ref sdmmc_conf_t::cd and
+@ref sdmmc_conf_t::wp. Otherwise they have to be set to undefined
+(@ref GPIO_UNDEF).
+
## SPI Interfaces {#esp32_spi_interfaces}
ESP32x SoCs have up to four SPI controllers dependent on the specific ESP32x
diff --git a/cpu/esp32/doc_esp32.txt b/cpu/esp32/doc_esp32.txt
index d533594e044a..3cb89d508d6b 100644
--- a/cpu/esp32/doc_esp32.txt
+++ b/cpu/esp32/doc_esp32.txt
@@ -139,6 +139,52 @@ I2C_DEV(0) | SDA | GPIO21 | `#I2C0_SDA` | -
The ESP32 LEDC module has 2 channel groups with 8 channels each. Each of
these channels can be clocked by one of the 4 timers.
+## SDMMC Interfaces {#esp32_sdmmc_interfaces_esp32}
+
+The ESP32 variant uses the direct I/O (i.e. `SOC_SDMMC_USE_IOMUX` is defined in
+the SoC capabilities file). The GPIOs used for SDMMC signals are therefore fixed
+for each slot. Since the GPIOs used for slot 0 are the same as those used
+for the Flash, slot 0 cannot be used. Therefore, only slot 1 can be used.
+
+The GPIOs used by ESP32 for slot 1 are:
+
+
+| Signal | GPIO Slot 1 |
+|:------ |:------------|
+| CLK | GPIO14 |
+| CMD | GPIO15 |
+| DAT0 | GPIO2 |
+| DAT1 | GPIO4 |
+| DAT2 | GPIO12 |
+| DAT3 | GPIO13 |
+
+
+The board-specific configuration is realized by defining the @ref sdmmc_config
+array, for example:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+static const sdmmc_conf_t sdmmc_config[] = {
+ {
+ .slot = SDMMC_SLOT_1,
+ .cd = GPIO21,
+ .wp = GPIO_UNDEF,
+ .bus_width = 1,
+ },
+};
+
+#define SDMMC_NUMOF 1
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Used data bus width has to be defined in sdmmc_conf_t::bus_width in addition
+to the slot, where 1 and 4 are valid values.
+
+@note The slot must be @ref SDMMC_SLOT_1 for ESP32.
+
+If the board supports a Card Detect pin or a Write Protect
+pin, the corresponding GPIOs have to be defined in @ref sdmmc_conf_t::cd and
+@ref sdmmc_conf_t::wp. Otherwise they have to be set to undefined
+(@ref GPIO_UNDEF).
+
## SPI Interfaces {#esp32_spi_interfaces_esp32}
ESP32 has four SPI controllers where SPI0 and SPI1 share the same bus and
diff --git a/cpu/esp32/doc_esp32s3.txt b/cpu/esp32/doc_esp32s3.txt
index d44ee143b030..9661713cdc81 100644
--- a/cpu/esp32/doc_esp32s3.txt
+++ b/cpu/esp32/doc_esp32s3.txt
@@ -209,6 +209,54 @@ I2C_DEV(0) | SDA | GPIO8 | `#I2C0_SDA` | -
The ESP32-S3 LEDC module has 1 channel group with 8 channels. Each of
these channels can be clocked by one of the 4 timers.
+## SDMMC Interfaces {#esp32_sdmmc_interfaces_esp32s3}
+
+The ESP32-S3 variant uses the GPIO matrix (i.e. `SOC_SDMMC_USE_GPIO_MATRIX`
+is defined in the SoC Capabilities file) to route the SDMMC signals to
+arbitrary pins. The GPIOs used for the SDMMC signals are therefore
+configurable and have to be defined in the board-specific configuration in
+array @ref sdmmc_config in addition to the used slot.
+
+The width of the data bus used is determined by the GPIOs defined for the
+DAT lines. To use a 1-bit data bus, only DAT0 (@ref sdmmc_conf_t::dat0)
+must be defined. All other GPIOs for the DAT lines must be set undefined
+(@ref GPIO_UNDEF). For a 4-bit data bus, the GPIOs for pins DAT1 to DAT3
+(@ref sdmmc_conf_t::dat1 ... @ref sdmmc_conf_t::dat3) must also be defined.
+An 8-bit data bus width requires the definition of DAT4 to DAT7
+(@ref sdmmc_conf_t::dat4 ... @ref sdmmc_conf_t::dat7) and the enabling
+of the `periph_sdmmc_8bit` module.
+
+The following example shows a configuration with 4-bit or 8-bit data
+bus width dependent on whether the `periph_sdmmc_8bit` module is enabled.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+static const sdmmc_conf_t sdmmc_config[] = {
+ {
+ .slot = SDMMC_SLOT_0,
+ .cd = GPIO16,
+ .wp = GPIO_UNDEF,
+ .clk = GPIO14,
+ .cmd = GPIO15,
+ .dat0 = GPIO2,
+ .dat1 = GPIO4,
+ .dat2 = GPIO12,
+ .dat3 = GPIO13,
+#if IS_USED(MODULE_PERIPH_SMMC_8BIT)
+ .dat4 = GPIO33,
+ .dat5 = GPIO33,
+ .dat6 = GPIO33,
+ .dat7 = GPIO33,
+#endif
+ },
+};
+
+#define SDMMC_NUMOF 1
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If the board supports a Card Detect pin or a Write Protect pin, the
+corresponding GPIOs have to be defined in @ref sdmmc_conf_t::cd and
+@ref sdmmc_conf_t::wp. Otherwise they have to be set to undefined
+(@ref GPIO_UNDEF).
+
## SPI Interfaces {#esp32_spi_interfaces_esp32s3}
ESP32-S3 has four SPI controllers where SPI0 and SPI1 share the same bus
diff --git a/cpu/esp32/esp-idf/Kconfig b/cpu/esp32/esp-idf/Kconfig
index 1967cb12dc37..d76951ae574f 100644
--- a/cpu/esp32/esp-idf/Kconfig
+++ b/cpu/esp32/esp-idf/Kconfig
@@ -29,6 +29,7 @@ rsource "heap/Kconfig"
rsource "lcd/Kconfig"
rsource "nvs_flash/Kconfig"
rsource "rmt/Kconfig"
+rsource "sdmmc/Kconfig"
rsource "spi_flash/Kconfig"
rsource "spi_ram/Kconfig"
rsource "usb/Kconfig"
diff --git a/cpu/esp32/esp-idf/Makefile b/cpu/esp32/esp-idf/Makefile
index 47090883ead9..7f5fe8c47c55 100644
--- a/cpu/esp32/esp-idf/Makefile
+++ b/cpu/esp32/esp-idf/Makefile
@@ -48,6 +48,10 @@ ifneq (,$(filter esp_idf_rmt,$(USEMODULE)))
DIRS += rmt
endif
+ifneq (,$(filter esp_idf_sdmmc,$(USEMODULE)))
+ DIRS += sdmmc
+endif
+
ifneq (,$(filter esp_idf_spi_flash,$(USEMODULE)))
DIRS += spi_flash
endif
diff --git a/cpu/esp32/esp-idf/sdmmc/Kconfig b/cpu/esp32/esp-idf/sdmmc/Kconfig
new file mode 100644
index 000000000000..d3bbea952bf8
--- /dev/null
+++ b/cpu/esp32/esp-idf/sdmmc/Kconfig
@@ -0,0 +1,17 @@
+# Copyright (c) 2021 HAW Hamburg
+# 2022 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.
+#
+
+config MODULE_ESP_IDF_SDMMC
+ bool
+ depends on TEST_KCONFIG
+ depends on MODULE_ESP_IDF
+ default y if MODULE_PERIPH_SDMMC
+ select PACKAGE_TLSF
+ help
+ ESP-IDF heap library. This library is required if external SPI RAM
+ or the WiFi interface is used.
diff --git a/cpu/esp32/esp-idf/sdmmc/Makefile b/cpu/esp32/esp-idf/sdmmc/Makefile
new file mode 100644
index 000000000000..6ff7663f0fbf
--- /dev/null
+++ b/cpu/esp32/esp-idf/sdmmc/Makefile
@@ -0,0 +1,18 @@
+MODULE = esp_idf_sdmmc
+
+# source files to be compiled for this module
+ESP32_SDK_SRC = \
+ components/driver/sdmmc_host.c \
+ components/driver/sdmmc_transaction.c \
+ components/soc/$(CPU_FAM)/sdmmc_periph.c \
+ #
+
+# additional include pathes required by this module
+# INCLUDES += -I$(ESP32_SDK_DIR)/components/driver/include
+
+include $(RIOTBASE)/Makefile.base
+
+ESP32_SDK_BIN = $(BINDIR)/$(MODULE)
+
+include ../esp_idf.mk
+include ../esp_idf_cflags.mk
diff --git a/cpu/esp32/include/irq_arch.h b/cpu/esp32/include/irq_arch.h
index da7c759a7b5a..8d54971a8ccf 100644
--- a/cpu/esp32/include/irq_arch.h
+++ b/cpu/esp32/include/irq_arch.h
@@ -52,6 +52,7 @@ extern "C" {
#define CPU_INUM_FRC2 20 /**< Level interrupt with medium priority 2 */
#define CPU_INUM_SYSTIMER 20 /**< Level interrupt with medium priority 2 */
#define CPU_INUM_BLE 21 /**< Level interrupt with medium priority 2 */
+#define CPU_INUM_SDMMC 23 /**< Level interrupt with medium priority 2 */
#define CPU_INUM_CACHEERR 25 /**< Level interrupt with high priority 4 */
/** @} */
diff --git a/cpu/esp32/include/periph_cpu.h b/cpu/esp32/include/periph_cpu.h
index fe2aaeea3139..6e0623533fe5 100644
--- a/cpu/esp32/include/periph_cpu.h
+++ b/cpu/esp32/include/periph_cpu.h
@@ -604,6 +604,82 @@ typedef struct {
/** @} */
+/**
+ * @name SDMMC configuration
+ *
+ * ESP32x SoC with SDMMC peripheral provide two SDMMC interfaces called slots.
+ * How many slots can be used depends on the ESP32x SoC, see @ref sdmmc_slot_t.
+ *
+ * @{
+ */
+/**
+ * @brief SDIO/SDMMC slots
+ *
+ * ESP32x SoCs that have a SDMMC peripheral provide two SDMMC interfaces called
+ * slots.
+ *
+ * @note If the ESP32x variant uses direct I/O functions for the SDMMC signals
+ * (i.e. `SOC_SDMMC_USE_IOMUX` is defined in SoC capabilities), the
+ * GPIOs used for the SDMMC slots are fixed. In this case, slot 0
+ * can't be used because the GPIOs are defined for Slot 0 are the
+ * same as those used for the Flash. If the ESP32x variant uses
+ * the GPIO matrix to route the SDMMC signals to arbitrary pins
+ * (i.e. `SOC_SDMMC_USE_GPIO_MATRIX` is defined in SoC capabilities),
+ * slot 0 can be used but the GPIOs used for the slot have to be
+ * different from those used for the Flash.
+ */
+typedef enum {
+#if IS_USED(SOC_SDMMC_USE_GPIO_MATRIX) || DOXYGEN
+ SDMMC_SLOT_0 = 0, /**< SD/MMC host controller slot 0 (not usable on ESP32 variant) */
+#endif
+ SDMMC_SLOT_1 = 1, /**< SD/MMC host controller slot 1 */
+} sdmmc_slot_t;
+
+/**
+ * @brief SDMMC slot configuration
+ *
+ * If the ESP32x variant uses the GPIO matrix to route the SDMMC signals
+ * to arbitrary pins (i.e. `SOC_SDMMC_USE_GPIO_MATRIX` is defined in SoC
+ * capabilities file), the pins must be configured. The bus width is then
+ * determined from the defined pins. Define the pins for the DAT lines
+ * as `GPIO_UNDEF` to use a smaller data bus width.
+ * If the ESP32x variant uses direct I/O (i.e. `SOC_SDMMC_USE_IOMUX` is
+ * defined in SoC capabilities file), the bus width has to be specified instead.
+ */
+typedef struct {
+ sdmmc_slot_t slot; /**< SDMMC slot used [ SDMMC_SLOT_0 | SDMMC_SLOT_1] */
+ gpio_t cd; /**< Card Detect pin (must be GPIO_UNDEF if not connected) */
+ gpio_t wp; /**< Write Protect pin (must be GPIO_UNDEF if not connected) */
+#if IS_USED(SOC_SDMMC_USE_GPIO_MATRIX) || DOXYGEN
+ gpio_t clk; /**< CLK pin (must be defined) */
+ gpio_t cmd; /**< CMD pin (must be defined) */
+ gpio_t dat0; /**< DAT[0] pin (must be defined) */
+ gpio_t dat1; /**< DAT[1] pin (GPIO_UNDEF if not connected) */
+ gpio_t dat2; /**< DAT[2] pin (GPIO_UNDEF if not connected) */
+ gpio_t dat3; /**< DAT[3] pin (GPIO_UNDEF if not connected) */
+#if IS_USED(MODULE_PERIPH_SMMC_8BIT) || DOXYGEN
+ gpio_t dat4; /**< DAT[4] pin (GPIO_UNDEF if not connected) */
+ gpio_t dat5; /**< DAT[5] pin (GPIO_UNDEF if not connected) */
+ gpio_t dat6; /**< DAT[6] pin (GPIO_UNDEF if not connected) */
+ gpio_t dat7; /**< DAT[7] pin (GPIO_UNDEF if not connected) */
+#endif /* IS_USED(MODULE_PERIPH_SMMC_8BIT) */
+#else /* IS_USED(SOC_SDMMC_USE_IOMUX) */
+ uint8_t bus_width; /**< Bus width */
+#endif
+} sdmmc_conf_t;
+
+/**
+ * @brief SDIO/SDMMC buffer instantiation requirement for SDHC
+ */
+#define SDMMC_CPU_DMA_REQUIREMENTS __attribute__((aligned(SDMMC_CPU_DMA_ALIGNMENT)))
+
+/**
+ * @brief SDIO/SDMMC buffer alignment for SDHC because of DMA/FIFO buffer restrictions
+ */
+#define SDMMC_CPU_DMA_ALIGNMENT 4
+
+/** @} */
+
/**
* @name SPI configuration
*
@@ -857,7 +933,8 @@ typedef struct {
* @name USB device configuration
* @{
*
- * ESP32x SoCs integrate depending on the specific ESP32x SoC variant (family) an USB OTG FS controller based on the Synopsys DWC2 IP core.
+ * ESP32x SoCs integrate depending on the specific ESP32x SoC variant (family)
+ * an USB OTG FS controller based on the Synopsys DWC2 IP core.
*/
#include "usbdev_synopsys_dwc2.h"
diff --git a/cpu/esp32/include/periph_cpu_esp32.h b/cpu/esp32/include/periph_cpu_esp32.h
index 98fe0a3a9197..b7fd02ce747d 100644
--- a/cpu/esp32/include/periph_cpu_esp32.h
+++ b/cpu/esp32/include/periph_cpu_esp32.h
@@ -95,8 +95,6 @@ extern "C" {
* - Vref can be read with function #adc_line_vref_to_gpio at GPIO25.
*/
-/** @} */
-
/**
* @name DAC configuration
*
@@ -130,6 +128,10 @@ extern "C" {
* these channels can be clocked by one of the 4 timers.
*/
+/**
+ * @name SDMMC configuration
+ */
+
/**
* @name SPI configuration
*
@@ -213,6 +215,7 @@ extern "C" {
#define TIMER_NUMOF (2)
#define TIMER_CHANNEL_NUMOF (1)
#endif
+/** @} */
/**
* @name UART configuration
diff --git a/cpu/esp32/irq_arch.c b/cpu/esp32/irq_arch.c
index 7510df505206..f84b92410fc2 100644
--- a/cpu/esp32/irq_arch.c
+++ b/cpu/esp32/irq_arch.c
@@ -92,6 +92,9 @@ static const struct intr_handle_data_t _irq_data_table[] = {
#elif defined(CPU_FAM_ESP32S3)
{ ETS_LCD_CAM_INTR_SOURCE, CPU_INUM_LCD, 1 },
#endif
+#if defined(CPU_FAM_ESP32) || defined(CPU_FAM_ESP32S2) || defined(CPU_FAM_ESP32S3)
+ { ETS_SDIO_HOST_INTR_SOURCE, CPU_INUM_SDMMC, 2 },
+#endif
};
#define IRQ_DATA_TABLE_SIZE ARRAY_SIZE(_irq_data_table)
diff --git a/cpu/esp32/periph/sdmmc.c b/cpu/esp32/periph/sdmmc.c
new file mode 100644
index 000000000000..06ffce79ac3e
--- /dev/null
+++ b/cpu/esp32/periph/sdmmc.c
@@ -0,0 +1,481 @@
+/*
+ * 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_esp32
+ * @{
+ *
+ * @file
+ * @brief Low-level SDIO/SD/MMC peripheral driver interface for ESP32
+ *
+ * @author Gunar Schorcht
+ *
+ * @}
+ */
+
+#include
+#include
+#include
+#include
+
+#include "assert.h"
+#include "bitarithm.h"
+#include "container.h"
+#include "log.h"
+#include "periph/gpio.h"
+#include "syscalls.h"
+#include "ztimer.h"
+
+#include "sdmmc/sdmmc.h"
+
+#include "driver/sdmmc_types.h"
+#include "driver/sdmmc_host.h"
+#include "soc/sdmmc_reg.h"
+
+#define ENABLE_DEBUG 0
+#include "debug.h"
+
+/* CLK_EDGE_SEL - clock phase selection register */
+#define SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_S (3)
+#define SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_M (0x7 << SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_S)
+
+/* we have to redefine it here since we can't include "gpio_types.h" due to
+ * naming conflicts */
+#define GPIO_NUM_NC (GPIO_UNDEF)
+
+/* debounce time for CD pin */
+#define CONFIG_CD_PIN_DEBOUNCE_US 25000
+
+/* limit the Default and High Speed clock rates for debugging */
+#if CONFIG_SDMMC_CLK_MAX_400KHZ
+#define CONFIG_SDMMC_CLK_MAX KHZ(400)
+#elif CONFIG_SDMMC_CLK_MAX_1MHZ
+#define CONFIG_SDMMC_CLK_MAX MHZ(1)
+#elif CONFIG_SDMMC_CLK_MAX_4MHZ
+#define CONFIG_SDMMC_CLK_MAX MHZ(4)
+#elif CONFIG_SDMMC_CLK_MAX_10MHZ
+#define CONFIG_SDMMC_CLK_MAX MHZ(10)
+#elif CONFIG_SDMMC_CLK_MAX_20MHZ || !IS_USED(MODULE_PERIPH_SDMMC_HS)
+#define CONFIG_SDMMC_CLK_MAX MHZ(20)
+#else
+#define CONFIG_SDMMC_CLK_MAX MHZ(40)
+#endif
+
+/* 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
+
+/* forward declaration of _driver */
+static const sdmmc_driver_t _driver;
+
+/* driver related */
+typedef struct {
+ sdmmc_dev_t sdmmc_dev; /**< Inherited sdmmc_dev_t struct */
+ const sdmmc_conf_t *config; /**< SDIO/SD/MMC peripheral config */
+ uint32_t last_cd_pin_irq; /**< Last CD Pin IRQ time for debouncing */
+ bool data_transfer; /**< Transfer active */
+} esp32_sdmmc_dev_t;
+
+static esp32_sdmmc_dev_t _sdmmc_devs[] = {
+ {
+ .sdmmc_dev = {
+ .driver = &_driver,
+ },
+ .config = &sdmmc_config[0],
+ },
+#if SDMMC_CONFIG_NUMOF == 2
+ {
+ .sdmmc_dev = {
+ .driver = &_driver,
+ },
+ .config = &sdmmc_config[1],
+ }
+#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");
+static_assert(SDMMC_CONFIG_NUMOF <= SOC_SDMMC_NUM_SLOTS,
+ "SDMMC_CONFIG_NUMOF is greater than the supported number of slots");
+
+XFA_CONST(sdmmc_devs, 0) sdmmc_dev_t * const _sdmmc_0 = (sdmmc_dev_t * const)&_sdmmc_devs[0];
+#if SDMMC_CONFIG_NUMOF > 1
+XFA_CONST(sdmmc_devs, 0) sdmmc_dev_t * const _sdmmc_1 = (sdmmc_dev_t * const)&_sdmmc_devs[1];
+#endif
+
+/* forward declaration of internal functions */
+static int _esp_err_to_sdmmc_err_code(esp_err_t code);
+static void _isr_cd_pin(void *arg);
+
+static void _init(sdmmc_dev_t *sdmmc_dev)
+{
+ esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
+ assert(dev);
+
+ const sdmmc_conf_t *conf = dev->config;
+ assert(conf);
+
+ /* additional sanity checks */
+ assert(conf->slot < SOC_SDMMC_NUM_SLOTS);
+
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+
+#if IS_USED(CPU_FAM_ESP32)
+
+ /* On ESP32 only Slot 1 can be used */
+ assert(conf->slot == SDMMC_SLOT_1);
+ /* Slot 1 has only 4 data lines */
+ assert((conf->bus_width == 1) || (conf->bus_width == 4));
+
+ sdmmc_dev->bus_width = conf->bus_width;
+
+#elif IS_USED(CPU_FAM_ESP32S3)
+
+ assert(gpio_is_valid(conf->clk) && !gpio_is_equal(conf->clk, GPIO0));
+ assert(gpio_is_valid(conf->cmd) && !gpio_is_equal(conf->cmd, GPIO0));
+ assert(gpio_is_valid(conf->dat0) && !gpio_is_equal(conf->dat0, GPIO0));
+
+ /* TODO Check for collision with Flash GPIOs */
+ slot_config.clk = conf->clk;
+ slot_config.cmd = conf->cmd;
+ slot_config.d0 = conf->dat0;
+
+ sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_1BIT;
+
+ slot_config.d1 = GPIO_UNDEF;
+ slot_config.d2 = GPIO_UNDEF;
+ slot_config.d3 = GPIO_UNDEF;
+ slot_config.d4 = GPIO_UNDEF;
+ slot_config.d5 = GPIO_UNDEF;
+ slot_config.d6 = GPIO_UNDEF;
+ slot_config.d7 = GPIO_UNDEF;
+
+ if (gpio_is_valid(conf->dat1) && !gpio_is_equal(conf->dat1, GPIO0) &&
+ gpio_is_valid(conf->dat2) && !gpio_is_equal(conf->dat2, GPIO0) &&
+ gpio_is_valid(conf->dat3) && !gpio_is_equal(conf->dat3, GPIO0)) {
+ slot_config.d1 = conf->dat1;
+ slot_config.d2 = conf->dat2;
+ slot_config.d3 = conf->dat3;
+ sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_4BIT;
+
+#if IS_USED(MODULE_PERIPH_SDMMC_8BIT)
+ if (gpio_is_valid(conf->dat4) && !gpio_is_equal(conf->dat4, GPIO0) &&
+ gpio_is_valid(conf->dat5) && !gpio_is_equal(conf->dat5, GPIO0) &&
+ gpio_is_valid(conf->dat6) && !gpio_is_equal(conf->dat6, GPIO0) &&
+ gpio_is_valid(conf->dat7) && !gpio_is_equal(conf->dat7, GPIO0)) {
+ slot_config.d4 = conf->dat4;
+ slot_config.d5 = conf->dat5;
+ slot_config.d6 = conf->dat6;
+ slot_config.d7 = conf->dat7;
+ sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_8BIT;
+ }
+#endif
+ }
+
+#else
+#error "ESP32x variant not supported"
+#endif
+
+#if IS_USED(CONFIG_SDMMC_INTERNAL_PULLUP)
+ slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
+#endif
+
+ slot_config.width = sdmmc_dev->bus_width;
+
+ dev->data_transfer = false;
+
+ esp_err_t res;
+
+ if ((res = sdmmc_host_init())) {
+ LOG_ERROR("[sdmmc] Could not initialize SDMMC host controller\n");
+ assert(false);
+ }
+ if ((res = sdmmc_host_init_slot(dev->config->slot, &slot_config))) {
+ LOG_ERROR("[sdmmc] Could not initialize SDMMC slot\n");
+ assert(false);
+ }
+
+ if (gpio_is_valid(conf->cd)) {
+ dev->last_cd_pin_irq = system_get_time();
+
+ gpio_init_int(conf->cd, GPIO_IN, GPIO_BOTH, _isr_cd_pin, sdmmc_dev);
+ sdmmc_dev->present = gpio_read(conf->cd) == 0;
+ }
+ else {
+ sdmmc_dev->present = true;
+ }
+
+ sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_1BIT; // SDMMC_BUS_WIDTH_4BIT;
+}
+
+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);
+
+ esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
+
+ assert(dev);
+ assert(dev->config);
+
+ if (dev->data_transfer) {
+ /* data transfer command is issued in _xfer_execute as one transaction
+ * together with data phase */
+ return 0;
+ }
+
+ sdmmc_command_t cmd = {
+ .opcode =cmd_idx,
+ .flags = 0,
+ .arg = arg,
+ .data = 0,
+ .datalen = 0,
+ .blklen = 0,
+ .timeout_ms = 100,
+ };
+
+ switch (resp_type) {
+ case SDMMC_R1:
+ cmd.flags |= SCF_RSP_R1;
+ break;
+ case SDMMC_R1B:
+ cmd.flags |= SCF_RSP_R1B;
+ break;
+ case SDMMC_R2:
+ cmd.flags |= SCF_RSP_R2;
+ break;
+ case SDMMC_R3:
+ cmd.flags |= SCF_RSP_R3;
+ break;
+ case SDMMC_R4:
+ cmd.flags |= SCF_RSP_R4;
+ break;
+ case SDMMC_R5:
+ cmd.flags |= SCF_RSP_R5;
+ break;
+ case SDMMC_R6:
+ cmd.flags |= SCF_RSP_R7;
+ break;
+ case SDMMC_R7:
+ cmd.flags |= SCF_RSP_R7;
+ break;
+ default:
+ break;
+ }
+
+ esp_err_t res = sdmmc_host_do_transaction(dev->config->slot, &cmd);
+ if (res) {
+ return _esp_err_to_sdmmc_err_code(res);
+ }
+ else if (cmd.error) {
+ return _esp_err_to_sdmmc_err_code(cmd.error);
+ }
+
+ if ((resp_type == SDMMC_R1) || (resp_type == SDMMC_R1B)) {
+ sdmmc_dev->status = cmd.response[0];
+ }
+
+ if (resp) {
+ if (resp_type == SDMMC_R2) {
+ resp[0] = cmd.response[3];
+ resp[1] = cmd.response[2];
+ resp[2] = cmd.response[1];
+ resp[3] = cmd.response[0];
+ }
+ else if (resp_type != SDMMC_NO_R) {
+ resp[0] = cmd.response[0];
+ }
+ }
+
+ return 0;
+}
+
+static int _set_bus_width(sdmmc_dev_t *sdmmc_dev, sdmmc_bus_width_t width)
+{
+ DEBUG("[sdmmc] %s width=%d\n", __func__, width);
+ esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
+ assert(dev);
+
+ esp_err_t res = sdmmc_host_set_bus_width(dev->config->slot, width);
+ if (res) {
+ return _esp_err_to_sdmmc_err_code(res);
+ }
+
+ return 0;
+}
+
+static int _set_clock_rate(sdmmc_dev_t *sdmmc_dev, sdmmc_clock_rate_t rate)
+{
+ DEBUG("[sdmmc] %s rate=%"PRIu32" ", __func__, (uint32_t)rate);
+
+ esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
+ assert(dev);
+
+ if (rate > CONFIG_SDMMC_CLK_MAX) {
+ rate = CONFIG_SDMMC_CLK_MAX;
+ }
+
+ DEBUG("actual_rate=%"PRIu32"\n", (uint32_t)rate);
+
+ esp_err_t res = sdmmc_host_set_card_clk(dev->config->slot, rate / KHZ(1));
+ if (res) {
+ return _esp_err_to_sdmmc_err_code(res);
+ }
+
+#if SOC_SDMMC_USE_GPIO_MATRIX
+ /* phase has to be modified to get it working for MMCs if
+ * SOC_SDMMC_USE_GPIO_MATRIX is used */
+ uint32_t reg = *((uint32_t *)SDMMC_CLOCK_REG);
+ reg &= ~SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_M;
+ reg |= (6 << SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_S);
+ *((uint32_t *)SDMMC_CLOCK_REG) = reg;
+#endif
+
+ return 0;
+}
+
+static int _xfer_prepare(sdmmc_dev_t *sdmmc_dev, sdmmc_xfer_desc_t *xfer)
+{
+ esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
+
+ assert(dev);
+ assert(dev->config);
+
+ /* SDIO/SD/MMC uses 32-bit words */
+ /* TODO: at the moment only 32-bit words supported */
+ assert((xfer->block_size % sizeof(uint32_t)) == 0);
+
+ dev->data_transfer = true;
+
+ 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));
+
+ esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
+
+ assert(dev);
+ assert(dev->config);
+
+ sdmmc_command_t cmd = {
+ .opcode = xfer->cmd_idx & ~SDMMC_ACMD_PREFIX,
+ .flags = SCF_RSP_R1 | (xfer->write ? 0 : SCF_CMD_READ),
+ .arg = xfer->arg,
+ .data = xfer->write ? (void *)data_wr : data_rd,
+ .datalen = xfer->block_num * xfer->block_size,
+ .blklen = xfer->block_size,
+ .timeout_ms = xfer->write ? 2500 : 1000, // TODO
+ };
+
+ if (done) {
+ *done = 0;
+ }
+
+ esp_err_t res = sdmmc_host_do_transaction(dev->config->slot, &cmd);
+ if (res) {
+ return _esp_err_to_sdmmc_err_code(res);
+ }
+ else if (cmd.error) {
+ return _esp_err_to_sdmmc_err_code(cmd.error);
+ }
+
+ if (done) {
+ *done = xfer->block_num;
+ }
+
+ return 0;
+}
+
+static int _xfer_finish(sdmmc_dev_t *sdmmc_dev, sdmmc_xfer_desc_t *xfer)
+{
+ (void)xfer;
+
+ esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
+ dev->data_transfer = false;
+
+ return 0;
+}
+
+static int _esp_err_to_sdmmc_err_code(esp_err_t error)
+{
+ switch (error) {
+ case ESP_ERR_TIMEOUT:
+ DEBUG("[sdmmc] Timeout error\n");
+ return -ETIMEDOUT;
+ case ESP_ERR_INVALID_CRC:
+ DEBUG("[sdmmc] CRC error\n");
+ return -EBADMSG;
+ case ESP_ERR_INVALID_RESPONSE:
+ DEBUG("[sdmmc] Invalid response\n");
+ return -EIO;
+ case ESP_ERR_INVALID_SIZE:
+ DEBUG("[sdmmc] Invalid size\n");
+ return -EIO;
+ case ESP_ERR_INVALID_ARG:
+ DEBUG("[sdmmc] Invalid argument\n");
+ return -EIO;
+ default:
+ DEBUG("[sdmmc] Other error\n");
+ return -EIO;
+ }
+}
+
+static void _isr_cd_pin(void *arg)
+{
+ uint32_t state = irq_disable();
+
+ esp32_sdmmc_dev_t *dev = arg;
+ assert(dev);
+
+ /* for debouncing handle only the first CD Pin interrupts and ignore further
+ * interrupts that happen within the debouncing time interval */
+ if ((system_get_time() - dev->last_cd_pin_irq) > CONFIG_CD_PIN_DEBOUNCE_US) {
+ dev->last_cd_pin_irq = system_get_time();
+
+ sdmmc_dev_t *sdmmc_dev = &dev->sdmmc_dev;
+
+ sdmmc_dev->present = !sdmmc_dev->present;
+ 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);
+ }
+ }
+ irq_restore(state);
+}
+
+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/pkg/esp32_sdk/patches/0031-driver-sdmmc-avoid-type-definition-conflicts.patch b/pkg/esp32_sdk/patches/0031-driver-sdmmc-avoid-type-definition-conflicts.patch
new file mode 100644
index 000000000000..cfe293be65bd
--- /dev/null
+++ b/pkg/esp32_sdk/patches/0031-driver-sdmmc-avoid-type-definition-conflicts.patch
@@ -0,0 +1,48 @@
+From b00693f50e7f4b8239384c7b06ef1dd5a85f2ca9 Mon Sep 17 00:00:00 2001
+From: Gunar Schorcht
+Date: Tue, 27 Jun 2023 16:24:27 +0200
+Subject: [PATCH 31/31] driver/sdmmc: avoid type definition conflicts
+
+---
+ components/driver/include/driver/sdmmc_types.h | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h
+index cbb796fdbb3..c3d27bec18d 100644
+--- a/components/driver/include/driver/sdmmc_types.h
++++ b/components/driver/include/driver/sdmmc_types.h
+@@ -29,6 +29,7 @@
+ #include "esp_err.h"
+ #include "freertos/FreeRTOS.h"
+
++#if !defined(RIOT_VERSION)
+ /**
+ * Decoded values from SD card Card Specific Data register
+ */
+@@ -69,6 +70,8 @@ typedef struct {
+ uint8_t power_class; /*!< Power class used by the card */
+ } sdmmc_ext_csd_t;
+
++#endif /* !defined(RIOT_VERSION) */
++
+ /**
+ * SD/MMC command response buffer
+ */
+@@ -160,6 +163,7 @@ typedef struct {
+ int command_timeout_ms; /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */
+ } sdmmc_host_t;
+
++#if !defined(RIOT_VERSION)
+ /**
+ * SD/MMC card information structure
+ */
+@@ -185,5 +189,6 @@ typedef struct {
+ uint32_t reserved : 23; /*!< Reserved for future expansion */
+ } sdmmc_card_t;
+
++#endif /* !defined(RIOT_VERSION) */
+
+ #endif // _SDMMC_TYPES_H_
+--
+2.34.1
+
diff --git a/tests/pkg/fatfs/main.c b/tests/pkg/fatfs/main.c
index 1ea15caaa566..5ab8eb474411 100644
--- a/tests/pkg/fatfs/main.c
+++ b/tests/pkg/fatfs/main.c
@@ -51,16 +51,28 @@
FATFS fat_fs; /* FatFs work area needed for each volume */
#ifdef MODULE_MTD_NATIVE
+
/* mtd device for native is provided in boards/native/board_init.c */
mtd_dev_t *fatfs_mtd_devs[1];
+
+#elif MODULE_MTD_SDMMC
+
+#include "mtd_sdmmc.h"
+
+mtd_dev_t *fatfs_mtd_devs[1];
+
#elif MODULE_MTD_SDCARD
+
#include "mtd_sdcard.h"
#include "sdcard_spi_params.h"
+
#define SDCARD_SPI_NUM ARRAY_SIZE(sdcard_spi_params)
+
/* sdcard devs are provided by drivers/sdcard_spi/sdcard_spi.c */
extern sdcard_spi_t sdcard_spi_devs[SDCARD_SPI_NUM];
mtd_sdcard_t mtd_sdcard_devs[SDCARD_SPI_NUM];
mtd_dev_t *fatfs_mtd_devs[SDCARD_SPI_NUM];
+
#endif
#define MTD_NUM ARRAY_SIZE(fatfs_mtd_devs)
@@ -105,11 +117,11 @@ static int _mount(int argc, char **argv)
}
else {
- #if FF_MAX_SS == FF_MIN_SS
+#if FF_MAX_SS == FF_MIN_SS
uint16_t sector_size = TEST_FATFS_FIXED_SECTOR_SIZE;
- #else
+#else
uint16_t sector_size = fs->ssize;
- #endif
+#endif
uint64_t total_bytes = (fs->n_fatent - TEST_FATFS_FATENT_OFFSET) * fs->csize;
total_bytes *= sector_size;
@@ -375,7 +387,7 @@ static const shell_command_t shell_commands[] = {
int main(void)
{
- #if FATFS_FFCONF_OPT_FS_NORTC == 0
+#if FATFS_FFCONF_OPT_FS_NORTC == 0
/* the rtc is used in diskio.c for timestamps of files */
puts("Initializing the RTC driver");
rtc_poweron();
@@ -396,11 +408,14 @@ int main(void)
time.tm_min,
time.tm_sec);
rtc_set_time(&time);
- #endif
+#endif
- #if MODULE_MTD_NATIVE
+#if MODULE_MTD_NATIVE
fatfs_mtd_devs[0] = mtd_dev_get(0);
- #elif MODULE_MTD_SDCARD
+#elif MODULE_MTD_SDMMC
+ extern mtd_sdmmc_t mtd_sdmmc_dev0;
+ fatfs_mtd_devs[0] = &mtd_sdmmc_dev0.base;
+#elif MODULE_MTD_SDCARD
for (unsigned int i = 0; i < SDCARD_SPI_NUM; i++){
mtd_sdcard_devs[i].base.driver = &mtd_sdcard_driver;
mtd_sdcard_devs[i].sd_card = &sdcard_spi_devs[i];
@@ -413,7 +428,7 @@ int main(void)
printf("init sdcard_mtd %u [FAILED]\n", i);
}
}
- #endif
+#endif
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
diff --git a/tests/pkg/fatfs_vfs/main.c b/tests/pkg/fatfs_vfs/main.c
index f7f4e4050d07..ce206bf7efff 100644
--- a/tests/pkg/fatfs_vfs/main.c
+++ b/tests/pkg/fatfs_vfs/main.c
@@ -29,7 +29,9 @@
#include "kernel_defines.h"
-#ifdef MODULE_MTD_SDCARD
+#if MODULE_MTD_SDMMC
+#include "mtd_sdmmc.h"
+#elif MODULE_MTD_SDCARD
#include "mtd_sdcard.h"
#include "sdcard_spi.h"
#include "sdcard_spi_params.h"
@@ -64,9 +66,9 @@ static vfs_mount_t _test_vfs_mount = {
#if defined(MODULE_MTD_NATIVE) || defined(MODULE_MTD_MCI)
/* mtd devices are provided in the board's board_init.c*/
-#endif
-
-#if defined(MODULE_MTD_SDCARD)
+#elif defined(MODULE_MTD_SDMMC)
+extern mtd_sdmmc_t mtd_sdmmc_dev0;
+#elif defined(MODULE_MTD_SDCARD)
#define SDCARD_SPI_NUM ARRAY_SIZE(sdcard_spi_params)
extern sdcard_spi_t sdcard_spi_devs[SDCARD_SPI_NUM];
mtd_sdcard_t mtd_sdcard_devs[SDCARD_SPI_NUM];
@@ -398,20 +400,18 @@ static void test_libc(void)
int main(void)
{
-#if MODULE_MTD_SDCARD
+#if defined(MODULE_MTD_NATIVE) || defined(MODULE_MTD_MCI)
+ fatfs.dev = mtd_dev_get(0);
+#elif defined(MODULE_MTD_SDMMC)
+ fatfs.dev = &mtd_sdmmc_dev0.base;
+#elif defined(MODULE_MTD_SDCARD)
for(unsigned int i = 0; i < SDCARD_SPI_NUM; i++){
mtd_sdcard_devs[i].base.driver = &mtd_sdcard_driver;
mtd_sdcard_devs[i].sd_card = &sdcard_spi_devs[i];
mtd_sdcard_devs[i].params = &sdcard_spi_params[i];
mtd_init(&mtd_sdcard_devs[i].base);
}
-#endif
-
-#if defined(MODULE_MTD_NATIVE) || defined(MODULE_MTD_MCI)
- fatfs.dev = mtd_dev_get(0);
-#endif
-#if defined(MODULE_MTD_SDCARD)
fatfs.dev = mtd_sdcard;
#endif