From 09616c2093b92e70bf56d9a9ab642de02f5f93b5 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Sun, 26 Jul 2026 15:34:45 -0400 Subject: [PATCH] fix(esp32c3): emulate GPIO interrupts --- hw/gpio/esp32_gpio.c | 96 ++++++++++++++++++++++++++++++++++++ hw/gpio/esp32c3_gpio.c | 3 ++ hw/riscv/esp32c3.c | 2 + include/hw/gpio/esp32_gpio.h | 3 ++ 4 files changed, 104 insertions(+) diff --git a/hw/gpio/esp32_gpio.c b/hw/gpio/esp32_gpio.c index 754990968c..4bc3711306 100644 --- a/hw/gpio/esp32_gpio.c +++ b/hw/gpio/esp32_gpio.c @@ -27,6 +27,22 @@ #define GPIO_ENABLE_W1TS 0x24 #define GPIO_ENABLE_W1TC 0x28 #define GPIO_IN 0x3c +#define GPIO_STATUS 0x44 +#define GPIO_STATUS_W1TS 0x48 +#define GPIO_STATUS_W1TC 0x4c +#define GPIO_PCPU_INT 0x5c + +#define GPIO_PIN_INT_TYPE_SHIFT 7 +#define GPIO_PIN_INT_TYPE_MASK 0x7 +#define GPIO_PIN_INT_ENA_SHIFT 13 +#define GPIO_PIN_INT_ENA_CPU0 BIT(0) + +#define GPIO_INTR_DISABLE 0 +#define GPIO_INTR_POSEDGE 1 +#define GPIO_INTR_NEGEDGE 2 +#define GPIO_INTR_ANYEDGE 3 +#define GPIO_INTR_LOW_LEVEL 4 +#define GPIO_INTR_HIGH_LEVEL 5 static void esp32_gpio_drive_outputs(Esp32GpioState *s) { @@ -38,6 +54,41 @@ static void esp32_gpio_drive_outputs(Esp32GpioState *s) } } +static uint32_t esp32_gpio_enabled_interrupts(Esp32GpioState *s) +{ + uint32_t enabled = 0; + + for (int pin = 0; pin < ESP32_GPIO_COUNT; pin++) { + uint32_t interrupt_enable = s->pin_config[pin] >> GPIO_PIN_INT_ENA_SHIFT; + if (interrupt_enable & GPIO_PIN_INT_ENA_CPU0) { + enabled |= BIT(pin); + } + } + return enabled; +} + +static void esp32_gpio_update_irq(Esp32GpioState *s) +{ + qemu_set_irq(s->irq, !!(s->interrupt_status & + esp32_gpio_enabled_interrupts(s))); +} + +static void esp32_gpio_latch_level_interrupts(Esp32GpioState *s) +{ + uint32_t levels = qatomic_read(&s->input_level); + + for (int pin = 0; pin < ESP32_GPIO_COUNT; pin++) { + uint32_t type = extract32(s->pin_config[pin], + GPIO_PIN_INT_TYPE_SHIFT, + 3); + bool level = !!(levels & BIT(pin)); + if ((type == GPIO_INTR_LOW_LEVEL && !level) || + (type == GPIO_INTR_HIGH_LEVEL && level)) { + s->interrupt_status |= BIT(pin); + } + } +} + void esp32_gpio_set_input_level(Esp32GpioState *s, int pin, bool level) { uint32_t old_level; @@ -47,6 +98,18 @@ void esp32_gpio_set_input_level(Esp32GpioState *s, int pin, bool level) old_level = qatomic_read(&s->input_level); new_level = deposit32(old_level, pin, 1, level); } while (qatomic_cmpxchg(&s->input_level, old_level, new_level) != old_level); + + bool old_pin_level = !!(old_level & BIT(pin)); + uint32_t type = extract32(s->pin_config[pin], + GPIO_PIN_INT_TYPE_SHIFT, 3); + if ((!old_pin_level && level && type == GPIO_INTR_POSEDGE) || + (old_pin_level && !level && type == GPIO_INTR_NEGEDGE) || + (old_pin_level != level && type == GPIO_INTR_ANYEDGE) || + (!level && type == GPIO_INTR_LOW_LEVEL) || + (level && type == GPIO_INTR_HIGH_LEVEL)) { + s->interrupt_status |= BIT(pin); + } + esp32_gpio_update_irq(s); } static void esp32_gpio_set_input(void *opaque, int pin, int level) @@ -89,7 +152,15 @@ static uint64_t esp32_gpio_read(void *opaque, hwaddr addr, unsigned int size) return s->strap_mode; case GPIO_IN: return qatomic_read(&s->input_level); + case GPIO_STATUS: + return s->interrupt_status; + case GPIO_PCPU_INT: + return s->interrupt_status & esp32_gpio_enabled_interrupts(s); default: + if (addr >= s->pin_config_base && + addr < s->pin_config_base + sizeof(s->pin_config)) { + return s->pin_config[(addr - s->pin_config_base) / 4]; + } return 0; } } @@ -99,6 +170,14 @@ static void esp32_gpio_write(void *opaque, hwaddr addr, { Esp32GpioState *s = ESP32_GPIO(opaque); + if (addr >= s->pin_config_base && + addr < s->pin_config_base + sizeof(s->pin_config)) { + s->pin_config[(addr - s->pin_config_base) / 4] = value; + esp32_gpio_latch_level_interrupts(s); + esp32_gpio_update_irq(s); + return; + } + switch (addr) { case GPIO_OUT: s->output_level = value; @@ -118,6 +197,19 @@ static void esp32_gpio_write(void *opaque, hwaddr addr, case GPIO_ENABLE_W1TC: s->output_enable &= ~value; break; + case GPIO_STATUS: + s->interrupt_status = value; + esp32_gpio_update_irq(s); + return; + case GPIO_STATUS_W1TS: + s->interrupt_status |= value; + esp32_gpio_update_irq(s); + return; + case GPIO_STATUS_W1TC: + s->interrupt_status &= ~value; + esp32_gpio_latch_level_interrupts(s); + esp32_gpio_update_irq(s); + return; default: return; } @@ -137,7 +229,10 @@ static void esp32_gpio_reset_hold(Object *obj, ResetType type) s->input_level = UINT32_MAX; s->output_level = 0; s->output_enable = 0; + s->interrupt_status = 0; + memset(s->pin_config, 0, sizeof(s->pin_config)); esp32_gpio_drive_outputs(s); + esp32_gpio_update_irq(s); } static void esp32_gpio_realize(DeviceState *dev, Error **errp) @@ -151,6 +246,7 @@ static void esp32_gpio_init(Object *obj) /* Set the default value for the strap_mode property */ object_property_set_int(obj, "strap_mode", ESP32_STRAP_MODE_FLASH_BOOT, &error_fatal); + s->pin_config_base = 0x88; memory_region_init_io(&s->iomem, obj, &uart_ops, s, TYPE_ESP32_GPIO, 0x1000); diff --git a/hw/gpio/esp32c3_gpio.c b/hw/gpio/esp32c3_gpio.c index 1e842381f2..0d006b55b4 100644 --- a/hw/gpio/esp32c3_gpio.c +++ b/hw/gpio/esp32c3_gpio.c @@ -22,8 +22,11 @@ static void esp32c3_gpio_init(Object *obj) { + Esp32GpioState *s = ESP32_GPIO(obj); + /* Set the default value for the property */ object_property_set_int(obj, "strap_mode", ESP32C3_STRAP_MODE_FLASH_BOOT, &error_fatal); + s->pin_config_base = 0x74; } /* If we need to override any function from the parent (reset, realize, ...), it shall be done diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c index 4fa8b6e5e2..efb3094979 100644 --- a/hw/riscv/esp32c3.c +++ b/hw/riscv/esp32c3.c @@ -621,6 +621,8 @@ static void esp32c3_machine_init(MachineState *machine) sysbus_realize(SYS_BUS_DEVICE(&ms->gpio), &error_fatal); MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->gpio), 0); memory_region_add_subregion_overlap(sys_mem, DR_REG_GPIO_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->gpio), 0, + qdev_get_gpio_in(intmatrix_dev, ETS_GPIO_INTR_SOURCE)); } /* (Extmem) Cache realization */ diff --git a/include/hw/gpio/esp32_gpio.h b/include/hw/gpio/esp32_gpio.h index 721f5de5df..e0ec7fbd51 100644 --- a/include/hw/gpio/esp32_gpio.h +++ b/include/hw/gpio/esp32_gpio.h @@ -28,6 +28,9 @@ typedef struct Esp32GpioState { uint32_t input_level; uint32_t output_level; uint32_t output_enable; + uint32_t interrupt_status; + uint32_t pin_config[ESP32_GPIO_COUNT]; + hwaddr pin_config_base; } Esp32GpioState; typedef struct Esp32GpioClass {