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 <peter.maydell@linaro.org>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
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 6ff3a79745..bc887233f5 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -946,6 +946,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)
{
@@ -1096,7 +1118,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;
}
@@ -1109,6 +1148,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;
@@ -1259,8 +1309,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