sdmmc: add SDIO support

- Add SDIO support at protocol layer (probing, data transfer, interrupts)
- Add SDIO interrupts support in SDMMC host
- Add test (communicate with ESP32 in SDIO download mode)
This commit is contained in:
Ivan Grokhotkov
2018-03-06 17:57:52 +08:00
parent 5f97e6bebb
commit ee600784c5
9 changed files with 1223 additions and 191 deletions

View File

@@ -54,6 +54,12 @@
#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 */
@@ -97,6 +103,8 @@
/* 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)
@@ -364,4 +372,78 @@ static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len)
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_

View File

@@ -44,6 +44,8 @@ extern "C" {
.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, \
}
/**
@@ -166,6 +168,26 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz);
*/
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
*

View File

@@ -21,6 +21,7 @@
#include <stdint.h>
#include <stddef.h>
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
/**
* Decoded values from SD card Card Specific Data register
@@ -78,6 +79,7 @@ typedef struct {
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
@@ -101,6 +103,7 @@ typedef struct {
#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;
@@ -129,6 +132,8 @@ typedef struct {
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;
@@ -142,9 +147,11 @@ typedef struct {
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_

View File

@@ -43,6 +43,8 @@ extern "C" {
.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, \
}

View File

@@ -26,6 +26,7 @@
#include "driver/sdmmc_host.h"
#include "driver/periph_ctrl.h"
#include "sdmmc_private.h"
#include "freertos/semphr.h"
#define SDMMC_EVENT_QUEUE_LENGTH 32
@@ -40,9 +41,11 @@ typedef struct {
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;
@@ -58,6 +61,7 @@ static const sdmmc_slot_info_t s_slot_info[2] = {
.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,
@@ -65,6 +69,7 @@ static const sdmmc_slot_info_t s_slot_info[2] = {
.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
},
{
@@ -74,9 +79,11 @@ static const sdmmc_slot_info_t s_slot_info[2] = {
.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
}
};
@@ -84,6 +91,7 @@ static const sdmmc_slot_info_t s_slot_info[2] = {
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};
@@ -282,11 +290,19 @@ esp_err_t sdmmc_host_init()
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
@@ -297,7 +313,7 @@ esp_err_t sdmmc_host_init()
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
@@ -308,6 +324,8 @@ esp_err_t sdmmc_host_init()
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;
@@ -376,10 +394,17 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config)
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);
@@ -405,6 +430,8 @@ esp_err_t sdmmc_host_deinit()
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);
@@ -497,24 +524,62 @@ void sdmmc_host_dma_resume()
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;
@@ -522,8 +587,17 @@ static void sdmmc_isr(void* arg) {
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();
}

View File

@@ -29,7 +29,8 @@ extern "C" {
* 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
@@ -47,8 +48,10 @@ 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 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
@@ -61,8 +64,10 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
/**
* 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
@@ -72,6 +77,149 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
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

View File

@@ -26,7 +26,8 @@
#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
@@ -53,13 +54,14 @@ static esp_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);
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);
@@ -68,7 +70,12 @@ static esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
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)
{
@@ -77,28 +84,38 @@ 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);
@@ -120,51 +137,84 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
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.
*/
/* 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);
/* 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_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);
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;
}
host_ocr &= card->ocr;
err = sdmmc_io_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: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
sdmmc_io_enable_int(card);
}
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);
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: send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
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);
}
/* 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) {
@@ -178,20 +228,22 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
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
@@ -205,112 +257,118 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, 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) {
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;
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;
}
}
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;
}
if (++count % 16 == 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;
}
}
}
}
/* 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 % 10 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
/* 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).
@@ -331,15 +389,21 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
* 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;
@@ -357,6 +421,26 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
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) {
@@ -675,20 +759,6 @@ static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width)
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");
@@ -944,6 +1014,10 @@ static esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
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;
@@ -974,3 +1048,242 @@ out:
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);
}

View File

@@ -0,0 +1,382 @@
// 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, &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)
{
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);
}

View File

@@ -66,6 +66,8 @@
#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)