From 8078ad784017f91cc0ff43a4abfb34c22d6e12fe Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Tue, 20 Aug 2024 13:36:08 +0800 Subject: [PATCH] feat(brownout): Add brownout detector support on esp32c61 --- .../src/esp32c61/bootloader_esp32c61.c | 5 +- components/esp_system/port/brownout.c | 11 +- .../port/soc/esp32c61/Kconfig.system | 45 ++++++ .../hal/esp32c5/include/hal/brownout_ll.h | 2 + .../hal/esp32c61/include/hal/brownout_ll.h | 150 ++++++++++++++++++ .../esp32c61/include/soc/Kconfig.soc_caps.in | 4 + .../esp32c61/include/soc/regi2c_brownout.h | 22 +++ .../soc/esp32c61/include/soc/soc_caps.h | 2 +- 8 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 components/hal/esp32c61/include/hal/brownout_ll.h create mode 100644 components/soc/esp32c61/include/soc/regi2c_brownout.h diff --git a/components/bootloader_support/src/esp32c61/bootloader_esp32c61.c b/components/bootloader_support/src/esp32c61/bootloader_esp32c61.c index 85291e3439..8d62e4746e 100644 --- a/components/bootloader_support/src/esp32c61/bootloader_esp32c61.c +++ b/components/bootloader_support/src/esp32c61/bootloader_esp32c61.c @@ -43,6 +43,7 @@ #include "hal/efuse_hal.h" #include "hal/lpwdt_ll.h" #include "hal/regi2c_ctrl_ll.h" +#include "hal/brownout_ll.h" static const char *TAG = "boot.esp32c61"; @@ -94,8 +95,8 @@ static inline void bootloader_ana_reset_config(void) { //Enable super WDT reset. bootloader_ana_super_wdt_reset_config(true); - //Enable BOD reset TODO: IDF-9254 BOD support - // brownout_ll_ana_reset_enable(true); + //Enable BOD reset (mode1) + brownout_ll_ana_reset_enable(true); } esp_err_t bootloader_init(void) diff --git a/components/esp_system/port/brownout.c b/components/esp_system/port/brownout.c index 56609285cd..b69550ba1d 100644 --- a/components/esp_system/port/brownout.c +++ b/components/esp_system/port/brownout.c @@ -22,6 +22,8 @@ #include "hal/brownout_hal.h" #include "hal/brownout_ll.h" #include "sdkconfig.h" +#include "esp_rom_uart.h" +#include "hal/uart_ll.h" #if defined(CONFIG_ESP_BROWNOUT_DET_LVL) #define BROWNOUT_DET_LVL CONFIG_ESP_BROWNOUT_DET_LVL @@ -57,6 +59,13 @@ IRAM_ATTR static void rtc_brownout_isr_handler(void *arg) ESP_DRAM_LOGI(TAG, "Brownout detector was triggered\r\n\r\n"); } + // Flush any data left in UART FIFOs + for (int i = 0; i < SOC_UART_HP_NUM; ++i) { + if (uart_ll_is_enabled(i)) { + esp_rom_output_tx_wait_idle(i); + } + } + esp_rom_software_reset_system(); while (true) { ; @@ -77,7 +86,7 @@ void esp_brownout_init(void) brownout_hal_config(&cfg); brownout_ll_intr_clear(); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 // TODO: ESP32C61] IDF-9254 +#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 // TODO IDF-6606: LP_RTC_TIMER interrupt source is shared by lp_timer and brownout detector, but lp_timer interrupt // is not used now. An interrupt allocator is needed when lp_timer intr gets supported. esp_intr_alloc_intrstatus(ETS_LP_RTC_TIMER_INTR_SOURCE, 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, NULL); diff --git a/components/esp_system/port/soc/esp32c61/Kconfig.system b/components/esp_system/port/soc/esp32c61/Kconfig.system index e69de29bb2..cc01c03f10 100644 --- a/components/esp_system/port/soc/esp32c61/Kconfig.system +++ b/components/esp_system/port/soc/esp32c61/Kconfig.system @@ -0,0 +1,45 @@ +menu "Brownout Detector" + config ESP_BROWNOUT_DET + bool "Hardware brownout detect & reset" + depends on !IDF_ENV_FPGA + default y + help + The ESP32-C61 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.51V" + config ESP_BROWNOUT_DET_LVL_SEL_6 + bool "2.64V" + config ESP_BROWNOUT_DET_LVL_SEL_5 + bool "2.76V" + config ESP_BROWNOUT_DET_LVL_SEL_4 + bool "2.92V" + config ESP_BROWNOUT_DET_LVL_SEL_3 + bool "3.10V" + config ESP_BROWNOUT_DET_LVL_SEL_2 + bool "3.27V" + 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 + +endmenu diff --git a/components/hal/esp32c5/include/hal/brownout_ll.h b/components/hal/esp32c5/include/hal/brownout_ll.h index e103d2639c..09dcb3cb5e 100644 --- a/components/hal/esp32c5/include/hal/brownout_ll.h +++ b/components/hal/esp32c5/include/hal/brownout_ll.h @@ -113,6 +113,8 @@ static inline void brownout_ll_ana_reset_enable(bool enable) 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; + // Disable the power glitch detect. + LP_ANA_PERI.fib_enable.val &= ~(BIT2|BIT3|BIT4|BIT5); } /** diff --git a/components/hal/esp32c61/include/hal/brownout_ll.h b/components/hal/esp32c61/include/hal/brownout_ll.h new file mode 100644 index 0000000000..7a4bb9e184 --- /dev/null +++ b/components/hal/esp32c61/include/hal/brownout_ll.h @@ -0,0 +1,150 @@ +/* + * SPDX-FileCopyrightText: 2024 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.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.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.bod_mode0_cntl.bod_mode0_reset_wait = reset_wait; + LP_ANA.bod_mode0_cntl.bod_mode0_reset_ena = reset_ena; + LP_ANA.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.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.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.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.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.bod_mode1_cntl.bod_mode1_reset_ena = enable; + // Disable the power glitch detect. + LP_ANA.fib_enable.val &= ~(BIT2|BIT3|BIT4|BIT5); +} + +/** + * @brief Clear interrupt bits. + */ +__attribute__((always_inline)) +static inline void brownout_ll_intr_clear(void) +{ + LP_ANA.int_clr.bod_mode0_int_clr = 1; +} + +/** + * @brief Clear BOD internal count. + */ +static inline void brownout_ll_clear_count(void) +{ + LP_ANA.bod_mode0_cntl.bod_mode0_cnt_clr = 1; + LP_ANA.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.int_st; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in index 0284406515..ac9aa6381e 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -79,6 +79,10 @@ config SOC_SECURE_BOOT_SUPPORTED bool default y +config SOC_BOD_SUPPORTED + bool + default y + config SOC_PMU_SUPPORTED bool default y diff --git a/components/soc/esp32c61/include/soc/regi2c_brownout.h b/components/soc/esp32c61/include/soc/regi2c_brownout.h new file mode 100644 index 0000000000..f9b43ebe98 --- /dev/null +++ b/components/soc/esp32c61/include/soc/regi2c_brownout.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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/esp32c61/include/soc/soc_caps.h b/components/soc/esp32c61/include/soc/soc_caps.h index f3bedfa321..b54d351500 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -46,7 +46,7 @@ #define SOC_ECC_EXTENDED_MODES_SUPPORTED 1 #define SOC_FLASH_ENC_SUPPORTED 1 #define SOC_SECURE_BOOT_SUPPORTED 1 -// \#define SOC_BOD_SUPPORTED 1 //TODO: [ESP32C61] IDF-9254 +#define SOC_BOD_SUPPORTED 1 // \#define SOC_APM_SUPPORTED 1 //TODO: [ESP32C61] IDF-9230 #define SOC_PMU_SUPPORTED 1 //TODO: [ESP32C61] IDF-9250 // \#define SOC_LP_TIMER_SUPPORTED 1 //TODO: [ESP32C61] IDF-9244