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