]> granicus.if.org Git - esp-idf/commitdiff
sdspi: fix compatibility issue in multi block read
authorIvan Grokhotkov <ivan@espressif.com>
Mon, 12 Mar 2018 07:47:06 +0000 (15:47 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Fri, 30 Mar 2018 10:49:42 +0000 (18:49 +0800)
SDSPI driver optimized polling of the response tokens by requesting
two extra bytes on top of the block size (512) and CRC (2), and
checking whether these bytes contained the data response token or
not. In case the token was there, further polling would not need to
happen, thereby reducing latency between two consecutive blocks
transferred. However this caused compatibility issues when these two
extra bytes were sent after reading the final block. When
STOP_TRANSMISSION command was sent, these extra two bytes were
treated as part of the command, causing an invalid command error.

This fixes the logic by only requesting extra two bytes if the block
being read is not the final block. In addition to that, more strict
error checking is implemented for command response tokens.

components/driver/sdspi_host.c

index ef8d62770346a2153f3da5dd511b4b9076b8b061..6ac8bfbe4d11badb3689fce3f3b5aaf936e79ac1 100644 (file)
@@ -34,6 +34,8 @@
 #define GPIO_UNUSED 0xff                //!< Flag indicating that CD/WP is unused\r
 /// Size of the buffer returned by get_block_buf\r
 #define SDSPI_BLOCK_BUF_SIZE    (SDSPI_MAX_DATA_LEN + 4)\r
+/// Maximum number of dummy bytes between the request and response (minimum is 1)\r
+#define SDSPI_RESPONSE_MAX_DELAY  8\r
 \r
 \r
 /// Structure containing run time configuration for a single SD slot\r
@@ -422,6 +424,30 @@ static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd)
         .rx_buffer = cmd\r
     };\r
     esp_err_t ret = spi_device_transmit(spi_handle(slot), &t);\r
+    if (cmd->cmd_index == MMC_STOP_TRANSMISSION) {\r
+        /* response is a stuff byte from previous transfer, ignore it */\r
+        cmd->r1 = 0xff;\r
+    }\r
+    int response_delay_bytes = SDSPI_RESPONSE_MAX_DELAY;\r
+    while ((cmd->r1 & SD_SPI_R1_NO_RESPONSE) != 0 && response_delay_bytes-- > 0) {\r
+        spi_transaction_t* t = get_transaction(slot);\r
+        *t = (spi_transaction_t) {\r
+            .flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA,\r
+            .length = 8,\r
+        };\r
+        t->tx_data[0] = 0xff;\r
+        ret = spi_device_transmit(spi_handle(slot), t);\r
+        uint8_t r1 = t->rx_data[0];\r
+        release_transaction(slot);\r
+        if (ret != ESP_OK) {\r
+            return ret;\r
+        }\r
+        cmd->r1 = r1;\r
+    }\r
+    if (cmd->r1 & SD_SPI_R1_NO_RESPONSE) {\r
+        ESP_LOGD(TAG, "%s: no response token found", __func__);\r
+        return ESP_ERR_TIMEOUT;\r
+    }\r
     return ret;\r
 }\r
 \r
@@ -564,6 +590,9 @@ static esp_err_t poll_data_token(int slot, spi_transaction_t* t,
  *    indicating the start of the next block. Actual scanning is done by\r
  *    setting pre_scan_data_ptr to point to these last 2 bytes, and setting\r
  *    pre_scan_data_size = 2, then going to step 2 to receive the next block.\r
+ *    When the final block is being received, the number of extra bytes is 2\r
+ *    (only for CRC), because we don't need to wait for start token of the\r
+ *    next block, and some cards are getting confused by these two extra bytes.\r
  *\r
  * With this approach the delay between blocks of a multi-block transfer is\r
  * ~95 microseconds, out of which 35 microseconds are spend doing the CRC check.\r
@@ -575,9 +604,8 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
 {\r
     bool need_stop_command = rx_length > SDSPI_MAX_DATA_LEN;\r
     spi_transaction_t* t_command = get_transaction(slot);\r
-    const int cmd_extra_bytes = 8;\r
     *t_command = (spi_transaction_t) {\r
-        .length = (SDSPI_CMD_R1_SIZE + cmd_extra_bytes) * 8,\r
+        .length = (SDSPI_CMD_R1_SIZE + SDSPI_RESPONSE_MAX_DELAY) * 8,\r
         .tx_buffer = cmd,\r
         .rx_buffer = cmd,\r
     };\r
@@ -588,7 +616,7 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
     release_transaction(slot);\r
 \r
     uint8_t* cmd_u8 = (uint8_t*) cmd;\r
-    size_t pre_scan_data_size = cmd_extra_bytes;\r
+    size_t pre_scan_data_size = SDSPI_RESPONSE_MAX_DELAY;\r
     uint8_t* pre_scan_data_ptr = cmd_u8 + SDSPI_CMD_R1_SIZE;\r
 \r
     /* R1 response is delayed by 1-8 bytes from the request.\r
@@ -640,7 +668,7 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
         }\r
 \r
         // receive actual data\r
-        const size_t receive_extra_bytes = 4;\r
+        const size_t receive_extra_bytes = (rx_length > SDSPI_MAX_DATA_LEN) ? 4 : 2;\r
         memset(rx_data, 0xff, will_receive + receive_extra_bytes);\r
         spi_transaction_t* t_data = get_transaction(slot);\r
         *t_data = (spi_transaction_t) {\r
@@ -695,6 +723,9 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
         if (ret != ESP_OK) {\r
             return ret;\r
         }\r
+        if (stop_cmd.r1 != 0) {\r
+            ESP_LOGD(TAG, "%s: STOP_TRANSMISSION response 0x%02x", __func__, stop_cmd.r1);\r
+        }\r
         spi_transaction_t* t_poll = get_transaction(slot);\r
         ret = poll_busy(slot, t_poll, cmd->timeout_ms);\r
         release_transaction(slot);\r