change(sdmmc): adapt host state machine for CMD11, commit reg/struct files

This commit is contained in:
Ivan Grokhotkov 2024-10-04 18:46:49 +02:00 committed by Armando
parent a8efd39877
commit b8de3dfb35
4 changed files with 124 additions and 20 deletions

View File

@ -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);
}

View File

@ -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
*

View File

@ -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)

View File

@ -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;