> -----Original Message-----
> From: Yubin Zou <[email protected]>
> Sent: Wednesday, December 17, 2025 8:22 AM
> To: [email protected]
> Cc: Cédric Le Goater <[email protected]>; Peter Maydell
> <[email protected]>; Steven Lee <[email protected]>; Troy
> Lee <[email protected]>; Jamin Lin <[email protected]>; Andrew
> Jeffery <[email protected]>; Joel Stanley <[email protected]>;
> Fabiano Rosas <[email protected]>; Laurent Vivier <[email protected]>;
> Paolo Bonzini <[email protected]>; Kane Chen
> <[email protected]>; Nabih Estefan <[email protected]>;
> [email protected]; Yubin Zou <[email protected]>
> Subject: [PATCH v4 3/6] hw/gpio/aspeed_sgpio: Implement SGPIO interrupt
> handling
>
> The SGPIO controller can generate interrupts based on various pin state
> changes, such as rising/falling edges or high/low levels. This change adds the
> necessary logic to detect these events, update the interrupt status registers,
> and signal the interrupt to the SoC.
>
> Signed-off-by: Yubin Zou <[email protected]>
> ---
> include/hw/gpio/aspeed_sgpio.h | 2 +
> hw/gpio/aspeed_sgpio.c | 127
> ++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 127 insertions(+), 2 deletions(-)
>
> diff --git a/include/hw/gpio/aspeed_sgpio.h
> b/include/hw/gpio/aspeed_sgpio.h index
> 60279a597c722f94fba406d60cb30a52ef9544bc..8a11a9998c013cb2e4be996
> 90ecd7bcd9dcb5815 100644
> --- a/include/hw/gpio/aspeed_sgpio.h
> +++ b/include/hw/gpio/aspeed_sgpio.h
> @@ -58,7 +58,9 @@ struct AspeedSGPIOState {
>
> /*< public >*/
> MemoryRegion iomem;
> + int pending;
> qemu_irq irq;
> + qemu_irq sgpios[ASPEED_SGPIO_MAX_PIN_PAIR];
> uint32_t ctrl_regs[ASPEED_SGPIO_MAX_PIN_PAIR];
> uint32_t int_regs[ASPEED_SGPIO_MAX_INT]; }; diff --git
> a/hw/gpio/aspeed_sgpio.c b/hw/gpio/aspeed_sgpio.c index
> dc2df137db247c178adc05807bd0595fc0cb5c52..c6e6b3f52a9a982171a03cda
> 820a4573674ab67d 100644
> --- a/hw/gpio/aspeed_sgpio.c
> +++ b/hw/gpio/aspeed_sgpio.c
> @@ -12,9 +12,131 @@
> #include "qemu/error-report.h"
> #include "qapi/error.h"
> #include "qapi/visitor.h"
> +#include "hw/irq.h"
> #include "hw/qdev-properties.h"
> #include "hw/gpio/aspeed_sgpio.h"
>
> +/*
> + * For each set of gpios there are three sensitivity registers that
> +control
> + * the interrupt trigger mode.
> + *
> + * | 2 | 1 | 0 | trigger mode
> + * -----------------------------
> + * | 0 | 0 | 0 | falling-edge
> + * | 0 | 0 | 1 | rising-edge
> + * | 0 | 1 | 0 | level-low
> + * | 0 | 1 | 1 | level-high
> + * | 1 | X | X | dual-edge
> + */
> +
> +/* GPIO Interrupt Triggers */
> +#define ASPEED_FALLING_EDGE 0
> +#define ASPEED_RISING_EDGE 1
> +#define ASPEED_LEVEL_LOW 2
> +#define ASPEED_LEVEL_HIGH 3
> +#define ASPEED_DUAL_EDGE 4
> +
> +static void aspeed_clear_irq(AspeedSGPIOState *s, int idx) {
> + uint32_t reg_index = idx / 32;
> + uint32_t bit_index = idx % 32;
> + uint32_t pending = extract32(s->int_regs[reg_index], bit_index, 1);
> +
> + assert(s->pending >= pending);
> +
> + /* No change to s->pending if pending is 0 */
> + s->pending -= pending;
> +
> + /*
> + * The write acknowledged the interrupt regardless of whether it
> + * was pending or not. The post-condition is that it mustn't be
> + * pending. Unconditionally clear the status bit.
> + */
> + s->int_regs[reg_index] = deposit32(s->int_regs[reg_index],
> +bit_index, 1, 0); }
> +
> +static void aspeed_evaluate_irq(AspeedSGPIOState *s, int sgpio_prev_high,
> + int sgpio_curr_high, int idx) {
> + uint32_t ctrl = s->ctrl_regs[idx];
> + uint32_t falling_edge = 0, rising_edge = 0;
> + uint32_t int_trigger = SHARED_FIELD_EX32(ctrl, SGPIO_INT_TYPE);
> + uint32_t int_enabled = SHARED_FIELD_EX32(ctrl, SGPIO_INT_EN);
> + uint32_t reg_index = idx / 32;
> + uint32_t bit_index = idx % 32;
> +
> + if (!int_enabled) {
> + return;
> + }
> +
> + /* Detect edges */
> + if (sgpio_curr_high && !sgpio_prev_high) {
> + rising_edge = 1;
> + } else if (!sgpio_curr_high && sgpio_prev_high) {
> + falling_edge = 1;
> + }
> +
> + if (((int_trigger == ASPEED_FALLING_EDGE) && falling_edge) ||
> + ((int_trigger == ASPEED_RISING_EDGE) && rising_edge) ||
> + ((int_trigger == ASPEED_LEVEL_LOW) && !sgpio_curr_high) ||
> + ((int_trigger == ASPEED_LEVEL_HIGH) && sgpio_curr_high) ||
> + ((int_trigger >= ASPEED_DUAL_EDGE) && (rising_edge ||
> falling_edge)))
> + {
> + s->int_regs[reg_index] = deposit32(s->int_regs[reg_index],
> + bit_index, 1, 1);
> + /* Trigger the VIC IRQ */
> + s->pending++;
> + }
> +}
> +
> +static void aspeed_sgpio_update(AspeedSGPIOState *s, uint32_t idx,
> + uint32_t value) {
> + uint32_t old = s->ctrl_regs[idx];
> + uint32_t new = value;
> + uint32_t diff = (old ^ new);
> + if (diff) {
> + /* If the interrupt clear bit is set */
> + if (SHARED_FIELD_EX32(new, SGPIO_INT_STATUS)) {
> + aspeed_clear_irq(s, idx);
> + /* Clear the interrupt clear bit */
> + new &= ~SGPIO_INT_STATUS_MASK;
> + }
> +
> + /* Uppdate the control register. */
There is a small typo here ("Uppdate"). Please correct it.
> + s->ctrl_regs[idx] = new;
> +
> + /* If the output value is changed */
> + if (SHARED_FIELD_EX32(diff, SGPIO_SERIAL_OUT_VAL)) {
> + /* ...trigger the line-state IRQ */
> + qemu_set_irq(s->sgpios[idx], 1);
> + }
> +
> + /* If the input value is changed */
> + if (SHARED_FIELD_EX32(diff, SGPIO_SERIAL_IN_VAL)) {
> + aspeed_evaluate_irq(s,
> + SHARED_FIELD_EX32(old,
> SGPIO_SERIAL_IN_VAL),
> + SHARED_FIELD_EX32(new,
> SGPIO_SERIAL_IN_VAL),
> + idx);
> + }
> + }
> + qemu_set_irq(s->irq, !!(s->pending)); }
> +
> +static uint64_t aspeed_sgpio_2700_read_int_status_reg(AspeedSGPIOState
> *s,
> + uint32_t
> reg) {
> + uint32_t idx = reg - R_SGPIO_INT_STATUS_0;
> + if (idx >= ASPEED_SGPIO_MAX_INT) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: interrupt status index: %d, out of
> bounds\n",
> + __func__, idx);
> + return 0;
> + }
> + return s->int_regs[idx];
> +}
> +
> +
There seems to be an extra empty line here. It can be removed.
> static uint64_t aspeed_sgpio_2700_read_control_reg(AspeedSGPIOState *s,
> uint32_t reg) { @@ -38,7 +160,7
> @@ static void aspeed_sgpio_2700_write_control_reg(AspeedSGPIOState *s,
> __func__, idx);
> return;
> }
> - s->ctrl_regs[idx] = data;
> + aspeed_sgpio_update(s, idx, data);
> }
>
> static uint64_t aspeed_sgpio_2700_read(void *opaque, hwaddr offset, @@
> -52,6 +174,7 @@ static uint64_t aspeed_sgpio_2700_read(void *opaque,
> hwaddr offset,
>
> switch (reg) {
> case R_SGPIO_INT_STATUS_0 ... R_SGPIO_INT_STATUS_7:
> + value = aspeed_sgpio_2700_read_int_status_reg(s, reg);
> break;
> case R_SGPIO_0_CONTROL ... R_SGPIO_255_CONTROL:
> value = aspeed_sgpio_2700_read_control_reg(s, reg); @@ -116,7
> +239,7 @@ static void aspeed_sgpio_set_pin_level(AspeedSGPIOState *s, int
> pin, bool level)
> } else {
> value &= ~bit_mask;
> }
> - s->ctrl_regs[pin >> 1] = value;
> + aspeed_sgpio_update(s, pin >> 1, value);
> }
>
> static void aspeed_sgpio_get_pin(Object *obj, Visitor *v, const char *name,
>
> --
> 2.52.0.305.g3fc767764a-goog
Best Regards,
Kane