sdspi: use polling transactions to increase reading speed.

This commit is contained in:
Michael (XIAO Xufeng) 2018-09-03 23:55:22 +08:00
parent 067f3d21c9
commit 48a62b3490

View file

@ -28,6 +28,7 @@
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "soc/soc_memory_layout.h"
/// Max number of transactions in flight (used in start_command_write_blocks)
@ -56,13 +57,9 @@ typedef struct {
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
uint8_t used_transaction_count: 3;
/// Intermediate buffer used when application buffer is not in DMA memory;
/// allocated on demand, SDSPI_BLOCK_BUF_SIZE bytes long. May be zero.
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;
@ -71,6 +68,10 @@ typedef struct {
static slot_info_t *s_slots[SOC_SPI_PERIPH_NUM] = {};
static const char *TAG = "sdspi_host";
static const bool use_polling = true;
static const bool no_use_polling = true;
/// Functions to send out different kinds of commands
static esp_err_t start_command_read_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cmd,
uint8_t *data, uint32_t rx_length, bool need_stop_command);
@ -166,30 +167,6 @@ static esp_err_t get_block_buf(slot_info_t *slot, uint8_t **out_buf)
return ESP_OK;
}
static spi_transaction_t* get_transaction(slot_info_t *slot)
{
size_t used_transaction_count = slot->used_transaction_count;
assert(used_transaction_count < SDSPI_TRANSACTION_COUNT);
spi_transaction_t* ret = &slot->transactions[used_transaction_count];
++slot->used_transaction_count;
return ret;
}
static void release_transaction(slot_info_t *slot)
{
--slot->used_transaction_count;
}
static void wait_for_transactions(slot_info_t *slot)
{
size_t used_transaction_count = slot->used_transaction_count;
for (size_t i = 0; i < used_transaction_count; ++i) {
spi_transaction_t* t_out;
spi_device_get_trans_result(slot->spi_handle, &t_out, portMAX_DELAY);
release_transaction(slot);
}
}
/// Clock out one byte (CS has to be high) to make the card release MISO
/// (clocking one bit would work as well, but that triggers a bug in SPI DMA)
static void release_bus(slot_info_t *slot)
@ -199,7 +176,7 @@ static void release_bus(slot_info_t *slot)
.length = 8,
.tx_data = {0xff}
};
spi_device_transmit(slot->spi_handle, &t);
spi_device_polling_transmit(slot->spi_handle, &t);
// don't care if this failed
}
@ -214,18 +191,10 @@ static void go_idle_clockout(slot_info_t *slot)
.tx_buffer = data,
.rx_buffer = data,
};
spi_device_transmit(slot->spi_handle, &t);
spi_device_polling_transmit(slot->spi_handle, &t);
// don't care if this failed
}
/// Return true if the pointer can be used for DMA
static bool ptr_dma_compatible(const void* ptr)
{
return (uintptr_t) ptr >= 0x3FFAE000 &&
(uintptr_t) ptr < 0x40000000;
}
/**
* (Re)Configure SPI device. Used to change clock speed.
* @param slot Pointer to the slot to be configured
@ -264,8 +233,7 @@ static esp_err_t deinit_slot(slot_info_t *slot)
free(slot->block_buf);
slot->block_buf = NULL;
}
free(slot->transactions);
slot->transactions = NULL;
uint64_t pin_bit_mask = 0;
if (slot->gpio_cs != GPIO_UNUSED) {
pin_bit_mask |= BIT64(slot->gpio_cs);
@ -439,11 +407,6 @@ esp_err_t sdspi_host_init_device(const sdspi_device_config_t* slot_config, sdspi
} else {
slot->gpio_int = GPIO_UNUSED;
}
slot->transactions = calloc(SDSPI_TRANSACTION_COUNT, sizeof(spi_transaction_t));
if (slot->transactions == NULL) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
//Initialization finished, store the store information if possible
//Then return corresponding handle
*out_handle = store_slot_info(slot);
@ -545,13 +508,13 @@ static esp_err_t start_command_default(slot_info_t *slot, int flags, sdspi_hw_cm
.tx_buffer = cmd,
.rx_buffer = cmd,
};
esp_err_t ret = spi_device_transmit(slot->spi_handle, &t);
esp_err_t ret = spi_device_polling_transmit(slot->spi_handle, &t);
if (cmd->cmd_index == MMC_STOP_TRANSMISSION) {
/* response is a stuff byte from previous transfer, ignore it */
cmd->r1 = 0xff;
}
if (ret != ESP_OK) {
ESP_LOGD(TAG, "%s: spi_device_transmit returned 0x%x", __func__, ret);
ESP_LOGD(TAG, "%s: spi_device_polling_transmit returned 0x%x", __func__, ret);
return ret;
}
if (flags & SDSPI_CMD_FLAG_NORSP) {
@ -568,10 +531,10 @@ static esp_err_t start_command_default(slot_info_t *slot, int flags, sdspi_hw_cm
}
// Wait until MISO goes high
static esp_err_t poll_busy(slot_info_t *slot, spi_transaction_t* t, int timeout_ms)
static esp_err_t poll_busy(slot_info_t *slot, int timeout_ms, bool polling)
{
uint8_t t_rx;
*t = (spi_transaction_t) {
spi_transaction_t t = {
.tx_buffer = &t_rx,
.flags = SPI_TRANS_USE_RXDATA, //data stored in rx_data
.length = 8,
@ -582,12 +545,16 @@ static esp_err_t poll_busy(slot_info_t *slot, spi_transaction_t* t, int timeout_
int nonzero_count = 0;
do {
t_rx = SDSPI_MOSI_IDLE_VAL;
t->rx_data[0] = 0;
ret = spi_device_transmit(slot->spi_handle, t);
t.rx_data[0] = 0;
if (polling) {
ret = spi_device_polling_transmit(slot->spi_handle, &t);
} else {
ret = spi_device_transmit(slot->spi_handle, &t);
}
if (ret != ESP_OK) {
return ret;
}
if (t->rx_data[0] != 0) {
if (t.rx_data[0] != 0) {
if (++nonzero_count == 2) {
return ESP_OK;
}
@ -600,11 +567,10 @@ static esp_err_t poll_busy(slot_info_t *slot, spi_transaction_t* t, int 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.
static esp_err_t poll_data_token(slot_info_t *slot, spi_transaction_t* t,
uint8_t* extra_ptr, size_t* extra_size, int timeout_ms)
static esp_err_t poll_data_token(slot_info_t *slot, uint8_t *extra_ptr, size_t *extra_size, int timeout_ms)
{
uint8_t t_rx[8];
*t = (spi_transaction_t) {
spi_transaction_t t = {
.tx_buffer = &t_rx,
.rx_buffer = &t_rx,
.length = sizeof(t_rx) * 8,
@ -613,7 +579,7 @@ static esp_err_t poll_data_token(slot_info_t *slot, spi_transaction_t* t,
uint64_t t_end = esp_timer_get_time() + timeout_ms * 1000;
do {
memset(t_rx, SDSPI_MOSI_IDLE_VAL, sizeof(t_rx));
ret = spi_device_transmit(slot->spi_handle, t);
ret = spi_device_polling_transmit(slot->spi_handle, &t);
if (ret != ESP_OK) {
return ret;
}
@ -706,17 +672,15 @@ static esp_err_t shift_cmd_response(sdspi_hw_cmd_t* cmd, int sent_bytes)
static esp_err_t start_command_read_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cmd,
uint8_t *data, uint32_t rx_length, bool need_stop_command)
{
spi_transaction_t* t_command = get_transaction(slot);
*t_command = (spi_transaction_t) {
spi_transaction_t t_command = {
.length = (SDSPI_CMD_R1_SIZE + SDSPI_RESPONSE_MAX_DELAY) * 8,
.tx_buffer = cmd,
.rx_buffer = cmd,
};
esp_err_t ret = spi_device_transmit(slot->spi_handle, t_command);
esp_err_t ret = spi_device_polling_transmit(slot->spi_handle, &t_command);
if (ret != ESP_OK) {
return ret;
}
release_transaction(slot);
uint8_t* cmd_u8 = (uint8_t*) cmd;
size_t pre_scan_data_size = SDSPI_RESPONSE_MAX_DELAY;
@ -751,9 +715,7 @@ static esp_err_t start_command_read_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cm
if (need_poll) {
// Wait for data to be ready
spi_transaction_t* t_poll = get_transaction(slot);
ret = poll_data_token(slot, t_poll, cmd_u8 + SDSPI_CMD_R1_SIZE, &extra_data_size, cmd->timeout_ms);
release_transaction(slot);
ret = poll_data_token(slot, cmd_u8 + SDSPI_CMD_R1_SIZE, &extra_data_size, cmd->timeout_ms);
if (ret != ESP_OK) {
return ret;
}
@ -773,18 +735,16 @@ static esp_err_t start_command_read_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cm
// receive actual data
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) {
spi_transaction_t t_data = {
.length = (will_receive + receive_extra_bytes) * 8,
.rx_buffer = rx_data,
.tx_buffer = rx_data
};
ret = spi_device_transmit(slot->spi_handle, t_data);
ret = spi_device_transmit(slot->spi_handle, &t_data);
if (ret != ESP_OK) {
return ret;
}
release_transaction(slot);
// CRC bytes need to be received even if CRC is not enabled
uint16_t crc = UINT16_MAX;
@ -829,9 +789,7 @@ static esp_err_t start_command_read_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cm
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);
ret = poll_busy(slot, cmd->timeout_ms, use_polling);
if (ret != ESP_OK) {
return ret;
}
@ -855,17 +813,15 @@ static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *c
// 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) {
spi_transaction_t t_command = {
.length = send_bytes * 8,
.tx_buffer = cmd,
.rx_buffer = cmd,
};
esp_err_t ret = spi_device_queue_trans(slot->spi_handle, t_command, 0);
esp_err_t ret = spi_device_polling_transmit(slot->spi_handle, &t_command);
if (ret != ESP_OK) {
return ret;
}
wait_for_transactions(slot);
// check if command response valid
ret = shift_cmd_response(cmd, send_bytes);
@ -879,12 +835,11 @@ static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *c
while (tx_length > 0) {
// Write block start token
spi_transaction_t* t_start_token = get_transaction(slot);
*t_start_token = (spi_transaction_t) {
spi_transaction_t t_start_token = {
.length = sizeof(start_token) * 8,
.tx_buffer = &start_token
};
ret = spi_device_queue_trans(slot->spi_handle, t_start_token, 0);
ret = spi_device_polling_transmit(slot->spi_handle, &t_start_token);
if (ret != ESP_OK) {
return ret;
}
@ -892,7 +847,7 @@ static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *c
// Prepare data to be sent
size_t will_send = MIN(tx_length, SDSPI_MAX_DATA_LEN);
const uint8_t* tx_data = data;
if (!ptr_dma_compatible(tx_data)) {
if (!esp_ptr_in_dram(tx_data)) {
// If the pointer can't be used with DMA, copy data into a new buffer
uint8_t* tmp;
ret = get_block_buf(slot, &tmp);
@ -904,12 +859,11 @@ static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *c
}
// Write data
spi_transaction_t* t_data = get_transaction(slot);
*t_data = (spi_transaction_t) {
spi_transaction_t t_data = {
.length = will_send * 8,
.tx_buffer = tx_data,
};
ret = spi_device_queue_trans(slot->spi_handle, t_data, 0);
ret = spi_device_transmit(slot->spi_handle, &t_data);
if (ret != ESP_OK) {
return ret;
}
@ -918,23 +872,19 @@ static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *c
uint16_t crc = sdspi_crc16(data, will_send);
const int size_crc_response = sizeof(crc) + 1;
spi_transaction_t* t_crc_rsp = get_transaction(slot);
*t_crc_rsp = (spi_transaction_t) {
spi_transaction_t t_crc_rsp = {
.length = size_crc_response * 8,
.flags = SPI_TRANS_USE_TXDATA|SPI_TRANS_USE_RXDATA,
};
memset(t_crc_rsp->tx_data, 0xff, 4);
memcpy(t_crc_rsp->tx_data, &crc, sizeof(crc));
memset(t_crc_rsp.tx_data, 0xff, 4);
memcpy(t_crc_rsp.tx_data, &crc, sizeof(crc));
ret = spi_device_queue_trans(slot->spi_handle, t_crc_rsp, 0);
ret = spi_device_polling_transmit(slot->spi_handle, &t_crc_rsp);
if (ret != ESP_OK) {
return ret;
}
// Wait for data to be sent
wait_for_transactions(slot);
uint8_t data_rsp = t_crc_rsp->rx_data[2];
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:
@ -948,9 +898,7 @@ static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *c
}
// Wait for the card to finish writing data
spi_transaction_t* t_poll = get_transaction(slot);
ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot);
ret = poll_busy(slot, cmd->timeout_ms, no_use_polling);
if (ret != ESP_OK) {
return ret;
}
@ -964,20 +912,16 @@ static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *c
TOKEN_BLOCK_STOP_WRITE_MULTI,
SDSPI_MOSI_IDLE_VAL
};
spi_transaction_t *t_stop_token = get_transaction(slot);
*t_stop_token = (spi_transaction_t) {
spi_transaction_t t_stop_token = {
.length = sizeof(stop_token) * 8,
.tx_buffer = &stop_token,
};
ret = spi_device_queue_trans(slot->spi_handle, t_stop_token, 0);
ret = spi_device_polling_transmit(slot->spi_handle, &t_stop_token);
if (ret != ESP_OK) {
return ret;
}
wait_for_transactions(slot);
spi_transaction_t *t_poll = get_transaction(slot);
ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot);
ret = poll_busy(slot, cmd->timeout_ms, use_polling);
if (ret != ESP_OK) {
return ret;
}