arch/xtensa/esp32s2: Add support for RTC IRQs

This commit is contained in:
Lucas Saavedra Vaz 2023-02-03 16:10:19 -03:00 committed by Alan Carvalho de Assis
parent 7df663bff8
commit 1e3af48fff
6 changed files with 337 additions and 25 deletions

View File

@ -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.
*

View File

@ -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

View File

@ -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 */

View File

@ -31,8 +31,10 @@
#include <sys/types.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#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

View File

@ -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 */

View File

@ -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: