When an SPI irq line changes level, this causes what the spec describes as SET_LEVEL, SET_EDGE or CLEAR events. These also happen when the trigger mode is reconfigured, or when software requests a manual resample via the IRS_SPI_RESAMPLER register.
SET_LEVEL and SET_EDGE events make the interrupt pending, and update its handler mode to match its trigger mode. CLEAR events make the interrupt no longer pending. Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/arm_gicv5.c | 59 ++++++++++++++++++++++++++++++++++++++++++++ hw/intc/trace-events | 1 + 2 files changed, 60 insertions(+) diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c index 9f4d44f975..d1baa015d1 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -908,6 +908,28 @@ static void irs_ist_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value) } } +static void spi_sample(GICv5SPIState *spi) +{ + /* + * Sample the state of the SPI input line; this generates + * SET_EDGE, SET_LEVEL or CLEAR events which update the SPI's + * pending state and handling mode per R_HHKMN. + * The logic is the same for "the input line changed" (R_QBXXV) + * and "software asked us to resample" (R_DMTFM). + */ + if (spi->level) { + /* + * SET_LEVEL or SET_EDGE: interrupt becomes pending, and the + * handling mode is updated to match the trigger mode. + */ + spi->pending = true; + spi->hm = spi->tm == GICV5_TRIGGER_EDGE ? GICV5_EDGE : GICV5_LEVEL; + } else if (spi->tm == GICV5_TRIGGER_LEVEL) { + /* falling edges only trigger a CLEAR event for level-triggered */ + spi->pending = false; + } +} + static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset, uint64_t *data, MemTxAttrs attrs) { @@ -1055,7 +1077,24 @@ static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset, { GICv5SPIState *spi = spi_for_selr(cs, domain); if (spi) { + GICv5TriggerMode old_tm = spi->tm; spi->tm = FIELD_EX32(data, IRS_SPI_CFGR, TM); + if (spi->tm != old_tm) { + /* + * R_KBPXL: updates to SPI trigger mode can generate CLEAR or + * SET_LEVEL events. This is not the same logic as spi_sample(). + */ + if (spi->tm == GICV5_TRIGGER_LEVEL) { + if (spi->level) { + spi->pending = true; + spi->hm = GICV5_LEVEL; + } else { + spi->pending = false; + } + } else if (spi->level) { + spi->pending = false; + } + } } return true; } @@ -1068,6 +1107,17 @@ static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset, } } return true; + case A_IRS_SPI_RESAMPLER: + { + uint32_t id = FIELD_EX32(data, IRS_SPI_RESAMPLER, SPI_ID); + GICv5SPIState *spi = gicv5_spi_state(cs, id, domain); + + if (spi) { + spi_sample(spi); + } + trace_gicv5_spi_state(id, spi->level, spi->pending, spi->active); + return true; + } } return false; } @@ -1236,8 +1286,17 @@ static void gicv5_set_spi(void *opaque, int irq, int level) /* These irqs are all SPIs; the INTID is irq + s->spi_base */ GICv5Common *cs = ARM_GICV5_COMMON(opaque); uint32_t spi_id = irq + cs->spi_base; + GICv5SPIState *spi = gicv5_raw_spi_state(cs, spi_id); + + if (!spi || spi->level == level) { + return; + } trace_gicv5_spi(spi_id, level); + + spi->level = level; + spi_sample(spi); + trace_gicv5_spi_state(spi_id, spi->level, spi->pending, spi->active); } static void gicv5_reset_hold(Object *obj, ResetType type) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 409935e15a..4c55af2780 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -241,6 +241,7 @@ gicv5_set_pending(const char *domain, const char *type, bool virtual, uint32_t i gicv5_set_handling(const char *domain, const char *type, bool virtual, uint32_t id, int handling) "GICv5 IRS SetHandling %s %s virtual:%d ID %u handling %d" gicv5_set_target(const char *domain, const char *type, bool virtual, uint32_t id, uint32_t iaffid, int irm) "GICv5 IRS SetTarget %s %s virtual:%d ID %u IAFFID %u routingmode %d" gicv5_request_config(const char *domain, const char *type, bool virtual, uint32_t id, uint64_t icsr) "GICv5 IRS RequestConfig %s %s virtual:%d ID %u ICSR 0x%" PRIx64 +gicv5_spi_state(uint32_t spi_id, bool level, bool pending, bool active) "GICv5 IRS SPI ID %u now level %d pending %d active %d" # arm_gicv5_common.c gicv5_common_realize(uint32_t irsid, uint32_t num_cpus, uint32_t spi_base, uint32_t spi_irs_range, uint32_t spi_range) "GICv5 IRS realized: IRS ID %u, %u CPUs, SPI base %u, SPI IRS range %u, SPI range %u" -- 2.43.0
