diff --git a/arch/xtensa/include/esp32s2/irq.h b/arch/xtensa/include/esp32s2/irq.h index 730ddd82be..9028fa850b 100644 --- a/arch/xtensa/include/esp32s2/irq.h +++ b/arch/xtensa/include/esp32s2/irq.h @@ -295,9 +295,53 @@ # define ESP32S2_NIRQ_GPIO 0 #endif +#ifdef CONFIG_ESP32S2_RTCIO_IRQ + +/* Second level RTC interrupts. RTC interrupts are decoded and dispatched + * as a second level of decoding: The first level dispatches to the RTC + * interrupt handler. The second to the decoded RTC interrupt handler. + * A third level might be required to be implemented on the driver (e.g. + * Touch pads) + */ + +# define ESP32S2_NIRQ_RTCIO_PERIPH 20 +# define ESP32S2_NIRQ_RTCIO_TOUCHPAD 15 +# define ESP32S2_NIRQ_RTCIO (ESP32S2_NIRQ_RTCIO_PERIPH+ESP32S2_NIRQ_RTCIO_TOUCHPAD) + +# define ESP32S2_FIRST_RTCIOIRQ_PERIPH (XTENSA_NIRQ_INTERNAL+ESP32S2_NIRQ_PERIPH+ESP32S2_NIRQ_GPIO) +# define ESP32S2_LAST_RTCIOIRQ_PERIPH (ESP32S2_FIRST_RTCIOIRQ_PERIPH+ESP32S2_NIRQ_RTCIO_PERIPH-1) +# define ESP32S2_IRQ_RTC_SLP_WAKEUP (ESP32S2_FIRST_RTCIOIRQ_PERIPH+0) +# define ESP32S2_IRQ_RTC_SLP_REJECT (ESP32S2_FIRST_RTCIOIRQ_PERIPH+1) +# define ESP32S2_IRQ_RTC_SDIO_IDLE (ESP32S2_FIRST_RTCIOIRQ_PERIPH+2) +# define ESP32S2_IRQ_RTC_WDT (ESP32S2_FIRST_RTCIOIRQ_PERIPH+3) +# define ESP32S2_IRQ_RTC_TOUCH_SCAN_DONE (ESP32S2_FIRST_RTCIOIRQ_PERIPH+4) +# define ESP32S2_IRQ_RTC_ULP_CP (ESP32S2_FIRST_RTCIOIRQ_PERIPH+5) +# define ESP32S2_IRQ_RTC_TOUCH_DONE (ESP32S2_FIRST_RTCIOIRQ_PERIPH+6) +# define ESP32S2_IRQ_RTC_TOUCH_ACTIVE (ESP32S2_FIRST_RTCIOIRQ_PERIPH+7) +# define ESP32S2_IRQ_RTC_TOUCH_INACTIVE (ESP32S2_FIRST_RTCIOIRQ_PERIPH+8) +# define ESP32S2_IRQ_RTC_BROWN_OUT (ESP32S2_FIRST_RTCIOIRQ_PERIPH+9) +# define ESP32S2_IRQ_RTC_MAIN_TIMER (ESP32S2_FIRST_RTCIOIRQ_PERIPH+10) +# define ESP32S2_IRQ_RTC_SARADC1 (ESP32S2_FIRST_RTCIOIRQ_PERIPH+11) +# define ESP32S2_IRQ_RTC_TSENS (ESP32S2_FIRST_RTCIOIRQ_PERIPH+12) +# define ESP32S2_IRQ_RTC_COCPU (ESP32S2_FIRST_RTCIOIRQ_PERIPH+13) +# define ESP32S2_IRQ_RTC_SARADC2 (ESP32S2_FIRST_RTCIOIRQ_PERIPH+14) +# define ESP32S2_IRQ_RTC_SWD (ESP32S2_FIRST_RTCIOIRQ_PERIPH+15) +# define ESP32S2_IRQ_RTC_XTAL32K_DEAD (ESP32S2_FIRST_RTCIOIRQ_PERIPH+16) +# define ESP32S2_IRQ_RTC_COCPU_TRAP (ESP32S2_FIRST_RTCIOIRQ_PERIPH+17) +# define ESP32S2_IRQ_RTC_TOUCH_TIMEOUT (ESP32S2_FIRST_RTCIOIRQ_PERIPH+18) +# define ESP32S2_IRQ_RTC_GLITCH_DET (ESP32S2_FIRST_RTCIOIRQ_PERIPH+19) + +# define ESP32S2_FIRST_RTCIOIRQ_TOUCHPAD (ESP32S2_LAST_RTCIOIRQ_PERIPH+1) +# define ESP32S2_LAST_RTCIOIRQ_TOUCHPAD (ESP32S2_FIRST_RTCIOIRQ_TOUCHPAD+ESP32S2_NIRQ_RTCIO_TOUCHPAD-1) +# define ESP32S2_TOUCHPAD2IRQ(t) ((t) + ESP32S2_FIRST_RTCIOIRQ_TOUCHPAD) +# define ESP32S2_IRQ2TOUCHPAD(i) ((i) - ESP32S2_FIRST_RTCIOIRQ_TOUCHPAD) +#else +# define ESP32S2_NIRQ_RTCIO 0 +#endif + /* Total number of interrupts */ -#define NR_IRQS (XTENSA_NIRQ_INTERNAL + ESP32S2_NIRQ_PERIPH + ESP32S2_NIRQ_GPIO) +#define NR_IRQS (XTENSA_NIRQ_INTERNAL + ESP32S2_NIRQ_PERIPH + ESP32S2_NIRQ_GPIO + ESP32S2_NIRQ_RTCIO) /* Xtensa CPU Interrupts. * diff --git a/arch/xtensa/src/esp32s2/Kconfig b/arch/xtensa/src/esp32s2/Kconfig index b1b0930bb0..775a83680a 100644 --- a/arch/xtensa/src/esp32s2/Kconfig +++ b/arch/xtensa/src/esp32s2/Kconfig @@ -408,6 +408,7 @@ config ESP32S2_RWDT bool "RTC Watchdog Timer" default n select ESP32S2_WDT + select ESP32S2_RTCIO_IRQ ---help--- Includes RWDT. This watchdog timer is from the RTC module. When it is selected, if the developer sets it to reset on expiration @@ -465,6 +466,12 @@ config ESP32S2_GPIO_IRQ ---help--- Enable support for interrupting GPIO pins. +config ESP32S2_RTCIO_IRQ + bool "RTC IO interrupts" + default n + ---help--- + Enable support for RTC peripherals interruptions. + menu "SPI configuration" depends on ESP32S2_SPI diff --git a/arch/xtensa/src/esp32s2/esp32s2_irq.c b/arch/xtensa/src/esp32s2/esp32s2_irq.c index cf14bb2113..509b32ef67 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_irq.c +++ b/arch/xtensa/src/esp32s2/esp32s2_irq.c @@ -40,6 +40,7 @@ #ifdef CONFIG_ESP32S2_GPIO_IRQ #include "esp32s2_gpio.h" #endif +#include "esp32s2_rtc_gpio.h" #include "esp32s2_irq.h" #include "hardware/esp32s2_soc.h" #include "hardware/esp32s2_system.h" @@ -299,6 +300,10 @@ void up_irqinitialize(void) esp32s2_gpioirqinitialize(); #endif + /* Initialize RTCIO interrupt support */ + + esp32s2_rtcioirqinitialize(); + #ifndef CONFIG_SUPPRESS_INTERRUPTS /* And finally, enable interrupts. Also clears PS.EXCM */ diff --git a/arch/xtensa/src/esp32s2/esp32s2_rtc_gpio.c b/arch/xtensa/src/esp32s2/esp32s2_rtc_gpio.c index 4da50dcae8..c30531ae17 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_rtc_gpio.c +++ b/arch/xtensa/src/esp32s2/esp32s2_rtc_gpio.c @@ -31,8 +31,10 @@ #include #include +#include #include "xtensa.h" +#include "esp32s2_irq.h" #include "esp32s2_rtc_gpio.h" #include "hardware/esp32s2_rtc_io.h" #include "hardware/esp32s2_sens.h" @@ -58,6 +60,11 @@ enum rtcio_lh_out_mode_e * Private Data ****************************************************************************/ +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +static int g_rtcio_cpuint; +static uint32_t last_status; +#endif + static const uint32_t rtc_gpio_to_addr[] = { RTCIO_RTC_GPIO_PIN0_REG, @@ -94,6 +101,12 @@ static const uint32_t rtc_gpio_to_addr[] = * Description: * Determine if the specified rtcio_num is a valid RTC GPIO. * + * Input Parameters: + * rtcio_num - RTC GPIO to be checked. + * + * Returned Value: + * True if valid. False otherwise. + * ****************************************************************************/ static inline bool is_valid_rtc_gpio(uint32_t rtcio_num) @@ -101,6 +114,85 @@ static inline bool is_valid_rtc_gpio(uint32_t rtcio_num) return (rtcio_num < RTC_GPIO_NUMBER); } +/**************************************************************************** + * Name: rtcio_dispatch + * + * Description: + * Second level dispatch for the RTC interrupt. + * + * Input Parameters: + * irq - The IRQ number; + * reg_status - Pointer to a copy of the interrupt status register. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +static void rtcio_dispatch(int irq, uint32_t *reg_status) +{ + uint32_t status = *reg_status; + uint32_t mask; + int i; + + /* Check each bit in the status register */ + + for (i = 0; i < ESP32S2_NIRQ_RTCIO_PERIPH && status != 0; i++) + { + /* Check if there is an interrupt pending for this type */ + + mask = (UINT32_C(1) << i); + if ((status & mask) != 0) + { + /* Yes... perform the second level dispatch. The IRQ context will + * contain the contents of the status register. + */ + + irq_dispatch(irq + i, (void *)reg_status); + + /* Clear the bit in the status so that we might execute this loop + * sooner. + */ + + status &= ~mask; + } + } +} +#endif + +/**************************************************************************** + * Name: rtcio_interrupt + * + * Description: + * RTC interrupt handler. + * + * Input Parameters: + * irq - The IRQ number; + * context - The interrupt context; + * args - The arguments passed to the handler. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +static int rtcio_interrupt(int irq, void *context, void *arg) +{ + /* Read and clear the lower RTC interrupt status */ + + last_status = getreg32(RTC_CNTL_INT_ST_RTC_REG); + putreg32(last_status, RTC_CNTL_INT_CLR_RTC_REG); + + /* Dispatch pending interrupts in the RTC status register */ + + rtcio_dispatch(ESP32S2_FIRST_RTCIOIRQ_PERIPH, &last_status); + + return OK; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -249,3 +341,94 @@ int esp32s2_configrtcio(int rtcio_num, rtcio_pinattr_t attr) return OK; } + +/**************************************************************************** + * Name: esp32s2_rtcioirqinitialize + * + * Description: + * Initialize logic to support a second level of interrupt decoding for + * RTC interrupts. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +void esp32s2_rtcioirqinitialize(void) +{ + /* Setup the RTCIO interrupt. */ + + g_rtcio_cpuint = esp32s2_setup_irq(ESP32S2_PERIPH_RTC_CORE, + 1, ESP32S2_CPUINT_LEVEL); + DEBUGASSERT(g_rtcio_cpuint >= 0); + + /* Attach and enable the interrupt handler */ + + DEBUGVERIFY(irq_attach(ESP32S2_IRQ_RTC_CORE, rtcio_interrupt, NULL)); + up_enable_irq(ESP32S2_IRQ_RTC_CORE); +} +#endif + +/**************************************************************************** + * Name: esp32s2_rtcioirqenable + * + * Description: + * Enable the interrupt for a specified RTC IRQ + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +void esp32s2_rtcioirqenable(int irq) +{ + uintptr_t regaddr = RTC_CNTL_INT_ENA_RTC_REG; + uint32_t regval; + int bit; + + DEBUGASSERT(irq >= ESP32S2_FIRST_RTCIOIRQ_PERIPH && + irq <= ESP32S2_LAST_RTCIOIRQ_PERIPH); + + /* Convert the IRQ number to the corresponding bit */ + + bit = irq - ESP32S2_FIRST_RTCIOIRQ_PERIPH; + + /* Get the address of the GPIO PIN register for this pin */ + + up_disable_irq(ESP32S2_IRQ_RTC_CORE); + + regval = getreg32(regaddr) | (UINT32_C(1) << bit); + putreg32(regval, regaddr); + + up_enable_irq(ESP32S2_IRQ_RTC_CORE); +} +#endif + +/**************************************************************************** + * Name: esp32s2_rtcioirqdisable + * + * Description: + * Disable the interrupt for a specified RTC IRQ + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +void esp32s2_rtcioirqdisable(int irq) +{ + uintptr_t regaddr = RTC_CNTL_INT_ENA_RTC_REG; + uint32_t regval; + int bit; + + DEBUGASSERT(irq >= ESP32S2_FIRST_RTCIOIRQ_PERIPH && + irq <= ESP32S2_LAST_RTCIOIRQ_PERIPH); + + /* Convert the IRQ number to the corresponding bit */ + + bit = irq - ESP32S2_FIRST_RTCIOIRQ_PERIPH; + + /* Disable IRQ */ + + up_disable_irq(ESP32S2_IRQ_RTC_CORE); + + regval = getreg32(regaddr) & (~(UINT32_C(1) << bit)); + putreg32(regval, regaddr); + + up_enable_irq(ESP32S2_IRQ_RTC_CORE); +} +#endif diff --git a/arch/xtensa/src/esp32s2/esp32s2_rtc_gpio.h b/arch/xtensa/src/esp32s2/esp32s2_rtc_gpio.h index 568dd88f0c..9a91cd4aa9 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_rtc_gpio.h +++ b/arch/xtensa/src/esp32s2/esp32s2_rtc_gpio.h @@ -574,5 +574,48 @@ static const rtc_io_desc_t g_rtc_io_desc[RTC_GPIO_NUMBER] = int esp32s2_configrtcio(int rtcio_num, rtcio_pinattr_t attr); +/**************************************************************************** + * Name: esp32s2_rtcioirqinitialize + * + * Description: + * Initialize logic to support a second level of interrupt decoding for + * RTC IRQs. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +void esp32s2_rtcioirqinitialize(void); +#else +# define esp32s2_rtcioirqinitialize() +#endif + +/**************************************************************************** + * Name: esp32s2_rtcioirqenable + * + * Description: + * Enable the interrupt for the specified RTC peripheral IRQ + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +void esp32s2_rtcioirqenable(int irq); +#else +# define esp32s2_rtcioirqenable(irq) +#endif + +/**************************************************************************** + * Name: esp32s2_rtcioirqdisable + * + * Description: + * Disable the interrupt for the specified RTC peripheral IRQ + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_RTCIO_IRQ +void esp32s2_rtcioirqdisable(int irq); +#else +# define esp32s2_rtcioirqdisable(irq) +#endif + #endif /* __ASSEMBLY__ */ #endif /* __ARCH_XTENSA_SRC_ESP32S2_ESP32S2_RTC_GPIO_H */ diff --git a/arch/xtensa/src/esp32s2/esp32s2_wdt.c b/arch/xtensa/src/esp32s2/esp32s2_wdt.c index 5aa77cc5eb..1fd5f04937 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_wdt.c +++ b/arch/xtensa/src/esp32s2/esp32s2_wdt.c @@ -31,6 +31,7 @@ #include "xtensa.h" #include "esp32s2_irq.h" +#include "esp32s2_rtc_gpio.h" #include "esp32s2_wdt.h" #include "hardware/esp32s2_efuse.h" #include "hardware/esp32s2_rtccntl.h" @@ -166,7 +167,7 @@ struct esp32s2_wdt_priv_s g_esp32s2_rwdt_priv = .ops = &esp32s2_rwdt_ops, .base = RTC_CNTL_OPTIONS0_REG, .periph = ESP32S2_PERIPH_RTC_CORE, - .irq = ESP32S2_IRQ_RTC_CORE, + .irq = ESP32S2_IRQ_RTC_WDT, .cpuint = -ENOMEM, .inuse = false, }; @@ -634,13 +635,23 @@ static int32_t wdt_setisr(struct esp32s2_wdt_dev_s *dev, xcpt_t handler, if (wdt->cpuint >= 0) { - /* Disable CPU Interrupt, free a previously allocated - * CPU Interrupt - */ +#ifdef CONFIG_ESP32S2_RWDT + if (wdt->irq == ESP32S2_IRQ_RTC_WDT) + { + esp32s2_rtcioirqdisable(wdt->irq); + irq_detach(wdt->irq); + } + else +#endif + { + /* Disable CPU Interrupt, free a previously allocated + * CPU Interrupt + */ - up_disable_irq(wdt->irq); - esp32s2_teardown_irq(wdt->periph, wdt->cpuint); - irq_detach(wdt->irq); + up_disable_irq(wdt->irq); + esp32s2_teardown_irq(wdt->periph, wdt->cpuint); + irq_detach(wdt->irq); + } } goto errout; @@ -652,27 +663,46 @@ static int32_t wdt_setisr(struct esp32s2_wdt_dev_s *dev, xcpt_t handler, { /* Set up to receive peripheral interrupts on the current CPU */ - wdt->cpuint = esp32s2_setup_irq(wdt->periph, 1, ESP32S2_CPUINT_LEVEL); - if (wdt->cpuint < 0) +#ifdef CONFIG_ESP32S2_RWDT + if (wdt->irq == ESP32S2_IRQ_RTC_WDT) { - wderr("ERROR: No CPU Interrupt available"); - ret = wdt->cpuint; - goto errout; + ret = irq_attach(wdt->irq, handler, arg); + + if (ret != OK) + { + esp32s2_rtcioirqdisable(wdt->irq); + tmrerr("ERROR: Failed to associate an IRQ Number"); + goto errout; + } + + esp32s2_rtcioirqenable(wdt->irq); } - - /* Associate an IRQ Number (from the WDT) to an ISR */ - - ret = irq_attach(wdt->irq, handler, arg); - if (ret != OK) + else +#endif { - esp32s2_teardown_irq(wdt->periph, wdt->cpuint); - wderr("ERROR: Failed to associate an IRQ Number"); - goto errout; + wdt->cpuint = esp32s2_setup_irq(wdt->periph, 1, + ESP32S2_CPUINT_LEVEL); + if (wdt->cpuint < 0) + { + wderr("ERROR: No CPU Interrupt available"); + ret = wdt->cpuint; + goto errout; + } + + /* Associate an IRQ Number (from the WDT) to an ISR */ + + ret = irq_attach(wdt->irq, handler, arg); + if (ret != OK) + { + esp32s2_teardown_irq(wdt->periph, wdt->cpuint); + wderr("ERROR: Failed to associate an IRQ Number"); + goto errout; + } + + /* Enable the CPU Interrupt that is linked to the WDT */ + + up_enable_irq(wdt->irq); } - - /* Enable the CPU Interrupt that is linked to the WDT */ - - up_enable_irq(wdt->irq); } errout: