From 8b7bc089824a7f535cf16a7e30432b574aa1c01f Mon Sep 17 00:00:00 2001 From: kettenis Date: Thu, 13 Oct 2022 07:04:53 +0000 Subject: [PATCH] Implement support for MBIs. MBIs are message based interrupts that can be used as an alternative implementation for MSIs on hardware that doesn't implement an ITS (or where the ITS is broken such as on the Rochchip RK3566 SoC). Based on an earlier WIP diff from patrick@; I just cleaned it up a bit. ok patrick@ --- sys/arch/arm64/dev/agintc.c | 106 +++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/sys/arch/arm64/dev/agintc.c b/sys/arch/arm64/dev/agintc.c index 58404941f5d..e342d23a951 100644 --- a/sys/arch/arm64/dev/agintc.c +++ b/sys/arch/arm64/dev/agintc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: agintc.c,v 1.42 2022/08/29 01:34:18 drahn Exp $ */ +/* $OpenBSD: agintc.c,v 1.43 2022/10/13 07:04:53 kettenis Exp $ */ /* * Copyright (c) 2007, 2009, 2011, 2017 Dale Rahn * Copyright (c) 2018 Mark Kettenis @@ -70,9 +70,12 @@ #define GICD_CTLR_ARE_NS (1 << 4) #define GICD_CTLR_DS (1 << 6) #define GICD_TYPER 0x0004 +#define GICD_TYPER_MBIS (1 << 16) #define GICD_TYPER_LPIS (1 << 17) #define GICD_TYPER_ITLINE_M 0x1f #define GICD_IIDR 0x0008 +#define GICD_SETSPI_NSR 0x0040 +#define GICD_CLRSPI_NSR 0x0048 #define GICD_IGROUPR(i) (0x0080 + (IRQ_TO_REG32(i) * 4)) #define GICD_ISENABLER(i) (0x0100 + (IRQ_TO_REG32(i) * 4)) #define GICD_ICENABLER(i) (0x0180 + (IRQ_TO_REG32(i) * 4)) @@ -139,6 +142,12 @@ #define IRQ_ENABLE 1 #define IRQ_DISABLE 0 +struct agintc_mbi_range { + int mr_base; + int mr_span; + void **mr_mbi; +}; + struct agintc_softc { struct simplebus_softc sc_sbus; struct intrq *sc_handler; @@ -152,6 +161,9 @@ struct agintc_softc { int sc_cpuremap[MAXCPUS]; int sc_nintr; int sc_nlpi; + bus_addr_t sc_mbi_addr; + int sc_mbi_nranges; + struct agintc_mbi_range *sc_mbi_ranges; int sc_prio_shift; int sc_pmr_shift; int sc_rk3399_quirk; @@ -206,6 +218,7 @@ void agintc_dmamem_free(bus_dma_tag_t, struct agintc_dmamem *); int agintc_match(struct device *, void *, void *); void agintc_attach(struct device *, struct device *, void *); +void agintc_mbiinit(struct agintc_softc *, int, bus_addr_t); void agintc_cpuinit(void); int agintc_spllower(int); void agintc_splx(int); @@ -217,6 +230,8 @@ 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); +void *agintc_intr_establish_mbi(void *, uint64_t *, uint64_t *, + int , struct cpu_info *, int (*)(void *), void *, char *); void agintc_intr_disestablish(void *); void agintc_irq_handler(void *); uint32_t agintc_iack(void); @@ -328,6 +343,9 @@ agintc_attach(struct device *parent, struct device *self, void *aux) sc->sc_nlpi = 8192; } + if (typer & GICD_TYPER_MBIS) + agintc_mbiinit(sc, faa->fa_node, faa->fa_reg[0].addr); + /* * We are guaranteed to have at least 16 priority levels, so * in principle we just want to use the top 4 bits of the @@ -628,6 +646,8 @@ agintc_attach(struct device *parent, struct device *self, void *aux) sc->sc_ic.ic_route = agintc_route_irq; sc->sc_ic.ic_cpu_enable = agintc_cpuinit; sc->sc_ic.ic_barrier = agintc_intr_barrier; + if (sc->sc_mbi_nranges > 0) + sc->sc_ic.ic_establish_msi = agintc_intr_establish_mbi; arm_intr_register_fdt(&sc->sc_ic); intr_restore(psw); @@ -656,6 +676,42 @@ unmap: bus_space_unmap(sc->sc_iot, sc->sc_d_ioh, faa->fa_reg[0].size); } +void +agintc_mbiinit(struct agintc_softc *sc, int node, bus_addr_t addr) +{ + uint32_t *ranges; + int i, len; + + if (OF_getproplen(node, "msi-controller") != 0) + return; + + len = OF_getproplen(node, "mbi-ranges"); + if (len <= 0 || len % 2 * sizeof(uint32_t) != 0) + return; + + ranges = malloc(len, M_TEMP, M_WAITOK); + OF_getpropintarray(node, "mbi-ranges", ranges, len); + + sc->sc_mbi_nranges = len / (2 * sizeof(uint32_t)); + sc->sc_mbi_ranges = mallocarray(sc->sc_mbi_nranges, + sizeof(struct agintc_mbi_range), M_DEVBUF, M_WAITOK); + + for (i = 0; i < sc->sc_mbi_nranges; i++) { + sc->sc_mbi_ranges[i].mr_base = ranges[2 * i + 0]; + sc->sc_mbi_ranges[i].mr_span = ranges[2 * i + 1]; + sc->sc_mbi_ranges[i].mr_mbi = + mallocarray(sc->sc_mbi_ranges[i].mr_span, + sizeof(void *), M_DEVBUF, M_WAITOK | M_ZERO); + } + + free(ranges, M_TEMP, len); + + addr = OF_getpropint64(node, "mbi-alias", addr); + sc->sc_mbi_addr = addr + GICD_SETSPI_NSR; + + printf(" mbi"); +} + /* Initialize redistributors on each core. */ void agintc_cpuinit(void) @@ -1126,6 +1182,8 @@ agintc_intr_disestablish(void *cookie) struct intrhand *ih = cookie; int irqno = ih->ih_irq; u_long psw; + struct agintc_mbi_range *mr; + int i; psw = intr_disable(); @@ -1135,11 +1193,57 @@ agintc_intr_disestablish(void *cookie) agintc_calc_irq(sc, irqno); + /* In case this is an MBI, free it */ + for (i = 0; i < sc->sc_mbi_nranges; i++) { + mr = &sc->sc_mbi_ranges[i]; + if (irqno < mr->mr_base) + continue; + if (irqno >= mr->mr_base + mr->mr_span) + break; + if (mr->mr_mbi[irqno - mr->mr_base] != NULL) + mr->mr_mbi[irqno - mr->mr_base] = NULL; + } + intr_restore(psw); free(ih, M_DEVBUF, 0); } +void * +agintc_intr_establish_mbi(void *self, uint64_t *addr, uint64_t *data, + int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name) +{ + struct agintc_softc *sc = agintc_sc; + struct agintc_mbi_range *mr; + void *cookie; + int i, j, hwcpu; + + if (ci == NULL) + ci = &cpu_info_primary; + hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid]; + + for (i = 0; i < sc->sc_mbi_nranges; i++) { + mr = &sc->sc_mbi_ranges[i]; + for (j = 0; j < mr->mr_span; j++) { + if (mr->mr_mbi[j] != NULL) + continue; + + cookie = agintc_intr_establish(mr->mr_base + j, + IST_EDGE_RISING, level, ci, func, arg, name); + if (cookie == NULL) + return NULL; + + *addr = sc->sc_mbi_addr; + *data = mr->mr_base + j; + + mr->mr_mbi[j] = cookie; + return cookie; + } + } + + return NULL; +} + void agintc_eoi(uint32_t eoi) { -- 2.20.1