sdmmc: add SDIO support

- Add SDIO support at protocol layer (probing, data transfer, interrupts)
- Add SDIO interrupts support in SDMMC host
- Add test (communicate with ESP32 in SDIO download mode)
This commit is contained in:
Ivan Grokhotkov
2018-03-06 17:57:52 +08:00
parent 5f97e6bebb
commit ee600784c5
9 changed files with 1223 additions and 191 deletions
+484 -171
View File
@@ -26,7 +26,8 @@
#include "sys/param.h"
#include "soc/soc_memory_layout.h"
#define SDMMC_GO_IDLE_DELAY_MS 20
#define SDMMC_GO_IDLE_DELAY_MS 20
#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10
/* 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
@@ -53,13 +54,14 @@ static 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);
static esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card);
static esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
static esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card);
static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca);
static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status);
static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
static esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
static uint32_t get_host_ocr(float voltage);
@@ -68,7 +70,12 @@ static esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count);
static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count);
static esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
static esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int function,
uint32_t reg, uint32_t arg, uint8_t *byte);
static esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
uint32_t reg, int arg, void *data, size_t size);
static void sdmmc_fix_host_flags(sdmmc_card_t* card);
static bool host_is_spi(const sdmmc_card_t* card)
{
@@ -77,28 +84,38 @@ static bool host_is_spi(const sdmmc_card_t* card)
esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
{
ESP_LOGD(TAG, "%s", __func__);
esp_err_t err;
memset(card, 0, sizeof(*card));
memcpy(&card->host, config, sizeof(*config));
const bool is_spi = host_is_spi(card);
if ( !is_spi ) {
//check HOST flags compatible with slot configuration.
int slot_bit_width = config->get_bus_width(config->slot);
if ( slot_bit_width == 1 && (config->flags & (SDMMC_HOST_FLAG_4BIT|SDMMC_HOST_FLAG_8BIT))) {
ESP_LOGW(TAG, "HOST slot only enables 1-bit.");
card->host.flags = ((card->host.flags&(~(SDMMC_HOST_FLAG_4BIT|SDMMC_HOST_FLAG_8BIT)))|SDMMC_HOST_FLAG_1BIT);
} else if ( slot_bit_width == 4 && (config->flags & SDMMC_HOST_FLAG_8BIT)){
ESP_LOGW(TAG, "HOST slot only enables 4-bit.");
card->host.flags = ((card->host.flags&(~(SDMMC_HOST_FLAG_1BIT|SDMMC_HOST_FLAG_8BIT)))|SDMMC_HOST_FLAG_4BIT);
}
if (!is_spi) {
// Check if host flags are compatible with slot configuration.
sdmmc_fix_host_flags(card);
}
/* ----------- standard initialization process starts here ---------- */
/* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5).
* Non-IO cards are allowed to time out.
*/
uint8_t sdio_reset = CCCR_CTL_RES;
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CTL, SD_ARG_CMD52_WRITE, &sdio_reset);
if (err != ESP_OK && err != ESP_ERR_TIMEOUT) {
ESP_LOGE(TAG, "%s: sdio_reset: unexpected return: 0x%x", __func__, err );
return err;
}
/* GO_IDLE_STATE (CMD0) command resets the card */
esp_err_t err = sdmmc_send_cmd_go_idle_state(card);
err = sdmmc_send_cmd_go_idle_state(card);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err);
return err;
}
/* FIXME: we should check card status to wait until it is out of idle
* state, instead of using a delay.
*/
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
sdmmc_send_cmd_go_idle_state(card);
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
@@ -120,51 +137,84 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
return err;
}
/* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
* ranges the card can support. This step is skipped since 1.8V isn't
* supported on the ESP32.
*/
/* In SD mode, CRC checks of data transfers are mandatory and performed
* by the hardware. In SPI mode, CRC16 of data transfers is optional and
* needs to be enabled.
*/
if (is_spi) {
err = sdmmc_send_cmd_crc_on_off(card, true);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
return err;
}
}
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
/* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
* Non-IO cards will not respond to this command.
*/
err = sdmmc_io_send_op_cond(card, 0, &card->ocr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
if (is_spi) {
err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
ESP_LOGD(TAG, "%s: io_send_op_cond (1) returned 0x%x; not IO card", __func__, err);
card->is_sdio = 0;
card->is_mem = 1;
} else {
card->is_sdio = 1;
if (card->ocr & SD_IO_OCR_MEM_PRESENT) {
ESP_LOGD(TAG, "%s: IO-only card", __func__);
card->is_mem = 0;
}
card->num_io_functions = SD_IO_OCR_NUM_FUNCTIONS(card->ocr);
ESP_LOGD(TAG, "%s: number of IO functions: %d", __func__, card->num_io_functions);
if (card->num_io_functions == 0) {
card->is_sdio = 0;
}
host_ocr &= card->ocr;
err = sdmmc_io_send_op_cond(card, host_ocr, &card->ocr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: read_ocr returned 0x%x", __func__, err);
ESP_LOGE(TAG, "%s: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
sdmmc_io_enable_int(card);
}
ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr);
/* Clear all voltage bits in host's OCR which the card doesn't support.
* Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41
* response.
*/
host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK));
ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
if (card->is_mem) {
/* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
* ranges the card can support. This step is skipped since 1.8V isn't
* supported on the ESP32.
*/
/* In SD mode, CRC checks of data transfers are mandatory and performed
* by the hardware. In SPI mode, CRC16 of data transfers is optional and
* needs to be enabled.
*/
if (is_spi) {
err = sdmmc_send_cmd_crc_on_off(card, true);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
return err;
}
}
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
if (is_spi) {
err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: read_ocr returned 0x%x", __func__, err);
return err;
}
}
ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr);
/* Clear all voltage bits in host's OCR which the card doesn't support.
* Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41
* response.
*/
host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK));
ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
}
/* Read and decode the contents of CID register */
if (!is_spi) {
err = sddmc_send_cmd_all_send_cid(card, &card->cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
return err;
if (card->is_mem) {
err = sddmc_send_cmd_all_send_cid(card, &card->cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
return err;
}
}
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
if (err != ESP_OK) {
@@ -178,20 +228,22 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
return err;
}
}
/* Get and decode the contents of CSD register. Determine card capacity. */
err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd (1) returned 0x%x", __func__, err);
return err;
}
const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
if (!(card->ocr & SD_OCR_SDHC_CAP) &&
card->csd.capacity > max_sdsc_capacity) {
ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
__func__, card->csd.capacity, max_sdsc_capacity);
card->csd.capacity = max_sdsc_capacity;
if (card->is_mem) {
/* Get and decode the contents of CSD register. Determine card capacity. */
err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd (1) returned 0x%x", __func__, err);
return err;
}
const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
if (!(card->ocr & SD_OCR_SDHC_CAP) &&
card->csd.capacity > max_sdsc_capacity) {
ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
__func__, card->csd.capacity, max_sdsc_capacity);
card->csd.capacity = max_sdsc_capacity;
}
}
/* ----------- standard initialization process ends here ----------- */
/* Switch the card from stand-by mode to data transfer mode (not needed if
* SPI interface is used). This is needed to issue SET_BLOCKLEN and
@@ -205,112 +257,118 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
}
}
/* SDSC cards support configurable data block lengths.
* We don't use this feature and set the block length to 512 bytes,
* same as the block length for SDHC cards.
*/
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
if (card->is_mem) {
/* SDSC cards support configurable data block lengths.
* We don't use this feature and set the block length to 512 bytes,
* same as the block length for SDHC cards.
*/
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
return err;
}
}
/* Get the contents of SCR register: bus width and the version of SD spec
* supported by the card.
* In SD mode, this is the first command which uses D0 line. Errors at
* this step usually indicate connection issue or lack of pull-up resistor.
*/
err = sdmmc_send_cmd_send_scr(card, &card->scr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
return err;
}
}
/* Get the contents of SCR register: bus width and the version of SD spec
* supported by the card.
* In SD mode, this is the first command which uses D0 line. Errors at
* this step usually indicate connection issue or lack of pull-up resistor.
*/
err = sdmmc_send_cmd_send_scr(card, &card->scr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
return err;
}
/* If the host has been initialized with 4-bit bus support, and the card
* supports 4-bit bus, switch to 4-bit bus now.
*/
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
(card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
ESP_LOGD(TAG, "switching to 4-bit bus mode");
err = sdmmc_send_cmd_set_bus_width(card, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "set_bus_width failed");
return err;
if (card->is_mem) {
/* If the host has been initialized with 4-bit bus support, and the card
* supports 4-bit bus, switch to 4-bit bus now.
*/
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
(card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
ESP_LOGD(TAG, "switching to 4-bit bus mode");
err = sdmmc_send_cmd_set_bus_width(card, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "set_bus_width failed");
return err;
}
err = (*config->set_bus_width)(config->slot, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "slot->set_bus_width failed");
return err;
}
}
err = (*config->set_bus_width)(config->slot, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "slot->set_bus_width failed");
return err;
/* Wait for the card to be ready for data transfers */
uint32_t status = 0;
while (!is_spi && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
uint32_t count = 0;
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
return err;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
} else {
/* IO card */
if (config->flags & SDMMC_HOST_FLAG_4BIT) {
uint8_t card_cap;
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CARD_CAP,
SD_ARG_CMD52_READ, &card_cap);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read SD_IO_CCCR_CARD_CAP) returned 0x%0x", __func__, err);
return err;
}
ESP_LOGD(TAG, "IO card capabilities byte: %02x", card_cap);
if (!(card_cap & CCCR_CARD_CAP_LSC) ||
(card_cap & CCCR_CARD_CAP_4BLS)) {
// This card supports 4-bit bus mode
uint8_t bus_width = CCCR_BUS_WIDTH_4;
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_BUS_WIDTH,
SD_ARG_CMD52_WRITE, &bus_width);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write SD_IO_CCCR_BUS_WIDTH) returned 0x%0x", __func__, err);
return err;
}
err = (*config->set_bus_width)(config->slot, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "slot->set_bus_width failed");
return err;
}
}
}
}
/* Wait for the card to be ready for data transfers */
uint32_t status = 0;
while (!is_spi && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
uint32_t count = 0;
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
return err;
}
if (++count % 10 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
/* So far initialization has been done using 400kHz clock. Determine the
* clock rate which both host and the card support, and switch to it.
*/
bool freq_switched = false;
if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
!is_spi /* SPI doesn't support >26MHz in some cases */) {
/* This will determine if the card supports SWITCH_FUNC command,
* and high speed mode. If the cards supports both, this will enable
* high speed mode at the card side.
*/
err = sdmmc_enable_hs_mode(card);
if (card->is_mem) {
err = sdmmc_enable_hs_mode_and_check(card);
} else {
err = sdmmc_io_enable_hs_mode(card);
}
if (err == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
} else if (err != ESP_OK) {
/* some other error */
return err;
} else { /* ESP_OK */
/* HS mode has been enabled on the card.
* Read CSD again, it should now indicate that the card supports
* 50MHz clock.
* Since SEND_CSD is allowed only in standby mode, and the card is
* currently in data transfer more, deselect the card first, then
* get the CSD, then select the card again.
} else {
ESP_LOGD(TAG, "%s: switching host to HS mode", __func__);
/* ESP_OK, HS mode has been enabled on the card side.
* Switch the host to HS mode.
*/
err = sdmmc_send_cmd_select_card(card, 0);
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err);
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
return err;
}
err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd (2) returned 0x%x", __func__, err);
return err;
}
err = sdmmc_send_cmd_select_card(card, card->rca);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card (3) returned 0x%x", __func__, err);
return err;
}
if (card->csd.tr_speed != 50000000) {
ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed);
} else {
/* Finally can switch the host to HS mode */
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
return err;
}
freq_switched = true;
}
freq_switched = true;
}
}
/* All SD cards must support default speed mode (25MHz).
@@ -331,15 +389,21 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
* an indicator of potential signal integrity issues.
*/
if (freq_switched) {
sdmmc_scr_t scr_tmp;
err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr (2) returned 0x%x", __func__, err);
return err;
}
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
return ESP_ERR_INVALID_RESPONSE;
if (card->is_mem) {
sdmmc_scr_t scr_tmp;
err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr (2) returned 0x%x", __func__, err);
return err;
}
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
return ESP_ERR_INVALID_RESPONSE;
}
} else {
/* TODO: For IO cards, read some data to see if frequency switch
* was successful.
*/
}
}
return ESP_OK;
@@ -357,6 +421,26 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
}
static void sdmmc_fix_host_flags(sdmmc_card_t* card)
{
const uint32_t width_1bit = SDMMC_HOST_FLAG_1BIT;
const uint32_t width_4bit = SDMMC_HOST_FLAG_4BIT;
const uint32_t width_8bit = SDMMC_HOST_FLAG_8BIT;
const uint32_t width_mask = width_1bit | width_4bit | width_8bit;
int slot_bit_width = card->host.get_bus_width(card->host.slot);
if (slot_bit_width == 1 &&
(card->host.flags & (width_4bit | width_8bit))) {
ESP_LOGW(TAG, "host slot is configured in 1-bit mode");
card->host.flags &= ~width_mask;
card->host.flags |= ~(width_1bit);
} else if (slot_bit_width == 4 && (card->host.flags & width_8bit)){
ESP_LOGW(TAG, "host slot is configured in 4-bit mode");
card->host.flags &= ~width_mask;
card->host.flags |= width_4bit;
}
}
static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
{
if (card->host.command_timeout_ms != 0) {
@@ -675,20 +759,6 @@ static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width)
return sdmmc_send_app_cmd(card, &cmd);
}
static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status)
{
sdmmc_command_t cmd = {
.opcode = MMC_STOP_TRANSMISSION,
.arg = 0,
.flags = SCF_RSP_R1B | SCF_CMD_AC
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err == 0) {
*status = MMC_R1(cmd.response);
}
return err;
}
static esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable)
{
assert(host_is_spi(card) && "CRC_ON_OFF can only be used in SPI mode");
@@ -944,6 +1014,10 @@ static esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
static esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card)
{
/* This will determine if the card supports SWITCH_FUNC command,
* and high speed mode. If the cards supports both, this will enable
* high speed mode at the card side.
*/
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
return ESP_ERR_NOT_SUPPORTED;
@@ -974,3 +1048,242 @@ out:
free(response);
return err;
}
static esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
{
/* Try to enabled HS mode */
esp_err_t err = sdmmc_enable_hs_mode(card);
if (err != ESP_OK) {
return err;
}
/* HS mode has been enabled on the card.
* Read CSD again, it should now indicate that the card supports
* 50MHz clock.
* Since SEND_CSD is allowed only in standby mode, and the card is
* currently in data transfer more, deselect the card first, then
* get the CSD, then select the card again.
*/
err = sdmmc_send_cmd_select_card(card, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err);
return err;
}
err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd (2) returned 0x%x", __func__, err);
return err;
}
err = sdmmc_send_cmd_select_card(card, card->rca);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card (3) returned 0x%x", __func__, err);
return err;
}
if (card->csd.tr_speed != 50000000) {
ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed);
return ESP_ERR_NOT_SUPPORTED;
}
return ESP_OK;
}
static esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
{
/* For IO cards, do write + read operation on "High Speed" register,
* setting EHS bit. If both EHS and SHS read back as set, then HS mode
* has been enabled.
*/
uint8_t val = CCCR_HIGHSPEED_ENABLE;
esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_HIGHSPEED,
SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &val);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_io_rw_direct returned 0x%x", __func__, err);
return err;
}
ESP_LOGD(TAG, "%s: CCCR_HIGHSPEED=0x%02x", __func__, val);
const uint8_t hs_mask = CCCR_HIGHSPEED_ENABLE | CCCR_HIGHSPEED_SUPPORT;
if ((val & hs_mask) != hs_mask) {
return ESP_ERR_NOT_SUPPORTED;
}
return ESP_OK;
}
static esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
{
esp_err_t err = ESP_OK;
sdmmc_command_t cmd = {
.flags = SCF_CMD_BCR | SCF_RSP_R4,
.arg = ocr,
.opcode = SD_IO_SEND_OP_COND
};
for (size_t i = 0; i < 100; i++) {
err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
break;
}
if ((MMC_R4(cmd.response) & SD_IO_OCR_MEM_READY) ||
ocr == 0) {
break;
}
err = ESP_ERR_TIMEOUT;
vTaskDelay(SDMMC_IO_SEND_OP_COND_DELAY_MS / portTICK_PERIOD_MS);
}
if (err == ESP_OK && ocrp != NULL)
*ocrp = MMC_R4(cmd.response);
return err;
}
static esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int func,
uint32_t reg, uint32_t arg, uint8_t *byte)
{
esp_err_t err;
sdmmc_command_t cmd = {
.flags = SCF_CMD_AC | SCF_RSP_R5,
.arg = 0,
.opcode = SD_IO_RW_DIRECT
};
arg |= (func & SD_ARG_CMD52_FUNC_MASK) << SD_ARG_CMD52_FUNC_SHIFT;
arg |= (reg & SD_ARG_CMD52_REG_MASK) << SD_ARG_CMD52_REG_SHIFT;
arg |= (*byte & SD_ARG_CMD52_DATA_MASK) << SD_ARG_CMD52_DATA_SHIFT;
cmd.arg = arg;
err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGV(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
*byte = SD_R5_DATA(cmd.response);
return ESP_OK;
}
esp_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
uint32_t addr, uint8_t *out_byte)
{
esp_err_t ret = sdmmc_io_rw_direct(card, function, addr, SD_ARG_CMD52_READ, out_byte);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read 0x%x) returned 0x%x", __func__, addr, ret);
}
return ret;
}
esp_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
uint32_t addr, uint8_t in_byte, uint8_t* out_byte)
{
uint8_t tmp_byte = in_byte;
esp_err_t ret = sdmmc_io_rw_direct(card, function, addr,
SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &tmp_byte);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write 0x%x) returned 0x%x", __func__, addr, ret);
return ret;
}
if (out_byte != NULL) {
*out_byte = tmp_byte;
}
return ESP_OK;
}
static 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;
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 */
};
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) {
return ESP_ERR_INVALID_SIZE;
}
count = cmd.datalen / cmd.blklen;
} else {
if (datalen > max_byte_transfer_size) {
/* TODO: split into multiple operations? */
return ESP_ERR_INVALID_SIZE;
}
if (datalen == max_byte_transfer_size) {
count = 0; // See 5.3.1 SDIO simplifed spec
} else {
count = datalen;
}
cmd.blklen = datalen;
}
arg |= (func & SD_ARG_CMD53_FUNC_MASK) << SD_ARG_CMD53_FUNC_SHIFT;
arg |= (reg & SD_ARG_CMD53_REG_MASK) << SD_ARG_CMD53_REG_SHIFT;
arg |= (count & SD_ARG_CMD53_LENGTH_MASK) << SD_ARG_CMD53_LENGTH_SHIFT;
cmd.arg = arg;
if ((arg & SD_ARG_CMD53_WRITE) == 0) {
cmd.flags |= SCF_CMD_READ;
}
err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size)
{
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT,
dst, size);
}
esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size)
{
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT,
(void*) src, size);
}
esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size)
{
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
dst, size);
}
esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size)
{
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
(void*) src, size);
}
esp_err_t sdmmc_io_enable_int(sdmmc_card_t* card)
{
if (card->host.io_int_enable == NULL) {
return ESP_ERR_NOT_SUPPORTED;
}
return (*card->host.io_int_enable)(card->host.slot);
}
esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks)
{
if (card->host.io_int_wait == NULL) {
return ESP_ERR_NOT_SUPPORTED;
}
return (*card->host.io_int_wait)(card->host.slot, timeout_ticks);
}