fix(esp32c3): emulate GPIO interrupts

This commit is contained in:
2026-07-26 15:34:45 -04:00
parent 21de913b7e
commit 09616c2093
4 changed files with 104 additions and 0 deletions
+96
View File
@@ -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);
+3
View File
@@ -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
+2
View File
@@ -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 */
+3
View File
@@ -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 {