From 8a364b4bdf9ab5eef6685cbd008d13104b108dd9 Mon Sep 17 00:00:00 2001 From: michael Date: Sat, 29 Dec 2018 02:04:37 +0800 Subject: [PATCH] sdmmc: support SDIO over SPI --- components/driver/include/driver/sdmmc_defs.h | 12 +- components/driver/include/driver/sdmmc_host.h | 12 +- .../driver/include/driver/sdmmc_types.h | 6 +- components/driver/include/driver/sdspi_host.h | 34 ++- components/driver/sdmmc_host.c | 6 +- components/driver/sdspi_host.c | 284 +++++++++++------- components/driver/sdspi_private.h | 24 +- components/driver/sdspi_transaction.c | 47 ++- components/sdmmc/sdmmc_common.c | 68 +++-- components/sdmmc/sdmmc_common.h | 2 + components/sdmmc/sdmmc_init.c | 10 +- components/sdmmc/sdmmc_io.c | 8 +- 12 files changed, 350 insertions(+), 163 deletions(-) diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index 6ea567b8f5..dd4c426411 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -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) diff --git a/components/driver/include/driver/sdmmc_host.h b/components/driver/include/driver/sdmmc_host.h index 4d94d03c7c..4e2db09946 100644 --- a/components/driver/include/driver/sdmmc_host.h +++ b/components/driver/include/driver/sdmmc_host.h @@ -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 diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h index 369a9210f8..1673bb5f23 100644 --- a/components/driver/include/driver/sdmmc_types.h +++ b/components/driver/include/driver/sdmmc_types.h @@ -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 */ diff --git a/components/driver/include/driver/sdspi_host.h b/components/driver/include/driver/sdspi_host.h index 1b3b67017e..605cd76240 100644 --- a/components/driver/include/driver/sdspi_host.h +++ b/components/driver/include/driver/sdspi_host.h @@ -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 diff --git a/components/driver/sdmmc_host.c b/components/driver/sdmmc_host.c index 1ca44cf4b8..ff3431fd83 100644 --- a/components/driver/sdmmc_host.c +++ b/components/driver/sdmmc_host.c @@ -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 { diff --git a/components/driver/sdspi_host.c b/components/driver/sdspi_host.c index e823ee7fa2..26d9f7b3fa 100644 --- a/components/driver/sdspi_host.c +++ b/components/driver/sdspi_host.c @@ -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; +} + diff --git a/components/driver/sdspi_private.h b/components/driver/sdspi_private.h index 32500ac777..3bee5c5429 100644 --- a/components/driver/sdspi_private.h +++ b/components/driver/sdspi_private.h @@ -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 diff --git a/components/driver/sdspi_transaction.c b/components/driver/sdspi_transaction.c index d25521deaa..3e74a68b81 100644 --- a/components/driver/sdspi_transaction.c +++ b/components/driver/sdspi_transaction.c @@ -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]; } } diff --git a/components/sdmmc/sdmmc_common.c b/components/sdmmc/sdmmc_common.c index cd7c1a9cda..c28d447581 100644 --- a/components/sdmmc/sdmmc_common.c +++ b/components/sdmmc/sdmmc_common.c @@ -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]); diff --git a/components/sdmmc/sdmmc_common.h b/components/sdmmc/sdmmc_common.h index dae2a9fa35..d57403ccb1 100644 --- a/components/sdmmc/sdmmc_common.h +++ b/components/sdmmc/sdmmc_common.h @@ -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); diff --git a/components/sdmmc/sdmmc_init.c b/components/sdmmc/sdmmc_init.c index 6bd5a88533..64cdff1df3 100644 --- a/components/sdmmc/sdmmc_init.c +++ b/components/sdmmc/sdmmc_init.c @@ -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. diff --git a/components/sdmmc/sdmmc_io.c b/components/sdmmc/sdmmc_io.c index 5fc7dbd5df..7229f18983 100644 --- a/components/sdmmc/sdmmc_io.c +++ b/components/sdmmc/sdmmc_io.c @@ -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,