From 556ef5235c412da59cdbc6d01eefb2ec8ecc170f Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 8 Jun 2023 09:51:28 +0200 Subject: [PATCH] drivers: add periph_sdmmc support --- drivers/Kconfig | 1 + drivers/include/sdmmc/sdmmc.h | 1909 +++++++++++++++++++++++++++ drivers/mtd/Kconfig | 40 +- drivers/periph_common/Kconfig | 1 + drivers/periph_common/Kconfig.sdmmc | 62 + drivers/periph_common/init.c | 9 + drivers/sdmmc/Kconfig | 22 + drivers/sdmmc/Makefile | 3 + drivers/sdmmc/Makefile.dep | 13 + drivers/sdmmc/Makefile.include | 1 + drivers/sdmmc/sdmmc.c | 1612 ++++++++++++++++++++++ kconfigs/Kconfig.features | 49 + 12 files changed, 3715 insertions(+), 7 deletions(-) create mode 100644 drivers/include/sdmmc/sdmmc.h create mode 100644 drivers/periph_common/Kconfig.sdmmc create mode 100644 drivers/sdmmc/Kconfig create mode 100644 drivers/sdmmc/Makefile create mode 100644 drivers/sdmmc/Makefile.dep create mode 100644 drivers/sdmmc/Makefile.include create mode 100644 drivers/sdmmc/sdmmc.c 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