From 2b7992213c9f5c15fd5103358865377232503638 Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Sat, 9 May 2026 13:35:38 +0800 Subject: [PATCH] feat(bod): Add bod support on esp32s31 --- .../src/esp32s31/bootloader_esp32s31.c | 8 + .../esp32s31/include/hal/brownout_ll.h | 148 ++++++++++++++++++ .../esp_hw_support/power_supply/brownout.c | 3 + .../power_supply/port/esp32s31/Kconfig.power | 59 +++++++ .../esp32s31/include/soc/Kconfig.soc_caps.in | 4 + .../esp32s31/include/soc/regi2c_brownout.h | 22 +++ .../soc/esp32s31/include/soc/soc_caps.h | 2 +- components/soc/esp32s31/power_supply_periph.c | 11 ++ .../register/soc/lp_analog_peri_struct.h | 1 + .../expected_output/esp32s31.txt | 12 +- 10 files changed, 263 insertions(+), 7 deletions(-) create mode 100644 components/esp_hal_pmu/esp32s31/include/hal/brownout_ll.h create mode 100644 components/esp_hw_support/power_supply/port/esp32s31/Kconfig.power create mode 100644 components/soc/esp32s31/include/soc/regi2c_brownout.h create mode 100644 components/soc/esp32s31/power_supply_periph.c diff --git a/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c b/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c index c155b74e6cf..140f3506822 100644 --- a/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c +++ b/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c @@ -25,6 +25,7 @@ #include "hal/rwdt_ll.h" #endif #include "hal/gpio_ll.h" +#include "hal/brownout_ll.h" #include "soc/pmu_reg.h" #include "hal/regi2c_ctrl_ll.h" #include "hal/modem_lpcon_ll.h" @@ -92,6 +93,12 @@ bool bootloader_check_if_wdt_reset(int cpu, soc_reset_reason_t reset_reason) return false; } +static inline void bootloader_ana_reset_config(void) +{ + //Enable BOD reset + brownout_ll_ana_reset_enable(true); +} + #if SOC_RTC_WDT_SUPPORTED static void bootloader_super_wdt_auto_feed(void) { @@ -106,6 +113,7 @@ esp_err_t bootloader_init(void) esp_err_t ret = ESP_OK; bootloader_hardware_init(); // TODO: IDF-14696 + bootloader_ana_reset_config(); #if SOC_RTC_WDT_SUPPORTED bootloader_super_wdt_auto_feed(); #endif diff --git a/components/esp_hal_pmu/esp32s31/include/hal/brownout_ll.h b/components/esp_hal_pmu/esp32s31/include/hal/brownout_ll.h new file mode 100644 index 00000000000..4a65fd82b5c --- /dev/null +++ b/components/esp_hal_pmu/esp32s31/include/hal/brownout_ll.h @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2026 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/readme.md + ******************************************************************************/ + +#pragma once +#include +#include "esp_bit_defs.h" +#include "soc/lp_analog_peri_struct.h" +#include "hal/regi2c_ctrl.h" +#include "hal/psdet_types.h" +#include "soc/regi2c_brownout.h" + +#define BROWNOUT_DETECTOR_LL_INTERRUPT_MASK (BIT(31)) +#define BROWNOUT_DETECTOR_LL_FIB_ENABLE (BIT(1)) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief suspend the flash when a brown out happens. + * + * @param enable true: suspend flash. false: not suspend + */ +static inline void brownout_ll_enable_flash_suspend(bool enable) +{ + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_close_flash_ena = enable; +} + +/** + * @brief power down the RF circuits when a brown out happens + * + * @param enable true: power down. false: not power down. + */ +static inline void brownout_ll_enable_rf_power_down(bool enable) +{ + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_pd_rf_ena = enable; +} + +/** + * @brief Configure the brown out detector to do a hardware reset + * + * @note: If brown out interrupt is also used, the hardware reset can be disabled, + * because we can call software reset in the interrupt handler. + * + * @param reset_ena true: enable reset. false: disable reset. + * @param reset_wait brown out reset wait cycles + * @param reset_level reset level + */ +static inline void brownout_ll_reset_config(bool reset_ena, uint32_t reset_wait, brownout_reset_level_t reset_level) +{ + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_reset_wait = reset_wait; + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_reset_ena = reset_ena; + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_reset_sel = reset_level; +} + +/** + * @brief Set brown out threshold voltage + * + * @param threshold brownout threshold + */ +static inline void brownout_ll_set_threshold(uint8_t threshold) +{ + REGI2C_WRITE_MASK(I2C_BOD, I2C_BOD_THRESHOLD, threshold); +} + +/** + * @brief Set this bit to enable the brown out detection + * + * @param bod_enable true: enable, false: disable + */ +static inline void brownout_ll_bod_enable(bool bod_enable) +{ + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_intr_ena = bod_enable; +} + +/** + * @brief configure the waiting cycles before sending an interrupt + * + * @param cycle waiting cycles. + */ +static inline void brownout_ll_set_intr_wait_cycles(uint8_t cycle) +{ + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_intr_wait = cycle; +} + +/** + * @brief Enable brown out interrupt + * + * @param enable true: enable, false: disable + */ +static inline void brownout_ll_intr_enable(bool enable) +{ + LP_ANA_PERI.int_ena.bod_mode0_int_ena = enable; +} + +/** + * @brief Enable brownout hardware reset (mode1) + * + * @param enable true: enable, false: disable + */ +static inline void brownout_ll_ana_reset_enable(bool enable) +{ + // give BOD mode1 control permission to the software + LP_ANA_PERI.fib_enable.val &= ~BROWNOUT_DETECTOR_LL_FIB_ENABLE; + // then we can enable or disable if we want the BOD mode1 to reset the system + LP_ANA_PERI.bod_mode1_cntl.bod_mode1_reset_ena = enable; +} + +/** + * @brief Clear interrupt bits. + */ +__attribute__((always_inline)) +static inline void brownout_ll_intr_clear(void) +{ + LP_ANA_PERI.int_clr.val = BROWNOUT_DETECTOR_LL_INTERRUPT_MASK; +} + +/** + * @brief Clear BOD internal count. + */ +static inline void brownout_ll_clear_count(void) +{ + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_cnt_clr = 1; + LP_ANA_PERI.bod_mode0_cntl.bod_mode0_cnt_clr = 0; +} + +/** + * @brief Get interrupt status register address + * + * @return Register address + */ +static inline volatile void *brownout_ll_intr_get_status_reg(void) +{ + return &LP_ANA_PERI.int_st; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/power_supply/brownout.c b/components/esp_hw_support/power_supply/brownout.c index 4e13a7a67f5..6dc655dfc78 100644 --- a/components/esp_hw_support/power_supply/brownout.c +++ b/components/esp_hw_support/power_supply/brownout.c @@ -99,6 +99,9 @@ void esp_brownout_init(void) #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 rtc_isr_register(rtc_brownout_isr_handler, NULL, RTC_CNTL_BROWN_OUT_INT_ENA_M, RTC_INTR_FLAG_IRAM); +#elif CONFIG_IDF_TARGET_ESP32S31 + intr_handle_t bod_intr; + esp_intr_alloc_intrstatus(power_supply_periph_signal.irq, ESP_INTR_FLAG_IRAM, (uint32_t)brownout_ll_intr_get_status_reg(), BROWNOUT_DETECTOR_LL_INTERRUPT_MASK, &rtc_brownout_isr_handler, NULL, &bod_intr); #else intr_handle_t bod_intr; esp_intr_alloc_intrstatus(power_supply_periph_signal.irq, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED, (uint32_t)brownout_ll_intr_get_status_reg(), BROWNOUT_DETECTOR_LL_INTERRUPT_MASK, &rtc_brownout_isr_handler, NULL, &bod_intr); diff --git a/components/esp_hw_support/power_supply/port/esp32s31/Kconfig.power b/components/esp_hw_support/power_supply/port/esp32s31/Kconfig.power new file mode 100644 index 00000000000..38b2b1906d6 --- /dev/null +++ b/components/esp_hw_support/power_supply/port/esp32s31/Kconfig.power @@ -0,0 +1,59 @@ +menu "Power Supplier" + + menu "Brownout Detector" + config ESP_BROWNOUT_DET + bool "Hardware brownout detect & reset" + depends on !IDF_ENV_FPGA + default y + help + The ESP32-S31 has a built-in brownout detector which can detect if the voltage is lower than + a specific value. If this happens, it will reset the chip in order to prevent unintended + behaviour. + + choice ESP_BROWNOUT_DET_LVL_SEL + prompt "Brownout voltage level" + depends on ESP_BROWNOUT_DET + default ESP_BROWNOUT_DET_LVL_SEL_7 + help + The brownout detector will reset the chip when the supply voltage is approximately + below this level. Note that there may be some variation of brownout voltage level + between each chip. + + #The voltage levels here are estimates, more work needs to be done to figure out the exact voltages + #of the brownout threshold levels. + config ESP_BROWNOUT_DET_LVL_SEL_7 + bool "2.4V" + config ESP_BROWNOUT_DET_LVL_SEL_6 + bool "2.51V" + config ESP_BROWNOUT_DET_LVL_SEL_5 + bool "2.65V" + config ESP_BROWNOUT_DET_LVL_SEL_4 + bool "2.79V" + config ESP_BROWNOUT_DET_LVL_SEL_3 + bool "2.95V" + config ESP_BROWNOUT_DET_LVL_SEL_2 + bool "3.14V" + endchoice + + config ESP_BROWNOUT_DET_LVL + int + default 2 if ESP_BROWNOUT_DET_LVL_SEL_2 + default 3 if ESP_BROWNOUT_DET_LVL_SEL_3 + default 4 if ESP_BROWNOUT_DET_LVL_SEL_4 + default 5 if ESP_BROWNOUT_DET_LVL_SEL_5 + default 6 if ESP_BROWNOUT_DET_LVL_SEL_6 + default 7 if ESP_BROWNOUT_DET_LVL_SEL_7 + + config ESP_BROWNOUT_USE_INTR + bool + default n + help + This config allows to trigger an interrupt when brownout detected. Software restart will be done + at the end of the default callback. + This is because for some special workflow, the chip needs do more things when brownout happens + before restart instead of restarting directly. This part needs to be done in callback function + of interrupt. + + endmenu + +endmenu diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 47973ee3cb2..b7a4357b06c 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -163,6 +163,10 @@ config SOC_ECC_EXTENDED_MODES_SUPPORTED bool default y +config SOC_BOD_SUPPORTED + bool + default y + config SOC_PAU_SUPPORTED bool default y diff --git a/components/soc/esp32s31/include/soc/regi2c_brownout.h b/components/soc/esp32s31/include/soc/regi2c_brownout.h new file mode 100644 index 00000000000..58f816b7368 --- /dev/null +++ b/components/soc/esp32s31/include/soc/regi2c_brownout.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + */ + +#pragma once + +/** + * @file regi2c_brownout.h + * @brief Register definitions for brownout detector + * + * This file lists register fields of the brownout detector, located on an internal configuration + * bus. These definitions are used via macros defined in regi2c_ctrl.h. + */ + +#define I2C_BOD 0x61 +#define I2C_BOD_HOSTID 0 + +#define I2C_BOD_THRESHOLD 0x5 +#define I2C_BOD_THRESHOLD_MSB 2 +#define I2C_BOD_THRESHOLD_LSB 0 diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 904eca6fa1b..e160f888bfe 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -74,7 +74,7 @@ #define SOC_ECC_EXTENDED_MODES_SUPPORTED 1 // #define SOC_FLASH_ENC_SUPPORTED 1 // TODO: [ESP32S31] IDF-14628 // #define SOC_SECURE_BOOT_SUPPORTED 1 // TODO: [ESP32S31] IDF-14629 -// #define SOC_BOD_SUPPORTED 1 // TODO: [ESP32S31] IDF-14658 +#define SOC_BOD_SUPPORTED 1 // #define SOC_APM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14620 #define SOC_PAU_SUPPORTED 1 #define SOC_PMU_SUPPORTED 1 diff --git a/components/soc/esp32s31/power_supply_periph.c b/components/soc/esp32s31/power_supply_periph.c new file mode 100644 index 00000000000..e45ea11026c --- /dev/null +++ b/components/soc/esp32s31/power_supply_periph.c @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + */ + +#include "soc/power_supply_periph.h" + +const power_supply_signal_conn_t power_supply_periph_signal = { + .irq = ETS_LP_ANAPERI_INTR_SOURCE, +}; diff --git a/components/soc/esp32s31/register/soc/lp_analog_peri_struct.h b/components/soc/esp32s31/register/soc/lp_analog_peri_struct.h index dbba8fc196b..5791d03dc0f 100644 --- a/components/soc/esp32s31/register/soc/lp_analog_peri_struct.h +++ b/components/soc/esp32s31/register/soc/lp_analog_peri_struct.h @@ -257,6 +257,7 @@ typedef struct { volatile lp_ana_date_reg_t date; } lp_ana_dev_t; +extern lp_ana_dev_t LP_ANA_PERI; #ifndef __cplusplus _Static_assert(sizeof(lp_ana_dev_t) == 0x400, "Invalid size of lp_ana_dev_t structure"); diff --git a/tools/test_apps/system/esp_intr_dump/expected_output/esp32s31.txt b/tools/test_apps/system/esp_intr_dump/expected_output/esp32s31.txt index 1c832ccca09..0b5ea255bf3 100644 --- a/tools/test_apps/system/esp_intr_dump/expected_output/esp32s31.txt +++ b/tools/test_apps/system/esp_intr_dump/expected_output/esp32s31.txt @@ -1,11 +1,11 @@ CPU 0 interrupt status: Int Level Type Status - 0 1 Level Used: CPU_FROM_CPU_0 + 0 1 Level Used: LP_ANAPERI 1 * * Reserved - 2 1 Level Used: SYSTIMER_TARGET0 - 3 1 Level Used: TIMERGRP0_WDT - 4 1 Level Used: UART0 - 5 * * Free + 2 1 Level Used: CPU_FROM_CPU_0 + 3 1 Level Used: SYSTIMER_TARGET0 + 4 1 Level Used: TIMERGRP0_WDT + 5 1 Level Used: UART0 6 * * Reserved 7 * * Free 8 * * Free @@ -66,5 +66,5 @@ CPU 1 interrupt status: 29 * * Free 30 * * Free 31 * * Free -Interrupts available for general use: 46 +Interrupts available for general use: 45 Shared interrupts: 0