sdmmc: support SDIO over SPI

This commit is contained in:
michael 2018-12-29 02:04:37 +08:00 committed by Michael (XIAO Xufeng)
parent 3ec7bec6a7
commit 8a364b4bdf
12 changed files with 350 additions and 163 deletions

View file

@ -103,6 +103,8 @@
#define SD_SPI_R1_PARAM_ERR (1<<6)
#define SD_SPI_R1_NO_RESPONSE (1<<7)
#define SDIO_R1_FUNC_NUM_ERR (1<<4)
/* 48-bit response decoding (32 bits w/o CRC) */
#define MMC_R1(resp) ((resp)[0])
#define MMC_R3(resp) ((resp)[0])
@ -117,6 +119,13 @@
#define SD_SPI_R3(resp) ((resp)[0])
#define SD_SPI_R7(resp) ((resp)[0])
/* SPI mode data response decoding */
#define SD_SPI_DATA_RSP_VALID(resp_byte) (((resp_byte)&0x11)==0x1)
#define SD_SPI_DATA_RSP(resp_byte) (((resp_byte)>>1)&0x7)
#define SD_SPI_DATA_ACCEPTED 0x2
#define SD_SPI_DATA_CRC_ERROR 0x5
#define SD_SPI_DATA_WR_ERROR 0x6
/* RCA argument and response */
#define MMC_ARG_RCA(rca) ((rca) << 16)
#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16)
@ -258,7 +267,7 @@
#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \
(SD_CSD_C_SIZE_MULT((resp))+2))
#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22)
#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10)
#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10)
#define SD_CSD_V2_BL_LEN 0x9 /* 512 */
#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3)
#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3)
@ -429,6 +438,7 @@ static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len)
#define CCCR_BUS_WIDTH_1 (0<<0)
#define CCCR_BUS_WIDTH_4 (2<<0)
#define CCCR_BUS_WIDTH_8 (3<<0)
#define CCCR_BUS_WIDTH_ECSI (1<<5)
#define SD_IO_CCCR_CARD_CAP 0x08
#define CCCR_CARD_CAP_LSC BIT(6)
#define CCCR_CARD_CAP_4BLS BIT(7)

View file

@ -60,15 +60,15 @@ typedef struct {
gpio_num_t gpio_wp; ///< GPIO number of write protect signal
uint8_t width; ///< Bus width used by the slot (might be less than the max width supported)
uint32_t flags; ///< Features used by this slot
#define SDMMC_SLOT_FLAG_INTERNAL_PULLUP BIT(0)
#define SDMMC_SLOT_FLAG_INTERNAL_PULLUP BIT(0)
/**< Enable internal pullups on enabled pins. The internal pullups
are insufficient however, please make sure external pullups are
connected on the bus. This is for debug / example purpose only.
*/
} sdmmc_slot_config_t;
#define SDMMC_SLOT_NO_CD ((gpio_num_t) -1) ///< indicates that card detect line is not used
#define SDMMC_SLOT_NO_WP ((gpio_num_t) -1) ///< indicates that write protect line is not used
#define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used
#define SDMMC_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used
#define SDMMC_SLOT_WIDTH_DEFAULT 0 ///< use the default width for the slot (8 for slot 0, 4 for slot 1)
/**
@ -222,14 +222,14 @@ esp_err_t sdmmc_host_deinit();
/**
* @brief Enable the pull-ups of sd pins.
*
*
* @note You should always place actual pullups on the lines instead of using
* this function. Internal pullup resistance are high and not sufficient, may
* cause instability in products. This is for debug or examples only.
*
*
* @param slot Slot to use, normally set it to 1.
* @param width Bit width of your configuration, 1 or 4.
*
*
* @return
* - ESP_OK: if success
* - ESP_ERR_INVALID_ARG: if configured width larger than maximum the slot can

View file

@ -156,7 +156,11 @@ typedef struct {
typedef struct {
sdmmc_host_t host; /*!< Host with which the card is associated */
uint32_t ocr; /*!< OCR (Operation Conditions Register) value */
sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */
union {
sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */
sdmmc_response_t raw_cid; /*!< raw CID of MMC card to be decoded
after the CSD is fetched in the data transfer mode*/
};
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */

View file

@ -45,8 +45,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, \
.io_int_enable = &sdspi_host_io_int_enable, \
.io_int_wait = &sdspi_host_io_int_wait, \
.command_timeout_ms = 0, \
}
@ -60,11 +60,13 @@ typedef struct {
gpio_num_t gpio_cs; ///< GPIO number of CS signal
gpio_num_t gpio_cd; ///< GPIO number of card detect signal
gpio_num_t gpio_wp; ///< GPIO number of write protect signal
gpio_num_t gpio_int; ///< GPIO number of interrupt line (input) for SDIO card.
int dma_channel; ///< DMA channel to be used by SPI driver (1 or 2)
} sdspi_slot_config_t;
#define SDSPI_SLOT_NO_CD ((gpio_num_t) -1) ///< indicates that card detect line is not used
#define SDSPI_SLOT_NO_WP ((gpio_num_t) -1) ///< indicates that write protect line is not used
#define SDSPI_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used
#define SDSPI_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used
#define SDSPI_SLOT_NO_INT GPIO_NUM_NC ///< indicates that interrupt line is not used
/**
* Macro defining default configuration of SPI host
@ -76,6 +78,7 @@ typedef struct {
.gpio_cs = GPIO_NUM_13, \
.gpio_cd = SDSPI_SLOT_NO_CD, \
.gpio_wp = SDSPI_SLOT_NO_WP, \
.gpio_int = GPIO_NUM_NC, \
.dma_channel = 1 \
}
@ -95,6 +98,8 @@ esp_err_t sdspi_host_init();
*
* @note This function is not thread safe
*
* @note The SDIO over sdspi needs an extra interrupt line. Call ``gpio_install_isr_service()`` before this function.
*
* @param slot SPI controller to use (HSPI_HOST or VSPI_HOST)
* @param slot_config pointer to slot configuration structure
*
@ -156,6 +161,27 @@ esp_err_t sdspi_host_set_card_clk(int slot, uint32_t freq_khz);
*/
esp_err_t sdspi_host_deinit();
/**
* @brief Enable SDIO interrupt.
*
* @param slot SPI controller to use (HSPI_HOST or VSPI_HOST)
*
* @return
* - ESP_OK on success
*/
esp_err_t sdspi_host_io_int_enable(int slot);
/**
* @brief Wait for SDIO interrupt until timeout.
*
* @param slot SPI controller to use (HSPI_HOST or VSPI_HOST)
* @param timeout_ticks Ticks to wait before timeout.
*
* @return
* - ESP_OK on success
*/
esp_err_t sdspi_host_io_int_wait(int slot, TickType_t timeout_ticks);
#ifdef __cplusplus
}
#endif

View file

@ -292,7 +292,7 @@ static void configure_pin(int pin)
{
const int sdmmc_func = 3;
const int drive_strength = 3;
assert(pin!=-1);
assert(pin!=GPIO_NUM_NC);
gpio_pulldown_dis(pin);
uint32_t reg = GPIO_PIN_MUX_REG[pin];
@ -541,7 +541,7 @@ esp_err_t sdmmc_host_io_int_enable(int slot)
}
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.
*
@ -560,7 +560,7 @@ esp_err_t sdmmc_host_io_int_wait(int slot, TickType_t timeout_ticks)
*/
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 {

View file

@ -44,6 +44,7 @@ typedef struct {
uint8_t gpio_cs; //!< CS GPIO
uint8_t gpio_cd; //!< Card detect GPIO, or GPIO_UNUSED
uint8_t gpio_wp; //!< Write protect GPIO, or GPIO_UNUSED
uint8_t gpio_int; //!< Write protect GPIO, or GPIO_UNUSED
/// Set to 1 if the higher layer has asked the card to enable CRC checks
uint8_t data_crc_enabled : 1;
/// Number of transactions in 'transactions' array which are in use
@ -53,6 +54,8 @@ typedef struct {
uint8_t* block_buf;
/// array with SDSPI_TRANSACTION_COUNT transaction structures
spi_transaction_t* transactions;
/// semaphore of gpio interrupt
SemaphoreHandle_t semphr_int;
} slot_info_t;
static slot_info_t s_slots[3];
@ -60,14 +63,14 @@ static const char *TAG = "sdspi_host";
/// Functions to send out different kinds of commands
static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
uint8_t *data, uint32_t rx_length);
uint8_t *data, uint32_t rx_length, bool need_stop_command);
static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
const uint8_t *data, uint32_t tx_length);
const uint8_t *data, uint32_t tx_length, bool multi_block, bool stop_trans);
static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd);
static esp_err_t poll_cmd_response(int slot, sdspi_hw_cmd_t *cmd);
static esp_err_t shift_cmd_response(sdspi_hw_cmd_t *cmd, int sent_bytes);
/// A few helper functions
@ -214,7 +217,7 @@ static esp_err_t init_spi_dev(int slot, int clock_speed_hz)
.mode = 0,
// For SD cards, CS must stay low during the whole read/write operation,
// rather than a single SPI transaction.
.spics_io_num = -1,
.spics_io_num = GPIO_NUM_NC,
.queue_size = SDSPI_TRANSACTION_COUNT,
};
return spi_bus_add_device((spi_host_device_t) slot, &devcfg, &s_slots[slot].handle);
@ -237,6 +240,10 @@ esp_err_t sdspi_host_deinit()
spi_bus_free((spi_host_device_t) i);
s_slots[i].handle = NULL;
}
if (s_slots[i].semphr_int) {
vSemaphoreDelete(s_slots[i].semphr_int);
s_slots[i].semphr_int = NULL;
}
}
return ESP_OK;
}
@ -253,6 +260,17 @@ esp_err_t sdspi_host_set_card_clk(int slot, uint32_t freq_khz)
return init_spi_dev(slot, freq_khz * 1000);
}
static void gpio_intr(void* arg)
{
BaseType_t awoken = pdFALSE;
slot_info_t* slot = (slot_info_t*)arg;
xSemaphoreGiveFromISR(slot->semphr_int, &awoken);
gpio_intr_disable(slot->gpio_int);
if (awoken) {
portYIELD_FROM_ISR();
}
}
esp_err_t sdspi_host_init_slot(int slot, const sdspi_slot_config_t* slot_config)
{
ESP_LOGD(TAG, "%s: SPI%d miso=%d mosi=%d sck=%d cs=%d cd=%d wp=%d, dma_ch=%d",
@ -271,8 +289,8 @@ esp_err_t sdspi_host_init_slot(int slot, const sdspi_slot_config_t* slot_config)
.miso_io_num = slot_config->gpio_miso,
.mosi_io_num = slot_config->gpio_mosi,
.sclk_io_num = slot_config->gpio_sck,
.quadwp_io_num = -1,
.quadhd_io_num = -1
.quadwp_io_num = GPIO_NUM_NC,
.quadhd_io_num = GPIO_NUM_NC
};
// Initialize SPI bus
@ -287,8 +305,7 @@ esp_err_t sdspi_host_init_slot(int slot, const sdspi_slot_config_t* slot_config)
ret = init_spi_dev(slot, SDMMC_FREQ_PROBING * 1000);
if (ret != ESP_OK) {
ESP_LOGD(TAG, "spi_bus_add_device failed with rc=0x%x", ret);
spi_bus_free(host);
return ret;
goto cleanup;
}
// Configure CS pin
@ -302,10 +319,7 @@ esp_err_t sdspi_host_init_slot(int slot, const sdspi_slot_config_t* slot_config)
ret = gpio_config(&io_conf);
if (ret != ESP_OK) {
ESP_LOGD(TAG, "gpio_config (CS) failed with rc=0x%x", ret);
spi_bus_remove_device(spi_handle(slot));
s_slots[slot].handle = NULL;
spi_bus_free(host);
return ret;
goto cleanup;
}
cs_high(slot);
@ -334,22 +348,62 @@ esp_err_t sdspi_host_init_slot(int slot, const sdspi_slot_config_t* slot_config)
ret = gpio_config(&io_conf);
if (ret != ESP_OK) {
ESP_LOGD(TAG, "gpio_config (CD/WP) failed with rc=0x%x", ret);
spi_bus_remove_device(spi_handle(slot));
s_slots[slot].handle = NULL;
spi_bus_free(host);
return ret;
goto cleanup;
}
}
if (slot_config->gpio_int != SDSPI_SLOT_NO_INT) {
s_slots[slot].gpio_int = slot_config->gpio_int;
io_conf = (gpio_config_t) {
.intr_type = GPIO_INTR_LOW_LEVEL,
.mode = GPIO_MODE_INPUT,
.pull_up_en = true,
.pin_bit_mask = (1 << slot_config->gpio_int),
};
ret = gpio_config(&io_conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "gpio_config (interrupt) failed with rc=0x%x", ret);
goto cleanup;
}
gpio_intr_disable(slot_config->gpio_int);
s_slots[slot].semphr_int = xSemaphoreCreateBinary();
if (s_slots[slot].semphr_int == NULL) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
// 1. the interrupt is better to be disabled before the ISR is registered
// 2. the semaphore MUST be initialized before the ISR is registered
// 3. the gpio_int member should be filled before the ISR is registered
ret = gpio_isr_handler_add(slot_config->gpio_int, &gpio_intr, &s_slots[slot]);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "gpio_isr_handle_add failed with rc=0x%x", ret);
goto cleanup;
}
} else {
s_slots[slot].gpio_int = GPIO_UNUSED;
}
s_slots[slot].transactions = calloc(SDSPI_TRANSACTION_COUNT, sizeof(spi_transaction_t));
if (s_slots[slot].transactions == NULL) {
spi_bus_remove_device(spi_handle(slot));
s_slots[slot].handle = NULL;
spi_bus_free(host);
return ESP_ERR_NO_MEM;
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
return ESP_OK;
cleanup:
if (s_slots[slot].semphr_int) {
vSemaphoreDelete(s_slots[slot].semphr_int);
s_slots[slot].semphr_int = NULL;
}
if (s_slots[slot].handle) {
spi_bus_remove_device(spi_handle(slot));
s_slots[slot].handle = NULL;
}
spi_bus_free(host);
return ret;
}
@ -383,10 +437,13 @@ esp_err_t sdspi_host_start_command(int slot, sdspi_hw_cmd_t *cmd, void *data,
esp_err_t ret = ESP_OK;
cs_low(slot);
if (flags & SDSPI_CMD_FLAG_DATA) {
const bool multi_block = flags & SDSPI_CMD_FLAG_MULTI_BLK;
//send stop transmission token only when multi-block write and non-SDIO mode
const bool stop_transmission = multi_block && !(flags & SDSPI_CMD_FLAG_RSP_R5);
if (flags & SDSPI_CMD_FLAG_WRITE) {
ret = start_command_write_blocks(slot, cmd, data, data_size);
ret = start_command_write_blocks(slot, cmd, data, data_size, multi_block, stop_transmission);
} else {
ret = start_command_read_blocks(slot, cmd, data, data_size);
ret = start_command_read_blocks(slot, cmd, data, data_size, stop_transmission);
}
} else {
ret = start_command_default(slot, flags, cmd);
@ -417,14 +474,20 @@ static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd)
cmd_size = SDSPI_CMD_R2_SIZE;
} else if (flags & SDSPI_CMD_FLAG_RSP_R3) {
cmd_size = SDSPI_CMD_R3_SIZE;
} else if (flags & SDSPI_CMD_FLAG_RSP_R4) {
cmd_size = SDSPI_CMD_R4_SIZE;
} else if (flags & SDSPI_CMD_FLAG_RSP_R5) {
cmd_size = SDSPI_CMD_R5_SIZE;
} else if (flags & SDSPI_CMD_FLAG_RSP_R7) {
cmd_size = SDSPI_CMD_R7_SIZE;
}
//add extra clocks to avoid polling
cmd_size += (SDSPI_NCR_MAX_SIZE-SDSPI_NCR_MIN_SIZE);
spi_transaction_t t = {
.flags = 0,
.length = cmd_size * 8,
.tx_buffer = cmd,
.rx_buffer = cmd
.rx_buffer = cmd,
};
esp_err_t ret = spi_device_transmit(spi_handle(slot), &t);
if (cmd->cmd_index == MMC_STOP_TRANSMISSION) {
@ -440,11 +503,11 @@ static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd)
ESP_LOGV(TAG, "%s: ignoring response byte", __func__);
cmd->r1 = 0x00;
}
ret = poll_cmd_response(slot, cmd);
if (ret != ESP_OK) {
ESP_LOGD(TAG, "%s: poll_cmd_response returned 0x%x", __func__, ret);
return ret;
}
// we have sent and received bytes with enough length.
// now shift the response to match the offset of sdspi_hw_cmd_t
ret = shift_cmd_response(cmd, cmd_size);
if (ret != ESP_OK) return ESP_ERR_TIMEOUT;
return ESP_OK;
}
@ -478,39 +541,6 @@ static esp_err_t poll_busy(int slot, spi_transaction_t* t, int timeout_ms)
return ESP_ERR_TIMEOUT;
}
// Wait for response token
static esp_err_t poll_response_token(int slot, spi_transaction_t* t, int timeout_ms)
{
uint8_t t_rx;
*t = (spi_transaction_t) {
.tx_buffer = &t_rx,
.flags = SPI_TRANS_USE_RXDATA,
.length = 8,
};
esp_err_t ret;
uint64_t t_end = esp_timer_get_time() + timeout_ms * 1000;
do {
t_rx = SDSPI_MOSI_IDLE_VAL;
t->rx_data[0] = 0;
ret = spi_device_transmit(spi_handle(slot), t);
if (ret != ESP_OK) {
return ret;
}
if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_OK) {
return ESP_OK;
}
if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_CRC_ERR) {
return ESP_ERR_INVALID_CRC;
}
if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_WRITE_ERR) {
return ESP_ERR_INVALID_RESPONSE;
}
} while (esp_timer_get_time() < t_end);
ESP_LOGD(TAG, "%s: timeout", __func__);
return ESP_ERR_TIMEOUT;
}
// Wait for data token, reading 8 bytes at a time.
// If the token is found, write all subsequent bytes to extra_ptr,
// and store the number of bytes written to extra_size.
@ -554,27 +584,24 @@ static esp_err_t poll_data_token(int slot, spi_transaction_t* t,
return ESP_ERR_TIMEOUT;
}
static esp_err_t poll_cmd_response(int slot, sdspi_hw_cmd_t *cmd)
// the r1 respond could appear 1-8 clocks after the command token is sent
// this function search for r1 in the buffer after 1 clocks to max 8 clocks
// then shift the data after R1, to match the definition of sdspi_hw_cmd_t.
static esp_err_t shift_cmd_response(sdspi_hw_cmd_t* cmd, int sent_bytes)
{
int response_delay_bytes = SDSPI_RESPONSE_MAX_DELAY;
while ((cmd->r1 & SD_SPI_R1_NO_RESPONSE) != 0 && response_delay_bytes-- > 0) {
spi_transaction_t* t = get_transaction(slot);
*t = (spi_transaction_t) {
.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA,
.length = 8,
};
t->tx_data[0] = 0xff;
esp_err_t ret = spi_device_transmit(spi_handle(slot), t);
uint8_t r1 = t->rx_data[0];
release_transaction(slot);
if (ret != ESP_OK) {
return ret;
}
cmd->r1 = r1;
uint8_t* pr1 = &cmd->r1;
int ncr_cnt = 1;
while(true) {
if ((*pr1 & SD_SPI_R1_NO_RESPONSE) == 0) break;
pr1++;
if (++ncr_cnt > 8) return ESP_ERR_NOT_FOUND;
}
if (cmd->r1 & SD_SPI_R1_NO_RESPONSE) {
return ESP_ERR_TIMEOUT;
int copy_bytes = sent_bytes - SDSPI_CMD_SIZE - ncr_cnt;
if (copy_bytes > 0) {
memcpy(&cmd->r1, pr1, copy_bytes);
}
return ESP_OK;
}
@ -621,9 +648,8 @@ static esp_err_t poll_cmd_response(int slot, sdspi_hw_cmd_t *cmd)
* expense of one extra temporary buffer.
*/
static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
uint8_t *data, uint32_t rx_length)
uint8_t *data, uint32_t rx_length, bool need_stop_command)
{
bool need_stop_command = rx_length > SDSPI_MAX_DATA_LEN;
spi_transaction_t* t_command = get_transaction(slot);
*t_command = (spi_transaction_t) {
.length = (SDSPI_CMD_R1_SIZE + SDSPI_RESPONSE_MAX_DELAY) * 8,
@ -757,16 +783,25 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
return ESP_OK;
}
/* For CMD53, we can send in byte mode, or block mode
* The data start token is different, and cannot be determined by the length
* That's why we need ``multi_block``.
* It's also different that stop transmission token is not needed in the SDIO mode.
*/
static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
const uint8_t *data, uint32_t tx_length)
const uint8_t *data, uint32_t tx_length, bool multi_block, bool stop_trans)
{
if (card_write_protected(slot)) {
ESP_LOGW(TAG, "%s: card write protected", __func__);
return ESP_ERR_INVALID_STATE;
}
// Send the minimum length that is sure to get the complete response
// SD cards always return R1 (1bytes), SDIO returns R5 (2 bytes)
const int send_bytes = SDSPI_CMD_R5_SIZE+SDSPI_NCR_MAX_SIZE-SDSPI_NCR_MIN_SIZE;
spi_transaction_t* t_command = get_transaction(slot);
*t_command = (spi_transaction_t) {
.length = SDSPI_CMD_R1_SIZE * 8,
.length = send_bytes * 8,
.tx_buffer = cmd,
.rx_buffer = cmd,
};
@ -776,18 +811,17 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
}
wait_for_transactions(slot);
// Poll for command response which may be delayed up to 8 bytes
ret = poll_cmd_response(slot, cmd);
// check if command response valid
ret = shift_cmd_response(cmd, send_bytes);
if (ret != ESP_OK) {
ESP_LOGD(TAG, "%s: poll_cmd_response returned 0x%x", __func__, ret);
ESP_LOGD(TAG, "%s: check_cmd_response returned 0x%x", __func__, ret);
return ret;
}
uint8_t start_token = tx_length <= SDSPI_MAX_DATA_LEN ?
TOKEN_BLOCK_START : TOKEN_BLOCK_START_WRITE_MULTI;
uint8_t start_token = multi_block ?
TOKEN_BLOCK_START_WRITE_MULTI : TOKEN_BLOCK_START;
while (tx_length > 0) {
// Write block start token
spi_transaction_t* t_start_token = get_transaction(slot);
*t_start_token = (spi_transaction_t) {
@ -824,14 +858,19 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
return ret;
}
// Write CRC
// Write CRC and get the response in one transaction
uint16_t crc = sdspi_crc16(data, will_send);
spi_transaction_t* t_crc = get_transaction(slot);
*t_crc = (spi_transaction_t) {
.length = sizeof(crc) * 8,
.tx_buffer = (uint8_t*) &crc,
const int size_crc_response = sizeof(crc) + 1;
spi_transaction_t* t_crc_rsp = get_transaction(slot);
*t_crc_rsp = (spi_transaction_t) {
.length = size_crc_response * 8,
.flags = SPI_TRANS_USE_TXDATA|SPI_TRANS_USE_RXDATA,
};
ret = spi_device_queue_trans(spi_handle(slot), t_crc, 0);
memset(t_crc_rsp->tx_data, 0xff, 4);
memcpy(t_crc_rsp->tx_data, &crc, sizeof(crc));
ret = spi_device_queue_trans(spi_handle(slot), t_crc_rsp, 0);
if (ret != ESP_OK) {
return ret;
}
@ -839,16 +878,21 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
// Wait for data to be sent
wait_for_transactions(slot);
// Poll for response
spi_transaction_t* t_poll = get_transaction(slot);
ret = poll_response_token(slot, t_poll, cmd->timeout_ms);
release_transaction(slot);
if (ret != ESP_OK) {
return ret;
uint8_t data_rsp = t_crc_rsp->rx_data[2];
if (!SD_SPI_DATA_RSP_VALID(data_rsp)) return ESP_ERR_INVALID_RESPONSE;
switch (SD_SPI_DATA_RSP(data_rsp)) {
case SD_SPI_DATA_ACCEPTED:
break;
case SD_SPI_DATA_CRC_ERROR:
return ESP_ERR_INVALID_CRC;
case SD_SPI_DATA_WR_ERROR:
return ESP_FAIL;
default:
return ESP_ERR_INVALID_RESPONSE;
}
// Wait for the card to finish writing data
t_poll = get_transaction(slot);
spi_transaction_t* t_poll = get_transaction(slot);
ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot);
if (ret != ESP_OK) {
@ -859,12 +903,12 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
data += will_send;
}
if (start_token == TOKEN_BLOCK_START_WRITE_MULTI) {
if (stop_trans) {
uint8_t stop_token[2] = {
TOKEN_BLOCK_STOP_WRITE_MULTI,
SDSPI_MOSI_IDLE_VAL
TOKEN_BLOCK_STOP_WRITE_MULTI,
SDSPI_MOSI_IDLE_VAL
};
spi_transaction_t* t_stop_token = get_transaction(slot);
spi_transaction_t *t_stop_token = get_transaction(slot);
*t_stop_token = (spi_transaction_t) {
.length = sizeof(stop_token) * 8,
.tx_buffer = &stop_token,
@ -875,7 +919,7 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
}
wait_for_transactions(slot);
spi_transaction_t* t_poll = get_transaction(slot);
spi_transaction_t *t_poll = get_transaction(slot);
ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot);
if (ret != ESP_OK) {
@ -885,3 +929,29 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
return ESP_OK;
}
esp_err_t sdspi_host_io_int_enable(int slot)
{
//the pin and its interrupt is already initialized, nothing to do here.
return ESP_OK;
}
//the interrupt will give the semaphore and then disable itself
esp_err_t sdspi_host_io_int_wait(int slot, TickType_t timeout_ticks)
{
slot_info_t* pslot = &s_slots[slot];
//skip the interrupt and semaphore if the gpio is already low.
if (gpio_get_level(pslot->gpio_int)==0) return ESP_OK;
//clear the semaphore before wait
xSemaphoreTake(pslot->semphr_int, 0);
//enable the interrupt and wait for the semaphore
gpio_intr_enable(pslot->gpio_int);
BaseType_t ret = xSemaphoreTake(pslot->semphr_int, timeout_ticks);
if (ret == pdFALSE) {
gpio_intr_disable(pslot->gpio_int);
return ESP_ERR_TIMEOUT;
}
return ESP_OK;
}

View file

@ -77,19 +77,29 @@ typedef struct {
int timeout_ms;
} sdspi_hw_cmd_t;
#define SDSPI_CMD_NORESP_SIZE 6 //!< Size of the command without any response
#define SDSPI_CMD_R1_SIZE 8 //!< Size of the command with R1 response
#define SDSPI_CMD_R2_SIZE 9 //!< Size of the command with R1b response
#define SDSPI_CMD_R3_SIZE 12 //!< Size of the command with R3 response
#define SDSPI_CMD_R7_SIZE 12 //!< Size of the command with R7 response
#define SDSPI_CMD_SIZE 6
#define SDSPI_NCR_MIN_SIZE 1
#define SDSPI_NCR_MAX_SIZE 8
//the size here contains 6 bytes of CMD, 1 bytes of dummy and the actual response
#define SDSPI_CMD_NORESP_SIZE (SDSPI_CMD_SIZE+0) //!< Size of the command without any response
#define SDSPI_CMD_R1_SIZE (SDSPI_CMD_SIZE+SDSPI_NCR_MIN_SIZE+1) //!< Size of the command with R1 response
#define SDSPI_CMD_R2_SIZE (SDSPI_CMD_SIZE+SDSPI_NCR_MIN_SIZE+2) //!< Size of the command with R1b response
#define SDSPI_CMD_R3_SIZE (SDSPI_CMD_SIZE+SDSPI_NCR_MIN_SIZE+5) //!< Size of the command with R3 response
#define SDSPI_CMD_R4_SIZE (SDSPI_CMD_SIZE+SDSPI_NCR_MIN_SIZE+5) //!< Size of the command with R4 response
#define SDSPI_CMD_R5_SIZE (SDSPI_CMD_SIZE+SDSPI_NCR_MIN_SIZE+2) //!< Size of the command with R5 response
#define SDSPI_CMD_R7_SIZE (SDSPI_CMD_SIZE+SDSPI_NCR_MIN_SIZE+5) //!< Size of the command with R7 response
#define SDSPI_CMD_FLAG_DATA BIT(0) //!< Command has data transfer
#define SDSPI_CMD_FLAG_WRITE BIT(1) //!< Data is written to the card
#define SDSPI_CMD_FLAG_RSP_R1 BIT(2) //!< Response format R1 (1 byte)
#define SDSPI_CMD_FLAG_RSP_R2 BIT(3) //!< Response format R2 (2 bytes)
#define SDSPI_CMD_FLAG_RSP_R3 BIT(4) //!< Response format R3 (5 bytes)
#define SDSPI_CMD_FLAG_RSP_R7 BIT(5) //!< Response format R7 (5 bytes)
#define SDSPI_CMD_FLAG_NORSP BIT(6) //!< Don't expect response (used when sending CMD0 first time).
#define SDSPI_CMD_FLAG_RSP_R4 BIT(5) //!< Response format R4 (5 bytes)
#define SDSPI_CMD_FLAG_RSP_R5 BIT(6) //!< Response format R5 (2 bytes)
#define SDSPI_CMD_FLAG_RSP_R7 BIT(7) //!< Response format R7 (5 bytes)
#define SDSPI_CMD_FLAG_NORSP BIT(8) //!< Don't expect response (used when sending CMD0 first time).
#define SDSPI_CMD_FLAG_MULTI_BLK BIT(9) //!< For the write multiblock commands, the start token should be different
#define SDSPI_MAX_DATA_LEN 512 //!< Max size of single block transfer

View file

@ -79,11 +79,36 @@ static void r1_response_to_err(uint8_t r1, int cmd, esp_err_t *out_err)
}
}
static void r1_sdio_response_to_err(uint8_t r1, int cmd, esp_err_t *out_err)
{
if (r1 & SD_SPI_R1_NO_RESPONSE) {
ESP_LOGI(TAG, "cmd=%d, R1 response not found", cmd);
*out_err = ESP_ERR_TIMEOUT;
} else if (r1 & SD_SPI_R1_CMD_CRC_ERR) {
ESP_LOGI(TAG, "cmd=%d, R1 response: command CRC error", cmd);
*out_err = ESP_ERR_INVALID_CRC;
} else if (r1 & SD_SPI_R1_ILLEGAL_CMD) {
ESP_LOGI(TAG, "cmd=%d, R1 response: command not supported", cmd);
*out_err = ESP_ERR_NOT_SUPPORTED;
} else if (r1 & SD_SPI_R1_PARAM_ERR) {
ESP_LOGI(TAG, "cmd=%d, R1 response: size error", cmd);
*out_err = ESP_ERR_INVALID_SIZE;
} else if (r1 & SDIO_R1_FUNC_NUM_ERR) {
ESP_LOGI(TAG, "cmd=%d, R1 response: function number error", cmd);
*out_err = ESP_ERR_INVALID_ARG;
} else if (r1 & SD_SPI_R1_IDLE_STATE) {
// Idle state is handled at command layer
} else if (r1 != 0) {
ESP_LOGI(TAG, "cmd=%d, R1 response: unexpected value 0x%02x", cmd, r1);
*out_err = ESP_ERR_INVALID_RESPONSE;
}
}
esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
{
_lock_acquire(&s_lock);
// Convert the command to wire format
sdspi_hw_cmd_t hw_cmd;
WORD_ALIGNED_ATTR sdspi_hw_cmd_t hw_cmd;
make_hw_cmd(cmdinfo->opcode, cmdinfo->arg, cmdinfo->timeout_ms, &hw_cmd);
// Flags indicate which of the transfer types should be used
@ -94,6 +119,11 @@ esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
flags = SDSPI_CMD_FLAG_DATA;
}
// The block size is 512, when larger than 512, the data must send in multi blocks
if (cmdinfo->datalen > SDSPI_MAX_DATA_LEN) {
flags |= SDSPI_CMD_FLAG_MULTI_BLK;
}
// In SD host, response format is encoded using SCF_RSP_* flags which come
// as part of sdmmc_command_t from the upper layer (sdmmc_cmd.c).
// SPI mode uses different command formats. In fact, most of the commands
@ -111,6 +141,15 @@ esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
!(cmdinfo->flags & SCF_RSP_R1)) {
/* used to send CMD0 without expecting a response */
flags |= SDSPI_CMD_FLAG_NORSP;
} else if (!s_app_cmd && cmdinfo->opcode == SD_IO_SEND_OP_COND) {
flags |= SDSPI_CMD_FLAG_RSP_R4;
} else if (!s_app_cmd && cmdinfo->opcode == SD_IO_RW_DIRECT) {
flags |= SDSPI_CMD_FLAG_RSP_R5;
} else if (!s_app_cmd && cmdinfo->opcode == SD_IO_RW_EXTENDED) {
flags |= SDSPI_CMD_FLAG_RSP_R5 | SDSPI_CMD_FLAG_DATA;
if (cmdinfo->arg & SD_ARG_CMD53_WRITE) flags |= SDSPI_CMD_FLAG_WRITE;
// The CMD53 can assign block mode in the arg when the length is exactly 512 bytes
if (cmdinfo->arg & SD_ARG_CMD53_BLOCK_MODE) flags |= SDSPI_CMD_FLAG_MULTI_BLK;
} else {
flags |= SDSPI_CMD_FLAG_RSP_R1;
}
@ -131,6 +170,12 @@ esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
} else if (flags & (SDSPI_CMD_FLAG_RSP_R3 | SDSPI_CMD_FLAG_RSP_R7)) {
r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
cmdinfo->response[0] = __builtin_bswap32(hw_cmd.response[0]);
} else if (flags & SDSPI_CMD_FLAG_RSP_R4) {
r1_sdio_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
cmdinfo->response[0] = __builtin_bswap32(hw_cmd.response[0]);
} else if (flags & SDSPI_CMD_FLAG_RSP_R5) {
r1_sdio_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
cmdinfo->response[0] = hw_cmd.response[0];
}
}

View file

@ -66,40 +66,27 @@ esp_err_t sdmmc_init_ocr(sdmmc_card_t* card)
esp_err_t sdmmc_init_cid(sdmmc_card_t* card)
{
esp_err_t err;
sdmmc_csd_t csd;
sdmmc_response_t raw_cid;
if (!host_is_spi(card)) {
if (card->is_mem) {
err = sdmmc_send_cmd_all_send_cid(card, &raw_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);
err = sdmmc_send_cmd_all_send_cid(card, &raw_cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
return err;
}
if (card->is_mmc) {
/* For MMC, need to know CSD to decode CID.
* But CSD can only be read in data transfer mode,
* and it is not possible to read CID in data transfer mode.
* Luckily at this point the RCA is set and the card is in data
* transfer mode, so we can get its CSD to decode the CID...
*/
err = sdmmc_send_cmd_send_csd(card, &csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
return err;
}
err = sdmmc_mmc_decode_cid(csd.mmc_ver, raw_cid, &card->cid);
} else {
if (!card->is_mmc) {
err = sdmmc_decode_cid(raw_cid, &card->cid);
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
return err;
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
return err;
}
} else {
/* For MMC, need to know CSD to decode CID. But CSD can only be read
* in data transfer mode, and it is not possible to read CID in data
* transfer mode. We temporiliy store the raw cid and do the
* decoding after the RCA is set and the card is in data transfer
* mode.
*/
memcpy(card->raw_cid, raw_cid, sizeof(sdmmc_response_t));
}
} else {
err = sdmmc_send_cmd_send_cid(card, &card->cid);
@ -111,6 +98,30 @@ esp_err_t sdmmc_init_cid(sdmmc_card_t* card)
return ESP_OK;
}
esp_err_t sdmmc_init_rca(sdmmc_card_t* card)
{
esp_err_t err;
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card)
{
esp_err_t err;
sdmmc_response_t raw_cid;
memcpy(raw_cid, card->raw_cid, sizeof(raw_cid));
err = sdmmc_mmc_decode_cid(card->csd.mmc_ver, raw_cid, &card->cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_init_csd(sdmmc_card_t* card)
{
assert(card->is_mem == 1);
@ -194,6 +205,7 @@ esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
SDMMC_FREQ_HIGHSPEED,
SDMMC_FREQ_26M,
SDMMC_FREQ_DEFAULT
//NOTE: in sdspi mode, 20MHz may not work. in that case, add 10MHz here.
};
const int n_freq_values = sizeof(freq_values) / sizeof(freq_values[0]);

View file

@ -101,6 +101,8 @@ esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card);
esp_err_t sdmmc_init_select_card(sdmmc_card_t* card);
esp_err_t sdmmc_init_csd(sdmmc_card_t* card);
esp_err_t sdmmc_init_cid(sdmmc_card_t* card);
esp_err_t sdmmc_init_rca(sdmmc_card_t* card);
esp_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card);
esp_err_t sdmmc_init_ocr(sdmmc_card_t* card);
esp_err_t sdmmc_init_spi_crc(sdmmc_card_t* card);
esp_err_t sdmmc_init_io(sdmmc_card_t* card);

View file

@ -69,12 +69,18 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
ESP_LOGD(TAG, "%s: card type is %s", __func__,
is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
/* Read and decode the contents of CID register and assign RCA */
SDMMC_INIT_STEP(always, sdmmc_init_cid);
/* Read the contents of CID register*/
SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);
/* Assign RCA */
SDMMC_INIT_STEP(!is_spi, sdmmc_init_rca);
/* Read and decode the contents of CSD register */
SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);
/* Decode the contents of mmc CID register */
SDMMC_INIT_STEP(is_mmc && !is_spi, sdmmc_init_mmc_decode_cid);
/* Switch the card from stand-by mode to data transfer mode (not needed if
* SPI interface is used). This is needed to issue SET_BLOCKLEN and
* SEND_SCR commands.

View file

@ -106,11 +106,13 @@ esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card)
esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
{
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
if (card->host.max_freq_khz <= card->max_freq_khz) {
/* Host is configured to use low frequency, don't attempt to switch */
/* If the host is configured to use low frequency, don't attempt to switch */
if (card->host.max_freq_khz < SDMMC_FREQ_DEFAULT) {
card->max_freq_khz = card->host.max_freq_khz;
return ESP_OK;
} else if (card->host.max_freq_khz < SDMMC_FREQ_HIGHSPEED) {
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
return ESP_OK;
}
/* For IO cards, do write + read operation on "High Speed" register,