diff --git a/components/driver/include/driver/rtc_cntl.h b/components/driver/include/driver/rtc_cntl.h new file mode 100644 index 0000000000..a79745713e --- /dev/null +++ b/components/driver/include/driver/rtc_cntl.h @@ -0,0 +1,48 @@ +// Copyright 2016-2017 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. + +#pragma once + +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" + +/** + * @brief Register a handler for specific RTC_CNTL interrupts + * + * Multiple handlers can be registered using this function. Whenever an + * RTC interrupt happens, all handlers with matching rtc_intr_mask values + * will be called. + * + * @param handler handler function to call + * @param handler_arg argument to be passed to the handler + * @param rtc_intr_mask combination of RTC_CNTL_*_INT_ENA bits indicating the + * sources to call the handler for + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM not enough memory to allocate handler structure + * - other errors returned by esp_intr_alloc + */ +esp_err_t rtc_isr_register(intr_handler_t handler, void* handler_arg, + uint32_t rtc_intr_mask); +/** + * @brief Deregister the handler previously registered using rtc_isr_register + * @param handler handler function to call (as passed to rtc_isr_register) + * @param handler_arg argument of the handler (as passed to rtc_isr_register) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if a handler matching both handler and + * handler_arg isn't registered + */ +esp_err_t rtc_isr_deregister(intr_handler_t handler, void* handler_arg); diff --git a/components/driver/rtc_module.c b/components/driver/rtc_module.c index 8de8ee2497..c6d2727e02 100644 --- a/components/driver/rtc_module.c +++ b/components/driver/rtc_module.c @@ -24,6 +24,16 @@ #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" #include "freertos/semphr.h" +#include "esp_intr_alloc.h" +#include "sys/lock.h" +#include "driver/rtc_cntl.h" + +#ifndef NDEBUG +// Enable built-in checks in queue.h in debug builds +#define INVARIANTS +#endif +#include "rom/queue.h" + static const char *RTC_MODULE_TAG = "RTC_MODULE"; @@ -792,3 +802,99 @@ int hall_sensor_read() adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_0db); return hall_sensor_get_value(); } + +/*--------------------------------------------------------------- + INTERRUPT HANDLER +---------------------------------------------------------------*/ + + +typedef struct rtc_isr_handler_ { + uint32_t mask; + intr_handler_t handler; + void* handler_arg; + SLIST_ENTRY(rtc_isr_handler_) next; +} rtc_isr_handler_t; + +static SLIST_HEAD(rtc_isr_handler_list_, rtc_isr_handler_) s_rtc_isr_handler_list = + SLIST_HEAD_INITIALIZER(s_rtc_isr_handler_list); +portMUX_TYPE s_rtc_isr_handler_list_lock = portMUX_INITIALIZER_UNLOCKED; +static intr_handle_t s_rtc_isr_handle; + +static void rtc_isr(void* arg) +{ + uint32_t status = REG_READ(RTC_CNTL_INT_ST_REG); + rtc_isr_handler_t* it; + portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); + SLIST_FOREACH(it, &s_rtc_isr_handler_list, next) { + if (it->mask & status) { + portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); + (*it->handler)(it->handler_arg); + portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); + } + } + portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); + REG_WRITE(RTC_CNTL_INT_CLR_REG, status); +} + +static esp_err_t rtc_isr_ensure_installed() +{ + esp_err_t err = ESP_OK; + portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); + if (s_rtc_isr_handle) { + goto out; + } + + REG_WRITE(RTC_CNTL_INT_ENA_REG, 0); + REG_WRITE(RTC_CNTL_INT_CLR_REG, UINT32_MAX); + err = esp_intr_alloc(ETS_RTC_CORE_INTR_SOURCE, 0, &rtc_isr, NULL, &s_rtc_isr_handle); + if (err != ESP_OK) { + goto out; + } + +out: + portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); + return err; +} + + +esp_err_t rtc_isr_register(intr_handler_t handler, void* handler_arg, uint32_t rtc_intr_mask) +{ + esp_err_t err = rtc_isr_ensure_installed(); + if (err != ESP_OK) { + return err; + } + + rtc_isr_handler_t* item = malloc(sizeof(*item)); + if (item == NULL) { + return ESP_ERR_NO_MEM; + } + item->handler = handler; + item->handler_arg = handler_arg; + item->mask = rtc_intr_mask; + portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); + SLIST_INSERT_HEAD(&s_rtc_isr_handler_list, item, next); + portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); + return ESP_OK; +} + +esp_err_t rtc_isr_deregister(intr_handler_t handler, void* handler_arg) +{ + rtc_isr_handler_t* it; + rtc_isr_handler_t* prev = NULL; + bool found = false; + portENTER_CRITICAL(&s_rtc_isr_handler_list_lock); + SLIST_FOREACH(it, &s_rtc_isr_handler_list, next) { + if (it->handler == handler && it->handler_arg == handler_arg) { + if (it == SLIST_FIRST(&s_rtc_isr_handler_list)) { + SLIST_REMOVE_HEAD(&s_rtc_isr_handler_list, next); + } else { + SLIST_REMOVE_AFTER(prev, next); + } + found = true; + break; + } + prev = it; + } + portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock); + return found ? ESP_OK : ESP_ERR_INVALID_STATE; +} diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 8804787015..6a46abbf93 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -407,7 +407,6 @@ config TASK_WDT_CHECK_IDLE_TASK_CPU1 config BROWNOUT_DET bool "Hardware brownout detect & reset" default y - depends on NEEDS_ESP32_NEW_SILICON_REV help The ESP32 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 @@ -452,16 +451,6 @@ config BROWNOUT_DET_LVL default 7 if BROWNOUT_DET_LVL_SEL_7 -config BROWNOUT_DET_RESETDELAY - int "Brownout reset delay (in uS)" - depends on BROWNOUT_DET - range 0 6820 - default 1000 - help - The brownout detector can reset the chip after a certain delay, in order to make sure e.g. a voltage dip has entirely passed - before trying to restart the chip. You can set the delay here. - - choice ESP32_TIME_SYSCALL prompt "Timers used for gettimeofday function" default ESP32_TIME_SYSCALL_USE_RTC_FRC1 diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c new file mode 100644 index 0000000000..50d8ac0442 --- /dev/null +++ b/components/esp32/brownout.c @@ -0,0 +1,61 @@ +// Copyright 2015-2017 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. + +#include +#include +#include +#include +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/cpu.h" +#include "soc/rtc_cntl_reg.h" +#include "rom/ets_sys.h" +#include "esp_system.h" +#include "driver/rtc_cntl.h" +#include "freertos/FreeRTOS.h" + +#ifdef CONFIG_BROWNOUT_DET_LVL +#define BROWNOUT_DET_LVL CONFIG_BROWNOUT_DET_LVL +#else +#define BROWNOUT_DET_LVL 0 +#endif //CONFIG_BROWNOUT_DET_LVL + +static void rtc_brownout_isr_handler() +{ + /* Normally RTC ISR clears the interrupt flag after the application-supplied + * handler returns. Since restart is called here, the flag needs to be + * cleared manually. + */ + REG_WRITE(RTC_CNTL_INT_CLR_REG, RTC_CNTL_BROWN_OUT_INT_CLR); + /* Stall the other CPU to make sure the code running there doesn't use UART + * at the same time as the following ets_printf. + */ + esp_cpu_stall(!xPortGetCoreID()); + ets_printf("\r\nBrownout detector was triggered\r\n\r\n"); + esp_restart_noos(); +} + +void esp_brownout_init() +{ + REG_WRITE(RTC_CNTL_BROWN_OUT_REG, + RTC_CNTL_BROWN_OUT_ENA /* Enable BOD */ + | RTC_CNTL_BROWN_OUT_PD_RF_ENA /* Automatically power down RF */ + /* Reset timeout must be set to >1 even if BOR feature is not used */ + | (2 << RTC_CNTL_BROWN_OUT_RST_WAIT_S) + | (BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S)); + + ESP_ERROR_CHECK( rtc_isr_register(rtc_brownout_isr_handler, NULL, RTC_CNTL_BROWN_OUT_INT_ENA_M) ); + + REG_SET_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_BROWN_OUT_INT_ENA_M); +} diff --git a/components/soc/esp32/include/esp_brownout.h b/components/esp32/include/esp_brownout.h similarity index 100% rename from components/soc/esp32/include/esp_brownout.h rename to components/esp32/include/esp_brownout.h diff --git a/components/esp32/include/esp_system.h b/components/esp32/include/esp_system.h index e7fc76b713..3921dfb8dd 100644 --- a/components/esp32/include/esp_system.h +++ b/components/esp32/include/esp_system.h @@ -59,6 +59,17 @@ void system_restore(void) __attribute__ ((deprecated)); */ void esp_restart(void) __attribute__ ((noreturn)); +/** + * @brief Internal function to restart PRO and APP CPUs. + * + * @note This function should not be called from FreeRTOS applications. + * Use esp_restart instead. + * + * This is an internal function called by esp_restart. It is called directly + * by the panic handler and brownout detector interrupt. + */ +void esp_restart_noos() __attribute__ ((noreturn)); + /** * @brief Restart system. * diff --git a/components/esp32/panic.c b/components/esp32/panic.c index f5ef3170ac..6318ca39d1 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -39,6 +39,7 @@ #include "esp_spi_flash.h" #include "esp_cache_err_int.h" #include "esp_app_trace.h" +#include "esp_system.h" #if CONFIG_SYSVIEW_ENABLE #include "SEGGER_RTT.h" #endif @@ -409,8 +410,6 @@ static void doBacktrace(XtExcFrame *frame) panicPutStr("\r\n\r\n"); } -void esp_restart_noos() __attribute__ ((noreturn)); - /* We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the serial port and either jump to the gdb stub, halt the CPU or reboot. diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index f322c93a3b..1b541f1cbf 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -265,13 +265,15 @@ void IRAM_ATTR esp_restart_noos() esp_dport_access_int_deinit(); // We need to disable TG0/TG1 watchdogs - // First enable RTC watchdog to be on the safe side + // First enable RTC watchdog for 1 second REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); REG_WRITE(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M | + (RTC_WDT_STG_SEL_RESET_SYSTEM << RTC_CNTL_WDT_STG0_S) | + (RTC_WDT_STG_SEL_RESET_RTC << RTC_CNTL_WDT_STG1_S) | (1 << RTC_CNTL_WDT_SYS_RESET_LENGTH_S) | (1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) ); - REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, 128000); + REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 1); // Disable TG0/TG1 watchdogs TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; diff --git a/components/soc/esp32/brownout.c b/components/soc/esp32/brownout.c deleted file mode 100644 index 1dcde078e3..0000000000 --- a/components/soc/esp32/brownout.c +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015-2016 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. - - -#include -#include -#include -#include -#include "sdkconfig.h" -#include "soc/soc.h" -#include "soc/rtc_cntl_reg.h" - - -#if CONFIG_BROWNOUT_DET -/* -This file is included in esp-idf, but the menuconfig option for this is disabled because a silicon bug -prohibits the brownout detector from functioning correctly on the ESP32. -*/ - -void esp_brownout_init() { - WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, - RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) | - RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) | - RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA); - -} - -#endif \ No newline at end of file diff --git a/components/soc/esp32/include/soc/rtc_cntl_reg.h b/components/soc/esp32/include/soc/rtc_cntl_reg.h index 02f8dff2c4..ffcbb3c033 100644 --- a/components/soc/esp32/include/soc/rtc_cntl_reg.h +++ b/components/soc/esp32/include/soc/rtc_cntl_reg.h @@ -1718,6 +1718,7 @@ #define RTC_WDT_STG_SEL_INT 1 #define RTC_WDT_STG_SEL_RESET_CPU 2 #define RTC_WDT_STG_SEL_RESET_SYSTEM 3 +#define RTC_WDT_STG_SEL_RESET_RTC 4 #define RTC_CNTL_WDTCONFIG1_REG (DR_REG_RTCCNTL_BASE + 0x90) /* RTC_CNTL_WDT_STG0_HOLD : R/W ;bitpos:[31:0] ;default: 32'd128000 ; */