mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 17:19:09 -04:00
change(sdmmc): adapt host state machine for CMD11, commit reg/struct files
This commit is contained in:
parent
a8efd39877
commit
b8de3dfb35
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
@ -37,6 +38,8 @@ typedef enum {
|
||||
SDMMC_SENDING_CMD,
|
||||
SDMMC_SENDING_DATA,
|
||||
SDMMC_BUSY,
|
||||
SDMMC_SENDING_VOLTAGE_SWITCH,
|
||||
SDMMC_WAITING_VOLTAGE_SWITCH,
|
||||
} sdmmc_req_state_t;
|
||||
|
||||
typedef struct {
|
||||
@ -70,14 +73,17 @@ static esp_pm_lock_handle_t s_pm_lock;
|
||||
|
||||
static esp_err_t handle_idle_state_events(void);
|
||||
static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd);
|
||||
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state,
|
||||
static esp_err_t handle_event(int slot, sdmmc_command_t* cmd, sdmmc_req_state_t* state,
|
||||
sdmmc_event_t* unhandled_events);
|
||||
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
|
||||
static esp_err_t process_events(int slot, sdmmc_event_t evt, sdmmc_command_t* cmd,
|
||||
sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events);
|
||||
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(void);
|
||||
static bool wait_for_busy_cleared(uint32_t timeout_ms);
|
||||
static void handle_voltage_switch_stage1(int slot, sdmmc_command_t* cmd);
|
||||
static void handle_voltage_switch_stage2(int slot, sdmmc_command_t* cmd);
|
||||
static void handle_voltage_switch_stage3(int slot, sdmmc_command_t* cmd);
|
||||
|
||||
esp_err_t sdmmc_host_transaction_handler_init(void)
|
||||
{
|
||||
@ -124,6 +130,12 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
|
||||
|
||||
// dispose of any events which happened asynchronously
|
||||
handle_idle_state_events();
|
||||
|
||||
// special handling for voltage switch command
|
||||
if (cmdinfo->opcode == SD_SWITCH_VOLTAGE) {
|
||||
handle_voltage_switch_stage1(slot, cmdinfo);
|
||||
}
|
||||
|
||||
// convert cmdinfo to hardware register value
|
||||
sdmmc_hw_cmd_t hw_cmd = make_hw_cmd(cmdinfo);
|
||||
if (cmdinfo->data) {
|
||||
@ -174,9 +186,12 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
|
||||
// process events until transfer is complete
|
||||
cmdinfo->error = ESP_OK;
|
||||
sdmmc_req_state_t state = SDMMC_SENDING_CMD;
|
||||
if (cmdinfo->opcode == SD_SWITCH_VOLTAGE) {
|
||||
state = SDMMC_SENDING_VOLTAGE_SWITCH;
|
||||
}
|
||||
sdmmc_event_t unhandled_events = { 0 };
|
||||
while (state != SDMMC_IDLE) {
|
||||
ret = handle_event(cmdinfo, &state, &unhandled_events);
|
||||
ret = handle_event(slot, cmdinfo, &state, &unhandled_events);
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
@ -286,7 +301,7 @@ static esp_err_t handle_idle_state_events(void)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state,
|
||||
static esp_err_t handle_event(int slot, sdmmc_command_t* cmd, sdmmc_req_state_t* state,
|
||||
sdmmc_event_t* unhandled_events)
|
||||
{
|
||||
sdmmc_event_t event;
|
||||
@ -298,13 +313,13 @@ static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state,
|
||||
}
|
||||
return err;
|
||||
}
|
||||
ESP_LOGV(TAG, "sdmmc_handle_event: event %08"PRIx32" %08"PRIx32", unhandled %08"PRIx32" %08"PRIx32,
|
||||
event.sdmmc_status, event.dma_status,
|
||||
ESP_LOGV(TAG, "sdmmc_handle_event: slot %d event %08"PRIx32" %08"PRIx32", unhandled %08"PRIx32" %08"PRIx32,
|
||||
slot, event.sdmmc_status, event.dma_status,
|
||||
unhandled_events->sdmmc_status, unhandled_events->dma_status);
|
||||
event.sdmmc_status |= unhandled_events->sdmmc_status;
|
||||
event.dma_status |= unhandled_events->dma_status;
|
||||
process_events(event, cmd, state, unhandled_events);
|
||||
ESP_LOGV(TAG, "sdmmc_handle_event: events unhandled: %08"PRIx32" %08"PRIx32, unhandled_events->sdmmc_status, unhandled_events->dma_status);
|
||||
process_events(slot, event, cmd, state, unhandled_events);
|
||||
ESP_LOGV(TAG, "sdmmc_handle_event: slot %d events unhandled: %08"PRIx32" %08"PRIx32, slot, unhandled_events->sdmmc_status, unhandled_events->dma_status);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@ -327,12 +342,15 @@ static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd)
|
||||
res.stop_abort_cmd = 1;
|
||||
} else if (cmd->opcode == MMC_GO_IDLE_STATE) {
|
||||
res.send_init = 1;
|
||||
} else if (cmd->opcode == SD_SWITCH_VOLTAGE) {
|
||||
res.volt_switch = 1;
|
||||
} else {
|
||||
res.wait_complete = 1;
|
||||
}
|
||||
if (cmd->opcode == MMC_GO_IDLE_STATE) {
|
||||
res.send_init = 1;
|
||||
}
|
||||
|
||||
if (cmd->flags & SCF_RSP_PRESENT) {
|
||||
res.response_expect = 1;
|
||||
if (cmd->flags & SCF_RSP_136) {
|
||||
@ -419,18 +437,20 @@ static inline bool mask_check_and_clear(uint32_t* state, uint32_t mask)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
|
||||
static esp_err_t process_events(int slot, sdmmc_event_t evt, sdmmc_command_t* cmd,
|
||||
sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events)
|
||||
{
|
||||
const char* const s_state_names[] __attribute__((unused)) = {
|
||||
"IDLE",
|
||||
"SENDING_CMD",
|
||||
"SENDIND_DATA",
|
||||
"BUSY"
|
||||
"BUSY",
|
||||
"SENDING_VOLTAGE_SWITCH",
|
||||
"WAITING_VOLTAGE_SWITCH",
|
||||
};
|
||||
sdmmc_event_t orig_evt = evt;
|
||||
ESP_LOGV(TAG, "%s: state=%s evt=%"PRIx32" dma=%"PRIx32, __func__, s_state_names[*pstate],
|
||||
evt.sdmmc_status, evt.dma_status);
|
||||
ESP_LOGV(TAG, "%s: slot=%d state=%s evt=%"PRIx32" dma=%"PRIx32, __func__, slot,
|
||||
s_state_names[*pstate], evt.sdmmc_status, evt.dma_status);
|
||||
sdmmc_req_state_t next_state = *pstate;
|
||||
sdmmc_req_state_t state = (sdmmc_req_state_t) -1;
|
||||
while (next_state != state) {
|
||||
@ -461,6 +481,32 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
|
||||
}
|
||||
break;
|
||||
|
||||
case SDMMC_SENDING_VOLTAGE_SWITCH:
|
||||
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_CMD_ERR_MASK)) {
|
||||
process_command_response(orig_evt.sdmmc_status, cmd);
|
||||
next_state = SDMMC_IDLE;
|
||||
}
|
||||
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_VOLT_SW)) {
|
||||
handle_voltage_switch_stage2(slot, cmd);
|
||||
if (cmd->error != ESP_OK) {
|
||||
next_state = SDMMC_IDLE;
|
||||
} else {
|
||||
next_state = SDMMC_WAITING_VOLTAGE_SWITCH;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDMMC_WAITING_VOLTAGE_SWITCH:
|
||||
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_CMD_ERR_MASK)) {
|
||||
process_command_response(orig_evt.sdmmc_status, cmd);
|
||||
next_state = SDMMC_IDLE;
|
||||
}
|
||||
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_VOLT_SW)) {
|
||||
handle_voltage_switch_stage3(slot, cmd);
|
||||
next_state = SDMMC_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDMMC_SENDING_DATA:
|
||||
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_DATA_ERR_MASK)) {
|
||||
process_data_status(orig_evt.sdmmc_status, cmd);
|
||||
@ -518,3 +564,32 @@ static bool wait_for_busy_cleared(uint32_t timeout_ms)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void handle_voltage_switch_stage1(int slot, sdmmc_command_t* cmd)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s: enabling clock", __func__);
|
||||
sdmmc_host_set_cclk_always_on(slot, true);
|
||||
}
|
||||
|
||||
static void handle_voltage_switch_stage2(int slot, sdmmc_command_t* cmd)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s: disabling clock", __func__);
|
||||
sdmmc_host_enable_clk_cmd11(slot, false);
|
||||
usleep(100);
|
||||
ESP_LOGV(TAG, "%s: switching voltage", __func__);
|
||||
esp_err_t err = cmd->volt_switch_cb(cmd->volt_switch_cb_arg, 1800);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to switch voltage (0x%x)", err);
|
||||
cmd->error = err;
|
||||
}
|
||||
ESP_LOGV(TAG, "%s: waiting 10ms", __func__);
|
||||
usleep(10000);
|
||||
ESP_LOGV(TAG, "%s: enabling clock", __func__);
|
||||
sdmmc_host_enable_clk_cmd11(slot, true);
|
||||
}
|
||||
|
||||
static void handle_voltage_switch_stage3(int slot, sdmmc_command_t* cmd)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s: voltage switch complete, clock back to low-power mode", __func__);
|
||||
sdmmc_host_set_cclk_always_on(slot, false);
|
||||
}
|
||||
|
@ -451,6 +451,22 @@ static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t sl
|
||||
return is_protected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch between 3.3V and 1.8V mode
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
* @param en enable / disable 1.8V (3.3V on disable)
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_18v_mode(sdmmc_dev_t *hw, uint32_t slot, bool en)
|
||||
{
|
||||
if (en) {
|
||||
hw->uhs.volt |= BIT(slot);
|
||||
} else {
|
||||
hw->uhs.volt &= ~BIT(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable DDR mode
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -1502,6 +1502,7 @@ extern "C" {
|
||||
#define SDMMC_INTMASK_HLE BIT(12)
|
||||
#define SDMMC_INTMASK_FRUN BIT(11)
|
||||
#define SDMMC_INTMASK_HTO BIT(10)
|
||||
#define SDMMC_INTMASK_VOLT_SW SDMMC_INTMASK_HTO
|
||||
#define SDMMC_INTMASK_DTO BIT(9)
|
||||
#define SDMMC_INTMASK_RTO BIT(8)
|
||||
#define SDMMC_INTMASK_DCRC BIT(7)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -458,7 +458,13 @@ typedef union {
|
||||
* not masked.
|
||||
*/
|
||||
uint32_t ccs_expected:1;
|
||||
uint32_t reserved_24:5;
|
||||
uint32_t reserved_24:4;
|
||||
/** volt_switch : R/W; bitpos: [28]; default: 0;
|
||||
* Voltage switch bit.
|
||||
* 0: No voltage switching.
|
||||
* 1: Voltage switching enabled; must be set for CMD11 only.
|
||||
*/
|
||||
uint32_t volt_switch:1;
|
||||
/** use_hole_reg : R/W; bitpos: [29]; default: 1;
|
||||
* Use Hold Register.
|
||||
* 0: CMD and DATA sent to card bypassing HOLD Register;
|
||||
@ -907,11 +913,17 @@ typedef union {
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t reserved_0:16;
|
||||
/** ddr : R/W; bitpos: [17:16]; default: 0;
|
||||
* DDR mode selection,1 bit for each card.
|
||||
* 0-Non-DDR mode.
|
||||
* 1-DDR mode.
|
||||
/** volt: R/W; bitpos: [1:0]; default: 0;
|
||||
* Voltage mode selection, 1 bit for each card.
|
||||
* 0: 3.3V mode.
|
||||
* 1: 1.8V mode.
|
||||
*/
|
||||
uint32_t volt:2;
|
||||
uint32_t reserved_0:14;
|
||||
/** ddr: R/W; bitpos: [17:16]; default: 0;
|
||||
* DDR mode selection, 1 bit for each card.
|
||||
* 0: Non-DDR mode.
|
||||
* 1: DDR mode.
|
||||
*/
|
||||
uint32_t ddr:2;
|
||||
uint32_t reserved_18:14;
|
||||
|
Loading…
x
Reference in New Issue
Block a user