1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers/sdcard_spi: make retries configurable

- convert count based retry implementation to timeout based
  implementation
- allow user definition of retry parameters
This commit is contained in:
Daniel Lockau 2020-07-01 15:33:33 +02:00
parent 11360cc7f7
commit e7fbc174bc
3 changed files with 81 additions and 64 deletions

View File

@ -125,16 +125,37 @@ extern "C" {
#define SD_CSD_V2 1
#define SD_CSD_VUNSUPPORTED -1
/* the retry counters below are used as timeouts for specific actions.
The values may need some adjustments to either give the card more time to respond
to commands or to achieve a lower delay / avoid infinite blocking. */
#define R1_POLLING_RETRY_CNT 1000000
#define SD_DATA_TOKEN_RETRY_CNT 1000000
#define INIT_CMD_RETRY_CNT 1000000
#define INIT_CMD0_RETRY_CNT 3
#define SD_WAIT_FOR_NOT_BUSY_CNT 1000000 /* use -1 for full blocking till the card isn't busy */
#define SD_BLOCK_READ_CMD_RETRIES 10 /* only affects sending of cmd not whole transaction! */
#define SD_BLOCK_WRITE_CMD_RETRIES 10 /* only affects sending of cmd not whole transaction! */
/**
* @name Set retry parameters for specific actions
*
* Retry timeouts in microseconds for specific actions.
* The value interpretation is uint32_t.
* A value of 0 disables retries.
*
* @{
*/
#ifndef INIT_CMD_RETRY_US
#define INIT_CMD_RETRY_US (250 * US_PER_MS) /**< initialization command retry */
#endif
#ifndef INIT_CMD0_RETRY_US
#define INIT_CMD0_RETRY_US (100UL) /**< initialization command 0 retry */
#endif
#ifndef R1_POLLING_RETRY_US
#define R1_POLLING_RETRY_US (100 * US_PER_MS) /**< initialization first response */
#endif
#ifndef SD_DATA_TOKEN_RETRY_US
#define SD_DATA_TOKEN_RETRY_US (100 * US_PER_MS) /**< data packet token read retry */
#endif
#ifndef SD_WAIT_FOR_NOT_BUSY_US
#define SD_WAIT_FOR_NOT_BUSY_US (250 * US_PER_MS) /**< wait for SD card */
#endif
#ifndef SD_BLOCK_READ_CMD_RETRY_US
#define SD_BLOCK_READ_CMD_RETRY_US (100UL) /**< only affects sending of cmd not whole transaction! */
#endif
#ifndef SD_BLOCK_WRITE_CMD_RETRY_US
#define SD_BLOCK_WRITE_CMD_RETRY_US (100UL) /**< only affects sending of cmd not whole transaction! */
#endif
/** @} */
/* memory capacity in bytes = (C_SIZE+1) * SD_CSD_V2_C_SIZE_BLOCK_MULT * BLOCK_LEN */
#define SD_CSD_V2_C_SIZE_BLOCK_MULT 1024
@ -179,14 +200,14 @@ typedef enum {
* (for CMDX this parameter is simply the integer value X).
* @param[in] argument The argument for the given cmd. As described by "7.3.1.1 Command Format".
* This argument is transmitted byte wise with most significant byte first.
* @param[in] max_retry Specifies how often the command should be retried if an error occurs.
* Use 0 to try only once, -1 to try forever, or n to retry n times.
* @param[in] retry_us Specifies microsecond timeout for retries in case of command errors.
* Use 0 to try exactly once.
*
* @return R1 response of the command if no (low-level) communication error occurred
* @return SD_INVALID_R1_RESPONSE if either waiting for the card to enter
* not-busy-state timed out or spi communication failed
*/
uint8_t sdcard_spi_send_cmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t argument, int32_t max_retry);
uint8_t sdcard_spi_send_cmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t argument, uint32_t retry_us);
/**
* @brief Sends an acmd to the sd card. ACMD<n> consists of sending CMD55 + CMD<n>
@ -197,14 +218,14 @@ uint8_t sdcard_spi_send_cmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t arg
* (for ACMDX this parameter is simply the integer value X).
* @param[in] argument The argument for the given cmd. As described by "7.3.1.1 Command Format".
* This argument is transmitted byte wise with most significant byte first.
* @param[in] max_retry Specifies how often the command should be retried if an error occurs.
* Use 0 to try only once, -1 to try forever, or n to retry n times.
* @param[in] retry_us Specifies microsecond timeout for retries in case of command errors.
* Use 0 to try exactly once.
*
* @return R1 response of the command if no (low-level) communication error occurred
* @return SD_INVALID_R1_RESPONSE if either waiting for the card to enter
* not-busy-state timed out or spi communication failed
*/
uint8_t sdcard_spi_send_acmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t argument, int32_t max_retry);
uint8_t sdcard_spi_send_acmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t argument, uint32_t retry_us);
/**
* @brief Gets the sector count of the card.

View File

@ -33,10 +33,10 @@
static inline void _select_card_spi(sdcard_spi_t *card);
static inline void _unselect_card_spi(sdcard_spi_t *card);
static inline uint8_t _wait_for_r1(sdcard_spi_t *card, int32_t max_retries);
static inline uint8_t _wait_for_r1(sdcard_spi_t *card, uint32_t retry_us);
static inline void _send_dummy_byte(sdcard_spi_t *card);
static inline bool _wait_for_not_busy(sdcard_spi_t *card, int32_t max_retries);
static inline bool _wait_for_token(sdcard_spi_t *card, uint8_t token, int32_t max_retries);
static inline bool _wait_for_not_busy(sdcard_spi_t *card, uint32_t retry_us);
static inline bool _wait_for_token(sdcard_spi_t *card, uint8_t token, uint32_t retry_us);
static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_state_t state);
static sd_rw_response_t _read_cid(sdcard_spi_t *card);
static sd_rw_response_t _read_csd(sdcard_spi_t *card);
@ -132,7 +132,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
/* select sdcard for cmd0 */
gpio_clear(card->params.cs);
uint8_t cmd0_r1 = sdcard_spi_send_cmd(card, SD_CMD_0, SD_CMD_NO_ARG, INIT_CMD0_RETRY_CNT);
uint8_t cmd0_r1 = sdcard_spi_send_cmd(card, SD_CMD_0, SD_CMD_NO_ARG, INIT_CMD0_RETRY_US);
gpio_set(card->params.cs);
if (R1_VALID(cmd0_r1) && !R1_ERROR(cmd0_r1) && R1_IDLE_BIT_SET(cmd0_r1)) {
@ -150,7 +150,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
case SD_INIT_ENABLE_CRC:
DEBUG("SD_INIT_ENABLE_CRC\n");
_select_card_spi(card);
uint8_t r1 = sdcard_spi_send_cmd(card, SD_CMD_59, SD_CMD_59_ARG_EN, INIT_CMD_RETRY_CNT);
uint8_t r1 = sdcard_spi_send_cmd(card, SD_CMD_59, SD_CMD_59_ARG_EN, INIT_CMD_RETRY_US);
_unselect_card_spi(card);
if (R1_VALID(r1) && !R1_ERROR(r1)) {
@ -163,7 +163,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
DEBUG("SD_INIT_SEND_CMD8\n");
_select_card_spi(card);
int cmd8_arg = (SD_CMD_8_VHS_2_7_V_TO_3_6_V << 8) | SD_CMD_8_CHECK_PATTERN;
uint8_t cmd8_r1 = sdcard_spi_send_cmd(card, SD_CMD_8, cmd8_arg, INIT_CMD_RETRY_CNT);
uint8_t cmd8_r1 = sdcard_spi_send_cmd(card, SD_CMD_8, cmd8_arg, INIT_CMD_RETRY_US);
if (R1_VALID(cmd8_r1) && !R1_ERROR(cmd8_r1)) {
DEBUG("CMD8: [OK] --> reading remaining bytes for R7\n");
@ -199,7 +199,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
case SD_INIT_SEND_ACMD41_HCS:
DEBUG("SD_INIT_SEND_ACMD41_HCS\n");
int acmd41_hcs_retries = 0;
const uint32_t acmd41_hcs_retry_timeout = xtimer_now_usec() + INIT_CMD_RETRY_US;
do {
uint8_t acmd41hcs_r1 = sdcard_spi_send_acmd(card, SD_CMD_41, SD_ACMD_41_ARG_HC, 0);
if (R1_VALID(acmd41hcs_r1) && !R1_ERROR(acmd41hcs_r1) &&
@ -207,14 +207,13 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
DEBUG("ACMD41: [OK]\n");
return SD_INIT_SEND_CMD58;
}
acmd41_hcs_retries++;
} while ((INIT_CMD_RETRY_CNT < 0) || (acmd41_hcs_retries <= (int)INIT_CMD_RETRY_CNT));
} while (((uint32_t)INIT_CMD_RETRY_US > 0) && (xtimer_now_usec() < acmd41_hcs_retry_timeout));
_unselect_card_spi(card);
return SD_INIT_CARD_UNKNOWN;
case SD_INIT_SEND_ACMD41:
DEBUG("SD_INIT_SEND_ACMD41\n");
int acmd41_retries = 0;
const uint32_t acmd41_retry_timeout = xtimer_now_usec() + INIT_CMD_RETRY_US;
do {
uint8_t acmd41_r1 = sdcard_spi_send_acmd(card, SD_CMD_41, SD_CMD_NO_ARG, 0);
if (R1_VALID(acmd41_r1) && !R1_ERROR(acmd41_r1) && !R1_IDLE_BIT_SET(acmd41_r1)) {
@ -223,8 +222,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
card->card_type = SD_V1;
return SD_INIT_SEND_CMD16;
}
acmd41_retries++;
} while ((INIT_CMD_RETRY_CNT < 0) || (acmd41_retries <= (int)INIT_CMD_RETRY_CNT));
} while (((uint32_t)INIT_CMD_RETRY_US > 0) && (xtimer_now_usec() < acmd41_retry_timeout));
DEBUG("ACMD41: [ERROR]\n");
return SD_INIT_SEND_CMD1;
@ -237,7 +235,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
case SD_INIT_SEND_CMD58:
DEBUG("SD_INIT_SEND_CMD58\n");
uint8_t cmd58_r1 = sdcard_spi_send_cmd(card, SD_CMD_58, SD_CMD_NO_ARG, INIT_CMD_RETRY_CNT);
uint8_t cmd58_r1 = sdcard_spi_send_cmd(card, SD_CMD_58, SD_CMD_NO_ARG, INIT_CMD_RETRY_US);
if (R1_VALID(cmd58_r1) && !R1_ERROR(cmd58_r1)) {
DEBUG("CMD58: [OK]\n");
card->card_type = SD_V2;
@ -285,7 +283,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
case SD_INIT_SEND_CMD16:
DEBUG("SD_INIT_SEND_CMD16\n");
uint8_t r1_16 = sdcard_spi_send_cmd(card, SD_CMD_16, SD_HC_BLOCK_SIZE, INIT_CMD_RETRY_CNT);
uint8_t r1_16 = sdcard_spi_send_cmd(card, SD_CMD_16, SD_HC_BLOCK_SIZE, INIT_CMD_RETRY_US);
if (R1_VALID(r1_16) && !R1_ERROR(r1_16)) {
DEBUG("CARD TYPE IS SDSC (SD_V1 with byte addressing)\n");
_unselect_card_spi(card);
@ -335,9 +333,9 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta
}
}
static inline bool _wait_for_token(sdcard_spi_t *card, uint8_t token, int32_t max_retries)
static inline bool _wait_for_token(sdcard_spi_t *card, uint8_t token, uint32_t retry_us)
{
int tried = 0;
const uint32_t retry_timeout = xtimer_now_usec() + retry_us;
do {
uint8_t read_byte = 0;
@ -351,8 +349,7 @@ static inline bool _wait_for_token(sdcard_spi_t *card, uint8_t token, int32_t ma
DEBUG("_wait_for_token: [NO MATCH] (0x%02x)\n", read_byte);
}
tried++;
} while ((max_retries < 0) || (tried <= max_retries));
} while ((retry_us > 0) && (xtimer_now_usec() < retry_timeout));
return false;
}
@ -369,12 +366,12 @@ static inline void _send_dummy_byte(sdcard_spi_t *card)
}
}
static inline bool _wait_for_not_busy(sdcard_spi_t *card, int32_t max_retries)
static inline bool _wait_for_not_busy(sdcard_spi_t *card, uint32_t retry_us)
{
uint8_t read_byte;
int tried = 0;
const uint32_t retry_timeout = xtimer_now_usec() + retry_us;
do {
uint8_t read_byte;
if (_dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &read_byte) == 1) {
if (read_byte == 0xFF) {
DEBUG("_wait_for_not_busy: [OK]\n");
@ -389,8 +386,7 @@ static inline bool _wait_for_not_busy(sdcard_spi_t *card, int32_t max_retries)
return false;
}
tried++;
} while ((max_retries < 0) || (tried <= max_retries));
} while ((retry_us > 0) && (xtimer_now_usec() < retry_timeout));
DEBUG("_wait_for_not_busy: [FAILED]\n");
return false;
@ -413,9 +409,10 @@ static uint8_t _crc_7(const uint8_t *data, int n)
return (crc << 1) | 1;
}
uint8_t sdcard_spi_send_cmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t argument, int32_t max_retry)
uint8_t sdcard_spi_send_cmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t argument, uint32_t retry_us)
{
int try_cnt = 0;
const uint32_t retry_timeout = xtimer_now_usec() + retry_us;
uint8_t r1_resu;
uint8_t cmd_data[6];
@ -429,19 +426,18 @@ uint8_t sdcard_spi_send_cmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t arg
uint8_t echo[sizeof(cmd_data)];
do {
DEBUG("sdcard_spi_send_cmd: CMD%02d (0x%08" PRIx32 ") (retry %d)\n", sd_cmd_idx, argument, try_cnt);
DEBUG("sdcard_spi_send_cmd: CMD%02d (0x%08" PRIx32 ") (remaining retry time %"PRIu32" usec)\n", sd_cmd_idx, argument,
(retry_timeout > xtimer_now_usec()) ? (retry_timeout - xtimer_now_usec()) : 0);
if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) {
if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_US)) {
DEBUG("sdcard_spi_send_cmd: timeout while waiting for bus to be not busy!\n");
r1_resu = SD_INVALID_R1_RESPONSE;
try_cnt++;
continue;
}
if (_transfer_bytes(card, cmd_data, echo, sizeof(cmd_data)) != sizeof(cmd_data)) {
DEBUG("sdcard_spi_send_cmd: _transfer_bytes: send cmd [%d]: [ERROR]\n", sd_cmd_idx);
r1_resu = SD_INVALID_R1_RESPONSE;
try_cnt++;
continue;
}
@ -456,7 +452,7 @@ uint8_t sdcard_spi_send_cmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t arg
_send_dummy_byte(card);
}
r1_resu = _wait_for_r1(card, R1_POLLING_RETRY_CNT);
r1_resu = _wait_for_r1(card, R1_POLLING_RETRY_US);
if (R1_VALID(r1_resu)) {
break;
@ -465,20 +461,21 @@ uint8_t sdcard_spi_send_cmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t arg
DEBUG("sdcard_spi_send_cmd: R1_TIMEOUT (0x%02x)\n", r1_resu);
r1_resu = SD_INVALID_R1_RESPONSE;
}
try_cnt++;
} while ((max_retry < 0) || (try_cnt <= max_retry));
} while ((retry_us > 0) && (xtimer_now_usec() < retry_timeout));
return r1_resu;
}
uint8_t sdcard_spi_send_acmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t argument, int32_t max_retry)
uint8_t sdcard_spi_send_acmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t argument, uint32_t retry_us)
{
int err_cnt = 0;
const uint32_t retry_timeout = xtimer_now_usec() + retry_us;
uint8_t r1_resu;
do {
DEBUG("sdcard_spi_send_acmd: CMD%02d (0x%08" PRIx32 ")(retry %d)\n", sd_cmd_idx, argument, err_cnt);
DEBUG("sdcard_spi_send_acmd: CMD%02d (0x%08" PRIx32 ") (remaining retry time %"PRIu32" usec)\n", sd_cmd_idx, argument,
(retry_timeout > xtimer_now_usec()) ? (retry_timeout - xtimer_now_usec()) : 0);
r1_resu = sdcard_spi_send_cmd(card, SD_CMD_55, SD_CMD_NO_ARG, 0);
if (R1_VALID(r1_resu) && !R1_ERROR(r1_resu)) {
r1_resu = sdcard_spi_send_cmd(card, sd_cmd_idx, argument, 0);
@ -488,29 +485,27 @@ uint8_t sdcard_spi_send_acmd(sdcard_spi_t *card, uint8_t sd_cmd_idx, uint32_t ar
}
else {
DEBUG("ACMD%02d: [ERROR / NO RESPONSE]\n", sd_cmd_idx);
err_cnt++;
}
}
else {
DEBUG("CMD55: [ERROR / NO RESPONSE]\n");
err_cnt++;
}
} while ((max_retry < 0) || (err_cnt <= max_retry));
} while ((retry_us > 0) && (xtimer_now_usec() < retry_timeout));
DEBUG("sdcard_spi_send_acmd: [TIMEOUT]\n");
return r1_resu;
}
static inline uint8_t _wait_for_r1(sdcard_spi_t *card, int32_t max_retries)
static inline uint8_t _wait_for_r1(sdcard_spi_t *card, uint32_t retry_us)
{
int tried = 0;
const uint32_t retry_timeout = xtimer_now_usec() + retry_us;
uint8_t r1;
do {
if (_dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &r1) != 1) {
DEBUG("_wait_for_r1: _dyn_spi_rxtx_byte:[ERROR]\n");
tried++;
continue;
}
else {
@ -521,8 +516,8 @@ static inline uint8_t _wait_for_r1(sdcard_spi_t *card, int32_t max_retries)
DEBUG("_wait_for_r1: R1_VALID\n");
return r1;
}
tried++;
} while ((max_retries < 0) || (tried <= max_retries));
} while ((retry_us > 0) && (xtimer_now_usec() < retry_timeout));
DEBUG("_wait_for_r1: [TIMEOUT]\n");
return r1;
@ -591,7 +586,7 @@ static inline int _transfer_bytes(sdcard_spi_t *card, const uint8_t *out, uint8_
static sd_rw_response_t _read_data_packet(sdcard_spi_t *card, uint8_t token, uint8_t *data, int size)
{
DEBUG("_read_data_packet: size: %d\n", size);
if (_wait_for_token(card, token, SD_DATA_TOKEN_RETRY_CNT) == true) {
if (_wait_for_token(card, token, SD_DATA_TOKEN_RETRY_US) == true) {
DEBUG("_read_data_packet: [GOT TOKEN]\n");
}
else {
@ -636,7 +631,7 @@ static inline int _read_blocks(sdcard_spi_t *card, int cmd_idx, int bladdr, uint
int reads = 0;
uint32_t addr = card->use_block_addr ? bladdr : (bladdr * SD_HC_BLOCK_SIZE);
uint8_t cmd_r1_resu = sdcard_spi_send_cmd(card, cmd_idx, addr, SD_BLOCK_READ_CMD_RETRIES);
uint8_t cmd_r1_resu = sdcard_spi_send_cmd(card, cmd_idx, addr, SD_BLOCK_READ_CMD_RETRY_US);
if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) {
DEBUG("_read_blocks: send CMD%d: [OK]\n", cmd_idx);
@ -752,7 +747,7 @@ static inline int _write_blocks(sdcard_spi_t *card, uint8_t cmd_idx, int bladdr,
int written = 0;
uint32_t addr = card->use_block_addr ? bladdr : (bladdr * SD_HC_BLOCK_SIZE);
uint8_t cmd_r1_resu = sdcard_spi_send_cmd(card, cmd_idx, addr, SD_BLOCK_WRITE_CMD_RETRIES);
uint8_t cmd_r1_resu = sdcard_spi_send_cmd(card, cmd_idx, addr, SD_BLOCK_WRITE_CMD_RETRY_US);
if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) {
DEBUG("_write_blocks: send CMD%d: [OK]\n", cmd_idx);
@ -773,7 +768,7 @@ static inline int _write_blocks(sdcard_spi_t *card, uint8_t cmd_idx, int bladdr,
*state = write_resu;
return written;
}
if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) {
if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_US)) {
DEBUG("_write_blocks: _wait_for_not_busy: [FAILED]\n");
_unselect_card_spi(card);
*state = SD_RW_TIMEOUT;
@ -792,7 +787,7 @@ static inline int _write_blocks(sdcard_spi_t *card, uint8_t cmd_idx, int bladdr,
/* sd card needs dummy byte before we can wait for not-busy
state */
_send_dummy_byte(card);
if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) {
if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_US)) {
_unselect_card_spi(card);
*state = SD_RW_TIMEOUT;
}

View File

@ -7,6 +7,7 @@ BOARD_INSUFFICIENT_MEMORY := \
msb-430 \
msb-430h \
nucleo-f031k6 \
nucleo-f042k6 \
stm32f030f4-demo \
telosb \
wsn430-v1_3b \