mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
sdmmc: support SDIO over SPI
This commit is contained in:
parent
3ec7bec6a7
commit
8a364b4bdf
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user