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_PARAM_ERR (1<<6)
#define SD_SPI_R1_NO_RESPONSE (1<<7) #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) */ /* 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])
@ -117,6 +119,13 @@
#define SD_SPI_R3(resp) ((resp)[0]) #define SD_SPI_R3(resp) ((resp)[0])
#define SD_SPI_R7(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 */ /* RCA argument and response */
#define MMC_ARG_RCA(rca) ((rca) << 16) #define MMC_ARG_RCA(rca) ((rca) << 16)
#define SD_R6_RCA(resp) (SD_R6((resp)) >> 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) << \ #define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \
(SD_CSD_C_SIZE_MULT((resp))+2)) (SD_CSD_C_SIZE_MULT((resp))+2))
#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22) #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_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_MIN(resp) MMC_RSP_BITS((resp), 59, 3)
#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3) #define SD_CSD_VDD_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_1 (0<<0)
#define CCCR_BUS_WIDTH_4 (2<<0) #define CCCR_BUS_WIDTH_4 (2<<0)
#define CCCR_BUS_WIDTH_8 (3<<0) #define CCCR_BUS_WIDTH_8 (3<<0)
#define CCCR_BUS_WIDTH_ECSI (1<<5)
#define SD_IO_CCCR_CARD_CAP 0x08 #define SD_IO_CCCR_CARD_CAP 0x08
#define CCCR_CARD_CAP_LSC BIT(6) #define CCCR_CARD_CAP_LSC BIT(6)
#define CCCR_CARD_CAP_4BLS BIT(7) #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 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) 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 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 /**< Enable internal pullups on enabled pins. The internal pullups
are insufficient however, please make sure external pullups are are insufficient however, please make sure external pullups are
connected on the bus. This is for debug / example purpose only. connected on the bus. This is for debug / example purpose only.
*/ */
} sdmmc_slot_config_t; } 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_CD GPIO_NUM_NC ///< 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_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) #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. * @brief Enable the pull-ups of sd pins.
* *
* @note You should always place actual pullups on the lines instead of using * @note You should always place actual pullups on the lines instead of using
* this function. Internal pullup resistance are high and not sufficient, may * this function. Internal pullup resistance are high and not sufficient, may
* cause instability in products. This is for debug or examples only. * cause instability in products. This is for debug or examples only.
* *
* @param slot Slot to use, normally set it to 1. * @param slot Slot to use, normally set it to 1.
* @param width Bit width of your configuration, 1 or 4. * @param width Bit width of your configuration, 1 or 4.
* *
* @return * @return
* - ESP_OK: if success * - ESP_OK: if success
* - ESP_ERR_INVALID_ARG: if configured width larger than maximum the slot can * - ESP_ERR_INVALID_ARG: if configured width larger than maximum the slot can

View file

@ -156,7 +156,11 @@ typedef struct {
typedef struct { typedef struct {
sdmmc_host_t host; /*!< Host with which the card is associated */ sdmmc_host_t host; /*!< Host with which the card is associated */
uint32_t ocr; /*!< OCR (Operation Conditions Register) value */ 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_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 */
sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) 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, \ .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_enable = &sdspi_host_io_int_enable, \
.io_int_wait = NULL, \ .io_int_wait = &sdspi_host_io_int_wait, \
.command_timeout_ms = 0, \ .command_timeout_ms = 0, \
} }
@ -60,11 +60,13 @@ typedef struct {
gpio_num_t gpio_cs; ///< GPIO number of CS signal 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_cd; ///< GPIO number of card detect signal
gpio_num_t gpio_wp; ///< GPIO number of write protect 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) int dma_channel; ///< DMA channel to be used by SPI driver (1 or 2)
} sdspi_slot_config_t; } 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_CD GPIO_NUM_NC ///< 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_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 * Macro defining default configuration of SPI host
@ -76,6 +78,7 @@ typedef struct {
.gpio_cs = GPIO_NUM_13, \ .gpio_cs = GPIO_NUM_13, \
.gpio_cd = SDSPI_SLOT_NO_CD, \ .gpio_cd = SDSPI_SLOT_NO_CD, \
.gpio_wp = SDSPI_SLOT_NO_WP, \ .gpio_wp = SDSPI_SLOT_NO_WP, \
.gpio_int = GPIO_NUM_NC, \
.dma_channel = 1 \ .dma_channel = 1 \
} }
@ -95,6 +98,8 @@ esp_err_t sdspi_host_init();
* *
* @note This function is not thread safe * @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 SPI controller to use (HSPI_HOST or VSPI_HOST)
* @param slot_config pointer to slot configuration structure * @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(); 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -292,7 +292,7 @@ static void configure_pin(int pin)
{ {
const int sdmmc_func = 3; const int sdmmc_func = 3;
const int drive_strength = 3; const int drive_strength = 3;
assert(pin!=-1); assert(pin!=GPIO_NUM_NC);
gpio_pulldown_dis(pin); gpio_pulldown_dis(pin);
uint32_t reg = GPIO_PIN_MUX_REG[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) 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 /* SDIO interrupts are negedge sensitive ones: the status bit is only set
* when first interrupt triggered. * 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); xSemaphoreTake(s_io_intr_event, 0);
SDMMC.intmask.sdio |= BIT(slot); /* Re-enable SDIO interrupt */ SDMMC.intmask.sdio |= BIT(slot); /* Re-enable SDIO interrupt */
if (xSemaphoreTake(s_io_intr_event, timeout_ticks) == pdTRUE) { if (xSemaphoreTake(s_io_intr_event, timeout_ticks) == pdTRUE) {
return ESP_OK; return ESP_OK;
} else { } else {

View file

@ -44,6 +44,7 @@ typedef struct {
uint8_t gpio_cs; //!< CS GPIO uint8_t gpio_cs; //!< CS GPIO
uint8_t gpio_cd; //!< Card detect GPIO, or GPIO_UNUSED uint8_t gpio_cd; //!< Card detect GPIO, or GPIO_UNUSED
uint8_t gpio_wp; //!< Write protect 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 /// Set to 1 if the higher layer has asked the card to enable CRC checks
uint8_t data_crc_enabled : 1; uint8_t data_crc_enabled : 1;
/// Number of transactions in 'transactions' array which are in use /// Number of transactions in 'transactions' array which are in use
@ -53,6 +54,8 @@ typedef struct {
uint8_t* block_buf; uint8_t* block_buf;
/// array with SDSPI_TRANSACTION_COUNT transaction structures /// array with SDSPI_TRANSACTION_COUNT transaction structures
spi_transaction_t* transactions; spi_transaction_t* transactions;
/// semaphore of gpio interrupt
SemaphoreHandle_t semphr_int;
} slot_info_t; } slot_info_t;
static slot_info_t s_slots[3]; 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 /// Functions to send out different kinds of commands
static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd, 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, 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 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 /// A few helper functions
@ -214,7 +217,7 @@ static esp_err_t init_spi_dev(int slot, int clock_speed_hz)
.mode = 0, .mode = 0,
// For SD cards, CS must stay low during the whole read/write operation, // For SD cards, CS must stay low during the whole read/write operation,
// rather than a single SPI transaction. // rather than a single SPI transaction.
.spics_io_num = -1, .spics_io_num = GPIO_NUM_NC,
.queue_size = SDSPI_TRANSACTION_COUNT, .queue_size = SDSPI_TRANSACTION_COUNT,
}; };
return spi_bus_add_device((spi_host_device_t) slot, &devcfg, &s_slots[slot].handle); 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); spi_bus_free((spi_host_device_t) i);
s_slots[i].handle = NULL; 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; 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); 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_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", 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, .miso_io_num = slot_config->gpio_miso,
.mosi_io_num = slot_config->gpio_mosi, .mosi_io_num = slot_config->gpio_mosi,
.sclk_io_num = slot_config->gpio_sck, .sclk_io_num = slot_config->gpio_sck,
.quadwp_io_num = -1, .quadwp_io_num = GPIO_NUM_NC,
.quadhd_io_num = -1 .quadhd_io_num = GPIO_NUM_NC
}; };
// Initialize SPI bus // 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); ret = init_spi_dev(slot, SDMMC_FREQ_PROBING * 1000);
if (ret != ESP_OK) { if (ret != ESP_OK) {
ESP_LOGD(TAG, "spi_bus_add_device failed with rc=0x%x", ret); ESP_LOGD(TAG, "spi_bus_add_device failed with rc=0x%x", ret);
spi_bus_free(host); goto cleanup;
return ret;
} }
// Configure CS pin // 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); ret = gpio_config(&io_conf);
if (ret != ESP_OK) { if (ret != ESP_OK) {
ESP_LOGD(TAG, "gpio_config (CS) failed with rc=0x%x", ret); ESP_LOGD(TAG, "gpio_config (CS) failed with rc=0x%x", ret);
spi_bus_remove_device(spi_handle(slot)); goto cleanup;
s_slots[slot].handle = NULL;
spi_bus_free(host);
return ret;
} }
cs_high(slot); 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); ret = gpio_config(&io_conf);
if (ret != ESP_OK) { if (ret != ESP_OK) {
ESP_LOGD(TAG, "gpio_config (CD/WP) failed with rc=0x%x", ret); ESP_LOGD(TAG, "gpio_config (CD/WP) failed with rc=0x%x", ret);
spi_bus_remove_device(spi_handle(slot)); goto cleanup;
s_slots[slot].handle = NULL;
spi_bus_free(host);
return ret;
} }
} }
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)); s_slots[slot].transactions = calloc(SDSPI_TRANSACTION_COUNT, sizeof(spi_transaction_t));
if (s_slots[slot].transactions == NULL) { if (s_slots[slot].transactions == NULL) {
spi_bus_remove_device(spi_handle(slot)); ret = ESP_ERR_NO_MEM;
s_slots[slot].handle = NULL; goto cleanup;
spi_bus_free(host);
return ESP_ERR_NO_MEM;
} }
return ESP_OK; 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; esp_err_t ret = ESP_OK;
cs_low(slot); cs_low(slot);
if (flags & SDSPI_CMD_FLAG_DATA) { 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) { 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 { } else {
ret = start_command_read_blocks(slot, cmd, data, data_size); ret = start_command_read_blocks(slot, cmd, data, data_size, stop_transmission);
} }
} else { } else {
ret = start_command_default(slot, flags, cmd); 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; cmd_size = SDSPI_CMD_R2_SIZE;
} else if (flags & SDSPI_CMD_FLAG_RSP_R3) { } else if (flags & SDSPI_CMD_FLAG_RSP_R3) {
cmd_size = SDSPI_CMD_R3_SIZE; 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) { } else if (flags & SDSPI_CMD_FLAG_RSP_R7) {
cmd_size = SDSPI_CMD_R7_SIZE; 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 = { spi_transaction_t t = {
.flags = 0, .flags = 0,
.length = cmd_size * 8, .length = cmd_size * 8,
.tx_buffer = cmd, .tx_buffer = cmd,
.rx_buffer = cmd .rx_buffer = cmd,
}; };
esp_err_t ret = spi_device_transmit(spi_handle(slot), &t); esp_err_t ret = spi_device_transmit(spi_handle(slot), &t);
if (cmd->cmd_index == MMC_STOP_TRANSMISSION) { 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__); ESP_LOGV(TAG, "%s: ignoring response byte", __func__);
cmd->r1 = 0x00; cmd->r1 = 0x00;
} }
ret = poll_cmd_response(slot, cmd); // we have sent and received bytes with enough length.
if (ret != ESP_OK) { // now shift the response to match the offset of sdspi_hw_cmd_t
ESP_LOGD(TAG, "%s: poll_cmd_response returned 0x%x", __func__, ret); ret = shift_cmd_response(cmd, cmd_size);
return ret; if (ret != ESP_OK) return ESP_ERR_TIMEOUT;
}
return ESP_OK; 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; 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. // Wait for data token, reading 8 bytes at a time.
// If the token is found, write all subsequent bytes to extra_ptr, // If the token is found, write all subsequent bytes to extra_ptr,
// and store the number of bytes written to extra_size. // 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; 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; uint8_t* pr1 = &cmd->r1;
while ((cmd->r1 & SD_SPI_R1_NO_RESPONSE) != 0 && response_delay_bytes-- > 0) { int ncr_cnt = 1;
spi_transaction_t* t = get_transaction(slot); while(true) {
*t = (spi_transaction_t) { if ((*pr1 & SD_SPI_R1_NO_RESPONSE) == 0) break;
.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA, pr1++;
.length = 8, if (++ncr_cnt > 8) return ESP_ERR_NOT_FOUND;
};
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;
} }
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; 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. * expense of one extra temporary buffer.
*/ */
static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd, 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); spi_transaction_t* t_command = get_transaction(slot);
*t_command = (spi_transaction_t) { *t_command = (spi_transaction_t) {
.length = (SDSPI_CMD_R1_SIZE + SDSPI_RESPONSE_MAX_DELAY) * 8, .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; 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, 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)) { if (card_write_protected(slot)) {
ESP_LOGW(TAG, "%s: card write protected", __func__); ESP_LOGW(TAG, "%s: card write protected", __func__);
return ESP_ERR_INVALID_STATE; 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); spi_transaction_t* t_command = get_transaction(slot);
*t_command = (spi_transaction_t) { *t_command = (spi_transaction_t) {
.length = SDSPI_CMD_R1_SIZE * 8, .length = send_bytes * 8,
.tx_buffer = cmd, .tx_buffer = cmd,
.rx_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); wait_for_transactions(slot);
// Poll for command response which may be delayed up to 8 bytes // check if command response valid
ret = poll_cmd_response(slot, cmd); ret = shift_cmd_response(cmd, send_bytes);
if (ret != ESP_OK) { 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; return ret;
} }
uint8_t start_token = tx_length <= SDSPI_MAX_DATA_LEN ? uint8_t start_token = multi_block ?
TOKEN_BLOCK_START : TOKEN_BLOCK_START_WRITE_MULTI; TOKEN_BLOCK_START_WRITE_MULTI : TOKEN_BLOCK_START;
while (tx_length > 0) { while (tx_length > 0) {
// Write block start token // Write block start token
spi_transaction_t* t_start_token = get_transaction(slot); spi_transaction_t* t_start_token = get_transaction(slot);
*t_start_token = (spi_transaction_t) { *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; return ret;
} }
// Write CRC // Write CRC and get the response in one transaction
uint16_t crc = sdspi_crc16(data, will_send); uint16_t crc = sdspi_crc16(data, will_send);
spi_transaction_t* t_crc = get_transaction(slot); const int size_crc_response = sizeof(crc) + 1;
*t_crc = (spi_transaction_t) {
.length = sizeof(crc) * 8, spi_transaction_t* t_crc_rsp = get_transaction(slot);
.tx_buffer = (uint8_t*) &crc, *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) { if (ret != ESP_OK) {
return ret; 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 data to be sent
wait_for_transactions(slot); wait_for_transactions(slot);
// Poll for response uint8_t data_rsp = t_crc_rsp->rx_data[2];
spi_transaction_t* t_poll = get_transaction(slot); if (!SD_SPI_DATA_RSP_VALID(data_rsp)) return ESP_ERR_INVALID_RESPONSE;
ret = poll_response_token(slot, t_poll, cmd->timeout_ms); switch (SD_SPI_DATA_RSP(data_rsp)) {
release_transaction(slot); case SD_SPI_DATA_ACCEPTED:
if (ret != ESP_OK) { break;
return ret; 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 // 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); ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot); release_transaction(slot);
if (ret != ESP_OK) { 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; data += will_send;
} }
if (start_token == TOKEN_BLOCK_START_WRITE_MULTI) { if (stop_trans) {
uint8_t stop_token[2] = { uint8_t stop_token[2] = {
TOKEN_BLOCK_STOP_WRITE_MULTI, TOKEN_BLOCK_STOP_WRITE_MULTI,
SDSPI_MOSI_IDLE_VAL 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) { *t_stop_token = (spi_transaction_t) {
.length = sizeof(stop_token) * 8, .length = sizeof(stop_token) * 8,
.tx_buffer = &stop_token, .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); 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); ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot); release_transaction(slot);
if (ret != ESP_OK) { 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; 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; int timeout_ms;
} sdspi_hw_cmd_t; } sdspi_hw_cmd_t;
#define SDSPI_CMD_NORESP_SIZE 6 //!< Size of the command without any response #define SDSPI_CMD_SIZE 6
#define SDSPI_CMD_R1_SIZE 8 //!< Size of the command with R1 response #define SDSPI_NCR_MIN_SIZE 1
#define SDSPI_CMD_R2_SIZE 9 //!< Size of the command with R1b response #define SDSPI_NCR_MAX_SIZE 8
#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 //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_DATA BIT(0) //!< Command has data transfer
#define SDSPI_CMD_FLAG_WRITE BIT(1) //!< Data is written to the card #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_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_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_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_RSP_R4 BIT(5) //!< Response format R4 (5 bytes)
#define SDSPI_CMD_FLAG_NORSP BIT(6) //!< Don't expect response (used when sending CMD0 first time). #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 #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) esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
{ {
_lock_acquire(&s_lock); _lock_acquire(&s_lock);
// Convert the command to wire format // 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); make_hw_cmd(cmdinfo->opcode, cmdinfo->arg, cmdinfo->timeout_ms, &hw_cmd);
// Flags indicate which of the transfer types should be used // 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; 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 // 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). // 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 // 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)) { !(cmdinfo->flags & SCF_RSP_R1)) {
/* used to send CMD0 without expecting a response */ /* used to send CMD0 without expecting a response */
flags |= SDSPI_CMD_FLAG_NORSP; 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 { } else {
flags |= SDSPI_CMD_FLAG_RSP_R1; 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)) { } else if (flags & (SDSPI_CMD_FLAG_RSP_R3 | SDSPI_CMD_FLAG_RSP_R7)) {
r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret); r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
cmdinfo->response[0] = __builtin_bswap32(hw_cmd.response[0]); 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 sdmmc_init_cid(sdmmc_card_t* card)
{ {
esp_err_t err; esp_err_t err;
sdmmc_csd_t csd;
sdmmc_response_t raw_cid; sdmmc_response_t raw_cid;
if (!host_is_spi(card)) { if (!host_is_spi(card)) {
if (card->is_mem) { err = sdmmc_send_cmd_all_send_cid(card, &raw_cid);
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);
if (err != ESP_OK) { 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; return err;
} }
if (card->is_mmc) { 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 {
err = sdmmc_decode_cid(raw_cid, &card->cid); err = sdmmc_decode_cid(raw_cid, &card->cid);
} if (err != ESP_OK) {
if (err != ESP_OK) { ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err); return 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 { } else {
err = sdmmc_send_cmd_send_cid(card, &card->cid); 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; 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) esp_err_t sdmmc_init_csd(sdmmc_card_t* card)
{ {
assert(card->is_mem == 1); 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_HIGHSPEED,
SDMMC_FREQ_26M, SDMMC_FREQ_26M,
SDMMC_FREQ_DEFAULT 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]); 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_select_card(sdmmc_card_t* card);
esp_err_t sdmmc_init_csd(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_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_ocr(sdmmc_card_t* card);
esp_err_t sdmmc_init_spi_crc(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); 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__, ESP_LOGD(TAG, "%s: card type is %s", __func__,
is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD"); is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
/* Read and decode the contents of CID register and assign RCA */ /* Read the contents of CID register*/
SDMMC_INIT_STEP(always, sdmmc_init_cid); 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 */ /* Read and decode the contents of CSD register */
SDMMC_INIT_STEP(is_mem, sdmmc_init_csd); 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 /* 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
* SEND_SCR commands. * 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) esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
{ {
card->max_freq_khz = SDMMC_FREQ_DEFAULT; /* If the host is configured to use low frequency, don't attempt to switch */
if (card->host.max_freq_khz <= card->max_freq_khz) { if (card->host.max_freq_khz < SDMMC_FREQ_DEFAULT) {
/* Host is configured to use low frequency, don't attempt to switch */
card->max_freq_khz = card->host.max_freq_khz; card->max_freq_khz = card->host.max_freq_khz;
return ESP_OK; 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, /* For IO cards, do write + read operation on "High Speed" register,