Skip to content

Commit

Permalink
Merge branch 'feature/esp32p4_base_ldo_support' into 'master'
Browse files Browse the repository at this point in the history
ldo: new ldo driver on P4

Closes IDF-8808

See merge request espressif/esp-idf!27728
  • Loading branch information
Icarus113 committed Dec 15, 2023
2 parents d875d02 + 9143a9d commit 30a0d84
Show file tree
Hide file tree
Showing 10 changed files with 601 additions and 399 deletions.
4 changes: 4 additions & 0 deletions components/esp_hw_support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "dma/gdma.c")
endif()

if(CONFIG_SOC_MULTI_USAGE_LDO_SUPPORTED)
list(APPEND srcs "ldo/esp_ldo.c")
endif()

if(CONFIG_SOC_ASYNC_MEMCPY_SUPPORTED)
list(APPEND srcs "dma/esp_async_memcpy.c")
if(CONFIG_SOC_GDMA_SUPPORTED)
Expand Down
123 changes: 123 additions & 0 deletions components/esp_hw_support/include/esp_private/esp_ldo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"

#ifdef __cplusplus
extern "C" {
#endif

#define ESP_LDO_ID_1 0 ///< See datasheet `VFB/VO1`
#define ESP_LDO_ID_2 1 ///< See datasheet `VFB/VO2`
#define ESP_LDO_ID_3 2 ///< See datasheet `VFB/VO3`
#define ESP_LDO_ID_4 3 ///< See datasheet `VFB/VO4`

/**
* @brief Type of LDO unit handle
*/
typedef struct ldo_unit_ctx_t *esp_ldo_unit_handle_t;

/**
* @brief LDO unit configurations
*/
typedef struct {
int voltage_mv; ///< LDO output voltage in mV
} esp_ldo_unit_cfg_t;

/**
* @brief LDO driver initial configurations
*/
typedef struct {
int unit_id; ///< LDO unit
esp_ldo_unit_cfg_t cfg; ///< LDO unit configuration
struct {
uint32_t enable_unit: 1; ///< Enable the LDO unit after it's initialised
uint32_t shared_ldo: 1; ///< Mark this LDO unit as shared
} flags; ///< LDO unit flags
} esp_ldo_unit_init_cfg_t;

/**
* @Brief Init a LDO during early stage
*
* @note This API is only for early stage usage
*
* @param[in] unit_id LDO unit ID
* @param[in] cfg LDO unit configuration
*
* @return LDO unit handle
*/
esp_ldo_unit_handle_t esp_ldo_init_unit_early(const esp_ldo_unit_init_cfg_t *init_config);

/**
* @Brief Init a LDO
*
* @param[in] init_config LDO initial configurations
* @param[out] ret_unit LDO unit handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_NOT_FOUND: LDO unit is in use already
*/
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
*
* @param[in] unit LDO unit handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_INVALID_STATE: LDO is enabled already
*/
esp_err_t esp_ldo_enable_unit(esp_ldo_unit_handle_t unit);

/**
* @Brief Disable a LDO
*
* @param[in] unit LDO unit handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_INVALID_STATE: LDO is disabled already
*/
esp_err_t esp_ldo_disable_unit(esp_ldo_unit_handle_t unit);

/**
* @Brief Deinit a LDO
*
* @param[in] unit LDO unit handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_INVALID_STATE: LDO is still enabled
*/
esp_err_t esp_ldo_deinit_unit(esp_ldo_unit_handle_t unit);

/**
* Dump LDO usages
*
* @note This API shall not be called from an ISR.
* @note This API does not guarantee thread safety
*
* @param stream stream to print information to; use stdout or stderr to print
* to the console; use fmemopen/open_memstream to print to a
* string buffer.
* @return
* - ESP_OK
*/
esp_err_t esp_ldo_usage_dump(FILE* stream);

#ifdef __cplusplus
}
#endif
208 changes: 208 additions & 0 deletions components/esp_hw_support/ldo/esp_ldo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdlib.h>
#include <string.h>
#include "sdkconfig.h"
#include "stdatomic.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "soc/soc_caps.h"
#include "hal/ldo_ll.h"
#include "esp_private/esp_ldo.h"

static const DRAM_ATTR char TAG[] = "ldo";

typedef struct ldo_unit_ctx_t {
int unit_id;
int voltage_mv;
bool enabled;
int ref_cnt;
portMUX_TYPE spinlock;
} ldo_unit_ctx_t;

typedef struct ldo_ctx_t {
_lock_t mutex;
ldo_unit_ctx_t units[LDO_LL_UNIT_NUM];
} ldo_ctx_t;

static ldo_ctx_t s_ctx; //LDO context
//LDO1 is always reserved for Flash usage
static atomic_bool s_ldo_unit_claimed[LDO_LL_UNIT_NUM] = {ATOMIC_VAR_INIT(true),
ATOMIC_VAR_INIT(false),
ATOMIC_VAR_INIT(false),
ATOMIC_VAR_INIT(false)};

static bool s_ldo_unit_claim(uint32_t unit);
static bool s_ldo_unit_free(uint32_t unit);
static bool s_ldo_unit_needs_claim(const esp_ldo_unit_init_cfg_t *init_config);


//This API should always success
esp_ldo_unit_handle_t esp_ldo_init_unit_early(const esp_ldo_unit_init_cfg_t *init_config)
{
assert(init_config);
assert(init_config->unit_id < LDO_LL_UNIT_NUM);

ldo_unit_ctx_t *unit = &s_ctx.units[init_config->unit_id];
bool needs_claim = s_ldo_unit_needs_claim(init_config);
bool success_claim = false;

if (needs_claim) {
success_claim = s_ldo_unit_claim(init_config->unit_id);
assert(success_claim);
unit->unit_id = init_config->unit_id;
unit->voltage_mv = init_config->cfg.voltage_mv;
unit->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
ldo_ll_set_output_voltage_mv(init_config->unit_id, init_config->cfg.voltage_mv);

if (init_config->flags.enable_unit) {
ldo_ll_enable(init_config->unit_id, true);
unit->enabled = true;
}
} else {
bool same_voltage = init_config->cfg.voltage_mv == unit->voltage_mv;
assert(same_voltage);
}

unit->ref_cnt++;

return unit;
}

esp_err_t esp_ldo_init_unit(const esp_ldo_unit_init_cfg_t *init_config, esp_ldo_unit_handle_t *ret_unit)
{
ESP_RETURN_ON_FALSE(init_config && ret_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(init_config->unit_id < LDO_LL_UNIT_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid unit");

ldo_unit_ctx_t *unit = &s_ctx.units[init_config->unit_id];
bool needs_claim = s_ldo_unit_needs_claim(init_config);
bool success_claim = false;

if (needs_claim) {
success_claim = s_ldo_unit_claim(init_config->unit_id);
ESP_RETURN_ON_FALSE(success_claim, ESP_ERR_NOT_FOUND, TAG, "ldo%d is already in use", init_config->unit_id + 1);
unit->unit_id = init_config->unit_id;
unit->voltage_mv = init_config->cfg.voltage_mv;
unit->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
ldo_ll_set_output_voltage_mv(init_config->unit_id, init_config->cfg.voltage_mv);
if (init_config->flags.enable_unit) {
ldo_ll_enable(init_config->unit_id, true);
unit->enabled = true;
}
ESP_LOGD(TAG, "new ldo unit%d is created", unit->unit_id);
} else {
bool same_voltage = init_config->cfg.voltage_mv == unit->voltage_mv;
ESP_RETURN_ON_FALSE(same_voltage, ESP_ERR_INVALID_ARG, TAG, "not same voltage, cannot share ldo%d", init_config->unit_id + 1);
ESP_LOGD(TAG, "new ldo unit%d is shared", unit->unit_id);
}

portENTER_CRITICAL(&unit->spinlock);
unit->ref_cnt++;
portEXIT_CRITICAL(&unit->spinlock);

*ret_unit = unit;
return ESP_OK;
}

esp_err_t esp_ldo_deinit_unit(esp_ldo_unit_handle_t unit)
{
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(unit->enabled == false, ESP_ERR_INVALID_STATE, TAG, "invalid state: ldo unit is still enabled");

bool needs_free = false;
portENTER_CRITICAL(&unit->spinlock);
unit->ref_cnt -= 1;
if (unit->ref_cnt == 0) {
needs_free = true;
}
portEXIT_CRITICAL(&unit->spinlock);

if (needs_free) {
bool success_free = false;
success_free = s_ldo_unit_free(unit->unit_id);
ESP_RETURN_ON_FALSE(success_free, ESP_ERR_NOT_FOUND, TAG, "ldo%d isn't in use", unit->unit_id + 1);
ESP_LOGD(TAG, "ldo unit%d is deleted", unit->unit_id + 1);
_lock_acquire(&s_ctx.mutex);
memset(&s_ctx.units[unit->unit_id], 0x0, sizeof(ldo_unit_ctx_t));
_lock_release(&s_ctx.mutex);
}

return ESP_OK;
}

esp_err_t esp_ldo_enable_unit(esp_ldo_unit_handle_t unit)
{
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(unit->enabled == false, ESP_ERR_INVALID_STATE, TAG, "invalid state: ldo unit is enabled already");

ldo_ll_enable(unit->unit_id, true);
unit->enabled = true;

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");
ESP_RETURN_ON_FALSE(unit->enabled == true, ESP_ERR_INVALID_STATE, TAG, "invalid state: ldo unit is disabled already");

ldo_ll_enable(unit->unit_id, false);
unit->enabled = false;

return ESP_OK;
}

esp_err_t esp_ldo_usage_dump(FILE* stream)
{
char line[100];
fprintf(stream, "LDOs Info:\n");
for (int i = 0; i < LDO_LL_UNIT_NUM; i++) {
fprintf(stream, "%-15s %-14s %-14s\n", "LDO ID", "voltage_mv", "enabled");

char *buf = line;
size_t len = sizeof(line);
memset(line, 0x0, len);
snprintf(buf, len, "%-15d 0x%-12d %-11x\n",
s_ctx.units[i].unit_id + 1,
s_ctx.units[i].voltage_mv,
s_ctx.units[i].enabled);
fputs(line, stream);
}

fprintf(stream, "You can use a disabled LDO unit, or share an enabled LDO unit\n");

return ESP_OK;
}

static bool s_ldo_unit_claim(uint32_t unit)
{
bool false_var = false;
return atomic_compare_exchange_strong(&s_ldo_unit_claimed[unit], &false_var, true);
}

static bool s_ldo_unit_free(uint32_t unit)
{
bool true_var = true;
return atomic_compare_exchange_strong(&s_ldo_unit_claimed[unit], &true_var, false);
}

static bool s_ldo_unit_needs_claim(const esp_ldo_unit_init_cfg_t *init_config)
{
bool needs_claim = false;

if (s_ctx.units[init_config->unit_id].ref_cnt == 0) {
needs_claim = true;
} else {
if (!init_config->flags.shared_ldo) {
needs_claim = true;
}
}

return needs_claim;
}
4 changes: 4 additions & 0 deletions components/esp_hw_support/ldo/linker.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[mapping:ldo_driver]
archive: libesp_hw_support.a
entries:
esp_ldo: esp_ldo_init_unit_early (noflash)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ set(srcs "test_app_main.c"
"test_random.c"
)

if(CONFIG_SOC_MULTI_USAGE_LDO_SUPPORTED)
list(APPEND srcs "test_ldo.c")
endif()

if(CONFIG_SOC_GPIO_CLOCKOUT_BY_GPIO_MATRIX OR CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX)
list(APPEND srcs "test_esp_clock_output.c")
endif()
Expand Down
Loading

0 comments on commit 30a0d84

Please sign in to comment.