feat(sdio): add sdio over sdmmc support for esp32p4

This commit is contained in:
Xiao Xufeng 2024-01-02 02:57:13 +08:00 committed by Michael (XIAO Xufeng)
parent 6f053704dc
commit 216284b767
11 changed files with 119 additions and 51 deletions

View File

@ -57,6 +57,13 @@ extern "C" {
// Forces data to be placed to DMA-capable places
#define DMA_ATTR WORD_ALIGNED_ATTR DRAM_ATTR
//Force data to be placed in DRAM and aligned according to DMA and cache's requirement
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#define DRAM_DMA_ALIGNED_ATTR __attribute__((aligned(CONFIG_CACHE_L1_CACHE_LINE_SIZE))) DRAM_ATTR
#else
#define DRAM_DMA_ALIGNED_ATTR WORD_ALIGNED_ATTR DRAM_ATTR
#endif
// Forces the data to be tightly packed with minimum required padding and no extra bytes are added for alignment
#define PACKED_ATTR __attribute__((packed))

View File

@ -47,9 +47,9 @@ static void s_master_init(test_sdio_param_t *host_param, essl_handle_t *out_hand
{
sdmmc_host_t host_config = (sdmmc_host_t)SDMMC_HOST_DEFAULT();
host_config.flags = host_param->host_flags;
if (host_config.flags == SDMMC_HOST_FLAG_4BIT) {
if (host_config.flags & SDMMC_HOST_FLAG_4BIT) {
ESP_LOGI(TAG, "Probe using SD 4-bit...");
} else if (host_config.flags == SDMMC_HOST_FLAG_1BIT) {
} else if (host_config.flags & SDMMC_HOST_FLAG_1BIT) {
ESP_LOGI(TAG, "Probe using SD 1-bit...");
}
host_config.max_freq_khz = host_param->max_freq_khz;
@ -83,6 +83,14 @@ static void s_master_init(test_sdio_param_t *host_param, essl_handle_t *out_hand
TEST_ESP_OK(essl_init(*out_handle, TEST_TIMEOUT_MAX));
}
static void s_master_deinit(void)
{
free(s_card.host.dma_aligned_buffer);
s_card.host.dma_aligned_buffer = 0;
sdmmc_host_deinit();
}
//trigger event 7 to indicate Slave to stop the test
static void s_send_finish_test(essl_handle_t handle)
{
@ -102,7 +110,7 @@ TEST_CASE("SDIO_SDMMC: test interrupt", "[sdio]")
esp_err_t ret = ESP_FAIL;
essl_handle_t handle = NULL;
test_sdio_param_t test_param = {
.host_flags = SDMMC_HOST_FLAG_4BIT,
.host_flags = SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF,
.max_freq_khz = SDMMC_FREQ_HIGHSPEED,
};
//essl init and sdmmc init
@ -127,7 +135,7 @@ TEST_CASE("SDIO_SDMMC: test interrupt", "[sdio]")
TEST_ESP_OK(essl_clear_intr(handle, int_st, TEST_TIMEOUT_MAX));
}
sdmmc_host_deinit();
s_master_deinit();
}
/*---------------------------------------------------------------
@ -137,7 +145,7 @@ TEST_CASE("SDIO_SDMMC: test register", "[sdio]")
{
essl_handle_t handle = NULL;
test_sdio_param_t test_param = {
.host_flags = SDMMC_HOST_FLAG_4BIT,
.host_flags = SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF,
.max_freq_khz = SDMMC_FREQ_HIGHSPEED,
};
//essl init and sdmmc init
@ -161,7 +169,7 @@ TEST_CASE("SDIO_SDMMC: test register", "[sdio]")
}
s_send_finish_test(handle);
sdmmc_host_deinit();
s_master_deinit();
}
/*---------------------------------------------------------------
@ -171,7 +179,7 @@ TEST_CASE("SDIO_SDMMC: test reset", "[sdio]")
{
essl_handle_t handle = NULL;
test_sdio_param_t test_param = {
.host_flags = SDMMC_HOST_FLAG_4BIT,
.host_flags = SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF,
.max_freq_khz = SDMMC_FREQ_HIGHSPEED,
};
//essl init and sdmmc init
@ -204,7 +212,7 @@ TEST_CASE("SDIO_SDMMC: test reset", "[sdio]")
}
s_send_finish_test(handle);
sdmmc_host_deinit();
s_master_deinit();
}
/*---------------------------------------------------------------
@ -212,11 +220,11 @@ TEST_CASE("SDIO_SDMMC: test reset", "[sdio]")
---------------------------------------------------------------*/
test_sdio_param_t test_param_lists[TEST_TARNS_PARAM_NUMS] = {
{
.host_flags = SDMMC_HOST_FLAG_4BIT,
.host_flags = SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF,
.max_freq_khz = SDMMC_FREQ_HIGHSPEED,
},
{
.host_flags = SDMMC_HOST_FLAG_1BIT,
.host_flags = SDMMC_HOST_FLAG_1BIT | SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF,
.max_freq_khz = SDMMC_FREQ_HIGHSPEED,
},
};
@ -229,7 +237,7 @@ static void test_from_host(bool check_data)
test_prepare_buffer_pool(TEST_RX_BUFFER_SIZE * 4, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
for (int i = 0; i < TEST_TARNS_PARAM_NUMS; i++) {
ESP_LOGI(TAG, "host mode: %s", (test_param_lists[i].host_flags == SDMMC_HOST_FLAG_4BIT) ? "4BIT Mode" : "1BIT Mode");
ESP_LOGI(TAG, "host mode: %s", (test_param_lists[i].host_flags & SDMMC_HOST_FLAG_4BIT) ? "4BIT Mode" : "1BIT Mode");
ESP_LOGI(TAG, "host speed: %"PRIu32" kHz", test_param_lists[i].max_freq_khz);
essl_handle_t handle = NULL;
@ -262,7 +270,7 @@ static void test_from_host(bool check_data)
esp_rom_delay_us(50 * 1000);
s_send_finish_test(handle);
sdmmc_host_deinit();
s_master_deinit();
}
test_destroy_buffer_pool();
@ -286,7 +294,7 @@ static void test_to_host(bool check_data)
uint8_t *host_rx_buffer = (uint8_t *)heap_caps_calloc(1, recv_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
for (int i = 0; i < TEST_TARNS_PARAM_NUMS; i++) {
ESP_LOGI(TAG, "host mode: %s", (test_param_lists[i].host_flags == SDMMC_HOST_FLAG_4BIT) ? "4BIT Mode" : "1BIT Mode");
ESP_LOGI(TAG, "host mode: %s", (test_param_lists[i].host_flags & SDMMC_HOST_FLAG_4BIT) ? "4BIT Mode" : "1BIT Mode");
ESP_LOGI(TAG, "host speed: %"PRIu32" kHz", test_param_lists[i].max_freq_khz);
essl_handle_t handle = NULL;
@ -333,7 +341,7 @@ static void test_to_host(bool check_data)
esp_rom_delay_us(50 * 1000);
s_send_finish_test(handle);
sdmmc_host_deinit();
s_master_deinit();
}
free(host_rx_buffer);

View File

@ -165,7 +165,8 @@ esp_err_t sdmmc_host_set_cclk_always_on(int slot, bool cclk_always_on);
* can call sdmmc_host_do_transaction as long as other sdmmc_host_*
* functions are not called.
*
* @attention Data buffer passed in cmdinfo->data must be in DMA capable memory
* @attention Data buffer passed in cmdinfo->data must be in DMA capable memory and aligned to 4 byte boundary. If it's
* behind the cache, both cmdinfo->data and cmdinfo->buflen need to be aligned to cache line boundary.
*
* @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
* @param cmdinfo pointer to structure describing command and data to transfer

View File

@ -29,12 +29,6 @@
*/
#define SDMMC_DMA_DESC_CNT 4
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#define SDMMC_ALIGN_ATTR __attribute__((aligned(CONFIG_CACHE_L1_CACHE_LINE_SIZE)))
#else
#define SDMMC_ALIGN_ATTR
#endif
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
static const char* TAG = "sdmmc_req";
@ -67,7 +61,7 @@ const uint32_t SDMMC_CMD_ERR_MASK =
SDMMC_INTMASK_RCRC |
SDMMC_INTMASK_RESP_ERR;
SDMMC_ALIGN_ATTR static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT];
DRAM_DMA_ALIGNED_ATTR static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT];
static sdmmc_transfer_state_t s_cur_transfer = { 0 };
static QueueHandle_t s_request_mutex;
static bool s_is_app_cmd; // This flag is set if the next command is an APP command

View File

@ -175,7 +175,12 @@ typedef struct {
#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
#define SDMMC_HOST_FLAG_DDR BIT(4) /*!< host supports DDR mode for SD/MMC */
#define SDMMC_HOST_FLAG_DEINIT_ARG BIT(5) /*!< host `deinit` function called with the slot argument */
#define SDMMC_HOST_FLAG_DEINIT_ARG BIT(5) /*!< host `deinit` function called with the slot argument */
#define SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF \
BIT(6) /*!< Allocate internal buffer of 512 bytes that meets DMA's requirements.
Currently this is only used by the SDIO driver. Set this flag when
using SDIO CMD53 byte mode, with user buffer that is behind the cache
or not aligned to 4 byte boundary. */
int slot; /*!< slot number, to be passed to host functions */
int max_freq_khz; /*!< max frequency supported by the host */
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
@ -201,6 +206,7 @@ typedef struct {
esp_err_t (*get_real_freq)(int slot, int* real_freq); /*!< Host function to provide real working freq, based on SDMMC controller setup */
sdmmc_delay_phase_t input_delay_phase; /*!< input delay phase, this will only take into effect when the host works in SDMMC_FREQ_HIGHSPEED or SDMMC_FREQ_52M. Driver will print out how long the delay is*/
esp_err_t (*set_input_delay)(int slot, sdmmc_delay_phase_t delay_phase); /*!< set input delay phase */
void* dma_aligned_buffer; /*!< Leave it NULL. Reserved for cache aligned buffers for SDIO mode */
} sdmmc_host_t;
/**

View File

@ -200,8 +200,10 @@ esp_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
* using sdmmc_card_init
* @param function IO function number
* @param addr byte address within IO function where reading starts
* @param dst buffer which receives the data read from card
* @param size number of bytes to read
* @param dst buffer which receives the data read from card. Aligned to 4 byte boundary unless
* `SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF` flag is set when calling `sdmmc_card_init`. The flag is mandatory
* when the buffer is behind the cache.
* @param size number of bytes to read, 1 to 512.
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_SIZE if size exceeds 512 bytes
@ -220,8 +222,9 @@ esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
* using sdmmc_card_init
* @param function IO function number
* @param addr byte address within IO function where writing starts
* @param src data to be written
* @param size number of bytes to write
* @param src data to be written. Aligned to 4 byte boundary unless `SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF` flag is set
* when calling `sdmmc_card_init`. The flag is mandatory when the buffer is behind the cache.
* @param size number of bytes to write, 1 to 512.
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_SIZE if size exceeds 512 bytes
@ -240,7 +243,8 @@ esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
* using sdmmc_card_init
* @param function IO function number
* @param addr byte address within IO function where writing starts
* @param dst buffer which receives the data read from card
* @param dst buffer which receives the data read from card. Aligned to 4 byte boundary, and also cache line size if
* the buffer is behind the cache.
* @param size number of bytes to read, must be divisible by the card block
* size.
* @return
@ -261,7 +265,8 @@ esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
* using sdmmc_card_init
* @param function IO function number
* @param addr byte address within IO function where writing starts
* @param src data to be written
* @param src data to be written. Aligned to 4 byte boundary, and also cache line size if the buffer is behind the
* cache.
* @param size number of bytes to read, must be divisible by the card block
* size.
* @return

View File

@ -325,6 +325,21 @@ esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
return ESP_OK;
}
esp_err_t sdmmc_allocate_aligned_buf(sdmmc_card_t* card)
{
if (card->host.flags & SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF) {
void* buf = NULL;
size_t actual_size = 0;
esp_err_t ret = esp_dma_malloc(SDMMC_IO_BLOCK_SIZE, 0, &buf, &actual_size);
if (ret != ESP_OK) {
return ret;
}
assert(actual_size == SDMMC_IO_BLOCK_SIZE);
card->host.dma_aligned_buffer = buf;
}
return ESP_OK;
}
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
{
if (card->is_mmc) {

View File

@ -99,11 +99,12 @@ uint32_t sdmmc_sd_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t
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);
esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
uint32_t reg, int arg, void *data, size_t size);
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);
/* 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);
@ -154,3 +155,7 @@ static inline uint32_t get_host_ocr(float voltage)
void sdmmc_flip_byte_order(uint32_t* response, size_t size);
esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card);
//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);

View File

@ -35,10 +35,14 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
{
memset(card, 0, sizeof(*card));
memcpy(&card->host, config, sizeof(*config));
const bool is_spi = host_is_spi(card);
const bool always = true;
const bool io_supported = true;
/* Allocate cache-aligned buffer for SDIO over SDMMC.*/
SDMMC_INIT_STEP(!is_spi, sdmmc_allocate_aligned_buf);
/* Check if host flags are compatible with slot configuration. */
SDMMC_INIT_STEP(!is_spi, sdmmc_fix_host_flags);

View File

@ -266,16 +266,28 @@ esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func,
uint32_t reg, int arg, void *datap, size_t datalen)
{
esp_err_t err;
const size_t max_byte_transfer_size = 512;
const int buflen = (datalen + 3) & (~3); //round up to 4
sdmmc_command_t cmd = {
.flags = SCF_CMD_AC | SCF_RSP_R5,
.arg = 0,
.opcode = SD_IO_RW_EXTENDED,
.data = datap,
.datalen = datalen,
.blklen = max_byte_transfer_size /* TODO: read max block size from CIS */
.buflen = buflen,
.blklen = SDMMC_IO_BLOCK_SIZE /* TODO: read max block size from CIS */
};
if (unlikely(datalen > 0 && !esp_dma_is_buffer_aligned(datap, buflen, ESP_DMA_BUF_LOCATION_AUTO))) {
if (datalen > SDMMC_IO_BLOCK_SIZE || card->host.dma_aligned_buffer == NULL) {
// User gives unaligned buffer while `SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF` not set.
return ESP_ERR_INVALID_ARG;
}
memset(card->host.dma_aligned_buffer, 0xcc, SDMMC_IO_BLOCK_SIZE);
memcpy(card->host.dma_aligned_buffer, datap, datalen);
cmd.data = card->host.dma_aligned_buffer;
cmd.buflen = SDMMC_IO_BLOCK_SIZE;
}
uint32_t count; /* number of bytes or blocks, depending on transfer mode */
if (arg & SD_ARG_CMD53_BLOCK_MODE) {
if (cmd.datalen % cmd.blklen != 0) {
@ -283,11 +295,11 @@ esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func,
}
count = cmd.datalen / cmd.blklen;
} else {
if (datalen > max_byte_transfer_size) {
if (datalen > SDMMC_IO_BLOCK_SIZE) {
/* TODO: split into multiple operations? */
return ESP_ERR_INVALID_SIZE;
}
if (datalen == max_byte_transfer_size) {
if (datalen == SDMMC_IO_BLOCK_SIZE) {
count = 0; // See 5.3.1 SDIO simplifed spec
} else {
count = datalen;
@ -305,6 +317,12 @@ esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func,
}
err = sdmmc_send_cmd(card, &cmd);
if (datalen > 0 && cmd.data == card->host.dma_aligned_buffer) {
assert(datalen <= SDMMC_IO_BLOCK_SIZE);
memcpy(datap, card->host.dma_aligned_buffer, datalen);
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
@ -368,8 +386,8 @@ esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size)
{
if (unlikely(size % 4 != 0)) {
return ESP_ERR_INVALID_SIZE;
if (unlikely(!esp_dma_is_buffer_aligned(dst, size, ESP_DMA_BUF_LOCATION_INTERNAL))) {
return ESP_ERR_INVALID_ARG;
}
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
@ -379,8 +397,8 @@ esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size)
{
if (unlikely(size % 4 != 0)) {
return ESP_ERR_INVALID_SIZE;
if (unlikely(!esp_dma_is_buffer_aligned(src, size, ESP_DMA_BUF_LOCATION_INTERNAL))) {
return ESP_ERR_INVALID_ARG;
}
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,

View File

@ -197,6 +197,10 @@ esp_err_t slave_init(essl_handle_t* handle)
config.max_freq_khz = SDMMC_FREQ_DEFAULT;
#endif
// Set this flag to allocate aligned buffer of 512 bytes to meet DMA's requirements for CMD53 byte mode. Mandatory
// when any buffer is behind the cache, or not aligned to 4 byte boundary.
config.flags |= SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
/* Note: For small devkits there may be no pullups on the board.
This enables the internal pullups to help evaluate the driver quickly.
@ -424,17 +428,13 @@ void job_write_reg(essl_handle_t handle, int value)
//so first 5 packets (use 1+1+8+4+1=15 buffers) are sent, the others (513, 517) failed (timeout)
int packet_len[] = {6, 12, 1024, 512, 3, 513, 517};
//the sending buffer should be word aligned
DMA_ATTR uint8_t send_buffer[READ_BUFFER_LEN];
DRAM_DMA_ALIGNED_ATTR uint8_t send_buffer[READ_BUFFER_LEN];
//send packets to the slave (they will return and be handled by the interrupt handler)
void job_fifo(essl_handle_t handle)
{
for (int i = 0; i < READ_BUFFER_LEN; i++) {
send_buffer[i] = 0x46 + i * 5;
}
esp_err_t ret;
int pointer = 0;
int data_index = 0;
ESP_LOGI(TAG, "========JOB: send fifos========");
/* CAUTION: This example shows that we can send random length of packet to the slave.
@ -443,16 +443,21 @@ void job_fifo(essl_handle_t handle)
* Try to avoid unaligned packets if possible to get higher effeciency.
*/
for (int i = 0; i < sizeof(packet_len) / sizeof(int); i++) {
const int wait_ms = 50;
//Prepare data to send. The length can be random, but data should start at the 32-bit boundary.
int length = packet_len[i];
ret = essl_send_packet(handle, send_buffer + pointer, length, wait_ms);
for (int i = 0; i < length; i ++) {
send_buffer[i] = 0x46 + (data_index + i) * 5;
}
data_index += (length + 3) & (~3); //get different data next time
const int wait_ms = 50;
ret = essl_send_packet(handle, send_buffer, length, wait_ms);
if (ret == ESP_ERR_TIMEOUT || ret == ESP_ERR_NOT_FOUND) {
ESP_LOGD(TAG, "slave not ready to receive packet %d", i); // And there are several packets expected to timeout.
} else {
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "send packet length: %d", length);
}
pointer += (length + 3) & (~3); //the length can be random, but data should start at the 32-bit boundary.
}
}