feat(sdmmc): support for voltage switching in protocol layer

This commit is contained in:
Ivan Grokhotkov 2024-10-04 18:48:06 +02:00 committed by Armando
parent a03cbdba2e
commit 31dc34a909
6 changed files with 65 additions and 4 deletions

View File

@ -3,7 +3,7 @@
* *
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
* *
* SPDX-FileContributor: 2016-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileContributor: 2016-2024 Espressif Systems (Shanghai) CO LTD
*/ */
/* /*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@ -59,6 +59,7 @@ extern "C" {
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */ #define SD_SEND_RELATIVE_ADDR 3 /* R6 */
#define SD_SEND_SWITCH_FUNC 6 /* R1 */ #define SD_SEND_SWITCH_FUNC 6 /* R1 */
#define SD_SEND_IF_COND 8 /* R7 */ #define SD_SEND_IF_COND 8 /* R7 */
#define SD_SWITCH_VOLTAGE 11 /* R1 */
#define SD_ERASE_GROUP_START 32 /* R1 */ #define SD_ERASE_GROUP_START 32 /* R1 */
#define SD_ERASE_GROUP_END 33 /* R1 */ #define SD_ERASE_GROUP_END 33 /* R1 */
#define SD_READ_OCR 58 /* R3 */ #define SD_READ_OCR 58 /* R3 */
@ -98,8 +99,20 @@ extern "C" {
#define MMC_OCR_2_0V_2_1V (1<<8) #define MMC_OCR_2_0V_2_1V (1<<8)
#define MMC_OCR_1_65V_1_95V (1<<7) #define MMC_OCR_1_65V_1_95V (1<<7)
#define SD_OCR_SDHC_CAP (1<<30) #define SD_OCR_CARD_READY MMC_OCR_MEM_READY /* bit-31: power-up status */
#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */ #define SD_OCR_SDHC_CAP (1<<30) /* HCS bit */
#define SD_OCR_XPC (1<<28) /* SDXC Power Control (bit 28) */
#define SD_OCR_S18_RA (1<<24) /* S18R/A bit: 1.8V voltage support, UHS-I only */
#define SD_OCR_VOL_MASK 0xFF8000 /* SD OCR voltage bits 23:15 */
#define SD_OCR_3_5V_3_6V MMC_OCR_3_5V_3_6V /* bit-23 */
#define SD_OCR_3_4V_3_5V MMC_OCR_3_4V_3_5V /* bit-22 */
#define SD_OCR_3_3V_3_4V MMC_OCR_3_3V_3_4V /* ... */
#define SD_OCR_3_2V_3_3V MMC_OCR_3_2V_3_3V
#define SD_OCR_3_1V_3_2V MMC_OCR_3_1V_3_2V
#define SD_OCR_3_0V_3_1V MMC_OCR_3_0V_3_1V
#define SD_OCR_2_9V_3_0V MMC_OCR_2_9V_3_0V
#define SD_OCR_2_8V_2_9V MMC_OCR_2_8V_2_9V /* ... */
#define SD_OCR_2_7V_2_8V MMC_OCR_2_7V_2_8V /* bit-15 */
/* SD mode R1 response type bits */ /* SD mode R1 response type bits */
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */ #define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */

View File

@ -149,6 +149,8 @@ typedef struct {
/** @endcond */ /** @endcond */
esp_err_t error; /*!< error returned from transfer */ esp_err_t error; /*!< error returned from transfer */
uint32_t timeout_ms; /*!< response timeout, in milliseconds */ uint32_t timeout_ms; /*!< response timeout, in milliseconds */
esp_err_t (*volt_switch_cb)(void*, int); /*!< callback to be called during CMD11 to switch voltage */
void* volt_switch_cb_arg; /*!< argument to be passed to the CMD11 callback */
} sdmmc_command_t; } sdmmc_command_t;
/** /**
@ -183,6 +185,7 @@ typedef struct {
Currently this is only used by the SDIO driver. Set this flag when 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 using SDIO CMD53 byte mode, with user buffer that is behind the cache
or not aligned to 4 byte boundary. */ or not aligned to 4 byte boundary. */
#define SDMMC_HOST_FLAG_UHS1 BIT(7) /*!< host supports UHS-I mode */
int slot; /*!< slot number, to be passed to host functions */ int slot; /*!< slot number, to be passed to host functions */
int max_freq_khz; /*!< max frequency supported by the host */ int max_freq_khz; /*!< max frequency supported by the host */
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */ #define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */

View File

@ -42,6 +42,12 @@ esp_err_t sdmmc_init_ocr(sdmmc_card_t* card)
acmd41_arg |= SD_OCR_SDHC_CAP; acmd41_arg |= SD_OCR_SDHC_CAP;
} }
if ((card->host.flags & SDMMC_HOST_FLAG_UHS1) != 0) {
acmd41_arg |= SD_OCR_S18_RA;
acmd41_arg |= SD_OCR_XPC;
}
ESP_LOGV(TAG, "%s: acmd41_arg=0x%08" PRIx32, __func__, card->ocr);
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */ /* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
err = sdmmc_send_cmd_send_op_cond(card, acmd41_arg, &card->ocr); err = sdmmc_send_cmd_send_op_cond(card, acmd41_arg, &card->ocr);
@ -278,7 +284,15 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
type = "MMC"; type = "MMC";
print_csd = true; print_csd = true;
} else { } else {
type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC"; if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
type = "SDSC";
} else {
if (card->ocr & SD_OCR_S18_RA) {
type = "SDHC/SDXC (UHS-I)";
} else {
type = "SDHC";
}
}
print_csd = true; print_csd = true;
} }
fprintf(stream, "Type: %s\n", type); fprintf(stream, "Type: %s\n", type);

View File

@ -77,6 +77,7 @@ 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_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_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_crc_on_off(sdmmc_card_t* card, bool crc_enable);
esp_err_t sdmmc_send_cmd_voltage_switch(sdmmc_card_t* card);
/* Higher level functions */ /* Higher level functions */
esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card); esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card);
@ -139,6 +140,7 @@ 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_card_hs_mode(sdmmc_card_t* card);
esp_err_t sdmmc_init_host_frequency(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_mmc_check_ext_csd(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_uhs1(sdmmc_card_t* card);
/* Various helper functions */ /* Various helper functions */
static inline bool host_is_spi(const sdmmc_card_t* card) static inline bool host_is_spi(const sdmmc_card_t* card)

View File

@ -95,6 +95,10 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
ESP_LOGD(TAG, "%s: card type is %s", __func__, ESP_LOGD(TAG, "%s: card type is %s", __func__,
is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD"); is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
/* switch to 1.8V if supported (UHS-I) */
bool is_uhs1 = is_sdmem && (card->ocr & SD_OCR_S18_RA) && (card->ocr & SD_OCR_SDHC_CAP);
SDMMC_INIT_STEP(is_uhs1, sdmmc_init_sd_uhs1);
/* Read the contents of CID register*/ /* Read the contents of CID register*/
SDMMC_INIT_STEP(is_mem, sdmmc_init_cid); SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);

View File

@ -322,6 +322,31 @@ esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
return ESP_OK; return ESP_OK;
} }
static esp_err_t sdmmc_init_sd_uhs1_volt_sw_cb(void* arg, int voltage_mv)
{
sdmmc_card_t* card = (sdmmc_card_t*)arg;
ESP_LOGV(TAG, "%s: Voltage switch callback (%umv)", __func__, voltage_mv);
return sd_pwr_ctrl_set_io_voltage(card->host.pwr_ctrl_handle, voltage_mv);
}
esp_err_t sdmmc_init_sd_uhs1(sdmmc_card_t* card)
{
sdmmc_command_t cmd = {
.opcode = SD_SWITCH_VOLTAGE,
.arg = 0,
.flags = SCF_CMD_AC | SCF_RSP_R1,
.volt_switch_cb = &sdmmc_init_sd_uhs1_volt_sw_cb,
.volt_switch_cb_arg = card
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_cmd returned 0x%x", __func__, err);
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_check_scr(sdmmc_card_t* card) esp_err_t sdmmc_check_scr(sdmmc_card_t* card)
{ {
/* If frequency switch has been performed, read SCR register one more time /* If frequency switch has been performed, read SCR register one more time