1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/esp32: add SDMMC support

This commit is contained in:
Gunar Schorcht 2023-06-28 17:31:54 +02:00
parent 866f2a00c0
commit 92657f5fd2
18 changed files with 818 additions and 38 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)))

View File

@ -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 <br> 44 mA @ 160 MHz (34 mA @ 160 MHz single core) <br> 31 mA @ 80 MHz (25 mA @ 80 MHz single core) <br> 800 uA in light sleep mode <br> 10 uA in deep sleep mode | yes <br> yes <br> yes <br> yes <br> 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 <br> 15 mA @ 80 MHz <br> 130 uA in light sleep mode <br> 5 uA in deep sleep mode | yes <br> yes <br> yes <br> 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 <br> 50 mA @ 160 MHz (40 mA @ 160 MHz single core) <br> 33 mA @ 80 MHz (28 mA @ 80 MHz single core) <br> 19 mA @ 40 MHz (16 mA @ 40 MHz single core) <br>240 uA in light sleep mode <br> 8 uA in deep sleep mode | yes <br> yes <br> yes <br> yes <br> yes <br> 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 <br> 50 mA @ 160 MHz (40 mA @ 160 MHz single core) <br> 33 mA @ 80 MHz (28 mA @ 80 MHz single core) <br> 19 mA @ 40 MHz (16 mA @ 40 MHz single core) <br>240 uA in light sleep mode <br> 8 uA in deep sleep mode | yes <br> yes <br> yes <br> yes <br> yes <br> 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

View File

@ -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:
<center>
| Signal | GPIO Slot 1 |
|:------ |:------------|
| CLK | GPIO14 |
| CMD | GPIO15 |
| DAT0 | GPIO2 |
| DAT1 | GPIO4 |
| DAT2 | GPIO12 |
| DAT3 | GPIO13 |
</center>
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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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.

View File

@ -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

View File

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

View File

@ -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"

View File

@ -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

View File

@ -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)

481
cpu/esp32/periph/sdmmc.c Normal file
View File

@ -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 <gunar@schorcht.net>
*
* @}
*/
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include "assert.h"
#include "bitarithm.h"
#include "container.h"
#include "log.h"
#include "periph/gpio.h"
#include "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,
};