Files
Adam Múdry 339628fbb7 fix(sdmmc): back off between CMD13 polls while waiting for card to be ready
The loops waiting for the card to leave its busy state started their yield
backoff at 100 ms. A card is typically busy for a few milliseconds after a
write, so the backoff never fired and every write was followed by hundreds
of back-to-back CMD13 commands. Occupying the host controller like this
slows down unrelated work on both cores, not just the calling task.

Delay between polls instead, starting at CONFIG_SD_READY_POLL_PERIOD_START_US
(100 us) and doubling. Both the delay and the configured start period are
capped at one FreeRTOS tick period, where vTaskDelay() already yields and one
command per tick is not a storm. A typical wait now costs a handful of
commands instead of hundreds.

Applies to sdmmc_wait_for_idle(), sdmmc_init_sd_wait_data_ready() and
read_tuning_block().

Closes https://github.com/espressif/esp-idf/issues/19034
2026-09-07 19:39:52 +02:00

242 lines
11 KiB
C

/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2024 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "sd_protocol_defs.h"
#include "sd_protocol_types.h"
#include "sdmmc_cmd.h"
#include "sys/param.h"
#include "soc/soc_memory_layout.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SDMMC_GO_IDLE_DELAY_MS 20
#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10
#define SDMMC_INIT_SPI_CRC_RETRY_DELAY_MS 10
#define SDMMC_INIT_WAIT_DATA_READY_TIMEOUT_US (5000 * 1000)
#define SDMMC_READY_FOR_DATA_TIMEOUT_US (5000 * 1000)
/* Delay between two consecutive card status (CMD13) polls while waiting for the
* card to become ready. Starts at SDMMC_READY_POLL_PERIOD_START_US and doubles
* after every poll, up to one FreeRTOS tick period.
*
* Polling without a delay turns every write into a storm of hundreds of CMD13
* commands, which occupies the host controller and slows down unrelated work.
*
* While the delay is below one tick period it is busy-waited and doubles, so a
* handful of commands covers a busy period of a few milliseconds and a card that
* becomes ready early is noticed without waiting for the next tick.
*
* Once the delay reaches one tick period it stops growing: from there on
* vTaskDelay() already yields and one command per tick is not a storm, while a
* longer delay would only delay noticing that the card is ready. Long busy
* periods therefore cost one poll per tick and are detected within one tick.
*
* One tick period is a hard cap: a configured start period larger than that is
* clamped to it as well.
*/
#define SDMMC_READY_POLL_PERIOD_START_US CONFIG_SD_READY_POLL_PERIOD_START_US
/* These delay values are mostly useful for cases when CD pin is not used, and
* the card is removed. In this case, SDMMC peripheral may not always return
* CMD_DONE / DATA_DONE interrupts after signaling the error. These timeouts work
* as a safety net in such cases.
*/
#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands
#define SDMMC_WRITE_CMD_TIMEOUT_MS 5000 // Max timeout of write commands
#define SDMMC_SD_DISCARD_TIMEOUT 250 // SD erase (discard) timeout
/* Maximum retry/error count for SEND_OP_COND (CMD1).
* These are somewhat arbitrary, values originate from OpenBSD driver.
*/
#define SDMMC_SEND_OP_COND_MAX_RETRIES 300
#define SDMMC_SEND_OP_COND_MAX_ERRORS 3
/* supported arguments for erase command 38 */
#define SDMMC_SD_ERASE_ARG 0
#define SDMMC_SD_DISCARD_ARG 1
#define SDMMC_MMC_TRIM_ARG 1
#define SDMMC_MMC_DISCARD_ARG 3
/**
* Delay mode
*/
typedef enum {
SDMMC_DELAY_MODE_PHASE,
SDMMC_DELAY_MODE_LINE,
} sdmmc_delay_mode_t;
/* Functions to send individual commands */
esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card);
esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr);
esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp);
esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid);
esp_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid);
esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd);
esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
uint32_t mode, uint32_t group, uint32_t function,
sdmmc_switch_func_rsp_t* resp);
esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca);
esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
esp_err_t sdmmc_send_cmd_num_of_written_blocks(sdmmc_card_t* card, size_t* out_num_blocks);
esp_err_t sdmmc_send_cmd_voltage_switch(sdmmc_card_t* card);
/* Higher level functions */
esp_err_t sdmmc_enter_higher_speed_mode(sdmmc_card_t* card);
esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count, size_t buffer_len);
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count, size_t buffer_len);
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
esp_err_t sdmmc_select_driver_strength(sdmmc_card_t *card, sdmmc_driver_strength_t driver_strength);
esp_err_t sdmmc_select_current_limit(sdmmc_card_t *card, sdmmc_current_limit_t current_limit);
esp_err_t sdmmc_do_timing_tuning(sdmmc_card_t *card, sdmmc_delay_mode_t delay_mode);
/* SD specific */
esp_err_t sdmmc_check_scr(sdmmc_card_t* card);
esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
esp_err_t sdmmc_decode_ssr(uint32_t *raw_ssr, sdmmc_ssr_t* out_ssr);
uint32_t sdmmc_sd_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
#if CONFIG_SD_ENABLE_SDIO_SUPPORT
/* SDIO specific */
esp_err_t sdmmc_io_reset(sdmmc_card_t* card);
esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card);
esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int function, uint32_t reg, uint32_t arg, uint8_t *byte);
// Requirement to `data` and `size` when using SDMMC host:
// Buffer pointer (`data`) needs to be aligned to 4 byte boundary, and also cache line size if the buffer is behind the
// cache, unless `SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF` flag is set when calling `sdmmc_card_init`. This flag is mandory
// when the buffer is behind the cache in byte mode.
esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function, uint32_t reg, int arg, void *data, size_t size);
#endif
/* MMC specific */
esp_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen, size_t buffer_len);
esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value);
esp_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid);
esp_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
esp_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card);
uint32_t sdmmc_mmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
/* Parts of card initialization flow */
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_sd_blocklen(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_scr(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_ssr(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card);
esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card);
esp_err_t sdmmc_init_mmc_read_cid(sdmmc_card_t* card);
esp_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card);
#if CONFIG_SD_ENABLE_SDIO_SUPPORT
esp_err_t sdmmc_init_io(sdmmc_card_t* card);
esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card);
esp_err_t sdmmc_io_init_read_card_cap(sdmmc_card_t* card, uint8_t* card_cap);
esp_err_t sdmmc_io_init_check_card_cap(sdmmc_card_t* card, uint8_t* card_cap);
#endif
esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card);
esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card);
esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card);
esp_err_t sdmmc_init_mmc_check_ext_csd(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_uhs1(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_driver_strength(sdmmc_card_t *card);
esp_err_t sdmmc_init_sd_current_limit(sdmmc_card_t *card);
esp_err_t sdmmc_init_sd_timing_tuning(sdmmc_card_t *card);
/* Various helper functions */
static inline bool host_is_spi(const sdmmc_card_t* card)
{
return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0;
}
static inline uint32_t get_host_ocr(float voltage)
{
// TODO: report exact voltage to the card
// For now tell that the host has 2.8-3.6V voltage range
(void) voltage;
return SD_OCR_VOL_MASK;
}
static inline bool sdmmc_ready_for_data(uint32_t status)
{
return (status & MMC_R1_READY_FOR_DATA) && (MMC_R1_CURRENT_STATE_STATUS(status) == MMC_R1_CURRENT_STATE_TRAN);
}
/**
* @brief Wait before the next card status poll, and back off for the poll after that
*
* Delays for *period_us, then doubles *period_us. Both the delay and the stored
* period are capped at one FreeRTOS tick period, so a *period_us larger than that
* on entry is clamped rather than used as-is. A delay shorter than one tick period
* is busy-waited, since no shorter blocking sleep is available; one tick period
* blocks on vTaskDelay() so that other tasks can run.
*
* @param[in,out] period_us Delay to apply now; updated to the delay to apply next time
*/
void sdmmc_poll_delay_and_backoff(uint32_t* period_us);
void sdmmc_flip_byte_order(uint32_t* response, size_t size);
esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card);
// Use only with SDMMC mode (not SDSPI)
esp_err_t sdmmc_wait_for_idle(sdmmc_card_t* card, uint32_t status);
//Currently only SDIO support using this buffer. And only 512 block size is supported.
#define SDMMC_IO_BLOCK_SIZE 512
esp_err_t sdmmc_allocate_aligned_buf(sdmmc_card_t* card);
/**
* For newly added host driver function pointers,
* use this function to check if they are correctly initialised.
*/
esp_err_t sdmmc_check_host_function_ptr_integrity(sdmmc_card_t *card);
#ifdef __cplusplus
}
#endif