Merge branch 'feature/sdmmc_power_ctrl' into 'master'

sdmmc: power ctrl driver

Closes IDF-6751

See merge request espressif/esp-idf!28270
This commit is contained in:
Armando (Dou Yiwen)
2024-03-25 14:31:59 +08:00
24 changed files with 414 additions and 46 deletions
@@ -43,7 +43,9 @@ extern "C" {
.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 \
.set_input_delay = &sdmmc_host_set_input_delay, \
.dma_aligned_buffer = NULL, \
.pwr_ctrl_handle = NULL, \
}
#define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used
@@ -665,20 +665,6 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
return ret;
}
#if SOC_MULTI_USAGE_LDO_SUPPORTED
esp_ldo_unit_init_cfg_t init_ldo_cfg = {
.unit_id = LDO_UNIT_4,
.cfg = {
.voltage_mv = 3300,
},
.flags.shared_ldo = true,
};
esp_ldo_unit_handle_t ldo_unit = NULL;
ESP_RETURN_ON_ERROR(esp_ldo_init_unit(&init_ldo_cfg, &ldo_unit), TAG, "LDO init failed");
ESP_RETURN_ON_ERROR(esp_ldo_enable_unit(ldo_unit), TAG, "LDO enable failed");
s_host_ctx.slot_ctx[slot].ldo_unit = ldo_unit;
#endif
return ESP_OK;
}
@@ -700,16 +686,6 @@ esp_err_t sdmmc_host_deinit(void)
sdmmc_ll_enable_bus_clock(s_host_ctx.hal.dev, false);
}
#if SOC_MULTI_USAGE_LDO_SUPPORTED
for (int i = 0; i < SOC_SDMMC_NUM_SLOTS; i++) {
if (s_host_ctx.slot_ctx[i].ldo_unit) {
ESP_RETURN_ON_ERROR(esp_ldo_disable_unit(s_host_ctx.slot_ctx[i].ldo_unit), TAG, "LDO disable failed");
ESP_RETURN_ON_ERROR(esp_ldo_deinit_unit(s_host_ctx.slot_ctx[i].ldo_unit), TAG, "LDO deinit failed");
s_host_ctx.slot_ctx[i].ldo_unit = NULL;
}
}
#endif
return ESP_OK;
}
@@ -1,3 +1,3 @@
idf_component_register(SRCS sdmmc_test_board.c sdmmc_test_board_defs.c
INCLUDE_DIRS include
REQUIRES esp_driver_sdmmc esp_driver_sdspi esp_driver_gpio)
REQUIRES esp_driver_sdmmc esp_driver_sdspi esp_driver_gpio unity)
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,12 +1,15 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdmmc_test_board.h"
#include "sdkconfig.h"
#include "unity.h"
#include "soc/soc_caps.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#include "sd_pwr_ctrl.h"
const sdmmc_test_board_slot_info_t* sdmmc_test_board_get_slot_info(int slot_index)
{
@@ -32,6 +35,18 @@ void sdmmc_test_board_get_config_sdmmc(int slot_index, sdmmc_host_t *out_host_co
out_host_config->max_freq_khz = slot->max_freq_khz;
}
#if SOC_SDMMC_IO_POWER_EXTERNAL
#define SDMMC_PWR_LDO_CHANNEL 4
sd_pwr_ctrl_ldo_config_t ldo_config = {
.ldo_unit_id = SDMMC_PWR_LDO_CHANNEL,
};
sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;
TEST_ESP_OK(sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle));
out_host_config->pwr_ctrl_handle = pwr_ctrl_handle;
#endif
#if SOC_SDMMC_USE_GPIO_MATRIX
out_slot_config->clk = slot->clk;
out_slot_config->cmd = slot->cmd_mosi;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -15,6 +15,8 @@
#include "sdmmc_cmd.h"
#include "sdmmc_test_begin_end_sd.h"
#include "hal/gpio_hal.h"
#include "sd_pwr_ctrl.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
void sdmmc_test_sd_skip_if_board_incompatible(int slot, int width, int freq_khz, int ddr)
{
@@ -25,14 +27,22 @@ void sdmmc_test_sd_skip_if_board_incompatible(int slot, int width, int freq_khz,
TEST_IGNORE_MESSAGE("Board doesn't have the required slot");
}
sdmmc_test_board_get_config_sdmmc(slot, &config, &slot_config);
int board_max_freq_khz = sdmmc_test_board_get_slot_info(slot)->max_freq_khz;
if (board_max_freq_khz > 0 && board_max_freq_khz < freq_khz) {
#if SOC_SDMMC_IO_POWER_EXTERNAL
TEST_ESP_OK(sd_pwr_ctrl_del_on_chip_ldo(config.pwr_ctrl_handle));
#endif
TEST_IGNORE_MESSAGE("Board doesn't support required max_freq_khz");
}
if (slot_config.width < width) {
#if SOC_SDMMC_IO_POWER_EXTERNAL
TEST_ESP_OK(sd_pwr_ctrl_del_on_chip_ldo(config.pwr_ctrl_handle));
#endif
TEST_IGNORE_MESSAGE("Board doesn't support required bus width");
}
}
void sdmmc_test_sd_begin(int slot, int width, int freq_khz, int ddr, sdmmc_card_t *out_card)
{
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
@@ -115,4 +125,7 @@ void sdmmc_test_sd_end(sdmmc_card_t *card)
//Need to reset GPIO first, otherrwise cannot discharge VDD of card completely.
sdmmc_test_board_card_power_set(false);
#if SOC_SDMMC_IO_POWER_EXTERNAL
TEST_ESP_OK(sd_pwr_ctrl_del_on_chip_ldo(card->host.pwr_ctrl_handle));
#endif
}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,6 +9,8 @@
#include "driver/sdmmc_host.h"
#include "sdmmc_test_cd_wp_common.h"
#include "sdmmc_test_board.h"
#include "sd_pwr_ctrl.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
//TODO: IDF-8734
#if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S3
@@ -27,6 +29,9 @@ TEST_CASE("CD input works in SD mode", "[sdmmc]")
TEST_ESP_OK(sdmmc_host_deinit());
sdmmc_test_board_card_power_set(false);
#if SOC_SDMMC_IO_POWER_EXTERNAL
TEST_ESP_OK(sd_pwr_ctrl_del_on_chip_ldo(config.pwr_ctrl_handle));
#endif
}
TEST_CASE("WP input works in SD mode", "[sdmmc]")
@@ -44,5 +49,8 @@ TEST_CASE("WP input works in SD mode", "[sdmmc]")
TEST_ESP_OK(sdmmc_host_deinit());
sdmmc_test_board_card_power_set(false);
#if SOC_SDMMC_IO_POWER_EXTERNAL
TEST_ESP_OK(sd_pwr_ctrl_del_on_chip_ldo(config.pwr_ctrl_handle));
#endif
}
#endif
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -52,7 +52,7 @@ typedef struct {
esp_ldo_unit_handle_t esp_ldo_init_unit_early(const esp_ldo_unit_init_cfg_t *init_config);
/**
* @Brief Init a LDO
* @brief Init a LDO
*
* @param[in] init_config LDO initial configurations
* @param[out] ret_unit LDO unit handle
@@ -65,7 +65,7 @@ esp_ldo_unit_handle_t esp_ldo_init_unit_early(const esp_ldo_unit_init_cfg_t *ini
esp_err_t esp_ldo_init_unit(const esp_ldo_unit_init_cfg_t *init_config, esp_ldo_unit_handle_t *ret_unit);
/**
* @Brief Enable a LDO
* @brief Enable a LDO
*
* @param[in] unit LDO unit handle
*
@@ -76,6 +76,18 @@ esp_err_t esp_ldo_init_unit(const esp_ldo_unit_init_cfg_t *init_config, esp_ldo_
*/
esp_err_t esp_ldo_enable_unit(esp_ldo_unit_handle_t unit);
/**
* @brief Set LDO output voltage
*
* @param[in] unit LDO unit handle
* @param[in] voltage_mv Voltage in mV
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
*/
esp_err_t esp_ldo_set_voltage(esp_ldo_unit_handle_t unit, int voltage_mv);
/**
* @Brief Disable a LDO
*
@@ -89,7 +101,7 @@ esp_err_t esp_ldo_enable_unit(esp_ldo_unit_handle_t unit);
esp_err_t esp_ldo_disable_unit(esp_ldo_unit_handle_t unit);
/**
* @Brief Deinit a LDO
* @brief Deinit a LDO
*
* @param[in] unit LDO unit handle
*
@@ -101,7 +113,7 @@ esp_err_t esp_ldo_disable_unit(esp_ldo_unit_handle_t unit);
esp_err_t esp_ldo_deinit_unit(esp_ldo_unit_handle_t unit);
/**
* Dump LDO usages
* @brief Dump LDO usages
*
* @note This API shall not be called from an ISR.
* @note This API does not guarantee thread safety
+9
View File
@@ -149,6 +149,15 @@ esp_err_t esp_ldo_enable_unit(esp_ldo_unit_handle_t unit)
return ESP_OK;
}
esp_err_t esp_ldo_set_voltage(esp_ldo_unit_handle_t unit, int voltage_mv)
{
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ldo_ll_set_output_voltage_mv(unit->unit_id, voltage_mv);
return ESP_OK;
}
esp_err_t esp_ldo_disable_unit(esp_ldo_unit_handle_t unit)
{
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
+15 -8
View File
@@ -4,11 +4,18 @@ if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator
endif()
idf_component_register(SRCS "sdmmc_cmd.c"
"sdmmc_common.c"
"sdmmc_init.c"
"sdmmc_io.c"
"sdmmc_mmc.c"
"sdmmc_sd.c"
INCLUDE_DIRS include
PRIV_REQUIRES soc esp_timer esp_mm)
set(srcs "sdmmc_cmd.c"
"sdmmc_common.c"
"sdmmc_init.c"
"sdmmc_io.c"
"sdmmc_mmc.c"
"sdmmc_sd.c"
"sd_pwr_ctrl/sd_pwr_ctrl.c")
if(CONFIG_SOC_MULTI_USAGE_LDO_SUPPORTED)
list(APPEND srcs "sd_pwr_ctrl/sd_pwr_ctrl_by_on_chip_ldo.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS include
PRIV_REQUIRES soc esp_timer esp_mm)
+3 -1
View File
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: ISC
*
* SPDX-FileContributor: 2016-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileContributor: 2016-2024 Espressif Systems (Shanghai) CO LTD
*/
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -27,6 +27,7 @@
#include <stddef.h>
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "sd_pwr_ctrl.h"
#ifdef __cplusplus
extern "C" {
@@ -207,6 +208,7 @@ typedef struct {
sdmmc_delay_phase_t input_delay_phase; /*!< input delay phase, this will only take into effect when the host works in SDMMC_FREQ_HIGHSPEED or SDMMC_FREQ_52M. Driver will print out how long the delay is*/
esp_err_t (*set_input_delay)(int slot, sdmmc_delay_phase_t delay_phase); /*!< set input delay phase */
void* dma_aligned_buffer; /*!< Leave it NULL. Reserved for cache aligned buffers for SDIO mode */
sd_pwr_ctrl_handle_t pwr_ctrl_handle; /*!< Power control handle */
} sdmmc_host_t;
/**
+31
View File
@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "esp_bit_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief SD power control handle
*/
typedef struct sd_pwr_ctrl_drv_t *sd_pwr_ctrl_handle_t;
/**
* @brief Set SD IO voltage by a registered SD power control driver handle
*
* @param[in] handle SD power control driver handle
* @param[in] voltage_mv Voltage in mV
*/
esp_err_t sd_pwr_ctrl_set_io_voltage(sd_pwr_ctrl_handle_t handle, int voltage_mv);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "sd_pwr_ctrl.h"
#include "sd_protocol_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LDO configurations
*/
typedef struct {
int ldo_unit_id; ///< On-chip LDO ID
} sd_pwr_ctrl_ldo_config_t;
/**
* @brief New an SD power control driver via on-chip LDO
*
* @param[in] configs On-chip LDO power control driver configurations
* @param[out] ret_drv Created power control driver handle
*
* @return
* - ESP_OK
* - ESP_ERR_INVALID_ARG Invalid arguments
* - ESP_ERR_NO_MEM Out of memory
*/
esp_err_t sd_pwr_ctrl_new_on_chip_ldo(const sd_pwr_ctrl_ldo_config_t *configs, sd_pwr_ctrl_handle_t *ret_drv);
/**
* @brief Delete a previously created on-chip LDO power control driver
*
* @param[in] ctrl_handle Power control driver handle
*
* @return
* - ESP_OK
* - ESP_ERR_INVALID_ARG Invalid arguments
*/
esp_err_t sd_pwr_ctrl_del_on_chip_ldo(sd_pwr_ctrl_handle_t ctrl_handle);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_types.h"
#include "esp_err.h"
#include "sd_pwr_ctrl.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct sd_pwr_ctrl_drv_t sd_pwr_ctrl_drv_t;
struct sd_pwr_ctrl_drv_t {
/**
* @brief Set SD IO voltage
*
* @param[in] ctx SD power control specific driver context
* @param[in] voltage_mv Voltage in mV
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
*/
esp_err_t (*set_io_voltage)(void *ctx, int voltage_mv);
/**
* @brief SD power control driver context
* Can be customized to difference power control methods
*/
void *ctx;
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include "esp_types.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "soc/soc_caps.h"
#include "sd_pwr_ctrl_interface.h"
const static char *TAG = "sd_power";
esp_err_t sd_pwr_ctrl_set_io_voltage(sd_pwr_ctrl_handle_t handle, int voltage_mv)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
return handle->set_io_voltage(handle->ctx, voltage_mv);
}
@@ -0,0 +1,86 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "esp_types.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_private/esp_ldo.h"
#include "soc/soc_caps.h"
#include "sd_pwr_ctrl.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#include "sd_pwr_ctrl_interface.h"
typedef struct {
esp_ldo_unit_handle_t ldo_unit;
int voltage_mv;
} sd_pwr_ctrl_ldo_ctx_t;
const static char *TAG = "sd_ldo";
static esp_err_t s_ldo_set_voltage(void *arg, int voltage_mv);
esp_err_t sd_pwr_ctrl_new_on_chip_ldo(const sd_pwr_ctrl_ldo_config_t *configs, sd_pwr_ctrl_handle_t *ret_drv)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(configs && ret_drv, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
sd_pwr_ctrl_drv_t *driver = (sd_pwr_ctrl_drv_t *)heap_caps_calloc(1, sizeof(sd_pwr_ctrl_drv_t), MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(driver, ESP_ERR_NO_MEM, TAG, "no mem for on-chip ldo control driver");
sd_pwr_ctrl_ldo_ctx_t *ctx = (sd_pwr_ctrl_ldo_ctx_t *)heap_caps_calloc(1, sizeof(sd_pwr_ctrl_ldo_ctx_t), MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(ctx, ESP_ERR_NO_MEM, err, TAG, "no mem for on-chip ldo control driver context");
esp_ldo_unit_init_cfg_t unit_cfg = {
.unit_id = configs->ldo_unit_id,
.cfg = {
.voltage_mv = 0, //will be adjusted dynamically by sdmmc driver later
},
};
esp_ldo_unit_handle_t ldo_unit = NULL;
ESP_GOTO_ON_ERROR(esp_ldo_init_unit(&unit_cfg, &ldo_unit), err, TAG, "failed to create an on-chip LDO unit handle");
ESP_GOTO_ON_ERROR(esp_ldo_enable_unit(ldo_unit), err, TAG, "failed to enable the on-chip LDO unit");
driver->set_io_voltage = s_ldo_set_voltage;
driver->ctx = ctx;
ctx->ldo_unit = ldo_unit;
ctx->voltage_mv = 0;
*ret_drv = driver;
return ESP_OK;
err:
free(ctx);
free(driver);
return ret;
}
esp_err_t sd_pwr_ctrl_del_on_chip_ldo(sd_pwr_ctrl_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
sd_pwr_ctrl_ldo_ctx_t *ctx = handle->ctx;
ESP_RETURN_ON_ERROR(esp_ldo_disable_unit(ctx->ldo_unit), TAG, "failed to disable the on-chip LDO unit");
ESP_RETURN_ON_ERROR(esp_ldo_deinit_unit(ctx->ldo_unit), TAG, "failed to deinit the LDO unit handle");
free(handle->ctx);
handle->ctx = NULL;
free(handle);
handle = NULL;
return ESP_OK;
}
static esp_err_t s_ldo_set_voltage(void *arg, int voltage_mv)
{
//API checks done by caller
sd_pwr_ctrl_ldo_ctx_t *ctx = arg;
ESP_RETURN_ON_ERROR(esp_ldo_set_voltage(ctx->ldo_unit, voltage_mv), TAG, "failed to set LDO unit output voltage");
ctx->voltage_mv = voltage_mv;
return ESP_OK;
}
+12
View File
@@ -16,6 +16,8 @@
*/
#include "sdmmc_common.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#include "sd_pwr_ctrl.h"
static const char* TAG = "sdmmc_init";
@@ -33,6 +35,7 @@ static const char* TAG = "sdmmc_init";
esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
{
esp_err_t ret = ESP_FAIL;
memset(card, 0, sizeof(*card));
memcpy(&card->host, config, sizeof(*config));
@@ -40,6 +43,15 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
const bool always = true;
const bool io_supported = true;
if (config->pwr_ctrl_handle) {
int voltage_mv = config->io_voltage * 1000;
ret = sd_pwr_ctrl_set_io_voltage(config->pwr_ctrl_handle, voltage_mv);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "failed to set voltage (0x%x)", ret);
return ret;
}
}
/* Allocate cache-aligned buffer for SDIO over SDMMC.*/
SDMMC_INIT_STEP(!is_spi, sdmmc_allocate_aligned_buf);
@@ -967,6 +967,10 @@ config SOC_SDMMC_DELAY_PHASE_NUM
int
default 4
config SOC_SDMMC_IO_POWER_EXTERNAL
bool
default y
config SOC_SHA_DMA_MAX_BUFFER_SIZE
int
default 3968
@@ -396,6 +396,7 @@
#define SOC_SDMMC_NUM_SLOTS 2
/* Supported host clock delay phase number */
#define SOC_SDMMC_DELAY_PHASE_NUM 4
#define SOC_SDMMC_IO_POWER_EXTERNAL 1 ///< SDMMC IO power controlled by external power supply
// TODO: IDF-5353 (Copy from esp32c3, need check)
/*--------------------------- SHA CAPS ---------------------------------------*/