mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-01 19:54:32 +02:00
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:
@@ -54,6 +54,12 @@
|
|||||||
#define SD_APP_OP_COND 41 /* R3 */
|
#define SD_APP_OP_COND 41 /* R3 */
|
||||||
#define SD_APP_SEND_SCR 51 /* R1 */
|
#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 */
|
/* OCR bits */
|
||||||
#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
|
#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
|
||||||
#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */
|
#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */
|
||||||
@@ -97,6 +103,8 @@
|
|||||||
/* 48-bit response decoding (32 bits w/o CRC) */
|
/* 48-bit response decoding (32 bits w/o CRC) */
|
||||||
#define MMC_R1(resp) ((resp)[0])
|
#define MMC_R1(resp) ((resp)[0])
|
||||||
#define MMC_R3(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 SD_R6(resp) ((resp)[0])
|
||||||
#define MMC_R1_CURRENT_STATE(resp) (((resp)[0] >> 9) & 0xf)
|
#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;
|
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_
|
#endif //_SDMMC_DEFS_H_
|
||||||
|
@@ -44,6 +44,8 @@ extern "C" {
|
|||||||
.do_transaction = &sdmmc_host_do_transaction, \
|
.do_transaction = &sdmmc_host_do_transaction, \
|
||||||
.deinit = &sdmmc_host_deinit, \
|
.deinit = &sdmmc_host_deinit, \
|
||||||
.command_timeout_ms = 0, \
|
.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);
|
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
|
* @brief Disable SDMMC host and release allocated resources
|
||||||
*
|
*
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decoded values from SD card Card Specific Data register
|
* Decoded values from SD card Card Specific Data register
|
||||||
@@ -78,6 +79,7 @@ typedef struct {
|
|||||||
size_t datalen; /*!< length of data buffer */
|
size_t datalen; /*!< length of data buffer */
|
||||||
size_t blklen; /*!< block length */
|
size_t blklen; /*!< block length */
|
||||||
int flags; /*!< see below */
|
int flags; /*!< see below */
|
||||||
|
/** @cond */
|
||||||
#define SCF_ITSDONE 0x0001 /*!< command is complete */
|
#define SCF_ITSDONE 0x0001 /*!< command is complete */
|
||||||
#define SCF_CMD(flags) ((flags) & 0x00f0)
|
#define SCF_CMD(flags) ((flags) & 0x00f0)
|
||||||
#define SCF_CMD_AC 0x0000
|
#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_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_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||||
#define SCF_RSP_R7 (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 */
|
esp_err_t error; /*!< error returned from transfer */
|
||||||
int timeout_ms; /*!< response timeout, in milliseconds */
|
int timeout_ms; /*!< response timeout, in milliseconds */
|
||||||
} sdmmc_command_t;
|
} 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 (*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 (*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 (*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. */
|
int command_timeout_ms; /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */
|
||||||
} sdmmc_host_t;
|
} sdmmc_host_t;
|
||||||
|
|
||||||
@@ -142,9 +147,11 @@ typedef struct {
|
|||||||
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
|
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
|
||||||
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
|
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
|
||||||
uint16_t rca; /*!< RCA (Relative Card Address) */
|
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;
|
} sdmmc_card_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // _SDMMC_TYPES_H_
|
#endif // _SDMMC_TYPES_H_
|
||||||
|
@@ -43,6 +43,8 @@ extern "C" {
|
|||||||
.set_card_clk = &sdspi_host_set_card_clk, \
|
.set_card_clk = &sdspi_host_set_card_clk, \
|
||||||
.do_transaction = &sdspi_host_do_transaction, \
|
.do_transaction = &sdspi_host_do_transaction, \
|
||||||
.deinit = &sdspi_host_deinit, \
|
.deinit = &sdspi_host_deinit, \
|
||||||
|
.io_int_enable = NULL, \
|
||||||
|
.io_int_wait = NULL, \
|
||||||
.command_timeout_ms = 0, \
|
.command_timeout_ms = 0, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
#include "driver/sdmmc_host.h"
|
#include "driver/sdmmc_host.h"
|
||||||
#include "driver/periph_ctrl.h"
|
#include "driver/periph_ctrl.h"
|
||||||
#include "sdmmc_private.h"
|
#include "sdmmc_private.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
#define SDMMC_EVENT_QUEUE_LENGTH 32
|
#define SDMMC_EVENT_QUEUE_LENGTH 32
|
||||||
|
|
||||||
@@ -40,9 +41,11 @@ typedef struct {
|
|||||||
uint32_t d5;
|
uint32_t d5;
|
||||||
uint32_t d6;
|
uint32_t d6;
|
||||||
uint32_t d7;
|
uint32_t d7;
|
||||||
|
uint8_t d1_gpio;
|
||||||
uint8_t d3_gpio;
|
uint8_t d3_gpio;
|
||||||
uint8_t card_detect;
|
uint8_t card_detect;
|
||||||
uint8_t write_protect;
|
uint8_t write_protect;
|
||||||
|
uint8_t card_int;
|
||||||
uint8_t width;
|
uint8_t width;
|
||||||
} sdmmc_slot_info_t;
|
} 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,
|
.d1 = PERIPHS_IO_MUX_SD_DATA1_U,
|
||||||
.d2 = PERIPHS_IO_MUX_SD_DATA2_U,
|
.d2 = PERIPHS_IO_MUX_SD_DATA2_U,
|
||||||
.d3 = PERIPHS_IO_MUX_SD_DATA3_U,
|
.d3 = PERIPHS_IO_MUX_SD_DATA3_U,
|
||||||
|
.d1_gpio = 8,
|
||||||
.d3_gpio = 10,
|
.d3_gpio = 10,
|
||||||
.d4 = PERIPHS_IO_MUX_GPIO16_U,
|
.d4 = PERIPHS_IO_MUX_GPIO16_U,
|
||||||
.d5 = PERIPHS_IO_MUX_GPIO17_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,
|
.d7 = PERIPHS_IO_MUX_GPIO18_U,
|
||||||
.card_detect = HOST_CARD_DETECT_N_1_IDX,
|
.card_detect = HOST_CARD_DETECT_N_1_IDX,
|
||||||
.write_protect = HOST_CARD_WRITE_PRT_1_IDX,
|
.write_protect = HOST_CARD_WRITE_PRT_1_IDX,
|
||||||
|
.card_int = HOST_CARD_INT_N_1_IDX,
|
||||||
.width = 8
|
.width = 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -74,9 +79,11 @@ static const sdmmc_slot_info_t s_slot_info[2] = {
|
|||||||
.d1 = PERIPHS_IO_MUX_GPIO4_U,
|
.d1 = PERIPHS_IO_MUX_GPIO4_U,
|
||||||
.d2 = PERIPHS_IO_MUX_MTDI_U,
|
.d2 = PERIPHS_IO_MUX_MTDI_U,
|
||||||
.d3 = PERIPHS_IO_MUX_MTCK_U,
|
.d3 = PERIPHS_IO_MUX_MTCK_U,
|
||||||
|
.d1_gpio = 4,
|
||||||
.d3_gpio = 13,
|
.d3_gpio = 13,
|
||||||
.card_detect = HOST_CARD_DETECT_N_2_IDX,
|
.card_detect = HOST_CARD_DETECT_N_2_IDX,
|
||||||
.write_protect = HOST_CARD_WRITE_PRT_2_IDX,
|
.write_protect = HOST_CARD_WRITE_PRT_2_IDX,
|
||||||
|
.card_int = HOST_CARD_INT_N_2_IDX,
|
||||||
.width = 4
|
.width = 4
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -84,6 +91,7 @@ static const sdmmc_slot_info_t s_slot_info[2] = {
|
|||||||
static const char* TAG = "sdmmc_periph";
|
static const char* TAG = "sdmmc_periph";
|
||||||
static intr_handle_t s_intr_handle;
|
static intr_handle_t s_intr_handle;
|
||||||
static QueueHandle_t s_event_queue;
|
static QueueHandle_t s_event_queue;
|
||||||
|
static SemaphoreHandle_t s_io_intr_event;
|
||||||
|
|
||||||
size_t s_slot_width[2] = {1,1};
|
size_t s_slot_width[2] = {1,1};
|
||||||
|
|
||||||
@@ -282,11 +290,19 @@ esp_err_t sdmmc_host_init()
|
|||||||
if (!s_event_queue) {
|
if (!s_event_queue) {
|
||||||
return ESP_ERR_NO_MEM;
|
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
|
// Attach interrupt handler
|
||||||
esp_err_t ret = esp_intr_alloc(ETS_SDIO_HOST_INTR_SOURCE, 0, &sdmmc_isr, s_event_queue, &s_intr_handle);
|
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) {
|
if (ret != ESP_OK) {
|
||||||
vQueueDelete(s_event_queue);
|
vQueueDelete(s_event_queue);
|
||||||
s_event_queue = NULL;
|
s_event_queue = NULL;
|
||||||
|
vSemaphoreDelete(s_io_intr_event);
|
||||||
|
s_io_intr_event = NULL;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
// Enable interrupts
|
// Enable interrupts
|
||||||
@@ -297,7 +313,7 @@ esp_err_t sdmmc_host_init()
|
|||||||
SDMMC_INTMASK_RCRC | SDMMC_INTMASK_DCRC |
|
SDMMC_INTMASK_RCRC | SDMMC_INTMASK_DCRC |
|
||||||
SDMMC_INTMASK_RTO | SDMMC_INTMASK_DTO | SDMMC_INTMASK_HTO |
|
SDMMC_INTMASK_RTO | SDMMC_INTMASK_DTO | SDMMC_INTMASK_HTO |
|
||||||
SDMMC_INTMASK_SBE | SDMMC_INTMASK_EBE |
|
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;
|
SDMMC.ctrl.int_enable = 1;
|
||||||
|
|
||||||
// Enable DMA
|
// Enable DMA
|
||||||
@@ -308,6 +324,8 @@ esp_err_t sdmmc_host_init()
|
|||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
vQueueDelete(s_event_queue);
|
vQueueDelete(s_event_queue);
|
||||||
s_event_queue = NULL;
|
s_event_queue = NULL;
|
||||||
|
vSemaphoreDelete(s_io_intr_event);
|
||||||
|
s_io_intr_event = NULL;
|
||||||
esp_intr_free(s_intr_handle);
|
esp_intr_free(s_intr_handle);
|
||||||
s_intr_handle = NULL;
|
s_intr_handle = NULL;
|
||||||
return ret;
|
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);
|
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) {
|
if (gpio_cd != -1) {
|
||||||
gpio_set_direction(gpio_cd, GPIO_MODE_INPUT);
|
gpio_set_direction(gpio_cd, GPIO_MODE_INPUT);
|
||||||
gpio_matrix_in(gpio_cd, pslot->card_detect, 0);
|
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) {
|
if (gpio_wp != -1) {
|
||||||
gpio_set_direction(gpio_wp, GPIO_MODE_INPUT);
|
gpio_set_direction(gpio_wp, GPIO_MODE_INPUT);
|
||||||
gpio_matrix_in(gpio_wp, pslot->write_protect, 0);
|
gpio_matrix_in(gpio_wp, pslot->write_protect, 0);
|
||||||
@@ -405,6 +430,8 @@ esp_err_t sdmmc_host_deinit()
|
|||||||
s_intr_handle = NULL;
|
s_intr_handle = NULL;
|
||||||
vQueueDelete(s_event_queue);
|
vQueueDelete(s_event_queue);
|
||||||
s_event_queue = NULL;
|
s_event_queue = NULL;
|
||||||
|
vQueueDelete(s_io_intr_event);
|
||||||
|
s_io_intr_event = NULL;
|
||||||
sdmmc_host_input_clk_disable();
|
sdmmc_host_input_clk_disable();
|
||||||
sdmmc_host_transaction_handler_deinit();
|
sdmmc_host_transaction_handler_deinit();
|
||||||
periph_module_disable(PERIPH_SDMMC_MODULE);
|
periph_module_disable(PERIPH_SDMMC_MODULE);
|
||||||
@@ -497,24 +524,62 @@ void sdmmc_host_dma_resume()
|
|||||||
SDMMC.pldmnd = 1;
|
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
|
* @brief SDMMC interrupt handler
|
||||||
*
|
*
|
||||||
* Ignoring SDIO and streaming read/writes for now (and considering just SD memory cards),
|
* All communication in SD protocol is driven by the master, and the hardware
|
||||||
* all communication is driven by the master, and the hardware handles things like stop
|
* handles things like stop commands automatically.
|
||||||
* commands automatically. So the interrupt handler doesn't need to do much, we just push
|
* So the interrupt handler doesn't need to do much, we just push interrupt
|
||||||
* interrupt status into a queue, clear interrupt flags, and let the task currently doing
|
* status into a queue, clear interrupt flags, and let the task currently
|
||||||
* communication figure out what to do next.
|
* 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
|
* Card detect interrupts pose a small issue though, because if a card is
|
||||||
* out a few times, while there is no task to process the events, event queue can become
|
* plugged in and out a few times, while there is no task to process
|
||||||
* full and some card detect events may be dropped. We ignore this problem for now, since
|
* the events, event queue can become full and some card detect events
|
||||||
* the there are no other interesting events which can get lost due to this.
|
* 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) {
|
static void sdmmc_isr(void* arg) {
|
||||||
QueueHandle_t queue = (QueueHandle_t) arg;
|
QueueHandle_t queue = (QueueHandle_t) arg;
|
||||||
sdmmc_event_t event;
|
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;
|
SDMMC.rintsts.val = pending;
|
||||||
event.sdmmc_status = pending;
|
event.sdmmc_status = pending;
|
||||||
|
|
||||||
@@ -522,8 +587,17 @@ static void sdmmc_isr(void* arg) {
|
|||||||
SDMMC.idsts.val = dma_pending;
|
SDMMC.idsts.val = dma_pending;
|
||||||
event.dma_status = dma_pending & 0x1f;
|
event.dma_status = dma_pending & 0x1f;
|
||||||
|
|
||||||
int higher_priority_task_awoken = pdFALSE;
|
if (pending != 0 || dma_pending != 0) {
|
||||||
xQueueSendFromISR(queue, &event, &higher_priority_task_awoken);
|
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) {
|
if (higher_priority_task_awoken == pdTRUE) {
|
||||||
portYIELD_FROM_ISR();
|
portYIELD_FROM_ISR();
|
||||||
}
|
}
|
||||||
|
@@ -29,7 +29,8 @@ extern "C" {
|
|||||||
* Support for MMC/eMMC cards will be added later.
|
* Support for MMC/eMMC cards will be added later.
|
||||||
*
|
*
|
||||||
* @param host pointer to structure defining host controller
|
* @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
|
* @return
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
* - One of the error codes from SDMMC host controller
|
* - 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
|
* Write given number of sectors to SD/MMC card
|
||||||
*
|
*
|
||||||
* @param card pointer to card information structure previously initialized using sdmmc_card_init
|
* @param card pointer to card information structure previously initialized
|
||||||
* @param src pointer to data buffer to read data from; data size must be equal to sector_count * card->csd.sector_size
|
* 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 start_sector sector where to start writing
|
||||||
* @param sector_count number of sectors to write
|
* @param sector_count number of sectors to write
|
||||||
* @return
|
* @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
|
* Write given number of sectors to SD/MMC card
|
||||||
*
|
*
|
||||||
* @param card pointer to card information structure previously initialized using sdmmc_card_init
|
* @param card pointer to card information structure previously initialized
|
||||||
* @param dst pointer to data buffer to write into; buffer size must be at least sector_count * card->csd.sector_size
|
* 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 start_sector sector where to start reading
|
||||||
* @param sector_count number of sectors to read
|
* @param sector_count number of sectors to read
|
||||||
* @return
|
* @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,
|
esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
|
||||||
size_t start_sector, size_t sector_count);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -26,7 +26,8 @@
|
|||||||
#include "sys/param.h"
|
#include "sys/param.h"
|
||||||
#include "soc/soc_memory_layout.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
|
/* 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
|
* 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,
|
uint32_t mode, uint32_t group, uint32_t function,
|
||||||
sdmmc_switch_func_rsp_t* resp);
|
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(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_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_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_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_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_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_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_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 esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
|
||||||
static uint32_t get_host_ocr(float voltage);
|
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);
|
size_t start_block, size_t block_count);
|
||||||
static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
|
static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
|
||||||
size_t start_block, size_t block_count);
|
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)
|
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_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));
|
memset(card, 0, sizeof(*card));
|
||||||
memcpy(&card->host, config, sizeof(*config));
|
memcpy(&card->host, config, sizeof(*config));
|
||||||
const bool is_spi = host_is_spi(card);
|
const bool is_spi = host_is_spi(card);
|
||||||
|
|
||||||
if ( !is_spi ) {
|
if (!is_spi) {
|
||||||
//check HOST flags compatible with slot configuration.
|
// Check if host flags are compatible with slot configuration.
|
||||||
int slot_bit_width = config->get_bus_width(config->slot);
|
sdmmc_fix_host_flags(card);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------- 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 */
|
/* 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) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err);
|
ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err);
|
||||||
return 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);
|
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
|
||||||
sdmmc_send_cmd_go_idle_state(card);
|
sdmmc_send_cmd_go_idle_state(card);
|
||||||
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
|
/* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
|
||||||
* ranges the card can support. This step is skipped since 1.8V isn't
|
* Non-IO cards will not respond to this command.
|
||||||
* supported on the ESP32.
|
*/
|
||||||
*/
|
err = sdmmc_io_send_op_cond(card, 0, &card->ocr);
|
||||||
|
|
||||||
/* 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) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
|
ESP_LOGD(TAG, "%s: io_send_op_cond (1) returned 0x%x; not IO card", __func__, err);
|
||||||
return err;
|
card->is_sdio = 0;
|
||||||
}
|
card->is_mem = 1;
|
||||||
if (is_spi) {
|
} else {
|
||||||
err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
|
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) {
|
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;
|
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.
|
if (card->is_mem) {
|
||||||
* Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41
|
/* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
|
||||||
* response.
|
* ranges the card can support. This step is skipped since 1.8V isn't
|
||||||
*/
|
* supported on the ESP32.
|
||||||
host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK));
|
*/
|
||||||
ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
|
|
||||||
|
/* 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 */
|
/* Read and decode the contents of CID register */
|
||||||
if (!is_spi) {
|
if (!is_spi) {
|
||||||
err = sddmc_send_cmd_all_send_cid(card, &card->cid);
|
if (card->is_mem) {
|
||||||
if (err != ESP_OK) {
|
err = sddmc_send_cmd_all_send_cid(card, &card->cid);
|
||||||
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
|
if (err != ESP_OK) {
|
||||||
return err;
|
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
|
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
|
||||||
if (err != ESP_OK) {
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (card->is_mem) {
|
||||||
/* Get and decode the contents of CSD register. Determine card capacity. */
|
/* Get and decode the contents of CSD register. Determine card capacity. */
|
||||||
err = sdmmc_send_cmd_send_csd(card, &card->csd);
|
err = sdmmc_send_cmd_send_csd(card, &card->csd);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "%s: send_csd (1) returned 0x%x", __func__, err);
|
ESP_LOGE(TAG, "%s: send_csd (1) returned 0x%x", __func__, err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
|
const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
|
||||||
if (!(card->ocr & SD_OCR_SDHC_CAP) &&
|
if (!(card->ocr & SD_OCR_SDHC_CAP) &&
|
||||||
card->csd.capacity > max_sdsc_capacity) {
|
card->csd.capacity > max_sdsc_capacity) {
|
||||||
ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
|
ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
|
||||||
__func__, card->csd.capacity, max_sdsc_capacity);
|
__func__, card->csd.capacity, max_sdsc_capacity);
|
||||||
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
|
/* 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
|
* 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.
|
if (card->is_mem) {
|
||||||
* We don't use this feature and set the block length to 512 bytes,
|
/* SDSC cards support configurable data block lengths.
|
||||||
* same as the block length for SDHC cards.
|
* 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->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) {
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the contents of SCR register: bus width and the version of SD spec
|
if (card->is_mem) {
|
||||||
* supported by the card.
|
/* If the host has been initialized with 4-bit bus support, and the card
|
||||||
* In SD mode, this is the first command which uses D0 line. Errors at
|
* supports 4-bit bus, switch to 4-bit bus now.
|
||||||
* this step usually indicate connection issue or lack of pull-up resistor.
|
*/
|
||||||
*/
|
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
|
||||||
err = sdmmc_send_cmd_send_scr(card, &card->scr);
|
(card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
|
||||||
if (err != ESP_OK) {
|
ESP_LOGD(TAG, "switching to 4-bit bus mode");
|
||||||
ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
|
err = sdmmc_send_cmd_set_bus_width(card, 4);
|
||||||
return err;
|
if (err != ESP_OK) {
|
||||||
}
|
ESP_LOGE(TAG, "set_bus_width failed");
|
||||||
|
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.
|
err = (*config->set_bus_width)(config->slot, 4);
|
||||||
*/
|
if (err != ESP_OK) {
|
||||||
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
|
ESP_LOGE(TAG, "slot->set_bus_width failed");
|
||||||
(card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
|
return err;
|
||||||
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) {
|
/* Wait for the card to be ready for data transfers */
|
||||||
ESP_LOGE(TAG, "slot->set_bus_width failed");
|
uint32_t status = 0;
|
||||||
return err;
|
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
|
/* So far initialization has been done using 400kHz clock. Determine the
|
||||||
* clock rate which both host and the card support, and switch to it.
|
* clock rate which both host and the card support, and switch to it.
|
||||||
*/
|
*/
|
||||||
bool freq_switched = false;
|
bool freq_switched = false;
|
||||||
if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
|
if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
|
||||||
!is_spi /* SPI doesn't support >26MHz in some cases */) {
|
!is_spi /* SPI doesn't support >26MHz in some cases */) {
|
||||||
/* This will determine if the card supports SWITCH_FUNC command,
|
if (card->is_mem) {
|
||||||
* and high speed mode. If the cards supports both, this will enable
|
err = sdmmc_enable_hs_mode_and_check(card);
|
||||||
* high speed mode at the card side.
|
} else {
|
||||||
*/
|
err = sdmmc_io_enable_hs_mode(card);
|
||||||
err = sdmmc_enable_hs_mode(card);
|
}
|
||||||
|
|
||||||
if (err == ESP_ERR_NOT_SUPPORTED) {
|
if (err == ESP_ERR_NOT_SUPPORTED) {
|
||||||
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
|
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
|
||||||
} else if (err != ESP_OK) {
|
} else if (err != ESP_OK) {
|
||||||
/* some other error */
|
|
||||||
return err;
|
return err;
|
||||||
} else { /* ESP_OK */
|
} else {
|
||||||
/* HS mode has been enabled on the card.
|
ESP_LOGD(TAG, "%s: switching host to HS mode", __func__);
|
||||||
* Read CSD again, it should now indicate that the card supports
|
/* ESP_OK, HS mode has been enabled on the card side.
|
||||||
* 50MHz clock.
|
* Switch the host to HS mode.
|
||||||
* 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);
|
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
|
||||||
if (err != ESP_OK) {
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
err = sdmmc_send_cmd_send_csd(card, &card->csd);
|
freq_switched = true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* All SD cards must support default speed mode (25MHz).
|
/* 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.
|
* an indicator of potential signal integrity issues.
|
||||||
*/
|
*/
|
||||||
if (freq_switched) {
|
if (freq_switched) {
|
||||||
sdmmc_scr_t scr_tmp;
|
if (card->is_mem) {
|
||||||
err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
|
sdmmc_scr_t scr_tmp;
|
||||||
if (err != ESP_OK) {
|
err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
|
||||||
ESP_LOGE(TAG, "%s: send_scr (2) returned 0x%x", __func__, err);
|
if (err != ESP_OK) {
|
||||||
return err;
|
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");
|
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
|
||||||
return ESP_ERR_INVALID_RESPONSE;
|
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;
|
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);
|
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)
|
static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
|
||||||
{
|
{
|
||||||
if (card->host.command_timeout_ms != 0) {
|
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);
|
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)
|
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");
|
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)
|
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 ||
|
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
|
||||||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
|
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
|
||||||
return ESP_ERR_NOT_SUPPORTED;
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
@@ -974,3 +1048,242 @@ out:
|
|||||||
free(response);
|
free(response);
|
||||||
return err;
|
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);
|
||||||
|
}
|
||||||
|
382
components/sdmmc/test/test_sdio.c
Normal file
382
components/sdmmc/test/test_sdio.c
Normal 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, ®_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);
|
||||||
|
}
|
||||||
|
|
@@ -66,6 +66,8 @@
|
|||||||
|
|
||||||
#define SDMMC_CLOCK_REG (DR_REG_SDMMC_BASE + 0x800)
|
#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_EBE BIT(15)
|
||||||
#define SDMMC_INTMASK_ACD BIT(14)
|
#define SDMMC_INTMASK_ACD BIT(14)
|
||||||
#define SDMMC_INTMASK_SBE BIT(13)
|
#define SDMMC_INTMASK_SBE BIT(13)
|
||||||
|
Reference in New Issue
Block a user