feat(sdmmc): supported sd2.0 on esp32p4

This commit is contained in:
Armando 2023-08-29 19:50:27 +08:00 committed by Armando (Dou Yiwen)
parent 1685dbc985
commit 70314b56d5
39 changed files with 2255 additions and 565 deletions

View File

@ -0,0 +1,112 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "sdmmc_types.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SDMMC_HOST_SLOT_0 0 ///< SDMMC slot 0
#define SDMMC_HOST_SLOT_1 1 ///< SDMMC slot 1
/**
* @brief Default sdmmc_host_t structure initializer for SDMMC peripheral
*
* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
*/
#define SDMMC_HOST_DEFAULT() {\
.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, \
.set_cclk_always_on = &sdmmc_host_set_cclk_always_on, \
.do_transaction = &sdmmc_host_do_transaction, \
.deinit = &sdmmc_host_deinit, \
.io_int_enable = sdmmc_host_io_int_enable, \
.io_int_wait = sdmmc_host_io_int_wait, \
.command_timeout_ms = 0, \
.get_real_freq = &sdmmc_host_get_real_freq, \
.input_delay_phase = SDMMC_DELAY_PHASE_0, \
.set_input_delay = &sdmmc_host_set_input_delay \
}
#define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used
#define SDMMC_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used
#define SDMMC_SLOT_WIDTH_DEFAULT 0 ///< use the maximum possible width for the slot
#if SOC_SDMMC_USE_IOMUX && !SOC_SDMMC_USE_GPIO_MATRIX
/**
* Macro defining default configuration of SDMMC host slot
*/
#define SDMMC_SLOT_CONFIG_DEFAULT() {\
.cd = SDMMC_SLOT_NO_CD, \
.wp = SDMMC_SLOT_NO_WP, \
.width = SDMMC_SLOT_WIDTH_DEFAULT, \
.flags = 0, \
}
#else
/**
* Macro defining default configuration of SDMMC host slot
*/
#if CONFIG_IDF_TARGET_ESP32P4
#define SDMMC_SLOT_CONFIG_DEFAULT() {\
.clk = GPIO_NUM_43, \
.cmd = GPIO_NUM_44, \
.d0 = GPIO_NUM_39, \
.d1 = GPIO_NUM_40, \
.d2 = GPIO_NUM_41, \
.d3 = GPIO_NUM_42, \
.d4 = GPIO_NUM_45, \
.d5 = GPIO_NUM_46, \
.d6 = GPIO_NUM_47, \
.d7 = GPIO_NUM_48, \
.cd = SDMMC_SLOT_NO_CD, \
.wp = SDMMC_SLOT_NO_WP, \
.width = SDMMC_SLOT_WIDTH_DEFAULT, \
.flags = 0, \
}
#elif CONFIG_IDF_TARGET_ESP32S3
#define SDMMC_SLOT_CONFIG_DEFAULT() {\
.clk = GPIO_NUM_14, \
.cmd = GPIO_NUM_15, \
.d0 = GPIO_NUM_2, \
.d1 = GPIO_NUM_4, \
.d2 = GPIO_NUM_12, \
.d3 = GPIO_NUM_13, \
.d4 = GPIO_NUM_33, \
.d5 = GPIO_NUM_34, \
.d6 = GPIO_NUM_35, \
.d7 = GPIO_NUM_36, \
.cd = SDMMC_SLOT_NO_CD, \
.wp = SDMMC_SLOT_NO_WP, \
.width = SDMMC_SLOT_WIDTH_DEFAULT, \
.flags = 0, \
}
#endif // GPIO Matrix chips
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,44 +13,13 @@
#include <stddef.h>
#include "esp_err.h"
#include "sdmmc_types.h"
#include "driver/sdmmc_default_configs.h"
#include "driver/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SDMMC_HOST_SLOT_0 0 ///< SDMMC slot 0
#define SDMMC_HOST_SLOT_1 1 ///< SDMMC slot 1
/**
* @brief Default sdmmc_host_t structure initializer for SDMMC peripheral
*
* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
*/
#define SDMMC_HOST_DEFAULT() {\
.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, \
.set_cclk_always_on = &sdmmc_host_set_cclk_always_on, \
.do_transaction = &sdmmc_host_do_transaction, \
.deinit = &sdmmc_host_deinit, \
.io_int_enable = sdmmc_host_io_int_enable, \
.io_int_wait = sdmmc_host_io_int_wait, \
.command_timeout_ms = 0, \
.get_real_freq = &sdmmc_host_get_real_freq, \
.input_delay_phase = SDMMC_DELAY_PHASE_0, \
.set_input_delay = &sdmmc_host_set_input_delay \
}
/**
* Extra configuration for SDMMC peripheral slot
*/
@ -89,46 +58,6 @@ typedef struct {
*/
} sdmmc_slot_config_t;
#define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used
#define SDMMC_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used
#define SDMMC_SLOT_WIDTH_DEFAULT 0 ///< use the maximum possible width for the slot
#ifdef SOC_SDMMC_USE_GPIO_MATRIX
/**
* Macro defining default configuration of SDMMC host slot
*/
#define SDMMC_SLOT_CONFIG_DEFAULT() {\
.clk = GPIO_NUM_14, \
.cmd = GPIO_NUM_15, \
.d0 = GPIO_NUM_2, \
.d1 = GPIO_NUM_4, \
.d2 = GPIO_NUM_12, \
.d3 = GPIO_NUM_13, \
.d4 = GPIO_NUM_33, \
.d5 = GPIO_NUM_34, \
.d6 = GPIO_NUM_35, \
.d7 = GPIO_NUM_36, \
.cd = SDMMC_SLOT_NO_CD, \
.wp = SDMMC_SLOT_NO_WP, \
.width = SDMMC_SLOT_WIDTH_DEFAULT, \
.flags = 0, \
}
#else // SOC_SDMMC_USE_GPIO_MATRIX
/**
* Macro defining default configuration of SDMMC host slot
*/
#define SDMMC_SLOT_CONFIG_DEFAULT() {\
.cd = SDMMC_SLOT_NO_CD, \
.wp = SDMMC_SLOT_NO_WP, \
.width = SDMMC_SLOT_WIDTH_DEFAULT, \
.flags = 0, \
}
#endif // SOC_SDMMC_USE_GPIO_MATRIX
/**
* @brief Initialize SDMMC host peripheral
*

View File

@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: ISC
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileContributor: 2016-2023 Espressif Systems (Shanghai) CO LTD
*/
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@ -114,7 +114,8 @@ typedef struct {
uint32_t arg; /*!< SD/MMC command argument */
sdmmc_response_t response; /*!< response buffer */
void* data; /*!< buffer to send or read into */
size_t datalen; /*!< length of data buffer */
size_t datalen; /*!< length of data in the buffer */
size_t buflen; /*!< length of the buffer */
size_t blklen; /*!< block length */
int flags; /*!< see below */
/** @cond */

View File

@ -26,49 +26,46 @@
#include "soc/sdmmc_periph.h"
#include "soc/soc_caps.h"
#include "hal/gpio_hal.h"
#include "hal/sdmmc_hal.h"
#include "hal/sdmmc_ll.h"
#define SDMMC_EVENT_QUEUE_LENGTH 32
#define SDMMC_CLK_NEEDS_RCC CONFIG_IDF_TARGET_ESP32P4
#if SDMMC_CLK_NEEDS_RCC
// Reset and Clock Control registers are mixing with other peripherals, so we need to use a critical section
#define SDMMC_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define SDMMC_RCC_ATOMIC()
#endif
static const char *TAG = "sdmmc_periph";
/**
* Slot contexts
*/
typedef struct slot_ctx_t {
size_t slot_width;
sdmmc_slot_io_info_t slot_gpio_num;
bool use_gpio_matrix;
} slot_ctx_t;
/**
* Host contexts
*/
typedef struct host_ctx_t {
intr_handle_t intr_handle;
QueueHandle_t event_queue;
SemaphoreHandle_t io_intr_event;
sdmmc_hal_context_t hal;
slot_ctx_t slot_ctx[SOC_SDMMC_NUM_SLOTS];
} host_ctx_t;
static host_ctx_t s_host_ctx;
static void sdmmc_isr(void *arg);
static void sdmmc_host_dma_init(void);
static const char *TAG = "sdmmc_periph";
static intr_handle_t s_intr_handle;
static QueueHandle_t s_event_queue;
static SemaphoreHandle_t s_io_intr_event;
static size_t s_slot_width[2] = {1, 1};
/* The following definitions are used to simplify GPIO configuration in the driver,
* whether IOMUX or GPIO Matrix is used by the chip.
* Two simple "APIs" are provided to the driver code:
* - configure_pin(name, slot, mode): Configures signal "name" for the given slot and mode.
* - GPIO_NUM(slot, name): Returns the GPIO number of signal "name" for the given slot.
*
* To make this work, configure_pin is defined as a macro that picks the parameters required
* for configuring GPIO matrix or IOMUX from relevant arrays, and passes them to either of
* configure_pin_gpio_matrix, configure_pin_iomux functions.
* Likewise, GPIO_NUM is a macro that picks the pin number from one of the two structures.
*
* Macros are used rather than inline functions to look up members of different structures
* with same names. E.g. the number of pin d3 is obtained either from .d3 member of
* sdmmc_slot_gpio_num array (for IOMUX) or from .d3 member of s_sdmmc_slot_gpio_num array
* (for GPIO matrix).
*/
#ifdef SOC_SDMMC_USE_GPIO_MATRIX
static void configure_pin_gpio_matrix(uint8_t gpio_num, uint8_t gpio_matrix_sig, gpio_mode_t mode, const char *name);
#define configure_pin(name, slot, mode) \
configure_pin_gpio_matrix(s_sdmmc_slot_gpio_num[slot].name, sdmmc_slot_gpio_sig[slot].name, mode, #name)
static sdmmc_slot_io_info_t s_sdmmc_slot_gpio_num[SOC_SDMMC_NUM_SLOTS];
#define GPIO_NUM(slot, name) s_sdmmc_slot_gpio_num[slot].name
#elif SOC_SDMMC_USE_IOMUX
static void configure_pin_iomux(uint8_t gpio_num);
#define configure_pin(name, slot, mode) configure_pin_iomux(sdmmc_slot_gpio_num[slot].name)
#define GPIO_NUM(slot, name) sdmmc_slot_gpio_num[slot].name
#endif // SOC_SDMMC_USE_GPIO_MATRIX
static esp_err_t sdmmc_host_pullup_en_internal(int slot, int width);
esp_err_t sdmmc_host_reset(void)
@ -120,74 +117,18 @@ esp_err_t sdmmc_host_reset(void)
* Of the second stage dividers, div0 is used for card 0, and div1 is used
* for card 1.
*/
static void sdmmc_host_set_clk_div(int div)
{
/**
* Set frequency to 160MHz / div
*
* n: counter resets at div_factor_n.
* l: negedge when counter equals div_factor_l.
* h: posedge when counter equals div_factor_h.
*
* We set the duty cycle to 1/2
*/
#if CONFIG_IDF_TARGET_ESP32
assert (div > 1 && div <= 16);
int h = div - 1;
int l = div / 2 - 1;
SDMMC.clock.div_factor_h = h;
SDMMC.clock.div_factor_l = l;
SDMMC.clock.div_factor_n = h;
// Set phases for in/out clocks
// 180 degree phase on input and output clocks
SDMMC.clock.phase_dout = 4;
SDMMC.clock.phase_din = 4;
SDMMC.clock.phase_core = 0;
#elif CONFIG_IDF_TARGET_ESP32S3
assert (div > 1 && div <= 16);
int l = div - 1;
int h = div / 2 - 1;
SDMMC.clock.div_factor_h = h;
SDMMC.clock.div_factor_l = l;
SDMMC.clock.div_factor_n = l;
// Make sure SOC_MOD_CLK_PLL_F160M (160 MHz) source clock is used
#if SOC_SDMMC_SUPPORT_XTAL_CLOCK
SDMMC.clock.clk_sel = 1;
#endif
SDMMC.clock.phase_core = 0;
/* 90 deg. delay for cclk_out to satisfy large hold time for SDR12 (up to 25MHz) and SDR25 (up to 50MHz) modes.
* Whether this delayed clock will be used depends on use_hold_reg bit in CMD structure,
* determined when sending out the command.
*/
SDMMC.clock.phase_dout = 1;
SDMMC.clock.phase_din = 0;
#endif //CONFIG_IDF_TARGET_ESP32S3
SDMMC_RCC_ATOMIC() {
sdmmc_ll_set_clock_div(s_host_ctx.hal.dev, div);
sdmmc_ll_select_clk_source(s_host_ctx.hal.dev, SDMMC_CLK_SRC_DEFAULT);
sdmmc_ll_init_phase_delay(s_host_ctx.hal.dev);
}
// Wait for the clock to propagate
esp_rom_delay_us(10);
}
static inline int s_get_host_clk_div(void)
{
#if CONFIG_IDF_TARGET_ESP32
return SDMMC.clock.div_factor_h + 1;
#elif CONFIG_IDF_TARGET_ESP32S3
return SDMMC.clock.div_factor_l + 1;
#endif
}
static void sdmmc_host_input_clk_disable(void)
{
SDMMC.clock.val = 0;
}
static esp_err_t sdmmc_host_clock_update_command(int slot)
{
// Clock update command (not a real command; just updates CIU registers)
@ -232,12 +173,18 @@ static esp_err_t sdmmc_host_clock_update_command(int slot)
return ESP_OK;
}
void sdmmc_host_get_clk_dividers(const uint32_t freq_khz, int *host_div, int *card_div)
void sdmmc_host_get_clk_dividers(uint32_t freq_khz, int *host_div, int *card_div)
{
uint32_t clk_src_freq_hz = 0;
esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
assert(clk_src_freq_hz == (160 * 1000 * 1000));
#if SDMMC_LL_MAX_FREQ_KHZ_FPGA
if (freq_khz >= SDMMC_LL_MAX_FREQ_KHZ_FPGA) {
ESP_LOGW(TAG, "working on FPGA, fallback to use the %d KHz", SDMMC_LL_MAX_FREQ_KHZ_FPGA);
freq_khz = SDMMC_LL_MAX_FREQ_KHZ_FPGA;
}
#endif
// Calculate new dividers
if (freq_khz >= SDMMC_FREQ_HIGHSPEED) {
*host_div = 4; // 160 MHz / 4 = 40 MHz
@ -282,7 +229,7 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
}
// Disable clock first
SDMMC.clkena.cclk_enable &= ~BIT(slot);
sdmmc_ll_enable_card_clock(s_host_ctx.hal.dev, slot, false);
esp_err_t err = sdmmc_host_clock_update_command(slot);
if (err != ESP_OK) {
ESP_LOGE(TAG, "disabling clk failed");
@ -297,17 +244,8 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
int real_freq = sdmmc_host_calc_freq(host_div, card_div);
ESP_LOGD(TAG, "slot=%d host_div=%d card_div=%d freq=%dkHz (max %" PRIu32 "kHz)", slot, host_div, card_div, real_freq, freq_khz);
// Program CLKDIV and CLKSRC, send them to the CIU
switch(slot) {
case 0:
SDMMC.clksrc.card0 = 0;
SDMMC.clkdiv.div0 = card_div;
break;
case 1:
SDMMC.clksrc.card1 = 1;
SDMMC.clkdiv.div1 = card_div;
break;
}
// Program card clock settings, send them to the CIU
sdmmc_ll_set_card_clock_div(s_host_ctx.hal.dev, slot, card_div);
sdmmc_host_set_clk_div(host_div);
err = sdmmc_host_clock_update_command(slot);
if (err != ESP_OK) {
@ -317,8 +255,8 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
}
// Re-enable clocks
SDMMC.clkena.cclk_enable |= BIT(slot);
SDMMC.clkena.cclk_low_power |= BIT(slot);
sdmmc_ll_enable_card_clock(s_host_ctx.hal.dev, slot, true);
sdmmc_ll_enable_card_clock_low_power(s_host_ctx.hal.dev, slot, true);
err = sdmmc_host_clock_update_command(slot);
if (err != ESP_OK) {
ESP_LOGE(TAG, "re-enabling clk failed");
@ -329,13 +267,10 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
// set data timeout
const uint32_t data_timeout_ms = 100;
uint32_t data_timeout_cycles = data_timeout_ms * freq_khz;
const uint32_t data_timeout_cycles_max = 0xffffff;
if (data_timeout_cycles > data_timeout_cycles_max) {
data_timeout_cycles = data_timeout_cycles_max;
}
SDMMC.tmout.data = data_timeout_cycles;
sdmmc_ll_set_data_timeout(s_host_ctx.hal.dev, data_timeout_cycles);
// always set response timeout to highest value, it's small enough anyway
SDMMC.tmout.response = 255;
sdmmc_ll_set_response_timeout(s_host_ctx.hal.dev, 255);
return ESP_OK;
}
@ -348,8 +283,8 @@ esp_err_t sdmmc_host_get_real_freq(int slot, int *real_freq_khz)
return ESP_ERR_INVALID_ARG;
}
int host_div = s_get_host_clk_div();
int card_div = slot == 0 ? SDMMC.clkdiv.div0 : SDMMC.clkdiv.div1;
int host_div = sdmmc_ll_get_clock_div(s_host_ctx.hal.dev);
int card_div = sdmmc_ll_get_card_clock_div(s_host_ctx.hal.dev, slot);
*real_freq_khz = sdmmc_host_calc_freq(host_div, card_div);
return ESP_OK;
@ -371,26 +306,31 @@ esp_err_t sdmmc_host_set_input_delay(int slot, sdmmc_delay_phase_t delay_phase)
//Now we're in high speed. Note ESP SDMMC Host HW only supports integer divider.
int delay_phase_num = 0;
sdmmc_ll_delay_phase_t phase = SDMMC_LL_DELAY_PHASE_0;
switch (delay_phase) {
case SDMMC_DELAY_PHASE_1:
SDMMC.clock.phase_din = 0x1;
phase = SDMMC_LL_DELAY_PHASE_1;
delay_phase_num = 1;
break;
case SDMMC_DELAY_PHASE_2:
SDMMC.clock.phase_din = 0x4;
phase = SDMMC_LL_DELAY_PHASE_2;
delay_phase_num = 2;
break;
case SDMMC_DELAY_PHASE_3:
SDMMC.clock.phase_din = 0x6;
phase = SDMMC_LL_DELAY_PHASE_3;
delay_phase_num = 3;
break;
default:
SDMMC.clock.phase_din = 0x0;
phase = SDMMC_LL_DELAY_PHASE_0;
delay_phase_num = 0;
break;
}
SDMMC_RCC_ATOMIC() {
sdmmc_ll_set_din_delay(s_host_ctx.hal.dev, phase);
}
int src_clk_period_ps = (1 * 1000 * 1000) / (clk_src_freq_hz / (1 * 1000 * 1000));
int phase_diff_ps = src_clk_period_ps * (SDMMC.clock.div_factor_n + 1) / SOC_SDMMC_DELAY_PHASE_NUM;
int phase_diff_ps = src_clk_period_ps * sdmmc_ll_get_clock_div(s_host_ctx.hal.dev) / SOC_SDMMC_DELAY_PHASE_NUM;
ESP_LOGD(TAG, "difference between input delay phases is %d ps", phase_diff_ps);
ESP_LOGI(TAG, "host sampling edge is delayed by %d ps", phase_diff_ps * delay_phase_num);
#endif
@ -403,10 +343,10 @@ esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg)
if (!(slot == 0 || slot == 1)) {
return ESP_ERR_INVALID_ARG;
}
if ((SDMMC.cdetect.cards & BIT(slot)) != 0) {
if (!sdmmc_ll_is_card_detected(s_host_ctx.hal.dev, slot)) {
return ESP_ERR_NOT_FOUND;
}
if (cmd.data_expected && cmd.rw && (SDMMC.wrtprt.cards & BIT(slot)) != 0) {
if (cmd.data_expected && cmd.rw && sdmmc_ll_is_card_write_protected(s_host_ctx.hal.dev, slot)) {
return ESP_ERR_INVALID_STATE;
}
/* Outputs should be synchronized to cclk_out */
@ -434,12 +374,23 @@ esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg)
esp_err_t sdmmc_host_init(void)
{
if (s_intr_handle) {
if (s_host_ctx.intr_handle) {
return ESP_ERR_INVALID_STATE;
}
//enable bus clock for registers
#if SDMMC_CLK_NEEDS_RCC
SDMMC_RCC_ATOMIC() {
sdmmc_ll_enable_bus_clock(s_host_ctx.hal.dev, true);
sdmmc_ll_reset_register(s_host_ctx.hal.dev);
}
#else
periph_module_reset(PERIPH_SDMMC_MODULE);
periph_module_enable(PERIPH_SDMMC_MODULE);
#endif
//hal context init
sdmmc_hal_init(&s_host_ctx.hal);
// Enable clock to peripheral. Use smallest divider first.
sdmmc_host_set_clk_div(2);
@ -451,7 +402,7 @@ esp_err_t sdmmc_host_init(void)
return err;
}
ESP_LOGD(TAG, "peripheral version %"PRIx32", hardware config %08"PRIx32, SDMMC.verid, SDMMC.hcon);
ESP_LOGD(TAG, "peripheral version %"PRIx32", hardware config %08"PRIx32, SDMMC.verid, SDMMC.hcon.val);
// Clear interrupt status and set interrupt mask to known state
SDMMC.rintsts.val = 0xffffffff;
@ -459,23 +410,23 @@ esp_err_t sdmmc_host_init(void)
SDMMC.ctrl.int_enable = 0;
// Allocate event queue
s_event_queue = xQueueCreate(SDMMC_EVENT_QUEUE_LENGTH, sizeof(sdmmc_event_t));
if (!s_event_queue) {
s_host_ctx.event_queue = xQueueCreate(SDMMC_EVENT_QUEUE_LENGTH, sizeof(sdmmc_event_t));
if (!s_host_ctx.event_queue) {
return ESP_ERR_NO_MEM;
}
s_io_intr_event = xSemaphoreCreateBinary();
if (!s_io_intr_event) {
vQueueDelete(s_event_queue);
s_event_queue = NULL;
s_host_ctx.io_intr_event = xSemaphoreCreateBinary();
if (!s_host_ctx.io_intr_event) {
vQueueDelete(s_host_ctx.event_queue);
s_host_ctx.event_queue = NULL;
return ESP_ERR_NO_MEM;
}
// Attach interrupt handler
esp_err_t ret = esp_intr_alloc(ETS_SDIO_HOST_INTR_SOURCE, 0, &sdmmc_isr, s_event_queue, &s_intr_handle);
esp_err_t ret = esp_intr_alloc(ETS_SDIO_HOST_INTR_SOURCE, 0, &sdmmc_isr, s_host_ctx.event_queue, &s_host_ctx.intr_handle);
if (ret != ESP_OK) {
vQueueDelete(s_event_queue);
s_event_queue = NULL;
vSemaphoreDelete(s_io_intr_event);
s_io_intr_event = NULL;
vQueueDelete(s_host_ctx.event_queue);
s_host_ctx.event_queue = NULL;
vSemaphoreDelete(s_host_ctx.io_intr_event);
s_host_ctx.io_intr_event = NULL;
return ret;
}
// Enable interrupts
@ -498,23 +449,21 @@ esp_err_t sdmmc_host_init(void)
// Initialize transaction handler
ret = sdmmc_host_transaction_handler_init();
if (ret != ESP_OK) {
vQueueDelete(s_event_queue);
s_event_queue = NULL;
vSemaphoreDelete(s_io_intr_event);
s_io_intr_event = NULL;
esp_intr_free(s_intr_handle);
s_intr_handle = NULL;
vQueueDelete(s_host_ctx.event_queue);
s_host_ctx.event_queue = NULL;
vSemaphoreDelete(s_host_ctx.io_intr_event);
s_host_ctx.io_intr_event = NULL;
esp_intr_free(s_host_ctx.intr_handle);
s_host_ctx.intr_handle = NULL;
return ret;
}
return ESP_OK;
}
#ifdef SOC_SDMMC_USE_IOMUX
static void configure_pin_iomux(uint8_t gpio_num)
{
const int sdmmc_func = 3;
const int sdmmc_func = SDMMC_LL_IOMUX_FUNC;
const int drive_strength = 3;
assert(gpio_num != (uint8_t) GPIO_NUM_NC);
gpio_pulldown_dis(gpio_num);
@ -526,8 +475,6 @@ static void configure_pin_iomux(uint8_t gpio_num)
PIN_SET_DRV(reg, drive_strength);
}
#elif SOC_SDMMC_USE_GPIO_MATRIX
static void configure_pin_gpio_matrix(uint8_t gpio_num, uint8_t gpio_matrix_sig, gpio_mode_t mode, const char *name)
{
assert (gpio_num != (uint8_t) GPIO_NUM_NC);
@ -543,11 +490,30 @@ static void configure_pin_gpio_matrix(uint8_t gpio_num, uint8_t gpio_matrix_sig,
}
}
#endif // SOC_SDMMC_USE_{IOMUX,GPIO_MATRIX}
static void configure_pin(uint8_t gpio_num, uint8_t gpio_matrix_sig, gpio_mode_t mode, const char *name, bool use_gpio_matrix)
{
if (use_gpio_matrix) {
configure_pin_gpio_matrix(gpio_num, gpio_matrix_sig, mode, name);
} else {
configure_pin_iomux(gpio_num);
}
}
//True: pins are all not set; False: one or more pins are set
static bool s_check_pin_not_set(const sdmmc_slot_config_t *slot_config)
{
#if SOC_SDMMC_USE_GPIO_MATRIX
bool pin_not_set = !slot_config->clk && !slot_config->cmd && !slot_config->d0 && !slot_config->d1 && !slot_config->d2 &&
!slot_config->d3 && !slot_config->d4 && !slot_config->d5 && !slot_config->d6 && !slot_config->d7;
return pin_not_set;
#else
return true;
#endif
}
esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
{
if (!s_intr_handle) {
if (!s_host_ctx.intr_handle) {
return ESP_ERR_INVALID_STATE;
}
if (!(slot == 0 || slot == 1)) {
@ -569,57 +535,87 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
} else if (slot_width > slot_info->width) {
return ESP_ERR_INVALID_ARG;
}
s_slot_width[slot] = slot_width;
s_host_ctx.slot_ctx[slot].slot_width = slot_width;
bool pin_not_set = s_check_pin_not_set(slot_config);
//SD driver behaviour is: all pins not defined == using iomux
bool use_gpio_matrix = !pin_not_set;
if (slot == 0) {
#if !SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(0)
ESP_RETURN_ON_FALSE(!use_gpio_matrix, ESP_ERR_INVALID_ARG, TAG, "doesn't support routing from GPIO matrix, driver uses dedicated IOs");
#endif
} else {
#if !SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(1)
ESP_RETURN_ON_FALSE(!use_gpio_matrix, ESP_ERR_INVALID_ARG, TAG, "doesn't support routing from GPIO matrix, driver uses dedicated IOs");
#endif
}
s_host_ctx.slot_ctx[slot].use_gpio_matrix = use_gpio_matrix;
#if SOC_SDMMC_USE_GPIO_MATRIX
/* Save pin configuration for this slot */
s_sdmmc_slot_gpio_num[slot].clk = slot_config->clk;
s_sdmmc_slot_gpio_num[slot].cmd = slot_config->cmd;
s_sdmmc_slot_gpio_num[slot].d0 = slot_config->d0;
/* Save d1 even in 1-line mode, it might be needed for SDIO INT line */
s_sdmmc_slot_gpio_num[slot].d1 = slot_config->d1;
if (slot_width >= 4) {
s_sdmmc_slot_gpio_num[slot].d2 = slot_config->d2;
if (use_gpio_matrix) {
/* Save pin configuration for this slot */
s_host_ctx.slot_ctx[slot].slot_gpio_num.clk = slot_config->clk;
s_host_ctx.slot_ctx[slot].slot_gpio_num.cmd = slot_config->cmd;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d0 = slot_config->d0;
/* Save d1 even in 1-line mode, it might be needed for SDIO INT line */
s_host_ctx.slot_ctx[slot].slot_gpio_num.d1 = slot_config->d1;
if (slot_width >= 4) {
s_host_ctx.slot_ctx[slot].slot_gpio_num.d2 = slot_config->d2;
}
/* Save d3 even for 1-line mode, as it needs to be set high */
s_host_ctx.slot_ctx[slot].slot_gpio_num.d3 = slot_config->d3;
if (slot_width >= 8) {
s_host_ctx.slot_ctx[slot].slot_gpio_num.d4 = slot_config->d4;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d5 = slot_config->d5;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d6 = slot_config->d6;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d7 = slot_config->d7;
}
} else
#endif //#if SOC_SDMMC_USE_GPIO_MATRIX
{
/* init pin configuration for this slot */
s_host_ctx.slot_ctx[slot].slot_gpio_num.clk = sdmmc_slot_gpio_num[slot].clk;
s_host_ctx.slot_ctx[slot].slot_gpio_num.cmd = sdmmc_slot_gpio_num[slot].cmd;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d0 = sdmmc_slot_gpio_num[slot].d0;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d1 = sdmmc_slot_gpio_num[slot].d1;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d2 = sdmmc_slot_gpio_num[slot].d2;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d3 = sdmmc_slot_gpio_num[slot].d3;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d4 = sdmmc_slot_gpio_num[slot].d4;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d5 = sdmmc_slot_gpio_num[slot].d5;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d6 = sdmmc_slot_gpio_num[slot].d6;
s_host_ctx.slot_ctx[slot].slot_gpio_num.d7 = sdmmc_slot_gpio_num[slot].d7;
}
/* Save d3 even for 1-line mode, as it needs to be set high */
s_sdmmc_slot_gpio_num[slot].d3 = slot_config->d3;
if (slot_width >= 8) {
s_sdmmc_slot_gpio_num[slot].d4 = slot_config->d4;
s_sdmmc_slot_gpio_num[slot].d5 = slot_config->d5;
s_sdmmc_slot_gpio_num[slot].d6 = slot_config->d6;
s_sdmmc_slot_gpio_num[slot].d7 = slot_config->d7;
}
#endif
bool pullup = slot_config->flags & SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
if (pullup) {
sdmmc_host_pullup_en_internal(slot, slot_config->width);
}
configure_pin(clk, slot, GPIO_MODE_OUTPUT);
configure_pin(cmd, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(d0, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.clk, sdmmc_slot_gpio_sig[slot].clk, GPIO_MODE_OUTPUT, "clk", use_gpio_matrix);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.cmd, sdmmc_slot_gpio_sig[slot].cmd, GPIO_MODE_INPUT_OUTPUT, "cmd", use_gpio_matrix);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d0, sdmmc_slot_gpio_sig[slot].d0, GPIO_MODE_INPUT_OUTPUT, "d0", use_gpio_matrix);
if (slot_width >= 4) {
configure_pin(d1, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(d2, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d1, sdmmc_slot_gpio_sig[slot].d1, GPIO_MODE_INPUT_OUTPUT, "d1", use_gpio_matrix);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d2, sdmmc_slot_gpio_sig[slot].d2, GPIO_MODE_INPUT_OUTPUT, "d2", use_gpio_matrix);
// Force D3 high to make slave enter SD mode.
// Connect to peripheral after width configuration.
gpio_config_t gpio_conf = {
.pin_bit_mask = BIT64(GPIO_NUM(slot, d3)),
.pin_bit_mask = BIT64(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = 0,
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&gpio_conf);
gpio_set_level(GPIO_NUM(slot, d3), 1);
gpio_set_level(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3, 1);
}
if (slot_width == 8) {
configure_pin(d4, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(d5, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(d6, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(d7, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d4, sdmmc_slot_gpio_sig[slot].d4, GPIO_MODE_INPUT_OUTPUT, "d4", use_gpio_matrix);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d5, sdmmc_slot_gpio_sig[slot].d5, GPIO_MODE_INPUT_OUTPUT, "d5", use_gpio_matrix);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d6, sdmmc_slot_gpio_sig[slot].d6, GPIO_MODE_INPUT_OUTPUT, "d6", use_gpio_matrix);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d7, sdmmc_slot_gpio_sig[slot].d7, GPIO_MODE_INPUT_OUTPUT, "d7", use_gpio_matrix);
}
// SDIO slave interrupt is edge sensitive to ~(int_n | card_int | card_detect)
@ -671,18 +667,26 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
esp_err_t sdmmc_host_deinit(void)
{
if (!s_intr_handle) {
if (!s_host_ctx.intr_handle) {
return ESP_ERR_INVALID_STATE;
}
esp_intr_free(s_intr_handle);
s_intr_handle = NULL;
vQueueDelete(s_event_queue);
s_event_queue = NULL;
vQueueDelete(s_io_intr_event);
s_io_intr_event = NULL;
sdmmc_host_input_clk_disable();
esp_intr_free(s_host_ctx.intr_handle);
s_host_ctx.intr_handle = NULL;
vQueueDelete(s_host_ctx.event_queue);
s_host_ctx.event_queue = NULL;
vQueueDelete(s_host_ctx.io_intr_event);
s_host_ctx.io_intr_event = NULL;
sdmmc_ll_deinit_clk(s_host_ctx.hal.dev);
sdmmc_host_transaction_handler_deinit();
#if SDMMC_CLK_NEEDS_RCC
//disable bus clock for registers
SDMMC_RCC_ATOMIC() {
sdmmc_ll_enable_bus_clock(s_host_ctx.hal.dev, false);
}
#else
periph_module_disable(PERIPH_SDMMC_MODULE);
#endif
return ESP_OK;
}
@ -691,10 +695,10 @@ esp_err_t sdmmc_host_wait_for_event(int tick_count, sdmmc_event_t *out_event)
if (!out_event) {
return ESP_ERR_INVALID_ARG;
}
if (!s_event_queue) {
if (!s_host_ctx.event_queue) {
return ESP_ERR_INVALID_STATE;
}
int ret = xQueueReceive(s_event_queue, out_event, tick_count);
int ret = xQueueReceive(s_host_ctx.event_queue, out_event, tick_count);
if (ret == pdFALSE) {
return ESP_ERR_TIMEOUT;
}
@ -717,11 +721,11 @@ esp_err_t sdmmc_host_set_bus_width(int slot, size_t width)
SDMMC.ctype.card_width_8 &= ~mask;
SDMMC.ctype.card_width |= mask;
// D3 was set to GPIO high to force slave into SD mode, until 4-bit mode is set
configure_pin(d3, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", s_host_ctx.slot_ctx[slot].use_gpio_matrix);
} else if (width == 8) {
SDMMC.ctype.card_width_8 |= mask;
// D3 was set to GPIO high to force slave into SD mode, until 4-bit mode is set
configure_pin(d3, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", s_host_ctx.slot_ctx[slot].use_gpio_matrix);
} else {
return ESP_ERR_INVALID_ARG;
}
@ -732,7 +736,7 @@ esp_err_t sdmmc_host_set_bus_width(int slot, size_t width)
size_t sdmmc_host_get_slot_width(int slot)
{
assert( slot == 0 || slot == 1 );
return s_slot_width[slot];
return s_host_ctx.slot_ctx[slot].slot_width;
}
esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled)
@ -740,19 +744,13 @@ 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) {
if (s_host_ctx.slot_ctx[slot].slot_width == 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;
}
sdmmc_ll_enable_ddr_mode(s_host_ctx.hal.dev, slot, ddr_enabled);
ESP_LOGD(TAG, "slot=%d ddr=%d", slot, ddr_enabled ? 1 : 0);
return ESP_OK;
}
@ -763,9 +761,9 @@ esp_err_t sdmmc_host_set_cclk_always_on(int slot, bool cclk_always_on)
return ESP_ERR_INVALID_ARG;
}
if (cclk_always_on) {
SDMMC.clkena.cclk_low_power &= ~BIT(slot);
sdmmc_ll_enable_card_clock_low_power(s_host_ctx.hal.dev, slot, false);
} else {
SDMMC.clkena.cclk_low_power |= BIT(slot);
sdmmc_ll_enable_card_clock_low_power(s_host_ctx.hal.dev, slot, true);
}
sdmmc_host_clock_update_command(slot);
return ESP_OK;
@ -792,21 +790,18 @@ void sdmmc_host_dma_stop(void)
void sdmmc_host_dma_prepare(sdmmc_desc_t *desc, size_t block_size, size_t data_size)
{
// Set size of data and DMA descriptor pointer
SDMMC.bytcnt = data_size;
SDMMC.blksiz = block_size;
SDMMC.dbaddr = desc;
sdmmc_ll_set_data_transfer_len(s_host_ctx.hal.dev, data_size);
sdmmc_ll_set_block_size(s_host_ctx.hal.dev, block_size);
sdmmc_ll_set_desc_addr(s_host_ctx.hal.dev, (uint32_t)desc);
// Enable everything needed to use DMA
SDMMC.ctrl.dma_enable = 1;
SDMMC.ctrl.use_internal_dma = 1;
SDMMC.bmod.enable = 1;
SDMMC.bmod.fb = 1;
sdmmc_ll_enable_dma(s_host_ctx.hal.dev, true);
sdmmc_host_dma_resume();
}
void sdmmc_host_dma_resume(void)
{
SDMMC.pldmnd = 1;
sdmmc_ll_poll_demand(s_host_ctx.hal.dev);
}
bool sdmmc_host_card_busy(void)
@ -816,7 +811,7 @@ bool sdmmc_host_card_busy(void)
esp_err_t sdmmc_host_io_int_enable(int slot)
{
configure_pin(d1, slot, GPIO_MODE_INPUT_OUTPUT);
configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d1, sdmmc_slot_gpio_sig[slot].d1, GPIO_MODE_INPUT_OUTPUT, "d1", s_host_ctx.slot_ctx[slot].use_gpio_matrix);
return ESP_OK;
}
@ -829,19 +824,32 @@ esp_err_t sdmmc_host_io_int_wait(int slot, TickType_t timeout_ticks)
* (in SDIO sense) has occurred and we don't need to use SDMMC peripheral
* interrupt.
*/
assert(slot == 0 || slot == 1);
SDMMC.intmask.sdio &= ~BIT(slot); /* Disable SDIO interrupt */
SDMMC.rintsts.sdio = BIT(slot);
if (gpio_get_level(GPIO_NUM(slot, d1)) == 0) {
/* Disable SDIO interrupt */
if (slot == 0) {
sdmmc_ll_enable_interrupt(s_host_ctx.hal.dev, SDMMC_INTMASK_IO_SLOT0, false);
sdmmc_ll_clear_interrupt(s_host_ctx.hal.dev, SDMMC_INTMASK_IO_SLOT0);
} else {
sdmmc_ll_enable_interrupt(s_host_ctx.hal.dev, SDMMC_INTMASK_IO_SLOT1, false);
sdmmc_ll_clear_interrupt(s_host_ctx.hal.dev, SDMMC_INTMASK_IO_SLOT1);
}
if (gpio_get_level(s_host_ctx.slot_ctx[slot].slot_gpio_num.d1) == 0) {
return ESP_OK;
}
/* Otherwise, need to wait for an interrupt. Since D1 was high,
* SDMMC peripheral interrupt is guaranteed to trigger on negedge.
*/
xSemaphoreTake(s_io_intr_event, 0);
SDMMC.intmask.sdio |= BIT(slot); /* Re-enable SDIO interrupt */
xSemaphoreTake(s_host_ctx.io_intr_event, 0);
/* Re-enable SDIO interrupt */
if (slot == 0) {
sdmmc_ll_enable_interrupt(s_host_ctx.hal.dev, SDMMC_INTMASK_IO_SLOT0, true);
} else {
sdmmc_ll_enable_interrupt(s_host_ctx.hal.dev, SDMMC_INTMASK_IO_SLOT1, true);
}
if (xSemaphoreTake(s_io_intr_event, timeout_ticks) == pdTRUE) {
if (xSemaphoreTake(s_host_ctx.io_intr_event, timeout_ticks) == pdTRUE) {
return ESP_OK;
} else {
return ESP_ERR_TIMEOUT;
@ -870,7 +878,7 @@ static void sdmmc_isr(void *arg)
sdmmc_event_t event;
int higher_priority_task_awoken = pdFALSE;
uint32_t pending = SDMMC.mintsts.val & 0xFFFF;
uint32_t pending = sdmmc_ll_get_intr_status(s_host_ctx.hal.dev) & 0xFFFF;
SDMMC.rintsts.val = pending;
event.sdmmc_status = pending;
@ -882,11 +890,11 @@ static void sdmmc_isr(void *arg)
xQueueSendFromISR(queue, &event, &higher_priority_task_awoken);
}
uint32_t sdio_pending = SDMMC.mintsts.sdio;
uint32_t sdio_pending = (sdmmc_ll_get_intr_status(s_host_ctx.hal.dev) & (SDMMC_INTMASK_IO_SLOT1 | SDMMC_INTMASK_IO_SLOT0));
if (sdio_pending) {
// disable the interrupt (no need to clear here, this is done in sdmmc_host_io_int_wait)
SDMMC.intmask.sdio &= ~sdio_pending;
xSemaphoreGiveFromISR(s_io_intr_event, &higher_priority_task_awoken);
sdmmc_ll_enable_interrupt(s_host_ctx.hal.dev, sdio_pending, false);
xSemaphoreGiveFromISR(s_host_ctx.io_intr_event, &higher_priority_task_awoken);
}
if (higher_priority_task_awoken == pdTRUE) {
@ -901,18 +909,18 @@ static esp_err_t sdmmc_host_pullup_en_internal(int slot, int width)
return ESP_ERR_INVALID_ARG;
}
// according to the spec, the host controls the clk, we don't to pull it up here
gpio_pullup_en(GPIO_NUM(slot, cmd));
gpio_pullup_en(GPIO_NUM(slot, d0));
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.cmd);
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.d0);
if (width >= 4) {
gpio_pullup_en(GPIO_NUM(slot, d1));
gpio_pullup_en(GPIO_NUM(slot, d2));
gpio_pullup_en(GPIO_NUM(slot, d3));
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.d1);
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.d2);
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3);
}
if (width == 8) {
gpio_pullup_en(GPIO_NUM(slot, d4));
gpio_pullup_en(GPIO_NUM(slot, d5));
gpio_pullup_en(GPIO_NUM(slot, d6));
gpio_pullup_en(GPIO_NUM(slot, d7));
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.d4);
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.d5);
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.d6);
gpio_pullup_en(s_host_ctx.slot_ctx[slot].slot_gpio_num.d7);
}
return ESP_OK;
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,6 +7,7 @@
#include <string.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_pm.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
@ -18,7 +19,10 @@
#include "driver/sdmmc_defs.h"
#include "driver/sdmmc_host.h"
#include "esp_timer.h"
#include "esp_cache.h"
#include "esp_private/esp_cache_private.h"
#include "sdmmc_private.h"
#include "soc/soc_caps.h"
/* Number of DMA descriptors used for transfer.
@ -27,6 +31,14 @@
*/
#define SDMMC_DMA_DESC_CNT 4
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#define SDMMC_ALIGN_ATTR __attribute__((aligned(CONFIG_CACHE_L1_CACHE_LINE_SIZE)))
#else
#define SDMMC_ALIGN_ATTR
#endif
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
static const char* TAG = "sdmmc_req";
typedef enum {
@ -57,7 +69,7 @@ const uint32_t SDMMC_CMD_ERR_MASK =
SDMMC_INTMASK_RCRC |
SDMMC_INTMASK_RESP_ERR;
static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT];
SDMMC_ALIGN_ATTR static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT];
static sdmmc_transfer_state_t s_cur_transfer = { 0 };
static QueueHandle_t s_request_mutex;
static bool s_is_app_cmd; // This flag is set if the next command is an APP command
@ -113,6 +125,12 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(s_pm_lock);
#endif
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
// cache sync related
size_t cache_sync_len = 0;
#endif
// dispose of any events which happened asynchronously
handle_idle_state_events();
// convert cmdinfo to hardware register value
@ -131,6 +149,14 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
ret = ESP_ERR_INVALID_ARG;
goto out;
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = cmdinfo->buflen;
ret = esp_cache_msync((void *)cmdinfo->data, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
if (ret != ESP_OK) {
goto out;
}
#endif
// this clears "owned by IDMAC" bits
memset(s_dma_desc, 0, sizeof(s_dma_desc));
// initialize first descriptor
@ -167,6 +193,15 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
}
s_is_app_cmd = (ret == ESP_OK && cmdinfo->opcode == MMC_APP_CMD);
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
if (cmdinfo->data) {
ret = esp_cache_msync((void *)cmdinfo->data, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
if (ret != ESP_OK) {
goto out;
}
}
#endif
out:
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(s_pm_lock);
@ -185,6 +220,10 @@ static size_t get_free_descriptors_count(void)
*/
for (size_t i = 0; i < SDMMC_DMA_DESC_CNT; ++i) {
sdmmc_desc_t* desc = &s_dma_desc[(next + i) % SDMMC_DMA_DESC_CNT];
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
if (desc->owned_by_idmac) {
break;
}
@ -224,6 +263,10 @@ static void fill_dma_descriptors(size_t num_desc)
ESP_LOGV(TAG, "fill %d desc=%d rem=%d next=%d last=%d sz=%d",
num_desc, next, s_cur_transfer.size_remaining,
s_cur_transfer.next_desc, desc->last_descriptor, desc->buffer1_size);
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
}
}

View File

@ -9,6 +9,7 @@
#include "esp_err.h"
#include "esp_check.h"
#include "soc/rtc.h"
#include "hal/clk_tree_ll.h"
static const char *TAG = "esp_clk_tree";
@ -31,6 +32,9 @@ uint32_t *freq_value)
case SOC_MOD_CLK_LP_PLL:
clk_src_freq = 8 * MHZ;
break;
case SOC_MOD_CLK_PLL_F160M:
clk_src_freq = CLK_LL_PLL_160M_FREQ_MHZ * MHZ;
break;
default:
break;
}

View File

@ -131,6 +131,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "sdm_hal.c")
endif()
if(CONFIG_SOC_SDMMC_HOST_SUPPORTED)
list(APPEND srcs "sdmmc_hal.c")
endif()
if(CONFIG_ETH_USE_ESP32_EMAC)
list(APPEND srcs "emac_hal.c")
endif()

View File

@ -0,0 +1,360 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_bit_defs.h"
#include "hal/assert.h"
#include "soc/clk_tree_defs.h"
#include "soc/sdmmc_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SDMMC_LL_GET_HW(id) (((id) == 0) ? (&SDMMC) : NULL)
/**
* SDMMC capabilities
*/
#define SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(SLOT_ID) 0
#define SDMMC_LL_IOMUX_FUNC 3
typedef enum {
SDMMC_LL_DELAY_PHASE_0,
SDMMC_LL_DELAY_PHASE_1,
SDMMC_LL_DELAY_PHASE_2,
SDMMC_LL_DELAY_PHASE_3,
} sdmmc_ll_delay_phase_t;
/**
* @brief Select SDMMC clock source
*
* @param hw hardware instance address
* @param clk_src clock source, see valid sources in type `soc_periph_psram_clk_src_t`
*/
static inline void sdmmc_ll_select_clk_source(sdmmc_dev_t *hw, soc_periph_sdmmc_clk_src_t clk_src)
{
//leave for compatibility
}
/**
* @brief Set SDMMC clock div
*
* @param hw hardware instance address
* @param div divider value
*/
static inline void sdmmc_ll_set_clock_div(sdmmc_dev_t *hw, uint32_t div)
{
/**
* Set frequency to 160MHz / div
*
* n: counter resets at div_factor_n.
* l: negedge when counter equals div_factor_l.
* h: posedge when counter equals div_factor_h.
*
* We set the duty cycle to 1/2
*/
HAL_ASSERT(div > 1 && div <= 16);
int h = div - 1;
int l = div / 2 - 1;
hw->clock.div_factor_h = h;
hw->clock.div_factor_l = l;
hw->clock.div_factor_n = h;
}
/**
* @brief Deinit clock
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_deinit_clk(sdmmc_dev_t *hw)
{
hw->clock.val = 0;
}
/**
* @brief Get SDMMC clock div
*
* @param hw hardware instance address
*
* @return Divider value
*/
static inline uint32_t sdmmc_ll_get_clock_div(sdmmc_dev_t *hw)
{
return hw->clock.div_factor_h + 1;
}
/**
* @brief Initialise the din, dout, self delay phase
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_init_phase_delay(sdmmc_dev_t *hw)
{
// 180 degree phase on input and output clocks
hw->clock.phase_dout = 4;
hw->clock.phase_din = 4;
hw->clock.phase_core = 0;
}
/**
* @brief Enable card clock
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_card_clock(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->clkena.cclk_enable |= BIT(slot);
} else {
hw->clkena.cclk_enable &= ~BIT(slot);
}
}
/**
* @brief Set card clock div
*
* @param hw hardware instance address
* @param slot slot
* @param card_div divider value
*/
static inline void sdmmc_ll_set_card_clock_div(sdmmc_dev_t *hw, uint32_t slot, uint32_t card_div)
{
if (slot == 0) {
hw->clksrc.card0 = 0;
hw->clkdiv.div0 = card_div;
} else if (slot == 1) {
hw->clksrc.card1 = 1;
hw->clkdiv.div1 = card_div;
} else {
HAL_ASSERT(false);
}
}
/**
* @brief Get card clock div
*
* @param hw hardware instance address
* @param slot slot
*
* @return Divider value
*/
static inline uint32_t sdmmc_ll_get_card_clock_div(sdmmc_dev_t *hw, uint32_t slot)
{
uint32_t card_div = 0;
if (slot == 0) {
HAL_ASSERT(hw->clksrc.card0 = 0);
card_div = hw->clkdiv.div0;
} else if (slot == 1) {
HAL_ASSERT(hw->clksrc.card1 = 1);
card_div = hw->clkdiv.div1;
} else {
HAL_ASSERT(false);
}
return card_div;
}
/**
* @brief Disable clock when the card is in IDLE state
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_card_clock_low_power(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->clkena.cclk_low_power |= BIT(slot);
} else {
hw->clkena.cclk_low_power &= ~BIT(slot);
}
}
/**
* @brief Set card data read timeout cycles
*
* @param hw hardware instance address
* @param timeout_cycles timeout cycles
*/
static inline void sdmmc_ll_set_data_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles)
{
if (timeout_cycles > 0xffffff) {
timeout_cycles = 0xffffff;
}
hw->tmout.data = timeout_cycles;
}
/**
* @brief Set response timeout cycles (in card output clocks)
*
* @param hw hardware instance address
* @param timeout_cycles timeout cycles
*/
static inline void sdmmc_ll_set_response_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles)
{
hw->tmout.response = timeout_cycles;
}
/**
* @brief Check if card is detected
*
* @param hw hardware instance address
* @param slot slot
*
* @return True for detected
*/
static inline bool sdmmc_ll_is_card_detected(sdmmc_dev_t *hw, uint32_t slot)
{
return ((hw->cdetect.cards & BIT(slot)) == 0);
}
/**
* @brief Check if card is write protected
*
* @param hw hardware instance address
* @param slot slot
*
* @return True for write protected
*/
static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t slot)
{
bool is_protected = hw->wrtprt.cards & BIT(slot);
return is_protected;
}
/**
* @brief Enable DDR mode
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_ddr_mode(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->uhs.ddr |= BIT(slot);
hw->emmc_ddr_reg |= BIT(slot);
} else {
hw->uhs.ddr &= ~BIT(slot);
hw->emmc_ddr_reg &= ~BIT(slot);
}
}
/**
* @brief Set data transfer length
*
* @param hw hardware instance address
* @param len length
*/
static inline void sdmmc_ll_set_data_transfer_len(sdmmc_dev_t *hw, uint32_t len)
{
hw->bytcnt = len;
}
/**
* @brief Set block size
*
* @param hw hardware instance address
* @param block_size block size
*/
static inline void sdmmc_ll_set_block_size(sdmmc_dev_t *hw, uint32_t block_size)
{
hw->blksiz = block_size;
}
/**
* @brief Set descriptor addr
*
* @param hw hardware instance address
* @param block_size block size
*/
static inline void sdmmc_ll_set_desc_addr(sdmmc_dev_t *hw, uint32_t desc_addr)
{
hw->dbaddr = (sdmmc_desc_t *)desc_addr;
}
/**
* @brief Enable DMA
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_dma(sdmmc_dev_t *hw, bool en)
{
hw->ctrl.dma_enable = en;
hw->ctrl.use_internal_dma = en;
hw->bmod.enable = en;
hw->bmod.fb = en;
}
/**
* @brief Poll demand
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_poll_demand(sdmmc_dev_t *hw)
{
hw->pldmnd = 1;
}
/**
* @brief Get interrupt status
*
* @param hw hardware instance address
*/
static inline uint32_t sdmmc_ll_get_intr_status(sdmmc_dev_t *hw)
{
return hw->mintsts.val;
}
/**
* @brief Enable interrupt
*
* @param hw hardware instance address
* @param mask interrupt mask
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_interrupt(sdmmc_dev_t *hw, uint32_t mask, bool en)
{
if (en) {
hw->intmask.val |= mask;
} else {
hw->intmask.val &= ~mask;
}
}
/**
* @brief Clear interrupt
*
* @param hw hardware instance address
* @param mask interrupt mask
*/
static inline void sdmmc_ll_clear_interrupt(sdmmc_dev_t *hw, uint32_t mask)
{
hw->rintsts.val = mask;
}
#ifdef __cplusplus
}
#endif

View File

@ -154,8 +154,6 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en
return ret;
case PERIPH_ECDSA_MODULE:
return HP_SYS_CLKRST_REG_RST_EN_CRYPTO | HP_SYS_CLKRST_REG_RST_EN_ECDSA;
case PERIPH_SDMMC_MODULE:
return LP_CLKRST_RST_EN_SDMMC;
case PERIPH_EMAC_MODULE:
return LP_CLKRST_RST_EN_EMAC;
default:
@ -237,7 +235,6 @@ static inline uint32_t periph_ll_get_rst_en_reg(periph_module_t periph)
case PERIPH_SHA_MODULE:
case PERIPH_ECDSA_MODULE:
return HP_SYS_CLKRST_HP_RST_EN2_REG;
case PERIPH_SDMMC_MODULE:
case PERIPH_EMAC_MODULE:
return LP_CLKRST_HP_SDMMC_EMAC_RST_CTRL_REG;
default:

View File

@ -0,0 +1,444 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_bit_defs.h"
#include "hal/assert.h"
#include "soc/clk_tree_defs.h"
#include "soc/sdmmc_struct.h"
#include "soc/hp_sys_clkrst_struct.h"
#include "soc/lp_clkrst_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SDMMC_LL_GET_HW(id) (((id) == 0) ? (&SDMMC) : NULL)
/**
* SDMMC capabilities
*/
#define SDMMC_LL_MAX_FREQ_KHZ_FPGA (4*1000)
#define SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(SLOT_ID) ((SLOT_ID == 0) ? 0 : 1)
#define SDMMC_LL_IOMUX_FUNC 0
typedef enum {
SDMMC_LL_DELAY_PHASE_0,
SDMMC_LL_DELAY_PHASE_1,
SDMMC_LL_DELAY_PHASE_2,
SDMMC_LL_DELAY_PHASE_3,
} sdmmc_ll_delay_phase_t;
/**
* @brief Enable the bus clock for SDMMC module
*
* @param hw hardware instance address
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_bus_clock(sdmmc_dev_t *hw, bool en)
{
HP_SYS_CLKRST.soc_clk_ctrl1.reg_sdmmc_sys_clk_en = en;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define sdmmc_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_enable_bus_clock(__VA_ARGS__)
/**
* @brief Reset the SDMMC module
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_reset_register(sdmmc_dev_t *hw)
{
LP_AON_CLKRST.hp_sdmmc_emac_rst_ctrl.rst_en_sdmmc = 1;
LP_AON_CLKRST.hp_sdmmc_emac_rst_ctrl.rst_en_sdmmc = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define sdmmc_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_reset_register(__VA_ARGS__)
/**
* @brief Select SDMMC clock source
*
* @param hw hardware instance address
* @param clk_src clock source, see valid sources in type `soc_periph_psram_clk_src_t`
*/
static inline void sdmmc_ll_select_clk_source(sdmmc_dev_t *hw, soc_periph_sdmmc_clk_src_t clk_src)
{
uint32_t clk_val = 0;
switch (clk_src) {
case SDMMC_CLK_SRC_PLL160M:
clk_val = 0;
break;
case SDMMC_CLK_SRC_PLL200M:
clk_val = 1;
break;
default:
HAL_ASSERT(false);
break;
}
HP_SYS_CLKRST.peri_clk_ctrl01.reg_sdio_ls_clk_src_sel = clk_val;
HP_SYS_CLKRST.peri_clk_ctrl01.reg_sdio_ls_clk_en = true;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define sdmmc_ll_select_clk_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_select_clk_source(__VA_ARGS__)
/**
* @brief Set SDMMC clock div
*
* @param hw hardware instance address
* @param div divider value
*/
static inline void sdmmc_ll_set_clock_div(sdmmc_dev_t *hw, uint32_t div)
{
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_h = div / 2 - 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_n = div - 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_l = div - 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_cfg_update = 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_cfg_update = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define sdmmc_ll_set_clock_div(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_set_clock_div(__VA_ARGS__)
/**
* @brief Deinit clock
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_deinit_clk(sdmmc_dev_t *hw)
{
hw->clk_edge_sel.val = 0;
}
/**
* @brief Get SDMMC clock div
*
* @param hw hardware instance address
*
* @return Divider value
*/
static inline uint32_t sdmmc_ll_get_clock_div(sdmmc_dev_t *hw)
{
return HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_l + 1;
}
/**
* @brief Initialise the din, dout, self delay phase
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_init_phase_delay(sdmmc_dev_t *hw)
{
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_drv_clk_en = 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_sam_clk_en = 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_slf_clk_en = 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_drv_clk_edge_sel = 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_sam_clk_edge_sel = 0;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_slf_clk_edge_sel = 0;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_cfg_update = 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_cfg_update = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define sdmmc_ll_init_phase_delay(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_init_phase_delay(__VA_ARGS__)
/**
* @brief Set SDMMC din delay
*
* @param hw hardware instance address
* @param phase delay phase
*/
static inline void sdmmc_ll_set_din_delay(sdmmc_dev_t *hw, sdmmc_ll_delay_phase_t phase)
{
switch (phase) {
case SDMMC_LL_DELAY_PHASE_1:
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_sam_clk_edge_sel = 0x1;
break;
case SDMMC_LL_DELAY_PHASE_2:
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_sam_clk_edge_sel = 0x2;
break;
case SDMMC_LL_DELAY_PHASE_3:
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_sam_clk_edge_sel = 0x3;
break;
default:
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_sam_clk_edge_sel = 0x0;
break;
}
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define sdmmc_ll_set_din_delay(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_set_din_delay(__VA_ARGS__)
/**
* @brief Enable card clock
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_card_clock(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->clkena.cclk_enable |= BIT(slot);
} else {
hw->clkena.cclk_enable &= ~BIT(slot);
}
}
/**
* @brief Set card clock div
*
* @param hw hardware instance address
* @param slot slot
* @param card_div divider value
*/
static inline void sdmmc_ll_set_card_clock_div(sdmmc_dev_t *hw, uint32_t slot, uint32_t card_div)
{
if (slot == 0) {
hw->clksrc.card0 = 0;
hw->clkdiv.clk_divider0 = card_div;
} else if (slot == 1) {
hw->clksrc.card1 = 1;
hw->clkdiv.clk_divider1 = card_div;
} else {
HAL_ASSERT(false);
}
}
/**
* @brief Get card clock div
*
* @param hw hardware instance address
* @param slot slot
*
* @return Divider value
*/
static inline uint32_t sdmmc_ll_get_card_clock_div(sdmmc_dev_t *hw, uint32_t slot)
{
uint32_t card_div = 0;
if (slot == 0) {
HAL_ASSERT(hw->clksrc.card0 = 0);
card_div = hw->clkdiv.clk_divider0;
} else if (slot == 1) {
HAL_ASSERT(hw->clksrc.card1 = 1);
card_div = hw->clkdiv.clk_divider1;
} else {
HAL_ASSERT(false);
}
return card_div;
}
/**
* @brief Disable clock when the card is in IDLE state
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_card_clock_low_power(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->clkena.lp_enable |= BIT(slot);
} else {
hw->clkena.lp_enable &= ~BIT(slot);
}
}
/**
* @brief Set card data read timeout cycles
*
* @param hw hardware instance address
* @param timeout_cycles timeout cycles
*/
static inline void sdmmc_ll_set_data_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles)
{
if (timeout_cycles > 0xffffff) {
timeout_cycles = 0xffffff;
}
hw->tmout.data_timeout = timeout_cycles;
}
/**
* @brief Set response timeout cycles (in card output clocks)
*
* @param hw hardware instance address
* @param timeout_cycles timeout cycles
*/
static inline void sdmmc_ll_set_response_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles)
{
hw->tmout.response_timeout = timeout_cycles;
}
/**
* @brief Check if card is detected
*
* @param hw hardware instance address
* @param slot slot
*
* @return True for detected
*/
static inline bool sdmmc_ll_is_card_detected(sdmmc_dev_t *hw, uint32_t slot)
{
return ((hw->cdetect.card_detect_n & BIT(slot)) == 0);
}
/**
* @brief Check if card is write protected
*
* @param hw hardware instance address
* @param slot slot
*
* @return True for write protected
*/
static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t slot)
{
bool is_protected = hw->wrtprt.write_protect & BIT(slot);
return is_protected;
}
/**
* @brief Enable DDR mode
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_ddr_mode(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->uhs.ddr |= BIT(slot);
hw->emmcddr.halfstartbit_reg |= BIT(slot);
} else {
hw->uhs.ddr &= ~BIT(slot);
hw->emmcddr.halfstartbit_reg &= ~BIT(slot);
}
}
/**
* @brief Set data transfer length
*
* @param hw hardware instance address
* @param len length
*/
static inline void sdmmc_ll_set_data_transfer_len(sdmmc_dev_t *hw, uint32_t len)
{
hw->bytcnt.byte_count = len;
}
/**
* @brief Set block size
*
* @param hw hardware instance address
* @param block_size block size
*/
static inline void sdmmc_ll_set_block_size(sdmmc_dev_t *hw, uint32_t block_size)
{
hw->blksiz.block_size = block_size;
}
/**
* @brief Set descriptor addr
*
* @param hw hardware instance address
* @param block_size block size
*/
static inline void sdmmc_ll_set_desc_addr(sdmmc_dev_t *hw, uint32_t desc_addr)
{
hw->dbaddr.dbaddr_reg = desc_addr;
}
/**
* @brief Enable DMA
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_dma(sdmmc_dev_t *hw, bool en)
{
hw->ctrl.dma_enable = en;
hw->ctrl.use_internal_dma = en;
hw->bmod.enable = en;
hw->bmod.fb = en;
}
/**
* @brief Poll demand
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_poll_demand(sdmmc_dev_t *hw)
{
hw->pldmnd.pldmnd_pd = 1;
}
/**
* @brief Get interrupt status
*
* @param hw hardware instance address
*/
static inline uint32_t sdmmc_ll_get_intr_status(sdmmc_dev_t *hw)
{
return hw->mintsts.val;
}
/**
* @brief Enable interrupt
*
* @param hw hardware instance address
* @param mask interrupt mask
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_interrupt(sdmmc_dev_t *hw, uint32_t mask, bool en)
{
if (en) {
hw->intmask.val |= mask;
} else {
hw->intmask.val &= ~mask;
}
}
/**
* @brief Clear interrupt
*
* @param hw hardware instance address
* @param mask interrupt mask
*/
static inline void sdmmc_ll_clear_interrupt(sdmmc_dev_t *hw, uint32_t mask)
{
hw->rintsts.val = mask;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,400 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_bit_defs.h"
#include "hal/assert.h"
#include "soc/clk_tree_defs.h"
#include "soc/sdmmc_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SDMMC_LL_GET_HW(id) (((id) == 0) ? (&SDMMC) : NULL)
/**
* SDMMC capabilities
*/
#define SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(SLOT_ID) 1
#define SDMMC_LL_IOMUX_FUNC -1
typedef enum {
SDMMC_LL_DELAY_PHASE_0,
SDMMC_LL_DELAY_PHASE_1,
SDMMC_LL_DELAY_PHASE_2,
SDMMC_LL_DELAY_PHASE_3,
} sdmmc_ll_delay_phase_t;
/**
* @brief Select SDMMC clock source
*
* @param hw hardware instance address
* @param clk_src clock source, see valid sources in type `soc_periph_psram_clk_src_t`
*/
static inline void sdmmc_ll_select_clk_source(sdmmc_dev_t *hw, soc_periph_sdmmc_clk_src_t clk_src)
{
uint32_t clk_val = 0;
switch (clk_src) {
case SDMMC_CLK_SRC_PLL160M:
clk_val = 1;
break;
case SDMMC_CLK_SRC_XTAL:
clk_val = 0;
break;
default:
HAL_ASSERT(false);
break;
}
hw->clock.clk_sel = clk_val;
}
/**
* @brief Set SDMMC clock div
*
* @param hw hardware instance address
* @param div divider value
*/
static inline void sdmmc_ll_set_clock_div(sdmmc_dev_t *hw, uint32_t div)
{
/**
* Set frequency to 160MHz / div
*
* n: counter resets at div_factor_n.
* l: negedge when counter equals div_factor_l.
* h: posedge when counter equals div_factor_h.
*
* We set the duty cycle to 1/2
*/
HAL_ASSERT(div > 1 && div <= 16);
int l = div - 1;
int h = div / 2 - 1;
hw->clock.div_factor_h = h;
hw->clock.div_factor_l = l;
hw->clock.div_factor_n = l;
}
/**
* @brief Deinit clock
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_deinit_clk(sdmmc_dev_t *hw)
{
hw->clock.val = 0;
}
/**
* @brief Get SDMMC clock div
*
* @param hw hardware instance address
*
* @return Divider value
*/
static inline uint32_t sdmmc_ll_get_clock_div(sdmmc_dev_t *hw)
{
return hw->clock.div_factor_l + 1;
}
/**
* @brief Initialise the din, dout, self delay phase
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_init_phase_delay(sdmmc_dev_t *hw)
{
hw->clock.phase_core = 0;
/* 90 deg. delay for cclk_out to satisfy large hold time for SDR12 (up to 25MHz) and SDR25 (up to 50MHz) modes.
* Whether this delayed clock will be used depends on use_hold_reg bit in CMD structure,
* determined when sending out the command.
*/
hw->clock.phase_dout = 1;
hw->clock.phase_din = 0;
}
/**
* @brief Set SDMMC din delay
*
* @param hw hardware instance address
* @param phase delay phase
*/
static inline void sdmmc_ll_set_din_delay(sdmmc_dev_t *hw, sdmmc_ll_delay_phase_t phase)
{
switch (phase) {
case SDMMC_LL_DELAY_PHASE_1:
hw->clock.phase_din = 0x1;
break;
case SDMMC_LL_DELAY_PHASE_2:
hw->clock.phase_din = 0x4;
break;
case SDMMC_LL_DELAY_PHASE_3:
hw->clock.phase_din = 0x6;
break;
default:
hw->clock.phase_din = 0x0;
break;
}
}
/**
* @brief Enable card clock
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_card_clock(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->clkena.cclk_enable |= BIT(slot);
} else {
hw->clkena.cclk_enable &= ~BIT(slot);
}
}
/**
* @brief Set card clock div
*
* @param hw hardware instance address
* @param slot slot
* @param card_div divider value
*/
static inline void sdmmc_ll_set_card_clock_div(sdmmc_dev_t *hw, uint32_t slot, uint32_t card_div)
{
if (slot == 0) {
hw->clksrc.card0 = 0;
hw->clkdiv.div0 = card_div;
} else if (slot == 1) {
hw->clksrc.card1 = 1;
hw->clkdiv.div1 = card_div;
} else {
HAL_ASSERT(false);
}
}
/**
* @brief Get card clock div
*
* @param hw hardware instance address
* @param slot slot
*
* @return Divider value
*/
static inline uint32_t sdmmc_ll_get_card_clock_div(sdmmc_dev_t *hw, uint32_t slot)
{
uint32_t card_div = 0;
if (slot == 0) {
HAL_ASSERT(hw->clksrc.card0 = 0);
card_div = hw->clkdiv.div0;
} else if (slot == 1) {
HAL_ASSERT(hw->clksrc.card1 = 1);
card_div = hw->clkdiv.div1;
} else {
HAL_ASSERT(false);
}
return card_div;
}
/**
* @brief Disable clock when the card is in IDLE state
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_card_clock_low_power(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->clkena.cclk_low_power |= BIT(slot);
} else {
hw->clkena.cclk_low_power &= ~BIT(slot);
}
}
/**
* @brief Set card data read timeout cycles
*
* @param hw hardware instance address
* @param timeout_cycles timeout cycles
*/
static inline void sdmmc_ll_set_data_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles)
{
if (timeout_cycles > 0xffffff) {
timeout_cycles = 0xffffff;
}
hw->tmout.data = timeout_cycles;
}
/**
* @brief Set response timeout cycles (in card output clocks)
*
* @param hw hardware instance address
* @param timeout_cycles timeout cycles
*/
static inline void sdmmc_ll_set_response_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles)
{
hw->tmout.response = timeout_cycles;
}
/**
* @brief Check if card is detected
*
* @param hw hardware instance address
* @param slot slot
*
* @return True for detected
*/
static inline bool sdmmc_ll_is_card_detected(sdmmc_dev_t *hw, uint32_t slot)
{
return ((hw->cdetect.cards & BIT(slot)) == 0);
}
/**
* @brief Check if card is write protected
*
* @param hw hardware instance address
* @param slot slot
*
* @return True for write protected
*/
static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t slot)
{
bool is_protected = hw->wrtprt.cards & BIT(slot);
return is_protected;
}
/**
* @brief Enable DDR mode
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_ddr_mode(sdmmc_dev_t *hw, uint32_t slot, bool en)
{
if (en) {
hw->uhs.ddr |= BIT(slot);
hw->emmc_ddr_reg |= BIT(slot);
} else {
hw->uhs.ddr &= ~BIT(slot);
hw->emmc_ddr_reg &= ~BIT(slot);
}
}
/**
* @brief Set data transfer length
*
* @param hw hardware instance address
* @param len length
*/
static inline void sdmmc_ll_set_data_transfer_len(sdmmc_dev_t *hw, uint32_t len)
{
hw->bytcnt = len;
}
/**
* @brief Set block size
*
* @param hw hardware instance address
* @param block_size block size
*/
static inline void sdmmc_ll_set_block_size(sdmmc_dev_t *hw, uint32_t block_size)
{
hw->blksiz = block_size;
}
/**
* @brief Set descriptor addr
*
* @param hw hardware instance address
* @param block_size block size
*/
static inline void sdmmc_ll_set_desc_addr(sdmmc_dev_t *hw, uint32_t desc_addr)
{
hw->dbaddr = (sdmmc_desc_t *)desc_addr;
}
/**
* @brief Enable DMA
*
* @param hw hardware instance address
* @param slot slot
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_dma(sdmmc_dev_t *hw, bool en)
{
hw->ctrl.dma_enable = en;
hw->ctrl.use_internal_dma = en;
hw->bmod.enable = en;
hw->bmod.fb = en;
}
/**
* @brief Poll demand
*
* @param hw hardware instance address
*/
static inline void sdmmc_ll_poll_demand(sdmmc_dev_t *hw)
{
hw->pldmnd = 1;
}
/**
* @brief Get interrupt status
*
* @param hw hardware instance address
*/
static inline uint32_t sdmmc_ll_get_intr_status(sdmmc_dev_t *hw)
{
return hw->mintsts.val;
}
/**
* @brief Enable interrupt
*
* @param hw hardware instance address
* @param mask interrupt mask
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_interrupt(sdmmc_dev_t *hw, uint32_t mask, bool en)
{
if (en) {
hw->intmask.val |= mask;
} else {
hw->intmask.val &= ~mask;
}
}
/**
* @brief Clear interrupt
*
* @param hw hardware instance address
* @param mask interrupt mask
*/
static inline void sdmmc_ll_clear_interrupt(sdmmc_dev_t *hw, uint32_t mask)
{
hw->rintsts.val = mask;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct sdmmc_dev_t *sdmmc_soc_handle_t;
/**
* @brief Context of the HAL
*/
typedef struct {
sdmmc_soc_handle_t dev; // SDMMC SOC layer handle (i.e. register base address)
} sdmmc_hal_context_t;
/**
* @brief Init the sdmmc hal context.
*
* @param hal Context of the HAL
*/
void sdmmc_hal_init(sdmmc_hal_context_t *hal);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include "hal/sdmmc_hal.h"
#include "hal/sdmmc_ll.h"
#include "soc/soc_caps.h"
void sdmmc_hal_init(sdmmc_hal_context_t *hal)
{
hal->dev = SDMMC_LL_GET_HW(0);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -324,18 +324,23 @@ 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)
{
size_t datalen = 8;
uint32_t* buf = (uint32_t*) heap_caps_malloc(datalen, MALLOC_CAP_DMA);
if (buf == NULL) {
return ESP_ERR_NO_MEM;
esp_err_t err = ESP_FAIL;
uint32_t *buf = NULL;
size_t actual_size = 0;
err = esp_dma_malloc(datalen, 0, (void *)&buf, &actual_size);
if (err != ESP_OK) {
return err;
}
sdmmc_command_t cmd = {
.data = buf,
.datalen = datalen,
.buflen = actual_size,
.blklen = datalen,
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
.opcode = SD_APP_SEND_SCR
};
esp_err_t err = sdmmc_send_app_cmd(card, &cmd);
err = sdmmc_send_app_cmd(card, &cmd);
if (err == ESP_OK) {
err = sdmmc_decode_scr(buf, out_scr);
}
@ -395,21 +400,24 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
esp_err_t err = ESP_OK;
size_t block_size = card->csd.sector_size;
if (esp_ptr_dma_capable(src) && (intptr_t)src % 4 == 0) {
err = sdmmc_write_sectors_dma(card, src, start_block, block_count);
if (esp_dma_is_buffer_aligned(src, block_size * block_count, ESP_DMA_BUF_LOCATION_INTERNAL)) {
err = sdmmc_write_sectors_dma(card, src, start_block, block_count, block_size * block_count);
} else {
// SDMMC peripheral needs DMA-capable buffers. Split the write into
// separate single block writes, if needed, and allocate a temporary
// DMA-capable buffer.
void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);
if (tmp_buf == NULL) {
return ESP_ERR_NO_MEM;
void *tmp_buf = NULL;
size_t actual_size = 0;
err = esp_dma_malloc(block_size, 0, &tmp_buf, &actual_size);
if (err != ESP_OK) {
return err;
}
const uint8_t* cur_src = (const uint8_t*) src;
for (size_t i = 0; i < block_count; ++i) {
memcpy(tmp_buf, cur_src, block_size);
cur_src += block_size;
err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1);
err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1, actual_size);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
__func__, err, start_block, i);
@ -422,7 +430,7 @@ esp_err_t sdmmc_write_sectors(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)
size_t start_block, size_t block_count, size_t buffer_len)
{
if (start_block + block_count > card->csd.capacity) {
return ESP_ERR_INVALID_SIZE;
@ -433,6 +441,7 @@ esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
.blklen = block_size,
.data = (void*) src,
.datalen = block_count * block_size,
.buflen = buffer_len,
.timeout_ms = SDMMC_WRITE_CMD_TIMEOUT_MS
};
if (block_count == 1) {
@ -509,19 +518,21 @@ esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
esp_err_t err = ESP_OK;
size_t block_size = card->csd.sector_size;
if (esp_ptr_dma_capable(dst) && (intptr_t)dst % 4 == 0) {
err = sdmmc_read_sectors_dma(card, dst, start_block, block_count);
if (esp_dma_is_buffer_aligned(dst, block_size * block_count, ESP_DMA_BUF_LOCATION_INTERNAL)) {
err = sdmmc_read_sectors_dma(card, dst, start_block, block_count, block_size * block_count);
} else {
// SDMMC peripheral needs DMA-capable buffers. Split the read into
// separate single block reads, if needed, and allocate a temporary
// DMA-capable buffer.
void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);
if (tmp_buf == NULL) {
return ESP_ERR_NO_MEM;
void *tmp_buf = NULL;
size_t actual_size = 0;
err = esp_dma_malloc(block_size, 0, &tmp_buf, &actual_size);
if (err != ESP_OK) {
return err;
}
uint8_t* cur_dst = (uint8_t*) dst;
for (size_t i = 0; i < block_count; ++i) {
err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1);
err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1, actual_size);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
__func__, err, start_block, i);
@ -536,7 +547,7 @@ esp_err_t sdmmc_read_sectors(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)
size_t start_block, size_t block_count, size_t buffer_len)
{
if (start_block + block_count > card->csd.capacity) {
return ESP_ERR_INVALID_SIZE;
@ -546,7 +557,8 @@ esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
.blklen = block_size,
.data = (void*) dst,
.datalen = block_count * block_size
.datalen = block_count * block_size,
.buflen = buffer_len,
};
if (block_count == 1) {
cmd.opcode = MMC_READ_BLOCK_SINGLE;

View File

@ -26,6 +26,7 @@
#include "sdmmc_cmd.h"
#include "sys/param.h"
#include "soc/soc_memory_layout.h"
#include "esp_dma_utils.h"
#define SDMMC_GO_IDLE_DELAY_MS 20
#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10
@ -81,9 +82,9 @@ esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
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);
size_t start_block, size_t block_count, size_t buffer_len);
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count);
size_t start_block, size_t block_count, size_t buffer_len);
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
/* SD specific */
@ -105,7 +106,7 @@ esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
/* 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_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen, size_t buffer_len);
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);

View File

@ -25,11 +25,12 @@ 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) {
uint8_t* ext_csd = NULL;
size_t actual_size = 0;
err = esp_dma_malloc(EXT_CSD_MMC_SIZE, 0, (void *)&ext_csd, &actual_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: could not allocate ext_csd", __func__);
return ESP_ERR_NO_MEM;
return err;
}
uint32_t sectors = 0;
@ -41,7 +42,7 @@ esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
}
/* read EXT_CSD */
err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE);
err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE, actual_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_ext_csd_data error 0x%x", __func__, err);
goto out;
@ -204,12 +205,13 @@ esp_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
return ESP_OK;
}
esp_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen)
esp_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen, size_t buffer_len)
{
assert(esp_ptr_dma_capable(out_data));
sdmmc_command_t cmd = {
.data = out_data,
.datalen = datalen,
.buflen = buffer_len,
.blklen = datalen,
.opcode = MMC_SEND_EXT_CSD,
.arg = 0,
@ -250,15 +252,17 @@ esp_err_t sdmmc_init_mmc_check_ext_csd(sdmmc_card_t* card)
}
/* ensure EXT_CSD buffer is available before starting any SD-card operation */
uint8_t* ext_csd = heap_caps_malloc(EXT_CSD_MMC_SIZE, MALLOC_CAP_DMA);
if (!ext_csd) {
uint8_t* ext_csd = NULL;
size_t actual_size = 0;
esp_err_t err = esp_dma_malloc(EXT_CSD_MMC_SIZE, 0, (void *)&ext_csd, &actual_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: could not allocate ext_csd", __func__);
return ESP_ERR_NO_MEM;
return err;
}
/* ensure card is in transfer state before read ext_csd */
uint32_t status;
esp_err_t err = sdmmc_send_cmd_send_status(card, &status);
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_status returned 0x%x", __func__, err);
goto out;
@ -271,7 +275,7 @@ esp_err_t sdmmc_init_mmc_check_ext_csd(sdmmc_card_t* card)
}
/* read EXT_CSD to ensure device works fine in HS mode */
err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE);
err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE, actual_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_ext_csd_data error 0x%x", __func__, err);
goto out;

View File

@ -87,15 +87,18 @@ esp_err_t sdmmc_init_sd_ssr(sdmmc_card_t* card)
/* Get the contents of SSR register: SD additional information
* ACMD13 to read 512byte SD status information
*/
uint32_t* sd_ssr = heap_caps_calloc(1, SD_SSR_SIZE, MALLOC_CAP_DMA);
if (!sd_ssr) {
uint32_t* sd_ssr = NULL;
size_t actual_size = 0;
err = esp_dma_calloc(1, SD_SSR_SIZE, 0, (void *)&sd_ssr, &actual_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: could not allocate sd_ssr", __func__);
return ESP_ERR_NO_MEM;
return err;
}
sdmmc_command_t cmd = {
.data = sd_ssr,
.datalen = SD_SSR_SIZE,
.buflen = actual_size,
.blklen = SD_SSR_SIZE,
.opcode = SD_APP_SD_STATUS,
.arg = 0,

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -286,7 +286,52 @@ typedef volatile struct sdmmc_dev_s {
uint32_t usrid; ///< user ID
uint32_t verid; ///< IP block version
uint32_t hcon; ///< compile-time IP configuration
union {
struct {
/** card_type_reg : RO; bitpos: [0]; default: 1;
* Hardware support SDIO and MMC.
*/
uint32_t card_type_reg:1;
/** card_num_reg : RO; bitpos: [5:1]; default: 1;
* Support card number is 2.
*/
uint32_t card_num_reg:5;
/** bus_type_reg : RO; bitpos: [6]; default: 1;
* Register config is APB bus.
*/
uint32_t bus_type_reg:1;
/** data_width_reg : RO; bitpos: [9:7]; default: 1;
* Regisger data widht is 32.
*/
uint32_t data_width_reg:3;
/** addr_width_reg : RO; bitpos: [15:10]; default: 19;
* Register address width is 32.
*/
uint32_t addr_width_reg:6;
uint32_t reserved_16:2;
/** dma_width_reg : RO; bitpos: [20:18]; default: 1;
* DMA data witdth is 32.
*/
uint32_t dma_width_reg:3;
/** ram_indise_reg : RO; bitpos: [21]; default: 0;
* Inside RAM in SDMMC module.
*/
uint32_t ram_indise_reg:1;
/** hold_reg : RO; bitpos: [22]; default: 1;
* Have a hold regiser in data path .
*/
uint32_t hold_reg:1;
uint32_t reserved_23:1;
/** num_clk_div_reg : RO; bitpos: [25:24]; default: 3;
* Have 4 clk divider in design .
*/
uint32_t num_clk_div_reg:2;
uint32_t reserved_26:6;
};
uint32_t val;
} hcon;
union {
struct {
uint32_t voltage: 16; ///< voltage control for slots; no-op on ESP32.

View File

@ -1,16 +1,8 @@
// 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.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/sdmmc_periph.h"
@ -55,3 +47,30 @@ const sdmmc_slot_io_info_t sdmmc_slot_gpio_num[SOC_SDMMC_NUM_SLOTS] = {
.d7 = -1,
}
};
const sdmmc_slot_io_info_t sdmmc_slot_gpio_sig[SOC_SDMMC_NUM_SLOTS] = {
{
.clk = -1,
.cmd = -1,
.d0 = -1,
.d1 = -1,
.d2 = -1,
.d3 = -1,
.d4 = -1,
.d5 = -1,
.d6 = -1,
.d7 = -1,
},
{
.clk = -1,
.cmd = -1,
.d0 = -1,
.d1 = -1,
.d2 = -1,
.d3 = -1,
.d4 = -1,
.d5 = -1,
.d6 = -1,
.d7 = -1,
}
};

View File

@ -139,6 +139,10 @@ config SOC_PSRAM_DMA_CAPABLE
bool
default y
config SOC_SDMMC_HOST_SUPPORTED
bool
default y
config SOC_WDT_SUPPORTED
bool
default y
@ -763,6 +767,22 @@ config SOC_RSA_MAX_BIT_LEN
int
default 4096
config SOC_SDMMC_USE_IOMUX
bool
default y
config SOC_SDMMC_USE_GPIO_MATRIX
bool
default y
config SOC_SDMMC_NUM_SLOTS
int
default 2
config SOC_SDMMC_DELAY_PHASE_NUM
int
default 4
config SOC_SHA_DMA_MAX_BUFFER_SIZE
int
default 3968
@ -795,26 +815,6 @@ config SOC_ECDSA_SUPPORT_EXPORT_PUBKEY
bool
default y
config SOC_SDMMC_USE_IOMUX
bool
default y
config SOC_SDMMC_USE_GPIO_MATRIX
bool
default y
config SOC_SDMMC_NUM_SLOTS
int
default 2
config SOC_SDMMC_IOMUX_FUNC
bool
default n
config SOC_SDMMC_DMA_NEED_CACHE_WB
bool
default y
config SOC_SDM_GROUPS
int
default 1

View File

@ -143,6 +143,7 @@ typedef enum {
// For digital domain: peripherals
SOC_MOD_CLK_PLL_F80M, /*!< PLL_F80M_CLK is derived from SPLL (clock gating + fixed divider of 6), it has a fixed frequency of 80MHz */
SOC_MOD_CLK_PLL_F160M, /*!< PLL_F160M_CLK is derived from SPLL (clock gating + fixed divider of 3), it has a fixed frequency of 160MHz */
SOC_MOD_CLK_PLL_F200M, /*!< PLL_F200M_CLK is derived from SPLL (clock gating + fixed divider of 3), it has a fixed frequency of 200MHz */
SOC_MOD_CLK_PLL_F240M, /*!< PLL_F240M_CLK is derived from SPLL (clock gating + fixed divider of 2), it has a fixed frequency of 240MHz */
SOC_MOD_CLK_CPLL, /*!< CPLL is from 40MHz XTAL oscillator frequency multipliers, it has a fixed frequency of 400MHz */
SOC_MOD_CLK_SPLL, /*!< SPLL is from 40MHz XTAL oscillator frequency multipliers, it has a fixed frequency of 480MHz */
@ -522,6 +523,22 @@ typedef enum {
#endif
} soc_periph_parlio_clk_src_t;
//////////////////////////////////////////////////SDMMC///////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of SDMMC
*/
#define SOC_SDMMC_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_PLL_F200M}
/**
* @brief Type of SDMMC clock source
*/
typedef enum {
SDMMC_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the default choice */
SDMMC_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the source clock */
SDMMC_CLK_SRC_PLL200M = SOC_MOD_CLK_PLL_F200M, /*!< Select PLL_200M as the source clock */
} soc_periph_sdmmc_clk_src_t;
#ifdef __cplusplus
}
#endif

View File

@ -785,6 +785,8 @@ typedef struct {
volatile lp_clkrst_date_reg_t date;
} lp_clkrst_dev_t;
extern lp_clkrst_dev_t LP_AON_CLKRST;
#ifndef __cplusplus
_Static_assert(sizeof(lp_clkrst_dev_t) == 0x400, "Invalid size of lp_clkrst_dev_t structure");

View File

@ -5,3 +5,17 @@
*/
#pragma once
#define SDMMC_SLOT0_IOMUX_PIN_NUM_CLK 43
#define SDMMC_SLOT0_IOMUX_PIN_NUM_CMD 44
#define SDMMC_SLOT0_IOMUX_PIN_NUM_D0 39
#define SDMMC_SLOT0_IOMUX_PIN_NUM_D1 40
#define SDMMC_SLOT0_IOMUX_PIN_NUM_D2 41
#define SDMMC_SLOT0_IOMUX_PIN_NUM_D3 42
#define SDMMC_SLOT0_IOMUX_PIN_NUM_D4 45
#define SDMMC_SLOT0_IOMUX_PIN_NUM_D5 46
#define SDMMC_SLOT0_IOMUX_PIN_NUM_D6 47
#define SDMMC_SLOT0_IOMUX_PIN_NUM_D7 48
#define SDMMC_SLOT0_FUNC 0
// SLOT1 doesn't go through IOMUX

View File

@ -163,7 +163,7 @@ extern "C" {
* Clock source selection register
*/
#define SDHOST_CLKSRC_REG (DR_REG_SDHOST_BASE + 0xc)
/** SDHOST_CLKSRC_REG : R/W; bitpos: [3:0]; default: 0;
/** SDHOST_CLKSRC : R/W; bitpos: [3:0]; default: 0;
* Clock divider source for two SD cards is supported. Each card has two bits assigned
* to it. For example, bit[1:0] are assigned for card 0, bit[3:2] are assigned for
* card 1. Card 0 maps and internally routes clock divider[0:3] outputs to
@ -173,10 +173,10 @@ extern "C" {
* 10 : Clock divider 2;
* 11 : Clock divider 3.
*/
#define SDHOST_CLKSRC_REG 0x0000000FU
#define SDHOST_CLKSRC_REG_M (SDHOST_CLKSRC_REG_V << SDHOST_CLKSRC_REG_S)
#define SDHOST_CLKSRC_REG_V 0x0000000FU
#define SDHOST_CLKSRC_REG_S 0
#define SDHOST_CLKSRC 0x0000000FU
#define SDHOST_CLKSRC_M (SDHOST_CLKSRC_V << SDHOST_CLKSRC_S)
#define SDHOST_CLKSRC_V 0x0000000FU
#define SDHOST_CLKSRC_S 0
/** SDHOST_CLKENA_REG register
* Clock enable register
@ -321,13 +321,13 @@ extern "C" {
* Command argument data register
*/
#define SDHOST_CMDARG_REG (DR_REG_SDHOST_BASE + 0x28)
/** SDHOST_CMDARG_REG : R/W; bitpos: [31:0]; default: 0;
/** SDHOST_CMDARG : R/W; bitpos: [31:0]; default: 0;
* Value indicates command argument to be passed to the card.
*/
#define SDHOST_CMDARG_REG 0xFFFFFFFFU
#define SDHOST_CMDARG_REG_M (SDHOST_CMDARG_REG_V << SDHOST_CMDARG_REG_S)
#define SDHOST_CMDARG_REG_V 0xFFFFFFFFU
#define SDHOST_CMDARG_REG_S 0
#define SDHOST_CMDARG 0xFFFFFFFFU
#define SDHOST_CMDARG_M (SDHOST_CMDARG_V << SDHOST_CMDARG_S)
#define SDHOST_CMDARG_V 0xFFFFFFFFU
#define SDHOST_CMDARG_S 0
/** SDHOST_CMD_REG register
* Command and boot configuration register
@ -808,25 +808,25 @@ extern "C" {
* Transferred byte count register
*/
#define SDHOST_TCBCNT_REG (DR_REG_SDHOST_BASE + 0x5c)
/** SDHOST_TCBCNT_REG : RO; bitpos: [31:0]; default: 0;
/** SDHOST_TCBCNT : RO; bitpos: [31:0]; default: 0;
* Number of bytes transferred by CIU unit to card.
*/
#define SDHOST_TCBCNT_REG 0xFFFFFFFFU
#define SDHOST_TCBCNT_REG_M (SDHOST_TCBCNT_REG_V << SDHOST_TCBCNT_REG_S)
#define SDHOST_TCBCNT_REG_V 0xFFFFFFFFU
#define SDHOST_TCBCNT_REG_S 0
#define SDHOST_TCBCNT 0xFFFFFFFFU
#define SDHOST_TCBCNT_M (SDHOST_TCBCNT_V << SDHOST_TCBCNT_S)
#define SDHOST_TCBCNT_V 0xFFFFFFFFU
#define SDHOST_TCBCNT_S 0
/** SDHOST_TBBCNT_REG register
* Transferred byte count register
*/
#define SDHOST_TBBCNT_REG (DR_REG_SDHOST_BASE + 0x60)
/** SDHOST_TBBCNT_REG : RO; bitpos: [31:0]; default: 0;
/** SDHOST_TBBCNT : RO; bitpos: [31:0]; default: 0;
* Number of bytes transferred between Host/DMA memory and BIU FIFO.
*/
#define SDHOST_TBBCNT_REG 0xFFFFFFFFU
#define SDHOST_TBBCNT_REG_M (SDHOST_TBBCNT_REG_V << SDHOST_TBBCNT_REG_S)
#define SDHOST_TBBCNT_REG_V 0xFFFFFFFFU
#define SDHOST_TBBCNT_REG_S 0
#define SDHOST_TBBCNT 0xFFFFFFFFU
#define SDHOST_TBBCNT_M (SDHOST_TBBCNT_V << SDHOST_TBBCNT_S)
#define SDHOST_TBBCNT_V 0xFFFFFFFFU
#define SDHOST_TBBCNT_S 0
/** SDHOST_DEBNCE_REG register
* Debounce filter time configuration register
@ -846,26 +846,26 @@ extern "C" {
* User ID (scratchpad) register
*/
#define SDHOST_USRID_REG (DR_REG_SDHOST_BASE + 0x68)
/** SDHOST_USRID_REG : R/W; bitpos: [31:0]; default: 0;
/** SDHOST_USRID : R/W; bitpos: [31:0]; default: 0;
* User identification register, value set by user. Can also be used as a scratchpad
* register by user.
*/
#define SDHOST_USRID_REG 0xFFFFFFFFU
#define SDHOST_USRID_REG_M (SDHOST_USRID_REG_V << SDHOST_USRID_REG_S)
#define SDHOST_USRID_REG_V 0xFFFFFFFFU
#define SDHOST_USRID_REG_S 0
#define SDHOST_USRID 0xFFFFFFFFU
#define SDHOST_USRID_M (SDHOST_USRID_V << SDHOST_USRID_S)
#define SDHOST_USRID_V 0xFFFFFFFFU
#define SDHOST_USRID_S 0
/** SDHOST_VERID_REG register
* Version ID (scratchpad) register
*/
#define SDHOST_VERID_REG (DR_REG_SDHOST_BASE + 0x6c)
/** SDHOST_VERSIONID_REG : RO; bitpos: [31:0]; default: 1412572938;
/** SDHOST_VERSIONID : RO; bitpos: [31:0]; default: 1412572938;
* Hardware version register. Can also be read by fireware.
*/
#define SDHOST_VERSIONID_REG 0xFFFFFFFFU
#define SDHOST_VERSIONID_REG_M (SDHOST_VERSIONID_REG_V << SDHOST_VERSIONID_REG_S)
#define SDHOST_VERSIONID_REG_V 0xFFFFFFFFU
#define SDHOST_VERSIONID_REG_S 0
#define SDHOST_VERSIONID 0xFFFFFFFFU
#define SDHOST_VERSIONID_M (SDHOST_VERSIONID_V << SDHOST_VERSIONID_S)
#define SDHOST_VERSIONID_V 0xFFFFFFFFU
#define SDHOST_VERSIONID_S 0
/** SDHOST_HCON_REG register
* Hardware feature register
@ -1036,15 +1036,15 @@ extern "C" {
* Descriptor base address register
*/
#define SDHOST_DBADDR_REG (DR_REG_SDHOST_BASE + 0x88)
/** SDHOST_DBADDR_REG : R/W; bitpos: [31:0]; default: 0;
/** SDHOST_DBADDR : R/W; bitpos: [31:0]; default: 0;
* Start of Descriptor List. Contains the base address of the First Descriptor. The
* LSB bits [1:0] are ignored and taken as all-zero by the IDMAC internally. Hence
* these LSB bits may be treated as read-only.
*/
#define SDHOST_DBADDR_REG 0xFFFFFFFFU
#define SDHOST_DBADDR_REG_M (SDHOST_DBADDR_REG_V << SDHOST_DBADDR_REG_S)
#define SDHOST_DBADDR_REG_V 0xFFFFFFFFU
#define SDHOST_DBADDR_REG_S 0
#define SDHOST_DBADDR 0xFFFFFFFFU
#define SDHOST_DBADDR_M (SDHOST_DBADDR_V << SDHOST_DBADDR_S)
#define SDHOST_DBADDR_V 0xFFFFFFFFU
#define SDHOST_DBADDR_S 0
/** SDHOST_IDSTS_REG register
* IDMAC status register
@ -1220,29 +1220,29 @@ extern "C" {
* Host descriptor address pointer
*/
#define SDHOST_DSCADDR_REG (DR_REG_SDHOST_BASE + 0x94)
/** SDHOST_DSCADDR_REG : RO; bitpos: [31:0]; default: 0;
/** SDHOST_DSCADDR : RO; bitpos: [31:0]; default: 0;
* Host Descriptor Address Pointer, updated by IDMAC during operation and cleared on
* reset. This register points to the start address of the current descriptor read by
* the IDMAC.
*/
#define SDHOST_DSCADDR_REG 0xFFFFFFFFU
#define SDHOST_DSCADDR_REG_M (SDHOST_DSCADDR_REG_V << SDHOST_DSCADDR_REG_S)
#define SDHOST_DSCADDR_REG_V 0xFFFFFFFFU
#define SDHOST_DSCADDR_REG_S 0
#define SDHOST_DSCADDR 0xFFFFFFFFU
#define SDHOST_DSCADDR_M (SDHOST_DSCADDR_V << SDHOST_DSCADDR_S)
#define SDHOST_DSCADDR_V 0xFFFFFFFFU
#define SDHOST_DSCADDR_S 0
/** SDHOST_BUFADDR_REG register
* Host buffer address pointer register
*/
#define SDHOST_BUFADDR_REG (DR_REG_SDHOST_BASE + 0x98)
/** SDHOST_BUFADDR_REG : RO; bitpos: [31:0]; default: 0;
/** SDHOST_BUFADDR : RO; bitpos: [31:0]; default: 0;
* Host Buffer Address Pointer, updated by IDMAC during operation and cleared on
* reset. This register points to the current Data Buffer Address being accessed by
* the IDMAC.
*/
#define SDHOST_BUFADDR_REG 0xFFFFFFFFU
#define SDHOST_BUFADDR_REG_M (SDHOST_BUFADDR_REG_V << SDHOST_BUFADDR_REG_S)
#define SDHOST_BUFADDR_REG_V 0xFFFFFFFFU
#define SDHOST_BUFADDR_REG_S 0
#define SDHOST_BUFADDR 0xFFFFFFFFU
#define SDHOST_BUFADDR_M (SDHOST_BUFADDR_V << SDHOST_BUFADDR_S)
#define SDHOST_BUFADDR_V 0xFFFFFFFFU
#define SDHOST_BUFADDR_S 0
/** SDHOST_CARDTHRCTL_REG register
* Card Threshold Control register
@ -1328,14 +1328,14 @@ extern "C" {
* CPU write and read transmit data by FIFO
*/
#define SDHOST_BUFFIFO_REG (DR_REG_SDHOST_BASE + 0x200)
/** SDHOST_BUFFIFO_REG : R/W; bitpos: [31:0]; default: 0;
/** SDHOST_BUFFIFO : R/W; bitpos: [31:0]; default: 0;
* CPU write and read transmit data by FIFO. This register points to the current Data
* FIFO .
*/
#define SDHOST_BUFFIFO_REG 0xFFFFFFFFU
#define SDHOST_BUFFIFO_REG_M (SDHOST_BUFFIFO_REG_V << SDHOST_BUFFIFO_REG_S)
#define SDHOST_BUFFIFO_REG_V 0xFFFFFFFFU
#define SDHOST_BUFFIFO_REG_S 0
#define SDHOST_BUFFIFO 0xFFFFFFFFU
#define SDHOST_BUFFIFO_M (SDHOST_BUFFIFO_V << SDHOST_BUFFIFO_S)
#define SDHOST_BUFFIFO_V 0xFFFFFFFFU
#define SDHOST_BUFFIFO_S 0
/** SDHOST_CLK_EDGE_SEL_REG register
* SDIO control register.
@ -1493,6 +1493,34 @@ extern "C" {
#define SDHOST_DLL_CAL_END_V 0x00000001U
#define SDHOST_DLL_CAL_END_S 1
#define SDMMC_INTMASK_IO_SLOT1 BIT(17)
#define SDMMC_INTMASK_IO_SLOT0 BIT(16)
#define SDMMC_INTMASK_EBE BIT(15)
#define SDMMC_INTMASK_ACD BIT(14)
#define SDMMC_INTMASK_SBE BIT(13)
#define SDMMC_INTMASK_HLE BIT(12)
#define SDMMC_INTMASK_FRUN BIT(11)
#define SDMMC_INTMASK_HTO BIT(10)
#define SDMMC_INTMASK_DTO BIT(9)
#define SDMMC_INTMASK_RTO BIT(8)
#define SDMMC_INTMASK_DCRC BIT(7)
#define SDMMC_INTMASK_RCRC BIT(6)
#define SDMMC_INTMASK_RXDR BIT(5)
#define SDMMC_INTMASK_TXDR BIT(4)
#define SDMMC_INTMASK_DATA_OVER BIT(3)
#define SDMMC_INTMASK_CMD_DONE BIT(2)
#define SDMMC_INTMASK_RESP_ERR BIT(1)
#define SDMMC_INTMASK_CD BIT(0)
#define SDMMC_IDMAC_INTMASK_AI BIT(9)
#define SDMMC_IDMAC_INTMASK_NI BIT(8)
#define SDMMC_IDMAC_INTMASK_CES BIT(5)
#define SDMMC_IDMAC_INTMASK_DU BIT(4)
#define SDMMC_IDMAC_INTMASK_FBE BIT(2)
#define SDMMC_IDMAC_INTMASK_RI BIT(1)
#define SDMMC_IDMAC_INTMASK_TI BIT(0)
#ifdef __cplusplus
}
#endif

View File

@ -10,6 +10,42 @@
extern "C" {
#endif
typedef struct sdmmc_desc_s {
struct {
uint32_t reserved1: 1;
uint32_t disable_int_on_completion: 1;
uint32_t last_descriptor: 1;
uint32_t first_descriptor: 1;
uint32_t second_address_chained: 1;
uint32_t end_of_ring: 1;
uint32_t reserved2: 24;
uint32_t card_error_summary: 1;
uint32_t owned_by_idmac: 1;
};
struct {
uint32_t buffer1_size: 13;
uint32_t buffer2_size: 13;
uint32_t reserved3: 6;
};
void* buffer1_ptr;
union {
void* buffer2_ptr;
void* next_desc_ptr;
};
/**
* These `reserved[12]` are for cache alignment. On P4, L1 Cache alignment is 64B.
* For those who want to access the DMA descriptor in a non-cacheable way, you can
* consider remove these `reserved[12]` bytes.
*/
uint32_t reserved[12];
} sdmmc_desc_t;
#define SDMMC_DMA_MAX_BUF_LEN 4096
#ifndef __cplusplus
_Static_assert(sizeof(sdmmc_desc_t) == 64, "invalid size of sdmmc_desc_t structure");
#endif
/** Group: Control register */
/** Type of ctrl register
* Control register
@ -39,7 +75,7 @@ typedef union {
* Global interrupt enable/disable bit. 0: Disable; 1: Enable.
*/
uint32_t int_enable:1;
uint32_t reserved_5:1;
uint32_t dma_enable:1;
/** read_wait : R/W; bitpos: [6]; default: 0;
* For sending read-wait to SDIO cards.
*/
@ -90,7 +126,12 @@ typedef union {
* then software should set this bit.
*/
uint32_t ceata_device_interrupt_status:1;
uint32_t reserved_12:20;
uint32_t reserved2:4;
uint32_t card_voltage_a:4;
uint32_t card_voltage_b:4;
uint32_t enable_od_pullup:1;
uint32_t use_internal_dma:1;
uint32_t reserved3:6;
};
uint32_t val;
} sdhost_ctrl_reg_t;
@ -147,7 +188,8 @@ typedef union {
* 10 : Clock divider 2;
* 11 : Clock divider 3.
*/
uint32_t clksrc_reg:4;
uint32_t card0:2;
uint32_t card1:2;
uint32_t reserved_4:28;
};
uint32_t val;
@ -211,21 +253,21 @@ typedef union {
*/
typedef union {
struct {
/** card_width4 : R/W; bitpos: [1:0]; default: 0;
/** card_width : R/W; bitpos: [1:0]; default: 0;
* One bit per card indicates if card is 1-bit or 4-bit mode.
* 0: 1-bit mode;
* 1: 4-bit mode.
* Bit[1:0] correspond to card[1:0] respectively.
*/
uint32_t card_width4:2;
uint32_t card_width:2;
uint32_t reserved_2:14;
/** card_width8 : R/W; bitpos: [17:16]; default: 0;
/** card_width_8 : R/W; bitpos: [17:16]; default: 0;
* One bit per card indicates if card is in 8-bit mode.
* 0: Non 8-bit mode;
* 1: 8-bit mode.
* Bit[17:16] correspond to card[1:0] respectively.
*/
uint32_t card_width8:2;
uint32_t card_width_8:2;
uint32_t reserved_18:14;
};
uint32_t val;
@ -305,21 +347,6 @@ typedef union {
} sdhost_intmask_reg_t;
/** Group: Command argument data register */
/** Type of cmdarg register
* Command argument data register
*/
typedef union {
struct {
/** cmdarg_reg : R/W; bitpos: [31:0]; default: 0;
* Value indicates command argument to be passed to the card.
*/
uint32_t cmdarg_reg:32;
};
uint32_t val;
} sdhost_cmdarg_reg_t;
/** Group: Command and boot configuration register */
/** Type of cmd register
* Command and boot configuration register
@ -334,10 +361,10 @@ typedef union {
* 0: No response expected from card; 1: Response expected from card.
*/
uint32_t response_expect:1;
/** response_length : R/W; bitpos: [7]; default: 0;
/** response_long : R/W; bitpos: [7]; default: 0;
* 0: Short response expected from card; 1: Long response expected from card.
*/
uint32_t response_length:1;
uint32_t response_long:1;
/** check_response_crc : R/W; bitpos: [8]; default: 0;
* 0: Do not check; 1: Check response CRC.
* Some of command responses do not return valid CRC bits. Software should disable CRC
@ -348,11 +375,11 @@ typedef union {
* 0: No data transfer expected; 1: Data transfer expected.
*/
uint32_t data_expected:1;
/** read_write : R/W; bitpos: [10]; default: 0;
/** rw : R/W; bitpos: [10]; default: 0;
* 0: Read from card; 1: Write to card.
* Don't care if no data is expected from card.
*/
uint32_t read_write:1;
uint32_t rw:1;
/** transfer_mode : R/W; bitpos: [11]; default: 0;
* 0: Block data transfer command; 1: Stream data transfer command.
* Don't care if no data expected.
@ -363,14 +390,14 @@ typedef union {
* end of data transfer.
*/
uint32_t send_auto_stop:1;
/** wait_prvdata_complete : R/W; bitpos: [13]; default: 0;
/** wait_complete : R/W; bitpos: [13]; default: 0;
* 0: Send command at once, even if previous data transfer has not completed; 1: Wait
* for previous data transfer to complete before sending Command.
* The SDHOST_WAIT_PRVDATA_COMPLETE] = 0 option is typically used to query status of
* The SDHOST_WAIT_COMPLETE] = 0 option is typically used to query status of
* card during data transfer or to stop current data transfer. SDHOST_CARD_NUMBERr
* should be same as in previous command.
*/
uint32_t wait_prvdata_complete:1;
uint32_t wait_complete:1;
/** stop_abort_cmd : R/W; bitpos: [14]; default: 0;
* 0: Neither stop nor abort command can stop current data transfer. If abort is sent
* to function-number currently selected or not in data-transfer mode, then bit should
@ -381,20 +408,20 @@ typedef union {
* state-machines of CIU can return correctly to idle state.
*/
uint32_t stop_abort_cmd:1;
/** send_initialization : R/W; bitpos: [15]; default: 0;
/** send_init : R/W; bitpos: [15]; default: 0;
* 0: Do not send initialization sequence (80 clocks of 1) before sending this
* command; 1: Send initialization sequence before sending this command.
* After powered on, 80 clocks must be sent to card for initialization before sending
* any commands to card. Bit should be set while sending first command to card so that
* controller will initialize clocks before sending command to card.
*/
uint32_t send_initialization:1;
/** card_number : R/W; bitpos: [20:16]; default: 0;
uint32_t send_init:1;
/** card_num : R/W; bitpos: [20:16]; default: 0;
* Card number in use. Represents physical slot number of card being accessed. In
* SD-only mode, up to two cards are supported.
*/
uint32_t card_number:5;
/** update_clock_registers_only : R/W; bitpos: [21]; default: 0;
uint32_t card_num:5;
/** update_clk_reg : R/W; bitpos: [21]; default: 0;
* 0: Normal command sequence; 1: Do not send commands, just update clock register
* value into card clock domain.
* Following register values are transferred into card clock domain: CLKDIV, CLRSRC,
@ -407,7 +434,7 @@ typedef union {
* register values for new command sequence to card(s). When bit is set, there are no
* Command Done interrupts because no command is sent to SD_MMC_CEATA cards.
*/
uint32_t update_clock_registers_only:1;
uint32_t update_clk_reg:1;
/** read_ceata_device : R/W; bitpos: [22]; default: 0;
* Read access flag.
* 0: Host is not performing read access (RW_REG or RW_BLK)towards CE-ATA device;
@ -437,16 +464,16 @@ typedef union {
* 0: CMD and DATA sent to card bypassing HOLD Register;
* 1: CMD and DATA sent to card through the HOLD Register.
*/
uint32_t use_hole_reg:1;
uint32_t use_hold_reg:1;
uint32_t reserved_30:1;
/** start_cmd : R/W; bitpos: [31]; default: 0;
/** start_command : R/W; bitpos: [31]; default: 0;
* Start command. Once command is served by the CIU, this bit is automatically
* cleared. When this bit is set, host should not attempt to write to any command
* registers. If a write is attempted, hardware lock error is set in raw interrupt
* register. Once command is sent and a response is received from SD_MMC_CEATA cards,
* Command Done bit is set in the raw interrupt Register.
*/
uint32_t start_cmd:1;
uint32_t start_command:1;
};
uint32_t val;
} sdhost_cmd_reg_t;
@ -573,7 +600,22 @@ typedef union {
* Bit 1 (RE): Response error;
* Bit 0 (CD): Card detect.
*/
uint32_t int_status_raw:16;
uint32_t cd:1;
uint32_t re:1;
uint32_t cmd_done:1;
uint32_t dto:1;
uint32_t txdr:1;
uint32_t rxdr:1;
uint32_t rcrc:1;
uint32_t dcrc:1;
uint32_t rto:1;
uint32_t drto:1;
uint32_t hto:1;
uint32_t frun:1;
uint32_t hle:1;
uint32_t sbi_bci:1;
uint32_t acd:1;
uint32_t ebe:1;
/** sdio_interrupt_raw : R/W; bitpos: [17:16]; default: 0;
* Interrupt from SDIO card, one bit for each card. Bit[17:16] correspond to card1 and
* card0, respectively. Setting a bit clears the corresponding interrupt bit and
@ -809,21 +851,6 @@ typedef union {
} sdhost_usrid_reg_t;
/** Group: Version ID (scratchpad) register */
/** Type of verid register
* Version ID (scratchpad) register
*/
typedef union {
struct {
/** versionid_reg : RO; bitpos: [31:0]; default: 1412572938;
* Hardware version register. Can also be read by fireware.
*/
uint32_t versionid_reg:32;
};
uint32_t val;
} sdhost_verid_reg_t;
/** Group: Hardware feature register */
/** Type of hcon register
* Hardware feature register
@ -881,12 +908,12 @@ typedef union {
typedef union {
struct {
uint32_t reserved_0:16;
/** ddr_reg : R/W; bitpos: [17:16]; default: 0;
/** ddr : R/W; bitpos: [17:16]; default: 0;
* DDR mode selecton,1 bit for each card.
* 0-Non-DDR mdoe.
* 1-DDR mdoe.
*/
uint32_t ddr_reg:2;
uint32_t ddr:2;
uint32_t reserved_18:14;
};
uint32_t val;
@ -920,23 +947,23 @@ typedef union {
*/
typedef union {
struct {
/** bmod_swr : R/W; bitpos: [0]; default: 0;
/** sw_reset : R/W; bitpos: [0]; default: 0;
* Software Reset. When set, the DMA Controller resets all its internal registers. It
* is automatically cleared after one clock cycle.
*/
uint32_t bmod_swr:1;
/** bmod_fb : R/W; bitpos: [1]; default: 0;
uint32_t sw_reset:1;
/** fb : R/W; bitpos: [1]; default: 0;
* Fixed Burst. Controls whether the AHB Master interface performs fixed burst
* transfers or not. When set, the AHB will use only SINGLE, INCR4, INCR8 or INCR16
* during start of normal burst transfers. When reset, the AHB will use SINGLE and
* INCR burst transfer operations.
*/
uint32_t bmod_fb:1;
uint32_t fb:1;
uint32_t reserved_2:5;
/** bmod_de : R/W; bitpos: [7]; default: 0;
/** enable : R/W; bitpos: [7]; default: 0;
* IDMAC Enable. When set, the IDMAC is enabled.
*/
uint32_t bmod_de:1;
uint32_t enable:1;
/** bmod_pbl : R/W; bitpos: [10:8]; default: 0;
* Programmable Burst Length. These bits indicate the maximum number of beats to be
* performed in one IDMAC???Internal DMA Control???transaction. The IDMAC will always
@ -1088,16 +1115,16 @@ typedef union {
*/
typedef union {
struct {
/** idinten_ti : R/W; bitpos: [0]; default: 0;
/** ti : R/W; bitpos: [0]; default: 0;
* Transmit Interrupt Enable. When set with Normal Interrupt Summary Enable, Transmit
* Interrupt is enabled. When reset, Transmit Interrupt is disabled.
*/
uint32_t idinten_ti:1;
/** idinten_ri : R/W; bitpos: [1]; default: 0;
uint32_t ti:1;
/** ri : R/W; bitpos: [1]; default: 0;
* Receive Interrupt Enable. When set with Normal Interrupt Summary Enable, Receive
* Interrupt is enabled. When reset, Receive Interrupt is disabled.
*/
uint32_t idinten_ri:1;
uint32_t ri:1;
/** idinten_fbe : R/W; bitpos: [2]; default: 0;
* Fatal Bus Error Enable. When set with Abnormal Interrupt Summary Enable, the Fatal
* Bus Error Interrupt is enabled. When reset, Fatal Bus Error Enable Interrupt is
@ -1116,13 +1143,13 @@ typedef union {
*/
uint32_t idinten_ces:1;
uint32_t reserved_6:2;
/** idinten_ni : R/W; bitpos: [8]; default: 0;
/** ni : R/W; bitpos: [8]; default: 0;
* Normal Interrupt Summary Enable. When set, a normal interrupt is enabled. When
* reset, a normal interrupt is disabled. This bit enables the following bits:
* IDINTEN[0]: Transmit Interrupt;
* IDINTEN[1]: Receive Interrupt.
*/
uint32_t idinten_ni:1;
uint32_t ni:1;
/** idinten_ai : R/W; bitpos: [9]; default: 0;
* Abnormal Interrupt Summary Enable. When set, an abnormal interrupt is enabled. This
* bit enables the following bits:
@ -1182,12 +1209,12 @@ typedef union {
* 1'b1-Card read threshold enabled.
*/
uint32_t cardrdthren_reg:1;
/** cardclrinten_reg : R/W; bitpos: [1]; default: 0;
/** busy_clr_int_en : R/W; bitpos: [1]; default: 0;
* Busy clear interrupt generation:
* 1'b0-Busy clear interrypt disabled.
* 1'b1-Busy clear interrypt enabled.
*/
uint32_t cardclrinten_reg:1;
uint32_t busy_clr_int_en:1;
/** cardwrthren_reg : R/W; bitpos: [2]; default: 0;
* Applicable when HS400 mode is enabled.
* 1'b0-Card write Threshold disabled.
@ -1392,7 +1419,7 @@ typedef union {
} sdhost_dll_conf_reg_t;
typedef struct {
typedef struct sdmmc_dev_t {
volatile sdhost_ctrl_reg_t ctrl;
uint32_t reserved_004;
volatile sdhost_clkdiv_reg_t clkdiv;
@ -1403,12 +1430,9 @@ typedef struct {
volatile sdhost_blksiz_reg_t blksiz;
volatile sdhost_bytcnt_reg_t bytcnt;
volatile sdhost_intmask_reg_t intmask;
volatile sdhost_cmdarg_reg_t cmdarg;
volatile uint32_t cmdarg;
volatile sdhost_cmd_reg_t cmd;
volatile sdhost_resp0_reg_t resp0;
volatile sdhost_resp1_reg_t resp1;
volatile sdhost_resp2_reg_t resp2;
volatile sdhost_resp3_reg_t resp3;
volatile uint32_t resp[4]; ///< Response from card
volatile sdhost_mintsts_reg_t mintsts;
volatile sdhost_rintsts_reg_t rintsts;
volatile sdhost_status_reg_t status;
@ -1420,7 +1444,7 @@ typedef struct {
volatile sdhost_tbbcnt_reg_t tbbcnt;
volatile sdhost_debnce_reg_t debnce;
volatile sdhost_usrid_reg_t usrid;
volatile sdhost_verid_reg_t verid;
volatile uint32_t verid;
volatile sdhost_hcon_reg_t hcon;
volatile sdhost_uhs_reg_t uhs;
volatile sdhost_rst_n_reg_t rst_n;
@ -1444,11 +1468,14 @@ typedef struct {
volatile sdhost_raw_ints_reg_t raw_ints;
volatile sdhost_dll_clk_conf_reg_t dll_clk_conf;
volatile sdhost_dll_conf_reg_t dll_conf;
} sdhost_dev_t;
} sdmmc_dev_t;
extern sdmmc_dev_t SDMMC;
typedef sdhost_cmd_reg_t sdmmc_hw_cmd_t;
#ifndef __cplusplus
_Static_assert(sizeof(sdhost_dev_t) == 0x810, "Invalid size of sdhost_dev_t structure");
_Static_assert(sizeof(sdmmc_dev_t) == 0x810, "Invalid size of sdmmc_dev_t structure");
#endif
#ifdef __cplusplus

View File

@ -78,7 +78,7 @@
#define SOC_SPIRAM_SUPPORTED 1
#define SOC_PSRAM_DMA_CAPABLE 1
// #define SOC_ULP_SUPPORTED 1 //TODO: IDF-7534
// #define SOC_SDMMC_HOST_SUPPORTED 1 //TODO: IDF-6502
#define SOC_SDMMC_HOST_SUPPORTED 1
// #define SOC_CLK_TREE_SUPPORTED 1 //TODO: IDF-7526
// #define SOC_ASSIST_DEBUG_SUPPORTED 1 //TODO: IDF-7565
#define SOC_WDT_SUPPORTED 1
@ -343,6 +343,19 @@
/*--------------------------- RSA CAPS ---------------------------------------*/
#define SOC_RSA_MAX_BIT_LEN (4096)
/*-------------------------- SDMMC CAPS -----------------------------------------*/
/**
* Card detect, write protect, interrupt use GPIO Matrix on all chips.
* Slot 0 clock/cmd/data pins use IOMUX
* Slot 1 clock/cmd/data pins use GPIO Matrix
*/
#define SOC_SDMMC_USE_IOMUX 1
#define SOC_SDMMC_USE_GPIO_MATRIX 1
#define SOC_SDMMC_NUM_SLOTS 2
/* Supported host clock delay phase number */
#define SOC_SDMMC_DELAY_PHASE_NUM 4
// TODO: IDF-5353 (Copy from esp32c3, need check)
/*--------------------------- SHA CAPS ---------------------------------------*/
@ -367,15 +380,6 @@
/*--------------------------- ECDSA CAPS ---------------------------------------*/
#define SOC_ECDSA_SUPPORT_EXPORT_PUBKEY (1)
#ifdef SDMMC_DEFAULT_IOMUX
#define SOC_SDMMC_USE_IOMUX 1
#else
#define SOC_SDMMC_USE_GPIO_MATRIX 1
#endif
#define SOC_SDMMC_NUM_SLOTS 2
#define SOC_SDMMC_IOMUX_FUNC 0
#define SOC_SDMMC_DMA_NEED_CACHE_WB 1
/*-------------------------- Sigma Delta Modulator CAPS -----------------*/
#define SOC_SDM_GROUPS 1U
#define SOC_SDM_CHANNELS_PER_GROUP 4

View File

@ -5,15 +5,71 @@
*/
#include "soc/sdmmc_periph.h"
// ESP32P4-TODO: need new iomux and sig map
const sdmmc_slot_info_t sdmmc_slot_info[SOC_SDMMC_NUM_SLOTS] = {
{
.width = 8,
.card_detect = SD_CARD_DETECT_N_1_PAD_IN_IDX,
.write_protect = SD_CARD_WRITE_PRT_1_PAD_IN_IDX,
.card_int = SD_CARD_INT_N_1_PAD_IN_IDX,
},
{
.width = 8,
.card_detect = SD_CARD_DETECT_N_2_PAD_IN_IDX,
.write_protect = SD_CARD_WRITE_PRT_2_PAD_IN_IDX,
.card_int = SD_CARD_INT_N_2_PAD_IN_IDX,
}
};
const sdmmc_slot_io_info_t sdmmc_slot_gpio_num[SOC_SDMMC_NUM_SLOTS] = {
{
.clk = SDMMC_SLOT0_IOMUX_PIN_NUM_CLK,
.cmd = SDMMC_SLOT0_IOMUX_PIN_NUM_CMD,
.d0 = SDMMC_SLOT0_IOMUX_PIN_NUM_D0,
.d1 = SDMMC_SLOT0_IOMUX_PIN_NUM_D1,
.d2 = SDMMC_SLOT0_IOMUX_PIN_NUM_D2,
.d3 = SDMMC_SLOT0_IOMUX_PIN_NUM_D3,
.d4 = SDMMC_SLOT0_IOMUX_PIN_NUM_D4,
.d5 = SDMMC_SLOT0_IOMUX_PIN_NUM_D5,
.d6 = SDMMC_SLOT0_IOMUX_PIN_NUM_D6,
.d7 = SDMMC_SLOT0_IOMUX_PIN_NUM_D7,
},
{
.clk = -1,
.cmd = -1,
.d0 = -1,
.d1 = -1,
.d2 = -1,
.d3 = -1,
.d4 = -1,
.d5 = -1,
.d6 = -1,
.d7 = -1,
}
};
const sdmmc_slot_io_info_t sdmmc_slot_gpio_sig[SOC_SDMMC_NUM_SLOTS] = {
{
.clk = -1,
.cmd = -1,
.d0 = -1,
.d1 = -1,
.d2 = -1,
.d3 = -1,
.d4 = -1,
.d5 = -1,
.d6 = -1,
.d7 = -1,
},
{
.clk = SD_CARD_CCLK_2_PAD_OUT_IDX,
.cmd = SD_CARD_CCMD_2_PAD_OUT_IDX,
.d0 = SD_CARD_CDATA0_2_PAD_OUT_IDX,
.d1 = SD_CARD_CDATA1_2_PAD_OUT_IDX,
.d2 = SD_CARD_CDATA2_2_PAD_OUT_IDX,
.d3 = SD_CARD_CDATA3_2_PAD_OUT_IDX,
.d4 = SD_CARD_CDATA4_2_PAD_OUT_IDX,
.d5 = SD_CARD_CDATA5_2_PAD_OUT_IDX,
.d6 = SD_CARD_CDATA6_2_PAD_OUT_IDX,
.d7 = SD_CARD_CDATA7_2_PAD_OUT_IDX,
},
};

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -284,7 +284,52 @@ typedef volatile struct sdmmc_dev_s {
uint32_t usrid; ///< user ID
uint32_t verid; ///< IP block version
uint32_t hcon; ///< compile-time IP configuration
union {
struct {
/** card_type_reg : RO; bitpos: [0]; default: 1;
* Hardware support SDIO and MMC.
*/
uint32_t card_type_reg:1;
/** card_num_reg : RO; bitpos: [5:1]; default: 1;
* Support card number is 2.
*/
uint32_t card_num_reg:5;
/** bus_type_reg : RO; bitpos: [6]; default: 1;
* Register config is APB bus.
*/
uint32_t bus_type_reg:1;
/** data_width_reg : RO; bitpos: [9:7]; default: 1;
* Regisger data widht is 32.
*/
uint32_t data_width_reg:3;
/** addr_width_reg : RO; bitpos: [15:10]; default: 19;
* Register address width is 32.
*/
uint32_t addr_width_reg:6;
uint32_t reserved_16:2;
/** dma_width_reg : RO; bitpos: [20:18]; default: 1;
* DMA data witdth is 32.
*/
uint32_t dma_width_reg:3;
/** ram_indise_reg : RO; bitpos: [21]; default: 0;
* Inside RAM in SDMMC module.
*/
uint32_t ram_indise_reg:1;
/** hold_reg : RO; bitpos: [22]; default: 1;
* Have a hold regiser in data path .
*/
uint32_t hold_reg:1;
uint32_t reserved_23:1;
/** num_clk_div_reg : RO; bitpos: [25:24]; default: 3;
* Have 4 clk divider in design .
*/
uint32_t num_clk_div_reg:2;
uint32_t reserved_26:6;
};
uint32_t val;
} hcon;
union {
struct {
uint32_t voltage: 16; ///< voltage control for slots; no-op on ESP32.

View File

@ -1,16 +1,8 @@
// Copyright 2015-2020 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.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/sdmmc_periph.h"
@ -29,6 +21,33 @@ const sdmmc_slot_info_t sdmmc_slot_info[SOC_SDMMC_NUM_SLOTS] = {
}
};
const sdmmc_slot_io_info_t sdmmc_slot_gpio_num[SOC_SDMMC_NUM_SLOTS] = {
{
.clk = -1,
.cmd = -1,
.d0 = -1,
.d1 = -1,
.d2 = -1,
.d3 = -1,
.d4 = -1,
.d5 = -1,
.d6 = -1,
.d7 = -1,
},
{
.clk = -1,
.cmd = -1,
.d0 = -1,
.d1 = -1,
.d2 = -1,
.d3 = -1,
.d4 = -1,
.d5 = -1,
.d6 = -1,
.d7 = -1,
}
};
const sdmmc_slot_io_info_t sdmmc_slot_gpio_sig[SOC_SDMMC_NUM_SLOTS] = {
{
.clk = SDHOST_CCLK_OUT_1_IDX,

View File

@ -52,19 +52,12 @@ typedef struct {
uint8_t d7;
} sdmmc_slot_io_info_t;
/* Note: it is in theory possible to have both IOMUX and GPIO Matrix supported
* in the same SoC. However this is not used on any SoC at this point, and would
* complicate the driver. Hence only one of these options is supported at a time.
*/
#if SOC_SDMMC_USE_IOMUX
/** GPIO pin numbers of SD bus signals, one structure per slot */
extern const sdmmc_slot_io_info_t sdmmc_slot_gpio_num[SOC_SDMMC_NUM_SLOTS];
#elif SOC_SDMMC_USE_GPIO_MATRIX
/** GPIO matrix signal numbers of SD bus signals, one structure per slot */
extern const sdmmc_slot_io_info_t sdmmc_slot_gpio_sig[SOC_SDMMC_NUM_SLOTS];
#endif // SOC_SDMMC_USE_{IOMUX,GPIO_MATRIX}
#endif // SOC_SDMMC_HOST_SUPPORTED
#ifdef __cplusplus
}

View File

@ -179,7 +179,8 @@ ESP32C6_DOCS = ['api-guides/RF_calibration.rst',
ESP32H2_DOCS = ['api-guides/RF_calibration.rst']
ESP32P4_DOCS = ['api-reference/system/ipc.rst']
ESP32P4_DOCS = ['api-reference/system/ipc.rst',
'api-reference/peripherals/sd_pullup_requirements.rst']
# format: {tag needed to include: documents to included}, tags are parsed from sdkconfig and peripheral_caps.h headers
conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS,

View File

@ -114,6 +114,7 @@ INPUT = \
$(PROJECT_PATH)/components/driver/rmt/include/driver/rmt_types.h \
$(PROJECT_PATH)/components/driver/sdio_slave/include/driver/sdio_slave.h \
$(PROJECT_PATH)/components/driver/sigma_delta/include/driver/sdm.h \
$(PROJECT_PATH)/components/driver/sdmmc/include/driver/sdmmc_default_configs.h \
$(PROJECT_PATH)/components/driver/sdmmc/include/driver/sdmmc_host.h \
$(PROJECT_PATH)/components/driver/sdmmc/include/driver/sdmmc_types.h \
$(PROJECT_PATH)/components/driver/spi/include/driver/sdspi_host.h \

View File

@ -26,9 +26,7 @@ Peripherals API
:SOC_PARLIO_SUPPORTED: parlio
:SOC_PCNT_SUPPORTED: pcnt
:SOC_RMT_SUPPORTED: rmt
:esp32: sd_pullup_requirements
:esp32s3: sd_pullup_requirements
:esp32c6: sd_pullup_requirements
:SOC_SDMMC_HOST_SUPPORTED or SOC_SDIO_SLAVE_SUPPORTED: sd_pullup_requirements
:SOC_SDMMC_HOST_SUPPORTED: sdmmc_host
sdspi_host
:SOC_SDIO_SLAVE_SUPPORTED: sdio_slave

View File

@ -28,6 +28,25 @@ For functions used to initialize and configure:
The protocol layer works with the host via the :cpp:class:`sdmmc_host_t` structure. This structure contains pointers to various functions of the host.
Pin Configurations
------------------
..only:: SOC_SDMMC_USE_IOMUX and not SOC_SDMMC_USE_GPIO_MATRIX
SDMMC pins are dedicated, you don't have to configure the pins.
..only:: SOC_SDMMC_USE_GPIO_MATRIX and not SOC_SDMMC_USE_IOMUX
SDMMC pin signals are routed via GPIO Matrix, so you will need to configure the pins in :cpp:type:`sdmmc_slot_config_t`.
..only:: esp32p4
SDMMC have two slots:
.. list::
- slot 0 pins are dedicated for UHS-I mode. This is not yet supported in the driver.
- slot 1 pins are routed via GPIO Matrix, and it's for non UHS-I usage. You will need to configure the pins in :cpp:type:`sdmmc_slot_config_t` to use the slot 1.
Application Example
-------------------

View File

@ -26,9 +26,7 @@
:SOC_PARLIO_SUPPORTED: parlio
:SOC_PCNT_SUPPORTED: pcnt
:SOC_RMT_SUPPORTED: rmt
:esp32: sd_pullup_requirements
:esp32s3: sd_pullup_requirements
:esp32c6: sd_pullup_requirements
:SOC_SDMMC_HOST_SUPPORTED or SOC_SDIO_SLAVE_SUPPORTED: sd_pullup_requirements
:SOC_SDMMC_HOST_SUPPORTED: sdmmc_host
sdspi_host
:SOC_SDIO_SLAVE_SUPPORTED: sdio_slave

View File

@ -215,6 +215,10 @@ examples/peripherals/rmt/stepper_motor:
- if: SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP != 1
examples/peripherals/sdio/host:
disable:
- if: IDF_TARGET == "esp32p4"
temporary: true
reason: request p4 essl spi to be ready for build, TODO IDF-8357
enable:
- if: SOC_SDMMC_HOST_SUPPORTED == 1
- if: IDF_TARGET == "esp32"

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-S3 |
| ----------------- | ----- | -------- |
| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- |
# SD Card example (SDMMC)
@ -48,7 +48,7 @@ GPIO13 (MTCK) | D3 | not used in 1-line SD mode, but card's D3 pin must
On ESP32-S3, SDMMC peripheral is connected to GPIO pins using GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card. In this example, GPIOs can be configured in two ways:
1. Using menuconfig: Run `idf.py menuconfig` in the project directory and open "SD/MMC Example Configuration" menu.
2. In the source code: See the initialization of ``sdmmc_slot_config_t slot_config`` structure in the example code.
2. In the source code: See the initialization of `sdmmc_slot_config_t slot_config` structure in the example code.
The table below lists the default pin assignments.
@ -63,6 +63,24 @@ GPIO38 | D1 | not used in 1-line SD mode; 10k pullup in 4-line m
GPIO33 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode
GPIO34 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup
### Pin assignments for ESP32-P4
On ESP32-P4, Slot 1 of the SDMMC peripheral is connected to GPIO pins using GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card. In this example, GPIOs can be configured in two ways:
1. Using menuconfig: Run `idf.py menuconfig` in the project directory and open "SD/MMC Example Configuration" menu.
2. In the source code: See the initialization of `sdmmc_slot_config_t slot_config` structure in the example code.
The table below lists the default pin assignments.
ESP32-P4 pin | SD card pin | Notes
--------------|-------------|------------
GPIO43 | CLK | 10k pullup
GPIO44 | CMD | 10k pullup
GPIO39 | D0 | 10k pullup
GPIO40 | D1 | not used in 1-line SD mode; 10k pullup in 4-line mode
GPIO41 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode
GPIO42 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup
### 4-line and 1-line SD modes
By default, this example uses 4 line SD mode, utilizing 6 pins: CLK, CMD, D0 - D3. It is possible to use 1-line mode (CLK, CMD, D0) by changing "SD/MMC bus width" in the example configuration menu (see `CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1`).

View File

@ -27,28 +27,34 @@ menu "SD/MMC Example Configuration"
config EXAMPLE_PIN_CMD
int "CMD GPIO number"
default 35 if IDF_TARGET_ESP32S3
default 44 if IDF_TARGET_ESP32P4
config EXAMPLE_PIN_CLK
int "CLK GPIO number"
default 36 if IDF_TARGET_ESP32S3
default 43 if IDF_TARGET_ESP32P4
config EXAMPLE_PIN_D0
int "D0 GPIO number"
default 37 if IDF_TARGET_ESP32S3
default 39 if IDF_TARGET_ESP32P4
if EXAMPLE_SDMMC_BUS_WIDTH_4
config EXAMPLE_PIN_D1
int "D1 GPIO number"
default 38 if IDF_TARGET_ESP32S3
default 40 if IDF_TARGET_ESP32P4
config EXAMPLE_PIN_D2
int "D2 GPIO number"
default 33 if IDF_TARGET_ESP32S3
default 41 if IDF_TARGET_ESP32P4
config EXAMPLE_PIN_D3
int "D3 GPIO number"
default 34 if IDF_TARGET_ESP32S3
default 42 if IDF_TARGET_ESP32P4
endif # EXAMPLE_SDMMC_BUS_WIDTH_4

View File

@ -655,7 +655,6 @@ components/soc/esp32/include/soc/uhci_reg.h
components/soc/esp32/include/soc/uhci_struct.h
components/soc/esp32/include/soc/wdev_reg.h
components/soc/esp32/ledc_periph.c
components/soc/esp32/sdmmc_periph.c
components/soc/esp32/spi_periph.c
components/soc/esp32/uart_periph.c
components/soc/esp32c3/i2c_periph.c
@ -796,7 +795,6 @@ components/soc/esp32s3/include/soc/usb_wrap_reg.h
components/soc/esp32s3/include/soc/usb_wrap_struct.h
components/soc/esp32s3/include/soc/wdev_reg.h
components/soc/esp32s3/ledc_periph.c
components/soc/esp32s3/sdmmc_periph.c
components/soc/esp32s3/uart_periph.c
components/soc/esp32s3/usb_periph.c
components/soc/include/soc/dedic_gpio_periph.h