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:
Ivan Grokhotkov
2018-08-22 18:16:32 +08:00
parent de42d99b1d
commit 78fab8a0f9
12 changed files with 156 additions and 33 deletions
@@ -161,10 +161,10 @@
/* EXT_CSD_CARD_TYPE */
/* The only currently valid values for this field are 0x01, 0x03, 0x07,
* 0x0B and 0x0F. */
#define EXT_CSD_CARD_TYPE_F_26M (1 << 0)
#define EXT_CSD_CARD_TYPE_F_52M (1 << 1)
#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2)
#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3)
#define EXT_CSD_CARD_TYPE_F_26M (1 << 0) /* SDR at "rated voltages */
#define EXT_CSD_CARD_TYPE_F_52M (1 << 1) /* SDR at "rated voltages */
#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) /* DDR, 1.2V I/O */
#define EXT_CSD_CARD_TYPE_26M 0x01
#define EXT_CSD_CARD_TYPE_52M 0x03
#define EXT_CSD_CARD_TYPE_52M_V18 0x07
+15 -1
View File
@@ -33,13 +33,17 @@ extern "C" {
* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
*/
#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, \
.max_freq_khz = SDMMC_FREQ_DEFAULT, \
.io_voltage = 3.3f, \
.init = &sdmmc_host_init, \
.set_bus_width = &sdmmc_host_set_bus_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, \
.do_transaction = &sdmmc_host_do_transaction, \
.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);
/**
* @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
*
@@ -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_R6 (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 */
esp_err_t error; /*!< error returned from transfer */
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_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
#define SDMMC_HOST_FLAG_DDR BIT(4) /*!< host supports DDR mode for SD/MMC */
int slot; /*!< slot number, to be passed to host functions */
int max_freq_khz; /*!< max frequency supported by the host */
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
@@ -138,6 +141,7 @@ typedef struct {
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 */
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 (*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 */
@@ -163,7 +167,8 @@ typedef struct {
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 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;
@@ -41,6 +41,7 @@ extern "C" {
.init = &sdspi_host_init, \
.set_bus_width = NULL, \
.get_bus_width = NULL, \
.set_bus_ddr_mode = NULL, \
.set_card_clk = &sdspi_host_set_card_clk, \
.do_transaction = &sdspi_host_do_transaction, \
.deinit = &sdspi_host_deinit, \
+30
View File
@@ -267,6 +267,9 @@ esp_err_t sdmmc_host_init()
SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE; //sdio is enabled only when use.
SDMMC.ctrl.int_enable = 1;
// Disable generation of Busy Clear Interrupt
SDMMC.cardthrctl.busy_clr_int_en = 0;
// Enable DMA
sdmmc_host_dma_init();
@@ -465,6 +468,28 @@ size_t sdmmc_host_get_slot_width(int 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()
{
SDMMC.ctrl.dma_enable = 1;
@@ -504,6 +529,11 @@ void sdmmc_host_dma_resume()
SDMMC.pldmnd = 1;
}
bool sdmmc_host_card_busy()
{
return SDMMC.status.data_busy == 1;
}
esp_err_t sdmmc_host_io_int_enable(int slot)
{
configure_pin(sdmmc_slot_info[slot].d1_gpio);
+2
View File
@@ -38,6 +38,8 @@ void sdmmc_host_dma_stop();
void sdmmc_host_dma_resume();
bool sdmmc_host_card_busy();
esp_err_t sdmmc_host_transaction_handler_init();
void sdmmc_host_transaction_handler_deinit();
+25
View File
@@ -19,6 +19,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "soc/sdmmc_periph.h"
#include "soc/soc_memory_layout.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 fill_dma_descriptors(size_t num_desc);
static size_t get_free_descriptors_count();
static bool wait_for_busy_cleared(int timeout_ms);
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;
}
}
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);
out:
@@ -461,5 +468,23 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
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;
}