* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
*/
#define SDMMC_HOST_DEFAULT() {\
- .flags = (SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_MEM_CARD), \
+ .flags = SDMMC_HOST_FLAG_4BIT, \
.slot = SDMMC_HOST_SLOT_1, \
.max_freq_khz = SDMMC_FREQ_DEFAULT, \
.io_voltage = 3.3f, \
int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */
} sdmmc_scr_t;
+/**
+ * Decoded values of Extended Card Specific Data
+ */
+typedef struct {
+ uint8_t power_class; /*!< Power class used by the card */
+} sdmmc_ext_csd_t;
+
/**
* SD/MMC command response buffer
*/
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */
#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */
-#define SDMCC_FREQ_52M 52000 /*!< MMC 52Mhz speed */
-#define SDMCC_FREQ_26M 26000 /*!< MMC 26Mhz speed */
+#define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */
+#define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */
float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */
esp_err_t (*init)(void); /*!< Host function to initialize the driver */
esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */
sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
+ sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */
uint16_t rca; /*!< RCA (Relative Card Address) */
-#define SDMMC_HOST_MMC_CARD BIT(8) /*!< card in MMC mode (SD otherwise) */
-#define SDMMC_HOST_IO_CARD BIT(9) /*!< card in IO mode (SD moe only) */
-#define SDMMC_HOST_MEM_CARD BIT(10) /*!< card in memory mode (SD or MMC) */
+ uint16_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */
uint32_t is_mem : 1; /*!< Bit indicates if the card is a memory card */
uint32_t is_sdio : 1; /*!< Bit indicates if the card is an IO card */
+ uint32_t is_mmc : 1; /*!< Bit indicates if the card is MMC */
uint32_t num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */
- uint32_t reserved : 27; /*!< Reserved for future expansion */
+ uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
+ uint32_t reserved : 24; /*!< Reserved for future expansion */
} sdmmc_card_t;
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
- * Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 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
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <string.h>
-#include "esp_log.h"
-#include "esp_heap_caps.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "driver/sdmmc_defs.h"
-#include "driver/sdmmc_types.h"
-#include "sdmmc_cmd.h"
-#include "sys/param.h"
-#include "soc/soc_memory_layout.h"
-
-#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
- * 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
-
-/* 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 100
-#define SDMMC_SEND_OP_COND_MAX_ERRORS 3
+#include "sdmmc_common.h"
static const char* TAG = "sdmmc_cmd";
-static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
-static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
-static esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card);
-static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr);
-static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
-static esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp);
-static esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid);
-static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
-static esp_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid);
-static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
-static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd);
-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 mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
-static esp_err_t sd_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_mem_send_cxd_data(sdmmc_card_t* card , int opcode, void *data, size_t datalen);
-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_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value);
-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);
-static void flip_byte_order(uint32_t* response, size_t size);
-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)
-{
- return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0;
-}
-
-esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
-{
- 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 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). */
- 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_ERR_TIMEOUT || (is_spi && err == ESP_ERR_NOT_SUPPORTED)) {
- /* Non-IO cards are allowed to time out (in SD mode) or
- * return "invalid command" error (in SPI mode).
- */
- } else if (err == ESP_ERR_NOT_FOUND) {
- ESP_LOGD(TAG, "%s: card not present", __func__);
- return err;
- } else if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: sdio_reset: unexpected return: 0x%x", __func__, err );
- return err;
- }
-
- /* GO_IDLE_STATE (CMD0) command resets the 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;
- }
- vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
-
- /* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards.
- * SD v1 and non-SD cards will not respond to this command.
- */
- uint32_t host_ocr = get_host_ocr(config->io_voltage);
- err = sdmmc_send_cmd_send_if_cond(card, host_ocr);
- if (err == ESP_OK) {
- ESP_LOGD(TAG, "SDHC/SDXC card");
- host_ocr |= SD_OCR_SDHC_CAP;
- } else if (err == ESP_ERR_TIMEOUT) {
- ESP_LOGD(TAG, "CMD8 timeout; not an SD v2.00 card");
- } else if (is_spi && err == ESP_ERR_NOT_SUPPORTED) {
- ESP_LOGD(TAG, "CMD8 rejected; not an SD v2.00 card");
- } else {
- ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
- return err;
- }
-
- /* 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_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: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
- return err;
- }
- sdmmc_io_enable_int(card);
- }
-
- 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 time-out try switching from SD to MMC and vice-versa
- if (err == ESP_ERR_TIMEOUT){
- if (card->host.flags & SDMMC_HOST_MMC_CARD) {
- card->host.flags &= ~((uint32_t)(SDMMC_HOST_MMC_CARD));
- } else {
- card->host.flags |= SDMMC_HOST_MMC_CARD;
- }
- //retry SEND_OP_COND operation
- 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) {
- if (card->is_mem) {
- err = sdmmc_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) {
- ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
- return err;
- }
- } else {
- err = sdmmc_send_cmd_send_cid(card, &card->cid);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: send_cid returned 0x%x", __func__, err);
- return err;
- }
- }
- 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
- * SEND_SCR commands.
- */
- if (!is_spi) {
- err = sdmmc_send_cmd_select_card(card, card->rca);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
- return err;
- }
- }
-
- if (card->is_mem) {
- if (card->host.flags & SDMMC_HOST_MMC_CARD) { //MMC CARD
- /* sdmmc_mem_mmc_init */
- int width, value;
- int card_type;
- int speed = SDMMC_FREQ_DEFAULT;
- uint8_t powerclass = 0;
-
- //!!!remember to free(ext_csd) before all return-s in this block !!!
- //if passing this buffer to the host driver, it might need to be in DMA-capable memory
- uint8_t* ext_csd = heap_caps_malloc(EXT_CSD_MMC_SIZE,MALLOC_CAP_DMA);
- if(!ext_csd){
- ESP_LOGE(TAG, "%s: could not allocate ext_csd\n", __func__);
- free(ext_csd);
- return ESP_ERR_NO_MEM;
- }
-
- int timing = SDMMC_TIMING_LEGACY;
- uint32_t sectors = 0;
-
- if (card->csd.mmc_ver >= MMC_CSD_MMCVER_4_0) {
- /* read EXT_CSD */
- err = sdmmc_mem_send_cxd_data(card,
- MMC_SEND_EXT_CSD, ext_csd, EXT_CSD_MMC_SIZE);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: can't read EXT_CSD", __func__);
- free(ext_csd);
- return err;
- }
-
- card_type = ext_csd[EXT_CSD_CARD_TYPE];
-
-
- //NOTE: ESP32 doesn't support DDR
- if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
- speed = SDMCC_FREQ_52M;
- timing = SDMMC_TIMING_HIGHSPEED;
- } else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
- speed = SDMCC_FREQ_52M;
- timing = SDMMC_TIMING_HIGHSPEED;
- } else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
- speed = SDMCC_FREQ_26M;
- } else {
- ESP_LOGE(TAG, "%s: unknown CARD_TYPE 0x%x\n", __func__,
- ext_csd[EXT_CSD_CARD_TYPE]);
- }
-
- if (timing != SDMMC_TIMING_LEGACY) {
- /* switch to high speed timing */
- err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: can't change high speed\n",
- __func__);
- free(ext_csd);
- return err;
- }
- ets_delay_us(10000);
- }
-
- if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
- speed >= SDMMC_FREQ_HIGHSPEED) {
- ESP_LOGD(TAG, "switching to HS bus 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");
- free(ext_csd);
- return err;
- }
- } else if (config->max_freq_khz >= SDMMC_FREQ_DEFAULT &&
- speed >= SDMMC_FREQ_DEFAULT) {
- ESP_LOGD(TAG, "switching to DS bus mode");
- err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
- free(ext_csd);
- return err;
- }
- }
-
- if (timing != SDMMC_TIMING_LEGACY) {
- /* read EXT_CSD again */
- err = sdmmc_mem_send_cxd_data(card,
- MMC_SEND_EXT_CSD, ext_csd, EXT_CSD_MMC_SIZE);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: can't re-read EXT_CSD\n", __func__);
- free(ext_csd);
- return err;
- }
- if (ext_csd[EXT_CSD_HS_TIMING] != EXT_CSD_HS_TIMING_HS) {
- ESP_LOGE(TAG, "%s, HS_TIMING set failed\n", __func__);
- free(ext_csd);
- return ESP_ERR_INVALID_RESPONSE;
- }
- }
-
- if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
- width = 8;
- value = EXT_CSD_BUS_WIDTH_8;
- powerclass = ext_csd[(speed > SDMCC_FREQ_26M) ? EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] >> 4;
- } else if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
- width = 4;
- value = EXT_CSD_BUS_WIDTH_4;
- powerclass = ext_csd[(speed > SDMCC_FREQ_26M) ? EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] & 0x0f;
- } else {
- width = 1;
- value = EXT_CSD_BUS_WIDTH_1;
- powerclass = 0; //card must be able to do full rate at powerclass 0 in 1-bit mode
- }
- if (powerclass != 0) {
- err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_POWER_CLASS, powerclass);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: can't change power class"
- " (%d bit)\n", __func__, powerclass);
- free(ext_csd);
- return err;
- }
- }
- if (width != 1) {
- err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH, value);
- if (err == ESP_OK) {
- err = (*config->set_bus_width)(config->slot, width);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "slot->set_bus_width failed");
- free(ext_csd);
- return err;
- }
- } else {
- ESP_LOGE(TAG, "%s: can't change bus width"
- " (%d bit)\n", __func__, width);
- free(ext_csd);
- return err;
- }
-
- /* XXXX: need bus test? (using by CMD14 & CMD19) */
- ets_delay_us(10000);
- }
-
- sectors = ( ext_csd[EXT_CSD_SEC_COUNT + 0] << 0 )
- | ( ext_csd[EXT_CSD_SEC_COUNT + 1] << 8 )
- | ( ext_csd[EXT_CSD_SEC_COUNT + 2] << 16 )
- | ( ext_csd[EXT_CSD_SEC_COUNT + 3] << 24 );
-
- if (sectors > (2u * 1024 * 1024 * 1024) / 512) {
- //card->flags |= SFF_SDHC;
- card->csd.capacity = sectors;
- }
-
- free(ext_csd); //done with ext_csd
- }
-
- } else { //SD 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 (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: 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;
- }
- 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 = 0;
- 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;
- }
- }
- }
- }
-
-
- if ( !(card->host.flags & SDMMC_HOST_MMC_CARD) ) { //SD / SDIO
- /* 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 */) {
- 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) {
- return err;
- } 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 = (*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;
- }
- }
-
- /* All SD cards must support default speed mode (25MHz).
- * config->max_freq_khz may be used to limit the clock frequency.
- */
- if (!freq_switched &&
- config->max_freq_khz >= SDMMC_FREQ_DEFAULT) {
- ESP_LOGD(TAG, "switching to DS bus mode");
- err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
- return err;
- }
- freq_switched = true;
- }
- /* If frequency switch has been performed, read SCR register one more time
- * and compare the result with the previous one. Use this simple check as
- * an indicator of potential signal integrity issues.
- */
- if (freq_switched) {
- 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;
-}
-
-void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
-{
- fprintf(stream, "Name: %s\n", card->cid.name);
- fprintf(stream, "Type: %s\n", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC");
- fprintf(stream, "Speed: %s\n", (card->csd.tr_speed > 25000000)?"high speed":"default speed");
- fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
- fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n",
- card->csd.csd_ver,
- card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
- 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)
+esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
{
if (card->host.command_timeout_ms != 0) {
cmd->timeout_ms = card->host.command_timeout_ms;
return cmd->error;
}
-static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
+esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
{
sdmmc_command_t app_cmd = {
.opcode = MMC_APP_CMD,
}
-static esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card)
+esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card)
{
sdmmc_command_t cmd = {
.opcode = MMC_GO_IDLE_STATE,
cmd.flags |= SCF_RSP_R1;
err = sdmmc_send_cmd(card, &cmd);
}
+ if (err == ESP_OK) {
+ vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
+ }
return err;
}
-static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
+esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
{
const uint8_t pattern = 0xaa; /* any pattern will do here */
sdmmc_command_t cmd = {
return ESP_OK;
}
-static 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_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
{
esp_err_t err;
bzero(&cmd, sizeof cmd);
cmd.arg = ocr;
cmd.flags = SCF_CMD_BCR | SCF_RSP_R3;
- if (card->host.flags & SDMMC_HOST_MMC_CARD) { /* MMC mode */
+ if (!card->is_mmc) { /* SD mode */
+ cmd.opcode = SD_APP_OP_COND;
+ err = sdmmc_send_app_cmd(card, &cmd);
+ } else { /* MMC mode */
cmd.arg &= ~MMC_OCR_ACCESS_MODE_MASK;
cmd.arg |= MMC_OCR_SECTOR_MODE;
cmd.opcode = MMC_SEND_OP_COND;
err = sdmmc_send_cmd(card, &cmd);
- } else { /* SD mode */
- cmd.opcode = SD_APP_OP_COND;
- err = sdmmc_send_app_cmd(card, &cmd);
- }
+ }
if (err != ESP_OK) {
if (--err_cnt == 0) {
return ESP_OK;
}
-static esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp)
+esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp)
{
assert(ocrp);
sdmmc_command_t cmd = {
return ESP_OK;
}
-esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
-{
- out_cid->mfg_id = SD_CID_MID(resp);
- out_cid->oem_id = SD_CID_OID(resp);
- SD_CID_PNM_CPY(resp, out_cid->name);
- out_cid->revision = SD_CID_REV(resp);
- out_cid->serial = SD_CID_PSN(resp);
- out_cid->date = SD_CID_MDT(resp);
- return ESP_OK;
-}
-static esp_err_t sdmmc_send_cmd_all_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)
{
- assert(out_cid);
+ assert(out_raw_cid);
sdmmc_command_t cmd = {
.opcode = MMC_ALL_SEND_CID,
.flags = SCF_CMD_BCR | SCF_RSP_R2
if (err != ESP_OK) {
return err;
}
- return sdmmc_decode_cid(cmd.response, out_cid);
+ memcpy(out_raw_cid, &cmd.response, sizeof(sdmmc_response_t));
+ return ESP_OK;
}
-static esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid)
+esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid)
{
assert(out_cid);
assert(host_is_spi(card) && "SEND_CID should only be used in SPI mode");
+ assert(!card->is_mmc && "MMC cards are not supported in SPI mode");
sdmmc_response_t buf;
sdmmc_command_t cmd = {
.opcode = MMC_SEND_CID,
if (err != ESP_OK) {
return err;
}
- flip_byte_order(buf, sizeof(buf));
+ sdmmc_flip_byte_order(buf, sizeof(buf));
return sdmmc_decode_cid(buf, out_cid);
}
-static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
+esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
{
- static uint16_t next_rca_mmc = 0;
assert(out_rca);
sdmmc_command_t cmd = {
.opcode = SD_SEND_RELATIVE_ADDR,
.flags = SCF_CMD_BCR | SCF_RSP_R6
};
- if (card->host.flags & SDMMC_HOST_MMC_CARD) {
- // MMC cards expect you to set the RCA, so just keep a counter of them
- next_rca_mmc++;
- if (next_rca_mmc == 0) /* 0 means deselcted, so can't use that for an RCA */
- next_rca_mmc++;
- cmd.arg = MMC_ARG_RCA(next_rca_mmc);
+ /* MMC cards expect us to set the RCA.
+ * Set RCA to 1 since we don't support multiple cards on the same bus, for now.
+ */
+ uint16_t mmc_rca = 1;
+ if (card->is_mmc) {
+ cmd.arg = MMC_ARG_RCA(mmc_rca);
}
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
return err;
}
- *out_rca = (card->host.flags & SDMMC_HOST_MMC_CARD) ? next_rca_mmc : SD_R6_RCA(cmd.response);
+ *out_rca = (card->is_mmc) ? mmc_rca : SD_R6_RCA(cmd.response);
return ESP_OK;
}
-
-static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd)
+esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd)
{
sdmmc_command_t cmd = {
.opcode = MMC_SET_BLOCKLEN,
return sdmmc_send_cmd(card, &cmd);
}
-static esp_err_t mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
-{
- out_csd->csd_ver = MMC_CSD_CSDVER(response);
- if (out_csd->csd_ver == MMC_CSD_CSDVER_1_0 ||
- out_csd->csd_ver == MMC_CSD_CSDVER_2_0 ||
- out_csd->csd_ver == MMC_CSD_CSDVER_EXT_CSD) {
- out_csd->mmc_ver = MMC_CSD_MMCVER(response);
- out_csd->capacity = MMC_CSD_CAPACITY(response);
- out_csd->read_block_len = MMC_CSD_READ_BL_LEN(response);
- } else {
- ESP_LOGE(TAG, "unknown MMC CSD structure version 0x%x\n", out_csd->csd_ver);
- return 1;
- }
- int read_bl_size = 1 << out_csd->read_block_len;
- out_csd->sector_size = MIN(read_bl_size, 512);
- if (out_csd->sector_size < read_bl_size) {
- out_csd->capacity *= read_bl_size / out_csd->sector_size;
- }
- /* MMC special handling? */
- int speed = SD_CSD_SPEED(response);
- if (speed == SD_CSD_SPEED_50_MHZ) {
- out_csd->tr_speed = 50000000;
- } else {
- out_csd->tr_speed = 25000000;
- }
- return ESP_OK;
-}
-
-static esp_err_t sd_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
-{
- out_csd->csd_ver = SD_CSD_CSDVER(response);
- switch (out_csd->csd_ver) {
- case SD_CSD_CSDVER_2_0:
- out_csd->capacity = SD_CSD_V2_CAPACITY(response);
- out_csd->read_block_len = SD_CSD_V2_BL_LEN;
- break;
- case SD_CSD_CSDVER_1_0:
- out_csd->capacity = SD_CSD_CAPACITY(response);
- out_csd->read_block_len = SD_CSD_READ_BL_LEN(response);
- break;
- default:
- ESP_LOGE(TAG, "unknown SD CSD structure version 0x%x", out_csd->csd_ver);
- return ESP_ERR_NOT_SUPPORTED;
- }
- out_csd->card_command_class = SD_CSD_CCC(response);
- int read_bl_size = 1 << out_csd->read_block_len;
- out_csd->sector_size = MIN(read_bl_size, 512);
- if (out_csd->sector_size < read_bl_size) {
- out_csd->capacity *= read_bl_size / out_csd->sector_size;
- }
- int speed = SD_CSD_SPEED(response);
- if (speed == SD_CSD_SPEED_50_MHZ) {
- out_csd->tr_speed = 50000000;
- } else {
- out_csd->tr_speed = 25000000;
- }
- return ESP_OK;
-}
-
-static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd)
+esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd)
{
/* The trick with SEND_CSD is that in SPI mode, it acts as a data read
* command, while in SD mode it is an AC command with R2 response.
}
uint32_t* ptr = cmd.response;
if (is_spi) {
- flip_byte_order(spi_buf, sizeof(spi_buf));
+ sdmmc_flip_byte_order(spi_buf, sizeof(spi_buf));
ptr = spi_buf;
}
- if (card->host.flags & SDMMC_HOST_MMC_CARD) {/* MMC mode */
- err = mmc_decode_csd(cmd.response, out_csd);
- } else {/* SD mode */
- err = sd_decode_csd(ptr, out_csd);
- }
- return err;
-}
-
-static esp_err_t sdmmc_mem_send_cxd_data(sdmmc_card_t* card , int opcode, void *data, size_t datalen)
-{
- sdmmc_command_t cmd;
- void *ptr = NULL;
- esp_err_t error = ESP_OK;
-
- ptr = malloc(datalen);
- if (ptr == NULL) {
- error = ESP_ERR_NO_MEM;
+ if (card->is_mmc) {
+ err = sdmmc_mmc_decode_csd(cmd.response, out_csd);
} else {
- memset(&cmd, 0, sizeof(cmd));
- cmd.data = ptr;
- cmd.datalen = datalen;
- cmd.blklen = datalen;
- cmd.opcode = opcode;
- cmd.arg = 0;
- cmd.flags = SCF_CMD_ADTC | SCF_CMD_READ;
- if (opcode == MMC_SEND_EXT_CSD) {
- cmd.flags |= SCF_RSP_R1;
- } else {
- cmd.flags |= SCF_RSP_R2;
- }
- error = sdmmc_send_cmd(card, &cmd);
- if (error == 0) {
- memcpy(data, ptr, datalen);
- }
- if (ptr != NULL) {
- free(ptr);
- }
+ err = sdmmc_decode_csd(ptr, out_csd);
}
-
- return error;
+ return err;
}
-static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca)
+esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca)
{
/* Don't expect to see a response when de-selecting a card */
uint32_t response = (rca == 0) ? 0 : SCF_RSP_R1;
return sdmmc_send_cmd(card, &cmd);
}
-static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
-{
- sdmmc_response_t resp = {0xabababab, 0xabababab, 0x12345678, 0x09abcdef};
- resp[1] = __builtin_bswap32(raw_scr[0]);
- resp[0] = __builtin_bswap32(raw_scr[1]);
- int ver = SCR_STRUCTURE(resp);
- if (ver != 0) {
- return ESP_ERR_NOT_SUPPORTED;
- }
- out_scr->sd_spec = SCR_SD_SPEC(resp);
- out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp);
- return ESP_OK;
-}
-
-static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr)
+esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr)
{
size_t datalen = 8;
uint32_t* buf = (uint32_t*) heap_caps_malloc(datalen, MALLOC_CAP_DMA);
return err;
}
-static 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)
{
- uint8_t ignored[8];
sdmmc_command_t cmd = {
.opcode = SD_APP_SET_BUS_WIDTH,
.flags = SCF_RSP_R1 | SCF_CMD_AC,
.arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1,
- .data = ignored,
- .datalen = 8,
- .blklen = 4,
};
return sdmmc_send_app_cmd(card, &cmd);
}
-static esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value)
-{
- sdmmc_command_t cmd = {
- .opcode = MMC_SWITCH,
- .arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
- .flags = SCF_RSP_R1B | SCF_CMD_AC,
- };
- esp_err_t err = sdmmc_send_cmd(card, &cmd);
- if (err == ESP_OK) {
- //check response bit to see that switch was accepted
- if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR)
- err = ESP_ERR_INVALID_RESPONSE;
- }
-
- return err;
-}
-
-
-static 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)
{
assert(host_is_spi(card) && "CRC_ON_OFF can only be used in SPI mode");
sdmmc_command_t cmd = {
return sdmmc_send_cmd(card, &cmd);
}
-static 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 void flip_byte_order(uint32_t* response, size_t size)
-{
- assert(size % (2 * sizeof(uint32_t)) == 0);
- const size_t n_words = size / sizeof(uint32_t);
- for (int i = 0; i < n_words / 2; ++i) {
- uint32_t left = __builtin_bswap32(response[i]);
- uint32_t right = __builtin_bswap32(response[n_words - i - 1]);
- response[i] = right;
- response[n_words - i - 1] = left;
- }
-}
-
-static 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)
{
sdmmc_command_t cmd = {
.opcode = MMC_SEND_STATUS,
return err;
}
-static esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
+esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count)
{
if (start_block + block_count > card->csd.capacity) {
return err;
}
-static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
+esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count)
{
if (start_block + block_count > card->csd.capacity) {
return ESP_OK;
}
-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)
-{
- 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;
- }
-
- if (group == 0 ||
- group > SD_SFUNC_GROUP_MAX ||
- function > SD_SFUNC_FUNC_MAX) {
- return ESP_ERR_INVALID_ARG;
- }
-
- if (mode > 1) {
- return ESP_ERR_INVALID_ARG;
- }
-
- uint32_t group_shift = (group - 1) << 2;
- /* all functions which should not be affected are set to 0xf (no change) */
- uint32_t other_func_mask = (0x00ffffff & ~(0xf << group_shift));
- uint32_t func_val = (function << group_shift) | other_func_mask;
-
- sdmmc_command_t cmd = {
- .opcode = MMC_SWITCH,
- .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
- .blklen = sizeof(sdmmc_switch_func_rsp_t),
- .data = resp->data,
- .datalen = sizeof(sdmmc_switch_func_rsp_t),
- .arg = (!!mode << 31) | func_val
- };
-
- esp_err_t err = sdmmc_send_cmd(card, &cmd);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
- return err;
- }
- flip_byte_order(resp->data, sizeof(sdmmc_switch_func_rsp_t));
- uint32_t resp_ver = SD_SFUNC_VER(resp->data);
- if (resp_ver == 0) {
- /* busy response is never sent */
- } else if (resp_ver == 1) {
- if (SD_SFUNC_BUSY(resp->data, group) & (1 << function)) {
- ESP_LOGD(TAG, "%s: response indicates function %d:%d is busy",
- __func__, group, function);
- return ESP_ERR_INVALID_STATE;
- }
- } else {
- ESP_LOGD(TAG, "%s: got an invalid version of SWITCH_FUNC response: 0x%02x",
- __func__, resp_ver);
- return ESP_ERR_INVALID_RESPONSE;
- }
- return ESP_OK;
-}
-
-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;
- }
- sdmmc_switch_func_rsp_t* response = (sdmmc_switch_func_rsp_t*)
- heap_caps_malloc(sizeof(*response), MALLOC_CAP_DMA);
- if (response == NULL) {
- return ESP_ERR_NO_MEM;
- }
-
- esp_err_t err = sdmmc_send_cmd_switch_func(card, 0, SD_ACCESS_MODE, 0, response);
- if (err != ESP_OK) {
- ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, err);
- goto out;
- }
- uint32_t supported_mask = SD_SFUNC_SUPPORTED(response->data, 1);
- if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) {
- err = ESP_ERR_NOT_SUPPORTED;
- goto out;
- }
- err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, response);
- if (err != ESP_OK) {
- ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
- goto out;
- }
-
-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)
-{
- /* host quirk: SDIO transfer with length not divisible by 4 bytes
- * has to be split into two transfers: one with aligned length,
- * the other one for the remaining 1-3 bytes.
- */
- uint8_t *pc_dst = dst;
- while (size > 0) {
- size_t size_aligned = size & (~3);
- size_t will_transfer = size_aligned > 0 ? size_aligned : size;
-
- esp_err_t err = sdmmc_io_rw_extended(card, function, addr,
- SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT,
- pc_dst, will_transfer);
- if (err != ESP_OK) {
- return err;
- }
- pc_dst += will_transfer;
- size -= will_transfer;
- addr += will_transfer;
- }
- return ESP_OK;
-}
-
-esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
- uint32_t addr, const void* src, size_t size)
-{
- /* same host quirk as in sdmmc_io_read_bytes */
- const uint8_t *pc_src = (const uint8_t*) src;
-
- while (size > 0) {
- size_t size_aligned = size & (~3);
- size_t will_transfer = size_aligned > 0 ? size_aligned : size;
-
- esp_err_t err = sdmmc_io_rw_extended(card, function, addr,
- SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT,
- (void*) pc_src, will_transfer);
- if (err != ESP_OK) {
- return err;
- }
- pc_src += will_transfer;
- size -= will_transfer;
- addr += will_transfer;
- }
- return ESP_OK;
-}
-
-esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
- uint32_t addr, void* dst, size_t size)
-{
- if (size % 4 != 0) {
- return ESP_ERR_INVALID_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)
-{
- if (size % 4 != 0) {
- return ESP_ERR_INVALID_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);
-}
--- /dev/null
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016 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.
+ */
+
+#include "sdmmc_common.h"
+
+static const char* TAG = "sdmmc_common";
+
+esp_err_t sdmmc_init_ocr(sdmmc_card_t* card)
+{
+ esp_err_t 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.
+ */
+
+ uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
+ if ((card->ocr & SD_OCR_SDHC_CAP) != 0) {
+ host_ocr |= SD_OCR_SDHC_CAP;
+ }
+ /* 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 time-out, re-try send_op_cond as MMC */
+ if (err == ESP_ERR_TIMEOUT && !host_is_spi(card)) {
+ ESP_LOGD(TAG, "send_op_cond timeout, trying MMC");
+ card->is_mmc = 1;
+ 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 (host_is_spi(card)) {
+ 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);
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_cid(sdmmc_card_t* card)
+{
+ esp_err_t err;
+ sdmmc_csd_t csd;
+ sdmmc_response_t raw_cid;
+ if (!host_is_spi(card)) {
+ if (card->is_mem) {
+ err = sdmmc_send_cmd_all_send_cid(card, &raw_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) {
+ ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
+ return err;
+ }
+ if (card->is_mmc) {
+ /* For MMC, need to know CSD to decode CID.
+ * But CSD can only be read in data transfer mode,
+ * and it is not possible to read CID in data transfer mode.
+ * Luckily at this point the RCA is set and the card is in data
+ * transfer mode, so we can get its CSD to decode the CID...
+ */
+ err = sdmmc_send_cmd_send_csd(card, &csd);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
+ return err;
+ }
+ err = sdmmc_mmc_decode_cid(csd.mmc_ver, raw_cid, &card->cid);
+ } else {
+ err = sdmmc_decode_cid(raw_cid, &card->cid);
+ }
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
+ return err;
+ }
+ } else {
+ err = sdmmc_send_cmd_send_cid(card, &card->cid);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: send_cid returned 0x%x", __func__, err);
+ return err;
+ }
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_csd(sdmmc_card_t* card)
+{
+ assert(card->is_mem);
+ /* Get and decode the contents of CSD register. Determine card capacity. */
+ esp_err_t err = sdmmc_send_cmd_send_csd(card, &card->csd);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: send_csd 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;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_select_card(sdmmc_card_t* card)
+{
+ assert(!host_is_spi(card));
+ esp_err_t err = sdmmc_send_cmd_select_card(card, card->rca);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
+ return err;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card)
+{
+ esp_err_t err = ESP_ERR_NOT_SUPPORTED;
+ if (card->is_mem && !card->is_mmc) {
+ err = sdmmc_enable_hs_mode_and_check(card);
+ } else if (card->is_sdio) {
+ err = sdmmc_io_enable_hs_mode(card);
+ } else if (card->is_mmc){
+ err = sdmmc_mmc_enable_hs_mode(card);
+ }
+ if (err == ESP_ERR_NOT_SUPPORTED) {
+ ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
+ card->max_freq_khz = SDMMC_FREQ_DEFAULT;
+ } else if (err != ESP_OK) {
+ return err;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card)
+{
+ int bus_width = 1;
+
+ if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
+ (card->log_bus_width == 2)) {
+ bus_width = 4;
+ } else if ((card->host.flags & SDMMC_HOST_FLAG_8BIT) &&
+ (card->log_bus_width == 3)) {
+ bus_width = 8;
+ }
+ ESP_LOGD(TAG, "%s: using %d-bit bus", __func__, bus_width);
+ if (bus_width > 1) {
+ esp_err_t err = (*card->host.set_bus_width)(card->host.slot, bus_width);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "host.set_bus_width failed (0x%x)", err);
+ return err;
+ }
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
+{
+ assert(card->max_freq_khz <= card->host.max_freq_khz);
+
+ /* Find highest frequency in the following list,
+ * which is below card->max_freq_khz.
+ */
+ const uint32_t freq_values[] = {
+ SDMMC_FREQ_52M,
+ SDMMC_FREQ_HIGHSPEED,
+ SDMMC_FREQ_26M,
+ SDMMC_FREQ_DEFAULT
+ };
+ const int n_freq_values = sizeof(freq_values) / sizeof(freq_values[0]);
+
+ uint32_t selected_freq = SDMMC_FREQ_PROBING;
+ for (int i = 0; i < n_freq_values; ++i) {
+ uint32_t freq = freq_values[i];
+ if (card->max_freq_khz >= freq) {
+ selected_freq = freq;
+ break;
+ }
+ }
+
+ ESP_LOGD(TAG, "%s: using %d kHz bus frequency", __func__, selected_freq);
+ if (selected_freq > SDMMC_FREQ_PROBING) {
+ esp_err_t err = (*card->host.set_card_clk)(card->host.slot, selected_freq);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "failed to switch bus frequency (0x%x)", err);
+ return err;
+ }
+ }
+ return ESP_OK;
+}
+
+void sdmmc_flip_byte_order(uint32_t* response, size_t size)
+{
+ assert(size % (2 * sizeof(uint32_t)) == 0);
+ const size_t n_words = size / sizeof(uint32_t);
+ for (int i = 0; i < n_words / 2; ++i) {
+ uint32_t left = __builtin_bswap32(response[i]);
+ uint32_t right = __builtin_bswap32(response[n_words - i - 1]);
+ response[i] = right;
+ response[n_words - i - 1] = left;
+ }
+}
+
+void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
+{
+ bool print_scr = false;
+ bool print_csd = false;
+ const char* type;
+ fprintf(stream, "Name: %s\n", card->cid.name);
+ if (card->is_sdio) {
+ type = "SDIO";
+ print_scr = true;
+ print_csd = true;
+ } else if (card->is_mmc) {
+ type = "MMC";
+ print_csd = true;
+ } else {
+ type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC";
+ }
+ fprintf(stream, "Type: %s\n", type);
+ fprintf(stream, "Speed: %s\n", (card->max_freq_khz > SDMMC_FREQ_26M) ? "high speed" : "default speed");
+ fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
+
+ if (print_csd) {
+ fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n",
+ card->csd.csd_ver,
+ card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
+ }
+ if (print_scr) {
+ fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
+ }
+}
+
+esp_err_t 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;
+ }
+ return ESP_OK;
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 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 "esp_log.h"
+#include "esp_heap_caps.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/sdmmc_defs.h"
+#include "driver/sdmmc_types.h"
+#include "sdmmc_cmd.h"
+#include "sys/param.h"
+#include "soc/soc_memory_layout.h"
+
+#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
+ * 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
+
+/* 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 100
+#define SDMMC_SEND_OP_COND_MAX_ERRORS 3
+
+/* 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);
+
+/* Higher level functions */
+esp_err_t sdmmc_enable_hs_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);
+esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
+ size_t start_block, size_t block_count);
+
+/* 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);
+
+/* 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);
+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);
+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);
+
+/* 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_ocr(sdmmc_card_t* card);
+esp_err_t sdmmc_init_spi_crc(sdmmc_card_t* card);
+esp_err_t sdmmc_init_io(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_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);
+esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card);
+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);
+
+/* 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;
+}
+
+void sdmmc_flip_byte_order(uint32_t* response, size_t size);
+
+esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card);
--- /dev/null
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 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.
+ */
+
+#include "sdmmc_common.h"
+
+static const char* TAG = "sdmmc_init";
+
+#define SDMMC_INIT_STEP(condition, function) \
+ do { \
+ if ((condition)) { \
+ esp_err_t err = (function)(card); \
+ if (err != ESP_OK) { \
+ ESP_LOGD(TAG, "%s: %s returned 0x%x", __func__, #function, err); \
+ return err; \
+ } \
+ } \
+ } while(0);
+
+
+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;
+
+ /* Check if host flags are compatible with slot configuration. */
+ SDMMC_INIT_STEP(!is_spi, sdmmc_fix_host_flags);
+
+ /* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5). */
+ SDMMC_INIT_STEP(io_supported, sdmmc_io_reset);
+
+ /* GO_IDLE_STATE (CMD0) command resets the card */
+ SDMMC_INIT_STEP(always, sdmmc_send_cmd_go_idle_state);
+
+ /* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards. */
+ SDMMC_INIT_STEP(always, sdmmc_init_sd_if_cond);
+
+ /* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card. */
+ SDMMC_INIT_STEP(io_supported, sdmmc_init_io);
+
+ const bool is_mem = card->is_mem;
+ const bool is_sdio = !is_mem;
+
+ /* Enable CRC16 checks for data transfers in SPI mode */
+ SDMMC_INIT_STEP(is_spi, sdmmc_init_spi_crc);
+
+ /* Use SEND_OP_COND to set up card OCR */
+ SDMMC_INIT_STEP(is_mem, sdmmc_init_ocr);
+
+ const bool is_mmc = is_mem && card->is_mmc;
+ const bool is_sdmem = is_mem && !is_mmc;
+
+ ESP_LOGD(TAG, "%s: card type is %s", __func__,
+ is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
+
+ /* Read and decode the contents of CID register and assign RCA */
+ SDMMC_INIT_STEP(always, sdmmc_init_cid);
+
+ /* Read and decode the contents of CSD register */
+ SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);
+
+ /* 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
+ * SEND_SCR commands.
+ */
+ SDMMC_INIT_STEP(!is_spi, sdmmc_init_select_card);
+
+ /* SD memory cards:
+ * Set block len for SDSC cards to 512 bytes (same as SDHC)
+ * Read SCR
+ * Wait to enter data transfer state
+ */
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_blocklen);
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_scr);
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_wait_data_ready);
+
+ /* MMC cards: read CXD */
+ SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
+
+ /* Set bus width. One call for every kind of card, then one for the host */
+ if (!is_spi) {
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
+ SDMMC_INIT_STEP(is_sdio, sdmmc_init_io_bus_width);
+ SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_bus_width);
+ SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
+ }
+
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
+
+ /* Try to switch card to HS mode if the card supports it.
+ * Set card->max_freq_khz value accordingly.
+ */
+ SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);
+
+ /* So far initialization has been done at probing frequency.
+ * Switch to the host to use card->max_freq_khz frequency.
+ */
+ SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
+
+ /* Sanity check after switching the frequency */
+ SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
+ /* TODO: add similar checks for eMMC and SDIO */
+
+ return ESP_OK;
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 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.
+ */
+
+#include "sdmmc_common.h"
+
+static const char* TAG = "sdmmc_io";
+
+esp_err_t sdmmc_io_reset(sdmmc_card_t* card)
+{
+ uint8_t sdio_reset = CCCR_CTL_RES;
+ esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CTL, SD_ARG_CMD52_WRITE, &sdio_reset);
+ if (err == ESP_ERR_TIMEOUT || (host_is_spi(card) && err == ESP_ERR_NOT_SUPPORTED)) {
+ /* Non-IO cards are allowed to time out (in SD mode) or
+ * return "invalid command" error (in SPI mode).
+ */
+ } else if (err == ESP_ERR_NOT_FOUND) {
+ ESP_LOGD(TAG, "%s: card not present", __func__);
+ return err;
+ } else if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: unexpected return: 0x%x", __func__, err );
+ return err;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_io(sdmmc_card_t* card)
+{
+ /* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
+ * Non-IO cards will not respond to this command.
+ */
+ esp_err_t err = sdmmc_io_send_op_cond(card, 0, &card->ocr);
+ if (err != ESP_OK) {
+ 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;
+ }
+ uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
+ host_ocr &= card->ocr;
+ err = sdmmc_io_send_op_cond(card, host_ocr, &card->ocr);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
+ return err;
+ }
+ err = sdmmc_io_enable_int(card);
+ if (err != ESP_OK) {
+ ESP_LOGD(TAG, "%s: sdmmc_enable_int failed (0x%x)", __func__, err);
+ }
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card)
+{
+ esp_err_t err;
+ card->log_bus_width = 0;
+ if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
+ uint8_t card_cap = 0;
+ 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;
+ }
+ card->log_bus_width = 2;
+ }
+ }
+ return ESP_OK;
+}
+
+
+esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
+{
+ card->max_freq_khz = SDMMC_FREQ_DEFAULT;
+ if (card->host.max_freq_khz <= card->max_freq_khz) {
+ /* Host is configured to use low frequency, don't attempt to switch */
+ card->max_freq_khz = card->host.max_freq_khz;
+ return ESP_OK;
+ }
+
+ /* 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;
+ }
+ card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
+ return ESP_OK;
+}
+
+
+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;
+}
+
+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;
+}
+
+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)
+{
+ /* host quirk: SDIO transfer with length not divisible by 4 bytes
+ * has to be split into two transfers: one with aligned length,
+ * the other one for the remaining 1-3 bytes.
+ */
+ uint8_t *pc_dst = dst;
+ while (size > 0) {
+ size_t size_aligned = size & (~3);
+ size_t will_transfer = size_aligned > 0 ? size_aligned : size;
+
+ esp_err_t err = sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT,
+ pc_dst, will_transfer);
+ if (err != ESP_OK) {
+ return err;
+ }
+ pc_dst += will_transfer;
+ size -= will_transfer;
+ addr += will_transfer;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, const void* src, size_t size)
+{
+ /* same host quirk as in sdmmc_io_read_bytes */
+ const uint8_t *pc_src = (const uint8_t*) src;
+
+ while (size > 0) {
+ size_t size_aligned = size & (~3);
+ size_t will_transfer = size_aligned > 0 ? size_aligned : size;
+
+ esp_err_t err = sdmmc_io_rw_extended(card, function, addr,
+ SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT,
+ (void*) pc_src, will_transfer);
+ if (err != ESP_OK) {
+ return err;
+ }
+ pc_src += will_transfer;
+ size -= will_transfer;
+ addr += will_transfer;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
+ uint32_t addr, void* dst, size_t size)
+{
+ if (size % 4 != 0) {
+ return ESP_ERR_INVALID_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)
+{
+ if (size % 4 != 0) {
+ return ESP_ERR_INVALID_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);
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 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.
+ */
+
+#include <unistd.h>
+#include "sdmmc_common.h"
+
+static const char* TAG = "sdmmc_mmc";
+
+
+esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
+{
+ int card_type;
+ esp_err_t err = ESP_OK;
+
+ uint8_t* ext_csd = heap_caps_malloc(EXT_CSD_MMC_SIZE, MALLOC_CAP_DMA);
+ if (!ext_csd) {
+ ESP_LOGE(TAG, "%s: could not allocate ext_csd", __func__);
+ return ESP_ERR_NO_MEM;
+ }
+
+ uint32_t sectors = 0;
+
+ ESP_LOGD(TAG, "MMC version: %d", card->csd.mmc_ver);
+ if (card->csd.mmc_ver < MMC_CSD_MMCVER_4_0) {
+ err = ESP_ERR_NOT_SUPPORTED;
+ goto out;
+ }
+
+ /* read EXT_CSD */
+ err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: send_ext_csd_data error 0x%x", __func__, err);
+ goto out;
+ }
+ card_type = ext_csd[EXT_CSD_CARD_TYPE];
+
+ /* TODO: add DDR support */
+ if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
+ card->max_freq_khz = SDMMC_FREQ_52M;
+ } else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
+ card->max_freq_khz = SDMMC_FREQ_52M;
+ } else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
+ card->max_freq_khz = SDMMC_FREQ_26M;
+ } else {
+ ESP_LOGW(TAG, "%s: unknown CARD_TYPE 0x%x", __func__, card_type);
+ }
+ /* For MMC cards, use speed value from EXT_CSD */
+ card->csd.tr_speed = card->max_freq_khz * 1000;
+ ESP_LOGD(TAG, "MMC card supports %d khz bus frequency", card->max_freq_khz);
+ card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz);
+
+ if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
+ card->ext_csd.power_class = ext_csd[(card->max_freq_khz > SDMMC_FREQ_26M) ?
+ EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] >> 4;
+ card->log_bus_width = 3;
+ } else if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
+ card->ext_csd.power_class = ext_csd[(card->max_freq_khz > SDMMC_FREQ_26M) ?
+ EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] & 0x0f;
+ card->log_bus_width = 2;
+ } else {
+ card->ext_csd.power_class = 0; //card must be able to do full rate at powerclass 0 in 1-bit mode
+ card->log_bus_width = 0;
+ }
+
+ sectors = ( ext_csd[EXT_CSD_SEC_COUNT + 0] << 0 )
+ | ( ext_csd[EXT_CSD_SEC_COUNT + 1] << 8 )
+ | ( ext_csd[EXT_CSD_SEC_COUNT + 2] << 16 )
+ | ( ext_csd[EXT_CSD_SEC_COUNT + 3] << 24 );
+
+ if (sectors > (2u * 1024 * 1024 * 1024) / 512) {
+ card->csd.capacity = sectors;
+ }
+
+out:
+ free(ext_csd);
+ return err;
+}
+
+esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card)
+{
+ esp_err_t err;
+ if (card->ext_csd.power_class != 0) {
+ err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_CLASS, card->ext_csd.power_class);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: can't change power class (%d bit), 0x%x"
+ , __func__, card->ext_csd.power_class, err);
+ return err;
+ }
+ }
+
+ if (card->log_bus_width > 0) {
+ int csd_bus_width_value = 0;
+ int bus_width = 1;
+ if (card->log_bus_width == 2) {
+ csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
+ bus_width = 4;
+ } else if (card->log_bus_width == 3) {
+ csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
+ bus_width = 8;
+ }
+ err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH, csd_bus_width_value);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: can't change bus width (%d bit), 0x%x",
+ __func__, bus_width, err);
+ return err;
+ }
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card)
+{
+ esp_err_t err;
+ if (card->max_freq_khz > SDMMC_FREQ_26M) {
+ /* switch to high speed timing */
+ err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: mmc_switch EXT_CSD_HS_TIMING_HS error 0x%x",
+ __func__, err);
+ return err;
+ }
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid)
+{
+ if (mmc_ver == MMC_CSD_MMCVER_1_0 ||
+ mmc_ver == MMC_CSD_MMCVER_1_4) {
+ out_cid->mfg_id = MMC_CID_MID_V1(resp);
+ out_cid->oem_id = 0;
+ MMC_CID_PNM_V1_CPY(resp, out_cid->name);
+ out_cid->revision = MMC_CID_REV_V1(resp);
+ out_cid->serial = MMC_CID_PSN_V1(resp);
+ out_cid->date = MMC_CID_MDT_V1(resp);
+ } else if (mmc_ver == MMC_CSD_MMCVER_2_0 ||
+ mmc_ver == MMC_CSD_MMCVER_3_1 ||
+ mmc_ver == MMC_CSD_MMCVER_4_0) {
+ out_cid->mfg_id = MMC_CID_MID_V2(resp);
+ out_cid->oem_id = MMC_CID_OID_V2(resp);
+ MMC_CID_PNM_V1_CPY(resp, out_cid->name);
+ out_cid->revision = 0;
+ out_cid->serial = MMC_CID_PSN_V1(resp);
+ out_cid->date = 0;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
+{
+ out_csd->csd_ver = MMC_CSD_CSDVER(response);
+ if (out_csd->csd_ver == MMC_CSD_CSDVER_1_0 ||
+ out_csd->csd_ver == MMC_CSD_CSDVER_2_0 ||
+ out_csd->csd_ver == MMC_CSD_CSDVER_EXT_CSD) {
+ out_csd->mmc_ver = MMC_CSD_MMCVER(response);
+ out_csd->capacity = MMC_CSD_CAPACITY(response);
+ out_csd->read_block_len = MMC_CSD_READ_BL_LEN(response);
+ } else {
+ ESP_LOGE(TAG, "unknown MMC CSD structure version 0x%x\n", out_csd->csd_ver);
+ return 1;
+ }
+ int read_bl_size = 1 << out_csd->read_block_len;
+ out_csd->sector_size = MIN(read_bl_size, 512);
+ if (out_csd->sector_size < read_bl_size) {
+ out_csd->capacity *= read_bl_size / out_csd->sector_size;
+ }
+ /* tr_speed will be determined when reading CXD */
+ out_csd->tr_speed = 0;
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen)
+{
+ assert(esp_ptr_dma_capable(out_data));
+ sdmmc_command_t cmd = {
+ .data = out_data,
+ .datalen = datalen,
+ .blklen = datalen,
+ .opcode = MMC_SEND_EXT_CSD,
+ .arg = 0,
+ .flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
+ };
+ return sdmmc_send_cmd(card, &cmd);
+}
+
+esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value)
+{
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SWITCH,
+ .arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
+ .flags = SCF_RSP_R1B | SCF_CMD_AC,
+ };
+ esp_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err == ESP_OK) {
+ //check response bit to see that switch was accepted
+ if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR)
+ err = ESP_ERR_INVALID_RESPONSE;
+ }
+
+ return err;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Adaptations to ESP-IDF Copyright (c) 2016-2018 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.
+ */
+
+#include "sdmmc_common.h"
+
+static const char* TAG = "sdmmc_sd";
+
+esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card)
+{
+ /* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards.
+ * SD v1 and non-SD cards will not respond to this command.
+ */
+ uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
+ esp_err_t err = sdmmc_send_cmd_send_if_cond(card, host_ocr);
+ if (err == ESP_OK) {
+ ESP_LOGD(TAG, "SDHC/SDXC card");
+ host_ocr |= SD_OCR_SDHC_CAP;
+ } else if (err == ESP_ERR_TIMEOUT) {
+ ESP_LOGD(TAG, "CMD8 timeout; not an SD v2.00 card");
+ } else if (host_is_spi(card) && err == ESP_ERR_NOT_SUPPORTED) {
+ ESP_LOGD(TAG, "CMD8 rejected; not an SD v2.00 card");
+ } else {
+ ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
+ return err;
+ }
+ card->ocr = host_ocr;
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_sd_blocklen(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) {
+ esp_err_t 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;
+ }
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_sd_scr(sdmmc_card_t* card)
+{
+ esp_err_t 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 ((card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)
+ && (card->host.flags & SDMMC_HOST_FLAG_4BIT)) {
+ card->log_bus_width = 2;
+ } else {
+ card->log_bus_width = 0;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card)
+{
+ int width = 1;
+ if (card->log_bus_width == 2) {
+ width = 4;
+ } else if (card->log_bus_width == 3) {
+ width = 8;
+ }
+ esp_err_t err = sdmmc_send_cmd_set_bus_width(card, width);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "set_bus_width failed (0x%x)", err);
+ return err;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card)
+{
+ /* Wait for the card to be ready for data transfers */
+ uint32_t status = 0;
+ uint32_t count = 0;
+ while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
+ // TODO: add some timeout here
+ esp_err_t 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);
+ }
+ }
+ return ESP_OK;
+}
+
+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)
+{
+ 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;
+ }
+
+ if (group == 0 ||
+ group > SD_SFUNC_GROUP_MAX ||
+ function > SD_SFUNC_FUNC_MAX) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (mode > 1) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ uint32_t group_shift = (group - 1) << 2;
+ /* all functions which should not be affected are set to 0xf (no change) */
+ uint32_t other_func_mask = (0x00ffffff & ~(0xf << group_shift));
+ uint32_t func_val = (function << group_shift) | other_func_mask;
+
+ sdmmc_command_t cmd = {
+ .opcode = MMC_SWITCH,
+ .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
+ .blklen = sizeof(sdmmc_switch_func_rsp_t),
+ .data = resp->data,
+ .datalen = sizeof(sdmmc_switch_func_rsp_t),
+ .arg = (!!mode << 31) | func_val
+ };
+
+ esp_err_t err = sdmmc_send_cmd(card, &cmd);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
+ return err;
+ }
+ sdmmc_flip_byte_order(resp->data, sizeof(sdmmc_switch_func_rsp_t));
+ uint32_t resp_ver = SD_SFUNC_VER(resp->data);
+ if (resp_ver == 0) {
+ /* busy response is never sent */
+ } else if (resp_ver == 1) {
+ if (SD_SFUNC_BUSY(resp->data, group) & (1 << function)) {
+ ESP_LOGD(TAG, "%s: response indicates function %d:%d is busy",
+ __func__, group, function);
+ return ESP_ERR_INVALID_STATE;
+ }
+ } else {
+ ESP_LOGD(TAG, "%s: got an invalid version of SWITCH_FUNC response: 0x%02x",
+ __func__, resp_ver);
+ return ESP_ERR_INVALID_RESPONSE;
+ }
+ return ESP_OK;
+}
+
+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;
+ }
+ sdmmc_switch_func_rsp_t* response = (sdmmc_switch_func_rsp_t*)
+ heap_caps_malloc(sizeof(*response), MALLOC_CAP_DMA);
+ if (response == NULL) {
+ return ESP_ERR_NO_MEM;
+ }
+
+ esp_err_t err = sdmmc_send_cmd_switch_func(card, 0, SD_ACCESS_MODE, 0, response);
+ if (err != ESP_OK) {
+ ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, err);
+ goto out;
+ }
+ uint32_t supported_mask = SD_SFUNC_SUPPORTED(response->data, 1);
+ if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) {
+ err = ESP_ERR_NOT_SUPPORTED;
+ goto out;
+ }
+ err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, response);
+ if (err != ESP_OK) {
+ ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
+ goto out;
+ }
+
+out:
+ free(response);
+ return err;
+}
+
+esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
+{
+ /* All cards should support at least default speed */
+ card->max_freq_khz = SDMMC_FREQ_DEFAULT;
+ if (card->host.max_freq_khz <= card->max_freq_khz) {
+ /* Host is configured to use low frequency, don't attempt to switch */
+ card->max_freq_khz = card->host.max_freq_khz;
+ return ESP_OK;
+ }
+
+ /* 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 (1) 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 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 (2) 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;
+ }
+
+ card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_check_scr(sdmmc_card_t* card)
+{
+ /* If frequency switch has been performed, read SCR register one more time
+ * and compare the result with the previous one. Use this simple check as
+ * an indicator of potential signal integrity issues.
+ */
+ sdmmc_scr_t scr_tmp;
+ esp_err_t err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "%s: send_scr 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;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_init_spi_crc(sdmmc_card_t* card)
+{
+ /* 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.
+ */
+ assert(host_is_spi(card));
+ esp_err_t 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;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
+{
+ out_cid->mfg_id = SD_CID_MID(resp);
+ out_cid->oem_id = SD_CID_OID(resp);
+ SD_CID_PNM_CPY(resp, out_cid->name);
+ out_cid->revision = SD_CID_REV(resp);
+ out_cid->serial = SD_CID_PSN(resp);
+ out_cid->date = SD_CID_MDT(resp);
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
+{
+ out_csd->csd_ver = SD_CSD_CSDVER(response);
+ switch (out_csd->csd_ver) {
+ case SD_CSD_CSDVER_2_0:
+ out_csd->capacity = SD_CSD_V2_CAPACITY(response);
+ out_csd->read_block_len = SD_CSD_V2_BL_LEN;
+ break;
+ case SD_CSD_CSDVER_1_0:
+ out_csd->capacity = SD_CSD_CAPACITY(response);
+ out_csd->read_block_len = SD_CSD_READ_BL_LEN(response);
+ break;
+ default:
+ ESP_LOGE(TAG, "unknown SD CSD structure version 0x%x", out_csd->csd_ver);
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+ out_csd->card_command_class = SD_CSD_CCC(response);
+ int read_bl_size = 1 << out_csd->read_block_len;
+ out_csd->sector_size = MIN(read_bl_size, 512);
+ if (out_csd->sector_size < read_bl_size) {
+ out_csd->capacity *= read_bl_size / out_csd->sector_size;
+ }
+ int speed = SD_CSD_SPEED(response);
+ if (speed == SD_CSD_SPEED_50_MHZ) {
+ out_csd->tr_speed = 50000000;
+ } else {
+ out_csd->tr_speed = 25000000;
+ }
+ return ESP_OK;
+}
+
+esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
+{
+ sdmmc_response_t resp = { 0 };
+ resp[1] = __builtin_bswap32(raw_scr[0]);
+ resp[0] = __builtin_bswap32(raw_scr[1]);
+ int ver = SCR_STRUCTURE(resp);
+ if (ver != 0) {
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+ out_scr->sd_spec = SCR_SD_SPEC(resp);
+ out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp);
+ return ESP_OK;
+}
+