mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 05:12:57 +01:00
cpu/esp32: add SDMMC support
This commit is contained in:
parent
866f2a00c0
commit
92657f5fd2
@ -14,6 +14,9 @@ config CPU_FAM_ESP32
|
|||||||
select HAS_ESP_BLE
|
select HAS_ESP_BLE
|
||||||
select HAS_ESP_BLE_ESP32
|
select HAS_ESP_BLE_ESP32
|
||||||
select HAS_PUF_SRAM
|
select HAS_PUF_SRAM
|
||||||
|
select HAS_PERIPH_SDMMC_AUTO_CLK
|
||||||
|
select HAS_PERIPH_SDMMC_AUTO_CMD12
|
||||||
|
select HAS_PERIPH_SDMMC_MMC
|
||||||
|
|
||||||
config CPU_FAM
|
config CPU_FAM
|
||||||
default "esp32" if CPU_FAM_ESP32
|
default "esp32" if CPU_FAM_ESP32
|
||||||
|
@ -15,6 +15,10 @@ config CPU_FAM_ESP32S3
|
|||||||
select HAS_BLE_PHY_2MBIT
|
select HAS_BLE_PHY_2MBIT
|
||||||
select HAS_ESP_BLE
|
select HAS_ESP_BLE
|
||||||
select HAS_ESP_BLE_ESP32C3
|
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_USBDEV_SYNOPSYS_DWC2 if MODULE_PERIPH_USBDEV
|
||||||
select MODULE_USB_BOARD_RESET if MODULE_USBUS_CDC_ACM || MODULE_TINYUSB_CLASS_CDC
|
select MODULE_USB_BOARD_RESET if MODULE_USBUS_CDC_ACM || MODULE_TINYUSB_CLASS_CDC
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ config CPU_COMMON_ESP32X
|
|||||||
|
|
||||||
select PACKAGE_ESP32_SDK if TEST_KCONFIG
|
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_PERIPH_RTT if HAS_PERIPH_RTT && MODULE_PM_LAYERED
|
||||||
select MODULE_PS if MODULE_SHELL
|
select MODULE_PS if MODULE_SHELL
|
||||||
select MODULE_PTHREAD if MODULE_CPP
|
select MODULE_PTHREAD if MODULE_CPP
|
||||||
|
@ -105,6 +105,11 @@ ifneq (,$(filter periph_i2c,$(USEMODULE)))
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter periph_sdmmc,$(USEMODULE)))
|
||||||
|
USEMODULE += esp_idf_sdmmc
|
||||||
|
USEMODULE += periph_gpio_irq
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter esp_spi_ram,$(USEMODULE)))
|
ifneq (,$(filter esp_spi_ram,$(USEMODULE)))
|
||||||
FEATURES_REQUIRED += esp_spi_ram
|
FEATURES_REQUIRED += esp_spi_ram
|
||||||
FEATURES_OPTIONAL += esp_spi_oct
|
FEATURES_OPTIONAL += esp_spi_oct
|
||||||
|
@ -46,6 +46,15 @@ else ifneq (,$(filter esp32c3 esp32s3,$(CPU_FAM)))
|
|||||||
FEATURES_PROVIDED += esp_ble_esp32c3
|
FEATURES_PROVIDED += esp_ble_esp32c3
|
||||||
endif
|
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)))
|
ifneq (,$(filter esp32-wrover% esp32s2%r2 esp32s3%r2 esp32s3%r8 esp32s3%r8v,$(CPU_MODEL)))
|
||||||
FEATURES_PROVIDED += esp_spi_ram
|
FEATURES_PROVIDED += esp_spi_ram
|
||||||
ifneq (,$(filter esp32s3%r8 esp32s3%r8v,$(CPU_MODEL)))
|
ifneq (,$(filter esp32s3%r8 esp32s3%r8v,$(CPU_MODEL)))
|
||||||
|
@ -55,13 +55,14 @@ This document describes the RIOT implementation for supported variants
|
|||||||
3. [DAC Channels](#esp32_dac_channels)
|
3. [DAC Channels](#esp32_dac_channels)
|
||||||
4. [I2C Interfaces](#esp32_i2c_interfaces)
|
4. [I2C Interfaces](#esp32_i2c_interfaces)
|
||||||
5. [PWM Channels](#esp32_pwm_channels)
|
5. [PWM Channels](#esp32_pwm_channels)
|
||||||
6. [SPI Interfaces](#esp32_spi_interfaces)
|
6. [SDMMC Interfaces](#esp32_sdmmc_interfaces)
|
||||||
7. [Timers](#esp32_timers)
|
8. [SPI Interfaces](#esp32_spi_interfaces)
|
||||||
8. [RTT Implementation](#esp32_rtt_counter)
|
9. [Timers](#esp32_timers)
|
||||||
9. [UART Interfaces](#esp32_uart_interfaces)
|
10. [RTT Implementation](#esp32_rtt_counter)
|
||||||
10. [CAN Interfaces](#esp32_can_interfaces)
|
12. [UART Interfaces](#esp32_uart_interfaces)
|
||||||
11. [Power Management](#esp32_power_management)
|
13. [CAN Interfaces](#esp32_can_interfaces)
|
||||||
12. [Other Peripherals](#esp32_other_peripherals)
|
14. [Power Management](#esp32_power_management)
|
||||||
|
15. [Other Peripherals](#esp32_other_peripherals)
|
||||||
7. [Special On-board Peripherals](#esp32_special_on_board_peripherals)
|
7. [Special On-board Peripherals](#esp32_special_on_board_peripherals)
|
||||||
1. [SPI RAM Modules](#esp32_spi_ram)
|
1. [SPI RAM Modules](#esp32_spi_ram)
|
||||||
2. [SPIFFS Device](#esp32_spiffs_device)
|
2. [SPIFFS Device](#esp32_spiffs_device)
|
||||||
@ -212,13 +213,14 @@ The key features of ESP32 are:
|
|||||||
| Flash | 512 KiB ... 16 MiB | yes |
|
| Flash | 512 KiB ... 16 MiB | yes |
|
||||||
| Frequency | 240 MHz, 160 MHz, 80 MHz | 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 |
|
| 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 |
|
| Timer | 4 x 64 bit | yes |
|
||||||
| ADCs | 2 x SAR-ADC with up to 18 x 12 bit channels total | yes |
|
| ADC | 2 x SAR-ADC with up to 18 x 12 bit channels total | yes |
|
||||||
| DACs | 2 x DAC with 8 bit | yes |
|
| DAC | 2 x DAC with 8 bit | yes |
|
||||||
| GPIOs | 34 (6 are only inputs, 18 are RTC GPIOs) | yes |
|
| GPIO | 34 (6 are only inputs, 18 are RTC GPIOs) | yes |
|
||||||
| I2Cs | 2 | yes |
|
| I2C | 2 | yes |
|
||||||
| SPIs | 4 | yes (2) |
|
| SDMMC | 2 | yes |
|
||||||
| UARTs | 3 | yes |
|
| SPI | 4 | yes (2) |
|
||||||
|
| UART | 3 | yes |
|
||||||
| WiFi | IEEE 802.11 b/g/n built in | yes |
|
| WiFi | IEEE 802.11 b/g/n built in | yes |
|
||||||
| Bluetooth | v4.2 BR/EDR and BLE | yes |
|
| Bluetooth | v4.2 BR/EDR and BLE | yes |
|
||||||
| Ethernet | MAC interface with dedicated DMA and IEEE 1588 support | 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 |
|
| Flash | 8 MiB | yes |
|
||||||
| Frequency | 160 MHz, 80 MHz | 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 |
|
| 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 |
|
| Timer | 2 x 54 bit | yes |
|
||||||
| ADCs | 2 x SAR-ADC with up to 6 x 12 bit channels total | yes |
|
| ADC | 2 x SAR-ADC with up to 6 x 12 bit channels total | yes |
|
||||||
| DACs | - | - |
|
| DAC | - | - |
|
||||||
| GPIOs | 22 | yes |
|
| GPIO | 22 | yes |
|
||||||
| I2Cs | 1 | yes |
|
| I2C | 1 | yes |
|
||||||
| SPIs | 3 | yes (1) |
|
| SPI | 3 | yes (1) |
|
||||||
| UARTs | 2 | yes |
|
| UART | 2 | yes |
|
||||||
| WiFi | IEEE 802.11 b/g/n built in | yes |
|
| WiFi | IEEE 802.11 b/g/n built in | yes |
|
||||||
| Bluetooth | Bluetooth 5 (LE) | yes |
|
| Bluetooth | Bluetooth 5 (LE) | yes |
|
||||||
| Ethernet | - | - |
|
| 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 |
|
| Flash | 512 KiB ... 32 MiB Dual/Quad/Octal SPI (external or internal) | yes |
|
||||||
| Frequency | 240 MHz, 160 MHz, 80 MHz | 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 |
|
| 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 |
|
| Timer | 4 x 54 bit | yes |
|
||||||
| ADCs | 2 x SAR-ADC with up to 20 x 13 bit channels total | yes |
|
| ADC | 2 x SAR-ADC with up to 20 x 13 bit channels total | yes |
|
||||||
| DACs | 2 x DAC with 8 bit | - |
|
| DAC | 2 x DAC with 8 bit | - |
|
||||||
| GPIOs | 43 (22 are RTC GPIOs) | yes |
|
| GPIO | 43 (22 are RTC GPIOs) | yes |
|
||||||
| I2Cs | 2 | yes |
|
| I2C | 2 | yes |
|
||||||
| SPIs | 4 | yes (2) |
|
| SPI | 4 | yes (2) |
|
||||||
| UARTs | 2 | yes |
|
| UART | 2 | yes |
|
||||||
| WiFi | IEEE 802.11 b/g/n built in | yes |
|
| WiFi | IEEE 802.11 b/g/n built in | yes |
|
||||||
| Bluetooth | - | - |
|
| Bluetooth | - | - |
|
||||||
| Ethernet | - | - |
|
| 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 |
|
| Flash | 512 KiB ... 32 MiB Dual/Quad/Octal SPI (external or internal) | yes |
|
||||||
| Frequency | 240 MHz, 160 MHz, 80 MHz | 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 |
|
| 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 |
|
| Timer | 4 x 54 bit | yes |
|
||||||
| ADCs | 2 x SAR-ADC with up to 20 x 12 bit channels total | yes |
|
| ADC | 2 x SAR-ADC with up to 20 x 12 bit channels total | yes |
|
||||||
| DACs | - | - |
|
| DAC | - | - |
|
||||||
| GPIOs | 45 (22 are RTC GPIOs) | yes |
|
| GPIO | 45 (22 are RTC GPIOs) | yes |
|
||||||
| I2Cs | 2 | yes |
|
| I2C | 2 | yes |
|
||||||
| SPIs | 4 | yes (2) |
|
| SDMMC | 2 | yes |
|
||||||
| UARTs | 3 | yes |
|
| SPI | 4 | yes (2) |
|
||||||
|
| UART | 3 | yes |
|
||||||
| WiFi | IEEE 802.11 b/g/n built in | yes |
|
| WiFi | IEEE 802.11 b/g/n built in | yes |
|
||||||
| Bluetooth | Bluetooth 5 (LE) | yes |
|
| Bluetooth | Bluetooth 5 (LE) | yes |
|
||||||
| Ethernet | - | - |
|
| Ethernet | - | - |
|
||||||
@ -1012,6 +1015,62 @@ definition of `PWM0_GPIOS`, `PWM1_GPIOS`, `PWM2_GPIOS` and `PWM3_GPIOS`.
|
|||||||
|
|
||||||
[Back to table of contents](#esp32_toc)
|
[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}
|
## SPI Interfaces {#esp32_spi_interfaces}
|
||||||
|
|
||||||
ESP32x SoCs have up to four SPI controllers dependent on the specific ESP32x
|
ESP32x SoCs have up to four SPI controllers dependent on the specific ESP32x
|
||||||
|
@ -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
|
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.
|
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}
|
## SPI Interfaces {#esp32_spi_interfaces_esp32}
|
||||||
|
|
||||||
ESP32 has four SPI controllers where SPI0 and SPI1 share the same bus and
|
ESP32 has four SPI controllers where SPI0 and SPI1 share the same bus and
|
||||||
|
@ -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
|
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.
|
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}
|
## SPI Interfaces {#esp32_spi_interfaces_esp32s3}
|
||||||
|
|
||||||
ESP32-S3 has four SPI controllers where SPI0 and SPI1 share the same bus
|
ESP32-S3 has four SPI controllers where SPI0 and SPI1 share the same bus
|
||||||
|
@ -29,6 +29,7 @@ rsource "heap/Kconfig"
|
|||||||
rsource "lcd/Kconfig"
|
rsource "lcd/Kconfig"
|
||||||
rsource "nvs_flash/Kconfig"
|
rsource "nvs_flash/Kconfig"
|
||||||
rsource "rmt/Kconfig"
|
rsource "rmt/Kconfig"
|
||||||
|
rsource "sdmmc/Kconfig"
|
||||||
rsource "spi_flash/Kconfig"
|
rsource "spi_flash/Kconfig"
|
||||||
rsource "spi_ram/Kconfig"
|
rsource "spi_ram/Kconfig"
|
||||||
rsource "usb/Kconfig"
|
rsource "usb/Kconfig"
|
||||||
|
@ -48,6 +48,10 @@ ifneq (,$(filter esp_idf_rmt,$(USEMODULE)))
|
|||||||
DIRS += rmt
|
DIRS += rmt
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter esp_idf_sdmmc,$(USEMODULE)))
|
||||||
|
DIRS += sdmmc
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter esp_idf_spi_flash,$(USEMODULE)))
|
ifneq (,$(filter esp_idf_spi_flash,$(USEMODULE)))
|
||||||
DIRS += spi_flash
|
DIRS += spi_flash
|
||||||
endif
|
endif
|
||||||
|
17
cpu/esp32/esp-idf/sdmmc/Kconfig
Normal file
17
cpu/esp32/esp-idf/sdmmc/Kconfig
Normal 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.
|
18
cpu/esp32/esp-idf/sdmmc/Makefile
Normal file
18
cpu/esp32/esp-idf/sdmmc/Makefile
Normal 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
|
@ -52,6 +52,7 @@ extern "C" {
|
|||||||
#define CPU_INUM_FRC2 20 /**< Level interrupt with medium priority 2 */
|
#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_SYSTIMER 20 /**< Level interrupt with medium priority 2 */
|
||||||
#define CPU_INUM_BLE 21 /**< 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 */
|
#define CPU_INUM_CACHEERR 25 /**< Level interrupt with high priority 4 */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
@ -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
|
* @name SPI configuration
|
||||||
*
|
*
|
||||||
@ -857,7 +933,8 @@ typedef struct {
|
|||||||
* @name USB device configuration
|
* @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"
|
#include "usbdev_synopsys_dwc2.h"
|
||||||
|
|
||||||
|
@ -95,8 +95,6 @@ extern "C" {
|
|||||||
* - Vref can be read with function #adc_line_vref_to_gpio at GPIO25.
|
* - Vref can be read with function #adc_line_vref_to_gpio at GPIO25.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @} */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name DAC configuration
|
* @name DAC configuration
|
||||||
*
|
*
|
||||||
@ -130,6 +128,10 @@ extern "C" {
|
|||||||
* these channels can be clocked by one of the 4 timers.
|
* these channels can be clocked by one of the 4 timers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name SDMMC configuration
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name SPI configuration
|
* @name SPI configuration
|
||||||
*
|
*
|
||||||
@ -213,6 +215,7 @@ extern "C" {
|
|||||||
#define TIMER_NUMOF (2)
|
#define TIMER_NUMOF (2)
|
||||||
#define TIMER_CHANNEL_NUMOF (1)
|
#define TIMER_CHANNEL_NUMOF (1)
|
||||||
#endif
|
#endif
|
||||||
|
/** @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name UART configuration
|
* @name UART configuration
|
||||||
|
@ -92,6 +92,9 @@ static const struct intr_handle_data_t _irq_data_table[] = {
|
|||||||
#elif defined(CPU_FAM_ESP32S3)
|
#elif defined(CPU_FAM_ESP32S3)
|
||||||
{ ETS_LCD_CAM_INTR_SOURCE, CPU_INUM_LCD, 1 },
|
{ ETS_LCD_CAM_INTR_SOURCE, CPU_INUM_LCD, 1 },
|
||||||
#endif
|
#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)
|
#define IRQ_DATA_TABLE_SIZE ARRAY_SIZE(_irq_data_table)
|
||||||
|
481
cpu/esp32/periph/sdmmc.c
Normal file
481
cpu/esp32/periph/sdmmc.c
Normal 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,
|
||||||
|
};
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user