espressif/esp_irq.c: fix up_disable_irq()/up_enable_irq() for GPIO IRQs

esp_gpio_irq() registers per-pin GPIO interrupts through
gpio_isr_handler_add(), never through esp_setup_irq(), so
esp_get_handle() never finds them and up_disable_irq()/up_enable_irq()
silently no-op for any GPIO-derived irq number. Fall back to
esp_gpioirqdisable()/esp_gpioirqenable() (translating irq back to a
pin via ESP_IRQ2PIN()) when the normal interrupt-matrix lookup misses.

This surfaced through drivers/sensors/lsm6ds3trc_uorb.c: its ISR
schedules a worker to drain the sensor's FIFO over I2C and disables
its own IRQ until the worker re-enables it, so a level-triggered
source (e.g. a PM GPIO wake source left in level mode) doesn't
refire continuously and starve every task, HPWORK included, before
the worker ever gets to run. That disable/enable only works now that
up_disable_irq()/up_enable_irq() actually do something for GPIO irqs.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Assisted-by: Claude:claude-sonnet-5
This commit is contained in:
Felipe Moura 2026-09-17 18:59:12 -03:00 • committed by Xiang Xiao
parent 074c8e3034
commit 3d267aab2a
2 changed files with 78 additions and 4 deletions

View file

@ -42,6 +42,7 @@
#include "xtensa.h"
#include "esp_gpio.h"
#include "esp_irq.h"
#include "esp_intr_alloc.h"
@ -596,6 +597,21 @@ void up_enable_irq(int irq)
if (intr_handle == IRQ_UNMAPPED)
{
/* Not a real interrupt-matrix source -- esp_gpio_irq() registers
* per-pin GPIO IRQs through gpio_isr_handler_add() instead, so
* esp_get_handle() never finds them here. Try it as a GPIO pin.
*/
#ifdef CONFIG_ESPRESSIF_GPIO_IRQ
int pin = ESP_IRQ2PIN(irq);
if (pin >= 0 && pin < SOC_GPIO_PIN_COUNT && ESP_PIN2IRQ(pin) == irq)
{
esp_gpioirqenable(pin);
return;
}
#endif
irqwarn("IRQ %d not mapped to handle\n", irq);
return;
}
@ -636,6 +652,17 @@ void up_disable_irq(int irq)
intr_handle = esp_get_handle(this_cpu(), irq);
if (intr_handle == IRQ_UNMAPPED)
{
/* See the matching comment in up_enable_irq(). */
#ifdef CONFIG_ESPRESSIF_GPIO_IRQ
int pin = ESP_IRQ2PIN(irq);
if (pin >= 0 && pin < SOC_GPIO_PIN_COUNT && ESP_PIN2IRQ(pin) == irq)
{
esp_gpioirqdisable(pin);
}
#endif
return;
}

View file

@ -25,6 +25,7 @@
#include <nuttx/config.h>
#include <nuttx/nuttx.h>
#include <nuttx/arch.h>
#include <nuttx/debug.h>
#include <nuttx/fs/fs.h>
@ -202,6 +203,8 @@ struct lsm6ds3trc_dev_s
bool interrupt_mode; /* True if using the INT pin instead of
* kthread polling */
enum lsm6ds3trc_int_e int_pin; /* Shared INT pin (interrupt mode only) */
int irq; /* IRQ number for int_pin, saved by the
* ISR so the worker can re-enable it */
struct work_s work; /* Shared interrupt work queue
* structure -- one burst read serves
* both sub-sensors */
@ -834,9 +837,8 @@ static int accel_thread(int argc, char **argv)
*
****************************************************************************/
static void lsm6ds3trc_fifo_worker(FAR void *arg)
static void lsm6ds3trc_fifo_worker_body(FAR struct lsm6ds3trc_dev_s *dev)
{
FAR struct lsm6ds3trc_dev_s *dev = arg;
uint8_t status[2];
int16_t raw[FIFO_MAX_WORDS];
int16_t raw_temp;
@ -980,6 +982,24 @@ static void lsm6ds3trc_fifo_worker(FAR void *arg)
}
}
/****************************************************************************
* Name: lsm6ds3trc_fifo_worker
*
* Description:
* work_queue() entry point. Re-enables the IRQ lsm6ds3trc_interrupt()
* disabled, after the FIFO has actually been drained -- see the comment
* on lsm6ds3trc_interrupt() for why the order matters.
*
****************************************************************************/
static void lsm6ds3trc_fifo_worker(FAR void *arg)
{
FAR struct lsm6ds3trc_dev_s *dev = arg;
lsm6ds3trc_fifo_worker_body(dev);
up_enable_irq(dev->irq);
}
#else
/****************************************************************************
* Name: lsm6ds3trc_worker
@ -993,9 +1013,8 @@ static void lsm6ds3trc_fifo_worker(FAR void *arg)
*
****************************************************************************/
static void lsm6ds3trc_worker(FAR void *arg)
static void lsm6ds3trc_worker_body(FAR struct lsm6ds3trc_dev_s *dev)
{
FAR struct lsm6ds3trc_dev_s *dev = arg;
int16_t raw[7]; /* temp, gx, gy, gz, ax, ay, az */
struct sensor_gyro gyro_data;
struct sensor_accel accel_data;
@ -1043,6 +1062,24 @@ static void lsm6ds3trc_worker(FAR void *arg)
sizeof(accel_data));
}
}
/****************************************************************************
* Name: lsm6ds3trc_worker
*
* Description:
* work_queue() entry point. Re-enables the IRQ lsm6ds3trc_interrupt()
* disabled, after the measurement has actually been drained -- see the
* comment on lsm6ds3trc_interrupt() for why the order matters.
*
****************************************************************************/
static void lsm6ds3trc_worker(FAR void *arg)
{
FAR struct lsm6ds3trc_dev_s *dev = arg;
lsm6ds3trc_worker_body(dev);
up_enable_irq(dev->irq);
}
#endif
/****************************************************************************
@ -1053,6 +1090,13 @@ static void lsm6ds3trc_worker(FAR void *arg)
* became ready -- the I2C read cannot run in interrupt context, so it's
* deferred to lsm6ds3trc_worker()/lsm6ds3trc_fifo_worker() on HPWORK.
*
* The IRQ is disabled here and only re-enabled once the worker has
* drained the condition that raised it: if a PM wake source ever leaves
* this pin level-triggered instead of edge-triggered (or the line is
* simply slow to fall), leaving the IRQ enabled would re-fire it
* continuously and starve every task, including the worker that would
* otherwise clear it.
*
****************************************************************************/
static int lsm6ds3trc_interrupt(int irq, FAR void *context, FAR void *arg)
@ -1064,6 +1108,9 @@ static int lsm6ds3trc_interrupt(int irq, FAR void *context, FAR void *arg)
DEBUGASSERT(arg != NULL);
dev->irq = irq;
up_disable_irq(irq);
dev->timestamp = sensor_get_timestamp();
#ifdef CONFIG_SENSORS_LSM6DS3TRC_FIFO