diff --git a/drivers/Kconfig b/drivers/Kconfig
index 13ffedf107..ef979f5b26 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -161,6 +161,7 @@ rsource "mtd_sdcard/Kconfig"
rsource "nvram/Kconfig"
rsource "nvram_spi/Kconfig"
rsource "sdcard_spi/Kconfig"
+rsource "sdmmc/Kconfig"
endmenu # Storage Device Drivers
endmenu # Drivers
diff --git a/drivers/include/sdmmc/sdmmc.h b/drivers/include/sdmmc/sdmmc.h
new file mode 100644
index 0000000000..f1ea8dbeb5
--- /dev/null
+++ b/drivers/include/sdmmc/sdmmc.h
@@ -0,0 +1,1909 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup drivers_sdmmc SDIO/SD/MMC Device API (SDMMC)
+ * @ingroup drivers_periph
+ * @ingroup drivers_storage
+ *
+ * @experimental This API is experimental and in an early state - expect
+ * changes!
+ *
+ * # Overview
+ *
+ * The SDIO/SD/MMC Device API (SDMMC) implements an SD host controller driver
+ * that provides a high-level API using a low-level SDIO/SD/MMC peripheral
+ * driver for accessing
+ *
+ * - MultiMediaCards (MMC) and Embedded MultiMediaCards (eMMC)
+ * - SD Memory Cards (SD Cards) with Standard Capacity (SDSC),
+ * High Capacity (SDHC) or Extended Capacity (SDXC).
+ *
+ * In the context of this API, the term _Card_ refers to either
+ * - a removable card such as SD Memory Card, SDIO Card, Combo Card
+ * or MMC Card or
+ * - an embedded device such as Embedded SD Memory Device, Embedded SDIO device,
+ * or eMMC device.
+ *
+ * The term _slot_ refers to the interface for a removable card.
+ *
+ * The SDIO/SD/MMC device API (SDMMC) is divided into two parts:
+ *
+ * 1. The high-level API that implements the SD Host Controller driver and
+ * allows
+ * - to inititialize and identify different types of cards,
+ * - to access them either blockwise or bytewise,
+ * - to get information about the used card, and
+ * - to send single commands or application specific commands to the card.
+ *
+ * 2. The low-level SDIO/SD/MMC peripheral driver of type @ref sdmmc_driver_t
+ * implements the low-level functions required by the high-level device API.
+ * It has to be implemented for each MCU. The support of the low-level
+ * SDIO/SD/MMC peripheral driver is indicated by the MCU or board by
+ * the `periph_sdmmc` feature.
+ *
+ * Currently the SDIO/SD/MMC Device API supports the following card types:
+ *
+ *
+ * | Card Type | Support | Remark |
+ * |:--------------------------------|:---:|------------|
+ * | MMC/eMMC in MultiMediaCard mode | yes | |
+ * | MMC/eMMC in SPI mode | no | |
+ * | SD Memory Card in SD mode | yes | SDSC, SDHC and SDXC with Default or High Speed |
+ * | SD Memory Card in SPI mode | no | |
+ * | SDIO in SD mode | no | |
+ * | SDIO in SPI mode | no | |
+ *
+ *
+ * # Limitations:
+ *
+ * - Only one card per SDIO/SD/MMC device is supported.
+ * - eMMCs specific features are not supported.
+ * - UHS-I, UHS-II and UHS-III are not supported.
+ *
+ * # Features and Modules
+ *
+ * A board that uses an MCU with an SDIO/SD/MMC peripheral and has a card
+ * slot or device connected has to indicate this as feature `periph_sdmmc`
+ * in order to use the SDIO/SD/MMC API or drivers based on it.
+ * Furthermore, specific features of the SDIO/SD/MMC peripheral or
+ * configurations of the board have to be defined by corresponding features.
+ *
+ * These are in detail:
+ *
+ * - `periph_sdmmc` (`HAS_PERIPH_SDMMC` in Kconfig)
+ * indicates that an SDIO/SD/MMC peripheral is present and used by the
+ * board. This feature shall be provided by the board configuration.
+ *
+ * - `periph_sdmmc_8bit` (`HAS_PERIPH_SDMMC_8BIT` in Kconfig)
+ * indicates that the SDIO/SD/MMC peripheral supports the 8-bit bus width
+ * and at least one component of the board is connected with 8 data lines.
+ * This feature shall be provided by the board configuration, if available.
+ *
+ * - `periph_sdmmc_auto_clk` (`HAS_PERIPH_SDMMC_AUTO_CLK` in Kconfig)
+ * indicates that the SDIO/SD/MMC peripheral supports the Auto-CLK
+ * feature, i.e. the automatic activation and deactivation of the SD CLK
+ * signal when required to save power. This function shall be provided by
+ * the MCU if supported.
+ *
+ * - `periph_sdmmc_auto_cmd12` (`HAS_PERIPH_SDMMC_AUTO_CMD12` in Kconfig)
+ * indicates that the SDIO/SD/MMC peripheral supports the Auto-CMD12
+ * feature, i.e. CMD12 is sent automatically to stop the transmission in
+ * multiple block operations. This feature shall be provided by the MCU
+ * if supported.
+ *
+ * - `periph_sdmmc_hs` (`HAS_PERIPH_SDMMC_HS` in Kconfig)
+ * indicates that the SDIO/SD/MMC peripheral supports the high speed
+ * access, that is 50 MHz for SD and 52 MHz for MMC. This feature shall be
+ * provided by the MCU if supported.
+
+ * - `periph_sdmmc_mmc` (`HAS_PERIPH_SDMMC_MMC` in Kconfig)
+ * indicates that the SDIO/SD/MMC peripheral supports MMC/eMMCs. This
+ * feature shall be provided by the MCU if supported.
+ *
+ * Some functionalities of the SDIO/SD/MMC Device API must be explicitly
+ * enabled via modules:
+ *
+ * - `sdmmc_mmc` (`MODULE_SDMMC_MMC` in Kconfig)
+ * enables the support for MMCs/eMMCs.
+ *
+ * - `periph_sdmmc_8bit` (`MODULE_PERIPH_SDMMC_8BIT` in Kconfig)
+ * enables the 8-bit bus width support. It requires the corresponding
+ * feature of the board.
+ *
+ * @{
+ *
+ * @file
+ * @brief SDIO/SD/MMC device API using a low-level peripheral driver
+ *
+ * @author Gunar Schorcht
+ */
+
+#ifndef SDMMC_SDMMC_H
+#define SDMMC_SDMMC_H
+
+#include
+
+#include "assert.h"
+#include "byteorder.h"
+#include "macros/units.h"
+#include "periph_conf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief CPU-specific requirements for SDIO/SD/MMC buffers
+ *
+ * Can be overridden by periph_cpu if needed by the SDMMC/SDIO peripheral.
+ * Example usage:
+ * ```
+ * SDMMC_CPU_DMA_REQUIREMENTS uint8_t buffer[64];
+ * ```
+ */
+#ifndef SDMMC_CPU_DMA_REQUIREMENTS
+#define SDMMC_CPU_DMA_REQUIREMENTS
+#endif
+
+/**
+ * @brief Instantiation type for SDIO/SD/MMC buffers
+ *
+ * Example usage:
+ * ```
+ * sdmmc_buf_t buffer[SDMMC_SDHC_BLOCK_SIZE];
+ * ```
+ */
+#define sdmmc_buf_t SDMMC_CPU_DMA_REQUIREMENTS uint8_t
+
+/**
+ * @brief Size of a single data block on SDHC/SDXC Cards in bytes
+ *
+ * The size of a data block depend on type and mode of the card. SDSC/SDXC
+ * use a fix size of 512 bytes.
+ */
+#define SDMMC_SDHC_BLOCK_SIZE (512)
+
+/**
+ * @name Command and Response related definitions
+ * @{
+ */
+
+/**
+ * @brief Command index
+ */
+#define SDMMC_CMD(n) (n)
+
+/**
+ * @brief Application specific command index prefix (Bit is 7 used)
+ */
+#define SDMMC_ACMD_PREFIX (1 << 7)
+
+/**
+ * @brief Application specific command index
+ */
+#define SDMMC_ACMD(n) (SDMMC_ACMD_PREFIX | SDMMC_CMD(n))
+
+/**
+ * @brief SDIO/SD/MMC Commands
+ *
+ * Commands used by the driver. Command indices are in range [0..63].
+ * Application specific command indices are ORed with
+ * @ref SDMMC_ACMD_PREFIX (0x80) and thus marked as application specific.
+ * @ref SDMMC_ACMD_PREFIX can be used to check whether a given command index
+ * specifies an application specific command.
+ */
+typedef enum {
+ SDMMC_CMD0 = SDMMC_CMD(0), /**< GO_IDLE_STATE */
+ SDMMC_CMD1 = SDMMC_CMD(1), /**< SEND_OP_COND */
+ SDMMC_CMD2 = SDMMC_CMD(2), /**< ALL_SEND_CID */
+ SDMMC_CMD3 = SDMMC_CMD(3), /**< SET_RELATIVE_ADDR */
+ SDMMC_CMD4 = SDMMC_CMD(4), /**< SET_DSR */
+ SDMMC_CMD5 = SDMMC_CMD(5), /**< SD_APP_OP_COND (SDIO only) */
+ SDMMC_CMD6 = SDMMC_CMD(6), /**< SWITCH */
+ SDMMC_CMD7 = SDMMC_CMD(7), /**< SELECT/DESELECT_CARD */
+ SDMMC_CMD8 = SDMMC_CMD(8), /**< SEND_IF_COND (SD), SEND_EXT_CSD (MMC) */
+ SDMMC_CMD9 = SDMMC_CMD(9), /**< SEND_CSD */
+ SDMMC_CMD10 = SDMMC_CMD(10), /**< SEND_CID */
+ SDMMC_CMD12 = SDMMC_CMD(12), /**< STOP_TRANSMISSION */
+ SDMMC_CMD13 = SDMMC_CMD(13), /**< SEND_STATUS */
+ SDMMC_CMD16 = SDMMC_CMD(16), /**< SET_BLOCKLEN */
+ SDMMC_CMD17 = SDMMC_CMD(17), /**< READ_SINGLE_BLOCK */
+ SDMMC_CMD18 = SDMMC_CMD(18), /**< READ_MULTIPLE_BLOCK */
+ SDMMC_CMD23 = SDMMC_CMD(23), /**< SET_BLOCK_COUNT */
+ SDMMC_CMD24 = SDMMC_CMD(24), /**< WRITE_BLOCK */
+ SDMMC_CMD25 = SDMMC_CMD(25), /**< WRITE_MULTIPLE_BLOCK */
+ SDMMC_CMD32 = SDMMC_CMD(32), /**< ERASE_WR_BLK_START */
+ SDMMC_CMD33 = SDMMC_CMD(33), /**< ERASE_WR_BLK_END */
+ SDMMC_CMD38 = SDMMC_CMD(38), /**< ERASE */
+ SDMMC_CMD52 = SDMMC_CMD(52), /**< IO_RW_DIRECT (SDIO only) */
+ SDMMC_CMD53 = SDMMC_CMD(53), /**< IO_RW_EXTENDED (SDIO only) */
+ SDMMC_CMD55 = SDMMC_CMD(55), /**< APP_CMD */
+ SDMMC_CMD58 = SDMMC_CMD(58), /**< READ_OCR (SPI mode only) */
+ SDMMC_CMD59 = SDMMC_CMD(59), /**< CRC_ON_OFF (SPI mode only) */
+ SDMMC_ACMD6 = SDMMC_ACMD(6), /**< SET_BUS_WIDTH */
+ SDMMC_ACMD13 = SDMMC_ACMD(13), /**< SD_STATUS */
+ SDMMC_ACMD23 = SDMMC_ACMD(23), /**< SET_WR_BLK_ERASE_COUNT */
+ SDMMC_ACMD41 = SDMMC_ACMD(41), /**< SD_APP_OP_COND */
+ SDMMC_ACMD51 = SDMMC_ACMD(51), /**< SEND_SCR */
+} sdmmc_cmd_t;
+
+/** Command argument if no argument is required*/
+#define SDMMC_CMD_NO_ARG (0x00000000UL)
+
+/** Command argument if RCA is used in addressed commands */
+#define SDMMC_CMD_ARG_RCA(n) ((uint32_t)n << 16)
+
+/** Mask to check whether the response type uses CRC7 */
+#define SDMMC_RESP_CRC (1UL << 4)
+/** Mask to check whether the response includes busy status from card */
+#define SDMMC_RESP_BUSY (1UL << 5)
+/** Mask of response index */
+#define SDMMC_RESP_IDX (0xf)
+
+/**
+ * @brief SDIO/SD/MMC Response types
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 9.00
+ * [[sdcard.org](https://www.sdcard.org)] and \n
+ * JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 7.10 for R4 and R5
+ * [[jedec.org](https://www.jedec.org)]
+ */
+typedef enum {
+ SDMMC_NO_R = 0, /**< No response expected */
+ SDMMC_R1 = 1 | SDMMC_RESP_CRC, /**< Normal Response [48 bit (32 bit card status)]*/
+ SDMMC_R1B = 1 | SDMMC_RESP_CRC
+ | SDMMC_RESP_BUSY, /**< Normal Response
+ [48 bit (R1 with optional busy signal on DAT0)] */
+ SDMMC_R2 = 2 | SDMMC_RESP_CRC, /**< CID/CSD [136 bit (128 bit CID or CSD)] */
+ SDMMC_R3 = 3, /**< OCR Resister [48 bit (32 bit OCR)] */
+ SDMMC_R4 = 4, /**< Fast I/O [48 bit (16 bit RCA, 1 bit status,
+ 7 bit addr, 8 bit reg)] */
+ SDMMC_R5 = 5 | SDMMC_RESP_CRC, /**< Interrupt Request
+ [48 bit (16 bit RCA, 16 bit not defined) */
+ SDMMC_R6 = 6 | SDMMC_RESP_CRC, /**< Published RCA Response [48 bit (16 bit RCA,
+ 16 bit card status) */
+ SDMMC_R7 = 7 | SDMMC_RESP_CRC, /**< Card Interface Condition [48 bit] (32 bit data,
+ see section 4.9.6) */
+} sdmmc_resp_t;
+/** @} */
+
+/**
+ * @name Definitions used for CMD8 - SEND_IF_COND (SD only)
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 4.3.13
+ * [[sdcard.org](https://www.sdcard.org)]
+ * @{
+ */
+
+#define SDMMC_CMD8_CHECK_PATTERN (0xaa) /**< Check pattern, 0xAA recommended */
+#define SDMMC_CMD8_VHS_27_36V (0b0001 << 8) /**< Voltage Supplied by host 2.7-3.6V */
+#define SDMMC_CMD8_PCIE_AVAIL (1 << 12) /**< PCIe Availability (not yet used) */
+#define SDMMC_CMD8_PCIE_12V (1 << 13) /**< PCIe 1.2V Support (not yet used) */
+
+/** Command argument used in CMD8 */
+#define SDMMC_CMD8_CHECK (SDMMC_CMD8_VHS_27_36V | SDMMC_CMD8_CHECK_PATTERN)
+/** @} */
+
+/**
+ * @name OCR Register Definition
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 5.1,
+ * Table 5-1 [[sdcard.org](https://www.sdcard.org)]
+ * @{
+ */
+#define SDMMC_OCR_18V (1UL << 7) /**< Low Voltage Range */
+#define SDMMC_OCR_27_28V (1UL << 15) /**< 2.7V to 2.8V Range */
+#define SDMMC_OCR_28_29V (1UL << 16) /**< 2.8V to 2.9V Range */
+#define SDMMC_OCR_29_30V (1UL << 17) /**< 2.9V to 3.0V Range */
+#define SDMMC_OCR_30_31V (1UL << 18) /**< 3.0V to 3.1V Range */
+#define SDMMC_OCR_31_32V (1UL << 19) /**< 3.1V to 3.2V Range */
+#define SDMMC_OCR_32_33V (1UL << 20) /**< 3.2V to 3.3V Range */
+#define SDMMC_OCR_33_34V (1UL << 21) /**< 3.3V to 3.4V Range */
+#define SDMMC_OCR_34_35V (1UL << 22) /**< 3.4V to 3.5V Range */
+#define SDMMC_OCR_35_36V (1UL << 23) /**< 3.5V to 3.6V Range */
+
+#define SDMMC_OCR_S18A (1UL << 24) /**< Switching to 1.8V Accepted */
+#define SDMMC_OCR_OVER_2TB (1UL << 27) /**< Over 2TB support status (CO2T) */
+#define SDMMC_OCR_UHS_II (1UL << 29) /**< UHS-II Card Status */
+#define SDMMC_OCR_CCS (1UL << 30) /**< Card Capacity Status (CCS) */
+#define SDMMC_OCR_POWER_UP (1UL << 31) /**< Card power up status bit (busy) */
+
+/**
+ * @brief Voltage profile for the range 2.7-3.6V as defined for MMC
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8, Table 29
+ * [[jedec.org](https://www.jedec.org)]
+ */
+#define SDMMC_OCR_ALL_VOLTAGES (SDMMC_OCR_27_28V | SDMMC_OCR_28_29V | \
+ SDMMC_OCR_29_30V | SDMMC_OCR_30_31V | \
+ SDMMC_OCR_31_32V | SDMMC_OCR_32_33V | \
+ SDMMC_OCR_33_34V | SDMMC_OCR_34_35V | \
+ SDMMC_OCR_35_36V)
+/** @} */
+
+/**
+ * @brief SDIO/SD/MMC Card types
+ *
+ * To be able to specify a Combo card (combined SD Memory and SDIO card),
+ * the enumeration values are defined bitwise so that they can be ORed to
+ * represent a Combo card with different versions of the SD Memory part. For
+ * example, `SDMMC_CARD_TYPE_SDIO | SDMMC_CARD_TYPE_SDSC_V2_V3` represents a
+ * Combo card with SDIO function and SD Memory Card Standard Capacity (SDSC)
+ * Version 2.x+.
+ */
+typedef enum {
+ SDMMC_CARD_TYPE_UNKNOWN = 0x00, /**< Card type unknown */
+ SDMMC_CARD_TYPE_SDSC_V1 = 0x01, /**< SD Memory Card Standard Capacity (SDSC) Version 1.x */
+ SDMMC_CARD_TYPE_SDSC_V2_V3 = 0x02, /**< SD Memory Card Standard Capacity (SDSC) Version 2.x+ */
+ SDMMC_CARD_TYPE_SDHC_SDXC = 0x04, /**< SD Memory Card High or Extended Capacity (SDHC/SDXC) */
+ /* bits 3..5 are left unused for later extensions of SD Memory Card types */
+ SDMMC_CARD_TYPE_SDIO = 0x40, /**< SDIO Card */
+ SDMMC_CARD_TYPE_MMC = 0x80, /**< MultiMedia Card */
+} sdmmc_card_type_t;
+
+/**
+ * @brief Mask for any type of SD Memory card
+ */
+#define SDMMC_CARD_TYPE_SD (SDMMC_CARD_TYPE_SDSC_V1 | \
+ SDMMC_CARD_TYPE_SDSC_V2_V3 | \
+ SDMMC_CARD_TYPE_SDHC_SDXC)
+
+/**
+ * @brief SDIO/SD/MMC Card data bus widths
+ *
+ * Possible bus widths are given as integer values as defined in SD Status
+ * register and the SCR register for SD Cards.
+ *
+ * @warning The values differ from the bus widths used in ACMD6 for SD Cards
+ * and the CCC register in SDIO Cards.
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, 4.10.2,
+ * Table 4-44, SD Status [[sdcard.org](https://www.sdcard.org)] \n
+ */
+typedef enum {
+ SDMMC_BUS_WIDTH_1BIT = 1, /**< Data bus width is 1 bit (default) */
+ SDMMC_BUS_WIDTH_4BIT = 4, /**< Data bus width is 4 bit */
+ SDMMC_BUS_WIDTH_8BIT = 8, /**< Data bus width is 8 bit */
+} sdmmc_bus_width_t;
+
+/**
+ * @brief SDIO/SD/MMC Card clock rate types
+ *
+ * Identifies the clock frequency to be used. The clock frequency in
+ * identification mode f_OD (Open Drain mode) is fixed and is f_OD = 400 kHz.
+ * The actual clock frequency in data transfer mode f_PP (Push-Pull mode)
+ * depends on the SDIO/SD/MMC device and the card used.
+ * The low-level SDIO/SD/MMC peripheral driver sets the actual clock rate
+ * in function sdmmc_driver_t::set_clock_rate.
+ */
+typedef enum {
+ SDMMC_CLK_400K = KHZ(400), /**< Identification Mode f_OD (400 kHz) */
+ SDMMC_CLK_20M = MHZ(20), /**< MMC Card in Data Transfer Mode (Backward Compatibility) */
+ SDMMC_CLK_25M = MHZ(25), /**< SD/SDIO Card in Data Transfer Mode (Default Speed) */
+ SDMMC_CLK_26M = MHZ(26), /**< MMC/eMMC Card in Data Transfer Mode (Default Speed) */
+ SDMMC_CLK_50M = MHZ(50), /**< SD/SDIO Card in Data Transfer Mode (High Speed) */
+ SDMMC_CLK_52M = MHZ(52), /**< MMC/eMMC in Data Transfer Mode (High Speed) */
+} sdmmc_clock_rate_t;
+
+/**
+ * @name SDIO/SD/MMC Card status as returned in responses R1 to CMD13 with b[15]=0
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 4.10.1,
+ * Table 4-42 [[sdcard.org](https://www.sdcard.org)] \n
+ * SDIO Simplified Specification Version 3.00, 4.10.8, Table 4-7,
+ * [[sdcard.org](https://www.sdcard.org)] \n
+ * JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 7.11, Table 25
+ * [[jedec.org](https://www.jedec.org)]
+ * @{
+ */
+#define SDMMC_CARD_STATUS_OUT_OF_RANGE (1UL << 31) /**< SD/SDIO and MMC */
+#define SDMMC_CARD_STATUS_ADDRESS_ERROR (1UL << 30) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_BLOCK_LEN_ERROR (1UL << 29) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_ERASE_SEQ_ERROR (1UL << 28) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_ERASE_PARAM (1UL << 27) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_WP_VIOLATION (1UL << 26) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_CARD_IS_LOCKED (1UL << 25) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_LOCK_UNLOCK_FAILED (1UL << 24) /**< SD/SDIO and MMC */
+#define SDMMC_CARD_STATUS_COM_CRC_ERROR (1UL << 23) /**< SD/SDIO and MMC */
+#define SDMMC_CARD_STATUS_ILLEGAL_COMMAND (1UL << 22) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_CARD_ECC_FAILED (1UL << 21) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_CC_ERROR (1UL << 20) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_ERROR (1UL << 19) /**< SD/SDIO and MMC */
+#define SDMMC_CARD_STATUS_UNDERRUN (1UL << 18) /**< MMC only */
+#define SDMMC_CARD_STATUS_OVERRUN (1UL << 17) /**< MMC only */
+#define SDMMC_CARD_STATUS_CSD_OVERWRITE (1UL << 16) /**< SD (CSD), MMC (CSD and CID) */
+#define SDMMC_CARD_STATUS_WP_ERASE_SKIP (1UL << 15) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_CARD_ECC_DISABLED (1UL << 14) /**< SD only */
+#define SDMMC_CARD_STATUS_ERASE_RESET (1UL << 13) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_READY_FOR_DATA (1UL << 8) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_SWITCH_ERROR (1UL << 7) /**< MMC only */
+#define SDMMC_CARD_STATUS_FX_EVENT (1UL << 6) /**< SD only */
+#define SDMMC_CARD_STATUS_APP_CMD (1UL << 5) /**< SD and MMC */
+#define SDMMC_CARD_STATUS_AKE_SEQ_ERROR (1UL << 3) /**< SD only */
+
+/** SD/MMC Card status mask for error flags */
+#define SDMMC_CARD_STATUS_ERRORS (SDMMC_CARD_STATUS_OUT_OF_RANGE | \
+ SDMMC_CARD_STATUS_ADDRESS_ERROR | \
+ SDMMC_CARD_STATUS_BLOCK_LEN_ERROR | \
+ SDMMC_CARD_STATUS_ERASE_SEQ_ERROR | \
+ SDMMC_CARD_STATUS_ERASE_PARAM | \
+ SDMMC_CARD_STATUS_WP_VIOLATION | \
+ SDMMC_CARD_STATUS_LOCK_UNLOCK_FAILED | \
+ SDMMC_CARD_STATUS_COM_CRC_ERROR | \
+ SDMMC_CARD_STATUS_ILLEGAL_COMMAND | \
+ SDMMC_CARD_STATUS_CARD_ECC_FAILED | \
+ SDMMC_CARD_STATUS_CC_ERROR | \
+ SDMMC_CARD_STATUS_ERROR | \
+ SDMMC_CARD_STATUS_UNDERRUN | \
+ SDMMC_CARD_STATUS_OVERRUN | \
+ SDMMC_CARD_STATUS_CSD_OVERWRITE | \
+ SDMMC_CARD_STATUS_WP_ERASE_SKIP | \
+ SDMMC_CARD_STATUS_SWITCH_ERROR | \
+ SDMMC_CARD_STATUS_AKE_SEQ_ERROR)
+
+/** SD/MMC Card status mask for current state */
+#define SDMMC_CARD_STATUS_CURRENT_STATE(n) (((n) >> SDMMC_CARD_STATUS_CURRENT_STATE_Pos) & 0x0f)
+#define SDMMC_CARD_STATUS_CURRENT_STATE_Pos (9) /**< CURRENT_STATE position */
+
+/**
+ * @brief SD/MMC Card states
+ */
+enum {
+ SDMMC_CARD_STATE_IDLE = 0, /**< Idle */
+ SDMMC_CARD_STATE_READY = 1, /**< Ready */
+ SDMMC_CARD_STATE_IDENT = 2, /**< Identification */
+ SDMMC_CARD_STATE_STBY = 3, /**< Stand-by */
+ SDMMC_CARD_STATE_TRAN = 4, /**< Transfer */
+ SDMMC_CARD_STATE_DATA = 5, /**< Data */
+ SDMMC_CARD_STATE_RCV = 6, /**< Receive */
+ SDMMC_CARD_STATE_PRG = 7, /**< Programming */
+ SDMMC_CARD_STATE_DIS = 8, /**< Disconnect */
+ SDMMC_CARD_STATE_BTST = 9, /**< Bus Test */
+};
+
+/**
+ * @brief SD/MMC Card status as structure
+ */
+typedef union {
+ struct {
+ uint32_t reserved0 :2; /**< [1:0] Reserved for appl. specific commands */
+ uint32_t reserved2 :1; /**< [2] Reserved for appl. specific commands */
+ uint32_t AKE_SEQ_ERROR :1; /**< [3] SD only, Reserved in MMC */
+ uint32_t reserved4 :1; /**< [4] Reserved */
+ uint32_t APP_CMD :1; /**< [5] SD and MMC */
+ uint32_t FX_EVENT :1; /**< [6] SD only, Reserved in MMC */
+ uint32_t SWITCH_ERROR :1; /**< [7] MMC only, reserved in SD/SDIO */
+ uint32_t READY_FOR_DATA :1; /**< [8] SD and MMC */
+ uint32_t CURRENT_STATE :4; /**< [12:9] SD and MMC */
+ uint32_t ERASE_RESET :1; /**< [13] SD and MMC */
+ uint32_t CARD_ECC_DISABLED :1; /**< [14] SD only, reserved in MMC */
+ uint32_t WP_ERASE_SKIP :1; /**< [15] SD and MMC */
+ uint32_t CSD_OVERWRITE :1; /**< [16] SD (CSD), MMC (CSD and CID) */
+ uint32_t OVERRUN :1; /**< [17] MMC only, DEFERRED_RESPONSE in SD */
+ uint32_t UNDERRUN :1; /**< [18] MMC only, reserved in SD */
+ uint32_t ERROR :1; /**< [19] SD/SDIO and MMC */
+ uint32_t CC_ERROR :1; /**< [20] SD and MMC */
+ uint32_t CARD_ECC_FAILED :1; /**< [21] SD and MMC */
+ uint32_t ILLEGAL_COMMAND :1; /**< [22] SD/SDIO and MMC */
+ uint32_t COM_CRC_ERROR :1; /**< [23] SD/SDIO and MMC */
+ uint32_t LOCK_UNLOCK_FAILED:1; /**< [24] SD and MMC */
+ uint32_t CARD_IS_LOCKED :1; /**< [25] SD and MMC */
+ uint32_t WP_VIOLATION :1; /**< [26] SD and MMC */
+ uint32_t ERASE_PARAM :1; /**< [27] SD and MMC */
+ uint32_t ERASE_SEQ_ERROR :1; /**< [28] SD and MMC */
+ uint32_t BLOCK_LEN_ERROR :1; /**< [29] SD and MMC */
+ uint32_t ADDRESS_ERROR :1; /**< [30] SD and MMC */
+ uint32_t OUT_OF_RANGE :1; /**< [31] SD/SDIO and MMC */
+ };
+ uint32_t value; /**< SD/MMC Card status as 32 bit value */
+} sdmmc_card_status_t;
+
+/** @} */
+
+/**
+ * @name SD Status register (SD Memory Cards)
+ * @{
+ */
+
+/** SD status register size */
+#define SDMMC_SD_STATUS_SIZE (64)
+
+/**
+ * @brief SD Status (SD Memory Card only)
+ *
+ * SD Status has a size of 512 bits and contains the status of SD Memory Card
+ * features. It is not supported by MMCs and SDIO Cards. In Combo Cards, it
+ * reflects the status of the SD Memory Card portion.
+ *
+ * Members are sorted by types to reduce the required RAM size.
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 4.10.2,
+ * Table 4-44 [[sdcard.org](https://www.sdcard.org)]
+ */
+typedef struct __attribute__((packed)) {
+ uint32_t SIZE_OF_PROTECTED_AREA:32; /**< [479:448] Section 4.10.2.1 */
+ uint32_t SUS_ADDR:22; /**< [367:346] Section 4.10.2.12, Table 4-57 */
+ uint32_t VSC_AU_SIZE:10; /**< [377:368] Section 4.10.2.11, Table 4-56 */
+ uint16_t SD_CARD_TYPE:16; /**< [495:480] Section 4.10.2, Table 4-44 */
+ uint16_t ERASE_SIZE:16; /**< [423:408] Section 4.10.2.5, Table 4-49 */
+ uint8_t SPEED_CLASS:8; /**< [447:440] Section 4.10.2.2, Table 4-45 */
+ uint8_t PERFORMANCE_MOVE:8; /**< [439:432] Section 4.10.2.3, Table 4-46 */
+ uint8_t VIDEO_SPEED_CLASS:8; /**< [391:384] Section 4.10.2.10, Table 4-54 */
+ uint8_t ERASE_TIMEOUT:6; /**< [407:402] Section 4.10.2.6, Table 4-50 */
+ uint8_t ERASE_OFFSET:2; /**< [401:400] Section 4.10.2.7, Table 4-51 */
+ uint8_t UHS_SPEED_GRADE:4; /**< [399:396] Section 4.10.2.8, Table 4-52 */
+ uint8_t UHS_AU_SIZE:4; /**< [395:392] Section 4.10.2.9, Table 4-3 */
+ uint8_t AU_SIZE:4; /**< [431:428] Section 4.10.2.4, Table 4-47 */
+ uint8_t DAT_BUS_WIDTH:2; /**< [511:510] Section 4.10.2, Table 4-44 */
+ uint8_t SECURED_MODE:1; /**< [509] Section 4.10.2, Table 4-44 */
+} sdmmc_sd_status_t;
+
+/** @} */
+
+/**
+ * @name CID register (SD Memory and MMC Cards)
+ * @{
+ */
+
+/** CID register size in bytes (CID is 128 bit) */
+#define SDMMC_CID_REG_SIZE (16)
+
+#define SDMMC_CID_OID_SIZE_SD (2) /**< OID (OEM/Application ID) size in byte (SD) */
+#define SDMMC_CID_PNM_SIZE_SD (5) /**< PNM (Product name) size in byte (SD) */
+#define SDMMC_CID_PNM_SIZE_MMC (6) /**< PNM (Product name) size in byte (MMC) */
+
+/**
+ * @brief CID register structure (SD Memory Cards)
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 5.2,
+ * Table 5.2 [[sdcard.org](https://www.sdcard.org)]
+ */
+typedef struct __attribute__((packed)) {
+ uint8_t MID; /**< Manufacturer ID */
+ char OID[SDMMC_CID_OID_SIZE_SD]; /**< OEM/Application ID */
+ char PNM[SDMMC_CID_PNM_SIZE_SD]; /**< Product name */
+ uint8_t PRV; /**< Product revision */
+ be_uint32_t PSN; /**< Product serial number in big-endian order */
+ be_uint16_t MDT; /**< Manufacturing date in big-endian order */
+ uint8_t CID_CRC; /**< CRC7 checksum including bit 0 */
+} sdmmc_cid_sd_t;
+
+/**
+ * @brief CID register structure (MMC)
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8.2, Table 30
+ * [[jedec.org](https://www.jedec.org)]
+ */
+typedef struct __attribute__((packed)) {
+ uint8_t MID; /**< Manufacturer ID */
+ be_uint16_t OID; /**< OEM/Application ID in big-endian order */
+ char PNM[SDMMC_CID_PNM_SIZE_MMC]; /**< Product name */
+ uint8_t PRV; /**< Product revision */
+ be_uint32_t PSN; /**< Product serial number in big-endian order */
+ uint8_t MDT; /**< Manufacturing date */
+ uint8_t CID_CRC; /**< CRC7 checksum including bit 0 */
+} sdmmc_cid_mmc_t;
+
+/**
+ * @brief CID register structure (SD Memory and MMC Cards)
+ */
+typedef union {
+ sdmmc_cid_sd_t sd; /**< CID register of SD Memory Cards */
+ sdmmc_cid_mmc_t mmc; /**< CID register of MMC */
+} sdmmc_cid_t;
+
+/** @} */
+
+/**
+ * @name CSD Register Definitions
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 5.3,
+ * [[sdcard.org](https://www.sdcard.org)] \n
+ * JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8.3, Table 35
+ * [[jedec.org](https://www.jedec.org)]
+ * @{
+ */
+
+/**
+ * @brief CSD Register Versions (SD Memory Card and MMC)
+ */
+typedef enum {
+ SDMMC_CSD_V1 = 0, /**< CSD Version 1.0 (SDSC and MMC) */
+ SDMMC_CSD_V2 = 1, /**< CSD Version 2.0 (SDHC/SDXC) or Version 1.1 (MMC) */
+ SDMMC_CSD_V3 = 2, /**< CSD Version 3.0 (SDUC) or Version 1.2 (MMC) */
+ SDMMC_CSD_Vx = 3, /**< Reserved (SD) or Version in EXT_CSD (MMC) */
+} sdmmc_csd_version_t;
+
+/** CSD register size in byte (CSD is 128 bit) */
+#define SDMMC_CSD_REG_SIZE (16)
+
+/**
+ * @brief CSD register structure Version 1.0
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 5.3.2,
+ * Table 5.4 [[sdcard.org](https://www.sdcard.org)]
+ */
+typedef struct __attribute__((packed)) {
+ uint32_t CSD_CRC:8; /**< CRC including End bit 1b [7:0] */
+ uint32_t reserved5:1; /**< reserved [8] */
+ uint32_t WP_UPC:1; /**< write protection until power cycle [9] */
+ uint32_t FILE_FORMAT:2; /**< File format [11:10] */
+ uint32_t TMP_WRITE_PROTECT:1; /**< temporary write protection [12] */
+ uint32_t PERM_WRITE_PROTECT:1; /**< permanent write protection [13] */
+ uint32_t COPY:1; /**< copy flag [14] */
+ uint32_t FILE_FORMAT_GRP:1; /**< File format group [15] */
+ uint32_t reserved4:5; /**< reserved [20:16] */
+ uint32_t WRITE_BL_PARTIAL:1; /**< partial blocks for write allowed [21] */
+ uint32_t WRITE_BL_LEN:4; /**< max. write data block length [25:22] */
+ uint32_t R2W_FACTOR:3; /**< write speed factor [28:26] */
+ uint32_t reserved3:2; /**< reserved [30:29] */
+ uint32_t WP_GRP_ENABLE:1; /**< write protect group enable [31] */
+ uint32_t WP_GRP_SIZE:7; /**< write protect group size [38:32] */
+ uint32_t SECTOR_SIZE:7; /**< erase sector size [45:39] */
+ uint32_t ERASE_BLK_EN:1; /**< erase single block enable [46] */
+ uint32_t C_SIZE_MULT:3; /**< device size multiplier [49:47] */
+ uint32_t VDD_W_CURR_MAX:3; /**< max. write current VDD max [52:50] */
+ uint32_t VDD_W_CURR_MIN:3; /**< max. write current VDD min [55:53] */
+ uint32_t VDD_R_CURR_MAX:3; /**< max. read current VDD max [58:56] */
+ uint32_t VDD_R_CURR_MIN:3; /**< max. read current VDD min [61:59] */
+ uint32_t C_SIZE:12; /**< device size [73:62] */
+ uint32_t reserved2:2; /**< reserved [75:74] */
+ uint32_t DSR_IMP:1; /**< DSR implemented [76] */
+ uint32_t READ_BLK_MISALIGN:1; /**< read block misalignment [77] */
+ uint32_t WRITE_BLK_MISALIGN:1; /**< write block misalignment [78] */
+ uint32_t READ_BL_PARTIAL:1; /**< partial blocks for read allowed [79] */
+ uint32_t READ_BL_LEN:4; /**< max. read data block length [83:80] */
+ uint32_t CCC:12; /**< card command classes [95:84] */
+ uint32_t TRAN_SPEED:8; /**< max. data transfer rate [103:96] */
+ uint32_t NSAC:8; /**< data read access-time-2 in CLK cycles [111:104] */
+ uint32_t TAAC:8; /**< data read access-time-1 [119:112] */
+ uint32_t reserved1:6; /**< reserved [125:120] */
+ uint32_t CSD_STRUCTURE:2; /**< CSD structure [127:126] */
+} sdmmc_csd_v1_t;
+
+/**
+ * @brief CSD register structure Version 2.0 and Version 3.0
+ *
+ * A combined format is used vor CSD Version 2.0 and 3.0 to reduce the code
+ * size. The only difference is the bit length of `C_SIZE`.
+ *
+ * @see Physical Layer Simplified Specification Version 9.00
+ * [[sdcard.org](https://www.sdcard.org)] \n
+ * - Version 2.0: Section 5.3.3, Table 5-16 \n
+ * - Version 3.0: Section 5.3.4, Table 5.3.4-1
+ */
+typedef struct __attribute__((packed)) {
+ uint32_t CSD_CRC:8; /**< CRC including End bit 1b [7:0] */
+ uint32_t reserved5:1; /**< reserved [8] */
+ uint32_t WP_UPC:1; /**< write protection until power cycle [9] */
+ uint32_t FILE_FORMAT:2; /**< File format [11:10] */
+ uint32_t TMP_WRITE_PROTECT:1; /**< temporary write protection [12] */
+ uint32_t PERM_WRITE_PROTECT:1; /**< permanent write protection [13] */
+ uint32_t COPY:1; /**< copy flag [14] */
+ uint32_t FILE_FORMAT_GRP:1; /**< File format group [15] */
+ uint32_t reserved4:5; /**< reserved [20:16] */
+ uint32_t WRITE_BL_PARTIAL:1; /**< partial blocks for write allowed [21] */
+ uint32_t WRITE_BL_LEN:4; /**< max. write data block length [25:22] */
+ uint32_t R2W_FACTOR:3; /**< write speed factor [28:26] */
+ uint32_t reserved3:2; /**< reserved [30:29] */
+ uint32_t WP_GRP_ENABLE:1; /**< write protect group enable [31] */
+ uint32_t WP_GRP_SIZE:7; /**< write protect group size [38:32] */
+ uint32_t SECTOR_SIZE:7; /**< erase sector size [45:39] */
+ uint32_t ERASE_BLK_EN:1; /**< erase single block enable [46] */
+ uint32_t reserved2:1; /**< reserved [47] */
+ uint32_t C_SIZE:28; /**< device size v2.0 [69:48], v3.0 [75:48] */
+ uint32_t DSR_IMP:1; /**< DSR implemented [76] */
+ uint32_t READ_BLK_MISALIGN:1; /**< read block misalignment [77] */
+ uint32_t WRITE_BLK_MISALIGN:1; /**< write block misalignment [78] */
+ uint32_t READ_BL_PARTIAL:1; /**< partial blocks for read allowed [79] */
+ uint32_t READ_BL_LEN:4; /**< max. read data block length [83:80] */
+ uint32_t CCC:12; /**< card command classes [95:84] */
+ uint32_t TRAN_SPEED:8; /**< max. data transfer rate [103:96] */
+ uint32_t NSAC:8; /**< data read access-time-2 in CLK cycles [111:104] */
+ uint32_t TAAC:8; /**< data read access-time-1 [119:112] */
+ uint32_t reserved1:6; /**< reserved [125:120] */
+ uint32_t CSD_STRUCTURE:2; /**< CSD structure [127:126] */
+} sdmmc_csd_v2_t;
+
+/**
+ * @brief CSD register structure for MMC
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8.3, Table 35
+ * [[jedec.org](https://www.jedec.org)]
+ */
+typedef struct __attribute__((packed)) {
+ uint32_t CSD_CRC:8; /**< CRC including End bit 1b [7:0] */
+ uint32_t ECC:2; /**< ECC code [9:8] */
+ uint32_t FILE_FORMAT:2; /**< File format [11:10] */
+ uint32_t TMP_WRITE_PROTECT:1; /**< Temporary write protection [12] */
+ uint32_t PERM_WRITE_PROTECT:1; /**< Permanent write protection [13] */
+ uint32_t COPY:1; /**< Copy flag [14] */
+ uint32_t FILE_FORMAT_GRP:1; /**< File format group [15] */
+ uint32_t CONTENT_PROT_APP:1; /**< Content protection application [16] */
+ uint32_t reserved4:4; /**< reserved [20:17] */
+ uint32_t WRITE_BL_PARTIAL:1; /**< partial blocks for write allowed [21] */
+ uint32_t WRITE_BL_LEN:4; /**< Max. write data block length [25:22] */
+ uint32_t R2W_FACTOR:3; /**< Write speed factor [28:26] */
+ uint32_t DEFAULT_ECC:2; /**< Manufacturer default ECC [30:29] */
+ uint32_t WP_GRP_ENABLE:1; /**< write protect group enable [31] */
+ uint32_t WP_GRP_SIZE:5; /**< Write protect group size [36:32] */
+ uint32_t ERASE_GRP_MULT:5; /**< Erase group size multiplier [41:37] */
+ uint32_t ERASE_GRP_SIZE:5; /**< Erase group size [46:42] */
+ uint32_t C_SIZE_MULT:3; /**< Device size multiplier [49:47] */
+ uint32_t VDD_W_CURR_MAX:3; /**< Max. write current VDD max [52:50] */
+ uint32_t VDD_W_CURR_MIN:3; /**< Max. write current VDD min [55:53] */
+ uint32_t VDD_R_CURR_MAX:3; /**< Max. read current VDD max [58:56] */
+ uint32_t VDD_R_CURR_MIN:3; /**< Max. read current VDD min [61:59] */
+ uint16_t C_SIZE:12; /**< Device size [73:62] */
+ uint32_t reserved2:2; /**< reserved [75:74] */
+ uint32_t DSR_IMP:1; /**< DSR implemented [76] */
+ uint32_t READ_BLK_MISALIGN:1; /**< Read block misalignment [77] */
+ uint32_t WRITE_BLK_MISALIGN:1; /**< Write block misalignment [78] */
+ uint32_t READ_BL_PARTIAL:1; /**< Partial blocks for read allowed [79] */
+ uint32_t READ_BL_LEN:4; /**< Max. read data block length [83:80] */
+ uint32_t CCC:12; /**< Card command classes [95:84] */
+ uint32_t TRAN_SPEED:8; /**< Max. bus clock frequency [103:96] */
+ uint32_t NSAC:8; /**< Data read access-time-2 in CLK cycles [111:104] */
+ uint32_t TAAC:8; /**< Data read access-time-1 [119:112] */
+ uint32_t reserved1:2; /**< reserved [121:120] */
+ uint32_t SPEC_VERS:4; /**< Specification version [125:122] */
+ uint32_t CSD_STRUCTURE:2; /**< CSD structure [127:126] */
+} sdmmc_csd_mmc_t;
+
+/**
+ * @brief CSD register (SD Memory Card and MMC)
+ */
+typedef union {
+ sdmmc_csd_v1_t v1; /**< CSD Version 1.0 */
+ sdmmc_csd_v2_t v2; /**< CSD Version 2.0 and 3.0 */
+ sdmmc_csd_mmc_t mmc; /**< CSD Version for MMC */
+} sdmmc_csd_t;
+
+/** Extended CSD (EXT_CSD) register size in byte (EXT_CSD is 512 byte) */
+#define SDMMC_EXT_CSD_REG_SIZE (512)
+
+/**
+ * @brief Extended CSD (EXT_CSD) register structure (MMC only)
+ *
+ * Since the Extended CSD register is a complete 512 byte block which is only
+ * sparsely used, only the elements that are absolutely needed are stored in
+ * the extended CSD register structure.
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8.4, Table 46
+ * [[jedec.org](https://www.jedec.org)]
+ */
+typedef struct {
+ uint32_t SEC_COUNT; /**< Sector Count [215:212] */
+ uint8_t CARD_TYPE; /**< Card Type [196] */
+ uint8_t CSD_STRUCTURE; /**< CSD Structure Version [194] */
+ uint8_t BUS_WIDTH; /**< Bus Width Mode [183] */
+} sdmmc_ext_csd_t;
+
+/** @} */
+
+/**
+ * @name SCR Register Definitions (SD Memory Card only)
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 5.6,
+ * Table 5-17 [[sdcard.org](https://www.sdcard.org)]
+ * @{
+ */
+
+/** SCR register size in byte (SCR is 64 bit) */
+#define SDMMC_SCR_REG_SIZE (8)
+
+/**
+ * @brief SCR register structure (SD Memory Card only)
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 5.6,
+ * Table 5-17 [[sdcard.org](https://www.sdcard.org)]
+ */
+typedef struct {
+ /* first 32-bit word */
+ uint32_t reserved0; /**< Reserved for manufacturer usage [31:0] */
+ /* second 32-bit word */
+ union {
+ struct {
+ uint32_t CMD_SUPPORT:5; /**< Command Support bits [36:32] */
+ uint32_t reserved37:1; /**< Reserved [37] */
+ uint32_t SD_SPECX:4; /**< Spec. Version 5.00 or higher [41:38] */
+ uint32_t SD_SPEC4:1; /**< Spec. Version 4.00 or higher [42] */
+ uint32_t EX_SECURITY:4; /**< Extended Security Support [46:43] */
+ uint32_t SD_SPEC3:1; /**< Spec. Version 3.00 or higher [47] */
+ uint32_t SD_BUS_WIDTHS:4; /**< DAT Bus widths supported [51:48] */
+ uint32_t SD_SECURITY:3; /**< CPRM Security Support [54:52] */
+ uint32_t DATA_STAT_AFTER_ERASE:1; /**< Data status after erases [55] */
+ uint32_t SD_SPEC:4; /**< SD Memory Card - Spec. Version [59:56] */
+ uint32_t SCR_STRUCTURE:4; /**< SCR Structure [63:60] */
+ };
+ uint32_t value;
+ };
+} sdmmc_scr_t;
+
+#define SDMMC_SCR_ACMD_53_54_SUPPORT (0b10000) /**< @ref sdmmc_scr_t::CMD_SUPPORT
+ Secure Receive/Send supported */
+#define SDMMC_SCR_ACMD_58_59_SUPPORT (0b01000) /**< @ref sdmmc_scr_t::CMD_SUPPORT
+ Extension Register Multi-Block */
+#define SDMMC_SCR_ACMD_48_49_SUPPORT (0b00100) /**< @ref sdmmc_scr_t::CMD_SUPPORT
+ Extension Register Single-Block */
+#define SDMMC_SCR_ACMD_23_SUPPORT (0b00010) /**< @ref sdmmc_scr_t::CMD_SUPPORT
+ Set Block Count */
+#define SDMMC_SCR_ACMD_20_SUPPORT (0b00001) /**< @ref sdmmc_scr_t::CMD_SUPPORT
+ Speed Class Control */
+
+/**
+ * @brief Get Physical Layer Specification Version value from SCR value
+ *
+ * @param scr SCR register value of type sdmmc_scr_t
+ *
+ * | Value | Physical Layer Specification Version Number |
+ * |---|:---------------------------------|
+ * | 0 | Version 1.0 and 1.01 |
+ * | 1 | Version 1.10 |
+ * | 2 | Version 2.00 |
+ * | 3 | Version 3.0X |
+ * | 4 | Version 4.XX |
+ * | 5 | Version 5.XX |
+ * | 6 | Version 6.XX |
+ * | 7 | Version 7.XX |
+ * | 8 | Version 8.XX |
+ * | 9 | Version 9.XX |
+ */
+#define SDMMC_SCR_SD_SPEC(scr) \
+ (scr.SD_SPEC + scr.SD_SPEC3 + (scr.SD_SPECX ? scr.SD_SPECX + 1 : scr.SD_SPEC4))
+
+/** @} */
+
+/**
+ * @brief SD/MMC device access macro
+ * @{
+ */
+#define SDMMC_DEV(x) (sdmmc_get_dev(x))
+/** @} */
+
+/**
+ * @brief sdmmc_dev_t forward declaration
+ */
+typedef struct sdmmc_dev sdmmc_dev_t;
+
+/**
+ * @brief Data transfer types
+ */
+typedef enum {
+ SDMMC_MULTIBYTE, /**< Transfer 1 to READ_BL_LEN/WRITE_BL_LEN bytes */
+ SDMMC_BLOCK, /**< Transfer block with size of READ_BL_LEN/WRITE_BL_LEN */
+ SDMMC_STREAM, /**< Transfer until CMD12 is sent (MMC and 1-bit bus only) */
+} sdmmc_xfer_type_t;
+
+/**
+ * @brief Transfer descriptor
+ *
+ * The transfer descriptor hold all information about a transfer such as the
+ * block size and the number of block.
+ */
+typedef struct {
+ sdmmc_xfer_type_t type; /**< Type of the transfer */
+ bool write; /**< Indicate a write transfer */
+ sdmmc_cmd_t cmd_idx; /**< Command index used for the transfer */
+ sdmmc_resp_t resp_type; /**< Type of expected response for the transfer */
+ uint32_t arg; /**< Command argument used for the transfer */
+ uint16_t block_size; /**< Size of a block or number of bytes for Byte transfer */
+ uint16_t block_num; /**< Number of blocks to be transferred, 1 for Byte transfer */
+} sdmmc_xfer_desc_t;
+
+/**
+ * @brief Auto Command features supported by the SDIO/SD/MMC peripheral
+ *
+ * Most SDIO/SD/MMC peripherals support the Auto-Command feature for
+ * CMD12 and CMD23. The low-level SDIO/SD/MMC peripheral driver defines
+ * in @ref sdmmc_driver_t::init which Auto-Command features are supported
+ * by the peripheral. To be able to specify a combination of these
+ * features, the enumeration values are defined bitwise.
+ */
+typedef enum {
+ SDMMC_AUTO_CMD_NONE = 0x00,
+ SDMMC_AUTO_CMD12 = 0x01,
+ SDMMC_AUTO_CMD23 = 0x02,
+ SDMMC_AUTO_CMD_BOTH = 0x03,
+} sdmmc_auto_cmd_t;
+
+/**
+ * @brief Events generated by SDIO/SD/MMC high level API
+ */
+typedef enum {
+ SDMMC_EVENT_CARD_INSERTED,
+ SDMMC_EVENT_CARD_REMOVED,
+} sdmmc_event_t;
+
+/**
+ * @brief Event callback function type
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] event Event
+ *
+ * @warning The function is called in the ISR context. Do not do anything
+ * comprehensive or time-consuming. Instead, use `thread_flags`,
+ * `event_queue` or `msg` mechanism to inform a thread about the event,
+ * which then handles the event asynchronously in thread context.
+ */
+typedef void (*sdmmc_event_cb_t)(sdmmc_dev_t *dev, sdmmc_event_t event);
+
+/**
+ * @brief Low-level SDIO/SD/MMC peripheral driver
+ *
+ * The low-level SDIO/SD/MMC peripheral driver interface as used by
+ * high-level SDIO/SD/MMC device API functions. It has to be implemented
+ * for a specific CPU.
+ *
+ * @note High-level functions such as @ref sdmmc_read_blocks are provided
+ * by the SDIO/SD/MMC API and should be used instead. Directly calling
+ * these functions is not recommended.
+ */
+typedef struct {
+ /**
+ * @brief Basic initialization of the given SDIO/SD/MMC device
+ *
+ * Low-level SDIO/SD/MMC peripheral driver function for basic initialization
+ * of the peripheral including pin configuration of used pins. It is
+ * called by @ref sdmmc_init during the board initialization to prepare
+ * the SDIO/SD/MMC peripheral for further usage. Specific initialization
+ * parameters required for this initialization are defined in the board's
+ * `periph_conf.h`.
+ *
+ * Errors like configuration parameter problems are not signaled by
+ * return values, but by using the `assert()`.
+ *
+ * @see @ref sdmmc_init
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to initialize
+ */
+ void (*init)(sdmmc_dev_t *dev);
+
+ /**
+ * @brief Send command to SDIO/SD/MMC Card and optionally wait for response
+ *
+ * Low-level SDIO/SD/MMC peripheral driver function to send command
+ * @p cmd_idx with argument @p arg to the SDIO/SD/MMC card. @p resp_type
+ * specifies the type of the response expected.
+ *
+ * @warning This function must not be used for application specific
+ * commands. The high-level SDIO/SD/MMC device API function
+ * @ref sdmmc_send_acmd is used instead.
+ *
+ * To ensure that the @ref sdmmc_send_acmd function is used for
+ * application specific commands, the function must fail if the
+ * command @p cmd_idx is an application specific command (ACMDx).
+ * Use
+ * ```
+ * assert((cmd_idx & SDMMC_ACMD_PREFIX) == 0);
+ * ```
+ * in the implementation of this function for that purpose.
+ *
+ * The response has to be stored word-wise in host byte order in the buffer
+ * provided by @p resp as follows:
+ *
+ * - @p resp_type = SDMMC_NO_R (No Response): @p resp can be NULL
+ * - @p resp_tpye = SDMMC_R2 (Long Response):
+ * @p resp = { R[127:96], R[95:64], R[63:32], R[31:0] }
+ * - @p resp_type = anything else (Short Response): @p resp = { R[39:8] }
+ *
+ * The buffer provided by @p resp can be NULL if the response is not
+ * needed. However, the low-level SDIO/SD/MMC peripheral driver must
+ * receive the expected response, but does not store it in @p resp
+ * in that case.
+ *
+ * @note R3 does not use CRC7 in CRC field but a fixed value of `0b111111`.
+ * The driver must check the CRC field for this response.
+ *
+ * @see @ref sdmmc_send_cmd and sdmmc_send_acmd
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] cmd_idx Command index
+ * @param[in] arg Command argument
+ * @param[in] resp_type Type of response expected
+ * @param[out] resp Buffer of 32-bit words to store the response if
+ * needed, otherwise NULL
+ *
+ * @retval 0 on success
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support the command or is in wrong state
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EBADMSG on CRC7 error in response
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+ int (*send_cmd)(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp);
+
+ /**
+ * @brief Card Initialization and Identification
+ *
+ * @warning Usually the standard procedure implemented by the high-level
+ * SDIO/SD/MMC device API function @ref sdmmc_card_init is used
+ * for card initialization and identification (recommended).
+ * The low-level SDIO/SD/MMC peripheral driver should implement
+ * its own @ref sdmmc_driver_t::card_init function only in very
+ * special cases, e.g. when special hardware handling is required.
+ * Therefore, this driver function should be set to NULL, that
+ * is the card initialization and identification function is
+ * not implemented by low-level SDIO/SD/MMC peripheral driver
+ *
+ * @see @ref sdmmc_card_init
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ *
+ * @retval 0 on success
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card is not supported or can't operate under supplied voltage
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on card initialization and identification timeout
+ * @retval -EBADMSG on CRC7 error
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+ int (*card_init)(sdmmc_dev_t *dev);
+
+ /**
+ * @brief Set data bus width
+ *
+ * Set the data bus width used by the SDIO/SD/MMC peripheral. The
+ * function is called by the high-level SDIO/SD/MMC device function
+ * @ref sdmmc_card_init at the end of the card initialization and
+ * identification procedure to change the data bus width used by the
+ * SDIO/SD/MMC peripheral. The data bus width of the card has been
+ * already changed at that time by sending the ACMD6 (`SET_BUS_WIDTH`)
+ * command.
+ *
+ * Supported data bus width depend on
+ *
+ * - the card type,
+ * - the mode in which the card operates and
+ * - the SDIO/SD/MMC peripheral.
+ *
+ * In identification mode, always 1-bit data bus width is used. When
+ * switching from the identification mode to the data transfer mode,
+ * the data bus width is changed. In data transfer mode,
+ * the data bus width depends on the type of the used card:
+ *
+ * - MMCs V3.x support 4-bit data bus width
+ * - MMCs V4.x support 8-bit data bus width
+ * - SD Memory Cards support 4-bit data bus width
+ * - SDIO Cards support 4-bit data bus width
+ * - Embedded SDIO devices support 8-bit data bus width
+ *
+ * The data bus width @p width is the minimum of the data bus width
+ * supported by the identified card and the data bus width
+ * @ref sdmmc_dev_t::bus_width configured for the SDIO/SD/MMC
+ * peripheral. The low-level SDIO/SD/MMC peripheral driver is
+ * responsible for correctly setting @ref sdmmc_dev_t::bus_width in the
+ * @ref sdmmc_driver_t::init function.
+ *
+ * @note The @ref set_bus_width function must not change
+ * @ref sdmmc_dev_t::bus_width because it is the configured data
+ * bus width which may be larger than the supported data bus width
+ * of the identified card.
+ *
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] width Data bus width to be set
+ *
+ * @retval 0 on success
+ * @retval -ENOTSUP if SDIO/SD/MMC peripheral does not support the width
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+ int (*set_bus_width)(sdmmc_dev_t *dev, sdmmc_bus_width_t width);
+
+ /**
+ * @brief Set SD CLK signal rate
+ *
+ * Set the SD CLK rate used by the SDIO/SD/MMC peripheral. The clock
+ * frequency in identification mode f_OD is fixed and is 400 kHz. The
+ * actual clock frequency in data transfer mode f_PP depends on the
+ * SDIO/SD/MMC device and the card used.
+ *
+ * The function is called by the high-level SDIO/SD/MMC device API function
+ * @ref sdmmc_card_init at the end of the card identification procedure
+ * with @p rate set to any of the clock rates defined in
+ * @ref sdmmc_clock_rate_t. The low-level SDIO/SD/MMC peripheral
+ * is responsible to set the clock rate to the highest value it supports
+ * for the identified card type.
+ *
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] rate Clock rate to be set
+ *
+ * @retval 0 on success
+ * @retval -ENOTSUP if SDIO/SD/MMC peripheral does not support the clock rate
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+ int (*set_clock_rate)(sdmmc_dev_t *dev, sdmmc_clock_rate_t rate);
+
+#if !IS_USED(MODULE_PERIPH_SDMMC_AUTO_CLK) || DOXYGEN
+ /**
+ * @brief Enable or disable the SD CLK signal
+ *
+ * The function is used by the SDIO/SD/MMC device API functions to enable
+ * or disable the SD CLK signal if the SDIO/SD/MMC peripheral driver does
+ * not support the Auto CLK feature (periph_sdmmc_auto_clk). It can be
+ * left NULL if the SDIO/SD/MMC peripheral driver does not support
+ * enabling or disabling the SD CLK signal.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] enable enable SD CLK signal on true or disable the
+ * SD CLK signal on false
+ *
+ * @retval 0 on success
+ * @retval -EIO if the SD CLK signal could not be enabled or disabled
+ */
+ int (*enable_clock)(sdmmc_dev_t *dev, bool enable);
+#endif
+
+ /**
+ * @brief Prepare a data transfer
+ *
+ * Low-level SDIO/SD/MMC peripheral driver function to prepare a data
+ * transfer (read or write) in the SDIO/SD/MMC peripheral.
+ *
+ * It is called by the high-level SDIO/SD/MMC device API function
+ * @ref sdmmc_xfer before it sends a block-oriented, stream or
+ * byte/multi-byte command to the card to start the transfer from or
+ * to the card.
+ *
+ * A typical activity of this function is the configuration of
+ * the DMA transfer.
+ *
+ * @see @ref sdmmc_xfer
+ * @pre @p dev and @p xfer must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] xfer Transfer descriptor of type @ref sdmmc_xfer_desc_t
+ *
+ * @retval 0 on success
+ * @retval -EINVAL on invalid transfer parameters
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+ int (*xfer_prepare)(sdmmc_dev_t *dev, sdmmc_xfer_desc_t *xfer);
+
+ /**
+ * @brief Execute the data transfer
+ *
+ * Low-level SDIO/SD/MMC peripheral driver function to transfer the data
+ * (read or write) by the SDIO/SD/MMC peripheral.
+ *
+ * It is called by the high-level SDIO/SD/MMC device API function
+ * @ref sdmmc_xfer after it sent the block-oriented, stream or
+ * byte/multi-byte command to start the transfer from or to the card.
+ *
+ * The function returns the number of transferred blocks in @p done.
+ * Most SDIO/SD/MMC peripherals use a block or byte counter when
+ * transferring data, which can be used for this purpose.
+ *
+ * @note If the SDIO/SD/MMC peripheral does not allow to determine
+ * the number of transferred blocks, the function must return `0`
+ * in @p done in case of an error or @ref sdmmc_xfer_desc_t::block_num
+ * on success.
+ *
+ * @see @ref sdmmc_xfer
+ * @pre @p dev and @p xfer must not be `NULL`. \n
+ * @p data_rd must not be `NULL` for read transfers and \n
+ * @p data_wr must not be `NULL` for write transfers.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] xfer Transfer descriptor of type @ref sdmmc_xfer_desc_t
+ * @param[in] data_wr Buffer with data to write in write transfers, NULL otherwise
+ * @param[out] data_rd Buffer for data to read in read transfers, NULL otherwise
+ * @param[out] done Number of blocks transferred, can be `NULL`
+ *
+ * @retval 0 on success
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support a used command or is in wrong state
+ * @retval -EBUSY if card is busy
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EINVAL on invalid transfer parameters
+ * @retval -EBADMSG on CRC7 error in data
+ * @retval -ENOMEM on RX FIFO overflow or TX FIFO underrun error
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+ int (*xfer_execute)(sdmmc_dev_t *dev, sdmmc_xfer_desc_t *xfer,
+ const void *data_wr, void *data_rd,
+ uint16_t *done);
+
+ /**
+ * @brief Finish the data transfer
+ *
+ * Low-level SDIO/SD/MMC peripheral driver function to terminate a data
+ * transfer (read or write) in the SDIO/SD/MMC peripheral.
+ *
+ * It is called by the high-level SDIO/SD/MMC device API function
+ * @ref sdmmc_xfer after the data transfer has been executed and the
+ * stop command (CMD12) has been sent if necessary.
+ *
+ * @see @ref sdmmc_xfer
+ * @pre @p dev and @p xfer must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] xfer Transfer descriptor of type @ref sdmmc_xfer_desc_t
+ *
+ * @retval 0 on success
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+ int (*xfer_finish)(sdmmc_dev_t *dev, sdmmc_xfer_desc_t *xfer);
+
+} sdmmc_driver_t;
+
+/**
+ * @brief SDIO/SD/MMC device descriptor
+ *
+ * The device descriptor holds all required information about the SDIO/SD/MMC
+ * device and the card that is used by this decive.
+ *
+ * @note Most of the information is determined or collected during the card
+ * initialization and identification procedure by the @ref sdmmc_card_init
+ * function. However, some information must be determined and collected
+ * by the low-level SDIO/SD/MMC peripheral driver. These are
+ * - @ref sdmmc_dev_t::driver
+ * - @ref sdmmc_dev_t::present
+ * - @ref sdmmc_dev_t::status
+ * - @ref sdmmc_dev_t::bus_width
+ * - @ref sdmmc_dev_t::spi_mode
+ */
+typedef struct sdmmc_dev {
+ /**
+ * Low-level SDIO/SD/MMC peripheral driver. It has to be set by the
+ * low-level SDIO/SD/MMC driver during the initialization.
+ */
+ const sdmmc_driver_t *driver;
+
+ /**
+ * The application can register an event callback function of type
+ * @ref sdmmc_event_cb_t which is called when one of the defined
+ * events (@ref sdmmc_event_t) is generated by the SDIO/SD/MMC driver.
+ *
+ * @warning The function is called in the ISR context. Do not do anything
+ * comprehensive or time-consuming. Instead, use `thread_flags`,
+ * `event_queue` or `msg` mechanism to inform a thread about
+ * the event, which then handles the event asynchronously in
+ * thread context.
+ */
+ sdmmc_event_cb_t event_cb;
+
+ /**
+ * CID register of the SD/MMC Card, read during the initialization and
+ * identification procedure in the @ref sdmmc_card_init function. It is not
+ * supported by SDIO cards and reflects the CID of the memory portion in
+ * case of Combo cards.
+ */
+ sdmmc_cid_t cid;
+
+ /**
+ * SCR register of the SD Card, read during the initialization and
+ * identification procedure in the @ref sdmmc_card_init function. It is not
+ * supported by SDIO and MMC cards. It reflects the SCR of the memory
+ * portion in case of Combo cards.
+ */
+ sdmmc_scr_t scr;
+
+ /**
+ * CSD register of the SD/MMC Card, read during the initialization and
+ * identification procedure in the @ref sdmmc_card_init function. It is not
+ * supported by SDIO cards. It reflects the CSD of the memory portion in
+ * case of Combo cards.
+ */
+ sdmmc_csd_t csd;
+
+#if IS_USED(MODULE_SDMMC_MMC)
+ /**
+ * EXT_CSD register of the MMC Card, read during the initialization and
+ * identification procedure int the @ref sdmmc_card_init function.
+ */
+ sdmmc_ext_csd_t ext_csd;
+#endif
+
+ /**
+ * Last SDIO/SD/MMC Card status reported in R1 response. It is
+ * set by the low-level SDIO/SD/MMC peripheral driver function
+ * @ref sdmmc_driver_t::send_cmd.
+ */
+ uint32_t status;
+
+ /**
+ * Relative Card Address (RCA) of the SDIO/SD/MMC Card as determined
+ * during the initialization and identification procedure in the
+ * @ref sdmmc_card_init function.
+ */
+ uint16_t rca;
+
+ /**
+ * Type of the SDIO/SD/MMC Card as identified during the initialization and
+ * identification procedure in the @ref sdmmc_card_init function
+ * (default @ref SDMMC_CARD_TYPE_UNKNOWN).
+ */
+ sdmmc_card_type_t type;
+
+ /**
+ * Data bus width supported by the SDIO/SD/MMC device
+ * (default @ref SDMMC_BUS_WIDTH_1BIT). It has to be set by the low-level
+ * SDIO/SD/MMC peripheral driver function @ref sdmmc_driver_t::init.
+ * It is either hard-configured for the SDIO/SD/MMC device or detected
+ * during its initialization.
+ */
+ sdmmc_bus_width_t bus_width;
+
+ /**
+ * Indicates whether a card is present. It has to be set by the low-level
+ * SDIO/SD/MMC peripheral driver in function @ref sdmmc_driver_t::init and
+ * if the card is removed or inserted.
+ * Either the CD signal is used if available, or it must be set to
+ * `true` by default.
+ */
+ bool present;
+
+ /**
+ * Indicates whether the card is initialized (default `false`).
+ * It is set by the initialization and identification procedure in
+ * function @ref sdmmc_card_init.
+ */
+ bool init_done;
+
+ /**
+ * Indicates whether the SDIO/SD/MMC peripheral supports the switching
+ * to 1.8V (default `false`). It has to be set by the low-level SDIO/SD/MMC
+ * peripheral driver function @ref sdmmc_driver_t::init if supported.
+ */
+ bool s18v_support;
+
+ /**
+ * Indicates whether the card supports the switching to 1.8V (default
+ * `false`). It is set during the initialization and identification
+ * procedure in function @ref sdmmc_card_init.
+ */
+ bool s18v_allowed;
+
+ /**
+ * Indicates whether SPI mode is used by the SDIO/SD/MMC device (default
+ * `false`). It has to be set by the low-level SDIO/SD/MMC peripheral driver
+ * function @ref sdmmc_driver_t::init.
+ */
+ bool spi_mode;
+
+} sdmmc_dev_t;
+
+/**
+ * @brief Retrieve SDIO/SD/MMC device descriptor from the peripheral index
+ *
+ * The function converts the peripheral index to the corresponding SDIO/SD/MMC
+ * device descriptor.
+ *
+ * @note This function has to be implemented by the low-level SDIO/SD/MMC
+ * peripheral driver to allow to convert a SDIO/SD/MMC peripheral index
+ * to the corresponding SDIO/SD/MMC device descriptor.
+ *
+ * @param[in] num SDIO/SD/MMC peripheral index
+ *
+ * @retval pointer to the SDIO/SD/MMC device descriptor at index @p num on success
+ * @retval NULL if @p num is greater than the number of SDIO/SD/MMC device
+ * descriptors
+ */
+sdmmc_dev_t *sdmmc_get_dev(unsigned num);
+
+/**
+ * @brief Basic initialization of the given SDIO/SD/MMC device
+ *
+ * The function calls the low-level SDIO/SD/MMC peripheral driver function
+ * @ref sdmmc_driver_t::init for the basic initialization of the SDIO/SD/MMC
+ * peripheral including the configuration of used pins.
+ *
+ * If the `auto_init` module is enabled, it is called automatically during the
+ * startup. Otherwise, it has to be called before the SDIO/SD/MMC device is
+ * used for the first time.
+ *
+ * Errors like configuration parameter problems are not signaled by
+ * return values, but by using the `assert()`.
+ *
+ * @see @ref sdmmc_driver_t::init
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to initialize
+ */
+static inline void sdmmc_init(sdmmc_dev_t *dev)
+{
+ assert(dev);
+ assert(dev->driver);
+
+ dev->driver->init(dev);
+}
+
+/**
+ * @brief Send command to SDIO/SD/MMC Card and optionally wait for response
+ *
+ * Send the command @p cmd_idx with argument @p arg to the SDIO/SD/MMC
+ * card. @p resp_type specifies the type of the response expected. Addressed
+ * (point-to-point) commands are sent to the card that is currently selected.
+ *
+ * This function just calls the low-level SDIO/SD/MMC peripheral driver function
+ * @ref sdmmc_driver_t::send_cmd.
+ *
+ * @note @ref sdmmc_card_init is called implicitly if necessary.
+ *
+ * @warning For application specific commands, the @ref sdmmc_send_acmd function
+ * MUST to be used.
+ *
+ * The response is stored word-wise in host byte order in the buffer
+ * provided by @p resp as follows:
+ *
+ * - @p resp_type = SDMMC_NO_R (No Response): @p resp can be NULL
+ * - @p resp_tpye = SDMMC_R2 (Long Response):
+ * @p resp = { R[127:96], R[95:64], R[63:32], R[31:0] }
+ * - @p resp_type = anything else (Short Response): @p resp = { R[39:8] }
+ *
+ * The buffer provided by @p resp can be NULL if the response is not
+ * needed.
+ *
+ * @note R3 does not use CRC7 in CRC field but a fixed value of `0b111111`.
+ * The low-level SDIO/SD/MMC peripheral driver must not check the CRC
+ * field for this response.
+ *
+ * @see @ref sdmmc_driver_t::send_cmd
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] cmd_idx Command index
+ * @param[in] arg Command argument
+ * @param[in] resp_type Type of response expected
+ * @param[out] resp Buffer of 32-bit words to store the response if
+ * needed, otherwise NULL
+ *
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support the command or is in wrong state
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EBADMSG on CRC7 error in response
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+int sdmmc_send_cmd(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp);
+
+/**
+ * @brief Send application specific command optionally wait for response
+ *
+ * Send an application specific command @p cmd_idx with argument @p arg
+ * to the SDIO/SD/MMC Card. This function is a convenience function that
+ * uses the low-level SDIO/SD/MMC peripheral driver function
+ * @ref sdmmc_driver_t::send_cmd. It just sends CMD55 before sending the
+ * command with index @p cmd_idx to the card. Addressed (point-to-point)
+ * commands are sent to the card that is currently selected.
+ *
+ * Instead of using this function, the following could also be used:
+ * ```c
+ * sdmmc_send_cmd(dev, SDMMC_CMD55, RCA, SDMMC_R1, NULL);
+ * sdmmc_send_cmd(dev, cmd_idx & ~SDMMC_ACMD_PREFIX, arg, resp_type, resp);
+ * ```
+ *
+ * @note @ref sdmmc_card_init is called implicitly if necessary.
+ *
+ * @warning The command index must be an ACMD command index, i.e. the command
+ * index ORed with @ref SDMMC_ACMD_PREFIX. Otherwise the function
+ * fails because of an assertion.
+ *
+ * @p resp_type specifies the response expected.
+ *
+ * The response is stored word-wise in host byte order in the buffer
+ * provided by @p resp as follows:
+ *
+ * - @p resp_type = SDMMC_NO_R (No Response): @p resp can be NULL
+ * - @p resp_tpye = SDMMC_R2 (Long Response):
+ * @p resp = { R[127:96], R[95:64], R[63:32], R[31:0] }
+ * - @p resp_type = anything else (Short Response): @p resp = { R[39:8] }
+ *
+ * The buffer provided by @p resp can be NULL if the response is not
+ * needed. However, the low-level SDIO/SD/MMC peripheral driver must
+ * receive the expected response, but does not store it in @p resp
+ * in that case.
+ *
+ * @note R3 does not use CRC7 in CRC field but a fixed value of `0b111111`.
+ * The low-level SDIO/SD/MMC peripheral driver must not check the CRC
+ * field for this response.
+ *
+ * @pre @p dev must not be `NULL`. @p cmd_idx must be in range 0 to 63.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] cmd_idx Command index of an application specific command
+ * @param[in] arg Command argument
+ * @param[in] resp_type Type of response expected
+ * @param[out] resp Buffer of 32-bit words to store the response
+ *
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support the command or is in wrong state
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EBADMSG on CRC7 error in response
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+int sdmmc_send_acmd(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp);
+
+/**
+ * @brief Card Initialization and Identification
+ *
+ * This function identifies the type of used SDIO/SD/MMC Cards and
+ * initializes them according to the standardized procedure specified in:
+ *
+ * - Physical Layer Simplified Specification Version 9.00,
+ * Section 4.2.3 Card Initialization and Identification Process, pp. 43
+ * [[sdcard.org](https://www.sdcard.org)]
+ * - SDIO Simplified Specification Version 3.00,
+ * Section 3.1.2 Initialization by I/O Aware Host, pp. 17
+ * [[sdcard.org](https://www.sdcard.org)]
+ * - SD Host Controller Simplified Specification Version 4.20,
+ * Section 3.6 Card Initialization and Identification (for SD I/F), pp. 160
+ * [[sdcard.org](https://www.sdcard.org)]
+ * - JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2),
+ * Section 7.2 Card Identification Mode, p. 24 and Figure 16 on p. 16
+ * [[jedec.org](https://www.jedec.org)]
+ *
+ * Identified card types are:
+ * - SD Memory Cards with Standard Capacity (SDSC) Version 1.x, Version 2.x+
+ * - SD Memory Cards with High or Extended Capacity (SDHC/SDXC)
+ * - SDIO Cards, not supported yet
+ * - Combined SDIO/SD Memory Cards (Combo Cards), SDIO part not supported yet
+ * - MultiMedia Cards (MMC) and Embedded Multimedia Cards (eMMC)
+ *
+ * @warning If the low-level SDIO/SD/MMC peripheral driver defines its own
+ * @ref sdmmc_driver_t::card_init function, this function is used
+ * instead. \n
+ * However, the low-level SDIO/SD/MMC peripheral driver should
+ * define its own @ref sdmmc_driver_t::card_init function only in
+ * very special cases, e.g. when special hardware handling is
+ * required. Otherwise it is strongly recommended to set
+ * sdmmc_driver_t::card_init to `NULL` and to use the default
+ * procedure implemented by @ref sdmmc_send_acmd.
+ *
+ * @see @ref sdmmc_driver_t::card_init
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ *
+ * @retval 0 on success
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card is not supported or can't operate under supplied voltage
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on card initialization and identification timeout
+ * @retval -EBADMSG on CRC7 error
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+int sdmmc_card_init(sdmmc_dev_t *dev);
+
+/**
+ * @brief Perform a data transfer with the selected card or embedded device
+ *
+ * This high-level SDIO/SD/MMC device API function performs a read or write data
+ * transfer either
+ *
+ * - block-oriented with a number of blocks of the same size (SD and MMC)
+ * - as stream until the stop command (CMD12) is sent by the host (MMC only)
+ * - in byte or multi-byte mode (SDIO only)
+ *
+ * The possible block sizes @p block_size depend on the card and mode
+ * used. For block-oriented data transfers, the block size is usually
+ * 512 bytes, but may differ for MMCs and SD Cards with Standard Capacity
+ * Version 1.x (SDSC):
+ *
+ * - MMC: block size can be 1, 2, 4, ..., 4096 bytes (as power of 2)
+ * - SDSC V1.x: block size can be 512, 1024 or 2048 bytes
+ * - SDSC V2.x and later, SDSC/SDHC: block size is 512 bytes
+ *
+ * The block size for SDIO transfers in byte or multi-byte mode, if
+ * supported, can be in the range of 1 to 512 bytes. The number of
+ * blocks @p block_num MUST be 1 in this case.
+ *
+ * For a data transfer as a stream, the block size @p block_size and the
+ * number of blocks @p block_num are 0.
+ *
+ * @note Some block-oriented data transfers such as reading the SD Card
+ * Configuration register (SCR) with ACMD51 may use the
+ * @ref sdmmc_xfer function with smaller block size, for example
+ * only 8 bytes. The low-level SDIO/SD/MMC peripheral driver MUST
+ * support block sizes smaller than the usual block size used
+ * for block-oriented operations.
+ *
+ * The @ref sdmmc_xfer function uses the low-level SDIO/SD/MMC peripheral
+ * driver functions @ref sdmmc_driver_t::xfer_prepare to prepare the data
+ * transfer, @ref sdmmc_driver_t::xfer_execute to perform the data transfer,
+ * and @ref sdmmc_driver_t::xfer_finish to complete the data transfer.
+ * In detail:
+ * ```c
+ * _wait_for_ready(dev);
+ * if (cmd_idx & SDMMC_ACMD_PREFIX) {
+ * sdmmc_send_cmd(dev, SDMMC_CMD55, ...);
+ * }
+ * dev->driver->xfer_prepare(dev, xfer);
+ * sdmmc_send_cmd(dev, cmd_idx, ...);
+ * dev->driver->xfer_execute(dev, xfer, ...);
+ * if (block_num > 1) {
+ * sdmmc_send_cmd(dev, SDMMC_CMD12, ...);
+ * }
+ * dev->driver->xfer_finish(dev, xfer);
+ * ```
+ * @p xfer is the transfer descriptor of type @ref sdmmc_xfer_desc_t that
+ * contains all transfer parameters.
+ *
+ * If the parameter @p done is not `NULL`, the function returns the number of
+ * transferred blocks.
+ *
+ * @warning
+ * The buffers specified by @p data_wr and @p data_rd may need to be
+ * word-aligned depending on CPU-specific requirements. @ref sdmmc_buf_t
+ * or @ref SDMMC_CPU_DMA_REQUIREMENTS have to be used to define such buffers:
+ * ```c
+ * sdmmc_buf_t buffer[SDMMC_SDHC_BLOCK_SIZE];
+ * ...
+ * SDMMC_CPU_DMA_REQUIREMENTS uint8_t buffer[SDMMC_SDHC_BLOCK_SIZE];
+ * ```
+ *
+ * @note @ref sdmmc_card_init is called implicitly if necessary.
+ *
+ * @see @ref sdmmc_driver_t::xfer_prepare, @ref sdmmc_driver_t::xfer_execute
+ * and @ref sdmmc_driver_t::xfer_finish
+ * @pre @p dev must not be `NULL`. \n
+ * @p data_rd must not be `NULL` for read transfers and
+ * @p data_wr must not be `NULL` for write transfers.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] cmd_idx Command index or application specific command index
+ * @param[in] arg Command argument
+ * @param[in] block_size Block size dependent on card and mode used
+ * - 512 bytes for block-oriented transfers (SDSC V2.x/SDHC/SDXC)
+ * - 1, 2, 4, ... 4096 for block-oriented transfers (MMC)
+ * - 512, 1024, 2048 for block-oriented transfers (SDSC V1.x)
+ * - 1...512 bytes in byte/multibyte mode (SDIO)
+ * - 0 in stream mode (SDIO)
+ * @param[in] block_num Number of blocks:
+ * - 1, ... for block-oriented transfers (SD Card/MMC)
+ * - 1 in byte/multibyte mode (SDIO)
+ * - 0 in stream mode (MMC)
+ * @param[in] data_wr Buffer with data to write in write transfers, NULL otherwise
+ * @param[out] data_rd Buffer for data to read in read transfers, NULL otherwise
+ * @param[out] done Number of transferred blocks, can be `NULL`
+ *
+ * @retval 0 on success
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support a used command or is in wrong state
+ * @retval -EBUSY if card is busy
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EINVAL on invalid transfer parameters
+ * @retval -EBADMSG on CRC7 error
+ * @retval -ENOMEM on RX FIFO overflow or TX FIFO underrun error
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+int sdmmc_xfer(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ uint16_t block_size, uint16_t block_num,
+ const void *data_wr, void *data_rd, uint16_t *done);
+
+/**
+ * @brief Read a number of blocks
+ *
+ * Read @p block_num blocks with size @p block_size from @p dev starting at
+ * block address @p block_addr to buffer @p data.
+ *
+ * The starting block address is always specified as block address independent
+ * on the actual addressing scheme of used card. The driver takes care of
+ * mapping to byte addressing if needed.
+ *
+ * If the parameter @p done is not `NULL`, the function returns the number of
+ * read blocks.
+ *
+ * @warning
+ * The buffer @p data may need to be word-aligned depending on CPU-specific
+ * requirements. @ref sdmmc_buf_t or @ref SDMMC_CPU_DMA_REQUIREMENTS have to
+ * be used to define the buffer:
+ * ```c
+ * sdmmc_buf_t buffer[SDMMC_SDHC_BLOCK_SIZE];
+ * ...
+ * SDMMC_CPU_DMA_REQUIREMENTS uint8_t buffer[SDMMC_SDHC_BLOCK_SIZE];
+ * ```
+ *
+ * @note @ref sdmmc_card_init is called implicitly if necessary.
+ *
+ * @pre @p dev and @p data must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] block_addr Start address to read from given as block address
+ * @param[in] block_size Block size dependent on card and mode used
+ * - 512 bytes for block-oriented transfers (SDSC V2.x/SDHC/SDXC)
+ * - 1, 2, 4, ... 4096 for block-oriented transfers (MMC)
+ * - 512, 1024, 2048 for block-oriented transfers (SDSC V1.x)
+ * - 1...512 bytes in byte/multibyte mode (SDIO only)
+ * - 0 in stream mode (MMC only)
+ * @param[in] block_num Number of blocks:
+ * - 1, ... for block-oriented transfers (SD Card/MMC)
+ * - 1 in byte/multibyte mode (SDIO only)
+ * - 0 in stream mode (MMC only)
+ * @param[out] data Buffer for read data
+ * @param[out] done Number of read blocks, can be `NULL`
+ *
+ * @retval 0 on success, @p block_num blocks were read successfully
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support a used command or is in wrong state
+ * @retval -EBUSY if card is busy
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EINVAL on invalid transfer parameters
+ * @retval -EBADMSG on CRC7 error
+ * @retval -ENOMEM on RX FIFO overflow error
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+int sdmmc_read_blocks(sdmmc_dev_t *dev,
+ uint32_t block_addr, uint16_t block_size,
+ uint16_t block_num, void *data, uint16_t *done);
+
+/**
+ * @brief Write a number of blocks
+ *
+ * Write @p block_num blocks with size @p block_size to @p dev starting at
+ * block address @p block_addr from buffer @p data.
+ *
+ * The starting block address is always specified as block address independent
+ * on the actual addressing scheme of used card. The driver takes care of
+ * mapping to byte addressing if needed.
+ *
+ * If the parameter @p done is not `NULL`, the function returns the number of
+ * written blocks.
+ *
+ * @warning
+ * The buffer @p data may need to be word-aligned depending on CPU-specific
+ * requirements. @ref sdmmc_buf_t or @ref SDMMC_CPU_DMA_REQUIREMENTS have to
+ * be used to define the buffer:
+ * ```c
+ * sdmmc_buf_t buffer[SDMMC_SDHC_BLOCK_SIZE];
+ * ...
+ * SDMMC_CPU_DMA_REQUIREMENTS uint8_t buffer[SDMMC_SDHC_BLOCK_SIZE];
+ * ```
+ *
+ * @note @ref sdmmc_card_init is called implicitly if necessary.
+ *
+ * @pre @p dev and @p data must not be `NULL`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] block_addr Start address to write to given as block address
+ * @param[in] block_size Block size dependent on card and mode used
+ * - 512 bytes for block-oriented transfers (SDSC V2.x/SDHC/SDXC)
+ * - 1, 2, 4, ... 4096 for block-oriented transfers (MMC)
+ * - 512, 1024, 2048 for block-oriented transfers (SDSC V1.x)
+ * - 1...512 bytes in byte/multibyte mode (SDIO only)
+ * - 0 in stream mode (MMC only)
+ * @param[in] block_num Number of blocks:
+ * - 1, ... for block-oriented transfers (SD and MMC)
+ * - 1 in byte/multibyte mode (SDIO only)
+ * - 0 in stream mode (MMC only)
+ * @param[in] data Buffer with data to write
+ * @param[out] done Number of blocks written, can be `NULL`
+ *
+ * @retval 0 on success, @p block_num blocks were read successfully
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support a used command or is in wrong state
+ * @retval -EBUSY if card is busy
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EINVAL on invalid transfer parameters
+ * @retval -EBADMSG on CRC7 error
+ * @retval -ENOMEM on TX FIFO underrun error
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+int sdmmc_write_blocks(sdmmc_dev_t *dev, uint32_t block_addr,
+ uint16_t block_size, uint16_t block_num,
+ const void *data, uint16_t *done);
+
+/**
+ * @brief Erase a number of blocks
+ *
+ * Erase @p block_num blocks starting at block address @p block_addr on
+ * SD/MMC Card device.
+ *
+ * The starting block address is always specified as block address independent
+ * on the actual addressing scheme of used card. The driver takes care of
+ * mapping to byte addressing if needed.
+ *
+ * @note This function is only available for SD Memory Cards, MMC Cards
+ * or the SD Memory Card portion of a combined SDIO/SD Memory Card
+ * (Combo Card). Calling this function for a SDIO only card returns
+ * the `-ENOTSUP` error.
+ *
+ * @note @ref sdmmc_card_init is called implicitly if necessary.
+ *
+ * @pre @p dev must not be `NULL`. @p block_num has to be greater than 0.
+ *
+ * @param[in] dev SD/MMC device to be used
+ * @param[in] block_addr Start address for erase given as block address
+ * @param[in] block_num Number of blocks to be erased
+ *
+ * @retval 0 on success, @p block_num blocks were erased successfully
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support erase operation or a command used
+ * @retval -EBUSY if card is busy
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EBADMSG on CRC7 error in response
+ * @retval -EINVAL on invalid erase parameters
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+int sdmmc_erase_blocks(sdmmc_dev_t *dev,
+ uint32_t block_addr, uint16_t block_num);
+
+/**
+ * @brief Read SD Status Register
+ *
+ * Read the SD Status register of a SD Memory Card using ACMD13 and stores
+ * the results in the @p sds of type @ref sdmmc_sd_status_t.
+ *
+ * @note This function is only available for SD Memory Cards
+ * or the SD Memory Card portion of a combined SDIO/SD Memory Card
+ * (Combo Card). Calling this function for a SDIO only card returns
+ * the `-ENOTSUP` error.
+ *
+ * @note @ref sdmmc_card_init is called implicitly if necessary.
+ *
+ * @pre @p dev and @p sds must not be `NULL`.
+ *
+ * @param[in] dev SD device to be used
+ * @param[out] sds SD Status register content
+ *
+ * @retval 0 on success
+ * @retval -ENODEV if card is not present or not usable
+ * @retval -ENOTSUP if card does not support the operation
+ * @retval -EBUSY if card is busy
+ * @retval -EFAULT on card status error
+ * @retval -ETIMEDOUT on timeout condition
+ * @retval -EINVAL on invalid transfer parameters
+ * @retval -EBADMSG on CRC7 error
+ * @retval -ENOMEM on RX FIFO overflow error
+ * @retval -EIO on not further specified error incl. hardware errors
+ */
+int sdmmc_read_sds(sdmmc_dev_t *dev, sdmmc_sd_status_t *sds);
+
+/**
+ * @brief Get Capacity of SD/MMC Card
+ *
+ * Get the capacity of a SD/MMC Card device.
+ *
+ * @note This function is only available for SD Memory Cards, MMC Cards
+ * or the SD Memory Card portion of a combined SDIO/SD Memory Card
+ * (Combo Card). Calling this function for a SDIO only card returns
+ * the `-ENOTSUP` error.
+ *
+ * @note @ref sdmmc_card_init is called implicitly if necessary.
+ *
+ * @pre @p dev must not be `NULL`.
+ *
+ * @param[in] dev SD/MMC device to be used
+ *
+ * @return the capacity in in byte or 0 on error
+ */
+uint64_t sdmmc_get_capacity(sdmmc_dev_t *dev);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SDMMC_SDMMC_H */
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 23aa76002a..a7b3d1fafd 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -38,6 +38,21 @@ config HAVE_MTD_SDCARD
help
Indicates that a sdcard MTD is present
+config HAVE_MTD_SDMMC_DEFAULT
+ bool
+ depends on HAS_PERIPH_SDMMC
+ imply MODULE_MTD_SDMMC if MODULE_MTD
+ imply MODULE_MTD_SDMMC_DEFAULT if MODULE_MTD
+ help
+ Indicates that a SD/MMC MTD is present with generic configuration
+
+config HAVE_MTD_SDMMC
+ bool
+ depends on HAS_PERIPH_SDMMC
+ imply MODULE_MTD_SDMMC if MODULE_MTD
+ help
+ Indicates that a SD/MMC MTD is present
+
config HAVE_MTD_SPI_NOR
bool
imply MODULE_MTD_SPI_NOR if MODULE_MTD
@@ -46,13 +61,13 @@ config HAVE_MTD_SPI_NOR
config HAVE_SAM0_SDHC
bool
- imply MODULE_SAM0_SDHC if MODULE_MTD
+ imply MODULE_SAM0_SDHC if MODULE_MTD && !MODULE_MTD_SDMMC
help
Indicates that a SAM0 SD Host Controller MTD is present
config HAVE_MTD_SPI_MCI
bool
- imply MODULE_MTD_MCI if MODULE_MTD
+ imply MODULE_MTD_MCI if MODULE_MTD && !MODULE_MTD_SDMMC
help
Indicates that a Multimedia Card Interface (MCI) MTD is present
@@ -62,7 +77,7 @@ menuconfig MODULE_MTD
if MODULE_MTD
-menu "MTD Interefaces"
+menu "MTD Interfaces"
config MODULE_MTD_SPI_NOR
bool "MTD interface for SPI NOR Flash"
@@ -95,22 +110,33 @@ config MODULE_MTD_MCI
depends on CPU_FAM_LPC23XX
select MODULE_MCI
+config MODULE_MTD_SDCARD
+ bool "MTD interface for SPI SD-Card"
+ depends on MODULE_SDCARD_SPI
+
config MODULE_MTD_SDCARD_DEFAULT
bool "Use Generic SD card configuration"
depends on MODULE_MTD_SDCARD
help
Automatically create a MTD device and mount point for the SD card.
-config MODULE_MTD_SDCARD
- bool "MTD interface for SPI SD-Card"
- depends on MODULE_SDCARD_SPI
+config MODULE_MTD_SDMMC
+ bool "MTD interface for SD/MMC"
+ depends on HAS_PERIPH_SDMMC
+ select MODULE_PERIPH_SDMMC
+
+config MODULE_MTD_SDMMC_DEFAULT
+ bool "Use Generic SD/MMC card configuration"
+ depends on MODULE_MTD_SDMMC
+ help
+ Automatically create a MTD device and mount point for the SD/MMC card.
config MODULE_MTD_EMULATED
bool "MTD interface for MTD emulated in RAM"
config MODULE_SAM0_SDHC
bool "MTD interface for SAM0 SD Host Controller"
- depends on CPU_COMMON_SAM0
+ depends on CPU_COMMON_SAM0 && HAVE_SAM0_SDHC && !MODULE_PERIPH_SDMMC
endmenu # MTD Interfacs
diff --git a/drivers/periph_common/Kconfig b/drivers/periph_common/Kconfig
index 5b5c53d64f..4db0969b8e 100644
--- a/drivers/periph_common/Kconfig
+++ b/drivers/periph_common/Kconfig
@@ -153,6 +153,7 @@ config MODULE_PERIPH_INIT_RTT
default y if MODULE_PERIPH_INIT
depends on MODULE_PERIPH_RTT
+rsource "Kconfig.sdmmc"
rsource "Kconfig.spi"
config MODULE_PERIPH_TEMPERATURE
diff --git a/drivers/periph_common/Kconfig.sdmmc b/drivers/periph_common/Kconfig.sdmmc
new file mode 100644
index 0000000000..a40f65f5fc
--- /dev/null
+++ b/drivers/periph_common/Kconfig.sdmmc
@@ -0,0 +1,62 @@
+# Copyright (c) 2020 HAW Hamburg
+#
+# 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.
+#
+
+menuconfig MODULE_PERIPH_SDMMC
+ bool "SDIO/SD/MMC peripheral driver"
+ depends on HAS_PERIPH_SDMMC
+ select MODULE_PERIPH_COMMON
+
+if MODULE_PERIPH_SDMMC
+
+config MODULE_PERIPH_INIT_SDMMC
+ bool "Auto initialize SDIO/SD/MMC peripheral"
+ default y if MODULE_PERIPH_INIT
+
+config MODULE_PERIPH_SDMMC_8BIT
+ bool "8 Bit data bus support"
+ depends on HAS_PERIPH_SDMMC_8BIT
+ default y
+ help
+ If the SDIO/SD/MMC peripheral supports the 8-bit bus width, it can be
+ used by enabling this option. If the option is disabled, the driver
+ requires less RAM and ROM.
+
+config MODULE_PERIPH_INIT_SDMMC_8BIT
+ bool
+ depends on MODULE_PERIPH_SDMMC_8BIT
+ default y if MODULE_PERIPH_INIT
+
+config MODULE_PERIPH_SDMMC_HS
+ bool "High speed access"
+ depends on HAS_PERIPH_SDMMC_HS
+ default y
+ help
+ If the SDIO/SD/MMC peripheral supports the high speed access, i.e. 50
+ MHz for SD and 52 MHz for MMC, it can be used by enabling this option.
+ If the option is disabled, the driver requires less RAM and ROM.
+
+config MODULE_PERIPH_INIT_SDMMC_HS
+ bool
+ depends on MODULE_PERIPH_SDMMC_HS
+ default y if MODULE_PERIPH_INIT
+
+config MODULE_PERIPH_SDMMC_AUTO_CLK
+ bool
+ depends on HAS_PERIPH_SDMMC_AUTO_CLK
+ default y
+ help
+ If the SDIO/SD/MMC peripheral supports the Auto-CLK feature, i.e.
+ the automatic activation and deactivation of the SD CLK signal,
+ it is enabled automatically by this option. Otherwise, the activation
+ and deactivation is controlled by SDIO/SD/MMC high-level functions.
+
+config MODULE_PERIPH_INIT_SDMMC_AUTO_CLK
+ bool
+ depends on MODULE_PERIPH_SDMMC_AUTO_CLK
+ default y if MODULE_PERIPH_INIT
+
+endif # MODULE_PERIPH_SDMMC
diff --git a/drivers/periph_common/init.c b/drivers/periph_common/init.c
index c8b4ea57b6..ebe09c3a61 100644
--- a/drivers/periph_common/init.c
+++ b/drivers/periph_common/init.c
@@ -57,6 +57,9 @@
#ifdef MODULE_PERIPH_INIT_PIO
#include "periph/pio.h"
#endif
+#ifdef MODULE_PERIPH_INIT_SDMMC
+#include "sdmmc/sdmmc.h"
+#endif
#endif /* MODULE_PERIPH_INIT */
void periph_init(void)
@@ -133,5 +136,11 @@ void periph_init(void)
pio_start_programs();
#endif
+#if defined(MODULE_PERIPH_INIT_SDMMC)
+ for (unsigned i = 0; i < SDMMC_NUMOF; i++) {
+ sdmmc_init(sdmmc_get_dev(i));
+ }
+#endif
+
#endif /* MODULE_PERIPH_INIT */
}
diff --git a/drivers/sdmmc/Kconfig b/drivers/sdmmc/Kconfig
new file mode 100644
index 0000000000..66bf2008b7
--- /dev/null
+++ b/drivers/sdmmc/Kconfig
@@ -0,0 +1,22 @@
+# Copyright (c) 2020 HAW Hamburg
+#
+# 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_SDMMC
+ bool "SDIO/SD/MMC interface"
+ depends on HAS_PERIPH_SDMMC
+ select MODULE_PERIPH_SDMMC
+ select MODULE_ZTIMER_MSEC if !ZTIMER_USEC
+
+if MODULE_SDMMC
+
+config MODULE_SDMMC_MMC
+ bool "MMC support"
+ depends on HAS_PERIPH_SDMMC_MMC
+ help
+ Say y to support MMCs/eMMCs.
+
+endif # MODULE_SDMMC
diff --git a/drivers/sdmmc/Makefile b/drivers/sdmmc/Makefile
new file mode 100644
index 0000000000..1952551650
--- /dev/null
+++ b/drivers/sdmmc/Makefile
@@ -0,0 +1,3 @@
+MODULE = sdmmc
+
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/sdmmc/Makefile.dep b/drivers/sdmmc/Makefile.dep
new file mode 100644
index 0000000000..fdd2a7834c
--- /dev/null
+++ b/drivers/sdmmc/Makefile.dep
@@ -0,0 +1,13 @@
+FEATURES_REQUIRED += periph_sdmmc
+FEATURES_OPTIONAL += periph_sdmmc_auto_clk
+FEATURES_OPTIONAL += periph_sdmmc_auto_cmd12
+FEATURES_OPTIONAL += periph_sdmmc_hs
+
+ifneq (,$(filter sdmmc_mmc,$(USEMODULE)))
+ FEATURES_REQUIRED += periph_sdmmc_mmc
+endif
+
+ifeq (,$(filter ztimer_usec,$(USEMODULE)))
+ # enable ztimer_msec backend if ztimer_usec is not enabled
+ USEMODULE += ztimer_msec
+endif
diff --git a/drivers/sdmmc/Makefile.include b/drivers/sdmmc/Makefile.include
new file mode 100644
index 0000000000..5f19835817
--- /dev/null
+++ b/drivers/sdmmc/Makefile.include
@@ -0,0 +1 @@
+PSEUDOMODULES += sdmmc_mmc
diff --git a/drivers/sdmmc/sdmmc.c b/drivers/sdmmc/sdmmc.c
new file mode 100644
index 0000000000..4e2b4a8dd8
--- /dev/null
+++ b/drivers/sdmmc/sdmmc.c
@@ -0,0 +1,1612 @@
+/*
+ * 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 drivers_sdmmc
+ * @{
+ *
+ * @file
+ * @brief SDIO/SD/MMC device API (SDMMC)
+ *
+ * @author Gunar Schorcht
+ *
+ * @}
+ */
+
+#include
+#include
+#include
+#include
+
+#include "architecture.h"
+#include "assert.h"
+#include "bitarithm.h"
+#include "byteorder.h"
+#include "log.h"
+#include "periph_cpu.h"
+#include "time_units.h"
+#include "ztimer.h"
+
+#include "sdmmc/sdmmc.h"
+
+#define ENABLE_DEBUG 0
+#include "debug.h"
+
+/* millisecond timer definitions dependent on active ztimer backend */
+#if IS_USED(MODULE_ZTIMER_MSEC)
+#define _ZTIMER_ACQUIRE() ztimer_acquire(ZTIMER_MSEC)
+#define _ZTIMER_RELEASE() ztimer_release(ZTIMER_MSEC)
+#define _ZTIMER_NOW() ztimer_now(ZTIMER_MSEC)
+#define _ZTIMER_SLEEP_MS(n) ztimer_sleep(ZTIMER_MSEC, n)
+#elif IS_USED(MODULE_ZTIMER_USEC)
+#define _ZTIMER_ACQUIRE() ztimer_acquire(ZTIMER_USEC)
+#define _ZTIMER_RELEASE() ztimer_release(ZTIMER_USEC)
+#define _ZTIMER_NOW() ztimer_now(ZTIMER_USEC) / US_PER_MS
+#define _ZTIMER_SLEEP_MS(n) ztimer_sleep(ZTIMER_USEC, n * US_PER_MS)
+#else
+#error "Either module ztimer_msec or ztimer_usec is needed"
+#endif
+
+/**
+ * @brief SDIO/SD/MMC Card initialization timeout in msec
+ *
+ * Card initialization shall be completed within 1 second from the first
+ * ACMD41.
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 4.2.3
+ * [[sdcard.org](https://www.sdcard.org)]
+ */
+#define SDMMC_INIT_TIMEOUT_MS (1000)
+
+/**
+ * @brief SD/MMC Timeout Conditions
+ *
+ * @see Physical Layer Simplified Specification Version 9.00,
+ * Section 4.6.2 Read, Write and Erase Timeout Conditions
+ * [[sdcard.org](https://www.sdcard.org)]
+ */
+#define SDMMC_DATA_R_TIMEOUT_MS (100) /**< Read timeout should be 100 ms */
+#define SDMMC_DATA_W_TIMEOUT_MS (500) /**< Write timeout should be 250 ms for,
+ SDSC and 500 ms for SDHC, SDXC */
+#define SDMMC_ERASE_TIMEOUT_MS (250) /**< Erase timeout per block 250 ms */
+
+/**
+ * @brief Time to wait for a SDIO/SD/MMC card to become ready for a data transfer
+ *
+ * When writing the data in a block write transfer, the card uses the
+ * READY_FOR_DATA bit to indicate when it is ready to receive more data.
+ * The host must wait until the card is ready before making the next data
+ * transfer. The maximum wait time is the maximum write time.
+ *
+ * Physical Layer Simplified Specification Version 9.00,
+ * 4.6.2 Read, Write and Erase Timeout Conditions
+ */
+#define SDMMC_WAIT_FOR_CARD_READY_MS 250
+
+/* bus widths as used in ACMD (power of 2) */
+#define SDMMC_ACMD6_BUS_WIDTH_1BIT 0
+#define SDMMC_ACMD6_BUS_WIDTH_4BIT 2
+#define SDMMC_ACMD6_BUS_WIDTH_8BIT 3
+
+#define SDMMC_ACMD41_S18R (SDMMC_OCR_S18A)
+#define SDMMC_ACMD41_HCS (SDMMC_OCR_CCS)
+#define SDMMC_ACMD41_POWER_UP (SDMMC_OCR_POWER_UP)
+#define SDMMC_ACMD41_VOLTAGES (SDMMC_OCR_ALL_VOLTAGES)
+
+#define SDMMC_R3_S18A (SDMMC_OCR_S18A)
+#define SDMMC_R3_CCS (SDMMC_OCR_CCS)
+#define SDMMC_R3_POWER_UP (SDMMC_OCR_POWER_UP)
+
+#define SDMMC_CMD6_ACCESS (24) /* CMD6 arg bit shift for EXT_CSD access mode */
+#define SDMMC_CMD6_INDEX (16) /* CMD6 arg bit shift for EXT_CSD byte index */
+#define SDMMC_CMD6_VALUE (8) /* CMD6 arg bit shift for EXT_CSD value */
+#define SDMMC_CMD6_CMD_SET (0) /* CMD6 arg bit shift for EXT_CSD command set */
+
+/**
+ * @brief Extended CSD Bus Widths (MMC only)
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8.4, Table 55
+ * [[jedec.org](https://www.jedec.org)]
+ */
+enum {
+ SDMMC_EXT_CSD_BUS_WIDTH_1BIT = 0, /**< Bus with 1-bit */
+ SDMMC_EXT_CSD_BUS_WIDTH_4BIT = 1, /**< Bus with 4-bit */
+ SDMMC_EXT_CSD_BUS_WIDTH_8BIT = 2, /**< Bus with 8-bit */
+};
+
+/**
+ * @brief Extended CSD High Speed Interface Timing (MMC only)
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8.4, Table 46
+ * [[jedec.org](https://www.jedec.org)]
+ */
+enum {
+ SDMMC_EXT_CSD_HS_TIMING_BACK = 0, /**< Backward compatible timing */
+ SDMMC_EXT_CSD_HS_TIMING_HS = 1, /**< High speed */
+ SDMMC_EXT_CSD_HS_TIMING_HS200 = 2, /**< HS200 */
+ SDMMC_EXT_CSD_HS_TIMING_HS400 = 3, /**< HS400 */
+};
+
+/**
+ * @brief Extended CSD Card Type (MMC only)
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8.4, Table 50
+ * [[jedec.org](https://www.jedec.org)]
+ */
+enum {
+ SDMMC_EXT_CSD_CARD_TYPE_HS26 = 0x01, /**< Highspeed MMC @ 26MHz */
+ SDMMC_EXT_CSD_CARD_TYPE_HS52 = 0x02, /**< Highspeed MMC @ 52MHz */
+};
+
+/**
+ * @name Indices in Extended CSD (MMC only)
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 8.4, Table 46
+ * [[jedec.org](https://www.jedec.org)]
+ * @{
+ */
+#define SDMMC_EXT_CSD_BUS_WIDTH (183) /**< Index of Bus width mode */
+#define SDMMC_EXT_CSD_HS_TIMING (185) /**< High Speed Interface Timing */
+#define SDMMC_EXT_CSD_CSD_STRUCTURE (194) /**< Card Type */
+#define SDMMC_EXT_CSD_CARD_TYPE (196) /**< CSD Structure Version */
+#define SDMMC_EXT_CSD_SEC_COUNT (212) /**< Card Type */
+/** @} */
+
+/**
+ * @brief EXT_CSD access mode (MMC only)
+ *
+ * @see JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 7.4.1, Table 2
+ * [[jedec.org](https://www.jedec.org)]
+ */
+typedef enum {
+ SDMMC_EXT_CSD_COMMAND_SET = 0b00, /**< command set changed */
+ SDMMC_EXT_CSD_SET_BITS = 0b01, /**< bits in pointed byte in EXT_CSD are set */
+ SDMMC_EXT_CSD_CLEAR_BITS = 0b01, /**< bits in pointed byte in EXT_CSD are cleared */
+ SDMMC_EXT_CSD_WRITE_BYTE = 0b11, /**< value is written to pointed byte */
+} sdmmc_ext_csd_access_t;
+
+#define SDMMC_CCC_SUPPORT_CMD6 (1 << 10) /** CMD6 support flag in CSD.CCC */
+
+#define SDMMC_CMD6_MODE_CHECK (0 << 31)
+#define SDMMC_CMD6_MODE_SWITCH (1 << 31)
+
+#define SDMMC_CMD6_FUNC_GROUP1_SHIFT (0) /**< Group 1 in argument */
+#define SDMMC_CMD6_FUNC_GROUP1_MASK (0x0f) /**< No influence */
+#define SDMMC_CMD6_FUNC_GROUP1_DS (0x00) /**< Default Speed / SDR12 */
+#define SDMMC_CMD6_FUNC_GROUP1_SDR12 (0x00) /**< Default Speed / SDR12 */
+#define SDMMC_CMD6_FUNC_GROUP1_HS (0x01) /**< High Speed / SDR25 */
+#define SDMMC_CMD6_FUNC_GROUP1_SDR25 (0x01) /**< High Speed / SDR25 */
+#define SDMMC_CMD6_FUNC_GROUP1_SDR50 (0x02) /**< SDR50 */
+#define SDMMC_CMD6_FUNC_GROUP1_SDR104 (0x03) /**< SDR104 */
+#define SDMMC_CMD6_FUNC_GROUP1_DDR50 (0x04) /**< DDR50 */
+
+#define SDMMC_CMD6_FUNC_GROUP2_SHIFT (4) /**< Group 2 in argument */
+#define SDMMC_CMD6_FUNC_GROUP2_MASK (0x0f) /**< No influence */
+#define SDMMC_CMD6_FUNC_GROUP2_DEFAULT (0x00) /**< Default */
+#define SDMMC_CMD6_FUNC_GROUP2_EC (0x01) /**< For eC */
+#define SDMMC_CMD6_FUNC_GROUP2_OTP (0x03) /**< OTP */
+#define SDMMC_CMD6_FUNC_GROUP2_ASSD (0x04) /**< ASSD */
+#define SDMMC_CMD6_FUNC_GROUP2_VENDOR (0x0e) /**< Vendor specific */
+
+#define SDMMC_CMD6_FUNC_GROUP3_SHIFT (8) /**< Group 3 in argument */
+#define SDMMC_CMD6_FUNC_GROUP3_MASK (0x0f) /**< No influence */
+#define SDMMC_CMD6_FUNC_GROUP4_SHIFT (12) /**< Group 4 in argument */
+#define SDMMC_CMD6_FUNC_GROUP4_MASK (0x0f) /**< No influence */
+#define SDMMC_CMD6_FUNC_GROUP5_SHIFT (16) /**< Group 5 in argument */
+#define SDMMC_CMD6_FUNC_GROUP5_MASK (0x0f) /**< No influence */
+#define SDMMC_CMD6_FUNC_GROUP6_SHIFT (20) /**< Group 6 in argument */
+#define SDMMC_CMD6_FUNC_GROUP6_MASK (0x0f) /**< No influence */
+
+#define SDMMC_CMD6_SW_STATUS_SIZE (64) /**< Switch Status Size */
+
+/**
+ * @brief Switch Function Status
+ *
+ * @note Since the structure is only used to directly map its members to the
+ * switch state buffer returned by the card, the members are given in
+ * big-endian order with word-wise host byte order.
+ *
+ * @see Physical Layer Simplified Specification Version 9.00, Section 4.3.10.4,
+ * Table 4-13 [[sdcard.org](https://www.sdcard.org)]
+ */
+typedef struct __attribute__((packed)) {
+ /* word 15, byte 60...63 */
+ uint32_t group6_supported:16; /**< Supported Functions in Group 6 [495:480] */
+ uint32_t max_power:16; /**< Maximum Current/Power Consumption [511:496] */
+ /* word 14, byte 56...59 */
+ uint32_t group4_supported:16; /**< Supported Functions in Group 4 [463:448] */
+ uint32_t group5_supported:16; /**< Supported Functions in Group 5 [479:464] */
+ /* word 13, byte 52...55 */
+ uint32_t group2_supported:16; /**< Supported Functions in Group 2 [431:416] */
+ uint32_t group3_supported:16; /**< Supported Functions in Group 3 [447:432] */
+ /* word 12, byte 48...51 */
+ uint32_t group3_selected:4; /**< Selected Function in Group 3 [387:384] */
+ uint32_t group4_selected:4; /**< Selected Function in Group 4 [391:388] */
+ uint32_t group5_selected:4; /**< Selected Function in Group 5 [395:392] */
+ uint32_t group6_selected:4; /**< Selected Function in Group 6 [399:396] */
+ uint32_t group1_supported:16; /**< Supported Functions in Group 1 [415:400] */
+ /* word 11, byte 44...47 */
+ uint32_t group6_busy:16; /**< Busy Status of Group 6 Functions [367:352] */
+ uint32_t data_struct_v:8; /**< Data Structure Version [375:368] */
+ uint32_t group1_selected:4; /**< Selected Function in Group 1 [379:376] */
+ uint32_t group2_selected:4; /**< Selected Function in Group 2 [383:380] */
+ /* word 10, byte 41...43 */
+ uint32_t group4_busy:16; /**< Busy Status of Group 4 Functions [335:320] */
+ uint32_t group5_busy:16; /**< Busy Status of Group 5 Functions [351:336] */
+ /* word 9, byte 36...40 */
+ uint32_t group2_busy:16; /**< Busy Status of Group 2 Functions [303:288] */
+ uint32_t group3_busy:16; /**< Busy Status of Group 3 Functions [319:304] */
+ /* word 8, byte 32...35 */
+ uint32_t reserved1:16; /**< reserved [271:256] */
+ uint32_t group1_busy:16; /**< Busy Status of Group 1 Functions [287:272] */
+ /* word 0...7, byte 0...31 */
+ uint8_t reserved[32]; /**< reserved [255:0] */
+} sdmmc_sw_status_t;
+
+/* forward declaration of internal functions */
+static int _send_cmd(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp);
+static int _send_acmd(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp);
+static inline int _send_xcmd(sdmmc_dev_t *dev,
+ sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp);
+static int _xfer(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ uint16_t block_size, uint16_t block_num,
+ const void *data_wr, void *data_rd, uint16_t *done);
+static int _assert_card(sdmmc_dev_t *dev);
+static int _select_deselect(sdmmc_dev_t *dev, bool select);
+static int _get_status(sdmmc_dev_t *dev, sdmmc_card_status_t *cs);
+static int _wait_for_ready(sdmmc_dev_t *dev, uint32_t timeout_ms);
+static int _wait_while_prg(sdmmc_dev_t *dev,
+ uint32_t sleep_ms, uint32_t timeout_ms);
+static int _read_cid(sdmmc_dev_t *dev, uint8_t cmd);
+static int _read_csd(sdmmc_dev_t *dev);
+static int _read_scr(sdmmc_dev_t *dev);
+static inline int _enable_clock(sdmmc_dev_t *dev);
+static inline int _disable_clock(sdmmc_dev_t *dev);
+
+#if IS_USED(MODULE_SDMMC_MMC)
+static int _read_ext_csd(sdmmc_dev_t *dev);
+static int _write_ext_csd(sdmmc_dev_t *dev, uint8_t index, uint8_t value);
+#endif
+
+#if ENABLE_DEBUG
+static uint8_t _crc_7(const uint8_t *data, unsigned n);
+static void _print_raw_data_crc(const uint8_t *data, unsigned size, bool crc);
+#endif
+
+int sdmmc_send_cmd(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp)
+{
+ assert(dev);
+ assert(dev->driver);
+
+ int res = _assert_card(dev);
+ if (res) {
+ return res;
+ }
+
+ return _send_cmd(dev, cmd_idx, arg, resp_type, resp);
+}
+
+int sdmmc_send_acmd(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp)
+{
+ assert(dev);
+ assert(dev->driver);
+ assert(cmd_idx & SDMMC_ACMD_PREFIX);
+
+ int res;
+ uint32_t response;
+
+ if (cmd_idx == SDMMC_ACMD41) {
+ res = sdmmc_send_cmd(dev, SDMMC_CMD55,
+ SDMMC_CMD_NO_ARG,
+ SDMMC_R1, &response);
+ }
+ else {
+ assert(dev->rca);
+ res = sdmmc_send_cmd(dev, SDMMC_CMD55,
+ SDMMC_CMD_ARG_RCA(dev->rca),
+ SDMMC_R1, &response);
+ }
+
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ return res;
+ }
+ return sdmmc_send_cmd(dev, cmd_idx & ~SDMMC_ACMD_PREFIX, arg,
+ resp_type, resp);
+}
+
+int sdmmc_card_init(sdmmc_dev_t *dev)
+{
+ assert(dev);
+ assert(dev->driver);
+
+ /* use driver's card_init function if it defines its own */
+ if (dev->driver->card_init) {
+ return dev->driver->card_init(dev);
+ }
+
+ /* set bus width to 1-bit and the peripheral clock to 400 kHz for init */
+ dev->driver->set_bus_width(dev, SDMMC_BUS_WIDTH_1BIT);
+ dev->driver->set_clock_rate(dev, SDMMC_CLK_400K);
+
+ _ZTIMER_ACQUIRE(); /* ztimer is needed for different timeouts */
+
+ uint32_t response[4]; /* long response requires four 32-bit words */
+ int res;
+
+ /* timeout handling */
+ uint32_t t_start = _ZTIMER_NOW();
+
+ _enable_clock(dev);
+
+ DEBUG("[sdmmc] send CMD0 to put all cards into 'idle' state\n");
+ /* TODO: SPI mode uses CMD0 with R1 */
+ res = _send_cmd(dev, SDMMC_CMD0, SDMMC_CMD_NO_ARG, SDMMC_NO_R, NULL);
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ goto out;
+ }
+
+ /* a short delay is required here */
+ _ZTIMER_SLEEP_MS(1);
+
+ /* reset RCA and card type */
+ dev->rca = 0;
+ dev->type = SDMMC_CARD_TYPE_UNKNOWN;
+
+ /* Card identification process */
+
+ DEBUG("[sdmmc] send CMD8 to test for a SD/SDIO card\n");
+ res = _send_cmd(dev, SDMMC_CMD8, SDMMC_CMD8_CHECK, SDMMC_R7, response);
+
+ /* SD Card should now be in `idle` state */
+
+ /* TODO: SPI mode uses CMD59 with CRC_ON_OFF (mandatory) */
+
+ /*
+ * If command CMD8 succeeds, the card is either
+ * - a SD Card compliant to Version 2.00 or later, or
+ * - a Combo card compliant to Version 2.00 or later.
+ * Use
+ * - CMD5 with Voltage Window 0 to determine whether it is a Combo Card
+ * - ACMD41 with HCS=1 to determine the capacity.
+ *
+ * If the card didn't respond to CMD8 (-ETIMEDOUT), the card is either
+ * - a SD Card Version 2.00 or later SD Card but the voltage mismatches, or
+ * - a SD Card Version 1.x, or
+ * - a MMC, or
+ * - a SDIO card
+ * Use
+ * - CMD5 with Voltage Window 0 to determine whether it is a SDIO Card
+ * - ACMD41 with HCS=0 to check
+ *
+ * If the card answers with invalid response, the card is unusable and
+ * the card initialization and identification process is stopped.
+ */
+
+ if (res && (res != -ETIMEDOUT)) {
+ /* card answers with invalid response, card is unusable */
+ LOG_ERROR("[sdmmc] command failed with error %d, card unusable\n", res);
+ res = -ENODEV;
+ goto out;
+ }
+
+ if ((res == 0) && ((response[0] & SDMMC_CMD8_CHECK) != SDMMC_CMD8_CHECK)) {
+ /* card answers but received R7 doesn't match, card unusable */
+ LOG_ERROR("[sdmmc] R7 mismatch %08"PRIx32"!=%08"PRIx32", "
+ "card unusable\n",
+ (uint32_t)SDMMC_CMD8_CHECK, response[0] & SDMMC_CMD8_CHECK);
+ res = -ENODEV;
+ goto out;
+ }
+
+ bool flag_f8 = (res != -ETIMEDOUT);
+
+ /* check whether it is a SDIO or Combo card using CMD5 with
+ * Voltage Window = 0 to test */
+ DEBUG("[sdmmc] send CMD5 to test for a SDIO or Combo card\n");
+
+ /* If CMD5 succeeds, it is a SDIO or Combo Card. */
+ if (_send_cmd(dev, SDMMC_CMD5, SDMMC_CMD_NO_ARG, SDMMC_R4, response) == 0) {
+ dev->type = SDMMC_CARD_TYPE_SDIO;
+ /* If CMD8 previously failed (flag_f8 = 0), it is a SDIO only card */
+ if (!IS_USED(MODULE_SDMMC_SDIO) && (res == 0) && !flag_f8) {
+ /* TODO: SDIO support */
+ LOG_ERROR("[sdmmc] card is a SDIO card, not supported yet\n");
+ res = -ENOTSUP;
+ goto out;
+ }
+ }
+
+ /* use ACMD41 with Voltage Window = 0 to test for a SD Card/Combo Card */
+ res = _send_acmd(dev, SDMMC_ACMD41, SDMMC_CMD_NO_ARG,
+ SDMMC_R3, response);
+
+ uint8_t cmd;
+ uint32_t arg;
+
+ /* Note: for use in SPI mode
+ * CMD58 has to be used here in SPI mode to determine the CCS flag */
+
+ if (res == 0) {
+ /* if SD Card/Combo Card, then use ACMD41 with Voltage Window and
+ * HCS=1 if flag_f8 is true or HCS=0 otherwise */
+ cmd = SDMMC_ACMD41;
+ arg = SDMMC_ACMD41_VOLTAGES | (flag_f8 ? SDMMC_ACMD41_HCS : 0);
+ }
+ else {
+ /* otherwise try CMD1 with Voltage Window for MMC */
+ cmd = SDMMC_CMD1;
+ arg = SDMMC_OCR_ALL_VOLTAGES | SDMMC_OCR_18V | SDMMC_OCR_CCS;
+ }
+
+ /* the card is either SD card, Combo card or MMC */
+ do {
+ _ZTIMER_SLEEP_MS(100);
+ DEBUG("[sdmmc] send %s %s to test whether card is SDSC/SDXC or MMC card\n",
+ cmd == SDMMC_ACMD41 ? "ACMD41" : "CMD1",
+ arg & SDMMC_ACMD41_HCS ? "HCS=1" : "HCS=0");
+ res = _send_xcmd(dev, cmd, arg, SDMMC_R3, response);
+ if (res == -ETIMEDOUT) {
+ LOG_ERROR("[sdmmc] no response, card not present or not a SD Card\n");
+ goto out;
+ }
+ else if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ res = -EIO;
+ goto out;
+ }
+ } while (((response[0] & SDMMC_R3_POWER_UP) == 0) &&
+ ((_ZTIMER_NOW() - t_start) < SDMMC_INIT_TIMEOUT_MS));
+
+ if ((_ZTIMER_NOW() - t_start) >= SDMMC_INIT_TIMEOUT_MS) {
+ /* if timed out, it is neither SD Card/Combo Card nor MMC card */
+ LOG_ERROR("[sdmmc] Card could not be detected\n");
+ res = -ENODEV;
+ goto out;
+ }
+
+ /* On success the card can operate at 2.7-3.6V VDD here */
+
+ if (cmd == SDMMC_ACMD41) {
+ /* card is either SD Card or Combo card
+ * TODO: since SDIO is not yet supported, the type is simply
+ * overwritten, otherwise the type would have to be ORed here */
+ if (!flag_f8) {
+ /* SDSC Card Version 1.x if it did not respond to CMD8 */
+ dev->type = SDMMC_CARD_TYPE_SDSC_V1;
+ }
+ else if (response[0] & SDMMC_R3_CCS) {
+ /* SDHC/SDXC Card card if did react on CMD8 and set CCS */
+ dev->type = SDMMC_CARD_TYPE_SDHC_SDXC;
+ }
+ else {
+ /* SDSC Card Version 2.x or later if it did respond to CMD8 but
+ * didn't set CCS */
+ dev->type = SDMMC_CARD_TYPE_SDSC_V2_V3;
+ }
+ }
+ else {
+ /* card is a MultimediaCard */
+ dev->type = SDMMC_CARD_TYPE_MMC;
+ /* TODO: MMC support */
+ if (!IS_USED(MODULE_SDMMC_MMC)) {
+ LOG_ERROR("[sdmmc] MultimediaCard detected, not supported\n");
+ res = -ENOTSUP;
+ goto out;
+ }
+ }
+
+ /* SD Card or all MMC cards are now in `ready` state */
+
+ /* Card identification using CMD2 and CMD3 is done once per peripheral */
+ DEBUG("[sdmmc] read CID to put the card in 'ident' state\n");
+
+ /* read CID and bring the card into the `ident` state */
+ res = _read_cid(dev, SDMMC_CMD2);
+ if (res) {
+ /* no card found */
+ goto out;
+ }
+
+ /* SD Card or MMC card is now in `ident` state */
+
+ if (IS_USED(MODULE_SDMMC_MMC) && (dev->type == SDMMC_CARD_TYPE_MMC)) {
+ DEBUG("[sdmmc] send CMD3 to set RCA\n");
+ /* for MMCs, RCA is selected and assigned by host using the device address */
+ dev->rca = ((uint32_t)dev & 0x0000ffff);
+ res = _send_cmd(dev, SDMMC_CMD3, SDMMC_CMD_ARG_RCA(dev->rca),
+ SDMMC_R1, response);
+ }
+ else {
+ DEBUG("[sdmmc] send CMD3 to get RCA\n");
+ /* for SD cards, the card selects RCA and sends it back in R3 */
+ res = _send_cmd(dev, SDMMC_CMD3, SDMMC_CMD_NO_ARG, SDMMC_R3, response);
+ dev->rca = response[0] >> 16;
+ }
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ goto out;
+ }
+
+ /* SD Card or MMC is now in `stby` state */
+
+ DEBUG("[sdmmc] read CSD\n");
+ res = _read_csd(dev);
+ if (res) {
+ goto out;
+ }
+
+ /* select the card to put it in the `tran` state for further programming */
+ res = _select_deselect(dev, true);
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ goto out;
+ }
+
+ /* SD Memory Card or MMC card is now selected and in `tran` state */
+
+ if (dev->type != SDMMC_CARD_TYPE_MMC) {
+ res = _read_scr(dev);
+ if (res) {
+ goto out;
+ }
+
+ DEBUG("[sdmmc] send CMD16 to set the block length to %d byte\n",
+ SDMMC_SDHC_BLOCK_SIZE);
+ res = _send_cmd(dev, SDMMC_CMD16, SDMMC_SDHC_BLOCK_SIZE,
+ SDMMC_R1, response);
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ goto out;
+ }
+
+ /* change the bus width if the configured bus width is not 1-bit */
+ if (dev->bus_width != SDMMC_BUS_WIDTH_1BIT) {
+ /* ensure that the sdmmc_bus_width_t is not changed accientially */
+ static_assert(SDMMC_BUS_WIDTH_1BIT == 1, "SDMMC_BUS_WIDTH_1BIT != 1");
+ static_assert(SDMMC_BUS_WIDTH_4BIT == 4, "SDMMC_BUS_WIDTH_4BIT != 4");
+ static_assert(SDMMC_BUS_WIDTH_8BIT == 8, "SDMMC_BUS_WIDTH_8BIT != 8");
+
+ /* use the minimum of configured and supported bus width,
+ * supported bus widths are given as ORed integer numbers. */
+ sdmmc_bus_width_t width = (dev->bus_width < dev->scr.SD_BUS_WIDTHS)
+ ? dev->bus_width
+ : bitarithm_msb(dev->scr.SD_BUS_WIDTHS);
+ DEBUG("[sdmmc] send ACMD6 to set %d-bit bus width\n", width);
+ res = _send_acmd(dev, SDMMC_ACMD6,
+ width == SDMMC_BUS_WIDTH_4BIT ? SDMMC_ACMD6_BUS_WIDTH_4BIT
+ : SDMMC_ACMD6_BUS_WIDTH_8BIT,
+ SDMMC_R1, response);
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ goto out;
+ }
+
+ dev->driver->set_bus_width(dev, width);
+ }
+
+ /* finally change the bus speed used by the peripheral to default speed */
+ dev->driver->set_clock_rate(dev, SDMMC_CLK_25M);
+
+ /* if the peripheral supports High Speed mode, try to switch into */
+ if (IS_USED(MODULE_PERIPH_SDMMC_HS) && dev->scr.SD_SPEC) {
+ static_assert(sizeof(sdmmc_sw_status_t) == SDMMC_CMD6_SW_STATUS_SIZE,
+ "sizeof(sdmmc_sw_status_t) != SDMMC_CMD6_SW_STATUS_SIZE");
+ /* try to switch to high speed for SD Memory Cards v1.10 or higher */
+ uint32_t arg = (SDMMC_CMD6_FUNC_GROUP1_HS << SDMMC_CMD6_FUNC_GROUP1_SHIFT) |
+ (SDMMC_CMD6_FUNC_GROUP2_DEFAULT << SDMMC_CMD6_FUNC_GROUP2_SHIFT) |
+ (SDMMC_CMD6_FUNC_GROUP3_MASK << SDMMC_CMD6_FUNC_GROUP3_SHIFT) |
+ (SDMMC_CMD6_FUNC_GROUP4_MASK << SDMMC_CMD6_FUNC_GROUP4_SHIFT) |
+ (SDMMC_CMD6_FUNC_GROUP5_MASK << SDMMC_CMD6_FUNC_GROUP5_SHIFT) |
+ (SDMMC_CMD6_FUNC_GROUP6_MASK << SDMMC_CMD6_FUNC_GROUP6_SHIFT);
+ sdmmc_buf_t status[SDMMC_CMD6_SW_STATUS_SIZE];
+ res = _xfer(dev, SDMMC_CMD6, SDMMC_CMD6_MODE_SWITCH | arg,
+ SDMMC_CMD6_SW_STATUS_SIZE, 1, NULL, status, NULL);
+#if ENABLE_DEBUG
+ _print_raw_data_crc(status, SDMMC_CMD6_SW_STATUS_SIZE, false);
+#endif
+ if (res) {
+ goto out;
+ }
+
+ /* convert big endian to host byte order */
+ uint32_t *raw_data = (void *)status;
+ for (unsigned i = 0; i < (SDMMC_CMD6_SW_STATUS_SIZE >> 2); i++) {
+ raw_data[i] = ntohl(raw_data[i]);
+ }
+
+ sdmmc_sw_status_t *sw_status = (void *)status;
+ if (sw_status->group1_selected == SDMMC_CMD6_FUNC_GROUP1_HS) {
+ /* switching the card to high speed was successful */
+ dev->driver->set_clock_rate(dev, SDMMC_CLK_50M);
+ }
+ }
+ }
+ else {
+ /* handle MMCs only if MMC support is enabled */
+#if IS_USED(MODULE_SDMMC_MMC)
+ /* read Extended CSD */
+ res = _read_ext_csd(dev);
+ if (res) {
+ goto out;
+ }
+
+ /* change the bus width if the configured bus width is not 1-bit */
+ if (dev->bus_width != SDMMC_BUS_WIDTH_1BIT) {
+ /* TODO: the bus width for MMC would require testing the
+ * functional pins with CMD19, for simplicity the configured
+ * device bus width is used for the moment */
+ sdmmc_bus_width_t width = dev->bus_width;
+ uint8_t value = (width == SDMMC_BUS_WIDTH_4BIT ? SDMMC_EXT_CSD_BUS_WIDTH_4BIT
+ : SDMMC_EXT_CSD_BUS_WIDTH_8BIT);
+ DEBUG("[sdmmc] set %d-bit bus width\n", dev->bus_width);
+ res = _write_ext_csd(dev, SDMMC_EXT_CSD_BUS_WIDTH, value);
+ if (res) {
+ goto out;
+ }
+ dev->driver->set_bus_width(dev, dev->bus_width);
+ }
+
+ /* change HS_TIMING if the card supports high speed interface */
+ if (IS_USED(MODULE_PERIPH_SDMMC_HS) &&
+ (dev->csd.mmc.SPEC_VERS >= 4) &&
+ (dev->ext_csd.CARD_TYPE & SDMMC_EXT_CSD_CARD_TYPE_HS52)) {
+ DEBUG("[sdmmc] set high speed interface timing\n");
+ res = _write_ext_csd(dev, SDMMC_EXT_CSD_HS_TIMING, SDMMC_EXT_CSD_HS_TIMING_HS);
+ if (res) {
+ goto out;
+ }
+ dev->driver->set_clock_rate(dev, SDMMC_CLK_52M);
+ }
+ else {
+ dev->driver->set_clock_rate(dev, SDMMC_CLK_26M);
+ }
+#endif /* IS_USED(MODULE_SDMMC_MMC) */
+ }
+
+ dev->init_done = true;
+
+out:
+ /* release ztimer */
+ _ZTIMER_RELEASE();
+ _disable_clock(dev);
+
+ return res;
+}
+
+int sdmmc_xfer(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ uint16_t block_size, uint16_t block_num,
+ const void *data_wr, void *data_rd, uint16_t *done)
+{
+ assert(dev);
+ assert(dev->driver);
+
+ if (done) {
+ *done = 0;
+ }
+
+ int res = _assert_card(dev);
+ if (res) {
+ return res;
+ }
+
+ return _xfer(dev, cmd_idx, arg, block_size, block_num,
+ data_wr, data_rd, done);
+}
+
+int sdmmc_read_blocks(sdmmc_dev_t *dev,
+ uint32_t block_addr, uint16_t block_size,
+ uint16_t block_num, void *data, uint16_t *done)
+{
+ DEBUG("[sdmmc] %s dev=%p block_addr=%"PRIu32" block_size=%u block_num=%u done=%p\n",
+ __func__, dev, block_addr, block_size, block_num, done);
+
+ return sdmmc_xfer(dev, (block_num == 1) ? SDMMC_CMD17 : SDMMC_CMD18,
+ block_addr, block_size, block_num, NULL, data, done);
+}
+
+int sdmmc_write_blocks(sdmmc_dev_t *dev,
+ uint32_t block_addr, uint16_t block_size,
+ uint16_t block_num, const void *data, uint16_t *done)
+{
+ DEBUG("[sdmmc] %s dev=%p block_addr=%"PRIu32" block_size=%u block_num=%u done=%p\n",
+ __func__, dev, block_addr, block_size, block_num, done);
+
+ return sdmmc_xfer(dev, (block_num == 1) ? SDMMC_CMD24 : SDMMC_CMD25,
+ block_addr, block_size, block_num, data, NULL, done);
+}
+
+int sdmmc_erase_blocks(sdmmc_dev_t *dev,
+ uint32_t block_addr, uint16_t block_num)
+{
+ DEBUG("[sdmmc] %s dev=%p addr=%"PRIu32" num=%u\n",
+ __func__, dev, block_addr, block_num);
+
+ assert(dev);
+ assert(dev->driver);
+ assert(block_num);
+
+ int res = _assert_card(dev);
+ if (res) {
+ return res;
+ }
+
+ if (IS_USED(MODULE_SDMMC_MMC) && (dev->type == SDMMC_CARD_TYPE_MMC)) {
+ /* MMCs don't support the erase of a single block but only the erase of
+ * a group of blocks. Blocks are erased implicitly on write. */
+ DEBUG("[sdmmc] MMCs don't support the erase of single blocks\n");
+ return -ENOTSUP;
+ }
+
+ uint32_t block_end = block_addr + block_num - 1;
+
+ /* SDSC Cards use byte unit address */
+ if ((dev->type == SDMMC_CARD_TYPE_SDSC_V1) ||
+ (dev->type == SDMMC_CARD_TYPE_SDSC_V2_V3)) {
+ block_addr *= SDMMC_SDHC_BLOCK_SIZE;
+ block_end *= SDMMC_SDHC_BLOCK_SIZE;
+ }
+
+ if (_enable_clock(dev)) {
+ return -EIO;
+ }
+
+ /* wait until the card is ready */
+ if ((res = _wait_for_ready(dev, SDMMC_WAIT_FOR_CARD_READY_MS))) {
+ /* reset the Card */
+ dev->init_done = false;
+ goto out;
+ }
+
+ uint32_t response;
+
+ DEBUG("[sdmmc] send CMD32 and CMD33 to set erase parameters\n");
+ if (((res = _send_cmd(dev, SDMMC_CMD32, block_addr, SDMMC_R1, &response)) != 0) ||
+ ((res = _send_cmd(dev, SDMMC_CMD33, block_end, SDMMC_R1, &response)) != 0)) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ goto out;
+ }
+
+ DEBUG("[sdmmc] send CMD38 to execute erase\n");
+ if ((res = _send_cmd(dev, SDMMC_CMD38, 0, SDMMC_R1B, &response))) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ goto out;
+ }
+
+ /* wait until the card left the programming state */
+ if ((res = _wait_while_prg(dev, 1, block_num * SDMMC_ERASE_TIMEOUT_MS))) {
+ goto out;
+ }
+
+out:
+ if (_disable_clock(dev)) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+uint64_t sdmmc_get_capacity(sdmmc_dev_t *dev)
+{
+ DEBUG("[sdmmc] %s dev=%p\n", __func__, dev);
+
+ assert(dev);
+ assert(dev->driver);
+
+ int res = _assert_card(dev);
+ if (res) {
+ return res;
+ }
+
+ uint32_t block_len = 0;
+ uint32_t block_nr = 0;
+
+ if (dev->type == SDMMC_CARD_TYPE_MMC) {
+#if IS_USED(MODULE_SDMMC_MMC)
+ if (dev->csd.mmc.C_SIZE == 0xfff) {
+ /* memory capacity = SEC_COUNT * 512 Byte */
+ block_nr = dev->ext_csd.SEC_COUNT;
+ block_len = SDMMC_SDHC_BLOCK_SIZE;
+ }
+ else {
+ /* memory capacity = BLOCKNR * BLOCK_LEN
+ * MULT = 2^(C_SIZE_MULT+2)
+ * BLOCKNR = (C_SIZE+1) * MULT
+ * BLOCK_LEN = 2^READ_BL_LEN */
+ block_nr = (dev->csd.mmc.C_SIZE + 1) *
+ (1 << (dev->csd.mmc.C_SIZE_MULT + 2));
+ block_len = 1 << dev->csd.mmc.READ_BL_LEN;
+ }
+#endif
+ }
+ else if (dev->csd.v1.CSD_STRUCTURE == SDMMC_CSD_V1) {
+ /* memory capacity = BLOCKNR * BLOCK_LEN
+ * MULT = 2^(C_SIZE_MULT+2)
+ * BLOCKNR = (C_SIZE+1) * MULT
+ * BLOCK_LEN = 2^READ_BL_LEN */
+ block_nr = (dev->csd.v1.C_SIZE + 1) *
+ (1 << (dev->csd.v1.C_SIZE_MULT + 2));
+ block_len = 1 << dev->csd.v1.READ_BL_LEN;
+ }
+ else if (dev->csd.v2.CSD_STRUCTURE == SDMMC_CSD_V2) {
+ /* memory capacity = (C_SIZE+1) * 512 KByte */
+ block_nr = dev->csd.v2.C_SIZE + 1;
+ block_len = SDMMC_SDHC_BLOCK_SIZE << 10;
+ }
+ return (uint64_t)block_nr * block_len;
+}
+
+int sdmmc_read_sds(sdmmc_dev_t *dev, sdmmc_sd_status_t *sds)
+{
+ DEBUG("[sdmmc] %s dev=%p sds=%p\n", __func__, dev, sds);
+
+ assert(dev);
+ assert(sds);
+
+ /* card must have a valid RCA */
+ assert(dev->rca);
+
+ int res = _assert_card(dev);
+ if (res) {
+ return res;
+ }
+
+ sdmmc_buf_t raw_data[SDMMC_SD_STATUS_SIZE];
+
+ if (IS_USED(MODULE_SDMMC_MMC) && (dev->type == SDMMC_CARD_TYPE_MMC)) {
+ LOG_ERROR("[sdmmc] MMC cards don't have a SD Status register\n");
+ return -ENOTSUP;
+ }
+
+ DEBUG("[sdmmc] read SD status raw data");
+ res = _xfer(dev, SDMMC_ACMD13, SDMMC_CMD_NO_ARG,
+ SDMMC_SD_STATUS_SIZE, 1, NULL, raw_data, NULL);
+#if ENABLE_DEBUG
+ _print_raw_data_crc(raw_data, SDMMC_SD_STATUS_SIZE, false);
+#endif
+
+ if (res) {
+ DEBUG("[sdmmc] reading SD status raw data failed");
+ return res;
+ }
+
+ sds->DAT_BUS_WIDTH = raw_data[0] >> 6;
+ sds->SECURED_MODE = (raw_data[0] & (1 << 5)) >> 5;
+ sds->SD_CARD_TYPE = (raw_data[2] << 8) | raw_data[3];
+ sds->SIZE_OF_PROTECTED_AREA = (raw_data[4] << 24) |
+ (raw_data[5] << 16) |
+ (raw_data[6] << 8) | raw_data[7];
+ sds->SPEED_CLASS = raw_data[8];
+ sds->PERFORMANCE_MOVE = raw_data[9];
+ sds->AU_SIZE = raw_data[10] >> 4;
+ sds->ERASE_SIZE = (raw_data[11] << 8) | raw_data[12];
+ sds->ERASE_TIMEOUT = raw_data[13] >> 2;
+ sds->ERASE_OFFSET = raw_data[13] & 0x03;
+ sds->UHS_SPEED_GRADE = raw_data[14] >> 4;
+ sds->UHS_AU_SIZE = raw_data[14] & 0x0F;
+ sds->VIDEO_SPEED_CLASS = raw_data[15];
+ sds->VSC_AU_SIZE = ((raw_data[16] & 0x03) << 8) | raw_data[17];
+ sds->SUS_ADDR = (raw_data[18] << 14) | (raw_data[19] << 6) | (raw_data[20] >> 2);
+
+ return 0;
+}
+
+/* Internal functions */
+
+static int _send_cmd(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp)
+{
+ DEBUG("[sdmmc] %s dev=%p cmd_idx=%u arg=%"PRIu32" resp_type=%u resp=%p\n",
+ __func__, dev, cmd_idx, arg, resp_type, resp);
+
+#if !IS_USED(MODULE_PERIPH_SDMMC_AUTO_CLK)
+ /* enable the SD CLK signal if the SDIO/SD/MMC peripheral driver does
+ * not support the Auto-CLK feature (periph_sdmmc_auto_clk) */
+ if (dev->driver->enable_clock && dev->driver->enable_clock(dev, true)) {
+ return -EIO;
+ }
+#endif
+
+ return dev->driver->send_cmd(dev, cmd_idx, arg, resp_type, resp);
+}
+
+static int _send_acmd(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp)
+{
+ uint32_t response;
+ int res;
+
+ DEBUG("[sdmmc] %s dev=%p cmd_idx=%u arg=%"PRIu32" resp_type=%u resp=%p\n",
+ __func__, dev, cmd_idx, arg, resp_type, resp);
+
+ assert(cmd_idx & SDMMC_ACMD_PREFIX);
+
+ if (cmd_idx == SDMMC_ACMD41) {
+ res = _send_cmd(dev, SDMMC_CMD55, SDMMC_CMD_NO_ARG,
+ SDMMC_R1, &response);
+ }
+ else {
+ assert(dev->rca);
+ res = _send_cmd(dev, SDMMC_CMD55, SDMMC_CMD_ARG_RCA(dev->rca),
+ SDMMC_R1, &response);
+ }
+
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ return res;
+ }
+
+ return _send_cmd(dev, cmd_idx & ~SDMMC_ACMD_PREFIX, arg, resp_type, resp);
+}
+
+static inline int _send_xcmd(sdmmc_dev_t *dev,
+ sdmmc_cmd_t cmd_idx, uint32_t arg,
+ sdmmc_resp_t resp_type, uint32_t *resp)
+{
+ assert(dev);
+ assert(dev->driver);
+
+ if (cmd_idx & SDMMC_ACMD_PREFIX) {
+ return _send_acmd(dev, cmd_idx, arg, resp_type, resp);
+ }
+ else {
+ return _send_cmd(dev, cmd_idx, arg, resp_type, resp);
+ }
+}
+
+static int _xfer(sdmmc_dev_t *dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
+ uint16_t block_size, uint16_t block_num,
+ const void *data_wr, void *data_rd, uint16_t *done)
+{
+ DEBUG("[sdmmc] %s dev=%p cmd_idx=%u arg=%"PRIu32" block_size=%u "
+ "block_num=%u data_wr=%p data_rd=%p done=%p\n",
+ __func__, dev, cmd_idx, arg, block_size, block_num,
+ data_wr, data_rd, done);
+
+ /* TODO: in SDIO multi-byte mode, the block_size must be between 1 and 512
+ * and block_num must be 1 */
+ /* TODO: in stream mode, the block_size and block_num must be 0 */
+ assert(block_num);
+
+ /* check for valid transfer commands */
+ assert((cmd_idx == SDMMC_CMD6) ||
+ (cmd_idx == SDMMC_CMD8) || (cmd_idx == SDMMC_CMD9) ||
+ (cmd_idx == SDMMC_CMD17) || (cmd_idx == SDMMC_CMD18) ||
+ (cmd_idx == SDMMC_CMD24) || (cmd_idx == SDMMC_CMD25) ||
+ (cmd_idx == SDMMC_ACMD13) || (cmd_idx == SDMMC_ACMD51));
+
+ /* only CMD18 and CMD25 are multiple block transfers */
+ if ((cmd_idx == SDMMC_CMD18) || (cmd_idx == SDMMC_CMD25)) {
+ assert(block_num > 1);
+ }
+ else {
+ assert(block_num == 1);
+ }
+
+ uint32_t response;
+ int res;
+
+ /* TODO: all transfer types (MMC Stream, SDIO Multibyte) and Auto-CDM23 feature */
+ sdmmc_xfer_desc_t xfer = {
+ .type = SDMMC_BLOCK, /* at the moment only block transfer supported */
+ .cmd_idx = cmd_idx,
+ .arg = arg,
+ .resp_type = SDMMC_R1, /* all supported transfer commands use R1 */
+ .write = (cmd_idx == SDMMC_CMD24) || (cmd_idx == SDMMC_CMD25),
+ .block_size = block_size,
+ .block_num = block_num,
+ };
+
+ /* for write transfers `data_wr` must not be NULL, otherwise `data_rd` */
+ assert((xfer.write && data_wr) || data_rd);
+
+ /* enable the SD CLK signal */
+ if (_enable_clock(dev)) {
+ return -EIO;
+ }
+
+ /* wait until the card is ready */
+ if ((res = _wait_for_ready(dev, SDMMC_WAIT_FOR_CARD_READY_MS))) {
+ /* reset the Card */
+ dev->init_done = false;
+ goto out;
+ }
+
+ /* send CMD55 for application specific commands before preparing the transfer */
+ if (cmd_idx & SDMMC_ACMD_PREFIX) {
+ res = _send_cmd(dev, SDMMC_CMD55, SDMMC_CMD_ARG_RCA(dev->rca),
+ SDMMC_R1, &response);
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ goto out;
+ }
+ }
+
+ /* prepare the transfer in device driver */
+ DEBUG("[sdmmc] prepare transfer @%08"PRIx32" for %u block(s) with %u bytes\n",
+ arg, block_num, block_size);
+ res = dev->driver->xfer_prepare(dev, &xfer);
+ if (res) {
+ res = -EINVAL;
+ goto out;
+ }
+
+ /* send the command to trigger the transfer */
+ DEBUG("[sdmmc] send %s%d\n", cmd_idx & SDMMC_ACMD_PREFIX ? "ACMD" : "CMD",
+ cmd_idx & ~SDMMC_ACMD_PREFIX);
+ res = _send_cmd(dev, cmd_idx & ~SDMMC_ACMD_PREFIX, arg,
+ SDMMC_R1, &response);
+ if (res) {
+ goto out;
+ }
+
+ /* execute the transfer in device driver */
+ DEBUG("[sdmmc] execute transfer\n");
+ res = dev->driver->xfer_execute(dev, &xfer, xfer.write ? data_wr : NULL,
+ !xfer.write ? data_rd : NULL, done);
+ if (res) {
+ DEBUG("[sdmmc] transfer failed with error %d\n", res);
+ /* Don't return by intention to call _xfer_finish in any case */
+ }
+
+ /* finish the transfer in device driver */
+ DEBUG("[sdmmc] stop transfer\n");
+ dev->driver->xfer_finish(dev, &xfer);
+
+ /* TODO: use CMD23 (SET_BLOCK_COUNT) instead of CMD12 (STOP_TRANSMISSION)
+ * in case of multiple block transfers if supported by card, requires
+ * that SCR.CMD_SUPPORT[1] (SCR bit 33) is set */
+ if (block_num > 1) {
+ if (IS_USED(MODULE_PERIPH_SDMMC_AUTO_CMD12)) {
+ DEBUG("[sdmmc] Auto CMD12 used\n");
+ }
+ else if (IS_USED(MODULE_SDMMC_MMC) && (dev->type == SDMMC_CARD_TYPE_MMC)) {
+ /* MMC cards use R1 in case of read and R1B in case of write */
+ _send_cmd(dev, SDMMC_CMD12, SDMMC_CMD_NO_ARG,
+ xfer.write ? SDMMC_R1B : SDMMC_R1, &response);
+ }
+ else {
+ /* SD Cards always use R1B */
+ _send_cmd(dev, SDMMC_CMD12, SDMMC_CMD_NO_ARG, SDMMC_R1B, &response);
+ }
+ }
+
+ /* wait until the card left the programming state */
+ if (xfer.write && (res = _wait_while_prg(dev, 1, SDMMC_DATA_W_TIMEOUT_MS))) {
+ /* reset the Card */
+ dev->init_done = false;
+ goto out;
+ }
+
+out:
+ if (_disable_clock(dev)) {
+ return -EIO;
+ }
+
+ return res;
+}
+
+/**
+ * @brief Ensure that the card is present and initialized
+ */
+static int _assert_card(sdmmc_dev_t *dev)
+{
+ if (!dev->present) {
+ return -ENODEV;
+ }
+
+ /* return if card is already initialized */
+ if (dev->init_done) {
+ return 0;
+ }
+
+ /* otherwise initialize the card */
+ return sdmmc_card_init(dev);
+}
+
+/**
+ * @brief Select or deselect a card (Internal Function)
+ *
+ * The function selects or deselects the given card. The card to be selected
+ * is addressed by its RCA and must be in the `stby` state. When the card is
+ * selected, it changes to the `tran` state. All other not addressed cards
+ * are deselected by this and go into the state `stby`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[in] card Card or embedded device to be selected or deselected
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int _select_deselect(sdmmc_dev_t *dev, bool select)
+{
+ /* card must have a valid RCA */
+ assert(dev->rca);
+
+ uint32_t response;
+ int res;
+
+ DEBUG("[sdmmc] send CMD7 to %s the card\n", select ? "select" : "deselect");
+ if (select) {
+ res = _send_cmd(dev, SDMMC_CMD7, SDMMC_CMD_ARG_RCA(dev->rca),
+ SDMMC_R1B, &response);
+ }
+ else {
+ res = _send_cmd(dev, SDMMC_CMD7, SDMMC_CMD_NO_ARG, SDMMC_NO_R, NULL);
+ }
+
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ return res;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Get card status word as returned in responses R1 (Internal Function)
+ *
+ * The function returns the status of the given card as defined in
+ * - Physical Layer Simplified Specification Version 9.00, Section 4.10.1,
+ * Table 4-42 [[sdcard.org](https://www.sdcard.org)](sdcard.org)
+ * - SDIO Simplified Specification Version 3.00, 4.10.8, Table 4-7,
+ * [[sdcard.org](https://www.sdcard.org)]
+ * - JEDEC Standard No. JESD84-B42, MultiMediaCard (MMC) Electrical
+ * Standard, High Capacity (MMCA, 4.2), Section 7.11, Table 25
+ * [[jedec.org](https://www.jedec.org)]
+ * and returned in R1 response.
+ *
+ * CMD13 can be used for the addressed card in most states except `idle`,
+ * `ready`, `ident` and `ina`.
+ *
+ * @param[in] dev SDIO/SD/MMC device to be used
+ * @param[out] cs Card status
+ *
+ * @retval 0 on success
+ * @retval -ENODEV on error
+ */
+static int _get_status(sdmmc_dev_t *dev, sdmmc_card_status_t *cs)
+{
+ static_assert(sizeof(sdmmc_card_status_t) == sizeof(uint32_t),
+ "sizeof(sdmmc_card_status_t) != sizeof(uint32_t)");
+
+ /* card must have a valid RCA */
+ assert(dev->rca);
+
+ if (_send_cmd(dev, SDMMC_CMD13, SDMMC_CMD_ARG_RCA(dev->rca),
+ SDMMC_R1, &cs->value)) {
+ /* we suppose that the device is not present on error */
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/**
+ * @brief Wait until the given SD Memory Card or MMC is ready (Internal Function)
+ *
+ * The function waits until the given SD Memory Card or MMC is ready
+ * which is indicated by the `READY_FOR_DATA` bit in the card status word.
+ *
+ * @param[in] dev SD/MMC device to be used
+ * @param[in] timeout_ms Timeout in ms, must not be 0
+ *
+ * @retval 0 on success
+ * @retval -ENODEV if _get_status returned with any error
+ * @retval -ETIMEDOUT if card was available but waiting for ready timed out
+ */
+static inline int _wait_for_ready(sdmmc_dev_t *dev, uint32_t timeout_ms)
+{
+ /* card must be any SD Memory Card or MMC */
+ assert(dev->type & (SDMMC_CARD_TYPE_SD | SDMMC_CARD_TYPE_MMC));
+ assert(timeout_ms);
+
+ uint32_t t_start = _ZTIMER_NOW();
+ sdmmc_card_status_t cs;
+
+ do {
+ if (_get_status(dev, &cs)) {
+ LOG_ERROR("[sdmmc] Card not present\n");
+ return -ENODEV;
+ }
+ if (cs.value & SDMMC_CARD_STATUS_READY_FOR_DATA) {
+ return 0;
+ }
+ /* check the state every 10 ms */
+ _ZTIMER_SLEEP_MS(10);
+ } while ((_ZTIMER_NOW() - t_start) < timeout_ms);
+
+ LOG_ERROR("[sdmmc] Card is busy\n");
+ return -ETIMEDOUT;
+}
+
+/**
+ * @brief Wait with sleep as long as the card is programming
+ *
+ * @param[in] dev SD/MMC device to be used
+ * @param[in] sleep_ms Sleep time in ms, busy wait if 0
+ * @param[in] timeout_ms Timeout in ms, no timeout if 0
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int _wait_while_prg(sdmmc_dev_t *dev,
+ uint32_t sleep_ms, uint32_t timeout_ms)
+{
+ uint32_t t_start = _ZTIMER_NOW();
+
+ int res;
+ sdmmc_card_status_t cs;
+
+ _ZTIMER_ACQUIRE();
+ do {
+ res = _get_status(dev, &cs);
+ if (res || (cs.CURRENT_STATE != SDMMC_CARD_STATE_PRG)) {
+ /* return on error with error code in res or with res=0 on success */
+ goto out;
+ }
+ if (sleep_ms) {
+ /* check the status every ms */
+ _ZTIMER_SLEEP_MS(1);
+ }
+ } while (!timeout_ms || ((_ZTIMER_NOW() - t_start) < timeout_ms));
+
+ LOG_ERROR("[sdmmc] Card did not leave the programming state \n");
+ res = -ETIMEDOUT;
+
+out:
+ _ZTIMER_RELEASE();
+ return 0;
+}
+
+/**
+ * @brief Read the CID register (Internal Function)
+ *
+ * The function reads the CID register of a SD Memory Card or MMC and stores
+ * it in the card descriptor.
+ *
+ * @pre The card has to be either in the `ready` state when CMD2 is used or in
+ * the `stby` state (deselected) when CMD10 is used to read the CID.
+ *
+ * @param[in] dev SD/MMC device to be used
+ * @param[in] card Card or embedded device
+ * @param[in] cmd Command to be used
+ * (CMD2 in state `ready` or CMD10 in state `stdby')
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int _read_cid(sdmmc_dev_t *dev, uint8_t cmd)
+{
+ /* sanity checks for CID structure versions */
+ static_assert(sizeof(sdmmc_cid_sd_t) == sizeof(sdmmc_cid_mmc_t),
+ "sizeof(sdmmc_cid_sd_t) != sizeof(sdmmc_cid_mmc_t)");
+ static_assert(sizeof(sdmmc_cid_sd_t) == SDMMC_CID_REG_SIZE,
+ "sizeof(sdmmc_cid_sd_t) != SDMMC_CID_REG_SIZE");
+
+ uint32_t response[4]; /* long response requires four 32-bit words */
+ int res;
+
+ assert((cmd == SDMMC_CMD2) || (cmd == SDMMC_CMD10));
+
+ /* card must have a valid RCA in case of CMD10 */
+ assert((cmd != SDMMC_CMD10) || dev->rca);
+
+ DEBUG("[sdmmc] send CMD%d to read CID\n", cmd);
+ res = _send_cmd(dev, cmd,
+ SDMMC_CMD10 ? SDMMC_CMD_ARG_RCA(dev->rca) : SDMMC_CMD_NO_ARG,
+ SDMMC_R2, response);
+
+ /* raw data that are used to fill CID struct have to be in big-endian order */
+ uint8_t cid_raw[SDMMC_CID_REG_SIZE];
+ byteorder_htobebufl(cid_raw, response[0]);
+ byteorder_htobebufl(cid_raw + 4, response[1]);
+ byteorder_htobebufl(cid_raw + 8, response[2]);
+ byteorder_htobebufl(cid_raw + 12, response[3]);
+
+#if ENABLE_DEBUG
+ _print_raw_data_crc(cid_raw, SDMMC_CID_REG_SIZE, true);
+#endif
+
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ return res;
+ }
+
+ memcpy(&dev->cid, cid_raw, sizeof(sdmmc_cid_t));
+
+ return 0;
+}
+
+#define SDMMC_CSD_STRUCTURE(raw_data) ((raw_data)[0] >> 6)
+
+/**
+ * @brief Read the CSD register (Internal Function)
+ *
+ * The function reads the CSD of a SD Memory Card or MMC and stores it in
+ * the card descriptor because it contains information that are required
+ * when handling the card.
+ *
+ * @pre The card has to be addressable by its RCA and has to be in the `stby`
+ * state to read the CSD (deselected).
+ *
+ * @param[in] dev SD/MMC device to be used
+ * @param[in] card Card or embedded device
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int _read_csd(sdmmc_dev_t *dev)
+{
+ /* sanity checks for CSD structure versions */
+ static_assert(sizeof(sdmmc_csd_v1_t) == sizeof(sdmmc_csd_v2_t),
+ "sizeof(sdmmc_csd_v1_t) != sizeof(sdmmc_csd_v2_t)");
+ static_assert(sizeof(sdmmc_csd_v1_t) == sizeof(sdmmc_csd_mmc_t),
+ "sizeof(sdmmc_csd_v1_t) != sizeof(sdmmc_csd_mmc_t)");
+ static_assert(sizeof(sdmmc_csd_v1_t) == sizeof(sdmmc_csd_t),
+ "sizeof(sdmmc_csd_v1_t) != sizeof(sdmmc_csd_t)");
+#ifndef NDEBUG
+ /* ensure to be able to use csd.mmc.* members for all CSD versions */
+ sdmmc_csd_t csd = {};
+ csd.mmc.CSD_STRUCTURE = 0x3;
+ csd.mmc.TAAC = 0xaa;
+ csd.mmc.NSAC = 0x55;
+ csd.mmc.R2W_FACTOR = 0x5;
+ assert(csd.v1.CSD_STRUCTURE == csd.mmc.CSD_STRUCTURE);
+ assert(csd.v2.CSD_STRUCTURE == csd.mmc.CSD_STRUCTURE);
+ assert(csd.v1.TAAC == csd.mmc.TAAC);
+ assert(csd.v2.TAAC == csd.mmc.TAAC);
+ assert(csd.v1.NSAC == csd.mmc.NSAC);
+ assert(csd.v2.NSAC == csd.mmc.NSAC);
+ assert(csd.v1.R2W_FACTOR == csd.mmc.R2W_FACTOR);
+ assert(csd.v2.R2W_FACTOR == csd.mmc.R2W_FACTOR);
+#endif
+
+ /* card must have a valid RCA */
+ assert(dev->rca);
+
+ uint32_t response[4]; /* long response requires four 32-bit words */
+ int res;
+
+ DEBUG("[sdmmc] send CMD9 to read CSD\n");
+ res = _send_cmd(dev, SDMMC_CMD9, SDMMC_CMD_ARG_RCA(dev->rca),
+ SDMMC_R2, response);
+#if ENABLE_DEBUG
+ /* raw data that are used to fill CSD struct have to be big endian */
+ uint8_t csd_data[SDMMC_CSD_REG_SIZE];
+
+ byteorder_htobebufl(csd_data, response[0]);
+ byteorder_htobebufl(csd_data + 4, response[1]);
+ byteorder_htobebufl(csd_data + 8, response[2]);
+ byteorder_htobebufl(csd_data + 12, response[3]);
+
+ _print_raw_data_crc(csd_data, SDMMC_CSD_REG_SIZE, true);
+#endif
+
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ return res;
+ }
+
+ uint32_t csd_raw[SDMMC_CSD_REG_SIZE >> 2];
+
+ /* read data are word-wise in host byte order */
+ csd_raw[0] = response[3]; /* response[3] contains CSD[31:0] */
+ csd_raw[1] = response[2]; /* response[2] contains CSD[63:32] */
+ csd_raw[2] = response[1]; /* response[1] contains CSD[95:64] */
+ csd_raw[3] = response[0]; /* response[0] contains CSD[127:96] */
+
+ memcpy(&dev->csd, csd_raw, sizeof(sdmmc_csd_t));
+
+ return 0;
+}
+
+/**
+ * @brief Read the SCR register of the selected SD Memory Card (Internal Function)
+ *
+ * The function reads the SCR register of the selected SD Memory Card and stores
+ * it in the card descriptor because it contains information that are required
+ * when handling the card.
+ *
+ * The SCR register is only availably on SD Memory cards
+ *
+ * @pre The card has to be selected to read the SCR, that is, the card is in
+ * the `tran` state.
+ *
+ * @param[in] dev SD device to be used
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int _read_scr(sdmmc_dev_t *dev)
+{
+ int res = 0;
+
+ /* selected card has to be any SD Memory Card */
+ assert(dev->type & SDMMC_CARD_TYPE_SD);
+
+ sdmmc_buf_t scr_raw[SDMMC_SCR_REG_SIZE];
+
+ DEBUG("[sdmmc] send ACMD51 to read SCR\n");
+
+ res = _xfer(dev, SDMMC_ACMD51, SDMMC_CMD_NO_ARG,
+ SDMMC_SCR_REG_SIZE, 1, NULL, scr_raw, NULL);
+#if ENABLE_DEBUG
+ _print_raw_data_crc(scr_raw, SDMMC_SCR_REG_SIZE, false);
+#endif
+
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ return res;
+ }
+
+ dev->scr.value = byteorder_bebuftohl(scr_raw);
+ dev->scr.reserved0 = byteorder_bebuftohl(scr_raw + 4);
+
+ return 0;
+}
+
+static inline int _enable_clock(sdmmc_dev_t *dev)
+{
+ DEBUG("[sdmmc] %s dev=%p\n", __func__, dev);
+
+#if !IS_USED(MODULE_PERIPH_SDMMC_AUTO_CLK)
+ return (dev->driver->enable_clock) ? dev->driver->enable_clock(dev, true)
+ : 0;
+#else
+ (void)dev;
+ return 0;
+#endif
+}
+
+static inline int _disable_clock(sdmmc_dev_t *dev)
+{
+ DEBUG("[sdmmc] %s dev=%p\n", __func__, dev);
+
+#if !IS_USED(MODULE_PERIPH_SDMMC_AUTO_CLK)
+ return (dev->driver->enable_clock) ? dev->driver->enable_clock(dev, false)
+ : 0;
+#else
+ (void)dev;
+ return 0;
+#endif
+}
+
+#if IS_USED(MODULE_SDMMC_MMC)
+
+/* To avoid a 512 byte buffer on the stack, a static variable is used here.
+ * The exclusive access to this buffer must be guaranteed. */
+static sdmmc_buf_t _ext_csd_raw[SDMMC_EXT_CSD_REG_SIZE];
+
+static mutex_t _ext_csd_access = MUTEX_INIT;
+
+/**
+ * @brief Read the EXT_CSD register of the selected MMC (Internal Function)
+ *
+ * The function reads the EXT_CSD register of the selected MMC and stores
+ * it in the card descriptor because it contains information that are required
+ * when handling the card.
+ *
+ * @pre The card has to be selected to read the EXT_CSD, that is, the card is in
+ * the `tran` state.
+ *
+ * @param[in] dev MMC device to be used
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int _read_ext_csd(sdmmc_dev_t *dev)
+{
+ /* card must be a MMC */
+ assert(dev->type == SDMMC_CARD_TYPE_MMC);
+
+ DEBUG("[sdmmc] send CMD8 to read Extended CSD\n");
+
+ mutex_lock(&_ext_csd_access);
+
+ int res = _xfer(dev, SDMMC_CMD8, SDMMC_CMD_NO_ARG,
+ SDMMC_EXT_CSD_REG_SIZE, 1, NULL, _ext_csd_raw, NULL);
+#if ENABLE_DEBUG
+ _print_raw_data_crc(_ext_csd_raw, SDMMC_EXT_CSD_REG_SIZE, false);
+#endif
+
+ if (res) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ mutex_unlock(&_ext_csd_access);
+ return res;
+ }
+
+ dev->ext_csd.SEC_COUNT = byteorder_lebuftohl(_ext_csd_raw + SDMMC_EXT_CSD_SEC_COUNT);
+ dev->ext_csd.CSD_STRUCTURE = _ext_csd_raw[SDMMC_EXT_CSD_CSD_STRUCTURE];
+ dev->ext_csd.BUS_WIDTH = _ext_csd_raw[SDMMC_EXT_CSD_BUS_WIDTH];
+ dev->ext_csd.CARD_TYPE = _ext_csd_raw[SDMMC_EXT_CSD_CARD_TYPE];
+
+ mutex_unlock(&_ext_csd_access);
+ return 0;
+}
+
+/**
+ * @brief Write the EXT_CSD register of the selected MMC (Internal Function)
+ *
+ * The function writes @p value to the byte @p index of the EXT_CSD register
+ * of the selected MMC.
+ *
+ * @pre The card has to be selected to write the EXT_CSD, that is, the card
+ * is in the `tran` state.
+ *
+ * @param[in] dev MMC device to be used
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int _write_ext_csd(sdmmc_dev_t *dev, uint8_t index, uint8_t value)
+{
+ /* card must be a MMC */
+ assert(dev->type == SDMMC_CARD_TYPE_MMC);
+
+ uint32_t response;
+ int res;
+ uint32_t arg = (SDMMC_EXT_CSD_WRITE_BYTE << SDMMC_CMD6_ACCESS) |
+ (index << SDMMC_CMD6_INDEX) |
+ (value << SDMMC_CMD6_VALUE);
+
+ DEBUG("[sdmmc] send CMD6 to set EXT_CSD[%u]=%02x\n", index, value);
+
+ if ((res = _send_cmd(dev, SDMMC_CMD6, arg, SDMMC_R1B, &response))) {
+ DEBUG("[sdmmc] command failed with error %d\n", res);
+ return res;
+ }
+
+ if ((res = _wait_while_prg(dev, 1, SDMMC_INIT_TIMEOUT_MS))) {
+ /* reset the Card */
+ dev->init_done = false;
+ return res;
+ }
+
+ return 0;
+}
+#endif /* IS_USED(MODULE_SDMMC_MMC) */
+
+#if ENABLE_DEBUG
+static uint8_t _crc_7(const uint8_t *data, unsigned n)
+{
+ uint8_t crc = 0;
+
+ for (unsigned i = 0; i < n; i++) {
+ uint8_t d = data[i];
+ for (unsigned j = 0; j < 8; j++) {
+ crc <<= 1;
+ if ((d & 0x80) ^ (crc & 0x80)) {
+ crc ^= 0x09;
+ }
+ d <<= 1;
+ }
+ }
+ return crc & 0x7f;
+}
+
+static void _print_raw_data_crc(const uint8_t *data, unsigned size, bool crc)
+{
+ DEBUG("[sdmmc] raw data: ");
+ for (unsigned i = 0; i < size; i++) {
+ if ((size > 16) && ((i % 16) == 0)) {
+ DEBUG("\n%04x: ", i);
+ }
+ DEBUG("0x%02x ", data[i]);
+ }
+ DEBUG("\n");
+ if (crc) {
+ /* print CRC if data include the CRC in last byte */
+ DEBUG("[sdmmc] CRC: 0x%02x (received 0x%02x)\n",
+ _crc_7(data, size - 1), data[size - 1] >> 1);
+ }
+}
+#endif
diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features
index 44c586f059..cace50b5c5 100644
--- a/kconfigs/Kconfig.features
+++ b/kconfigs/Kconfig.features
@@ -410,6 +410,55 @@ config HAS_PERIPH_RTT_OVERFLOW
help
Indicates that the RTT provides an overflow callback.
+config HAS_PERIPH_SDMMC
+ bool
+ help
+ Indicates that an SDIO/SD/MMC peripheral is present and used by the
+ board. This feature shall be provided by the board configuration,
+ if available.
+
+config HAS_PERIPH_SDMMC_8BIT
+ bool
+ help
+ Indicates that the SDIO/SD/MMC peripheral supports the 8-bit bus width
+ and at least one component of the board is connected with 8 data lines.
+ This feature shall be provided by the board configuration, if available.
+
+config HAS_PERIPH_SDMMC_AUTO_CLK
+ bool
+ help
+ Indicates that the SDIO/SD/MMC peripheral supports the Auto-CLK
+ feature, i.e. the automatic activation and deactivation of the SD CLK
+ signal when required. This feature shall be provided by the MCU
+ if supported.
+
+config HAS_PERIPH_SDMMC_AUTO_CMD12
+ bool
+ help
+ Indicates that the SDIO/SD/MMC peripheral supports the Auto-CMD12
+ feature, i.e. CMD12 is sent automatically to stop the transmission in
+ multiple block operations. This feature shall be provided by the MCU
+ if supported.
+
+config HAS_PERIPH_SDMMC_CLK
+ bool
+ help
+ Indicates that the SDIO/SD/MMC peripheral has special clock
+ functionality used by the peripheral driver.
+
+config HAS_PERIPH_SDMMC_HS
+ bool
+ help
+ Indicates that the SDIO/SD/MMC peripheral supports the high speed
+ access, that is 50 MHz for SD and 52 MHz for MMC. This feature shall be
+ provided by the MCU.
+
+config HAS_PERIPH_SDMMC_MMC
+ bool
+ help
+ Indicates that the SDIO/SD/MMC peripheral supports MMC/eMMCs. This
+ feature shall be provided by the MCU.
+
config HAS_PERIPH_SPI
bool
help