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)
@ -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

@ -67,8 +67,8 @@ typedef struct {
*/ */
} 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)
/** /**

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];

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,