summaryrefslogtreecommitdiff
path: root/sys/arch/arm64
diff options
context:
space:
mode:
authorPatrick Wildt <patrick@cvs.openbsd.org>2020-11-15 17:27:33 +0000
committerPatrick Wildt <patrick@cvs.openbsd.org>2020-11-15 17:27:33 +0000
commitf067d1343861d68e5d23906cb7201ef052670e09 (patch)
tree5629d052ecf4e41a953d894c78a97e4bfa782c7a /sys/arch/arm64
parent19c086220b33de6c104e8d7b341a4aeae99871c7 (diff)
Add support for edge-triggered interrupts to agintc(4). This is mostly
based on the ampintc(4) version, which already has support for it. For now only do this for SPIs. SGIs are always edge-triggered and cannot be modified, and PPIs can be both, but let's keep it safe for now. ok kettenis@
Diffstat (limited to 'sys/arch/arm64')
-rw-r--r--sys/arch/arm64/dev/agintc.c49
1 files changed, 41 insertions, 8 deletions
diff --git a/sys/arch/arm64/dev/agintc.c b/sys/arch/arm64/dev/agintc.c
index f03e361bd7d..27b56e396cc 100644
--- a/sys/arch/arm64/dev/agintc.c
+++ b/sys/arch/arm64/dev/agintc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: agintc.c,v 1.27 2020/09/05 14:47:21 deraadt Exp $ */
+/* $OpenBSD: agintc.c,v 1.28 2020/11/15 17:27:32 patrick Exp $ */
/*
* Copyright (c) 2007, 2009, 2011, 2017 Dale Rahn <drahn@dalerahn.com>
* Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
@@ -81,6 +81,9 @@
#define GICD_ICACTIVER(i) (0x0380 + (IRQ_TO_REG32(i) * 4))
#define GICD_IPRIORITYR(i) (0x0400 + (i))
#define GICD_ICFGR(i) (0x0c00 + (IRQ_TO_REG16(i) * 4))
+#define GICD_ICFGR_TRIG_LEVEL(i) (0x0 << (IRQ_TO_REG16BIT(i) * 2))
+#define GICD_ICFGR_TRIG_EDGE(i) (0x2 << (IRQ_TO_REG16BIT(i) * 2))
+#define GICD_ICFGR_TRIG_MASK(i) (0x2 << (IRQ_TO_REG16BIT(i) * 2))
#define GICD_NSACR(i) (0x0e00 + (IRQ_TO_REG16(i) * 4))
#define GICD_IROUTER(i) (0x6000 + ((i) * 8))
@@ -207,7 +210,7 @@ int agintc_splraise(int);
void agintc_setipl(int);
void agintc_calc_mask(void);
void agintc_calc_irq(struct agintc_softc *sc, int irq);
-void *agintc_intr_establish(int, int, struct cpu_info *ci,
+void *agintc_intr_establish(int, int, int, struct cpu_info *,
int (*)(void *), void *, char *);
void *agintc_intr_establish_fdt(void *cookie, int *cell, int level,
struct cpu_info *, int (*func)(void *), void *arg, char *name);
@@ -218,6 +221,7 @@ void agintc_eoi(uint32_t);
void agintc_set_priority(struct agintc_softc *sc, int, int);
void agintc_intr_enable(struct agintc_softc *, int);
void agintc_intr_disable(struct agintc_softc *, int);
+void agintc_intr_config(struct agintc_softc *, int, int);
void agintc_route(struct agintc_softc *, int, int,
struct cpu_info *);
void agintc_route_irq(void *, int, struct cpu_info *);
@@ -574,16 +578,19 @@ agintc_attach(struct device *parent, struct device *self, void *aux)
switch (nipi) {
case 1:
sc->sc_ipi_irq[0] = agintc_intr_establish(ipiirq[0],
- IPL_IPI|IPL_MPSAFE, NULL, agintc_ipi_combined, sc, "ipi");
+ IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
+ agintc_ipi_combined, sc, "ipi");
sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[0];
break;
case 2:
sc->sc_ipi_irq[0] = agintc_intr_establish(ipiirq[0],
- IPL_IPI|IPL_MPSAFE, NULL, agintc_ipi_nop, sc, "ipinop");
+ IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
+ agintc_ipi_nop, sc, "ipinop");
sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
sc->sc_ipi_irq[1] = agintc_intr_establish(ipiirq[1],
- IPL_IPI|IPL_MPSAFE, NULL, agintc_ipi_ddb, sc, "ipiddb");
+ IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
+ agintc_ipi_ddb, sc, "ipiddb");
sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1];
break;
default:
@@ -744,6 +751,24 @@ agintc_intr_disable(struct agintc_softc *sc, int irq)
}
void
+agintc_intr_config(struct agintc_softc *sc, int irq, int type)
+{
+ uint32_t reg;
+
+ /* Don't dare to change SGIs or PPIs (yet) */
+ if (irq < 32)
+ return;
+
+ reg = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_ICFGR(irq));
+ reg &= ~GICD_ICFGR_TRIG_MASK(irq);
+ if (type == IST_EDGE_RISING)
+ reg |= GICD_ICFGR_TRIG_EDGE(irq);
+ else
+ reg |= GICD_ICFGR_TRIG_LEVEL(irq);
+ bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_ICFGR(irq), reg);
+}
+
+void
agintc_calc_mask(void)
{
struct agintc_softc *sc = agintc_sc;
@@ -979,6 +1004,7 @@ agintc_intr_establish_fdt(void *cookie, int *cell, int level,
{
struct agintc_softc *sc = agintc_sc;
int irq;
+ int type;
/* 2nd cell contains the interrupt number */
irq = cell[1];
@@ -991,11 +1017,17 @@ agintc_intr_establish_fdt(void *cookie, int *cell, int level,
else
panic("%s: bogus interrupt type", sc->sc_sbus.sc_dev.dv_xname);
- return agintc_intr_establish(irq, level, ci, func, arg, name);
+ /* SPIs are only active-high level or low-to-high edge */
+ if (cell[2] & 0x3)
+ type = IST_EDGE_RISING;
+ else
+ type = IST_LEVEL_HIGH;
+
+ return agintc_intr_establish(irq, type, level, ci, func, arg, name);
}
void *
-agintc_intr_establish(int irqno, int level, struct cpu_info *ci,
+agintc_intr_establish(int irqno, int type, int level, struct cpu_info *ci,
int (*func)(void *), void *arg, char *name)
{
struct agintc_softc *sc = agintc_sc;
@@ -1041,6 +1073,7 @@ agintc_intr_establish(int irqno, int level, struct cpu_info *ci,
#endif
if (irqno < LPI_BASE) {
+ agintc_intr_config(sc, irqno, type);
agintc_calc_irq(sc, irqno);
} else {
uint8_t *prop = AGINTC_DMA_KVA(sc->sc_prop);
@@ -1538,7 +1571,7 @@ agintc_intr_establish_msi(void *self, uint64_t *addr, uint64_t *data,
continue;
cookie = agintc_intr_establish(LPI_BASE + i,
- level, ci, func, arg, name);
+ IST_EDGE_RISING, level, ci, func, arg, name);
if (cookie == NULL)
return NULL;