#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 */
/* 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)
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 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 SD_IO_CISTPL_NULL 0x00
+#define SD_IO_CISTPL_VERS_1 0x15
+#define SD_IO_CISTPL_MANFID 0x20
+#define SD_IO_CISTPL_FUNCID 0x21
+#define SD_IO_CISTPL_FUNCE 0x22
+#define SD_IO_CISTPL_END 0xff
+
+/* CISTPL_FUNCID codes */
+#define TPLFID_FUNCTION_SDIO 0x0c
+
+
#endif //_SDMMC_DEFS_H_
.do_transaction = &sdmmc_host_do_transaction, \
.deinit = &sdmmc_host_deinit, \
.command_timeout_ms = 0, \
+ .io_int_enable = sdmmc_host_io_int_enable, \
+ .io_int_wait = sdmmc_host_io_int_wait, \
}
/**
*/
esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo);
+/**
+ * @brief Enable IO interrupts
+ *
+ * This function configures the host to accept SDIO interrupts.
+ *
+ * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
+ * @return returns ESP_OK, other errors possible in the future
+ */
+esp_err_t sdmmc_host_io_int_enable(int slot);
+
+/**
+ * @brief Block until an SDIO interrupt is received, or timeout occurs
+ * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
+ * @param timeout_ticks number of RTOS ticks to wait for the interrupt
+ * @return
+ * - ESP_OK on success (interrupt received)
+ * - ESP_ERR_TIMEOUT if the interrupt did not occur within timeout_ticks
+ */
+esp_err_t sdmmc_host_io_int_wait(int slot, TickType_t timeout_ticks);
+
/**
* @brief Disable SDMMC host and release allocated resources
*
#include <stdint.h>
#include <stddef.h>
#include "esp_err.h"
+#include "freertos/FreeRTOS.h"
/**
* Decoded values from SD card Card Specific Data register
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_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)
+/** @endcond */
esp_err_t error; /*!< error returned from transfer */
int timeout_ms; /*!< response timeout, in milliseconds */
} sdmmc_command_t;
esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */
esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */
+ esp_err_t (*io_int_enable)(int slot); /*!< Host function to enable SDIO interrupt line */
+ esp_err_t (*io_int_wait)(int slot, TickType_t timeout_ticks); /*!< 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;
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
uint16_t rca; /*!< RCA (Relative Card Address) */
+ 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 num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */
+ uint32_t reserved : 27; /*!< Reserved for future expansion */
} sdmmc_card_t;
-
-
#endif // _SDMMC_TYPES_H_
.set_card_clk = &sdspi_host_set_card_clk, \
.do_transaction = &sdspi_host_do_transaction, \
.deinit = &sdspi_host_deinit, \
+ .io_int_enable = NULL, \
+ .io_int_wait = NULL, \
.command_timeout_ms = 0, \
}
#include "driver/sdmmc_host.h"
#include "driver/periph_ctrl.h"
#include "sdmmc_private.h"
+#include "freertos/semphr.h"
#define SDMMC_EVENT_QUEUE_LENGTH 32
uint32_t d5;
uint32_t d6;
uint32_t d7;
+ uint8_t d1_gpio;
uint8_t d3_gpio;
uint8_t card_detect;
uint8_t write_protect;
+ uint8_t card_int;
uint8_t width;
} sdmmc_slot_info_t;
.d1 = PERIPHS_IO_MUX_SD_DATA1_U,
.d2 = PERIPHS_IO_MUX_SD_DATA2_U,
.d3 = PERIPHS_IO_MUX_SD_DATA3_U,
+ .d1_gpio = 8,
.d3_gpio = 10,
.d4 = PERIPHS_IO_MUX_GPIO16_U,
.d5 = PERIPHS_IO_MUX_GPIO17_U,
.d7 = PERIPHS_IO_MUX_GPIO18_U,
.card_detect = HOST_CARD_DETECT_N_1_IDX,
.write_protect = HOST_CARD_WRITE_PRT_1_IDX,
+ .card_int = HOST_CARD_INT_N_1_IDX,
.width = 8
},
{
.d1 = PERIPHS_IO_MUX_GPIO4_U,
.d2 = PERIPHS_IO_MUX_MTDI_U,
.d3 = PERIPHS_IO_MUX_MTCK_U,
+ .d1_gpio = 4,
.d3_gpio = 13,
.card_detect = HOST_CARD_DETECT_N_2_IDX,
.write_protect = HOST_CARD_WRITE_PRT_2_IDX,
+ .card_int = HOST_CARD_INT_N_2_IDX,
.width = 4
}
};
static const char* TAG = "sdmmc_periph";
static intr_handle_t s_intr_handle;
static QueueHandle_t s_event_queue;
+static SemaphoreHandle_t s_io_intr_event;
size_t s_slot_width[2] = {1,1};
if (!s_event_queue) {
return ESP_ERR_NO_MEM;
}
+ s_io_intr_event = xSemaphoreCreateBinary();
+ if (!s_io_intr_event) {
+ vQueueDelete(s_event_queue);
+ s_event_queue = NULL;
+ return ESP_ERR_NO_MEM;
+ }
// Attach interrupt handler
esp_err_t ret = esp_intr_alloc(ETS_SDIO_HOST_INTR_SOURCE, 0, &sdmmc_isr, s_event_queue, &s_intr_handle);
if (ret != ESP_OK) {
vQueueDelete(s_event_queue);
s_event_queue = NULL;
+ vSemaphoreDelete(s_io_intr_event);
+ s_io_intr_event = NULL;
return ret;
}
// Enable interrupts
SDMMC_INTMASK_RCRC | SDMMC_INTMASK_DCRC |
SDMMC_INTMASK_RTO | SDMMC_INTMASK_DTO | SDMMC_INTMASK_HTO |
SDMMC_INTMASK_SBE | SDMMC_INTMASK_EBE |
- SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE;
+ SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE; //sdio is enabled only when use.
SDMMC.ctrl.int_enable = 1;
// Enable DMA
if (ret != ESP_OK) {
vQueueDelete(s_event_queue);
s_event_queue = NULL;
+ vSemaphoreDelete(s_io_intr_event);
+ s_io_intr_event = NULL;
esp_intr_free(s_intr_handle);
s_intr_handle = NULL;
return ret;
configure_pin(pslot->d7);
}
}
+
+ // SDIO slave interrupt is edge sensitive to ~(int_n | card_int | card_detect)
+ // set this and card_detect to high to enable sdio interrupt
+ gpio_matrix_in(GPIO_FUNC_IN_HIGH, pslot->card_int, 0);
if (gpio_cd != -1) {
gpio_set_direction(gpio_cd, GPIO_MODE_INPUT);
gpio_matrix_in(gpio_cd, pslot->card_detect, 0);
+ } else {
+ gpio_matrix_in(GPIO_FUNC_IN_HIGH, pslot->card_detect, 0);
}
+
if (gpio_wp != -1) {
gpio_set_direction(gpio_wp, GPIO_MODE_INPUT);
gpio_matrix_in(gpio_wp, pslot->write_protect, 0);
s_intr_handle = NULL;
vQueueDelete(s_event_queue);
s_event_queue = NULL;
+ vQueueDelete(s_io_intr_event);
+ s_io_intr_event = NULL;
sdmmc_host_input_clk_disable();
sdmmc_host_transaction_handler_deinit();
periph_module_disable(PERIPH_SDMMC_MODULE);
SDMMC.pldmnd = 1;
}
+esp_err_t sdmmc_host_io_int_enable(int slot)
+{
+ configure_pin(s_slot_info[slot].d1);
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_host_io_int_wait(int slot, TickType_t timeout_ticks)
+{
+ /* SDIO interrupts are negedge sensitive ones: the status bit is only set
+ * when first interrupt triggered.
+ *
+ * If D1 GPIO is low when entering this function, we know that interrupt
+ * (in SDIO sense) has occurred and we don't need to use SDMMC peripheral
+ * interrupt.
+ */
+
+ SDMMC.intmask.sdio &= ~BIT(slot); /* Disable SDIO interrupt */
+ SDMMC.rintsts.sdio = BIT(slot);
+ if (gpio_get_level(s_slot_info[slot].d1_gpio) == 0) {
+ return ESP_OK;
+ }
+ /* Otherwise, need to wait for an interrupt. Since D1 was high,
+ * SDMMC peripheral interrupt is guaranteed to trigger on negedge.
+ */
+ xSemaphoreTake(s_io_intr_event, 0);
+ SDMMC.intmask.sdio |= BIT(slot); /* Re-enable SDIO interrupt */
+
+ if (xSemaphoreTake(s_io_intr_event, timeout_ticks) == pdTRUE) {
+ return ESP_OK;
+ } else {
+ return ESP_ERR_TIMEOUT;
+ }
+}
+
/**
* @brief SDMMC interrupt handler
*
- * Ignoring SDIO and streaming read/writes for now (and considering just SD memory cards),
- * all communication is driven by the master, and the hardware handles things like stop
- * commands automatically. So the interrupt handler doesn't need to do much, we just push
- * interrupt status into a queue, clear interrupt flags, and let the task currently doing
- * communication figure out what to do next.
+ * All communication in SD protocol is driven by the master, and the hardware
+ * handles things like stop commands automatically.
+ * So the interrupt handler doesn't need to do much, we just push interrupt
+ * status into a queue, clear interrupt flags, and let the task currently
+ * doing communication figure out what to do next.
+ * This also applies to SDIO interrupts which are generated by the slave.
*
- * Card detect interrupts pose a small issue though, because if a card is plugged in and
- * out a few times, while there is no task to process the events, event queue can become
- * full and some card detect events may be dropped. We ignore this problem for now, since
- * the there are no other interesting events which can get lost due to this.
+ * Card detect interrupts pose a small issue though, because if a card is
+ * plugged in and out a few times, while there is no task to process
+ * the events, event queue can become full and some card detect events
+ * may be dropped. We ignore this problem for now, since the there are no other
+ * interesting events which can get lost due to this.
*/
static void sdmmc_isr(void* arg) {
QueueHandle_t queue = (QueueHandle_t) arg;
sdmmc_event_t event;
- uint32_t pending = SDMMC.mintsts.val;
+ int higher_priority_task_awoken = pdFALSE;
+
+ uint32_t pending = SDMMC.mintsts.val & 0xFFFF;
SDMMC.rintsts.val = pending;
event.sdmmc_status = pending;
SDMMC.idsts.val = dma_pending;
event.dma_status = dma_pending & 0x1f;
- int higher_priority_task_awoken = pdFALSE;
- xQueueSendFromISR(queue, &event, &higher_priority_task_awoken);
+ if (pending != 0 || dma_pending != 0) {
+ xQueueSendFromISR(queue, &event, &higher_priority_task_awoken);
+ }
+
+ uint32_t sdio_pending = SDMMC.mintsts.sdio;
+ if (sdio_pending) {
+ // disable the interrupt (no need to clear here, this is done in sdmmc_host_io_wait_int)
+ SDMMC.intmask.sdio &= ~sdio_pending;
+ xSemaphoreGiveFromISR(s_io_intr_event, &higher_priority_task_awoken);
+ }
+
if (higher_priority_task_awoken == pdTRUE) {
portYIELD_FROM_ISR();
}
* 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
+ * @param out_card pointer to structure which will receive information
+ * about the card when the function completes
* @return
* - ESP_OK on success
* - One of the error codes from SDMMC host controller
/**
* 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 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
/**
* Write given number of sectors to 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 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
esp_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
+ * - ESP_OK on success
+ * - One of the error codes from SDMMC host controller
+ */
+esp_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
+ * - ESP_OK on success
+ * - One of the error codes from SDMMC host controller
+ */
+esp_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
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_SIZE if size exceeds 512 bytes
+ * - One of the error codes from SDMMC host controller
+ */
+esp_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
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_SIZE if size exceeds 512 bytes
+ * - One of the error codes from SDMMC host controller
+ */
+esp_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
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_SIZE if size is not divisible by 512 bytes
+ * - One of the error codes from SDMMC host controller
+ */
+esp_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
+ * - ESP_OK on success
+ * - ESP_ERR_INVALID_SIZE if size is not divisible by 512 bytes
+ * - One of the error codes from SDMMC host controller
+ */
+esp_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
+ * - ESP_OK on success
+ * - ESP_ERR_NOT_SUPPORTED if the host controller does not support
+ * IO interrupts
+ */
+esp_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
+ * - ESP_OK if the interrupt is received
+ * - ESP_ERR_NOT_SUPPORTED if the host controller does not support
+ * IO interrupts
+ * - ESP_ERR_TIMEOUT if the interrupt does not happen in timeout_ticks
+ */
+esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks);
+
#ifdef __cplusplus
}
#endif
#include "sys/param.h"
#include "soc/soc_memory_layout.h"
-#define SDMMC_GO_IDLE_DELAY_MS 20
+#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
uint32_t mode, uint32_t group, uint32_t function,
sdmmc_switch_func_rsp_t* resp);
static esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card);
+static esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
+static esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card);
static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca);
static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
-static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status);
static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
static esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
static uint32_t get_host_ocr(float voltage);
size_t start_block, size_t block_count);
static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count);
-
+static esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
+static esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int function,
+ uint32_t reg, uint32_t arg, uint8_t *byte);
+static esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
+ uint32_t reg, int arg, void *data, size_t size);
+static void sdmmc_fix_host_flags(sdmmc_card_t* card);
static bool host_is_spi(const sdmmc_card_t* card)
{
esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
{
- ESP_LOGD(TAG, "%s", __func__);
+ esp_err_t err;
memset(card, 0, sizeof(*card));
memcpy(&card->host, config, sizeof(*config));
const bool is_spi = host_is_spi(card);
- if ( !is_spi ) {
- //check HOST flags compatible with slot configuration.
- int slot_bit_width = config->get_bus_width(config->slot);
- if ( slot_bit_width == 1 && (config->flags & (SDMMC_HOST_FLAG_4BIT|SDMMC_HOST_FLAG_8BIT))) {
- ESP_LOGW(TAG, "HOST slot only enables 1-bit.");
- card->host.flags = ((card->host.flags&(~(SDMMC_HOST_FLAG_4BIT|SDMMC_HOST_FLAG_8BIT)))|SDMMC_HOST_FLAG_1BIT);
- } else if ( slot_bit_width == 4 && (config->flags & SDMMC_HOST_FLAG_8BIT)){
- ESP_LOGW(TAG, "HOST slot only enables 4-bit.");
- card->host.flags = ((card->host.flags&(~(SDMMC_HOST_FLAG_1BIT|SDMMC_HOST_FLAG_8BIT)))|SDMMC_HOST_FLAG_4BIT);
- }
+ if (!is_spi) {
+ // Check if host flags are compatible with slot configuration.
+ sdmmc_fix_host_flags(card);
+ }
+
+ /* ----------- standard initialization process starts here ---------- */
+
+ /* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5).
+ * Non-IO cards are allowed to time out.
+ */
+ uint8_t sdio_reset = CCCR_CTL_RES;
+ err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CTL, SD_ARG_CMD52_WRITE, &sdio_reset);
+ if (err != ESP_OK && err != ESP_ERR_TIMEOUT) {
+ ESP_LOGE(TAG, "%s: sdio_reset: unexpected return: 0x%x", __func__, err );
+ return err;
}
+
/* GO_IDLE_STATE (CMD0) command resets the card */
- esp_err_t err = sdmmc_send_cmd_go_idle_state(card);
+ err = sdmmc_send_cmd_go_idle_state(card);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err);
return err;
}
+
+ /* FIXME: we should check card status to wait until it is out of idle
+ * state, instead of using a delay.
+ */
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
sdmmc_send_cmd_go_idle_state(card);
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
return 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.
- */
+ /* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
+ * Non-IO cards will not respond to this command.
+ */
+ err = sdmmc_io_send_op_cond(card, 0, &card->ocr);
+ if (err != ESP_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;
- /* 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.
- */
- if (is_spi) {
- err = sdmmc_send_cmd_crc_on_off(card, true);
+ 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;
+ }
+ host_ocr &= card->ocr;
+ err = sdmmc_io_send_op_cond(card, host_ocr, &card->ocr);
if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
+ ESP_LOGE(TAG, "%s: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
+ sdmmc_io_enable_int(card);
}
- /* 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 (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
- return err;
- }
- if (is_spi) {
- err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
+ if (card->is_mem) {
+ /* 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.
+ */
+
+ /* 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.
+ */
+ if (is_spi) {
+ err = sdmmc_send_cmd_crc_on_off(card, true);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
+ return err;
+ }
+ }
+
+ /* 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 (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: read_ocr returned 0x%x", __func__, err);
+ ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
- }
- ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr);
+ if (is_spi) {
+ err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
+ if (err != ESP_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);
+ /* 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);
+ }
/* Read and decode the contents of CID register */
if (!is_spi) {
- err = sddmc_send_cmd_all_send_cid(card, &card->cid);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
- return err;
+ if (card->is_mem) {
+ err = sddmc_send_cmd_all_send_cid(card, &card->cid);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
+ return err;
+ }
}
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
if (err != ESP_OK) {
return err;
}
}
-
- /* Get and decode the contents of CSD register. Determine card capacity. */
- err = sdmmc_send_cmd_send_csd(card, &card->csd);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: send_csd (1) 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) &&
- 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;
+ if (card->is_mem) {
+ /* Get and decode the contents of CSD register. Determine card capacity. */
+ err = sdmmc_send_cmd_send_csd(card, &card->csd);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: send_csd (1) 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) &&
+ 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;
+ }
}
+ /* ----------- standard initialization process ends here ----------- */
/* 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
}
}
- /* 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) {
- err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
+ if (card->is_mem) {
+ /* 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) {
+ err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
+ return 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 != ESP_OK) {
- ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
+ ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
return 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 != ESP_OK) {
- ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
- return err;
- }
-
- /* If the host has been initialized with 4-bit bus support, and the card
- * supports 4-bit bus, switch to 4-bit bus now.
- */
- if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
- (card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
- ESP_LOGD(TAG, "switching to 4-bit bus mode");
- err = sdmmc_send_cmd_set_bus_width(card, 4);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "set_bus_width failed");
- return err;
- }
- err = (*config->set_bus_width)(config->slot, 4);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "slot->set_bus_width failed");
- return err;
+ if (card->is_mem) {
+ /* If the host has been initialized with 4-bit bus support, and the card
+ * supports 4-bit bus, switch to 4-bit bus now.
+ */
+ if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
+ (card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
+ ESP_LOGD(TAG, "switching to 4-bit bus mode");
+ err = sdmmc_send_cmd_set_bus_width(card, 4);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "set_bus_width failed");
+ return err;
+ }
+ err = (*config->set_bus_width)(config->slot, 4);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "slot->set_bus_width failed");
+ return err;
+ }
}
- }
- /* Wait for the card to be ready for data transfers */
- uint32_t status = 0;
- while (!is_spi && !(status & MMC_R1_READY_FOR_DATA)) {
- // TODO: add some timeout here
- uint32_t count = 0;
- err = sdmmc_send_cmd_send_status(card, &status);
- if (err != ESP_OK) {
- return err;
+ /* Wait for the card to be ready for data transfers */
+ uint32_t status = 0;
+ while (!is_spi && !(status & MMC_R1_READY_FOR_DATA)) {
+ // TODO: add some timeout here
+ uint32_t count = 0;
+ err = sdmmc_send_cmd_send_status(card, &status);
+ if (err != ESP_OK) {
+ return err;
+ }
+ if (++count % 16 == 0) {
+ ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
+ }
}
- if (++count % 10 == 0) {
- ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
+ } else {
+ /* IO card */
+ if (config->flags & SDMMC_HOST_FLAG_4BIT) {
+ uint8_t card_cap;
+ err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CARD_CAP,
+ SD_ARG_CMD52_READ, &card_cap);
+ if (err != ESP_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 != ESP_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write SD_IO_CCCR_BUS_WIDTH) returned 0x%0x", __func__, err);
+ return err;
+ }
+ err = (*config->set_bus_width)(config->slot, 4);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "slot->set_bus_width failed");
+ return err;
+ }
+ }
}
}
-
/* So far initialization has been done using 400kHz clock. Determine the
* clock rate which both host and the card support, and switch to it.
*/
bool freq_switched = false;
if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
!is_spi /* SPI doesn't support >26MHz in some cases */) {
- /* 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.
- */
- err = sdmmc_enable_hs_mode(card);
+ if (card->is_mem) {
+ err = sdmmc_enable_hs_mode_and_check(card);
+ } else {
+ err = sdmmc_io_enable_hs_mode(card);
+ }
+
if (err == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
} else if (err != ESP_OK) {
- /* some other error */
return err;
- } else { /* ESP_OK */
- /* 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 more, deselect the card first, then
- * get the CSD, then select the card again.
+ } else {
+ ESP_LOGD(TAG, "%s: switching host to HS mode", __func__);
+ /* ESP_OK, HS mode has been enabled on the card side.
+ * Switch the host to HS mode.
*/
- err = sdmmc_send_cmd_select_card(card, 0);
+ err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err);
+ ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
return err;
}
- err = sdmmc_send_cmd_send_csd(card, &card->csd);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: send_csd (2) returned 0x%x", __func__, err);
- return err;
- }
- err = sdmmc_send_cmd_select_card(card, card->rca);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: select_card (3) 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);
- } else {
- /* Finally can switch the host to HS mode */
- err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
- return err;
- }
- freq_switched = true;
- }
+ freq_switched = true;
}
}
/* All SD cards must support default speed mode (25MHz).
* an indicator of potential signal integrity issues.
*/
if (freq_switched) {
- sdmmc_scr_t scr_tmp;
- err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: send_scr (2) 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 ESP_ERR_INVALID_RESPONSE;
+ if (card->is_mem) {
+ sdmmc_scr_t scr_tmp;
+ err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: send_scr (2) 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 ESP_ERR_INVALID_RESPONSE;
+ }
+ } else {
+ /* TODO: For IO cards, read some data to see if frequency switch
+ * was successful.
+ */
}
}
return ESP_OK;
fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
}
+static void 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))) {
+ ESP_LOGW(TAG, "host slot is configured in 1-bit mode");
+ card->host.flags &= ~width_mask;
+ card->host.flags |= ~(width_1bit);
+ } else if (slot_bit_width == 4 && (card->host.flags & width_8bit)){
+ ESP_LOGW(TAG, "host slot is configured in 4-bit mode");
+ card->host.flags &= ~width_mask;
+ card->host.flags |= width_4bit;
+ }
+}
+
static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
{
if (card->host.command_timeout_ms != 0) {
return sdmmc_send_app_cmd(card, &cmd);
}
-static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status)
-{
- sdmmc_command_t cmd = {
- .opcode = MMC_STOP_TRANSMISSION,
- .arg = 0,
- .flags = SCF_RSP_R1B | SCF_CMD_AC
- };
- esp_err_t err = sdmmc_send_cmd(card, &cmd);
- if (err == 0) {
- *status = MMC_R1(cmd.response);
- }
- return err;
-}
-
static esp_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");
static esp_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 ESP_ERR_NOT_SUPPORTED;
free(response);
return err;
}
+
+static esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
+{
+ /* Try to enabled HS mode */
+ esp_err_t err = sdmmc_enable_hs_mode(card);
+ if (err != ESP_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 more, deselect the card first, then
+ * get the CSD, then select the card again.
+ */
+ err = sdmmc_send_cmd_select_card(card, 0);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err);
+ return err;
+ }
+ err = sdmmc_send_cmd_send_csd(card, &card->csd);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: send_csd (2) returned 0x%x", __func__, err);
+ return err;
+ }
+ err = sdmmc_send_cmd_select_card(card, card->rca);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: select_card (3) 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 ESP_ERR_NOT_SUPPORTED;
+ }
+
+ return ESP_OK;
+}
+
+static esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
+{
+ /* 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;
+ esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_HIGHSPEED,
+ SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &val);
+ if (err != ESP_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 ESP_ERR_NOT_SUPPORTED;
+ }
+ return ESP_OK;
+}
+
+
+static esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
+{
+ esp_err_t err = ESP_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 != ESP_OK) {
+ break;
+ }
+ if ((MMC_R4(cmd.response) & SD_IO_OCR_MEM_READY) ||
+ ocr == 0) {
+ break;
+ }
+ err = ESP_ERR_TIMEOUT;
+ vTaskDelay(SDMMC_IO_SEND_OP_COND_DELAY_MS / portTICK_PERIOD_MS);
+ }
+ if (err == ESP_OK && ocrp != NULL)
+ *ocrp = MMC_R4(cmd.response);
+
+ return err;
+}
+
+static esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int func,
+ uint32_t reg, uint32_t arg, uint8_t *byte)
+{
+ esp_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 |= (*byte & SD_ARG_CMD52_DATA_MASK) << SD_ARG_CMD52_DATA_SHIFT;
+ cmd.arg = arg;
+
+ err = sdmmc_send_cmd(card, &cmd);
+ if (err != ESP_OK) {
+ ESP_LOGV(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
+ return err;
+ }
+
+ *byte = SD_R5_DATA(cmd.response);
+
+ return ESP_OK;
+}
+
+
+esp_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, uint8_t *out_byte)
+{
+ esp_err_t ret = sdmmc_io_rw_direct(card, function, addr, SD_ARG_CMD52_READ, out_byte);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read 0x%x) returned 0x%x", __func__, addr, ret);
+ }
+ return ret;
+}
+
+esp_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;
+ esp_err_t ret = sdmmc_io_rw_direct(card, function, addr,
+ SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &tmp_byte);
+ if (ret != ESP_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 ESP_OK;
+}
+
+static esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func,
+ uint32_t reg, int arg, void *datap, size_t datalen)
+{
+ esp_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 ESP_ERR_INVALID_SIZE;
+ }
+ count = cmd.datalen / cmd.blklen;
+ } else {
+ if (datalen > max_byte_transfer_size) {
+ /* TODO: split into multiple operations? */
+ return ESP_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 != ESP_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
+ return err;
+ }
+
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, void* dst, size_t size)
+{
+ return sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT,
+ dst, size);
+}
+
+esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, const void* src, size_t size)
+{
+ return sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT,
+ (void*) src, size);
+}
+
+esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, void* dst, size_t size)
+{
+ return sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
+ dst, size);
+}
+
+esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, const void* src, size_t 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);
+}
+
+esp_err_t sdmmc_io_enable_int(sdmmc_card_t* card)
+{
+ if (card->host.io_int_enable == NULL) {
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+ return (*card->host.io_int_enable)(card->host.slot);
+}
+
+esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks)
+{
+ if (card->host.io_int_wait == NULL) {
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+ return (*card->host.io_int_wait)(card->host.slot, timeout_ticks);
+}
--- /dev/null
+// 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 <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 "soc/gpio_reg.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_BASE 0x3ff58000
+#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_BASE 0x3ff55000
+#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()
+{
+ const int pin_en = 18;
+ const int pin_io0 = 19;
+ gpio_config_t gpio_cfg = {
+ .pin_bit_mask = BIT(pin_en) | BIT(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, ®_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, ®_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)
+{
+ 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;
+ const size_t n_words = 6;
+ srand(0);
+ for (size_t i = 0; i < n_words; ++i) {
+ src_32[i] = rand();
+ }
+ size_t len = n_words * sizeof(uint32_t);
+
+ TEST_ESP_OK(sdmmc_io_write_bytes(card, 1, scratch_area_reg, src, len));
+ ESP_LOG_BUFFER_HEX(TAG, src, len);
+
+ printf("Read back using CMD52\n");
+ uint8_t* dst = heap_caps_malloc(512, MALLOC_CAP_DMA);
+ for (size_t i = 0; i < len; ++i) {
+ TEST_ESP_OK(sdmmc_io_read_byte(card, 1, scratch_area_reg + i, &dst[i]));
+ }
+ ESP_LOG_BUFFER_HEX(TAG, dst, len);
+ TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, len);
+
+ printf("Read back using CMD53\n");
+ TEST_ESP_OK(sdmmc_io_read_bytes(card, 1, scratch_area_reg, dst, len));
+ ESP_LOG_BUFFER_HEX(TAG, dst, len);
+ TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, len);
+
+ 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);
+
+ for (int repeat = 0; repeat < 10; ++repeat) {
+ test_cmd52_read_write_single_byte(card);
+ test_cmd53_read_write_multiple_bytes(card);
+ }
+
+ 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);
+}
+
#define SDMMC_CLOCK_REG (DR_REG_SDMMC_BASE + 0x800)
+#define SDMMC_INTMASK_IO_SLOT1 BIT(17)
+#define SDMMC_INTMASK_IO_SLOT0 BIT(16)
#define SDMMC_INTMASK_EBE BIT(15)
#define SDMMC_INTMASK_ACD BIT(14)
#define SDMMC_INTMASK_SBE BIT(13)