sdmmc: implement partial DDR support
Works for 3.3V eMMC in 4 line mode. Not implemented: - DDR mode for SD cards (UHS-I) also need voltage to be switched to 1.8V. - 8-line DDR mode for eMMC to be implemented later.
This commit is contained in:
parent
de42d99b1d
commit
78fab8a0f9
|
@ -161,10 +161,10 @@
|
||||||
/* EXT_CSD_CARD_TYPE */
|
/* EXT_CSD_CARD_TYPE */
|
||||||
/* The only currently valid values for this field are 0x01, 0x03, 0x07,
|
/* The only currently valid values for this field are 0x01, 0x03, 0x07,
|
||||||
* 0x0B and 0x0F. */
|
* 0x0B and 0x0F. */
|
||||||
#define EXT_CSD_CARD_TYPE_F_26M (1 << 0)
|
#define EXT_CSD_CARD_TYPE_F_26M (1 << 0) /* SDR at "rated voltages */
|
||||||
#define EXT_CSD_CARD_TYPE_F_52M (1 << 1)
|
#define EXT_CSD_CARD_TYPE_F_52M (1 << 1) /* SDR at "rated voltages */
|
||||||
#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2)
|
#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2) /* DDR, 1.8V or 3.3V I/O */
|
||||||
#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3)
|
#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3) /* DDR, 1.2V I/O */
|
||||||
#define EXT_CSD_CARD_TYPE_26M 0x01
|
#define EXT_CSD_CARD_TYPE_26M 0x01
|
||||||
#define EXT_CSD_CARD_TYPE_52M 0x03
|
#define EXT_CSD_CARD_TYPE_52M 0x03
|
||||||
#define EXT_CSD_CARD_TYPE_52M_V18 0x07
|
#define EXT_CSD_CARD_TYPE_52M_V18 0x07
|
||||||
|
|
|
@ -33,13 +33,17 @@ extern "C" {
|
||||||
* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
|
* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
|
||||||
*/
|
*/
|
||||||
#define SDMMC_HOST_DEFAULT() {\
|
#define SDMMC_HOST_DEFAULT() {\
|
||||||
.flags = SDMMC_HOST_FLAG_4BIT, \
|
.flags = SDMMC_HOST_FLAG_8BIT | \
|
||||||
|
SDMMC_HOST_FLAG_4BIT | \
|
||||||
|
SDMMC_HOST_FLAG_1BIT | \
|
||||||
|
SDMMC_HOST_FLAG_DDR, \
|
||||||
.slot = SDMMC_HOST_SLOT_1, \
|
.slot = SDMMC_HOST_SLOT_1, \
|
||||||
.max_freq_khz = SDMMC_FREQ_DEFAULT, \
|
.max_freq_khz = SDMMC_FREQ_DEFAULT, \
|
||||||
.io_voltage = 3.3f, \
|
.io_voltage = 3.3f, \
|
||||||
.init = &sdmmc_host_init, \
|
.init = &sdmmc_host_init, \
|
||||||
.set_bus_width = &sdmmc_host_set_bus_width, \
|
.set_bus_width = &sdmmc_host_set_bus_width, \
|
||||||
.get_bus_width = &sdmmc_host_get_slot_width, \
|
.get_bus_width = &sdmmc_host_get_slot_width, \
|
||||||
|
.set_bus_ddr_mode = &sdmmc_host_set_bus_ddr_mode, \
|
||||||
.set_card_clk = &sdmmc_host_set_card_clk, \
|
.set_card_clk = &sdmmc_host_set_card_clk, \
|
||||||
.do_transaction = &sdmmc_host_do_transaction, \
|
.do_transaction = &sdmmc_host_do_transaction, \
|
||||||
.deinit = &sdmmc_host_deinit, \
|
.deinit = &sdmmc_host_deinit, \
|
||||||
|
@ -150,6 +154,16 @@ size_t sdmmc_host_get_slot_width(int slot);
|
||||||
*/
|
*/
|
||||||
esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz);
|
esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable or disable DDR mode of SD interface
|
||||||
|
* @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
|
||||||
|
* @param ddr_enabled enable or disable DDR mode
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED if DDR mode is not supported on this slot
|
||||||
|
*/
|
||||||
|
esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send command to the card and get response
|
* @brief Send command to the card and get response
|
||||||
*
|
*
|
||||||
|
|
|
@ -110,6 +110,8 @@ typedef struct {
|
||||||
#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||||
#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||||
#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||||
|
/* special flags */
|
||||||
|
#define SCF_WAIT_BUSY 0x2000 /*!< Wait for completion of card busy signal before returning */
|
||||||
/** @endcond */
|
/** @endcond */
|
||||||
esp_err_t error; /*!< error returned from transfer */
|
esp_err_t error; /*!< error returned from transfer */
|
||||||
int timeout_ms; /*!< response timeout, in milliseconds */
|
int timeout_ms; /*!< response timeout, in milliseconds */
|
||||||
|
@ -127,6 +129,7 @@ typedef struct {
|
||||||
#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */
|
#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */
|
||||||
#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
|
#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
|
||||||
#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
|
#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
|
||||||
|
#define SDMMC_HOST_FLAG_DDR BIT(4) /*!< host supports DDR mode for SD/MMC */
|
||||||
int slot; /*!< slot number, to be passed to host functions */
|
int slot; /*!< slot number, to be passed to host functions */
|
||||||
int max_freq_khz; /*!< max frequency supported by the host */
|
int max_freq_khz; /*!< max frequency supported by the host */
|
||||||
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
|
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
|
||||||
|
@ -138,6 +141,7 @@ typedef struct {
|
||||||
esp_err_t (*init)(void); /*!< Host function to initialize the driver */
|
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 */
|
esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */
|
||||||
size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
|
size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
|
||||||
|
esp_err_t (*set_bus_ddr_mode)(int slot, bool ddr_enable); /*!< host function to set DDR mode */
|
||||||
esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
|
esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
|
||||||
esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */
|
esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */
|
||||||
esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */
|
esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */
|
||||||
|
@ -163,7 +167,8 @@ typedef struct {
|
||||||
uint32_t is_mmc : 1; /*!< Bit indicates if the card is MMC */
|
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 num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */
|
||||||
uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
|
uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
|
||||||
uint32_t reserved : 24; /*!< Reserved for future expansion */
|
uint32_t is_ddr : 1; /*!< Card supports DDR mode */
|
||||||
|
uint32_t reserved : 23; /*!< Reserved for future expansion */
|
||||||
} sdmmc_card_t;
|
} sdmmc_card_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ extern "C" {
|
||||||
.init = &sdspi_host_init, \
|
.init = &sdspi_host_init, \
|
||||||
.set_bus_width = NULL, \
|
.set_bus_width = NULL, \
|
||||||
.get_bus_width = NULL, \
|
.get_bus_width = NULL, \
|
||||||
|
.set_bus_ddr_mode = NULL, \
|
||||||
.set_card_clk = &sdspi_host_set_card_clk, \
|
.set_card_clk = &sdspi_host_set_card_clk, \
|
||||||
.do_transaction = &sdspi_host_do_transaction, \
|
.do_transaction = &sdspi_host_do_transaction, \
|
||||||
.deinit = &sdspi_host_deinit, \
|
.deinit = &sdspi_host_deinit, \
|
||||||
|
|
|
@ -267,6 +267,9 @@ esp_err_t sdmmc_host_init()
|
||||||
SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE; //sdio is enabled only when use.
|
SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE; //sdio is enabled only when use.
|
||||||
SDMMC.ctrl.int_enable = 1;
|
SDMMC.ctrl.int_enable = 1;
|
||||||
|
|
||||||
|
// Disable generation of Busy Clear Interrupt
|
||||||
|
SDMMC.cardthrctl.busy_clr_int_en = 0;
|
||||||
|
|
||||||
// Enable DMA
|
// Enable DMA
|
||||||
sdmmc_host_dma_init();
|
sdmmc_host_dma_init();
|
||||||
|
|
||||||
|
@ -465,6 +468,28 @@ size_t sdmmc_host_get_slot_width(int slot)
|
||||||
return s_slot_width[slot];
|
return s_slot_width[slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled)
|
||||||
|
{
|
||||||
|
if (!(slot == 0 || slot == 1)) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (s_slot_width[slot] == 8 && ddr_enabled) {
|
||||||
|
ESP_LOGW(TAG, "DDR mode with 8-bit bus width is not supported yet");
|
||||||
|
// requires reconfiguring controller clock for 2x card frequency
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
uint32_t mask = BIT(slot);
|
||||||
|
if (ddr_enabled) {
|
||||||
|
SDMMC.uhs.ddr |= mask;
|
||||||
|
SDMMC.emmc_ddr_reg |= mask;
|
||||||
|
} else {
|
||||||
|
SDMMC.uhs.ddr &= ~mask;
|
||||||
|
SDMMC.emmc_ddr_reg &= ~mask;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "slot=%d ddr=%d", slot, ddr_enabled ? 1 : 0);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static void sdmmc_host_dma_init()
|
static void sdmmc_host_dma_init()
|
||||||
{
|
{
|
||||||
SDMMC.ctrl.dma_enable = 1;
|
SDMMC.ctrl.dma_enable = 1;
|
||||||
|
@ -504,6 +529,11 @@ void sdmmc_host_dma_resume()
|
||||||
SDMMC.pldmnd = 1;
|
SDMMC.pldmnd = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sdmmc_host_card_busy()
|
||||||
|
{
|
||||||
|
return SDMMC.status.data_busy == 1;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t sdmmc_host_io_int_enable(int slot)
|
esp_err_t sdmmc_host_io_int_enable(int slot)
|
||||||
{
|
{
|
||||||
configure_pin(sdmmc_slot_info[slot].d1_gpio);
|
configure_pin(sdmmc_slot_info[slot].d1_gpio);
|
||||||
|
|
|
@ -38,6 +38,8 @@ void sdmmc_host_dma_stop();
|
||||||
|
|
||||||
void sdmmc_host_dma_resume();
|
void sdmmc_host_dma_resume();
|
||||||
|
|
||||||
|
bool sdmmc_host_card_busy();
|
||||||
|
|
||||||
esp_err_t sdmmc_host_transaction_handler_init();
|
esp_err_t sdmmc_host_transaction_handler_init();
|
||||||
|
|
||||||
void sdmmc_host_transaction_handler_deinit();
|
void sdmmc_host_transaction_handler_deinit();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
#include "soc/sdmmc_periph.h"
|
#include "soc/sdmmc_periph.h"
|
||||||
#include "soc/soc_memory_layout.h"
|
#include "soc/soc_memory_layout.h"
|
||||||
#include "driver/sdmmc_types.h"
|
#include "driver/sdmmc_types.h"
|
||||||
|
@ -80,6 +81,7 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
|
||||||
static void process_command_response(uint32_t status, sdmmc_command_t* cmd);
|
static void process_command_response(uint32_t status, sdmmc_command_t* cmd);
|
||||||
static void fill_dma_descriptors(size_t num_desc);
|
static void fill_dma_descriptors(size_t num_desc);
|
||||||
static size_t get_free_descriptors_count();
|
static size_t get_free_descriptors_count();
|
||||||
|
static bool wait_for_busy_cleared(int timeout_ms);
|
||||||
|
|
||||||
esp_err_t sdmmc_host_transaction_handler_init()
|
esp_err_t sdmmc_host_transaction_handler_init()
|
||||||
{
|
{
|
||||||
|
@ -165,6 +167,11 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ret == ESP_OK && (cmdinfo->flags & SCF_WAIT_BUSY)) {
|
||||||
|
if (!wait_for_busy_cleared(cmdinfo->timeout_ms)) {
|
||||||
|
ret = ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
s_is_app_cmd = (ret == ESP_OK && cmdinfo->opcode == MMC_APP_CMD);
|
s_is_app_cmd = (ret == ESP_OK && cmdinfo->opcode == MMC_APP_CMD);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -461,5 +468,23 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool wait_for_busy_cleared(int timeout_ms)
|
||||||
|
{
|
||||||
|
if (timeout_ms == 0) {
|
||||||
|
return !sdmmc_host_card_busy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It would have been nice to do this without polling, however the peripheral
|
||||||
|
* can only generate Busy Clear Interrupt for data write commands, and waiting
|
||||||
|
* for busy clear is mostly needed for other commands such as MMC_SWITCH.
|
||||||
|
*/
|
||||||
|
int timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS;
|
||||||
|
while (timeout_ticks-- > 0) {
|
||||||
|
if (!sdmmc_host_card_busy()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
vTaskDelay(1);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
* 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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -214,6 +214,18 @@ esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (card->is_ddr) {
|
||||||
|
if (card->host.set_bus_ddr_mode == NULL) {
|
||||||
|
ESP_LOGE(TAG, "host doesn't support DDR mode or voltage switching");
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
esp_err_t err = (*card->host.set_bus_ddr_mode)(card->host.slot, true);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to switch bus to DDR mode (0x%x)", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +258,12 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
|
||||||
type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC";
|
type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC";
|
||||||
}
|
}
|
||||||
fprintf(stream, "Type: %s\n", type);
|
fprintf(stream, "Type: %s\n", type);
|
||||||
fprintf(stream, "Speed: %s\n", (card->max_freq_khz > SDMMC_FREQ_26M) ? "high speed" : "default speed");
|
if (card->max_freq_khz < 1000) {
|
||||||
|
fprintf(stream, "Speed: %d kHz\n", card->max_freq_khz);
|
||||||
|
} else {
|
||||||
|
fprintf(stream, "Speed: %d MHz%s\n", card->max_freq_khz / 1000,
|
||||||
|
card->is_ddr ? ", DDR" : "");
|
||||||
|
}
|
||||||
fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
|
fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
|
||||||
|
|
||||||
if (print_csd) {
|
if (print_csd) {
|
||||||
|
@ -269,13 +286,17 @@ esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
|
||||||
int slot_bit_width = card->host.get_bus_width(card->host.slot);
|
int slot_bit_width = card->host.get_bus_width(card->host.slot);
|
||||||
if (slot_bit_width == 1 &&
|
if (slot_bit_width == 1 &&
|
||||||
(card->host.flags & (width_4bit | width_8bit))) {
|
(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_mask;
|
||||||
card->host.flags |= ~(width_1bit);
|
card->host.flags |= width_1bit;
|
||||||
} else if (slot_bit_width == 4 && (card->host.flags & width_8bit)){
|
} else if (slot_bit_width == 4 && (card->host.flags & width_8bit)) {
|
||||||
ESP_LOGW(TAG, "host slot is configured in 4-bit mode");
|
if ((card->host.flags & width_4bit) == 0) {
|
||||||
card->host.flags &= ~width_mask;
|
ESP_LOGW(TAG, "slot width set to 4, but host flags don't have 4 line mode enabled; using 1 line mode");
|
||||||
card->host.flags |= width_4bit;
|
card->host.flags &= ~width_mask;
|
||||||
|
card->host.flags |= width_1bit;
|
||||||
|
} else {
|
||||||
|
card->host.flags &= ~width_mask;
|
||||||
|
card->host.flags |= width_4bit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,11 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
|
||||||
/* MMC cards: read CXD */
|
/* MMC cards: read CXD */
|
||||||
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
|
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
/* Set bus width. One call for every kind of card, then one for the host */
|
/* Set bus width. One call for every kind of card, then one for the host */
|
||||||
if (!is_spi) {
|
if (!is_spi) {
|
||||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
|
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
|
||||||
|
@ -101,19 +106,10 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
|
||||||
SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
|
SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
|
/* Switch to the host to use card->max_freq_khz frequency. */
|
||||||
|
|
||||||
/* 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);
|
SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
|
||||||
|
|
||||||
/* Sanity check after switching the frequency */
|
/* Sanity check after switching the bus mode and frequency */
|
||||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
|
SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
|
||||||
/* TODO: add similar checks for eMMC and SDIO */
|
/* TODO: add similar checks for eMMC and SDIO */
|
||||||
|
|
||||||
|
|
|
@ -48,9 +48,15 @@ esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
|
||||||
}
|
}
|
||||||
card_type = ext_csd[EXT_CSD_CARD_TYPE];
|
card_type = ext_csd[EXT_CSD_CARD_TYPE];
|
||||||
|
|
||||||
/* TODO: add DDR support */
|
card->is_ddr = 0;
|
||||||
if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
|
if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
|
||||||
card->max_freq_khz = SDMMC_FREQ_52M;
|
card->max_freq_khz = SDMMC_FREQ_52M;
|
||||||
|
if ((card->host.flags & SDMMC_HOST_FLAG_DDR) &&
|
||||||
|
card->host.max_freq_khz >= SDMMC_FREQ_26M &&
|
||||||
|
card->host.get_bus_width(card->host.slot) == 4) {
|
||||||
|
ESP_LOGD(TAG, "card and host support DDR mode");
|
||||||
|
card->is_ddr = 1;
|
||||||
|
}
|
||||||
} else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
|
} else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
|
||||||
card->max_freq_khz = SDMMC_FREQ_52M;
|
card->max_freq_khz = SDMMC_FREQ_52M;
|
||||||
} else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
|
} else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
|
||||||
|
@ -60,7 +66,7 @@ esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
|
||||||
}
|
}
|
||||||
/* For MMC cards, use speed value from EXT_CSD */
|
/* For MMC cards, use speed value from EXT_CSD */
|
||||||
card->csd.tr_speed = card->max_freq_khz * 1000;
|
card->csd.tr_speed = card->max_freq_khz * 1000;
|
||||||
ESP_LOGD(TAG, "MMC card supports %d khz bus frequency", card->max_freq_khz);
|
ESP_LOGD(TAG, "MMC card type %d, max_freq_khz=%d, is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr);
|
||||||
card->max_freq_khz = MIN(card->max_freq_khz, card->host.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) {
|
if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
|
||||||
|
@ -104,13 +110,21 @@ esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card->log_bus_width > 0) {
|
if (card->log_bus_width > 0) {
|
||||||
int csd_bus_width_value = 0;
|
int csd_bus_width_value = EXT_CSD_BUS_WIDTH_1;
|
||||||
int bus_width = 1;
|
int bus_width = 1;
|
||||||
if (card->log_bus_width == 2) {
|
if (card->log_bus_width == 2) {
|
||||||
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
|
if (card->is_ddr) {
|
||||||
|
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4_DDR;
|
||||||
|
} else {
|
||||||
|
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
|
||||||
|
}
|
||||||
bus_width = 4;
|
bus_width = 4;
|
||||||
} else if (card->log_bus_width == 3) {
|
} else if (card->log_bus_width == 3) {
|
||||||
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
|
if (card->is_ddr) {
|
||||||
|
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8_DDR;
|
||||||
|
} else {
|
||||||
|
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
|
||||||
|
}
|
||||||
bus_width = 8;
|
bus_width = 8;
|
||||||
}
|
}
|
||||||
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
@ -205,7 +219,7 @@ esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8
|
||||||
sdmmc_command_t cmd = {
|
sdmmc_command_t cmd = {
|
||||||
.opcode = MMC_SWITCH,
|
.opcode = MMC_SWITCH,
|
||||||
.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
|
.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
|
||||||
.flags = SCF_RSP_R1B | SCF_CMD_AC,
|
.flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY,
|
||||||
};
|
};
|
||||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
#define SDMMC_INTMASK_EBE BIT(15)
|
#define SDMMC_INTMASK_EBE BIT(15)
|
||||||
#define SDMMC_INTMASK_ACD BIT(14)
|
#define SDMMC_INTMASK_ACD BIT(14)
|
||||||
#define SDMMC_INTMASK_SBE BIT(13)
|
#define SDMMC_INTMASK_SBE BIT(13)
|
||||||
|
#define SDMMC_INTMASK_BCI BIT(13)
|
||||||
#define SDMMC_INTMASK_HLE BIT(12)
|
#define SDMMC_INTMASK_HLE BIT(12)
|
||||||
#define SDMMC_INTMASK_FRUN BIT(11)
|
#define SDMMC_INTMASK_FRUN BIT(11)
|
||||||
#define SDMMC_INTMASK_HTO BIT(10)
|
#define SDMMC_INTMASK_HTO BIT(10)
|
||||||
|
|
|
@ -283,7 +283,12 @@ typedef volatile struct {
|
||||||
uint32_t usrid; ///< user ID
|
uint32_t usrid; ///< user ID
|
||||||
uint32_t verid; ///< IP block version
|
uint32_t verid; ///< IP block version
|
||||||
uint32_t hcon; ///< compile-time IP configuration
|
uint32_t hcon; ///< compile-time IP configuration
|
||||||
uint32_t uhs; ///< TBD
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t voltage: 16; ///< voltage control for slots; no-op on ESP32.
|
||||||
|
uint32_t ddr: 16; ///< bit N enables DDR mode for card N
|
||||||
|
};
|
||||||
|
} uhs; ///< UHS related settings
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
|
@ -348,7 +353,16 @@ typedef volatile struct {
|
||||||
uint32_t bufaddrl; ///< unused
|
uint32_t bufaddrl; ///< unused
|
||||||
uint32_t bufaddru; ///< unused
|
uint32_t bufaddru; ///< unused
|
||||||
uint32_t reserved_a8[22];
|
uint32_t reserved_a8[22];
|
||||||
uint32_t cardthrctl;
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t read_thr_en : 1; ///< initiate transfer only if FIFO has more space than the read threshold
|
||||||
|
uint32_t busy_clr_int_en : 1; ///< enable generation of busy clear interrupts
|
||||||
|
uint32_t write_thr_en : 1; ///< equivalent of read_thr_en for writes
|
||||||
|
uint32_t reserved1 : 13;
|
||||||
|
uint32_t card_threshold : 12; ///< threshold value for reads/writes, in bytes
|
||||||
|
};
|
||||||
|
uint32_t val;
|
||||||
|
} cardthrctl;
|
||||||
uint32_t back_end_power;
|
uint32_t back_end_power;
|
||||||
uint32_t uhs_reg_ext;
|
uint32_t uhs_reg_ext;
|
||||||
uint32_t emmc_ddr_reg;
|
uint32_t emmc_ddr_reg;
|
||||||
|
|
Loading…
Reference in a new issue