aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/sdmmc
diff options
context:
space:
mode:
authorRaghuram Subramani <raghus2247@gmail.com>2022-06-19 19:47:51 +0530
committerRaghuram Subramani <raghus2247@gmail.com>2022-06-19 19:47:51 +0530
commit4fd287655a72b9aea14cdac715ad5b90ed082ed2 (patch)
tree65d393bc0e699dd12d05b29ba568e04cea666207 /circuitpython/lib/sdmmc
parent0150f70ce9c39e9e6dd878766c0620c85e47bed0 (diff)
add circuitpython code
Diffstat (limited to 'circuitpython/lib/sdmmc')
-rw-r--r--circuitpython/lib/sdmmc/CMakeLists.txt9
-rw-r--r--circuitpython/lib/sdmmc/component.mk0
-rw-r--r--circuitpython/lib/sdmmc/include/sdmmc_cmd.h274
-rw-r--r--circuitpython/lib/sdmmc/include/sdmmc_defs.h498
-rw-r--r--circuitpython/lib/sdmmc/include/sdmmc_types.h199
-rw-r--r--circuitpython/lib/sdmmc/sdmmc_cmd.c501
-rw-r--r--circuitpython/lib/sdmmc/sdmmc_common.c314
-rw-r--r--circuitpython/lib/sdmmc/sdmmc_common.h132
-rw-r--r--circuitpython/lib/sdmmc/sdmmc_init.c123
-rw-r--r--circuitpython/lib/sdmmc/sdmmc_io.c639
-rw-r--r--circuitpython/lib/sdmmc/sdmmc_mmc.c251
-rw-r--r--circuitpython/lib/sdmmc/sdmmc_sd.c343
-rw-r--r--circuitpython/lib/sdmmc/test/CMakeLists.txt4
-rw-r--r--circuitpython/lib/sdmmc/test/component.mk1
-rw-r--r--circuitpython/lib/sdmmc/test/test_sd.c584
-rw-r--r--circuitpython/lib/sdmmc/test/test_sdio.c388
16 files changed, 4260 insertions, 0 deletions
diff --git a/circuitpython/lib/sdmmc/CMakeLists.txt b/circuitpython/lib/sdmmc/CMakeLists.txt
new file mode 100644
index 0000000..f6e8fe2
--- /dev/null
+++ b/circuitpython/lib/sdmmc/CMakeLists.txt
@@ -0,0 +1,9 @@
+idf_component_register(SRCS "sdmmc_cmd.c"
+ "sdmmc_common.c"
+ "sdmmc_init.c"
+ "sdmmc_io.c"
+ "sdmmc_mmc.c"
+ "sdmmc_sd.c"
+ INCLUDE_DIRS include
+ REQUIRES driver
+ PRIV_REQUIRES soc)
diff --git a/circuitpython/lib/sdmmc/component.mk b/circuitpython/lib/sdmmc/component.mk
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/lib/sdmmc/component.mk
diff --git a/circuitpython/lib/sdmmc/include/sdmmc_cmd.h b/circuitpython/lib/sdmmc/include/sdmmc_cmd.h
new file mode 100644
index 0000000..7952ada
--- /dev/null
+++ b/circuitpython/lib/sdmmc/include/sdmmc_cmd.h
@@ -0,0 +1,274 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdio.h>
+#include "sdmmc_defs.h"
+#include "sdmmc_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Probe and initialize SD/MMC card using given host
+ *
+ * @note Only SD cards (SDSC and SDHC/SDXC) are supported now.
+ * Support for MMC/eMMC cards will be added later.
+ *
+ * @param host pointer to structure defining host controller
+ * @param out_card pointer to structure which will receive information
+ * about the card when the function completes
+ * @return
+ * - SDMMC_OK on success
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_card_init(const sdmmc_host_t* host,
+ sdmmc_card_t* out_card);
+
+/**
+ * @brief Print information about the card to a stream
+ * @param stream stream obtained using fopen or fdopen
+ * @param card card information structure initialized using sdmmc_card_init
+ */
+void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card);
+
+/**
+ * Write given number of sectors to SD/MMC card
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param src pointer to data buffer to read data from; data size must be
+ * equal to sector_count * card->csd.sector_size
+ * @param start_sector sector where to start writing
+ * @param sector_count number of sectors to write
+ * @return
+ * - SDMMC_OK on success
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
+ size_t start_sector, size_t sector_count);
+
+/**
+ * Read given number of sectors from the SD/MMC card
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param dst pointer to data buffer to write into; buffer size must be
+ * at least sector_count * card->csd.sector_size
+ * @param start_sector sector where to start reading
+ * @param sector_count number of sectors to read
+ * @return
+ * - SDMMC_OK on success
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
+ size_t start_sector, size_t sector_count);
+
+/**
+ * Read one byte from an SDIO card using IO_RW_DIRECT (CMD52)
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param function IO function number
+ * @param reg byte address within IO function
+ * @param[out] out_byte output, receives the value read from the card
+ * @return
+ * - SDMMC_OK on success
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
+ uint32_t reg, uint8_t *out_byte);
+
+/**
+ * Write one byte to an SDIO card using IO_RW_DIRECT (CMD52)
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param function IO function number
+ * @param reg byte address within IO function
+ * @param in_byte value to be written
+ * @param[out] out_byte if not NULL, receives new byte value read
+ * from the card (read-after-write).
+ * @return
+ * - SDMMC_OK on success
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
+ uint32_t reg, uint8_t in_byte, uint8_t* out_byte);
+
+/**
+ * Read multiple bytes from an SDIO card using IO_RW_EXTENDED (CMD53)
+ *
+ * This function performs read operation using CMD53 in byte mode.
+ * For block mode, see sdmmc_io_read_blocks.
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param function IO function number
+ * @param addr byte address within IO function where reading starts
+ * @param dst buffer which receives the data read from card
+ * @param size number of bytes to read
+ * @return
+ * - SDMMC_OK on success
+ * - SDMMC_ERR_INVALID_SIZE if size exceeds 512 bytes
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, void* dst, size_t size);
+
+/**
+ * Write multiple bytes to an SDIO card using IO_RW_EXTENDED (CMD53)
+ *
+ * This function performs write operation using CMD53 in byte mode.
+ * For block mode, see sdmmc_io_write_blocks.
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param function IO function number
+ * @param addr byte address within IO function where writing starts
+ * @param src data to be written
+ * @param size number of bytes to write
+ * @return
+ * - SDMMC_OK on success
+ * - SDMMC_ERR_INVALID_SIZE if size exceeds 512 bytes
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, const void* src, size_t size);
+
+/**
+ * Read blocks of data from an SDIO card using IO_RW_EXTENDED (CMD53)
+ *
+ * This function performs read operation using CMD53 in block mode.
+ * For byte mode, see sdmmc_io_read_bytes.
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param function IO function number
+ * @param addr byte address within IO function where writing starts
+ * @param dst buffer which receives the data read from card
+ * @param size number of bytes to read, must be divisible by the card block
+ * size.
+ * @return
+ * - SDMMC_OK on success
+ * - SDMMC_ERR_INVALID_SIZE if size is not divisible by 512 bytes
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, void* dst, size_t size);
+
+/**
+ * Write blocks of data to an SDIO card using IO_RW_EXTENDED (CMD53)
+ *
+ * This function performs write operation using CMD53 in block mode.
+ * For byte mode, see sdmmc_io_write_bytes.
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param function IO function number
+ * @param addr byte address within IO function where writing starts
+ * @param src data to be written
+ * @param size number of bytes to read, must be divisible by the card block
+ * size.
+ * @return
+ * - SDMMC_OK on success
+ * - SDMMC_ERR_INVALID_SIZE if size is not divisible by 512 bytes
+ * - One of the error codes from SDMMC host controller
+ */
+sdmmc_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, const void* src, size_t size);
+
+/**
+ * Enable SDIO interrupt in the SDMMC host
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @return
+ * - SDMMC_OK on success
+ * - SDMMC_ERR_NOT_SUPPORTED if the host controller does not support
+ * IO interrupts
+ */
+sdmmc_err_t sdmmc_io_enable_int(sdmmc_card_t* card);
+
+/**
+ * Block until an SDIO interrupt is received
+ *
+ * Slave uses D1 line to signal interrupt condition to the host.
+ * This function can be used to wait for the interrupt.
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param timeout_ticks time to wait for the interrupt, in RTOS ticks
+ * @return
+ * - SDMMC_OK if the interrupt is received
+ * - SDMMC_ERR_NOT_SUPPORTED if the host controller does not support
+ * IO interrupts
+ * - SDMMC_ERR_TIMEOUT if the interrupt does not happen in timeout_ticks
+ */
+sdmmc_err_t sdmmc_io_wait_int(sdmmc_card_t* card, int timeout_ms);
+
+/**
+ * Get the data of CIS region of a SDIO card.
+ *
+ * You may provide a buffer not sufficient to store all the CIS data. In this
+ * case, this functions store as much data into your buffer as possible. Also,
+ * this function will try to get and return the size required for you.
+ *
+ * @param card pointer to card information structure previously initialized
+ * using sdmmc_card_init
+ * @param out_buffer Output buffer of the CIS data
+ * @param buffer_size Size of the buffer.
+ * @param inout_cis_size Mandatory, pointer to a size, input and output.
+ * - input: Limitation of maximum searching range, should be 0 or larger than
+ * buffer_size. The function searches for CIS_CODE_END until this range. Set to
+ * 0 to search infinitely.
+ * - output: The size required to store all the CIS data, if CIS_CODE_END is found.
+ *
+ * @return
+ * - SDMMC_OK: on success
+ * - SDMMC_ERR_INVALID_RESPONSE: if the card does not (correctly) support CIS.
+ * - SDMMC_ERR_INVALID_SIZE: CIS_CODE_END found, but buffer_size is less than
+ * required size, which is stored in the inout_cis_size then.
+ * - SDMMC_ERR_NOT_FOUND: if the CIS_CODE_END not found. Increase input value of
+ * inout_cis_size or set it to 0, if you still want to search for the end;
+ * output value of inout_cis_size is invalid in this case.
+ * - and other error code return from sdmmc_io_read_bytes
+ */
+sdmmc_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size);
+
+/**
+ * Parse and print the CIS information of a SDIO card.
+ *
+ * @note Not all the CIS codes and all kinds of tuples are supported. If you
+ * see some unresolved code, you can add the parsing of these code in
+ * sdmmc_io.c and contribute to the IDF through the Github repository.
+ *
+ * using sdmmc_card_init
+ * @param buffer Buffer to parse
+ * @param buffer_size Size of the buffer.
+ * @param fp File pointer to print to, set to NULL to print to stdout.
+ *
+ * @return
+ * - SDMMC_OK: on success
+ * - SDMMC_ERR_NOT_SUPPORTED: if the value from the card is not supported to be parsed.
+ * - SDMMC_ERR_INVALID_SIZE: if the CIS size fields are not correct.
+ */
+sdmmc_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/circuitpython/lib/sdmmc/include/sdmmc_defs.h b/circuitpython/lib/sdmmc/include/sdmmc_defs.h
new file mode 100644
index 0000000..7ac6ae1
--- /dev/null
+++ b/circuitpython/lib/sdmmc/include/sdmmc_defs.h
@@ -0,0 +1,498 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDMMC_DEFS_H_
+#define _SDMMC_DEFS_H_
+
+#include <stdint.h>
+#include <limits.h>
+
+/* MMC commands */ /* response type */
+#define MMC_GO_IDLE_STATE 0 /* R0 */
+#define MMC_SEND_OP_COND 1 /* R3 */
+#define MMC_ALL_SEND_CID 2 /* R2 */
+#define MMC_SET_RELATIVE_ADDR 3 /* R1 */
+#define MMC_SWITCH 6 /* R1B */
+#define MMC_SELECT_CARD 7 /* R1 */
+#define MMC_SEND_EXT_CSD 8 /* R1 */
+#define MMC_SEND_CSD 9 /* R2 */
+#define MMC_SEND_CID 10 /* R1 */
+#define MMC_READ_DAT_UNTIL_STOP 11 /* R1 */
+#define MMC_STOP_TRANSMISSION 12 /* R1B */
+#define MMC_SEND_STATUS 13 /* R1 */
+#define MMC_SET_BLOCKLEN 16 /* R1 */
+#define MMC_READ_BLOCK_SINGLE 17 /* R1 */
+#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */
+#define MMC_WRITE_DAT_UNTIL_STOP 20 /* R1 */
+#define MMC_SET_BLOCK_COUNT 23 /* R1 */
+#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */
+#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */
+#define MMC_APP_CMD 55 /* R1 */
+
+/* SD commands */ /* response type */
+#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
+#define SD_SEND_SWITCH_FUNC 6 /* R1 */
+#define SD_SEND_IF_COND 8 /* R7 */
+#define SD_READ_OCR 58 /* R3 */
+#define SD_CRC_ON_OFF 59 /* R1 */
+
+/* SD application commands */ /* response type */
+#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
+#define SD_APP_SD_STATUS 13 /* R2 */
+#define SD_APP_OP_COND 41 /* R3 */
+#define SD_APP_SEND_SCR 51 /* R1 */
+
+/* SD IO commands */
+#define SD_IO_SEND_OP_COND 5 /* R4 */
+#define SD_IO_RW_DIRECT 52 /* R5 */
+#define SD_IO_RW_EXTENDED 53 /* R5 */
+
+
+/* OCR bits */
+#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
+#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */
+#define MMC_OCR_SECTOR_MODE (1<<30)
+#define MMC_OCR_BYTE_MODE (1<<29)
+#define MMC_OCR_3_5V_3_6V (1<<23)
+#define MMC_OCR_3_4V_3_5V (1<<22)
+#define MMC_OCR_3_3V_3_4V (1<<21)
+#define MMC_OCR_3_2V_3_3V (1<<20)
+#define MMC_OCR_3_1V_3_2V (1<<19)
+#define MMC_OCR_3_0V_3_1V (1<<18)
+#define MMC_OCR_2_9V_3_0V (1<<17)
+#define MMC_OCR_2_8V_2_9V (1<<16)
+#define MMC_OCR_2_7V_2_8V (1<<15)
+#define MMC_OCR_2_6V_2_7V (1<<14)
+#define MMC_OCR_2_5V_2_6V (1<<13)
+#define MMC_OCR_2_4V_2_5V (1<<12)
+#define MMC_OCR_2_3V_2_4V (1<<11)
+#define MMC_OCR_2_2V_2_3V (1<<10)
+#define MMC_OCR_2_1V_2_2V (1<<9)
+#define MMC_OCR_2_0V_2_1V (1<<8)
+#define MMC_OCR_1_65V_1_95V (1<<7)
+
+#define SD_OCR_SDHC_CAP (1<<30)
+#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */
+
+/* SD mode R1 response type bits */
+#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
+#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
+#define MMC_R1_SWITCH_ERROR (1<<7) /* switch command did not succeed */
+
+/* SPI mode R1 response type bits */
+#define SD_SPI_R1_IDLE_STATE (1<<0)
+#define SD_SPI_R1_ERASE_RST (1<<1)
+#define SD_SPI_R1_ILLEGAL_CMD (1<<2)
+#define SD_SPI_R1_CMD_CRC_ERR (1<<3)
+#define SD_SPI_R1_ERASE_SEQ_ERR (1<<4)
+#define SD_SPI_R1_ADDR_ERR (1<<5)
+#define SD_SPI_R1_PARAM_ERR (1<<6)
+#define SD_SPI_R1_NO_RESPONSE (1<<7)
+
+#define SDIO_R1_FUNC_NUM_ERR (1<<4)
+
+/* 48-bit response decoding (32 bits w/o CRC) */
+#define MMC_R1(resp) ((resp)[0])
+#define MMC_R3(resp) ((resp)[0])
+#define MMC_R4(resp) ((resp)[0])
+#define MMC_R5(resp) ((resp)[0])
+#define SD_R6(resp) ((resp)[0])
+#define MMC_R1_CURRENT_STATE(resp) (((resp)[0] >> 9) & 0xf)
+
+/* SPI mode response decoding */
+#define SD_SPI_R1(resp) ((resp)[0] & 0xff)
+#define SD_SPI_R2(resp) ((resp)[0] & 0xffff)
+#define SD_SPI_R3(resp) ((resp)[0])
+#define SD_SPI_R7(resp) ((resp)[0])
+
+/* SPI mode data response decoding */
+#define SD_SPI_DATA_RSP_VALID(resp_byte) (((resp_byte)&0x11)==0x1)
+#define SD_SPI_DATA_RSP(resp_byte) (((resp_byte)>>1)&0x7)
+#define SD_SPI_DATA_ACCEPTED 0x2
+#define SD_SPI_DATA_CRC_ERROR 0x5
+#define SD_SPI_DATA_WR_ERROR 0x6
+
+/* RCA argument and response */
+#define MMC_ARG_RCA(rca) ((rca) << 16)
+#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16)
+
+/* bus width argument */
+#define SD_ARG_BUS_WIDTH_1 0
+#define SD_ARG_BUS_WIDTH_4 2
+
+/* EXT_CSD fields */
+#define EXT_CSD_BUS_WIDTH 183 /* WO */
+#define EXT_CSD_HS_TIMING 185 /* R/W */
+#define EXT_CSD_REV 192 /* RO */
+#define EXT_CSD_STRUCTURE 194 /* RO */
+#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_SEC_COUNT 212 /* RO */
+#define EXT_CSD_PWR_CL_26_360 203 /* RO */
+#define EXT_CSD_PWR_CL_52_360 202 /* RO */
+#define EXT_CSD_PWR_CL_26_195 201 /* RO */
+#define EXT_CSD_PWR_CL_52_195 200 /* RO */
+#define EXT_CSD_POWER_CLASS 187 /* R/W */
+#define EXT_CSD_CMD_SET 191 /* R/W */
+#define EXT_CSD_S_CMD_SET 504 /* RO */
+
+/* EXT_CSD field definitions */
+#define EXT_CSD_CMD_SET_NORMAL (1U << 0)
+#define EXT_CSD_CMD_SET_SECURE (1U << 1)
+#define EXT_CSD_CMD_SET_CPSECURE (1U << 2)
+
+/* EXT_CSD_HS_TIMING */
+#define EXT_CSD_HS_TIMING_BC 0
+#define EXT_CSD_HS_TIMING_HS 1
+#define EXT_CSD_HS_TIMING_HS200 2
+#define EXT_CSD_HS_TIMING_HS400 3
+
+/* EXT_CSD_BUS_WIDTH */
+#define EXT_CSD_BUS_WIDTH_1 0
+#define EXT_CSD_BUS_WIDTH_4 1
+#define EXT_CSD_BUS_WIDTH_8 2
+#define EXT_CSD_BUS_WIDTH_4_DDR 5
+#define EXT_CSD_BUS_WIDTH_8_DDR 6
+
+/* EXT_CSD_CARD_TYPE */
+/* The only currently valid values for this field are 0x01, 0x03, 0x07,
+ * 0x0B and 0x0F. */
+#define EXT_CSD_CARD_TYPE_F_26M (1 << 0) /* SDR at "rated voltages */
+#define EXT_CSD_CARD_TYPE_F_52M (1 << 1) /* SDR at "rated voltages */
+#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2) /* DDR, 1.8V or 3.3V I/O */
+#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3) /* DDR, 1.2V I/O */
+#define EXT_CSD_CARD_TYPE_26M 0x01
+#define EXT_CSD_CARD_TYPE_52M 0x03
+#define EXT_CSD_CARD_TYPE_52M_V18 0x07
+#define EXT_CSD_CARD_TYPE_52M_V12 0x0b
+#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f
+
+/* EXT_CSD MMC */
+#define EXT_CSD_MMC_SIZE 512
+
+/* MMC_SWITCH access mode */
+#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
+#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */
+#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits in value */
+#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
+
+/* MMC R2 response (CSD) */
+#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
+#define MMC_CSD_CSDVER_1_0 1
+#define MMC_CSD_CSDVER_2_0 2
+#define MMC_CSD_CSDVER_EXT_CSD 3
+#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4)
+#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */
+#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */
+#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */
+#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */
+#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */
+#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
+#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
+#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \
+ (MMC_CSD_C_SIZE_MULT((resp))+2))
+#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
+
+/* MMC v1 R2 response (CID) */
+#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24)
+#define MMC_CID_PNM_V1_CPY(resp, pnm) \
+ do { \
+ (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
+ (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
+ (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
+ (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
+ (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
+ (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
+ (pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \
+ (pnm)[7] = '\0'; \
+ } while (0)
+#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8)
+#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24)
+#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8)
+
+/* MMC v2 R2 response (CID) */
+#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8)
+#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16)
+#define MMC_CID_PNM_V2_CPY(resp, pnm) \
+ do { \
+ (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
+ (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
+ (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
+ (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
+ (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
+ (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
+ (pnm)[6] = '\0'; \
+ } while (0)
+#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32)
+
+/* SD R2 response (CSD) */
+#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
+#define SD_CSD_CSDVER_1_0 0
+#define SD_CSD_CSDVER_2_0 1
+#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8)
+#define SD_CSD_TAAC_1_5_MSEC 0x26
+#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8)
+#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8)
+#define SD_CSD_SPEED_25_MHZ 0x32
+#define SD_CSD_SPEED_50_MHZ 0x5a
+#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12)
+#define SD_CSD_CCC_BASIC (1 << 0) /* basic */
+#define SD_CSD_CCC_BR (1 << 2) /* block read */
+#define SD_CSD_CCC_BW (1 << 4) /* block write */
+#define SD_CSD_CCC_ERASE (1 << 5) /* erase */
+#define SD_CSD_CCC_WP (1 << 6) /* write protection */
+#define SD_CSD_CCC_LC (1 << 7) /* lock card */
+#define SD_CSD_CCC_AS (1 << 8) /*application specific*/
+#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */
+#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */
+#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
+#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1)
+#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1)
+#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1)
+#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1)
+#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
+#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \
+ (SD_CSD_C_SIZE_MULT((resp))+2))
+#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22)
+#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10)
+#define SD_CSD_V2_BL_LEN 0x9 /* 512 */
+#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3)
+#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3)
+#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3)
+#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3)
+#define SD_CSD_VDD_RW_CURR_100mA 0x7
+#define SD_CSD_VDD_RW_CURR_80mA 0x6
+#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
+#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1)
+#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */
+#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */
+#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1)
+#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3)
+#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4)
+#define SD_CSD_RW_BL_LEN_2G 0xa
+#define SD_CSD_RW_BL_LEN_1G 0x9
+#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1)
+#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1)
+#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1)
+#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1)
+#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1)
+#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2)
+
+/* SD R2 response (CID) */
+#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8)
+#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16)
+#define SD_CID_PNM_CPY(resp, pnm) \
+ do { \
+ (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
+ (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
+ (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
+ (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
+ (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
+ (pnm)[5] = '\0'; \
+ } while (0)
+#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8)
+#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32)
+#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12)
+
+/* SCR (SD Configuration Register) */
+#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4)
+#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */
+#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4)
+#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 and 1.01 */
+#define SCR_SD_SPEC_VER_1_10 1 /* Version 1.10 */
+#define SCR_SD_SPEC_VER_2 2 /* Version 2.00 or Version 3.0X */
+#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1)
+#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3)
+#define SCR_SD_SECURITY_NONE 0 /* no security */
+#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */
+#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */
+#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4)
+#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */
+#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */
+#define SCR_SD_SPEC3(scr) MMC_RSP_BITS((scr), 47, 1)
+#define SCR_EX_SECURITY(scr) MMC_RSP_BITS((scr), 43, 4)
+#define SCR_SD_SPEC4(scr) MMC_RSP_BITS((scr), 42, 1)
+#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 34, 8)
+#define SCR_CMD_SUPPORT_CMD23(scr) MMC_RSP_BITS((scr), 33, 1)
+#define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1)
+#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32)
+
+/* Max supply current in SWITCH_FUNC response (in mA) */
+#define SD_SFUNC_I_MAX(status) (MMC_RSP_BITS((uint32_t *)(status), 496, 16))
+
+/* Supported flags in SWITCH_FUNC response */
+#define SD_SFUNC_SUPPORTED(status, group) \
+ (MMC_RSP_BITS((uint32_t *)(status), 400 + (group - 1) * 16, 16))
+
+/* Selected function in SWITCH_FUNC response */
+#define SD_SFUNC_SELECTED(status, group) \
+ (MMC_RSP_BITS((uint32_t *)(status), 376 + (group - 1) * 4, 4))
+
+/* Busy flags in SWITCH_FUNC response */
+#define SD_SFUNC_BUSY(status, group) \
+ (MMC_RSP_BITS((uint32_t *)(status), 272 + (group - 1) * 16, 16))
+
+/* Version of SWITCH_FUNC response */
+#define SD_SFUNC_VER(status) (MMC_RSP_BITS((uint32_t *)(status), 368, 8))
+
+#define SD_SFUNC_GROUP_MAX 6
+#define SD_SFUNC_FUNC_MAX 15
+
+#define SD_ACCESS_MODE 1 /* Function group 1, Access Mode */
+
+#define SD_ACCESS_MODE_SDR12 0 /* 25 MHz clock */
+#define SD_ACCESS_MODE_SDR25 1 /* 50 MHz clock */
+#define SD_ACCESS_MODE_SDR50 2 /* UHS-I, 100 MHz clock */
+#define SD_ACCESS_MODE_SDR104 3 /* UHS-I, 208 MHz clock */
+#define SD_ACCESS_MODE_DDR50 4 /* UHS-I, 50 MHz clock, DDR */
+
+/**
+ * @brief Extract up to 32 sequential bits from an array of 32-bit words
+ *
+ * Bits within the word are numbered in the increasing order from LSB to MSB.
+ *
+ * As an example, consider 2 32-bit words:
+ *
+ * 0x01234567 0x89abcdef
+ *
+ * On a little-endian system, the bytes are stored in memory as follows:
+ *
+ * 67 45 23 01 ef cd ab 89
+ *
+ * MMC_RSP_BITS will extact bits as follows:
+ *
+ * start=0 len=4 -> result=0x00000007
+ * start=0 len=12 -> result=0x00000567
+ * start=28 len=8 -> result=0x000000f0
+ * start=59 len=5 -> result=0x00000011
+ *
+ * @param src array of words to extract bits from
+ * @param start index of the first bit to extract
+ * @param len number of bits to extract, 1 to 32
+ * @return 32-bit word where requested bits start from LSB
+ */
+static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len)
+{
+ uint32_t mask = (len % 32 == 0) ? UINT_MAX : UINT_MAX >> (32 - (len % 32));
+ size_t word = start / 32;
+ size_t shift = start % 32;
+ uint32_t right = src[word] >> shift;
+ uint32_t left = (len + shift <= 32) ? 0 : src[word + 1] << ((32 - shift) % 32);
+ return (left | right) & mask;
+}
+
+/* SD R4 response (IO OCR) */
+#define SD_IO_OCR_MEM_READY (1<<31)
+#define SD_IO_OCR_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x7)
+#define SD_IO_OCR_MEM_PRESENT (1<<27)
+#define SD_IO_OCR_MASK 0x00fffff0
+
+/* CMD52 arguments */
+#define SD_ARG_CMD52_READ (0<<31)
+#define SD_ARG_CMD52_WRITE (1<<31)
+#define SD_ARG_CMD52_FUNC_SHIFT 28
+#define SD_ARG_CMD52_FUNC_MASK 0x7
+#define SD_ARG_CMD52_EXCHANGE (1<<27)
+#define SD_ARG_CMD52_REG_SHIFT 9
+#define SD_ARG_CMD52_REG_MASK 0x1ffff
+#define SD_ARG_CMD52_DATA_SHIFT 0
+#define SD_ARG_CMD52_DATA_MASK 0xff
+#define SD_R5_DATA(resp) ((resp)[0] & 0xff)
+
+/* CMD53 arguments */
+#define SD_ARG_CMD53_READ (0<<31)
+#define SD_ARG_CMD53_WRITE (1<<31)
+#define SD_ARG_CMD53_FUNC_SHIFT 28
+#define SD_ARG_CMD53_FUNC_MASK 0x7
+#define SD_ARG_CMD53_BLOCK_MODE (1<<27)
+#define SD_ARG_CMD53_INCREMENT (1<<26)
+#define SD_ARG_CMD53_REG_SHIFT 9
+#define SD_ARG_CMD53_REG_MASK 0x1ffff
+#define SD_ARG_CMD53_LENGTH_SHIFT 0
+#define SD_ARG_CMD53_LENGTH_MASK 0x1ff
+#define SD_ARG_CMD53_LENGTH_MAX 512
+
+/* Card Common Control Registers (CCCR) */
+#define SD_IO_CCCR_START 0x00000
+#define SD_IO_CCCR_SIZE 0x100
+#define SD_IO_CCCR_FN_ENABLE 0x02
+#define SD_IO_CCCR_FN_READY 0x03
+#define SD_IO_CCCR_INT_ENABLE 0x04
+#define SD_IO_CCCR_INT_PENDING 0x05
+#define SD_IO_CCCR_CTL 0x06
+#define CCCR_CTL_RES (1<<3)
+#define SD_IO_CCCR_BUS_WIDTH 0x07
+#define CCCR_BUS_WIDTH_1 (0<<0)
+#define CCCR_BUS_WIDTH_4 (2<<0)
+#define CCCR_BUS_WIDTH_8 (3<<0)
+#define CCCR_BUS_WIDTH_ECSI (1<<5)
+#define SD_IO_CCCR_CARD_CAP 0x08
+#define CCCR_CARD_CAP_LSC BIT(6)
+#define CCCR_CARD_CAP_4BLS BIT(7)
+#define SD_IO_CCCR_CISPTR 0x09
+#define SD_IO_CCCR_BLKSIZEL 0x10
+#define SD_IO_CCCR_BLKSIZEH 0x11
+#define SD_IO_CCCR_HIGHSPEED 0x13
+#define CCCR_HIGHSPEED_SUPPORT BIT(0)
+#define CCCR_HIGHSPEED_ENABLE BIT(1)
+
+/* Function Basic Registers (FBR) */
+#define SD_IO_FBR_START 0x00100
+#define SD_IO_FBR_SIZE 0x00700
+
+/* Card Information Structure (CIS) */
+#define SD_IO_CIS_START 0x01000
+#define SD_IO_CIS_SIZE 0x17000
+
+/* CIS tuple codes (based on PC Card 16) */
+#define CISTPL_CODE_NULL 0x00
+#define CISTPL_CODE_DEVICE 0x01
+#define CISTPL_CODE_CHKSUM 0x10
+#define CISTPL_CODE_VERS1 0x15
+#define CISTPL_CODE_ALTSTR 0x16
+#define CISTPL_CODE_CONFIG 0x1A
+#define CISTPL_CODE_CFTABLE_ENTRY 0x1B
+#define CISTPL_CODE_MANFID 0x20
+#define CISTPL_CODE_FUNCID 0x21
+#define TPLFID_FUNCTION_SDIO 0x0c
+#define CISTPL_CODE_FUNCE 0x22
+#define CISTPL_CODE_VENDER_BEGIN 0x80
+#define CISTPL_CODE_VENDER_END 0x8F
+#define CISTPL_CODE_SDIO_STD 0x91
+#define CISTPL_CODE_SDIO_EXT 0x92
+#define CISTPL_CODE_END 0xFF
+
+
+/* Timing */
+#define SDMMC_TIMING_LEGACY 0
+#define SDMMC_TIMING_HIGHSPEED 1
+#define SDMMC_TIMING_MMC_DDR52 2
+
+#include "py/runtime.h"
+
+// Logging macros
+// #define ESP_LOGD(tag, string, ...) mp_printf(&mp_plat_print, string "\n" __VA_OPT__(,) __VA_ARGS__)
+// #define ESP_LOGV(tag, string, ...) mp_printf(&mp_plat_print, string "\n" __VA_OPT__(,) __VA_ARGS__)
+// #define ESP_LOGW(tag, string, ...) mp_printf(&mp_plat_print, string "\n" __VA_OPT__(,) __VA_ARGS__)
+// #define ESP_LOGE(tag, string, ...) mp_printf(&mp_plat_print, string "\n" __VA_OPT__(,) __VA_ARGS__)
+
+#define ESP_LOGD(tag, string, ...)
+#define ESP_LOGV(tag, string, ...)
+#define ESP_LOGW(tag, string, ...)
+#define ESP_LOGE(tag, string, ...)
+#define TSD_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) )
+
+#endif //_SDMMC_DEFS_H_
diff --git a/circuitpython/lib/sdmmc/include/sdmmc_types.h b/circuitpython/lib/sdmmc/include/sdmmc_types.h
new file mode 100644
index 0000000..79a15e2
--- /dev/null
+++ b/circuitpython/lib/sdmmc/include/sdmmc_types.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDMMC_TYPES_H_
+#define _SDMMC_TYPES_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * Decoded values from SD card Card Specific Data register
+ */
+typedef struct {
+ int csd_ver; /*!< CSD structure format */
+ int mmc_ver; /*!< MMC version (for CID format) */
+ int capacity; /*!< total number of sectors */
+ int sector_size; /*!< sector size in bytes */
+ int read_block_len; /*!< block length for reads */
+ int card_command_class; /*!< Card Command Class for SD */
+ int tr_speed; /*!< Max transfer speed */
+} sdmmc_csd_t;
+
+/**
+ * Decoded values from SD card Card IDentification register
+ */
+typedef struct {
+ int mfg_id; /*!< manufacturer identification number */
+ int oem_id; /*!< OEM/product identification number */
+ char name[8]; /*!< product name (MMC v1 has the longest) */
+ int revision; /*!< product revision */
+ int serial; /*!< product serial number */
+ int date; /*!< manufacturing date */
+} sdmmc_cid_t;
+
+/**
+ * Decoded values from SD Configuration Register
+ */
+typedef struct {
+ int sd_spec; /*!< SD Physical layer specification version, reported by card */
+ int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */
+} sdmmc_scr_t;
+
+/**
+ * Decoded values of Extended Card Specific Data
+ */
+typedef struct {
+ uint8_t power_class; /*!< Power class used by the card */
+} sdmmc_ext_csd_t;
+
+/**
+ * SD/MMC command response buffer
+ */
+typedef uint32_t sdmmc_response_t[4];
+
+/**
+ * SD SWITCH_FUNC response buffer
+ */
+typedef struct {
+ uint32_t data[512 / 8 / sizeof(uint32_t)]; /*!< response data */
+} sdmmc_switch_func_rsp_t;
+
+typedef enum {
+ SDMMC_OK = 0,
+ SDMMC_ERR_NOT_SUPPORTED = 1,
+ SDMMC_ERR_INVALID_RESPONSE = 2,
+ SDMMC_ERR_TIMEOUT = 3,
+ SDMMC_ERR_NO_MEM = 4,
+ SDMMC_ERR_INVALID_SIZE = 5,
+ SDMMC_ERR_NO_CARD = 6,
+ SDMMC_ERR_INVALID_ARG = 7,
+ SDMMC_ERR_BUSY = 8,
+} sdmmc_err_t;
+
+/**
+ * SD/MMC command information
+ */
+typedef struct {
+ uint32_t opcode; /*!< SD or MMC command index */
+ uint32_t arg; /*!< SD/MMC command argument */
+ sdmmc_response_t response; /*!< response buffer */
+ void* data; /*!< buffer to send or read into */
+ size_t datalen; /*!< length of data buffer */
+ size_t blklen; /*!< block length */
+ int flags; /*!< see below */
+/** @cond */
+#define SCF_ITSDONE 0x0001 /*!< command is complete */
+#define SCF_CMD(flags) ((flags) & 0x00f0)
+#define SCF_CMD_AC 0x0000
+#define SCF_CMD_ADTC 0x0010
+#define SCF_CMD_BC 0x0020
+#define SCF_CMD_BCR 0x0030
+#define SCF_CMD_READ 0x0040 /*!< read command (data expected) */
+#define SCF_RSP_BSY 0x0100
+#define SCF_RSP_136 0x0200
+#define SCF_RSP_CRC 0x0400
+#define SCF_RSP_IDX 0x0800
+#define SCF_RSP_PRESENT 0x1000
+/* response types */
+#define SCF_RSP_R0 0 /*!< none */
+#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
+#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
+#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
+#define SCF_RSP_R3 (SCF_RSP_PRESENT)
+#define SCF_RSP_R4 (SCF_RSP_PRESENT)
+#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
+#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
+#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
+#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
+/* special flags */
+#define SCF_WAIT_BUSY 0x2000 /*!< Wait for completion of card busy signal before returning */
+#define SCF_AUTO_STOP 0x4000 /*!< Auto stop with command 12 */
+/** @endcond */
+ sdmmc_err_t error; /*!< error returned from transfer */
+ int timeout_ms; /*!< response timeout, in milliseconds */
+} sdmmc_command_t;
+
+/**
+ * SD/MMC Host description
+ *
+ * This structure defines properties of SD/MMC host and functions
+ * of SD/MMC host which can be used by upper layers.
+ */
+#define BIT(x) (1 << x)
+
+typedef struct {
+ uint32_t flags; /*!< flags defining host properties */
+#define SDMMC_HOST_FLAG_1BIT BIT(0) /*!< host supports 1-line SD and MMC protocol */
+#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */
+#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
+#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
+#define SDMMC_HOST_FLAG_DDR BIT(4) /*!< host supports DDR mode for SD/MMC */
+#define SDMMC_HOST_FLAG_DEINIT_ARG BIT(5) /*!< host `deinit` function called with the slot argument */
+ int slot; /*!< slot number, to be passed to host functions */
+ int max_freq_khz; /*!< max frequency supported by the host */
+#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
+#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */
+#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */
+#define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */
+#define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */
+ float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */
+ sdmmc_err_t (*init)(void); /*!< Host function to initialize the driver */
+ sdmmc_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */
+ size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
+ sdmmc_err_t (*set_bus_ddr_mode)(int slot, bool ddr_enable); /*!< host function to set DDR mode */
+ sdmmc_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
+ sdmmc_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */
+ union {
+ sdmmc_err_t (*deinit)(void); /*!< host function to deinitialize the driver */
+ sdmmc_err_t (*deinit_p)(int slot); /*!< host function to deinitialize the driver, called with the `slot` */
+ };
+ sdmmc_err_t (*io_int_enable)(int slot); /*!< Host function to enable SDIO interrupt line */
+ sdmmc_err_t (*io_int_wait)(int slot, int timeout_ms); /*!< Host function to wait for SDIO interrupt line to be active */
+ int command_timeout_ms; /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */
+} sdmmc_host_t;
+
+/**
+ * SD/MMC card information structure
+ */
+typedef struct {
+ sdmmc_host_t host; /*!< Host with which the card is associated */
+ uint32_t ocr; /*!< OCR (Operation Conditions Register) value */
+ union {
+ sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */
+ sdmmc_response_t raw_cid; /*!< raw CID of MMC card to be decoded
+ after the CSD is fetched in the data transfer mode*/
+ };
+ sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
+ sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
+ sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */
+ uint16_t rca; /*!< RCA (Relative Card Address) */
+ uint16_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */
+ uint32_t is_mem : 1; /*!< Bit indicates if the card is a memory card */
+ uint32_t is_sdio : 1; /*!< Bit indicates if the card is an IO card */
+ uint32_t is_mmc : 1; /*!< Bit indicates if the card is MMC */
+ uint32_t num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */
+ uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
+ uint32_t is_ddr : 1; /*!< Card supports DDR mode */
+ uint32_t reserved : 23; /*!< Reserved for future expansion */
+} sdmmc_card_t;
+
+// OS abstraction layer (copied from TinyUSB)
+void osal_task_delay(uint32_t msec);
+
+#endif // _SDMMC_TYPES_H_
diff --git a/circuitpython/lib/sdmmc/sdmmc_cmd.c b/circuitpython/lib/sdmmc/sdmmc_cmd.c
new file mode 100644
index 0000000..e2d853d
--- /dev/null
+++ b/circuitpython/lib/sdmmc/sdmmc_cmd.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "sdmmc_common.h"
+
+sdmmc_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
+{
+ if (card->host.command_timeout_ms != 0) {
+ cmd->timeout_ms = card->host.command_timeout_ms;
+ } else if (cmd->timeout_ms == 0) {
+ cmd->timeout_ms = SDMMC_DEFAULT_CMD_TIMEOUT_MS;
+ }
+
+ int slot = card->host.slot;
+ ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d timeout=%d",
+ slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen, cmd->timeout_ms);
+ sdmmc_err_t err = (*card->host.do_transaction)(slot, cmd);
+ if (err != 0) {
+ ESP_LOGD(TAG, "cmd=%d, sdmmc_req_run returned 0x%x", cmd->opcode, err);
+ return err;
+ }
+ ESP_LOGV(TAG, "cmd response %08x %08x %08x %08x err=0x%x state=%d",
+ cmd->response[0],
+ cmd->response[1],
+ cmd->response[2],
+ cmd->response[3],
+ cmd->error,
+ MMC_R1_CURRENT_STATE(cmd->response));
+ return cmd->error;
+}
+
+sdmmc_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
+{
+ sdmmc_command_t app_cmd = {
+ .opcode = MMC_APP_CMD,
+ .flags = SCF_CMD_AC | SCF_RSP_R1,
+ .arg = MMC_ARG_RCA(card->rca),
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &app_cmd);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ // Check APP_CMD status bit (only in SD mode)
+ if (!host_is_spi(card) && !(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) {
+ ESP_LOGW(TAG, "card doesn't support APP_CMD");
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ return sdmmc_send_cmd(card, cmd);
+}
+
+
+sdmmc_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card)
+{
+ sdmmc_command_t cmd = {
+ .opcode = MMC_GO_IDLE_STATE,
+ .flags = SCF_CMD_BC | SCF_RSP_R0,
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (host_is_spi(card)) {
+ /* To enter SPI mode, CMD0 needs to be sent twice (see figure 4-1 in
+ * SD Simplified spec v4.10). Some cards enter SD mode on first CMD0,
+ * so don't expect the above command to succeed.
+ * SCF_RSP_R1 flag below tells the lower layer to expect correct R1
+ * response (in SPI mode).
+ */
+ (void) err;
+ osal_task_delay(SDMMC_GO_IDLE_DELAY_MS);
+
+ cmd.flags |= SCF_RSP_R1;
+ err = sdmmc_send_cmd(card, &cmd);
+ }
+ if (err == SDMMC_OK) {
+ osal_task_delay(SDMMC_GO_IDLE_DELAY_MS);
+ }
+ return err;
+}
+
+
+sdmmc_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
+{
+ const uint8_t pattern = 0xaa; /* any pattern will do here */
+ sdmmc_command_t cmd = {
+ .opcode = SD_SEND_IF_COND,
+ .arg = (((ocr & SD_OCR_VOL_MASK) != 0) << 8) | pattern,
+ .flags = SCF_CMD_BCR | SCF_RSP_R7,
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ uint8_t response = cmd.response[0] & 0xff;
+ if (response != pattern) {
+ ESP_LOGD(TAG, "%s: received=0x%x expected=0x%x", __func__, response, pattern);
+ return SDMMC_ERR_INVALID_RESPONSE;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
+{
+ sdmmc_err_t err;
+
+ sdmmc_command_t cmd = {
+ .arg = ocr,
+ .flags = SCF_CMD_BCR | SCF_RSP_R3,
+ .opcode = SD_APP_OP_COND
+ };
+ int nretries = SDMMC_SEND_OP_COND_MAX_RETRIES;
+ int err_cnt = SDMMC_SEND_OP_COND_MAX_ERRORS;
+ for (; nretries != 0; --nretries) {
+ bzero(&cmd, sizeof cmd);
+ cmd.arg = ocr;
+ cmd.flags = SCF_CMD_BCR | SCF_RSP_R3;
+ if (!card->is_mmc) { /* SD mode */
+ cmd.opcode = SD_APP_OP_COND;
+ err = sdmmc_send_app_cmd(card, &cmd);
+ } else { /* MMC mode */
+ cmd.arg &= ~MMC_OCR_ACCESS_MODE_MASK;
+ cmd.arg |= MMC_OCR_SECTOR_MODE;
+ cmd.opcode = MMC_SEND_OP_COND;
+ err = sdmmc_send_cmd(card, &cmd);
+ }
+
+ if (err != SDMMC_OK) {
+ if (--err_cnt == 0) {
+ ESP_LOGD(TAG, "%s: sdmmc_send_app_cmd err=0x%x", __func__, err);
+ return err;
+ } else {
+ ESP_LOGV(TAG, "%s: ignoring err=0x%x", __func__, err);
+ continue;
+ }
+ }
+ // In SD protocol, card sets MEM_READY bit in OCR when it is ready.
+ // In SPI protocol, card clears IDLE_STATE bit in R1 response.
+ if (!host_is_spi(card)) {
+ if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) ||
+ ocr == 0) {
+ break;
+ }
+ } else {
+ if ((SD_SPI_R1(cmd.response) & SD_SPI_R1_IDLE_STATE) == 0) {
+ break;
+ }
+ }
+ osal_task_delay(10);
+ }
+ if (nretries == 0) {
+ return SDMMC_ERR_TIMEOUT;
+ }
+ if (ocrp) {
+ *ocrp = MMC_R3(cmd.response);
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp)
+{
+ assert(ocrp);
+ sdmmc_command_t cmd = {
+ .opcode = SD_READ_OCR,
+ .flags = SCF_CMD_BCR | SCF_RSP_R2
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ *ocrp = SD_SPI_R3(cmd.response);
+ return SDMMC_OK;
+}
+
+
+sdmmc_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid)
+{
+ assert(out_raw_cid);
+ sdmmc_command_t cmd = {
+ .opcode = MMC_ALL_SEND_CID,
+ .flags = SCF_CMD_BCR | SCF_RSP_R2
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ memcpy(out_raw_cid, &cmd.response, sizeof(sdmmc_response_t));
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid)
+{
+ assert(out_cid);
+ assert(host_is_spi(card) && "SEND_CID should only be used in SPI mode");
+ assert(!card->is_mmc && "MMC cards are not supported in SPI mode");
+ sdmmc_response_t buf;
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SEND_CID,
+ .flags = SCF_CMD_READ | SCF_CMD_ADTC,
+ .arg = 0,
+ .data = &buf[0],
+ .datalen = sizeof(buf)
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ sdmmc_flip_byte_order(buf, sizeof(buf));
+ return sdmmc_decode_cid(buf, out_cid);
+}
+
+
+sdmmc_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
+{
+ assert(out_rca);
+ sdmmc_command_t cmd = {
+ .opcode = SD_SEND_RELATIVE_ADDR,
+ .flags = SCF_CMD_BCR | SCF_RSP_R6
+ };
+
+ /* MMC cards expect us to set the RCA.
+ * Set RCA to 1 since we don't support multiple cards on the same bus, for now.
+ */
+ uint16_t mmc_rca = 1;
+ if (card->is_mmc) {
+ cmd.arg = MMC_ARG_RCA(mmc_rca);
+ }
+
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ *out_rca = (card->is_mmc) ? mmc_rca : SD_R6_RCA(cmd.response);
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd)
+{
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SET_BLOCKLEN,
+ .arg = csd->sector_size,
+ .flags = SCF_CMD_AC | SCF_RSP_R1
+ };
+ return sdmmc_send_cmd(card, &cmd);
+}
+
+sdmmc_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd)
+{
+ /* The trick with SEND_CSD is that in SPI mode, it acts as a data read
+ * command, while in SD mode it is an AC command with R2 response.
+ */
+ sdmmc_response_t spi_buf;
+ const bool is_spi = host_is_spi(card);
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SEND_CSD,
+ .arg = is_spi ? 0 : MMC_ARG_RCA(card->rca),
+ .flags = is_spi ? (SCF_CMD_READ | SCF_CMD_ADTC | SCF_RSP_R1) :
+ (SCF_CMD_AC | SCF_RSP_R2),
+ .data = is_spi ? &spi_buf[0] : 0,
+ .datalen = is_spi ? sizeof(spi_buf) : 0,
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ uint32_t* ptr = cmd.response;
+ if (is_spi) {
+ sdmmc_flip_byte_order(spi_buf, sizeof(spi_buf));
+ ptr = spi_buf;
+ }
+ if (card->is_mmc) {
+ err = sdmmc_mmc_decode_csd(cmd.response, out_csd);
+ } else {
+ err = sdmmc_decode_csd(ptr, out_csd);
+ }
+ return err;
+}
+
+sdmmc_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca)
+{
+ /* Don't expect to see a response when de-selecting a card */
+ uint32_t response = (rca == 0) ? 0 : SCF_RSP_R1;
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SELECT_CARD,
+ .arg = MMC_ARG_RCA(rca),
+ .flags = SCF_CMD_AC | response
+ };
+ return sdmmc_send_cmd(card, &cmd);
+}
+
+sdmmc_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr)
+{
+ size_t datalen = 8;
+ uint32_t buf[datalen];
+ sdmmc_command_t cmd = {
+ .data = buf,
+ .datalen = datalen,
+ .blklen = datalen,
+ .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
+ .opcode = SD_APP_SEND_SCR
+ };
+ sdmmc_err_t err = sdmmc_send_app_cmd(card, &cmd);
+ if (err == SDMMC_OK) {
+ err = sdmmc_decode_scr(buf, out_scr);
+ }
+ return err;
+}
+
+sdmmc_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width)
+{
+ sdmmc_command_t cmd = {
+ .opcode = SD_APP_SET_BUS_WIDTH,
+ .flags = SCF_RSP_R1 | SCF_CMD_AC,
+ .arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1,
+ };
+
+ return sdmmc_send_app_cmd(card, &cmd);
+}
+
+sdmmc_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable)
+{
+ assert(host_is_spi(card) && "CRC_ON_OFF can only be used in SPI mode");
+ sdmmc_command_t cmd = {
+ .opcode = SD_CRC_ON_OFF,
+ .arg = crc_enable ? 1 : 0,
+ .flags = SCF_CMD_AC | SCF_RSP_R1
+ };
+ return sdmmc_send_cmd(card, &cmd);
+}
+
+sdmmc_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
+{
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SEND_STATUS,
+ .arg = MMC_ARG_RCA(card->rca),
+ .flags = SCF_CMD_AC | SCF_RSP_R1
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ if (out_status) {
+ *out_status = MMC_R1(cmd.response);
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
+ size_t start_block, size_t block_count)
+{
+ sdmmc_err_t err = SDMMC_OK;
+ size_t block_size = card->csd.sector_size;
+ if ((intptr_t)src % 4 == 0) {
+ err = sdmmc_write_sectors_dma(card, src, start_block, block_count);
+ } else {
+ // SDMMC peripheral needs DMA-capable buffers. Split the write into
+ // separate single block writes, if needed, and allocate a temporary
+ // DMA-capable buffer.
+ uint32_t tmp_buf[block_size / sizeof(uint32_t)];
+ const uint8_t* cur_src = (const uint8_t*) src;
+ for (size_t i = 0; i < block_count; ++i) {
+ memcpy(tmp_buf, cur_src, block_size);
+ cur_src += block_size;
+ err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1);
+ if (err != SDMMC_OK) {
+ ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
+ __func__, err, start_block, i);
+ break;
+ }
+ }
+ }
+ return err;
+}
+
+sdmmc_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
+ size_t start_block, size_t block_count)
+{
+ if (start_block + block_count > (size_t) card->csd.capacity) {
+ return SDMMC_ERR_INVALID_SIZE;
+ }
+ size_t block_size = card->csd.sector_size;
+ sdmmc_command_t cmd = {
+ .flags = SCF_CMD_ADTC | SCF_RSP_R1,
+ .blklen = block_size,
+ .data = (void*) src,
+ .datalen = block_count * block_size,
+ .timeout_ms = SDMMC_WRITE_CMD_TIMEOUT_MS
+ };
+ if (block_count == 1) {
+ cmd.opcode = MMC_WRITE_BLOCK_SINGLE;
+ } else {
+ cmd.opcode = MMC_WRITE_BLOCK_MULTIPLE;
+ cmd.flags |= SCF_AUTO_STOP;
+ }
+ if (card->ocr & SD_OCR_SDHC_CAP) {
+ cmd.arg = start_block;
+ } else {
+ cmd.arg = start_block * block_size;
+ }
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
+ return err;
+ }
+ uint32_t status = 0;
+ size_t count = 0;
+ while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
+ // TODO: add some timeout here
+ err = sdmmc_send_cmd_send_status(card, &status);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ if (++count % 10 == 0) {
+ ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
+ }
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
+ size_t start_block, size_t block_count)
+{
+ sdmmc_err_t err = SDMMC_OK;
+ size_t block_size = card->csd.sector_size;
+ if ((intptr_t)dst % 4 == 0) {
+ err = sdmmc_read_sectors_dma(card, dst, start_block, block_count);
+ } else {
+ // SDMMC peripheral needs DMA-capable buffers. Split the read into
+ // separate single block reads, if needed, and allocate a temporary
+ // DMA-capable buffer.
+ uint32_t tmp_buf[block_size / sizeof(uint32_t)];
+ uint8_t* cur_dst = (uint8_t*) dst;
+ for (size_t i = 0; i < block_count; ++i) {
+ err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1);
+ if (err != SDMMC_OK) {
+ ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
+ __func__, err, start_block, i);
+ break;
+ }
+ memcpy(cur_dst, tmp_buf, block_size);
+ cur_dst += block_size;
+ }
+ }
+ return err;
+}
+
+sdmmc_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
+ size_t start_block, size_t block_count)
+{
+ if (start_block + block_count > (size_t) card->csd.capacity) {
+ return SDMMC_ERR_INVALID_SIZE;
+ }
+ size_t block_size = card->csd.sector_size;
+ sdmmc_command_t cmd = {
+ .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
+ .blklen = block_size,
+ .data = (void*) dst,
+ .datalen = block_count * block_size
+ };
+ if (block_count == 1) {
+ cmd.opcode = MMC_READ_BLOCK_SINGLE;
+ } else {
+ cmd.opcode = MMC_READ_BLOCK_MULTIPLE;
+ cmd.flags |= SCF_AUTO_STOP;
+ }
+ if (card->ocr & SD_OCR_SDHC_CAP) {
+ cmd.arg = start_block;
+ } else {
+ cmd.arg = start_block * block_size;
+ }
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
+ return err;
+ }
+ uint32_t status = 0;
+ size_t count = 0;
+ while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
+ // TODO: add some timeout here
+ err = sdmmc_send_cmd_send_status(card, &status);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ if (++count % 10 == 0) {
+ ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
+ }
+ }
+ return SDMMC_OK;
+}
diff --git a/circuitpython/lib/sdmmc/sdmmc_common.c b/circuitpython/lib/sdmmc/sdmmc_common.c
new file mode 100644
index 0000000..fea29ab
--- /dev/null
+++ b/circuitpython/lib/sdmmc/sdmmc_common.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "sdmmc_common.h"
+
+sdmmc_err_t sdmmc_init_ocr(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ /* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
+ * ranges the card can support. This step is skipped since 1.8V isn't
+ * supported on the ESP32.
+ */
+
+ uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
+ if ((card->ocr & SD_OCR_SDHC_CAP) != 0) {
+ host_ocr |= SD_OCR_SDHC_CAP;
+ }
+ /* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
+ err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
+
+ /* If time-out, re-try send_op_cond as MMC */
+ if (err == SDMMC_ERR_TIMEOUT && !host_is_spi(card)) {
+ ESP_LOGD(TAG, "send_op_cond timeout, trying MMC");
+ card->is_mmc = 1;
+ err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
+ }
+
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
+ return err;
+ }
+ if (host_is_spi(card)) {
+ err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: read_ocr returned 0x%x", __func__, err);
+ return err;
+ }
+ }
+ ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr);
+
+ /* Clear all voltage bits in host's OCR which the card doesn't support.
+ * Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41
+ * response.
+ */
+ host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK));
+ ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_cid(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ sdmmc_response_t raw_cid;
+ if (!host_is_spi(card)) {
+ err = sdmmc_send_cmd_all_send_cid(card, &raw_cid);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
+ return err;
+ }
+ if (!card->is_mmc) {
+ err = sdmmc_decode_cid(raw_cid, &card->cid);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
+ return err;
+ }
+ } else {
+ /* For MMC, need to know CSD to decode CID. But CSD can only be read
+ * in data transfer mode, and it is not possible to read CID in data
+ * transfer mode. We temporiliy store the raw cid and do the
+ * decoding after the RCA is set and the card is in data transfer
+ * mode.
+ */
+ memcpy(card->raw_cid, raw_cid, sizeof(sdmmc_response_t));
+ }
+ } else {
+ err = sdmmc_send_cmd_send_cid(card, &card->cid);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: send_cid returned 0x%x", __func__, err);
+ return err;
+ }
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_rca(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
+ return err;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ sdmmc_response_t raw_cid;
+ memcpy(raw_cid, card->raw_cid, sizeof(raw_cid));
+ err = sdmmc_mmc_decode_cid(card->csd.mmc_ver, raw_cid, &card->cid);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
+ return err;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_csd(sdmmc_card_t* card)
+{
+ assert(card->is_mem == 1);
+ /* Get and decode the contents of CSD register. Determine card capacity. */
+ sdmmc_err_t err = sdmmc_send_cmd_send_csd(card, &card->csd);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
+ return err;
+ }
+ const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
+ if (!(card->ocr & SD_OCR_SDHC_CAP) &&
+ (size_t) card->csd.capacity > max_sdsc_capacity) {
+ ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
+ __func__, card->csd.capacity, max_sdsc_capacity);
+ card->csd.capacity = max_sdsc_capacity;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_select_card(sdmmc_card_t* card)
+{
+ assert(!host_is_spi(card));
+ sdmmc_err_t err = sdmmc_send_cmd_select_card(card, card->rca);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
+ return err;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card)
+{
+ sdmmc_err_t err = SDMMC_ERR_NOT_SUPPORTED;
+ if (card->is_mem && !card->is_mmc) {
+ err = sdmmc_enable_hs_mode_and_check(card);
+ } else if (card->is_sdio) {
+ err = sdmmc_io_enable_hs_mode(card);
+ } else if (card->is_mmc){
+ err = sdmmc_mmc_enable_hs_mode(card);
+ }
+ if (err == SDMMC_ERR_NOT_SUPPORTED) {
+ ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
+ card->max_freq_khz = SDMMC_FREQ_DEFAULT;
+ } else if (err != SDMMC_OK) {
+ return err;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card)
+{
+ int bus_width = 1;
+
+ if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
+ (card->log_bus_width == 2)) {
+ bus_width = 4;
+ } else if ((card->host.flags & SDMMC_HOST_FLAG_8BIT) &&
+ (card->log_bus_width == 3)) {
+ bus_width = 8;
+ }
+ ESP_LOGD(TAG, "%s: using %d-bit bus", __func__, bus_width);
+ if (bus_width > 1) {
+ sdmmc_err_t err = (*card->host.set_bus_width)(card->host.slot, bus_width);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "host.set_bus_width failed (0x%x)", err);
+ return err;
+ }
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
+{
+ assert(card->max_freq_khz <= card->host.max_freq_khz);
+
+ /* Find highest frequency in the following list,
+ * which is below card->max_freq_khz.
+ */
+ const uint32_t freq_values[] = {
+ SDMMC_FREQ_52M,
+ SDMMC_FREQ_HIGHSPEED,
+ SDMMC_FREQ_26M,
+ SDMMC_FREQ_DEFAULT
+ //NOTE: in sdspi mode, 20MHz may not work. in that case, add 10MHz here.
+ };
+ const int n_freq_values = sizeof(freq_values) / sizeof(freq_values[0]);
+
+ uint32_t selected_freq = SDMMC_FREQ_PROBING;
+ for (int i = 0; i < n_freq_values; ++i) {
+ uint32_t freq = freq_values[i];
+ if (card->max_freq_khz >= freq) {
+ selected_freq = freq;
+ break;
+ }
+ }
+
+ ESP_LOGD(TAG, "%s: using %d kHz bus frequency", __func__, selected_freq);
+ if (selected_freq > SDMMC_FREQ_PROBING) {
+ sdmmc_err_t err = (*card->host.set_card_clk)(card->host.slot, selected_freq);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "failed to switch bus frequency (0x%x)", err);
+ return err;
+ }
+ }
+
+ if (card->is_ddr) {
+ if (card->host.set_bus_ddr_mode == NULL) {
+ ESP_LOGE(TAG, "host doesn't support DDR mode or voltage switching");
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ sdmmc_err_t err = (*card->host.set_bus_ddr_mode)(card->host.slot, true);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "failed to switch bus to DDR mode (0x%x)", err);
+ return err;
+ }
+ }
+ return SDMMC_OK;
+}
+
+void sdmmc_flip_byte_order(uint32_t* response, size_t size)
+{
+ assert(size % (2 * sizeof(uint32_t)) == 0);
+ const size_t n_words = size / sizeof(uint32_t);
+ for (size_t i = 0; i < n_words / 2; ++i) {
+ uint32_t left = __builtin_bswap32(response[i]);
+ uint32_t right = __builtin_bswap32(response[n_words - i - 1]);
+ response[i] = right;
+ response[n_words - i - 1] = left;
+ }
+}
+
+void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
+{
+ // bool print_scr = false;
+ // bool print_csd = false;
+ // const char* type;
+ // fprintf(stream, "Name: %s\n", card->cid.name);
+ // if (card->is_sdio) {
+ // type = "SDIO";
+ // print_scr = true;
+ // print_csd = true;
+ // } else if (card->is_mmc) {
+ // type = "MMC";
+ // print_csd = true;
+ // } else {
+ // type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC";
+ // }
+ // fprintf(stream, "Type: %s\n", type);
+ // if (card->max_freq_khz < 1000) {
+ // fprintf(stream, "Speed: %d kHz\n", card->max_freq_khz);
+ // } else {
+ // fprintf(stream, "Speed: %d MHz%s\n", card->max_freq_khz / 1000,
+ // card->is_ddr ? ", DDR" : "");
+ // }
+ // fprintf(stream, "Size: %luMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
+
+ // if (print_csd) {
+ // fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n",
+ // card->csd.csd_ver,
+ // card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
+ // }
+ // if (print_scr) {
+ // fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
+ // }
+}
+
+sdmmc_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
+{
+ const uint32_t width_1bit = SDMMC_HOST_FLAG_1BIT;
+ const uint32_t width_4bit = SDMMC_HOST_FLAG_4BIT;
+ const uint32_t width_8bit = SDMMC_HOST_FLAG_8BIT;
+ const uint32_t width_mask = width_1bit | width_4bit | width_8bit;
+
+ int slot_bit_width = card->host.get_bus_width(card->host.slot);
+ if (slot_bit_width == 1 &&
+ (card->host.flags & (width_4bit | width_8bit))) {
+ card->host.flags &= ~width_mask;
+ card->host.flags |= width_1bit;
+ } else if (slot_bit_width == 4 && (card->host.flags & width_8bit)) {
+ if ((card->host.flags & width_4bit) == 0) {
+ ESP_LOGW(TAG, "slot width set to 4, but host flags don't have 4 line mode enabled; using 1 line mode");
+ card->host.flags &= ~width_mask;
+ card->host.flags |= width_1bit;
+ } else {
+ card->host.flags &= ~width_mask;
+ card->host.flags |= width_4bit;
+ }
+ }
+ return SDMMC_OK;
+}
diff --git a/circuitpython/lib/sdmmc/sdmmc_common.h b/circuitpython/lib/sdmmc/sdmmc_common.h
new file mode 100644
index 0000000..583bf9d
--- /dev/null
+++ b/circuitpython/lib/sdmmc/sdmmc_common.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#pragma once
+
+#include <string.h>
+#include "sdmmc_defs.h"
+#include "sdmmc_types.h"
+#include "sdmmc_cmd.h"
+
+#define SDMMC_GO_IDLE_DELAY_MS 20
+#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10
+
+/* These delay values are mostly useful for cases when CD pin is not used, and
+ * the card is removed. In this case, SDMMC peripheral may not always return
+ * CMD_DONE / DATA_DONE interrupts after signaling the error. These timeouts work
+ * as a safety net in such cases.
+ */
+#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands
+#define SDMMC_WRITE_CMD_TIMEOUT_MS 5000 // Max timeout of write commands
+
+/* Maximum retry/error count for SEND_OP_COND (CMD1).
+ * These are somewhat arbitrary, values originate from OpenBSD driver.
+ */
+#define SDMMC_SEND_OP_COND_MAX_RETRIES 100
+#define SDMMC_SEND_OP_COND_MAX_ERRORS 3
+
+/* Functions to send individual commands */
+sdmmc_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
+sdmmc_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
+sdmmc_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr);
+sdmmc_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
+sdmmc_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp);
+sdmmc_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid);
+sdmmc_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid);
+sdmmc_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
+sdmmc_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd);
+sdmmc_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
+ uint32_t mode, uint32_t group, uint32_t function,
+ sdmmc_switch_func_rsp_t* resp);
+sdmmc_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
+sdmmc_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca);
+sdmmc_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
+sdmmc_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
+sdmmc_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
+sdmmc_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
+
+/* Higher level functions */
+sdmmc_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
+ size_t start_block, size_t block_count);
+sdmmc_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
+ size_t start_block, size_t block_count);
+
+/* SD specific */
+sdmmc_err_t sdmmc_check_scr(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
+sdmmc_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
+sdmmc_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
+
+/* SDIO specific */
+sdmmc_err_t sdmmc_io_reset(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
+sdmmc_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int function,
+ uint32_t reg, uint32_t arg, uint8_t *byte);
+sdmmc_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
+ uint32_t reg, int arg, void *data, size_t size);
+
+
+/* MMC specific */
+sdmmc_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen);
+sdmmc_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value);
+sdmmc_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid);
+sdmmc_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
+sdmmc_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card);
+
+/* Parts of card initialization flow */
+sdmmc_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_select_card(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_csd(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_cid(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_rca(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_ocr(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_spi_crc(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_io(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_sd_scr(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_mmc_read_cid(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_host_frequency(sdmmc_card_t* card);
+sdmmc_err_t sdmmc_init_mmc_check_csd(sdmmc_card_t* card);
+
+/* Various helper functions */
+static inline bool host_is_spi(const sdmmc_card_t* card)
+{
+ return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0;
+}
+
+static inline uint32_t get_host_ocr(float voltage)
+{
+ // TODO: report exact voltage to the card
+ // For now tell that the host has 2.8-3.6V voltage range
+ (void) voltage;
+ return SD_OCR_VOL_MASK;
+}
+
+void sdmmc_flip_byte_order(uint32_t* response, size_t size);
+
+sdmmc_err_t sdmmc_fix_host_flags(sdmmc_card_t* card);
diff --git a/circuitpython/lib/sdmmc/sdmmc_init.c b/circuitpython/lib/sdmmc/sdmmc_init.c
new file mode 100644
index 0000000..de5c739
--- /dev/null
+++ b/circuitpython/lib/sdmmc/sdmmc_init.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "sdmmc_common.h"
+
+#define SDMMC_INIT_STEP(condition, function) \
+ do { \
+ if ((condition)) { \
+ sdmmc_err_t err = (function)(card); \
+ if (err != SDMMC_OK) { \
+ ESP_LOGD(TAG, "%s: %s returned 0x%x", __func__, #function, err); \
+ return err; \
+ } \
+ } \
+ } while(0);
+
+
+sdmmc_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
+{
+ memset(card, 0, sizeof(*card));
+ memcpy(&card->host, config, sizeof(*config));
+ const bool is_spi = host_is_spi(card);
+ const bool always = true;
+ const bool io_supported = true;
+
+ /* Check if host flags are compatible with slot configuration. */
+ SDMMC_INIT_STEP(!is_spi, sdmmc_fix_host_flags);
+
+ /* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5). */
+ SDMMC_INIT_STEP(io_supported, sdmmc_io_reset);
+
+ /* GO_IDLE_STATE (CMD0) command resets the card */
+ SDMMC_INIT_STEP(always, sdmmc_send_cmd_go_idle_state);
+
+ /* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards. */
+ SDMMC_INIT_STEP(always, sdmmc_init_sd_if_cond);
+
+ /* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card. */
+ SDMMC_INIT_STEP(io_supported, sdmmc_init_io);
+
+ const bool is_mem = card->is_mem;
+ const bool is_sdio = !is_mem;
+
+ /* Enable CRC16 checks for data transfers in SPI mode */
+ SDMMC_INIT_STEP(is_spi, sdmmc_init_spi_crc);
+
+ /* Use SEND_OP_COND to set up card OCR */
+ SDMMC_INIT_STEP(is_mem, sdmmc_init_ocr);
+
+ const bool is_mmc = is_mem && card->is_mmc;
+ const bool is_sdmem = is_mem && !is_mmc;
+
+ ESP_LOGD(TAG, "%s: card type is %s", __func__,
+ is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
+
+ /* Read the contents of CID register*/
+ SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);
+
+ /* Assign RCA */
+ SDMMC_INIT_STEP(!is_spi, sdmmc_init_rca);
+
+ /* Read and decode the contents of CSD register */
+ SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);
+
+ /* Decode the contents of mmc CID register */
+ SDMMC_INIT_STEP(is_mmc && !is_spi, sdmmc_init_mmc_decode_cid);
+
+ /* Switch the card from stand-by mode to data transfer mode (not needed if
+ * SPI interface is used). This is needed to issue SET_BLOCKLEN and
+ * SEND_SCR commands.
+ */
+ SDMMC_INIT_STEP(!is_spi, sdmmc_init_select_card);
+
+ /* SD memory cards:
+ * Set block len for SDSC cards to 512 bytes (same as SDHC)
+ * Read SCR
+ * Wait to enter data transfer state
+ */
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_blocklen);
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_scr);
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_wait_data_ready);
+
+ /* MMC cards: read CXD */
+ SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
+
+ /* Try to switch card to HS mode if the card supports it.
+ * Set card->max_freq_khz value accordingly.
+ */
+ SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);
+
+ /* Set bus width. One call for every kind of card, then one for the host */
+ if (!is_spi) {
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
+ SDMMC_INIT_STEP(is_sdio, sdmmc_init_io_bus_width);
+ SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_bus_width);
+ SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
+ }
+
+ /* Switch to the host to use card->max_freq_khz frequency. */
+ SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
+
+ /* Sanity check after switching the bus mode and frequency */
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
+ /* TODO: this is CMD line only, add data checks for eMMC */
+ SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_check_csd);
+ /* TODO: add similar checks for SDIO */
+
+ return SDMMC_OK;
+}
diff --git a/circuitpython/lib/sdmmc/sdmmc_io.c b/circuitpython/lib/sdmmc/sdmmc_io.c
new file mode 100644
index 0000000..17ab291
--- /dev/null
+++ b/circuitpython/lib/sdmmc/sdmmc_io.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "sdmmc_common.h"
+
+
+#define CIS_TUPLE(NAME) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&cis_tuple_func_default, }
+#define CIS_TUPLE_WITH_FUNC(NAME, FUNC) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&(FUNC), }
+
+#define CIS_CHECK_SIZE(SIZE, MINIMAL) do {int store_size = (SIZE); if((store_size) < (MINIMAL)) return SDMMC_ERR_INVALID_SIZE;} while(0)
+#define CIS_CHECK_UNSUPPORTED(COND) do {if(!(COND)) return SDMMC_ERR_NOT_SUPPORTED;} while(0)
+#define CIS_GET_MINIMAL_SIZE 32
+
+typedef sdmmc_err_t (*cis_tuple_info_func_t)(const void* tuple_info, uint8_t* data, FILE* fp);
+
+typedef struct {
+ int code;
+ const char *name;
+ cis_tuple_info_func_t func;
+} cis_tuple_t;
+
+static sdmmc_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp);
+static sdmmc_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp);
+static sdmmc_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp);
+static sdmmc_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp);
+
+static const cis_tuple_t cis_table[] = {
+ CIS_TUPLE(NULL),
+ CIS_TUPLE(DEVICE),
+ CIS_TUPLE(CHKSUM),
+ CIS_TUPLE(VERS1),
+ CIS_TUPLE(ALTSTR),
+ CIS_TUPLE(CONFIG),
+ CIS_TUPLE_WITH_FUNC(CFTABLE_ENTRY, cis_tuple_func_cftable_entry),
+ CIS_TUPLE_WITH_FUNC(MANFID, cis_tuple_func_manfid),
+ CIS_TUPLE(FUNCID),
+ CIS_TUPLE(FUNCE),
+ CIS_TUPLE(VENDER_BEGIN),
+ CIS_TUPLE(VENDER_END),
+ CIS_TUPLE(SDIO_STD),
+ CIS_TUPLE(SDIO_EXT),
+ CIS_TUPLE_WITH_FUNC(END, cis_tuple_func_end),
+};
+
+
+sdmmc_err_t sdmmc_io_reset(sdmmc_card_t* card)
+{
+ uint8_t sdio_reset = CCCR_CTL_RES;
+ sdmmc_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CTL, SD_ARG_CMD52_WRITE, &sdio_reset);
+ if (err == SDMMC_ERR_TIMEOUT || (host_is_spi(card) && err == SDMMC_ERR_NOT_SUPPORTED)) {
+ /* Non-IO cards are allowed to time out (in SD mode) or
+ * return "invalid command" error (in SPI mode).
+ */
+ } else if (err == SDMMC_ERR_NO_CARD) {
+ ESP_LOGD(TAG, "%s: card not present", __func__);
+ return err;
+ } else if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: unexpected return: 0x%x", __func__, err );
+ return err;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_io(sdmmc_card_t* card)
+{
+ /* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
+ * Non-IO cards will not respond to this command.
+ */
+ sdmmc_err_t err = sdmmc_io_send_op_cond(card, 0, &card->ocr);
+ if (err != SDMMC_OK) {
+ ESP_LOGD(TAG, "%s: io_send_op_cond (1) returned 0x%x; not IO card", __func__, err);
+ card->is_sdio = 0;
+ card->is_mem = 1;
+ } else {
+ card->is_sdio = 1;
+
+ if (card->ocr & SD_IO_OCR_MEM_PRESENT) {
+ ESP_LOGD(TAG, "%s: IO-only card", __func__);
+ card->is_mem = 0;
+ }
+ card->num_io_functions = SD_IO_OCR_NUM_FUNCTIONS(card->ocr);
+ ESP_LOGD(TAG, "%s: number of IO functions: %d", __func__, card->num_io_functions);
+ if (card->num_io_functions == 0) {
+ card->is_sdio = 0;
+ }
+ uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
+ host_ocr &= card->ocr;
+ err = sdmmc_io_send_op_cond(card, host_ocr, &card->ocr);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
+ return err;
+ }
+ err = sdmmc_io_enable_int(card);
+ if (err != SDMMC_OK) {
+ ESP_LOGD(TAG, "%s: sdmmc_enable_int failed (0x%x)", __func__, err);
+ }
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ card->log_bus_width = 0;
+ if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
+ uint8_t card_cap = 0;
+ err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CARD_CAP,
+ SD_ARG_CMD52_READ, &card_cap);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read SD_IO_CCCR_CARD_CAP) returned 0x%0x", __func__, err);
+ return err;
+ }
+ ESP_LOGD(TAG, "IO card capabilities byte: %02x", card_cap);
+ if (!(card_cap & CCCR_CARD_CAP_LSC) ||
+ (card_cap & CCCR_CARD_CAP_4BLS)) {
+ // This card supports 4-bit bus mode
+ uint8_t bus_width = CCCR_BUS_WIDTH_4;
+ err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_BUS_WIDTH,
+ SD_ARG_CMD52_WRITE, &bus_width);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write SD_IO_CCCR_BUS_WIDTH) returned 0x%0x", __func__, err);
+ return err;
+ }
+ card->log_bus_width = 2;
+ }
+ }
+ return SDMMC_OK;
+}
+
+
+sdmmc_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
+{
+ /* If the host is configured to use low frequency, don't attempt to switch */
+ if (card->host.max_freq_khz < SDMMC_FREQ_DEFAULT) {
+ card->max_freq_khz = card->host.max_freq_khz;
+ return SDMMC_OK;
+ } else if (card->host.max_freq_khz < SDMMC_FREQ_HIGHSPEED) {
+ card->max_freq_khz = SDMMC_FREQ_DEFAULT;
+ return SDMMC_OK;
+ }
+
+ /* For IO cards, do write + read operation on "High Speed" register,
+ * setting EHS bit. If both EHS and SHS read back as set, then HS mode
+ * has been enabled.
+ */
+ uint8_t val = CCCR_HIGHSPEED_ENABLE;
+ sdmmc_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_HIGHSPEED,
+ SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &val);
+ if (err != SDMMC_OK) {
+ ESP_LOGD(TAG, "%s: sdmmc_io_rw_direct returned 0x%x", __func__, err);
+ return err;
+ }
+
+ ESP_LOGD(TAG, "%s: CCCR_HIGHSPEED=0x%02x", __func__, val);
+ const uint8_t hs_mask = CCCR_HIGHSPEED_ENABLE | CCCR_HIGHSPEED_SUPPORT;
+ if ((val & hs_mask) != hs_mask) {
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
+ return SDMMC_OK;
+}
+
+
+sdmmc_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
+{
+ sdmmc_err_t err = SDMMC_OK;
+ sdmmc_command_t cmd = {
+ .flags = SCF_CMD_BCR | SCF_RSP_R4,
+ .arg = ocr,
+ .opcode = SD_IO_SEND_OP_COND
+ };
+ for (size_t i = 0; i < 100; i++) {
+ err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ break;
+ }
+ if ((MMC_R4(cmd.response) & SD_IO_OCR_MEM_READY) ||
+ ocr == 0) {
+ break;
+ }
+ err = SDMMC_ERR_TIMEOUT;
+ osal_task_delay(SDMMC_IO_SEND_OP_COND_DELAY_MS);
+ }
+ if (err == SDMMC_OK && ocrp != NULL)
+ *ocrp = MMC_R4(cmd.response);
+
+ return err;
+}
+
+sdmmc_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int func,
+ uint32_t reg, uint32_t arg, uint8_t *data)
+{
+ sdmmc_err_t err;
+ sdmmc_command_t cmd = {
+ .flags = SCF_CMD_AC | SCF_RSP_R5,
+ .arg = 0,
+ .opcode = SD_IO_RW_DIRECT
+ };
+
+ arg |= (func & SD_ARG_CMD52_FUNC_MASK) << SD_ARG_CMD52_FUNC_SHIFT;
+ arg |= (reg & SD_ARG_CMD52_REG_MASK) << SD_ARG_CMD52_REG_SHIFT;
+ arg |= (*data & SD_ARG_CMD52_DATA_MASK) << SD_ARG_CMD52_DATA_SHIFT;
+ cmd.arg = arg;
+
+ err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ ESP_LOGV(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
+ return err;
+ }
+
+ *data = SD_R5_DATA(cmd.response);
+
+ return SDMMC_OK;
+}
+
+
+sdmmc_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, uint8_t *out_byte)
+{
+ sdmmc_err_t ret = sdmmc_io_rw_direct(card, function, addr, SD_ARG_CMD52_READ, out_byte);
+ if (ret != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read 0x%x) returned 0x%x", __func__, addr, ret);
+ }
+ return ret;
+}
+
+sdmmc_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, uint8_t in_byte, uint8_t* out_byte)
+{
+ uint8_t tmp_byte = in_byte;
+ sdmmc_err_t ret = sdmmc_io_rw_direct(card, function, addr,
+ SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &tmp_byte);
+ if (ret != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write 0x%x) returned 0x%x", __func__, addr, ret);
+ return ret;
+ }
+ if (out_byte != NULL) {
+ *out_byte = tmp_byte;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func,
+ uint32_t reg, int arg, void *datap, size_t datalen)
+{
+ sdmmc_err_t err;
+ const size_t max_byte_transfer_size = 512;
+ sdmmc_command_t cmd = {
+ .flags = SCF_CMD_AC | SCF_RSP_R5,
+ .arg = 0,
+ .opcode = SD_IO_RW_EXTENDED,
+ .data = datap,
+ .datalen = datalen,
+ .blklen = max_byte_transfer_size /* TODO: read max block size from CIS */
+ };
+
+ uint32_t count; /* number of bytes or blocks, depending on transfer mode */
+ if (arg & SD_ARG_CMD53_BLOCK_MODE) {
+ if (cmd.datalen % cmd.blklen != 0) {
+ return SDMMC_ERR_INVALID_SIZE;
+ }
+ count = cmd.datalen / cmd.blklen;
+ } else {
+ if (datalen > max_byte_transfer_size) {
+ /* TODO: split into multiple operations? */
+ return SDMMC_ERR_INVALID_SIZE;
+ }
+ if (datalen == max_byte_transfer_size) {
+ count = 0; // See 5.3.1 SDIO simplifed spec
+ } else {
+ count = datalen;
+ }
+ cmd.blklen = datalen;
+ }
+
+ arg |= (func & SD_ARG_CMD53_FUNC_MASK) << SD_ARG_CMD53_FUNC_SHIFT;
+ arg |= (reg & SD_ARG_CMD53_REG_MASK) << SD_ARG_CMD53_REG_SHIFT;
+ arg |= (count & SD_ARG_CMD53_LENGTH_MASK) << SD_ARG_CMD53_LENGTH_SHIFT;
+ cmd.arg = arg;
+
+ if ((arg & SD_ARG_CMD53_WRITE) == 0) {
+ cmd.flags |= SCF_CMD_READ;
+ }
+
+ err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
+ return err;
+ }
+
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, void* dst, size_t size)
+{
+ /* host quirk: SDIO transfer with length not divisible by 4 bytes
+ * has to be split into two transfers: one with aligned length,
+ * the other one for the remaining 1-3 bytes.
+ */
+ uint8_t *pc_dst = dst;
+ while (size > 0) {
+ size_t size_aligned = size & (~3);
+ size_t will_transfer = size_aligned > 0 ? size_aligned : size;
+
+ sdmmc_err_t err = sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT,
+ pc_dst, will_transfer);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ pc_dst += will_transfer;
+ size -= will_transfer;
+ addr += will_transfer;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, const void* src, size_t size)
+{
+ /* same host quirk as in sdmmc_io_read_bytes */
+ const uint8_t *pc_src = (const uint8_t*) src;
+
+ while (size > 0) {
+ size_t size_aligned = size & (~3);
+ size_t will_transfer = size_aligned > 0 ? size_aligned : size;
+
+ sdmmc_err_t err = sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT,
+ (void*) pc_src, will_transfer);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ pc_src += will_transfer;
+ size -= will_transfer;
+ addr += will_transfer;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, void* dst, size_t size)
+{
+ if (size % 4 != 0) {
+ return SDMMC_ERR_INVALID_SIZE;
+ }
+ return sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
+ dst, size);
+}
+
+sdmmc_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, const void* src, size_t size)
+{
+ if (size % 4 != 0) {
+ return SDMMC_ERR_INVALID_SIZE;
+ }
+ return sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
+ (void*) src, size);
+}
+
+sdmmc_err_t sdmmc_io_enable_int(sdmmc_card_t* card)
+{
+ if (card->host.io_int_enable == NULL) {
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ return (*card->host.io_int_enable)(card->host.slot);
+}
+
+sdmmc_err_t sdmmc_io_wait_int(sdmmc_card_t* card, int timeout_ms)
+{
+ if (card->host.io_int_wait == NULL) {
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ return (*card->host.io_int_wait)(card->host.slot, timeout_ms);
+}
+
+
+/*
+ * Print the CIS information of a CIS card, currently only ESP slave supported.
+ */
+
+static sdmmc_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp)
+{
+ const cis_tuple_t* tuple = (const cis_tuple_t*)p;
+ uint8_t code = *(data++);
+ int size = *(data++);
+ if (tuple) {
+ fprintf(fp, "TUPLE: %s, size: %d: ", tuple->name, size);
+ } else {
+ fprintf(fp, "TUPLE: unknown(%02X), size: %d: ", code, size);
+ }
+ for (int i = 0; i < size; i++) fprintf(fp, "%02X ", *(data++));
+ fprintf(fp, "\n");
+ return SDMMC_OK;
+}
+
+static sdmmc_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp)
+{
+ const cis_tuple_t* tuple = (const cis_tuple_t*)p;
+ data++;
+ int size = *(data++);
+ fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size);
+ CIS_CHECK_SIZE(size, 4);
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wcast-align"
+ fprintf(fp, " MANF: %04X, CARD: %04X\n", *(uint16_t*)(data), *(uint16_t*)(data+2));
+ #pragma GCC diagnostic pop
+ return SDMMC_OK;
+}
+
+static sdmmc_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp)
+{
+ const cis_tuple_t* tuple = (const cis_tuple_t*)p;
+ data++;
+ fprintf(fp, "TUPLE: %s\n", tuple->name);
+ return SDMMC_OK;
+}
+
+static sdmmc_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp)
+{
+ const cis_tuple_t* tuple = (const cis_tuple_t*)p;
+ data++;
+ int size = *(data++);
+ fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size);
+ CIS_CHECK_SIZE(size, 2);
+
+ CIS_CHECK_SIZE(size--, 1);
+ bool interface = data[0] & BIT(7);
+ bool def = data[0] & BIT(6);
+ int conf_ent_num = data[0] & 0x3F;
+ fprintf(fp, " INDX: %02X, Intface: %d, Default: %d, Conf-Entry-Num: %d\n", *(data++), interface, def, conf_ent_num);
+
+ if (interface) {
+ CIS_CHECK_SIZE(size--, 1);
+ fprintf(fp, " IF: %02X\n", *(data++));
+ }
+
+ CIS_CHECK_SIZE(size--, 1);
+ bool misc = data[0] & BIT(7);
+ int mem_space = (data[0] >> 5 )&(0x3);
+ bool irq = data[0] & BIT(4);
+ bool io_sp = data[0] & BIT(3);
+ bool timing = data[0] & BIT(2);
+ int power = data[0] & 3;
+ fprintf(fp, " FS: %02X, misc: %d, mem_space: %d, irq: %d, io_space: %d, timing: %d, power: %d\n", *(data++), misc, mem_space, irq, io_sp, timing, power);
+
+ CIS_CHECK_UNSUPPORTED(power == 0); //power descriptor is not handled yet
+ CIS_CHECK_UNSUPPORTED(!timing); //timing descriptor is not handled yet
+ CIS_CHECK_UNSUPPORTED(!io_sp); //io space descriptor is not handled yet
+
+ if (irq) {
+ CIS_CHECK_SIZE(size--, 1);
+ bool mask = data[0] & BIT(4);
+ fprintf(fp, " IR: %02X, mask: %d, ",*(data++), mask);
+ if (mask) {
+ CIS_CHECK_SIZE(size, 2);
+ size-=2;
+ fprintf(fp, " IRQ: %02X %02X\n", data[0], data[1]);
+ data+=2;
+ }
+ }
+
+ if (mem_space) {
+ CIS_CHECK_SIZE(size, 2);
+ size-=2;
+ CIS_CHECK_UNSUPPORTED(mem_space==1); //other cases not handled yet
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wcast-align"
+ int len = *(uint16_t*)data;
+ #pragma GCC diagnostic pop
+ fprintf(fp, " LEN: %04X\n", len);
+ data+=2;
+ }
+
+ CIS_CHECK_UNSUPPORTED(misc==0); //misc descriptor is not handled yet
+ return SDMMC_OK;
+}
+
+static const cis_tuple_t* get_tuple(uint8_t code)
+{
+ for (size_t i = 0; i < sizeof(cis_table)/sizeof(cis_tuple_t); i++) {
+ if (code == cis_table[i].code) return &cis_table[i];
+ }
+ return NULL;
+}
+
+sdmmc_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp)
+{
+ if (!fp) fp = stdout;
+
+ uint8_t* cis = buffer;
+ do {
+ const cis_tuple_t* tuple = get_tuple(cis[0]);
+ int size = cis[1];
+ sdmmc_err_t ret = SDMMC_OK;
+ if (tuple) {
+ ret = tuple->func(tuple, cis, fp);
+ } else {
+ ret = cis_tuple_func_default(NULL, cis, fp);
+ }
+ if (ret != SDMMC_OK) return ret;
+ cis += 2 + size;
+ if (tuple && tuple->code == CISTPL_CODE_END) break;
+ } while (cis < buffer + buffer_size) ;
+ return SDMMC_OK;
+}
+
+/**
+ * Check tuples in the buffer.
+ *
+ * @param buf Buffer to check
+ * @param buffer_size Size of the buffer
+ * @param inout_cis_offset
+ * - input: the last cis_offset, relative to the beginning of the buf. -1 if
+ * this buffer begin with the tuple length, otherwise should be no smaller than
+ * zero.
+ * - output: when the end tuple found, output offset of the CISTPL_CODE_END
+ * byte + 1 (relative to the beginning of the buffer; when not found, output
+ * the address of next tuple code.
+ *
+ * @return true if found, false if haven't.
+ */
+static bool check_tuples_in_buffer(uint8_t* buf, int buffer_size, int* inout_cis_offset)
+{
+ int cis_offset = *inout_cis_offset;
+ if (cis_offset == -1) {
+ //the CIS code is checked in the last buffer, skip to next tuple
+ cis_offset += buf[0] + 2;
+ }
+ assert(cis_offset >= 0);
+ while (1) {
+ if (cis_offset < buffer_size) {
+ //A CIS code in the buffer, check it
+ if (buf[cis_offset] == CISTPL_CODE_END) {
+ *inout_cis_offset = cis_offset + 1;
+ return true;
+ }
+ }
+ if (cis_offset + 1 < buffer_size) {
+ cis_offset += buf[cis_offset+1] + 2;
+ } else {
+ break;
+ }
+ }
+ *inout_cis_offset = cis_offset;
+ return false;
+}
+
+sdmmc_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size)
+{
+ sdmmc_err_t ret = SDMMC_OK;
+ uint32_t buf[CIS_GET_MINIMAL_SIZE / sizeof(uint32_t)];
+
+ /* Pointer to size is a mandatory parameter */
+ assert(inout_cis_size);
+
+ /*
+ * CIS region exist in 0x1000~0x17FFF of FUNC 0, get the start address of it
+ * from CCCR register.
+ */
+ uint32_t addr;
+ ret = sdmmc_io_read_bytes(card, 0, 9, &addr, 3);
+ if (ret != SDMMC_OK) return ret;
+ //the sdmmc_io driver reads 4 bytes, the most significant byte is not the address.
+ addr &= 0xffffff;
+ if (addr < 0x1000 || addr > 0x17FFF) {
+ return SDMMC_ERR_INVALID_RESPONSE;
+ }
+
+ /*
+ * To avoid reading too long, take the input value as limitation if
+ * existing.
+ */
+ size_t max_reading = UINT32_MAX;
+ if (*inout_cis_size != 0) {
+ max_reading = *inout_cis_size;
+ }
+
+ /*
+ * Parse the length while reading. If find the end tuple, or reaches the
+ * limitation, read no more and return both the data and the size already
+ * read.
+ */
+ size_t buffer_offset = 0;
+ size_t cur_cis_offset = 0;
+ bool end_tuple_found = false;
+ do {
+ ret = sdmmc_io_read_bytes(card, 0, addr + buffer_offset, &buf, CIS_GET_MINIMAL_SIZE);
+ if (ret != SDMMC_OK) return ret;
+
+ //calculate relative to the beginning of the buffer
+ int offset = cur_cis_offset - buffer_offset;
+ bool finish = check_tuples_in_buffer((uint8_t*) buf, CIS_GET_MINIMAL_SIZE, &offset);
+
+ int remain_size = buffer_size - buffer_offset;
+ int copy_len;
+ if (finish) {
+ copy_len = TSD_MIN(offset, remain_size);
+ end_tuple_found = true;
+ } else {
+ copy_len = TSD_MIN(CIS_GET_MINIMAL_SIZE, remain_size);
+ }
+ if (copy_len > 0) {
+ memcpy(out_buffer + buffer_offset, buf, copy_len);
+ }
+ cur_cis_offset = buffer_offset + offset;
+ buffer_offset += CIS_GET_MINIMAL_SIZE;
+ } while (!end_tuple_found && buffer_offset < max_reading);
+
+ if (end_tuple_found) {
+ *inout_cis_size = cur_cis_offset;
+ if (cur_cis_offset > buffer_size) {
+ return SDMMC_ERR_INVALID_SIZE;
+ } else {
+ return SDMMC_OK;
+ }
+ } else {
+ return SDMMC_ERR_NO_CARD;
+ }
+}
diff --git a/circuitpython/lib/sdmmc/sdmmc_mmc.c b/circuitpython/lib/sdmmc/sdmmc_mmc.c
new file mode 100644
index 0000000..b77d7dd
--- /dev/null
+++ b/circuitpython/lib/sdmmc/sdmmc_mmc.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <unistd.h>
+#include "sdmmc_common.h"
+
+sdmmc_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
+{
+ int card_type;
+ sdmmc_err_t err = SDMMC_OK;
+
+ uint32_t ext_csd[EXT_CSD_MMC_SIZE / sizeof(uint32_t)];
+
+ uint32_t sectors = 0;
+
+ ESP_LOGD(TAG, "MMC version: %d", card->csd.mmc_ver);
+ if (card->csd.mmc_ver < MMC_CSD_MMCVER_4_0) {
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+
+ /* read EXT_CSD */
+ err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: send_ext_csd_data error 0x%x", __func__, err);
+ return err;
+ }
+ card_type = ext_csd[EXT_CSD_CARD_TYPE / sizeof(uint32_t)];
+
+ card->is_ddr = 0;
+ if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
+ card->max_freq_khz = SDMMC_FREQ_52M;
+ if ((card->host.flags & SDMMC_HOST_FLAG_DDR) &&
+ card->host.max_freq_khz >= SDMMC_FREQ_26M &&
+ card->host.get_bus_width(card->host.slot) == 4) {
+ ESP_LOGD(TAG, "card and host support DDR mode");
+ card->is_ddr = 1;
+ }
+ } else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
+ card->max_freq_khz = SDMMC_FREQ_52M;
+ } else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
+ card->max_freq_khz = SDMMC_FREQ_26M;
+ } else {
+ ESP_LOGW(TAG, "%s: unknown CARD_TYPE 0x%x", __func__, card_type);
+ }
+ /* For MMC cards, use speed value from EXT_CSD */
+ card->csd.tr_speed = card->max_freq_khz * 1000;
+ ESP_LOGD(TAG, "MMC card type %d, max_freq_khz=%d, is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr);
+ card->max_freq_khz = TSD_MIN(card->max_freq_khz, card->host.max_freq_khz);
+
+ if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
+ card->ext_csd.power_class = ext_csd[((card->max_freq_khz > SDMMC_FREQ_26M) ?
+ EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360) / sizeof(uint32_t)] >> 4;
+ card->log_bus_width = 3;
+ } else if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
+ card->ext_csd.power_class = ext_csd[((card->max_freq_khz > SDMMC_FREQ_26M) ?
+ EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360) / sizeof(uint32_t)] & 0x0f;
+ card->log_bus_width = 2;
+ } else {
+ card->ext_csd.power_class = 0; //card must be able to do full rate at powerclass 0 in 1-bit mode
+ card->log_bus_width = 0;
+ }
+
+ sectors = ext_csd[EXT_CSD_SEC_COUNT / sizeof(uint32_t)];
+
+ if (sectors > (2u * 1024 * 1024 * 1024) / 512) {
+ card->csd.capacity = sectors;
+ }
+
+ return err;
+}
+
+sdmmc_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ if (card->ext_csd.power_class != 0) {
+ err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_CLASS, card->ext_csd.power_class);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: can't change power class (%d bit), 0x%x"
+ , __func__, card->ext_csd.power_class, err);
+ return err;
+ }
+ }
+
+ if (card->log_bus_width > 0) {
+ int csd_bus_width_value = EXT_CSD_BUS_WIDTH_1;
+ int bus_width = 1;
+ if (card->log_bus_width == 2) {
+ if (card->is_ddr) {
+ csd_bus_width_value = EXT_CSD_BUS_WIDTH_4_DDR;
+ } else {
+ csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
+ }
+ bus_width = 4;
+ } else if (card->log_bus_width == 3) {
+ if (card->is_ddr) {
+ csd_bus_width_value = EXT_CSD_BUS_WIDTH_8_DDR;
+ } else {
+ csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
+ }
+ bus_width = 8;
+ }
+ err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH, csd_bus_width_value);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: can't change bus width (%d bit), 0x%x",
+ __func__, bus_width, err);
+ (void) bus_width;
+ return err;
+ }
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ if (card->max_freq_khz > SDMMC_FREQ_26M) {
+ /* switch to high speed timing */
+ err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: mmc_switch EXT_CSD_HS_TIMING_HS error 0x%x",
+ __func__, err);
+ return err;
+ }
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid)
+{
+ if (mmc_ver == MMC_CSD_MMCVER_1_0 ||
+ mmc_ver == MMC_CSD_MMCVER_1_4) {
+ out_cid->mfg_id = MMC_CID_MID_V1(resp);
+ out_cid->oem_id = 0;
+ MMC_CID_PNM_V1_CPY(resp, out_cid->name);
+ out_cid->revision = MMC_CID_REV_V1(resp);
+ out_cid->serial = MMC_CID_PSN_V1(resp);
+ out_cid->date = MMC_CID_MDT_V1(resp);
+ } else if (mmc_ver == MMC_CSD_MMCVER_2_0 ||
+ mmc_ver == MMC_CSD_MMCVER_3_1 ||
+ mmc_ver == MMC_CSD_MMCVER_4_0) {
+ out_cid->mfg_id = MMC_CID_MID_V2(resp);
+ out_cid->oem_id = MMC_CID_OID_V2(resp);
+ MMC_CID_PNM_V1_CPY(resp, out_cid->name);
+ out_cid->revision = 0;
+ out_cid->serial = MMC_CID_PSN_V1(resp);
+ out_cid->date = 0;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
+{
+ out_csd->csd_ver = MMC_CSD_CSDVER(response);
+ if (out_csd->csd_ver == MMC_CSD_CSDVER_1_0 ||
+ out_csd->csd_ver == MMC_CSD_CSDVER_2_0 ||
+ out_csd->csd_ver == MMC_CSD_CSDVER_EXT_CSD) {
+ out_csd->mmc_ver = MMC_CSD_MMCVER(response);
+ out_csd->capacity = MMC_CSD_CAPACITY(response);
+ out_csd->read_block_len = MMC_CSD_READ_BL_LEN(response);
+ } else {
+ ESP_LOGE(TAG, "unknown MMC CSD structure version 0x%x\n", out_csd->csd_ver);
+ return 1;
+ }
+ int read_bl_size = 1 << out_csd->read_block_len;
+ out_csd->sector_size = TSD_MIN(read_bl_size, 512);
+ if (out_csd->sector_size < read_bl_size) {
+ out_csd->capacity *= read_bl_size / out_csd->sector_size;
+ }
+ /* tr_speed will be determined when reading CXD */
+ out_csd->tr_speed = 0;
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen)
+{
+ sdmmc_command_t cmd = {
+ .data = out_data,
+ .datalen = datalen,
+ .blklen = datalen,
+ .opcode = MMC_SEND_EXT_CSD,
+ .arg = 0,
+ .flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
+ };
+ return sdmmc_send_cmd(card, &cmd);
+}
+
+sdmmc_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value)
+{
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SWITCH,
+ .arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
+ .flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY,
+ };
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err == SDMMC_OK) {
+ //check response bit to see that switch was accepted
+ if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR)
+ err = SDMMC_ERR_INVALID_RESPONSE;
+ }
+
+ return err;
+}
+
+sdmmc_err_t sdmmc_init_mmc_check_csd(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ assert(card->is_mem == 1);
+ assert(card->rca != 0);
+ //The card will not respond to send_csd command in the transfer state.
+ //Deselect it first.
+ err = sdmmc_send_cmd_select_card(card, 0);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
+ return err;
+ }
+
+ sdmmc_csd_t csd;
+ /* Get the contents of CSD register to verify the communication over CMD line
+ is OK. */
+ err = sdmmc_send_cmd_send_csd(card, &csd);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
+ return err;
+ }
+
+ //Select the card again
+ err = sdmmc_send_cmd_select_card(card, card->rca);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
+ return err;
+ }
+ return SDMMC_OK;
+}
diff --git a/circuitpython/lib/sdmmc/sdmmc_sd.c b/circuitpython/lib/sdmmc/sdmmc_sd.c
new file mode 100644
index 0000000..b4391f2
--- /dev/null
+++ b/circuitpython/lib/sdmmc/sdmmc_sd.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "sdmmc_common.h"
+
+sdmmc_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card)
+{
+ /* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards.
+ * SD v1 and non-SD cards will not respond to this command.
+ */
+ uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
+ sdmmc_err_t err = sdmmc_send_cmd_send_if_cond(card, host_ocr);
+ if (err == SDMMC_OK) {
+ ESP_LOGD(TAG, "SDHC/SDXC card");
+ host_ocr |= SD_OCR_SDHC_CAP;
+ } else if (err == SDMMC_ERR_TIMEOUT) {
+ ESP_LOGD(TAG, "CMD8 timeout; not an SD v2.00 card");
+ } else if (host_is_spi(card) && err == SDMMC_ERR_NOT_SUPPORTED) {
+ ESP_LOGD(TAG, "CMD8 rejected; not an SD v2.00 card");
+ } else {
+ ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
+ return err;
+ }
+ card->ocr = host_ocr;
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card)
+{
+ /* SDSC cards support configurable data block lengths.
+ * We don't use this feature and set the block length to 512 bytes,
+ * same as the block length for SDHC cards.
+ */
+ if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
+ sdmmc_err_t err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
+ return err;
+ }
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_sd_scr(sdmmc_card_t* card)
+{
+ sdmmc_err_t err;
+ /* Get the contents of SCR register: bus width and the version of SD spec
+ * supported by the card.
+ * In SD mode, this is the first command which uses D0 line. Errors at
+ * this step usually indicate connection issue or lack of pull-up resistor.
+ */
+ err = sdmmc_send_cmd_send_scr(card, &card->scr);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
+ return err;
+ }
+
+ if ((card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)
+ && (card->host.flags & SDMMC_HOST_FLAG_4BIT)) {
+ card->log_bus_width = 2;
+ } else {
+ card->log_bus_width = 0;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card)
+{
+ int width = 1;
+ if (card->log_bus_width == 2) {
+ width = 4;
+ } else if (card->log_bus_width == 3) {
+ width = 8;
+ }
+ sdmmc_err_t err = sdmmc_send_cmd_set_bus_width(card, width);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "set_bus_width failed (0x%x)", err);
+ return err;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card)
+{
+ /* Wait for the card to be ready for data transfers */
+ uint32_t status = 0;
+ uint32_t count = 0;
+ while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
+ // TODO: add some timeout here
+ sdmmc_err_t err = sdmmc_send_cmd_send_status(card, &status);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ if (++count % 16 == 0) {
+ ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
+ }
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
+ uint32_t mode, uint32_t group, uint32_t function,
+ sdmmc_switch_func_rsp_t* resp)
+{
+ if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
+ ((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+
+ if (group == 0 ||
+ group > SD_SFUNC_GROUP_MAX ||
+ function > SD_SFUNC_FUNC_MAX) {
+ return SDMMC_ERR_INVALID_ARG;
+ }
+
+ if (mode > 1) {
+ return SDMMC_ERR_INVALID_ARG;
+ }
+
+ uint32_t group_shift = (group - 1) << 2;
+ /* all functions which should not be affected are set to 0xf (no change) */
+ uint32_t other_func_mask = (0x00ffffff & ~(0xf << group_shift));
+ uint32_t func_val = (function << group_shift) | other_func_mask;
+
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SWITCH,
+ .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
+ .blklen = sizeof(sdmmc_switch_func_rsp_t),
+ .data = resp->data,
+ .datalen = sizeof(sdmmc_switch_func_rsp_t),
+ .arg = (!!mode << 31) | func_val
+ };
+
+ sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
+ return err;
+ }
+ sdmmc_flip_byte_order(resp->data, sizeof(sdmmc_switch_func_rsp_t));
+ uint32_t resp_ver = SD_SFUNC_VER(resp->data);
+ if (resp_ver == 0) {
+ /* busy response is never sent */
+ } else if (resp_ver == 1) {
+ if (SD_SFUNC_BUSY(resp->data, group) & (1 << function)) {
+ ESP_LOGD(TAG, "%s: response indicates function %d:%d is busy",
+ __func__, group, function);
+ return SDMMC_ERR_BUSY;
+ }
+ } else {
+ ESP_LOGD(TAG, "%s: got an invalid version of SWITCH_FUNC response: 0x%02x",
+ __func__, resp_ver);
+ return SDMMC_ERR_INVALID_RESPONSE;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card)
+{
+ /* This will determine if the card supports SWITCH_FUNC command,
+ * and high speed mode. If the cards supports both, this will enable
+ * high speed mode at the card side.
+ */
+ if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
+ ((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ sdmmc_switch_func_rsp_t response;
+
+ sdmmc_err_t err = sdmmc_send_cmd_switch_func(card, 0, SD_ACCESS_MODE, 0, &response);
+ if (err != SDMMC_OK) {
+ ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, err);
+ return err;
+ }
+ uint32_t supported_mask = SD_SFUNC_SUPPORTED(response.data, 1);
+ if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) {
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, &response);
+ if (err != SDMMC_OK) {
+ ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
+ return err;
+ }
+
+ return err;
+}
+
+sdmmc_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
+{
+ /* All cards should support at least default speed */
+ card->max_freq_khz = SDMMC_FREQ_DEFAULT;
+ if (card->host.max_freq_khz <= card->max_freq_khz) {
+ /* Host is configured to use low frequency, don't attempt to switch */
+ card->max_freq_khz = card->host.max_freq_khz;
+ return SDMMC_OK;
+ }
+
+ /* Try to enabled HS mode */
+ sdmmc_err_t err = sdmmc_enable_hs_mode(card);
+ if (err != SDMMC_OK) {
+ return err;
+ }
+ /* HS mode has been enabled on the card.
+ * Read CSD again, it should now indicate that the card supports
+ * 50MHz clock.
+ * Since SEND_CSD is allowed only in standby mode, and the card is currently in data transfer
+ * mode, deselect the card first, then get the CSD, then select the card again. This step is
+ * not required in SPI mode, since CMD7 (select_card) is not supported.
+ */
+ const bool is_spi = host_is_spi(card);
+ if (!is_spi) {
+ err = sdmmc_send_cmd_select_card(card, 0);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: select_card (1) returned 0x%x", __func__, err);
+ return err;
+ }
+ }
+ err = sdmmc_send_cmd_send_csd(card, &card->csd);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
+ return err;
+ }
+ if (!is_spi) {
+ err = sdmmc_send_cmd_select_card(card, card->rca);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err);
+ return err;
+ }
+ }
+
+ if (card->csd.tr_speed != 50000000) {
+ ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed);
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+
+ card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_check_scr(sdmmc_card_t* card)
+{
+ /* If frequency switch has been performed, read SCR register one more time
+ * and compare the result with the previous one. Use this simple check as
+ * an indicator of potential signal integrity issues.
+ */
+ sdmmc_scr_t scr_tmp;
+ sdmmc_err_t err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err);
+ return err;
+ }
+ if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
+ ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
+ return SDMMC_ERR_INVALID_RESPONSE;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_init_spi_crc(sdmmc_card_t* card)
+{
+ /* In SD mode, CRC checks of data transfers are mandatory and performed
+ * by the hardware. In SPI mode, CRC16 of data transfers is optional and
+ * needs to be enabled.
+ */
+ assert(host_is_spi(card));
+ sdmmc_err_t err = sdmmc_send_cmd_crc_on_off(card, true);
+ if (err != SDMMC_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
+ return err;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
+{
+ out_cid->mfg_id = SD_CID_MID(resp);
+ out_cid->oem_id = SD_CID_OID(resp);
+ SD_CID_PNM_CPY(resp, out_cid->name);
+ out_cid->revision = SD_CID_REV(resp);
+ out_cid->serial = SD_CID_PSN(resp);
+ out_cid->date = SD_CID_MDT(resp);
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
+{
+ out_csd->csd_ver = SD_CSD_CSDVER(response);
+ switch (out_csd->csd_ver) {
+ case SD_CSD_CSDVER_2_0:
+ out_csd->capacity = SD_CSD_V2_CAPACITY(response);
+ out_csd->read_block_len = SD_CSD_V2_BL_LEN;
+ break;
+ case SD_CSD_CSDVER_1_0:
+ out_csd->capacity = SD_CSD_CAPACITY(response);
+ out_csd->read_block_len = SD_CSD_READ_BL_LEN(response);
+ break;
+ default:
+ ESP_LOGE(TAG, "unknown SD CSD structure version 0x%x", out_csd->csd_ver);
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ out_csd->card_command_class = SD_CSD_CCC(response);
+ int read_bl_size = 1 << out_csd->read_block_len;
+ out_csd->sector_size = TSD_MIN(read_bl_size, 512);
+ if (out_csd->sector_size < read_bl_size) {
+ out_csd->capacity *= read_bl_size / out_csd->sector_size;
+ }
+ int speed = SD_CSD_SPEED(response);
+ if (speed == SD_CSD_SPEED_50_MHZ) {
+ out_csd->tr_speed = 50000000;
+ } else {
+ out_csd->tr_speed = 25000000;
+ }
+ return SDMMC_OK;
+}
+
+sdmmc_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
+{
+ sdmmc_response_t resp = { 0 };
+ resp[1] = __builtin_bswap32(raw_scr[0]);
+ resp[0] = __builtin_bswap32(raw_scr[1]);
+ int ver = SCR_STRUCTURE(resp);
+ if (ver != 0) {
+ return SDMMC_ERR_NOT_SUPPORTED;
+ }
+ out_scr->sd_spec = SCR_SD_SPEC(resp);
+ out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp);
+ return SDMMC_OK;
+}
diff --git a/circuitpython/lib/sdmmc/test/CMakeLists.txt b/circuitpython/lib/sdmmc/test/CMakeLists.txt
new file mode 100644
index 0000000..d6d5859
--- /dev/null
+++ b/circuitpython/lib/sdmmc/test/CMakeLists.txt
@@ -0,0 +1,4 @@
+idf_component_register(SRC_DIRS "."
+ PRIV_INCLUDE_DIRS "."
+ PRIV_REQUIRES cmock sdmmc
+ )
diff --git a/circuitpython/lib/sdmmc/test/component.mk b/circuitpython/lib/sdmmc/test/component.mk
new file mode 100644
index 0000000..ce464a2
--- /dev/null
+++ b/circuitpython/lib/sdmmc/test/component.mk
@@ -0,0 +1 @@
+COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
diff --git a/circuitpython/lib/sdmmc/test/test_sd.c b/circuitpython/lib/sdmmc/test/test_sd.c
new file mode 100644
index 0000000..fd7051c
--- /dev/null
+++ b/circuitpython/lib/sdmmc/test/test_sd.c
@@ -0,0 +1,584 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include "unity.h"
+#include "driver/gpio.h"
+#include "soc/soc_caps.h"
+#if SOC_SDMMC_HOST_SUPPORTED
+#include "driver/sdmmc_host.h"
+#endif
+#include "driver/sdspi_host.h"
+#include "driver/sdmmc_defs.h"
+#include "sdmmc_cmd.h"
+#include "esp_log.h"
+#include "esp_heap_caps.h"
+#include "esp_rom_gpio.h"
+
+// Can't test eMMC (slot 0) and PSRAM together
+#ifndef CONFIG_SPIRAM
+#define WITH_EMMC_TEST
+#endif
+
+/* power supply enable pin */
+#define SD_TEST_BOARD_VSEL_EN_GPIO 27
+
+/* power supply voltage select pin */
+#define SD_TEST_BOARD_VSEL_GPIO 26
+#define SD_TEST_BOARD_VSEL_3V3 1
+#define SD_TEST_BOARD_VSEL_1V8 0
+
+#define TEST_SDSPI_DMACHAN 1
+
+/* time to wait for reset / power-on */
+#define SD_TEST_BOARD_PWR_RST_DELAY_MS 5
+#define SD_TEST_BOARD_PWR_ON_DELAY_MS 50
+
+/* gpio which is not connected to actual CD pin, used to simulate CD behavior */
+#define CD_WP_TEST_GPIO 18
+
+
+__attribute__((unused)) static void sd_test_board_power_on(void)
+{
+ gpio_set_direction(SD_TEST_BOARD_VSEL_GPIO, GPIO_MODE_OUTPUT);
+ gpio_set_level(SD_TEST_BOARD_VSEL_GPIO, SD_TEST_BOARD_VSEL_3V3);
+ gpio_set_direction(SD_TEST_BOARD_VSEL_EN_GPIO, GPIO_MODE_OUTPUT);
+ gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 0);
+ usleep(SD_TEST_BOARD_PWR_RST_DELAY_MS * 1000);
+ gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 1);
+ usleep(SD_TEST_BOARD_PWR_ON_DELAY_MS * 1000);
+}
+
+__attribute__((unused)) static void sd_test_board_power_off(void)
+{
+ gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 0);
+ gpio_set_direction(SD_TEST_BOARD_VSEL_GPIO, GPIO_MODE_INPUT);
+ gpio_set_level(SD_TEST_BOARD_VSEL_GPIO, 0);
+ gpio_set_direction(SD_TEST_BOARD_VSEL_EN_GPIO, GPIO_MODE_INPUT);
+}
+
+TEST_CASE("MMC_RSP_BITS", "[sd]")
+{
+ uint32_t data[2] = { 0x01234567, 0x89abcdef };
+ TEST_ASSERT_EQUAL_HEX32(0x7, MMC_RSP_BITS(data, 0, 4));
+ TEST_ASSERT_EQUAL_HEX32(0x567, MMC_RSP_BITS(data, 0, 12));
+ TEST_ASSERT_EQUAL_HEX32(0xf0, MMC_RSP_BITS(data, 28, 8));
+ TEST_ASSERT_EQUAL_HEX32(0x3, MMC_RSP_BITS(data, 1, 3));
+ TEST_ASSERT_EQUAL_HEX32(0x11, MMC_RSP_BITS(data, 59, 5));
+}
+
+#if SOC_SDMMC_HOST_SUPPORTED
+
+static void probe_sd(int slot, int width, int freq_khz, int ddr)
+{
+ sd_test_board_power_on();
+ sdmmc_host_t config = SDMMC_HOST_DEFAULT();
+ config.slot = slot;
+ config.max_freq_khz = freq_khz;
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+ if (width == 1) {
+ config.flags = SDMMC_HOST_FLAG_1BIT;
+ slot_config.width = 1;
+ } else if (width == 4) {
+ config.flags &= ~SDMMC_HOST_FLAG_8BIT;
+ slot_config.width = 4;
+ } else {
+ assert(!ddr && "host driver does not support 8-line DDR mode yet");
+ }
+ if (!ddr) {
+ config.flags &= ~SDMMC_HOST_FLAG_DDR;
+ }
+ TEST_ESP_OK(sdmmc_host_init());
+ TEST_ESP_OK(sdmmc_host_init_slot(slot, &slot_config));
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+ TEST_ESP_OK(sdmmc_card_init(&config, card));
+ sdmmc_card_print_info(stdout, card);
+ uint8_t* buffer = heap_caps_malloc(512, MALLOC_CAP_DMA);
+ TEST_ESP_OK(sdmmc_read_sectors(card, buffer, 0, 1));
+ free(buffer);
+ TEST_ESP_OK(sdmmc_host_deinit());
+ free(card);
+ sd_test_board_power_off();
+}
+
+TEST_CASE("probe SD, slot 1, 4-bit", "[sd][test_env=UT_T1_SDMODE]")
+{
+ probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_PROBING, 0);
+ probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_DEFAULT, 0);
+ probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, 0);
+}
+
+TEST_CASE("probe SD, slot 1, 1-bit", "[sd][test_env=UT_T1_SDMODE]")
+{
+ probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_PROBING, 0);
+ probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_DEFAULT, 0);
+ probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_HIGHSPEED, 0);
+}
+
+#ifdef WITH_EMMC_TEST
+TEST_CASE("probe eMMC, slot 0, 4-bit, DDR", "[sd][test_env=EMMC]")
+{
+ probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, 1);
+}
+
+TEST_CASE("probe eMMC, slot 0, 8-bit", "[sd][test_env=EMMC]")
+{
+ probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_PROBING, 0);
+ probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_DEFAULT, 0);
+ probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_HIGHSPEED, 0);
+}
+#endif // WITH_EMMC_TEST
+
+TEST_CASE("probe SD, slot 0, 4-bit", "[sd][test_env=UT_T1_SDCARD][ignore]")
+{
+ probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_PROBING, 0);
+ probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_DEFAULT, 0);
+ probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, 0);
+}
+
+TEST_CASE("probe SD, slot 0, 1-bit", "[sd][test_env=UT_T1_SDCARD][ignore]")
+{
+ probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_PROBING, 0);
+ probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_DEFAULT, 0);
+ probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_HIGHSPEED, 0);
+}
+
+#endif
+
+#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
+//No runners
+static void test_sdspi_init_bus(spi_host_device_t host, int mosi_pin, int miso_pin, int clk_pin, int dma_chan)
+{
+ spi_bus_config_t bus_config = {
+ .mosi_io_num = mosi_pin,
+ .miso_io_num = miso_pin,
+ .sclk_io_num = clk_pin,
+ .quadwp_io_num = -1,
+ .quadhd_io_num = -1,
+ };
+ esp_err_t err = spi_bus_initialize(host, &bus_config, dma_chan);
+ TEST_ESP_OK(err);
+}
+
+static void test_sdspi_deinit_bus(spi_host_device_t host)
+{
+ esp_err_t err = spi_bus_free(host);
+ TEST_ESP_OK(err);
+}
+
+static void probe_core(int slot)
+{
+ sdmmc_host_t config = SDSPI_HOST_DEFAULT();
+ config.slot = slot;
+
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+
+ TEST_ESP_OK(sdmmc_card_init(&config, card));
+ sdmmc_card_print_info(stdout, card);
+ free(card);
+}
+
+static void probe_spi(int freq_khz, int pin_miso, int pin_mosi, int pin_sck, int pin_cs)
+{
+ sd_test_board_power_on();
+
+ sdspi_dev_handle_t handle;
+ sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
+ dev_config.gpio_cs = pin_cs;
+ test_sdspi_init_bus(dev_config.host_id, pin_mosi, pin_miso, pin_sck, TEST_SDSPI_DMACHAN);
+ TEST_ESP_OK(sdspi_host_init());
+ TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
+
+ probe_core(handle);
+
+ TEST_ESP_OK(sdspi_host_deinit());
+ test_sdspi_deinit_bus(dev_config.host_id);
+ sd_test_board_power_off();
+}
+
+static void probe_spi_legacy(int freq_khz, int pin_miso, int pin_mosi, int pin_sck, int pin_cs)
+{
+ sd_test_board_power_on();
+ sdmmc_host_t config = SDSPI_HOST_DEFAULT();
+ sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
+ slot_config.gpio_miso = pin_miso;
+ slot_config.gpio_mosi = pin_mosi;
+ slot_config.gpio_sck = pin_sck;
+ slot_config.gpio_cs = pin_cs;
+
+ TEST_ESP_OK(sdspi_host_init());
+ TEST_ESP_OK(sdspi_host_init_slot(config.slot, &slot_config));
+
+ probe_core(config.slot);
+
+ TEST_ESP_OK(sdspi_host_deinit());
+ sd_test_board_power_off();
+}
+
+TEST_CASE("probe SD in SPI mode, slot 1", "[sd][test_env=UT_T1_SPIMODE]")
+{
+ probe_spi(SDMMC_FREQ_DEFAULT, 2, 15, 14, 13);
+ probe_spi_legacy(SDMMC_FREQ_DEFAULT, 2, 15, 14, 13);
+}
+
+TEST_CASE("probe SD in SPI mode, slot 0", "[sd][test_env=UT_T1_SDCARD][ignore]")
+{
+ probe_spi(SDMMC_FREQ_DEFAULT, 7, 11, 6, 10);
+ probe_spi_legacy(SDMMC_FREQ_DEFAULT, 7, 11, 6, 10);
+}
+
+#endif //DISABLED(ESP32S2)
+
+// Fill buffer pointed to by 'dst' with 'count' 32-bit ints generated
+// from 'rand' with the starting value of 'seed'
+__attribute__((unused)) static void fill_buffer(uint32_t seed, uint8_t* dst, size_t count) {
+ srand(seed);
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t val = rand();
+ memcpy(dst + i * sizeof(uint32_t), &val, sizeof(val));
+ }
+}
+
+// Check if the buffer pointed to by 'dst' contains 'count' 32-bit
+// ints generated from 'rand' with the starting value of 'seed'
+__attribute__((unused)) static void check_buffer(uint32_t seed, const uint8_t* src, size_t count) {
+ srand(seed);
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t val;
+ memcpy(&val, src + i * sizeof(uint32_t), sizeof(val));
+ TEST_ASSERT_EQUAL_HEX32(rand(), val);
+ }
+}
+
+__attribute__((unused)) static void do_single_write_read_test(sdmmc_card_t* card,
+ size_t start_block, size_t block_count, size_t alignment)
+{
+ size_t block_size = card->csd.sector_size;
+ size_t total_size = block_size * block_count;
+ printf(" %8d | %3d | %d | %4.1f ", start_block, block_count, alignment, total_size / 1024.0f);
+
+ uint32_t* buffer = heap_caps_malloc(total_size + 4, MALLOC_CAP_DMA);
+ size_t offset = alignment % 4;
+ uint8_t* c_buffer = (uint8_t*) buffer + offset;
+ fill_buffer(start_block, c_buffer, total_size / sizeof(buffer[0]));
+
+ struct timeval t_start_wr;
+ gettimeofday(&t_start_wr, NULL);
+ TEST_ESP_OK(sdmmc_write_sectors(card, c_buffer, start_block, block_count));
+ struct timeval t_stop_wr;
+ gettimeofday(&t_stop_wr, NULL);
+ float time_wr = 1e3f * (t_stop_wr.tv_sec - t_start_wr.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_wr.tv_usec);
+
+ memset(buffer, 0xbb, total_size + 4);
+
+ struct timeval t_start_rd;
+ gettimeofday(&t_start_rd, NULL);
+ TEST_ESP_OK(sdmmc_read_sectors(card, c_buffer, start_block, block_count));
+ struct timeval t_stop_rd;
+ gettimeofday(&t_stop_rd, NULL);
+ float time_rd = 1e3f * (t_stop_rd.tv_sec - t_start_rd.tv_sec) + 1e-3f * (t_stop_rd.tv_usec - t_start_rd.tv_usec);
+
+ printf(" | %6.2f | %5.2f | %6.2f | %5.2f\n",
+ time_wr, total_size / (time_wr / 1000) / (1024 * 1024),
+ time_rd, total_size / (time_rd / 1000) / (1024 * 1024));
+ check_buffer(start_block, c_buffer, total_size / sizeof(buffer[0]));
+ free(buffer);
+}
+
+__attribute__((unused)) static void read_write_test(sdmmc_card_t* card)
+{
+ sdmmc_card_print_info(stdout, card);
+ printf(" sector | count | align | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n");
+ do_single_write_read_test(card, 0, 1, 4);
+ do_single_write_read_test(card, 0, 4, 4);
+ do_single_write_read_test(card, 1, 16, 4);
+ do_single_write_read_test(card, 16, 32, 4);
+ do_single_write_read_test(card, 48, 64, 4);
+ do_single_write_read_test(card, 128, 128, 4);
+ do_single_write_read_test(card, card->csd.capacity - 64, 32, 4);
+ do_single_write_read_test(card, card->csd.capacity - 64, 64, 4);
+ do_single_write_read_test(card, card->csd.capacity - 8, 1, 4);
+ do_single_write_read_test(card, card->csd.capacity/2, 1, 4);
+ do_single_write_read_test(card, card->csd.capacity/2, 4, 4);
+ do_single_write_read_test(card, card->csd.capacity/2, 8, 4);
+ do_single_write_read_test(card, card->csd.capacity/2, 16, 4);
+ do_single_write_read_test(card, card->csd.capacity/2, 32, 4);
+ do_single_write_read_test(card, card->csd.capacity/2, 64, 4);
+ do_single_write_read_test(card, card->csd.capacity/2, 128, 4);
+ do_single_write_read_test(card, card->csd.capacity/2, 1, 1);
+ do_single_write_read_test(card, card->csd.capacity/2, 8, 1);
+ do_single_write_read_test(card, card->csd.capacity/2, 128, 1);
+}
+
+#if SOC_SDMMC_HOST_SUPPORTED
+void test_sd_rw_blocks(int slot, int width)
+{
+ sdmmc_host_t config = SDMMC_HOST_DEFAULT();
+ config.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
+ config.slot = slot;
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+ if (width != 0) {
+ slot_config.width = width;
+ }
+ if (slot_config.width == 8) {
+ config.flags &= ~SDMMC_HOST_FLAG_DDR;
+ }
+ TEST_ESP_OK(sdmmc_host_init());
+ TEST_ESP_OK(sdmmc_host_init_slot(slot, &slot_config));
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+ TEST_ESP_OK(sdmmc_card_init(&config, card));
+ read_write_test(card);
+ free(card);
+ TEST_ESP_OK(sdmmc_host_deinit());
+}
+
+TEST_CASE("SDMMC read/write test (SD slot 1)", "[sd][test_env=UT_T1_SDMODE]")
+{
+ sd_test_board_power_on();
+ test_sd_rw_blocks(1, 4);
+ sd_test_board_power_off();
+}
+
+#ifdef WITH_EMMC_TEST
+TEST_CASE("SDMMC read/write test (eMMC slot 0, 4 line DDR)", "[sd][test_env=EMMC]")
+{
+ sd_test_board_power_on();
+ test_sd_rw_blocks(0, 4);
+ sd_test_board_power_off();
+}
+
+TEST_CASE("SDMMC read/write test (eMMC slot 0, 8 line)", "[sd][test_env=EMMC]")
+{
+ sd_test_board_power_on();
+ test_sd_rw_blocks(0, 8);
+ sd_test_board_power_off();
+}
+#endif // WITH_EMMC_TEST
+#endif // SDMMC_HOST_SUPPORTED
+
+#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
+//No runners
+TEST_CASE("SDMMC read/write test (SD slot 1, in SPI mode)", "[sdspi][test_env=UT_T1_SPIMODE]")
+{
+ sd_test_board_power_on();
+
+ sdspi_dev_handle_t handle;
+ sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
+ test_sdspi_init_bus(dev_config.host_id, GPIO_NUM_15, GPIO_NUM_2, GPIO_NUM_14, TEST_SDSPI_DMACHAN);
+ TEST_ESP_OK(sdspi_host_init());
+ TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
+
+ sdmmc_host_t config = SDSPI_HOST_DEFAULT();
+ config.slot = handle;
+ // This test can only run under 20MHz on ESP32, because the runner connects the card to
+ // non-IOMUX pins of HSPI.
+
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+ TEST_ESP_OK(sdmmc_card_init(&config, card));
+ read_write_test(card);
+ TEST_ESP_OK(sdspi_host_deinit());
+ free(card);
+ test_sdspi_deinit_bus(dev_config.host_id);
+ sd_test_board_power_off();
+}
+#endif //DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
+
+#if SOC_SDMMC_HOST_SUPPORTED
+TEST_CASE("reads and writes with an unaligned buffer", "[sd][test_env=UT_T1_SDMODE]")
+{
+ sd_test_board_power_on();
+ sdmmc_host_t config = SDMMC_HOST_DEFAULT();
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+ TEST_ESP_OK(sdmmc_host_init());
+
+ TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+ TEST_ESP_OK(sdmmc_card_init(&config, card));
+
+ const size_t buffer_size = 4096;
+ const size_t block_count = buffer_size / 512;
+ const size_t extra = 4;
+ uint8_t* buffer = heap_caps_malloc(buffer_size + extra, MALLOC_CAP_DMA);
+
+ // Check read behavior: do aligned write, then unaligned read
+ const uint32_t seed = 0x89abcdef;
+ fill_buffer(seed, buffer, buffer_size / sizeof(uint32_t));
+ TEST_ESP_OK(sdmmc_write_sectors(card, buffer, 0, block_count));
+ memset(buffer, 0xcc, buffer_size + extra);
+ TEST_ESP_OK(sdmmc_read_sectors(card, buffer + 1, 0, block_count));
+ check_buffer(seed, buffer + 1, buffer_size / sizeof(uint32_t));
+
+ // Check write behavior: do unaligned write, then aligned read
+ fill_buffer(seed, buffer + 1, buffer_size / sizeof(uint32_t));
+ TEST_ESP_OK(sdmmc_write_sectors(card, buffer + 1, 8, block_count));
+ memset(buffer, 0xcc, buffer_size + extra);
+ TEST_ESP_OK(sdmmc_read_sectors(card, buffer, 8, block_count));
+ check_buffer(seed, buffer, buffer_size / sizeof(uint32_t));
+
+ free(buffer);
+ free(card);
+ TEST_ESP_OK(sdmmc_host_deinit());
+ sd_test_board_power_off();
+}
+#endif
+
+__attribute__((unused)) static void test_cd_input(int gpio_cd_num, const sdmmc_host_t* config)
+{
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+
+ // SDMMC host should have configured CD as input.
+ // Enable output as well (not using the driver, to avoid touching input
+ // enable bits).
+ esp_rom_gpio_connect_out_signal(gpio_cd_num, SIG_GPIO_OUT_IDX, false, false);
+ REG_WRITE(GPIO_ENABLE_W1TS_REG, BIT(gpio_cd_num));
+
+ // Check that card initialization fails if CD is high
+ REG_WRITE(GPIO_OUT_W1TS_REG, BIT(gpio_cd_num));
+ usleep(1000);
+ TEST_ESP_ERR(ESP_ERR_NOT_FOUND, sdmmc_card_init(config, card));
+
+ // Check that card initialization succeeds if CD is low
+ REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_cd_num));
+ usleep(1000);
+ TEST_ESP_OK(sdmmc_card_init(config, card));
+
+ free(card);
+}
+
+#if SOC_SDMMC_HOST_SUPPORTED
+TEST_CASE("CD input works in SD mode", "[sd][test_env=UT_T1_SDMODE]")
+{
+ sd_test_board_power_on();
+ sdmmc_host_t config = SDMMC_HOST_DEFAULT();
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+ slot_config.gpio_cd = CD_WP_TEST_GPIO;
+ TEST_ESP_OK(sdmmc_host_init());
+ TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
+
+ test_cd_input(CD_WP_TEST_GPIO, &config);
+
+ TEST_ESP_OK(sdmmc_host_deinit());
+ sd_test_board_power_off();
+}
+#endif
+
+#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
+//No runners
+TEST_CASE("CD input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
+{
+ sd_test_board_power_on();
+
+ sdspi_dev_handle_t handle;
+ sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
+ dev_config.gpio_cd = CD_WP_TEST_GPIO;
+ test_sdspi_init_bus(dev_config.host_id, GPIO_NUM_15, GPIO_NUM_2, GPIO_NUM_14, TEST_SDSPI_DMACHAN);
+ TEST_ESP_OK(sdspi_host_init());
+ TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
+
+ sdmmc_host_t config = SDSPI_HOST_DEFAULT();
+ config.slot = handle;
+
+ test_cd_input(CD_WP_TEST_GPIO, &config);
+
+ TEST_ESP_OK(sdspi_host_deinit());
+ test_sdspi_deinit_bus(dev_config.host_id);
+ sd_test_board_power_off();
+}
+#endif //DISABLED_FOR_TARGETS(ESP32S2)
+
+__attribute__((unused)) static void test_wp_input(int gpio_wp_num, const sdmmc_host_t* config)
+{
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+
+ // SDMMC host should have configured WP as input.
+ // Enable output as well (not using the driver, to avoid touching input
+ // enable bits).
+ esp_rom_gpio_connect_out_signal(gpio_wp_num, SIG_GPIO_OUT_IDX, false, false);
+ REG_WRITE(GPIO_ENABLE_W1TS_REG, BIT(gpio_wp_num));
+
+ // Check that the card can be initialized with WP low
+ REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_wp_num));
+ TEST_ESP_OK(sdmmc_card_init(config, card));
+
+ uint32_t* data = heap_caps_calloc(1, 512, MALLOC_CAP_DMA);
+
+ // Check that card write succeeds if WP is high
+ REG_WRITE(GPIO_OUT_W1TS_REG, BIT(gpio_wp_num));
+ usleep(1000);
+ TEST_ESP_OK(sdmmc_write_sectors(card, &data, 0, 1));
+
+ // Check that write fails if WP is low
+ REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_wp_num));
+ usleep(1000);
+ TEST_ESP_ERR(ESP_ERR_INVALID_STATE, sdmmc_write_sectors(card, &data, 0, 1));
+ // ...but reads still work
+ TEST_ESP_OK(sdmmc_read_sectors(card, &data, 0, 1));
+
+ free(data);
+ free(card);
+}
+
+#if SOC_SDMMC_HOST_SUPPORTED
+TEST_CASE("WP input works in SD mode", "[sd][test_env=UT_T1_SDMODE]")
+{
+ sd_test_board_power_on();
+ sdmmc_host_t config = SDMMC_HOST_DEFAULT();
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+ slot_config.gpio_wp = CD_WP_TEST_GPIO;
+ TEST_ESP_OK(sdmmc_host_init());
+ TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
+
+ test_wp_input(CD_WP_TEST_GPIO, &config);
+
+ TEST_ESP_OK(sdmmc_host_deinit());
+ sd_test_board_power_off();
+}
+#endif
+
+#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
+//No runners
+TEST_CASE("WP input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
+{
+ sd_test_board_power_on();
+
+ sdspi_dev_handle_t handle;
+ sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
+ dev_config.gpio_wp = CD_WP_TEST_GPIO;
+ test_sdspi_init_bus(dev_config.host_id, GPIO_NUM_15, GPIO_NUM_2, GPIO_NUM_14, TEST_SDSPI_DMACHAN);
+
+ TEST_ESP_OK(sdspi_host_init());
+ TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
+
+ sdmmc_host_t config = SDSPI_HOST_DEFAULT();
+ config.slot = handle;
+
+ test_wp_input(CD_WP_TEST_GPIO, &config);
+
+ TEST_ESP_OK(sdspi_host_deinit());
+ test_sdspi_deinit_bus(dev_config.host_id);
+ sd_test_board_power_off();
+}
+#endif //DISABLED_FOR_TARGETS(ESP32S2)
diff --git a/circuitpython/lib/sdmmc/test/test_sdio.c b/circuitpython/lib/sdmmc/test/test_sdio.c
new file mode 100644
index 0000000..ecafb16
--- /dev/null
+++ b/circuitpython/lib/sdmmc/test/test_sdio.c
@@ -0,0 +1,388 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "soc/soc_caps.h"
+#if SOC_SDMMC_HOST_SUPPORTED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "esp_log.h"
+#include "esp_heap_caps.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/gpio.h"
+#include "driver/sdmmc_host.h"
+#include "driver/sdmmc_defs.h"
+#include "sdmmc_cmd.h"
+#include "unity.h"
+
+/* Second ESP32 board attached as follows:
+ * Master Slave
+ * IO18 EN
+ * IO19 IO0
+ * IO14 SD_CLK
+ * IO15 SD_CMD
+ * IO2 SD_D0
+ * IO4 SD_D1
+ * IO12 SD_D2
+ * IO13 SD_D3
+ */
+
+
+/* TODO: add SDIO slave header files, remove these definitions */
+
+#define DR_REG_SLC_MASK 0xfffffc00
+
+#define SLCCONF1 (DR_REG_SLC_BASE + 0x60)
+#define SLC_SLC0_RX_STITCH_EN (BIT(6))
+#define SLC_SLC0_TX_STITCH_EN (BIT(5))
+
+#define SLC0TX_LINK (DR_REG_SLC_BASE + 0x40)
+#define SLC_SLC0_TXLINK_PARK (BIT(31))
+#define SLC_SLC0_TXLINK_RESTART (BIT(30))
+#define SLC_SLC0_TXLINK_START (BIT(29))
+
+#define DR_REG_SLCHOST_MASK 0xfffffc00
+#define SLCHOST_STATE_W0 (DR_REG_SLCHOST_BASE + 0x64)
+#define SLCHOST_CONF_W0 (DR_REG_SLCHOST_BASE + 0x6C)
+#define SLCHOST_CONF_W5 (DR_REG_SLCHOST_BASE + 0x80)
+#define SLCHOST_WIN_CMD (DR_REG_SLCHOST_BASE + 0x84)
+
+#define SLC_WIN_CMD_READ 0x80
+#define SLC_WIN_CMD_WRITE 0xC0
+#define SLC_WIN_CMD_S 8
+
+#define SLC_THRESHOLD_ADDR 0x1f800
+
+static const char* TAG = "sdio_test";
+
+static esp_err_t slave_slchost_reg_read(sdmmc_card_t* card, uint32_t addr, uint32_t* out_val)
+{
+ if ((addr & DR_REG_SLCHOST_MASK) != DR_REG_SLCHOST_BASE) {
+ ESP_LOGW(TAG, "%s: invalid addr 0x%08x\n", __func__, addr);
+ return ESP_ERR_INVALID_ARG;
+ }
+ return sdmmc_io_read_bytes(card, 1, addr & (~DR_REG_SLCHOST_MASK), out_val, sizeof(*out_val));
+}
+
+static esp_err_t slave_slchost_reg_write(sdmmc_card_t* card, uint32_t addr, uint32_t val)
+{
+ if ((addr & DR_REG_SLCHOST_MASK) != DR_REG_SLCHOST_BASE) {
+ ESP_LOGW(TAG, "%s: invalid addr 0x%08x\n", __func__, addr);
+ return ESP_ERR_INVALID_ARG;
+ }
+ return sdmmc_io_write_bytes(card, 1, addr & (~DR_REG_SLCHOST_MASK), &val, sizeof(val));
+}
+
+static esp_err_t slave_slc_reg_read(sdmmc_card_t* card, uint32_t addr, uint32_t* val)
+{
+ if ((addr & DR_REG_SLC_MASK) != DR_REG_SLC_BASE) {
+ ESP_LOGW(TAG, "%s: invalid addr 0x%08x\n", __func__, addr);
+ return ESP_ERR_INVALID_ARG;
+ }
+ uint32_t word = (addr - DR_REG_SLC_BASE) / 4;
+ if (word > INT8_MAX) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ uint32_t window_command = word | (SLC_WIN_CMD_READ << SLC_WIN_CMD_S);
+ esp_err_t err = slave_slchost_reg_write(card, SLCHOST_WIN_CMD, window_command);
+ if (err != ESP_OK) {
+ return err;
+ }
+
+ return slave_slchost_reg_read(card, SLCHOST_STATE_W0, val);
+}
+
+static esp_err_t slave_slc_reg_write(sdmmc_card_t* card, uint32_t addr, uint32_t val)
+{
+ if ((addr & DR_REG_SLC_MASK) != DR_REG_SLC_BASE) {
+ ESP_LOGW(TAG, "%s: invalid addr 0x%08x\n", __func__, addr);
+ return ESP_ERR_INVALID_ARG;
+ }
+ uint32_t word = (addr - DR_REG_SLC_BASE) / 4;
+ if (word > INT8_MAX) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ esp_err_t err = slave_slchost_reg_write(card, SLCHOST_CONF_W5, val);
+ if (err != ESP_OK) {
+ return err;
+ }
+
+ uint32_t window_command = word | (SLC_WIN_CMD_WRITE << SLC_WIN_CMD_S);
+ return slave_slchost_reg_write(card, SLCHOST_WIN_CMD, window_command);
+}
+
+/** Reset and put slave into download mode */
+static void reset_slave(void)
+{
+ const int pin_en = 18;
+ const int pin_io0 = 19;
+ gpio_config_t gpio_cfg = {
+ .pin_bit_mask = BIT64(pin_en) | BIT64(pin_io0),
+ .mode = GPIO_MODE_OUTPUT_OD,
+ };
+ TEST_ESP_OK(gpio_config(&gpio_cfg));
+ gpio_set_level(pin_en, 0);
+ gpio_set_level(pin_io0, 0);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ gpio_set_level(pin_en, 1);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ gpio_set_level(pin_io0, 1);
+}
+
+static void sdio_slave_common_init(sdmmc_card_t* card)
+{
+ uint8_t card_cap;
+ esp_err_t err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_CARD_CAP, &card_cap);
+ TEST_ESP_OK(err);
+ printf("CAP: 0x%02x\n", card_cap);
+
+ uint8_t hs;
+ err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_HIGHSPEED, &hs);
+ TEST_ESP_OK(err);
+ printf("HS: 0x%02x\n", hs);
+
+
+#define FUNC1_EN_MASK (BIT(1))
+
+ uint8_t ioe;
+ err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_ENABLE, &ioe);
+ TEST_ESP_OK(err);
+ printf("IOE: 0x%02x\n", ioe);
+
+ uint8_t ior = 0;
+ err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior);
+ TEST_ESP_OK(err);
+ printf("IOR: 0x%02x\n", ior);
+
+ // enable function 1
+ ioe |= FUNC1_EN_MASK;
+ err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_FN_ENABLE, ioe, NULL);
+ TEST_ESP_OK(err);
+
+ err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_ENABLE, &ioe);
+ TEST_ESP_OK(err);
+ printf("IOE: 0x%02x\n", ioe);
+
+ // wait for the card to become ready
+ while ( (ior & FUNC1_EN_MASK) == 0 ) {
+ err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior);
+ TEST_ESP_OK(err);
+ printf("IOR: 0x%02x\n", ior);
+ }
+
+ // get interrupt status
+ uint8_t ie;
+ err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_INT_ENABLE, &ie);
+ TEST_ESP_OK(err);
+ printf("IE: 0x%02x\n", ie);
+
+ // enable interrupts for function 1&2 and master enable
+ ie |= BIT(0) | FUNC1_EN_MASK;
+ err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_INT_ENABLE, ie, NULL);
+ TEST_ESP_OK(err);
+
+ err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_INT_ENABLE, &ie);
+ TEST_ESP_OK(err);
+ printf("IE: 0x%02x\n", ie);
+}
+
+/** Common for all SDIO devices, set block size for specific function */
+static void sdio_slave_set_blocksize(sdmmc_card_t* card, int function, uint16_t bs)
+{
+ const uint8_t* bs_u8 = (const uint8_t*) &bs;
+ uint16_t bs_read = 0;
+ uint8_t* bs_read_u8 = (uint8_t*) &bs_read;
+ uint32_t offset = SD_IO_FBR_START * function;
+ TEST_ESP_OK( sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, bs_u8[0], NULL));
+ TEST_ESP_OK( sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, bs_u8[1], NULL));
+ TEST_ESP_OK( sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0]));
+ TEST_ESP_OK( sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1]));
+ TEST_ASSERT_EQUAL_HEX16(bs, bs_read);
+}
+
+/**
+ * ESP32 ROM code does not set some SDIO slave registers to the defaults
+ * we need, this function clears/sets some bits.
+ */
+static void esp32_slave_init_extra(sdmmc_card_t* card)
+{
+ printf("Initialize some ESP32 SDIO slave registers\n");
+
+ uint32_t reg_val;
+ TEST_ESP_OK( slave_slc_reg_read(card, SLCCONF1, &reg_val) );
+ reg_val &= ~(SLC_SLC0_RX_STITCH_EN | SLC_SLC0_TX_STITCH_EN);
+ TEST_ESP_OK( slave_slc_reg_write(card, SLCCONF1, reg_val) );
+
+ TEST_ESP_OK( slave_slc_reg_read(card, SLC0TX_LINK, &reg_val) );
+ reg_val |= SLC_SLC0_TXLINK_START;
+ TEST_ESP_OK( slave_slc_reg_write(card, SLC0TX_LINK, reg_val) );
+}
+
+/**
+ * ESP32 bootloader implements "SIP" protocol which can be used to exchange
+ * some commands, events, and data packets between the host and the slave.
+ * This function sends a SIP command, testing CMD53 block writes along the way.
+ */
+static void esp32_send_sip_command(sdmmc_card_t* card)
+{
+ printf("Test block write using CMD53\n");
+ const size_t block_size = 512;
+ uint8_t* data = heap_caps_calloc(1, block_size, MALLOC_CAP_DMA);
+ struct sip_cmd_bootup {
+ uint32_t boot_addr;
+ uint32_t discard_link;
+ };
+ struct sip_cmd_write_reg {
+ uint32_t addr;
+ uint32_t val;
+ };
+ struct sip_hdr {
+ uint8_t fc[2];
+ uint16_t len;
+ uint32_t cmdid;
+ uint32_t seq;
+ };
+
+ struct sip_hdr* hdr = (struct sip_hdr*) data;
+ size_t len;
+
+#define SEND_WRITE_REG_CMD
+
+#ifdef SEND_WRITE_REG_CMD
+ struct sip_cmd_write_reg *write_reg = (struct sip_cmd_write_reg*) (data + sizeof(*hdr));
+ len = sizeof(*hdr) + sizeof(*write_reg);
+ hdr->cmdid = 3; /* SIP_CMD_WRITE_REG */
+ write_reg->addr = GPIO_ENABLE_W1TS_REG;
+ write_reg->val = BIT(0) | BIT(2) | BIT(4); /* Turn of RGB LEDs on WROVER-KIT */
+#else
+ struct sip_cmd_bootup *bootup = (struct sip_cmd_bootup*) (data + sizeof(*hdr));
+ len = sizeof(*hdr) + sizeof(*bootup);
+ hdr->cmdid = 5; /* SIP_CMD_BOOTUP */
+ bootup->boot_addr = 0x4005a980; /* start_tb_console function in ROM */
+ bootup->discard_link = 1;
+#endif
+ hdr->len = len;
+
+ TEST_ESP_OK( sdmmc_io_write_blocks(card, 1, SLC_THRESHOLD_ADDR - len, data, block_size) );
+ free(data);
+}
+
+static void test_cmd52_read_write_single_byte(sdmmc_card_t* card)
+{
+ esp_err_t err;
+ printf("Write bytes to slave's W0_REG using CMD52\n");
+ const size_t scratch_area_reg = SLCHOST_CONF_W0 - DR_REG_SLCHOST_BASE;
+
+ const uint8_t test_byte_1 = 0xa5;
+ const uint8_t test_byte_2 = 0xb6;
+ // used to check Read-After-Write
+ uint8_t test_byte_1_raw;
+ uint8_t test_byte_2_raw;
+ uint8_t val = 0;
+ err = sdmmc_io_write_byte(card, 1, scratch_area_reg, test_byte_1, &test_byte_1_raw);
+ TEST_ESP_OK(err);
+ TEST_ASSERT_EQUAL_UINT8(test_byte_1, test_byte_1_raw);
+ err = sdmmc_io_write_byte(card, 1, scratch_area_reg + 1, test_byte_2, &test_byte_2_raw);
+ TEST_ESP_OK(err);
+ TEST_ASSERT_EQUAL_UINT8(test_byte_2, test_byte_2_raw);
+
+ printf("Read back bytes using CMD52\n");
+ TEST_ESP_OK(sdmmc_io_read_byte(card, 1, scratch_area_reg, &val));
+ TEST_ASSERT_EQUAL_UINT8(test_byte_1, val);
+
+ TEST_ESP_OK(sdmmc_io_read_byte(card, 1, scratch_area_reg + 1, &val));
+ TEST_ASSERT_EQUAL_UINT8(test_byte_2, val);
+}
+
+static void test_cmd53_read_write_multiple_bytes(sdmmc_card_t* card, size_t n_bytes)
+{
+ printf("Write multiple bytes using CMD53\n");
+ const size_t scratch_area_reg = SLCHOST_CONF_W0 - DR_REG_SLCHOST_BASE;
+
+ uint8_t* src = heap_caps_malloc(512, MALLOC_CAP_DMA);
+ uint32_t* src_32 = (uint32_t*) src;
+
+ for (size_t i = 0; i < (n_bytes + 3) / 4; ++i) {
+ src_32[i] = rand();
+ }
+
+ TEST_ESP_OK(sdmmc_io_write_bytes(card, 1, scratch_area_reg, src, n_bytes));
+ ESP_LOG_BUFFER_HEX(TAG, src, n_bytes);
+
+ printf("Read back using CMD52\n");
+ uint8_t* dst = heap_caps_malloc(512, MALLOC_CAP_DMA);
+ for (size_t i = 0; i < n_bytes; ++i) {
+ TEST_ESP_OK(sdmmc_io_read_byte(card, 1, scratch_area_reg + i, &dst[i]));
+ }
+ ESP_LOG_BUFFER_HEX(TAG, dst, n_bytes);
+ TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, n_bytes);
+
+ printf("Read back using CMD53\n");
+ TEST_ESP_OK(sdmmc_io_read_bytes(card, 1, scratch_area_reg, dst, n_bytes));
+ ESP_LOG_BUFFER_HEX(TAG, dst, n_bytes);
+ TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, n_bytes);
+
+ free(src);
+ free(dst);
+}
+
+
+TEST_CASE("can probe and talk to ESP32 SDIO slave", "[sdio][ignore]")
+{
+ reset_slave();
+
+ /* Probe */
+ sdmmc_host_t config = SDMMC_HOST_DEFAULT();
+ config.flags = SDMMC_HOST_FLAG_1BIT;
+ config.max_freq_khz = SDMMC_FREQ_PROBING;
+
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+ (sdmmc_host_init());
+ (sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+ TEST_ESP_OK(sdmmc_card_init(&config, card));
+ sdmmc_card_print_info(stdout, card);
+
+ /* Set up standard SDIO registers */
+ sdio_slave_common_init(card);
+
+ srand(0);
+ for (int repeat = 0; repeat < 4; ++repeat) {
+ test_cmd52_read_write_single_byte(card);
+ test_cmd53_read_write_multiple_bytes(card, 1);
+ test_cmd53_read_write_multiple_bytes(card, 2);
+ test_cmd53_read_write_multiple_bytes(card, 3);
+ test_cmd53_read_write_multiple_bytes(card, 4);
+ test_cmd53_read_write_multiple_bytes(card, 5);
+ test_cmd53_read_write_multiple_bytes(card, 23);
+ test_cmd53_read_write_multiple_bytes(card, 24);
+ }
+
+ sdio_slave_set_blocksize(card, 0, 512);
+ sdio_slave_set_blocksize(card, 1, 512);
+
+ esp32_slave_init_extra(card);
+
+ esp32_send_sip_command(card);
+
+ TEST_ESP_OK(sdmmc_host_deinit());
+ free(card);
+}
+
+#endif //SOC_SDMMC_HOST_SUPPORTED