1*53f700b0Skettenis /* $OpenBSD: agintc.c,v 1.62 2025/01/24 20:17:28 kettenis Exp $ */ 266f727bcSkettenis /* 366f727bcSkettenis * Copyright (c) 2007, 2009, 2011, 2017 Dale Rahn <drahn@dalerahn.com> 4f0a2ac19Skettenis * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> 566f727bcSkettenis * 666f727bcSkettenis * Permission to use, copy, modify, and distribute this software for any 766f727bcSkettenis * purpose with or without fee is hereby granted, provided that the above 866f727bcSkettenis * copyright notice and this permission notice appear in all copies. 966f727bcSkettenis * 1066f727bcSkettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1166f727bcSkettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1266f727bcSkettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1366f727bcSkettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1466f727bcSkettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1566f727bcSkettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1666f727bcSkettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1766f727bcSkettenis */ 1866f727bcSkettenis 1966f727bcSkettenis /* 2066f727bcSkettenis * This is a device driver for the GICv3/GICv4 IP from ARM as specified 2166f727bcSkettenis * in IHI0069C, an example of this hardware is the GIC 500. 2266f727bcSkettenis */ 2366f727bcSkettenis 2466f727bcSkettenis #include <sys/param.h> 2566f727bcSkettenis #include <sys/systm.h> 2666f727bcSkettenis #include <sys/queue.h> 2766f727bcSkettenis #include <sys/malloc.h> 2866f727bcSkettenis #include <sys/device.h> 2966f727bcSkettenis #include <sys/evcount.h> 3066f727bcSkettenis 3166f727bcSkettenis #include <machine/bus.h> 322896618dSkettenis #include <machine/cpufunc.h> 3366f727bcSkettenis #include <machine/fdt.h> 3466f727bcSkettenis 3566f727bcSkettenis #include <dev/ofw/fdt.h> 3666f727bcSkettenis #include <dev/ofw/openfirm.h> 3766f727bcSkettenis 3894673892Sjsg #include <machine/simplebusvar.h> 39f0a2ac19Skettenis 4066f727bcSkettenis #define ICC_PMR s3_0_c4_c6_0 4166f727bcSkettenis #define ICC_IAR0 s3_0_c12_c8_0 4266f727bcSkettenis #define ICC_EOIR0 s3_0_c12_c8_1 4366f727bcSkettenis #define ICC_HPPIR0 s3_0_c12_c8_2 4466f727bcSkettenis #define ICC_BPR0 s3_0_c12_c8_3 4566f727bcSkettenis 4666f727bcSkettenis #define ICC_DIR s3_0_c12_c11_1 479a716617Skettenis #define ICC_RPR s3_0_c12_c11_3 4866f727bcSkettenis #define ICC_SGI1R s3_0_c12_c11_5 4966f727bcSkettenis #define ICC_SGI0R s3_0_c12_c11_7 5066f727bcSkettenis 5166f727bcSkettenis #define ICC_IAR1 s3_0_c12_c12_0 5266f727bcSkettenis #define ICC_EOIR1 s3_0_c12_c12_1 5366f727bcSkettenis #define ICC_HPPIR1 s3_0_c12_c12_2 5466f727bcSkettenis #define ICC_BPR1 s3_0_c12_c12_3 5566f727bcSkettenis #define ICC_CTLR s3_0_c12_c12_4 5666f727bcSkettenis #define ICC_SRE_EL1 s3_0_c12_c12_5 5766f727bcSkettenis #define ICC_SRE_EL1_EN 0x7 5866f727bcSkettenis #define ICC_IGRPEN0 s3_0_c12_c12_6 5966f727bcSkettenis #define ICC_IGRPEN1 s3_0_c12_c12_7 6066f727bcSkettenis 6166f727bcSkettenis #define _STR(x) #x 6266f727bcSkettenis #define STR(x) _STR(x) 6366f727bcSkettenis 6466f727bcSkettenis /* distributor registers */ 6566f727bcSkettenis #define GICD_CTLR 0x0000 6666f727bcSkettenis /* non-secure */ 6766f727bcSkettenis #define GICD_CTLR_RWP (1U << 31) 68163120c5Skettenis #define GICD_CTLR_EnableGrp1 (1 << 0) 69163120c5Skettenis #define GICD_CTLR_EnableGrp1A (1 << 1) 70163120c5Skettenis #define GICD_CTLR_ARE_NS (1 << 4) 71163120c5Skettenis #define GICD_CTLR_DS (1 << 6) 7266f727bcSkettenis #define GICD_TYPER 0x0004 738b7bc089Skettenis #define GICD_TYPER_MBIS (1 << 16) 745a4b4c9bSkettenis #define GICD_TYPER_LPIS (1 << 17) 75ab5b2c8cSkettenis #define GICD_TYPER_ITLINE_M 0x1f 7666f727bcSkettenis #define GICD_IIDR 0x0008 778b7bc089Skettenis #define GICD_SETSPI_NSR 0x0040 788b7bc089Skettenis #define GICD_CLRSPI_NSR 0x0048 79d532cfaeSpatrick #define GICD_IGROUPR(i) (0x0080 + (IRQ_TO_REG32(i) * 4)) 8066f727bcSkettenis #define GICD_ISENABLER(i) (0x0100 + (IRQ_TO_REG32(i) * 4)) 8166f727bcSkettenis #define GICD_ICENABLER(i) (0x0180 + (IRQ_TO_REG32(i) * 4)) 8266f727bcSkettenis #define GICD_ISPENDR(i) (0x0200 + (IRQ_TO_REG32(i) * 4)) 8366f727bcSkettenis #define GICD_ICPENDR(i) (0x0280 + (IRQ_TO_REG32(i) * 4)) 8466f727bcSkettenis #define GICD_ISACTIVER(i) (0x0300 + (IRQ_TO_REG32(i) * 4)) 8566f727bcSkettenis #define GICD_ICACTIVER(i) (0x0380 + (IRQ_TO_REG32(i) * 4)) 8666f727bcSkettenis #define GICD_IPRIORITYR(i) (0x0400 + (i)) 8766f727bcSkettenis #define GICD_ICFGR(i) (0x0c00 + (IRQ_TO_REG16(i) * 4)) 886214007cSpatrick #define GICD_ICFGR_TRIG_LEVEL(i) (0x0 << (IRQ_TO_REG16BIT(i) * 2)) 896214007cSpatrick #define GICD_ICFGR_TRIG_EDGE(i) (0x2 << (IRQ_TO_REG16BIT(i) * 2)) 906214007cSpatrick #define GICD_ICFGR_TRIG_MASK(i) (0x2 << (IRQ_TO_REG16BIT(i) * 2)) 91d532cfaeSpatrick #define GICD_IGRPMODR(i) (0x0d00 + (IRQ_TO_REG32(i) * 4)) 9216b913cdSkettenis #define GICD_NSACR(i) (0x0e00 + (IRQ_TO_REG16(i) * 4)) 9366f727bcSkettenis #define GICD_IROUTER(i) (0x6000 + ((i) * 8)) 9466f727bcSkettenis 9566f727bcSkettenis /* redistributor registers */ 9666f727bcSkettenis #define GICR_CTLR 0x00000 9766f727bcSkettenis #define GICR_CTLR_RWP ((1U << 31) | (1 << 3)) 98f0a2ac19Skettenis #define GICR_CTLR_ENABLE_LPIS (1 << 0) 9966f727bcSkettenis #define GICR_IIDR 0x00004 10066f727bcSkettenis #define GICR_TYPER 0x00008 10166f727bcSkettenis #define GICR_TYPER_LAST (1 << 4) 10266f727bcSkettenis #define GICR_TYPER_VLPIS (1 << 1) 10366f727bcSkettenis #define GICR_WAKER 0x00014 10466f727bcSkettenis #define GICR_WAKER_X31 (1U << 31) 10566f727bcSkettenis #define GICR_WAKER_CHILDRENASLEEP (1 << 2) 10666f727bcSkettenis #define GICR_WAKER_PROCESSORSLEEP (1 << 1) 10766f727bcSkettenis #define GICR_WAKER_X0 (1 << 0) 108f0a2ac19Skettenis #define GICR_PROPBASER 0x00070 109f0a2ac19Skettenis #define GICR_PROPBASER_ISH (1ULL << 10) 110f0a2ac19Skettenis #define GICR_PROPBASER_IC_NORM_NC (1ULL << 7) 111f0a2ac19Skettenis #define GICR_PENDBASER 0x00078 112f0a2ac19Skettenis #define GICR_PENDBASER_PTZ (1ULL << 62) 113f0a2ac19Skettenis #define GICR_PENDBASER_ISH (1ULL << 10) 114f0a2ac19Skettenis #define GICR_PENDBASER_IC_NORM_NC (1ULL << 7) 115d532cfaeSpatrick #define GICR_IGROUPR0 0x10080 11666f727bcSkettenis #define GICR_ISENABLE0 0x10100 11766f727bcSkettenis #define GICR_ICENABLE0 0x10180 11866f727bcSkettenis #define GICR_ISPENDR0 0x10200 11966f727bcSkettenis #define GICR_ICPENDR0 0x10280 12066f727bcSkettenis #define GICR_ISACTIVE0 0x10300 12166f727bcSkettenis #define GICR_ICACTIVE0 0x10380 12266f727bcSkettenis #define GICR_IPRIORITYR(i) (0x10400 + (i)) 12366f727bcSkettenis #define GICR_ICFGR0 0x10c00 12466f727bcSkettenis #define GICR_ICFGR1 0x10c04 125d532cfaeSpatrick #define GICR_IGRPMODR0 0x10d00 12666f727bcSkettenis 127f0a2ac19Skettenis #define GICR_PROP_SIZE (64 * 1024) 12816b913cdSkettenis #define GICR_PROP_GROUP1 (1 << 1) 129f0a2ac19Skettenis #define GICR_PROP_ENABLE (1 << 0) 130f0a2ac19Skettenis #define GICR_PEND_SIZE (64 * 1024) 131f0a2ac19Skettenis 132f0a2ac19Skettenis #define PPI_BASE 16 133f0a2ac19Skettenis #define SPI_BASE 32 134f0a2ac19Skettenis #define LPI_BASE 8192 135f0a2ac19Skettenis 1367d4ece3aSdrahn #define IRQ_TO_REG32(i) (((i) >> 5) & 0x1f) 13766f727bcSkettenis #define IRQ_TO_REG32BIT(i) ((i) & 0x1f) 13866f727bcSkettenis 1397d4ece3aSdrahn #define IRQ_TO_REG16(i) (((i) >> 4) & 0x3f) 14066f727bcSkettenis #define IRQ_TO_REG16BIT(i) ((i) & 0xf) 14166f727bcSkettenis 14266f727bcSkettenis #define IRQ_ENABLE 1 14366f727bcSkettenis #define IRQ_DISABLE 0 14466f727bcSkettenis 1458b7bc089Skettenis struct agintc_mbi_range { 1468b7bc089Skettenis int mr_base; 1478b7bc089Skettenis int mr_span; 1488b7bc089Skettenis void **mr_mbi; 1498b7bc089Skettenis }; 1508b7bc089Skettenis 151f83e6fb8Spatrick struct agintc_lpi_info { 152f83e6fb8Spatrick struct agintc_msi_softc *li_msic; 153f83e6fb8Spatrick struct cpu_info *li_ci; 154f83e6fb8Spatrick uint32_t li_deviceid; 155f83e6fb8Spatrick uint32_t li_eventid; 156f83e6fb8Spatrick struct intrhand *li_ih; 157f83e6fb8Spatrick }; 158f83e6fb8Spatrick 15966f727bcSkettenis struct agintc_softc { 160f0a2ac19Skettenis struct simplebus_softc sc_sbus; 161f0a2ac19Skettenis struct intrq *sc_handler; 162f83e6fb8Spatrick struct agintc_lpi_info **sc_lpi; 16366f727bcSkettenis bus_space_tag_t sc_iot; 16466f727bcSkettenis bus_space_handle_t sc_d_ioh; 165934f5440Skettenis bus_space_handle_t *sc_r_ioh; 166*53f700b0Skettenis bus_space_handle_t *sc_rbase_ioh; 167f0a2ac19Skettenis bus_dma_tag_t sc_dmat; 1687c59a772Spatrick uint16_t *sc_processor; 1695ce1efbdSkettenis int sc_cpuremap[MAXCPUS]; 17066f727bcSkettenis int sc_nintr; 171f0a2ac19Skettenis int sc_nlpi; 1728b7bc089Skettenis bus_addr_t sc_mbi_addr; 1738b7bc089Skettenis int sc_mbi_nranges; 1748b7bc089Skettenis struct agintc_mbi_range *sc_mbi_ranges; 175163120c5Skettenis int sc_prio_shift; 176163120c5Skettenis int sc_pmr_shift; 17716b913cdSkettenis int sc_rk3399_quirk; 17866f727bcSkettenis struct evcount sc_spur; 17966f727bcSkettenis int sc_ncells; 18066f727bcSkettenis int sc_num_redist; 181*53f700b0Skettenis int sc_num_redist_regions; 182f0a2ac19Skettenis struct agintc_dmamem *sc_prop; 183f0a2ac19Skettenis struct agintc_dmamem *sc_pend; 18466f727bcSkettenis struct interrupt_controller sc_ic; 1854002e08dSkettenis int sc_ipi_num[3]; /* id for each ipi */ 1864002e08dSkettenis int sc_ipi_reason[MAXCPUS]; /* cause of ipi */ 1874002e08dSkettenis void *sc_ipi_irq[3]; /* irqhandle for each ipi */ 18866f727bcSkettenis }; 18966f727bcSkettenis struct agintc_softc *agintc_sc; 19066f727bcSkettenis 19166f727bcSkettenis struct intrhand { 19266f727bcSkettenis TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ 19366f727bcSkettenis int (*ih_func)(void *); /* handler */ 19466f727bcSkettenis void *ih_arg; /* arg for handler */ 19566f727bcSkettenis int ih_ipl; /* IPL_* */ 1967a7b3facSkettenis int ih_flags; 19766f727bcSkettenis int ih_irq; /* IRQ number */ 19866f727bcSkettenis struct evcount ih_count; 19966f727bcSkettenis char *ih_name; 200452daaedSpatrick struct cpu_info *ih_ci; /* CPU the IRQ runs on */ 20166f727bcSkettenis }; 20266f727bcSkettenis 20366f727bcSkettenis struct intrq { 20466f727bcSkettenis TAILQ_HEAD(, intrhand) iq_list; /* handler list */ 2057c59a772Spatrick struct cpu_info *iq_ci; /* CPU the IRQ runs on */ 206eb0c9b3cSpatrick int iq_irq_max; /* IRQ to mask while handling */ 207eb0c9b3cSpatrick int iq_irq_min; /* lowest IRQ when shared */ 20866f727bcSkettenis int iq_ist; /* share type */ 209d7e3db9cSkettenis int iq_route; 21066f727bcSkettenis }; 21166f727bcSkettenis 212f0a2ac19Skettenis struct agintc_dmamem { 213f0a2ac19Skettenis bus_dmamap_t adm_map; 214f0a2ac19Skettenis bus_dma_segment_t adm_seg; 215f0a2ac19Skettenis size_t adm_size; 216f0a2ac19Skettenis caddr_t adm_kva; 217f0a2ac19Skettenis }; 218f0a2ac19Skettenis 219f0a2ac19Skettenis #define AGINTC_DMA_MAP(_adm) ((_adm)->adm_map) 220f0a2ac19Skettenis #define AGINTC_DMA_LEN(_adm) ((_adm)->adm_size) 221f0a2ac19Skettenis #define AGINTC_DMA_DVA(_adm) ((_adm)->adm_map->dm_segs[0].ds_addr) 222f0a2ac19Skettenis #define AGINTC_DMA_KVA(_adm) ((void *)(_adm)->adm_kva) 223f0a2ac19Skettenis 224f0a2ac19Skettenis struct agintc_dmamem *agintc_dmamem_alloc(bus_dma_tag_t, bus_size_t, 225f0a2ac19Skettenis bus_size_t); 226f0a2ac19Skettenis void agintc_dmamem_free(bus_dma_tag_t, struct agintc_dmamem *); 227f0a2ac19Skettenis 22866f727bcSkettenis int agintc_match(struct device *, void *, void *); 22966f727bcSkettenis void agintc_attach(struct device *, struct device *, void *); 2308b7bc089Skettenis void agintc_mbiinit(struct agintc_softc *, int, bus_addr_t); 23166f727bcSkettenis void agintc_cpuinit(void); 23266f727bcSkettenis int agintc_spllower(int); 23366f727bcSkettenis void agintc_splx(int); 23466f727bcSkettenis int agintc_splraise(int); 23566f727bcSkettenis void agintc_setipl(int); 23625da8179Spatrick void agintc_enable_wakeup(void); 23725da8179Spatrick void agintc_disable_wakeup(void); 23866f727bcSkettenis void agintc_calc_mask(void); 23966f727bcSkettenis void agintc_calc_irq(struct agintc_softc *sc, int irq); 2406214007cSpatrick void *agintc_intr_establish(int, int, int, struct cpu_info *, 241789e88a4Spatrick int (*)(void *), void *, char *); 24266f727bcSkettenis void *agintc_intr_establish_fdt(void *cookie, int *cell, int level, 243789e88a4Spatrick struct cpu_info *, int (*func)(void *), void *arg, char *name); 2448b7bc089Skettenis void *agintc_intr_establish_mbi(void *, uint64_t *, uint64_t *, 2458b7bc089Skettenis int , struct cpu_info *, int (*)(void *), void *, char *); 24666f727bcSkettenis void agintc_intr_disestablish(void *); 24725da8179Spatrick void agintc_intr_set_wakeup(void *); 24866f727bcSkettenis void agintc_irq_handler(void *); 24966f727bcSkettenis uint32_t agintc_iack(void); 25066f727bcSkettenis void agintc_eoi(uint32_t); 25166f727bcSkettenis void agintc_set_priority(struct agintc_softc *sc, int, int); 25266f727bcSkettenis void agintc_intr_enable(struct agintc_softc *, int); 25366f727bcSkettenis void agintc_intr_disable(struct agintc_softc *, int); 2546214007cSpatrick void agintc_intr_config(struct agintc_softc *, int, int); 25566f727bcSkettenis void agintc_route(struct agintc_softc *, int, int, 25666f727bcSkettenis struct cpu_info *); 257d7e3db9cSkettenis void agintc_route_irq(void *, int, struct cpu_info *); 258452daaedSpatrick void agintc_intr_barrier(void *); 25966f727bcSkettenis void agintc_r_wait_rwp(struct agintc_softc *sc); 26066f727bcSkettenis 261d7e3db9cSkettenis int agintc_ipi_ddb(void *v); 2624002e08dSkettenis int agintc_ipi_halt(void *v); 263d7e3db9cSkettenis int agintc_ipi_nop(void *v); 264d7e3db9cSkettenis int agintc_ipi_combined(void *); 265d7e3db9cSkettenis void agintc_send_ipi(struct cpu_info *, int); 266d7e3db9cSkettenis 267f83e6fb8Spatrick void agintc_msi_discard(struct agintc_lpi_info *); 268f83e6fb8Spatrick void agintc_msi_inv(struct agintc_lpi_info *); 269c60fe750Spatrick 2709fdf0c62Smpi const struct cfattach agintc_ca = { 27166f727bcSkettenis sizeof (struct agintc_softc), agintc_match, agintc_attach 27266f727bcSkettenis }; 27366f727bcSkettenis 27466f727bcSkettenis struct cfdriver agintc_cd = { 27566f727bcSkettenis NULL, "agintc", DV_DULL 27666f727bcSkettenis }; 27766f727bcSkettenis 27866f727bcSkettenis static char *agintc_compatibles[] = { 27966f727bcSkettenis "arm,gic-v3", 28066f727bcSkettenis "arm,gic-v4", 28166f727bcSkettenis NULL 28266f727bcSkettenis }; 28366f727bcSkettenis 28466f727bcSkettenis int 28566f727bcSkettenis agintc_match(struct device *parent, void *cfdata, void *aux) 28666f727bcSkettenis { 28766f727bcSkettenis struct fdt_attach_args *faa = aux; 28866f727bcSkettenis int i; 28966f727bcSkettenis 29066f727bcSkettenis for (i = 0; agintc_compatibles[i]; i++) 29166f727bcSkettenis if (OF_is_compatible(faa->fa_node, agintc_compatibles[i])) 29266f727bcSkettenis return (1); 29366f727bcSkettenis 29466f727bcSkettenis return (0); 29566f727bcSkettenis } 29666f727bcSkettenis 29766f727bcSkettenis static void 29866f727bcSkettenis __isb(void) 29966f727bcSkettenis { 30066f727bcSkettenis __asm volatile("isb"); 30166f727bcSkettenis } 30266f727bcSkettenis 30366f727bcSkettenis void 30466f727bcSkettenis agintc_attach(struct device *parent, struct device *self, void *aux) 30566f727bcSkettenis { 30666f727bcSkettenis struct agintc_softc *sc = (struct agintc_softc *)self; 30766f727bcSkettenis struct fdt_attach_args *faa = aux; 3087c59a772Spatrick struct cpu_info *ci; 3097c59a772Spatrick CPU_INFO_ITERATOR cii; 310daf2e6ccSkettenis u_long psw; 311f0a2ac19Skettenis uint32_t typer; 31216b913cdSkettenis uint32_t nsacr, oldnsacr; 313163120c5Skettenis uint32_t pmr, oldpmr; 31416b913cdSkettenis uint32_t ctrl, bits; 3157c59a772Spatrick uint32_t affinity; 31615e1ce75Spatrick uint64_t redist_stride; 317931e8929Spatrick int i, nbits, nintr; 318*53f700b0Skettenis int idx, offset, nredist; 319d7e3db9cSkettenis #ifdef MULTIPROCESSOR 3204002e08dSkettenis int nipi, ipiirq[3]; 321d7e3db9cSkettenis #endif 32266f727bcSkettenis 323daf2e6ccSkettenis psw = intr_disable(); 32466f727bcSkettenis arm_init_smask(); 32566f727bcSkettenis 32666f727bcSkettenis sc->sc_iot = faa->fa_iot; 327f0a2ac19Skettenis sc->sc_dmat = faa->fa_dmat; 32866f727bcSkettenis 329*53f700b0Skettenis sc->sc_num_redist_regions = 330*53f700b0Skettenis OF_getpropint(faa->fa_node, "#redistributor-regions", 1); 331*53f700b0Skettenis 332*53f700b0Skettenis if (faa->fa_nreg < sc->sc_num_redist_regions + 1) 333*53f700b0Skettenis panic("%s: missing registers", __func__); 334*53f700b0Skettenis 33566f727bcSkettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 33666f727bcSkettenis faa->fa_reg[0].size, 0, &sc->sc_d_ioh)) 337*53f700b0Skettenis panic("%s: GICD bus_space_map failed", __func__); 33866f727bcSkettenis 339*53f700b0Skettenis sc->sc_rbase_ioh = mallocarray(sc->sc_num_redist_regions, 340*53f700b0Skettenis sizeof(*sc->sc_rbase_ioh), M_DEVBUF, M_WAITOK); 341*53f700b0Skettenis for (idx = 0; idx < sc->sc_num_redist_regions; idx++) { 342*53f700b0Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[1 + idx].addr, 343*53f700b0Skettenis faa->fa_reg[1 + idx].size, 0, &sc->sc_rbase_ioh[idx])) 344*53f700b0Skettenis panic("%s: GICR bus_space_map failed", __func__); 345*53f700b0Skettenis } 34666f727bcSkettenis 347f0a2ac19Skettenis typer = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_TYPER); 348f0a2ac19Skettenis 349f0a2ac19Skettenis if (typer & GICD_TYPER_LPIS) { 350f0a2ac19Skettenis /* Allocate redistributor tables */ 351f0a2ac19Skettenis sc->sc_prop = agintc_dmamem_alloc(sc->sc_dmat, 352f0a2ac19Skettenis GICR_PROP_SIZE, GICR_PROP_SIZE); 353f0a2ac19Skettenis if (sc->sc_prop == NULL) { 354f0a2ac19Skettenis printf(": can't alloc LPI config table\n"); 355f0a2ac19Skettenis goto unmap; 356f0a2ac19Skettenis } 357f0a2ac19Skettenis sc->sc_pend = agintc_dmamem_alloc(sc->sc_dmat, 358f0a2ac19Skettenis GICR_PEND_SIZE, GICR_PEND_SIZE); 3597bbdd582Spatrick if (sc->sc_pend == NULL) { 360f0a2ac19Skettenis printf(": can't alloc LPI pending table\n"); 361f0a2ac19Skettenis goto unmap; 362f0a2ac19Skettenis } 363f0a2ac19Skettenis 364f0a2ac19Skettenis /* Minimum number of LPIs supported by any implementation. */ 365f0a2ac19Skettenis sc->sc_nlpi = 8192; 366f0a2ac19Skettenis } 367f0a2ac19Skettenis 3688b7bc089Skettenis if (typer & GICD_TYPER_MBIS) 3698b7bc089Skettenis agintc_mbiinit(sc, faa->fa_node, faa->fa_reg[0].addr); 3708b7bc089Skettenis 37116b913cdSkettenis /* 372163120c5Skettenis * We are guaranteed to have at least 16 priority levels, so 373163120c5Skettenis * in principle we just want to use the top 4 bits of the 374163120c5Skettenis * (non-secure) priority field. 375163120c5Skettenis */ 376163120c5Skettenis sc->sc_prio_shift = sc->sc_pmr_shift = 4; 377163120c5Skettenis 378163120c5Skettenis /* 379163120c5Skettenis * If the system supports two security states and SCR_EL3.FIQ 380163120c5Skettenis * is zero, the non-secure shifted view applies. We detect 381163120c5Skettenis * this by checking whether the number of writable bits 3829ccaae38Skettenis * matches the number of implemented priority bits. If that 3839ccaae38Skettenis * is the case we will need to adjust the priorities that we 3849ccaae38Skettenis * write into ICC_PMR_EL1 accordingly. 3859ccaae38Skettenis * 3869ccaae38Skettenis * On Ampere eMAG it appears as if there are five writable 3879ccaae38Skettenis * bits when we write 0xff. But for higher priorities 3889ccaae38Skettenis * (smaller values) only the top 4 bits stick. So we use 0xbf 3899ccaae38Skettenis * instead to determine the number of writable bits. 390163120c5Skettenis */ 391163120c5Skettenis ctrl = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR); 392163120c5Skettenis if ((ctrl & GICD_CTLR_DS) == 0) { 393163120c5Skettenis __asm volatile("mrs %x0, "STR(ICC_CTLR_EL1) : "=r"(ctrl)); 394163120c5Skettenis nbits = ICC_CTLR_EL1_PRIBITS(ctrl) + 1; 395163120c5Skettenis __asm volatile("mrs %x0, "STR(ICC_PMR) : "=r"(oldpmr)); 3969ccaae38Skettenis __asm volatile("msr "STR(ICC_PMR)", %x0" :: "r"(0xbf)); 397163120c5Skettenis __asm volatile("mrs %x0, "STR(ICC_PMR) : "=r"(pmr)); 398163120c5Skettenis __asm volatile("msr "STR(ICC_PMR)", %x0" :: "r"(oldpmr)); 399163120c5Skettenis if (nbits == 8 - (ffs(pmr) - 1)) 400163120c5Skettenis sc->sc_pmr_shift--; 401163120c5Skettenis } 402163120c5Skettenis 403163120c5Skettenis /* 40416b913cdSkettenis * The Rockchip RK3399 is busted. Its GIC-500 treats all 40516b913cdSkettenis * access to its memory mapped registers as "secure". As a 40616b913cdSkettenis * result, several registers don't behave as expected. For 40716b913cdSkettenis * example, the GICD_IPRIORITYRn and GICR_IPRIORITYRn 40816b913cdSkettenis * registers expose the full priority range available to 40916b913cdSkettenis * secure interrupts. We need to be aware of this and write 41016b913cdSkettenis * an adjusted priority value into these registers. We also 41116b913cdSkettenis * need to be careful not to touch any bits that shouldn't be 41216b913cdSkettenis * writable in non-secure mode. 41316b913cdSkettenis * 41416b913cdSkettenis * We check whether we have secure mode access to these 41516b913cdSkettenis * registers by attempting to write to the GICD_NSACR register 416163120c5Skettenis * and check whether its contents actually change. In that 417163120c5Skettenis * case we need to adjust the priorities we write into 418163120c5Skettenis * GICD_IPRIORITYRn and GICRIPRIORITYRn accordingly. 41916b913cdSkettenis */ 42016b913cdSkettenis oldnsacr = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_NSACR(32)); 42116b913cdSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_NSACR(32), 42216b913cdSkettenis oldnsacr ^ 0xffffffff); 42316b913cdSkettenis nsacr = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_NSACR(32)); 42416b913cdSkettenis if (nsacr != oldnsacr) { 42516b913cdSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_NSACR(32), 42616b913cdSkettenis oldnsacr); 42716b913cdSkettenis sc->sc_rk3399_quirk = 1; 428163120c5Skettenis sc->sc_prio_shift--; 429163120c5Skettenis printf(" sec"); 43016b913cdSkettenis } 43116b913cdSkettenis 432163120c5Skettenis printf(" shift %d:%d", sc->sc_prio_shift, sc->sc_pmr_shift); 433163120c5Skettenis 43466f727bcSkettenis evcount_attach(&sc->sc_spur, "irq1023/spur", NULL); 43566f727bcSkettenis 43666f727bcSkettenis __asm volatile("msr "STR(ICC_SRE_EL1)", %x0" : : "r" (ICC_SRE_EL1_EN)); 43766f727bcSkettenis __isb(); 43866f727bcSkettenis 439f0a2ac19Skettenis nintr = 32 * (typer & GICD_TYPER_ITLINE_M); 44066f727bcSkettenis nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */ 44166f727bcSkettenis sc->sc_nintr = nintr; 44266f727bcSkettenis 44366f727bcSkettenis agintc_sc = sc; /* save this for global access */ 44466f727bcSkettenis 445934f5440Skettenis /* find the redistributors. */ 446*53f700b0Skettenis idx = 0; 44766f727bcSkettenis offset = 0; 44815e1ce75Spatrick redist_stride = OF_getpropint64(faa->fa_node, "redistributor-stride", 0); 449*53f700b0Skettenis for (nredist = 0; idx < sc->sc_num_redist_regions; nredist++) { 45066f727bcSkettenis uint64_t typer; 45115e1ce75Spatrick int32_t sz; 45266f727bcSkettenis 453*53f700b0Skettenis typer = bus_space_read_8(sc->sc_iot, sc->sc_rbase_ioh[idx], 45466f727bcSkettenis offset + GICR_TYPER); 45566f727bcSkettenis 45615e1ce75Spatrick if (redist_stride == 0) { 45715e1ce75Spatrick sz = (64 * 1024 * 2); 45866f727bcSkettenis if (typer & GICR_TYPER_VLPIS) 45966f727bcSkettenis sz += (64 * 1024 * 2); 46015e1ce75Spatrick } else 46115e1ce75Spatrick sz = redist_stride; 46266f727bcSkettenis 463934f5440Skettenis #ifdef DEBUG_AGINTC 464934f5440Skettenis printf("probing redistributor %d %x\n", nredist, offset); 465934f5440Skettenis #endif 466934f5440Skettenis 467934f5440Skettenis offset += sz; 468*53f700b0Skettenis if (offset >= faa->fa_reg[1 + idx].size || 469*53f700b0Skettenis typer & GICR_TYPER_LAST) { 470*53f700b0Skettenis offset = 0; 471*53f700b0Skettenis idx++; 472934f5440Skettenis } 473934f5440Skettenis } 474934f5440Skettenis 475*53f700b0Skettenis sc->sc_num_redist = nredist; 476163120c5Skettenis printf(" nirq %d nredist %d", nintr, sc->sc_num_redist); 477934f5440Skettenis 478934f5440Skettenis sc->sc_r_ioh = mallocarray(sc->sc_num_redist, 479934f5440Skettenis sizeof(*sc->sc_r_ioh), M_DEVBUF, M_WAITOK); 4807c59a772Spatrick sc->sc_processor = mallocarray(sc->sc_num_redist, 4817c59a772Spatrick sizeof(*sc->sc_processor), M_DEVBUF, M_WAITOK); 482934f5440Skettenis 483934f5440Skettenis /* submap and configure the redistributors. */ 484*53f700b0Skettenis idx = 0; 485934f5440Skettenis offset = 0; 486934f5440Skettenis for (nredist = 0; nredist < sc->sc_num_redist; nredist++) { 487934f5440Skettenis uint64_t typer; 48815e1ce75Spatrick int32_t sz; 489934f5440Skettenis 490*53f700b0Skettenis typer = bus_space_read_8(sc->sc_iot, sc->sc_rbase_ioh[idx], 491934f5440Skettenis offset + GICR_TYPER); 492934f5440Skettenis 49315e1ce75Spatrick if (redist_stride == 0) { 49415e1ce75Spatrick sz = (64 * 1024 * 2); 495934f5440Skettenis if (typer & GICR_TYPER_VLPIS) 496934f5440Skettenis sz += (64 * 1024 * 2); 49715e1ce75Spatrick } else 49815e1ce75Spatrick sz = redist_stride; 499934f5440Skettenis 5007c59a772Spatrick affinity = bus_space_read_8(sc->sc_iot, 501*53f700b0Skettenis sc->sc_rbase_ioh[idx], offset + GICR_TYPER) >> 32; 5027c59a772Spatrick CPU_INFO_FOREACH(cii, ci) { 5037c59a772Spatrick if (affinity == (((ci->ci_mpidr >> 8) & 0xff000000) | 5047c59a772Spatrick (ci->ci_mpidr & 0x00ffffff))) 5057c59a772Spatrick break; 5067c59a772Spatrick } 50799ec89c2Spatrick if (ci != NULL) 50899ec89c2Spatrick sc->sc_cpuremap[ci->ci_cpuid] = nredist; 5097c59a772Spatrick 5107c59a772Spatrick sc->sc_processor[nredist] = bus_space_read_8(sc->sc_iot, 511*53f700b0Skettenis sc->sc_rbase_ioh[idx], offset + GICR_TYPER) >> 8; 51266f727bcSkettenis 513*53f700b0Skettenis bus_space_subregion(sc->sc_iot, sc->sc_rbase_ioh[idx], 51466f727bcSkettenis offset, sz, &sc->sc_r_ioh[nredist]); 51566f727bcSkettenis 516f0a2ac19Skettenis if (sc->sc_nlpi > 0) { 517*53f700b0Skettenis bus_space_write_8(sc->sc_iot, sc->sc_rbase_ioh[idx], 518f0a2ac19Skettenis offset + GICR_PROPBASER, 519f0a2ac19Skettenis AGINTC_DMA_DVA(sc->sc_prop) | 520f0a2ac19Skettenis GICR_PROPBASER_ISH | GICR_PROPBASER_IC_NORM_NC | 521f0a2ac19Skettenis fls(LPI_BASE + sc->sc_nlpi - 1) - 1); 522*53f700b0Skettenis bus_space_write_8(sc->sc_iot, sc->sc_rbase_ioh[idx], 523f0a2ac19Skettenis offset + GICR_PENDBASER, 524f0a2ac19Skettenis AGINTC_DMA_DVA(sc->sc_pend) | 525f0a2ac19Skettenis GICR_PENDBASER_ISH | GICR_PENDBASER_IC_NORM_NC | 526f0a2ac19Skettenis GICR_PENDBASER_PTZ); 527*53f700b0Skettenis bus_space_write_4(sc->sc_iot, sc->sc_rbase_ioh[idx], 528f0a2ac19Skettenis offset + GICR_CTLR, GICR_CTLR_ENABLE_LPIS); 529f0a2ac19Skettenis } 530f0a2ac19Skettenis 53166f727bcSkettenis offset += sz; 532*53f700b0Skettenis if (offset >= faa->fa_reg[1 + idx].size || 533*53f700b0Skettenis typer & GICR_TYPER_LAST) { 534*53f700b0Skettenis offset = 0; 535*53f700b0Skettenis idx++; 536*53f700b0Skettenis } 53766f727bcSkettenis } 53866f727bcSkettenis 53966f727bcSkettenis /* Disable all interrupts, clear all pending */ 54066f727bcSkettenis for (i = 1; i < nintr / 32; i++) { 54166f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 542105a7236Skettenis GICD_ICACTIVER(i * 32), ~0); 543105a7236Skettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 54466f727bcSkettenis GICD_ICENABLER(i * 32), ~0); 54566f727bcSkettenis } 54666f727bcSkettenis 54766f727bcSkettenis for (i = 4; i < nintr; i += 4) { 54866f727bcSkettenis /* lowest priority ?? */ 54966f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 55066f727bcSkettenis GICD_IPRIORITYR(i), 0xffffffff); 55166f727bcSkettenis } 55266f727bcSkettenis 553d532cfaeSpatrick /* Set all interrupts to G1NS */ 554d532cfaeSpatrick for (i = 1; i < nintr / 32; i++) { 555d532cfaeSpatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 556d532cfaeSpatrick GICD_IGROUPR(i * 32), ~0); 557d532cfaeSpatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 558d532cfaeSpatrick GICD_IGRPMODR(i * 32), 0); 559d532cfaeSpatrick } 560d532cfaeSpatrick 56166f727bcSkettenis for (i = 2; i < nintr / 16; i++) { 56266f727bcSkettenis /* irq 32 - N */ 56366f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 56466f727bcSkettenis GICD_ICFGR(i * 16), 0); 56566f727bcSkettenis } 56666f727bcSkettenis 56766f727bcSkettenis agintc_cpuinit(); 56866f727bcSkettenis 569f0a2ac19Skettenis sc->sc_handler = mallocarray(nintr, 570f0a2ac19Skettenis sizeof(*sc->sc_handler), M_DEVBUF, M_ZERO | M_WAITOK); 57166f727bcSkettenis for (i = 0; i < nintr; i++) 572f0a2ac19Skettenis TAILQ_INIT(&sc->sc_handler[i].iq_list); 573f83e6fb8Spatrick sc->sc_lpi = mallocarray(sc->sc_nlpi, 574f83e6fb8Spatrick sizeof(*sc->sc_lpi), M_DEVBUF, M_ZERO | M_WAITOK); 57566f727bcSkettenis 57666f727bcSkettenis /* set priority to IPL_HIGH until configure lowers to desired IPL */ 57766f727bcSkettenis agintc_setipl(IPL_HIGH); 57866f727bcSkettenis 57966f727bcSkettenis /* initialize all interrupts as disabled */ 58066f727bcSkettenis agintc_calc_mask(); 58166f727bcSkettenis 58266f727bcSkettenis /* insert self as interrupt handler */ 58366f727bcSkettenis arm_set_intr_handler(agintc_splraise, agintc_spllower, agintc_splx, 58425da8179Spatrick agintc_setipl, agintc_irq_handler, NULL, 58525da8179Spatrick agintc_enable_wakeup, agintc_disable_wakeup); 58666f727bcSkettenis 58766f727bcSkettenis /* enable interrupts */ 58816b913cdSkettenis ctrl = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR); 589163120c5Skettenis bits = GICD_CTLR_ARE_NS | GICD_CTLR_EnableGrp1A | GICD_CTLR_EnableGrp1; 59016b913cdSkettenis if (sc->sc_rk3399_quirk) { 591163120c5Skettenis bits &= ~GICD_CTLR_EnableGrp1A; 59216b913cdSkettenis bits <<= 1; 59316b913cdSkettenis } 59416b913cdSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR, ctrl | bits); 59566f727bcSkettenis 59666f727bcSkettenis __asm volatile("msr "STR(ICC_PMR)", %x0" :: "r"(0xff)); 5979a716617Skettenis __asm volatile("msr "STR(ICC_BPR1)", %x0" :: "r"(0)); 59816b913cdSkettenis __asm volatile("msr "STR(ICC_IGRPEN1)", %x0" :: "r"(1)); 59966f727bcSkettenis 600d7e3db9cSkettenis #ifdef MULTIPROCESSOR 601d7e3db9cSkettenis /* setup IPI interrupts */ 602d7e3db9cSkettenis 603d7e3db9cSkettenis /* 6044002e08dSkettenis * Ideally we want three IPI interrupts, one for NOP, one for 6054002e08dSkettenis * DDB and one for HALT. However we can survive if only one 6064002e08dSkettenis * is available; it is possible that most are not available to 6074002e08dSkettenis * the non-secure OS. 608d7e3db9cSkettenis */ 609d7e3db9cSkettenis nipi = 0; 610d7e3db9cSkettenis for (i = 0; i < 16; i++) { 611d7e3db9cSkettenis int hwcpu = sc->sc_cpuremap[cpu_number()]; 612d7e3db9cSkettenis int reg, oldreg; 613d7e3db9cSkettenis 614d7e3db9cSkettenis oldreg = bus_space_read_1(sc->sc_iot, sc->sc_r_ioh[hwcpu], 615d7e3db9cSkettenis GICR_IPRIORITYR(i)); 616d7e3db9cSkettenis bus_space_write_1(sc->sc_iot, sc->sc_r_ioh[hwcpu], 617d7e3db9cSkettenis GICR_IPRIORITYR(i), oldreg ^ 0x20); 618d7e3db9cSkettenis 619d7e3db9cSkettenis /* if this interrupt is not usable, pri will be unmodified */ 620d7e3db9cSkettenis reg = bus_space_read_1(sc->sc_iot, sc->sc_r_ioh[hwcpu], 621d7e3db9cSkettenis GICR_IPRIORITYR(i)); 622d7e3db9cSkettenis if (reg == oldreg) 623d7e3db9cSkettenis continue; 624d7e3db9cSkettenis 625d7e3db9cSkettenis /* return to original value, will be set when used */ 626d7e3db9cSkettenis bus_space_write_1(sc->sc_iot, sc->sc_r_ioh[hwcpu], 627d7e3db9cSkettenis GICR_IPRIORITYR(i), oldreg); 628d7e3db9cSkettenis 629d7e3db9cSkettenis if (nipi == 0) 630d7e3db9cSkettenis printf(" ipi: %d", i); 631d7e3db9cSkettenis else 632d7e3db9cSkettenis printf(", %d", i); 633d7e3db9cSkettenis ipiirq[nipi++] = i; 6344002e08dSkettenis if (nipi == 3) 635d7e3db9cSkettenis break; 636d7e3db9cSkettenis } 637d7e3db9cSkettenis 638d7e3db9cSkettenis if (nipi == 0) 639d7e3db9cSkettenis panic("no irq available for IPI"); 640d7e3db9cSkettenis 641d7e3db9cSkettenis switch (nipi) { 642d7e3db9cSkettenis case 1: 643d7e3db9cSkettenis sc->sc_ipi_irq[0] = agintc_intr_establish(ipiirq[0], 6446214007cSpatrick IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL, 6456214007cSpatrick agintc_ipi_combined, sc, "ipi"); 646d7e3db9cSkettenis sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0]; 647d7e3db9cSkettenis sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[0]; 6484002e08dSkettenis sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[0]; 649d7e3db9cSkettenis break; 650d7e3db9cSkettenis case 2: 651d7e3db9cSkettenis sc->sc_ipi_irq[0] = agintc_intr_establish(ipiirq[0], 6526214007cSpatrick IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL, 6536214007cSpatrick agintc_ipi_nop, sc, "ipinop"); 654d7e3db9cSkettenis sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0]; 655d7e3db9cSkettenis sc->sc_ipi_irq[1] = agintc_intr_establish(ipiirq[1], 6566214007cSpatrick IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL, 6574002e08dSkettenis agintc_ipi_combined, sc, "ipi"); 6584002e08dSkettenis sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1]; 6594002e08dSkettenis sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[1]; 6604002e08dSkettenis break; 6614002e08dSkettenis case 3: 6624002e08dSkettenis sc->sc_ipi_irq[0] = agintc_intr_establish(ipiirq[0], 6634002e08dSkettenis IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL, 6644002e08dSkettenis agintc_ipi_nop, sc, "ipinop"); 6654002e08dSkettenis sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0]; 6664002e08dSkettenis sc->sc_ipi_irq[1] = agintc_intr_establish(ipiirq[1], 6674002e08dSkettenis IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL, 6686214007cSpatrick agintc_ipi_ddb, sc, "ipiddb"); 669d7e3db9cSkettenis sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1]; 6704002e08dSkettenis sc->sc_ipi_irq[2] = agintc_intr_establish(ipiirq[2], 6714002e08dSkettenis IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL, 6724002e08dSkettenis agintc_ipi_halt, sc, "ipihalt"); 6734002e08dSkettenis sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[2]; 674d7e3db9cSkettenis break; 675d7e3db9cSkettenis default: 676d7e3db9cSkettenis panic("nipi unexpected number %d", nipi); 677d7e3db9cSkettenis } 678d7e3db9cSkettenis 679d7e3db9cSkettenis intr_send_ipi_func = agintc_send_ipi; 680d7e3db9cSkettenis #endif 681d7e3db9cSkettenis 68266f727bcSkettenis sc->sc_ic.ic_node = faa->fa_node; 68366f727bcSkettenis sc->sc_ic.ic_cookie = self; 68466f727bcSkettenis sc->sc_ic.ic_establish = agintc_intr_establish_fdt; 68566f727bcSkettenis sc->sc_ic.ic_disestablish = agintc_intr_disestablish; 686d7e3db9cSkettenis sc->sc_ic.ic_route = agintc_route_irq; 687d7e3db9cSkettenis sc->sc_ic.ic_cpu_enable = agintc_cpuinit; 688452daaedSpatrick sc->sc_ic.ic_barrier = agintc_intr_barrier; 6898b7bc089Skettenis if (sc->sc_mbi_nranges > 0) 6908b7bc089Skettenis sc->sc_ic.ic_establish_msi = agintc_intr_establish_mbi; 69125da8179Spatrick sc->sc_ic.ic_set_wakeup = agintc_intr_set_wakeup; 69266f727bcSkettenis arm_intr_register_fdt(&sc->sc_ic); 69366f727bcSkettenis 694daf2e6ccSkettenis intr_restore(psw); 695f0a2ac19Skettenis 696f0a2ac19Skettenis /* Attach ITS. */ 697f0a2ac19Skettenis simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa); 698f0a2ac19Skettenis 699f0a2ac19Skettenis return; 700f0a2ac19Skettenis 701f0a2ac19Skettenis unmap: 702934f5440Skettenis if (sc->sc_r_ioh) { 703934f5440Skettenis free(sc->sc_r_ioh, M_DEVBUF, 704934f5440Skettenis sc->sc_num_redist * sizeof(*sc->sc_r_ioh)); 705934f5440Skettenis } 7067c59a772Spatrick if (sc->sc_processor) { 7077c59a772Spatrick free(sc->sc_processor, M_DEVBUF, 7087c59a772Spatrick sc->sc_num_redist * sizeof(*sc->sc_processor)); 709934f5440Skettenis } 710934f5440Skettenis 711f0a2ac19Skettenis if (sc->sc_pend) 712f0a2ac19Skettenis agintc_dmamem_free(sc->sc_dmat, sc->sc_pend); 713f0a2ac19Skettenis if (sc->sc_prop) 714f0a2ac19Skettenis agintc_dmamem_free(sc->sc_dmat, sc->sc_prop); 715f0a2ac19Skettenis 716*53f700b0Skettenis for (idx = 0; idx < sc->sc_num_redist_regions; idx++) { 717*53f700b0Skettenis bus_space_unmap(sc->sc_iot, sc->sc_rbase_ioh[idx], 718*53f700b0Skettenis faa->fa_reg[1 + idx].size); 719*53f700b0Skettenis } 720*53f700b0Skettenis free(sc->sc_rbase_ioh, M_DEVBUF, 721*53f700b0Skettenis sc->sc_num_redist_regions * sizeof(*sc->sc_rbase_ioh)); 722*53f700b0Skettenis 723f0a2ac19Skettenis bus_space_unmap(sc->sc_iot, sc->sc_d_ioh, faa->fa_reg[0].size); 72466f727bcSkettenis } 72566f727bcSkettenis 7268b7bc089Skettenis void 7278b7bc089Skettenis agintc_mbiinit(struct agintc_softc *sc, int node, bus_addr_t addr) 7288b7bc089Skettenis { 7298b7bc089Skettenis uint32_t *ranges; 7308b7bc089Skettenis int i, len; 7318b7bc089Skettenis 7328b7bc089Skettenis if (OF_getproplen(node, "msi-controller") != 0) 7338b7bc089Skettenis return; 7348b7bc089Skettenis 7358b7bc089Skettenis len = OF_getproplen(node, "mbi-ranges"); 7368b7bc089Skettenis if (len <= 0 || len % 2 * sizeof(uint32_t) != 0) 7378b7bc089Skettenis return; 7388b7bc089Skettenis 7398b7bc089Skettenis ranges = malloc(len, M_TEMP, M_WAITOK); 7408b7bc089Skettenis OF_getpropintarray(node, "mbi-ranges", ranges, len); 7418b7bc089Skettenis 7428b7bc089Skettenis sc->sc_mbi_nranges = len / (2 * sizeof(uint32_t)); 7438b7bc089Skettenis sc->sc_mbi_ranges = mallocarray(sc->sc_mbi_nranges, 7448b7bc089Skettenis sizeof(struct agintc_mbi_range), M_DEVBUF, M_WAITOK); 7458b7bc089Skettenis 7468b7bc089Skettenis for (i = 0; i < sc->sc_mbi_nranges; i++) { 7478b7bc089Skettenis sc->sc_mbi_ranges[i].mr_base = ranges[2 * i + 0]; 7488b7bc089Skettenis sc->sc_mbi_ranges[i].mr_span = ranges[2 * i + 1]; 7498b7bc089Skettenis sc->sc_mbi_ranges[i].mr_mbi = 7508b7bc089Skettenis mallocarray(sc->sc_mbi_ranges[i].mr_span, 7518b7bc089Skettenis sizeof(void *), M_DEVBUF, M_WAITOK | M_ZERO); 7528b7bc089Skettenis } 7538b7bc089Skettenis 7548b7bc089Skettenis free(ranges, M_TEMP, len); 7558b7bc089Skettenis 7568b7bc089Skettenis addr = OF_getpropint64(node, "mbi-alias", addr); 7578b7bc089Skettenis sc->sc_mbi_addr = addr + GICD_SETSPI_NSR; 7588b7bc089Skettenis 7598b7bc089Skettenis printf(" mbi"); 7608b7bc089Skettenis } 7618b7bc089Skettenis 76266f727bcSkettenis /* Initialize redistributors on each core. */ 76366f727bcSkettenis void 76466f727bcSkettenis agintc_cpuinit(void) 76566f727bcSkettenis { 76666f727bcSkettenis struct agintc_softc *sc = agintc_sc; 7677c59a772Spatrick uint32_t waker; 76866f727bcSkettenis int timeout = 100000; 7697c59a772Spatrick int hwcpu; 77066f727bcSkettenis int i; 77166f727bcSkettenis 7727c59a772Spatrick hwcpu = sc->sc_cpuremap[cpu_number()]; 77366f727bcSkettenis waker = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 77466f727bcSkettenis GICR_WAKER); 77566f727bcSkettenis waker &= ~(GICR_WAKER_PROCESSORSLEEP); 77666f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], GICR_WAKER, 77766f727bcSkettenis waker); 77866f727bcSkettenis 77966f727bcSkettenis do { 78066f727bcSkettenis waker = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 78166f727bcSkettenis GICR_WAKER); 78266f727bcSkettenis } while (--timeout && (waker & GICR_WAKER_CHILDRENASLEEP)); 78366f727bcSkettenis if (timeout == 0) 78466f727bcSkettenis printf("%s: waker timed out\n", __func__); 78566f727bcSkettenis 78666f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 78766f727bcSkettenis GICR_ICENABLE0, ~0); 78866f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 78966f727bcSkettenis GICR_ICPENDR0, ~0); 79066f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 79166f727bcSkettenis GICR_ICACTIVE0, ~0); 79266f727bcSkettenis for (i = 0; i < 32; i += 4) { 79366f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 79466f727bcSkettenis GICR_IPRIORITYR(i), ~0); 79566f727bcSkettenis } 796d532cfaeSpatrick bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 797d532cfaeSpatrick GICR_IGROUPR0, ~0); 798d532cfaeSpatrick bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 799d532cfaeSpatrick GICR_IGRPMODR0, 0); 800d7e3db9cSkettenis 801d7e3db9cSkettenis if (sc->sc_ipi_irq[0] != NULL) 802d7e3db9cSkettenis agintc_route_irq(sc->sc_ipi_irq[0], IRQ_ENABLE, curcpu()); 803d7e3db9cSkettenis if (sc->sc_ipi_irq[1] != NULL) 804d7e3db9cSkettenis agintc_route_irq(sc->sc_ipi_irq[1], IRQ_ENABLE, curcpu()); 805a2ef92f0Skettenis if (sc->sc_ipi_irq[2] != NULL) 806a2ef92f0Skettenis agintc_route_irq(sc->sc_ipi_irq[2], IRQ_ENABLE, curcpu()); 807d7e3db9cSkettenis 808d7e3db9cSkettenis __asm volatile("msr "STR(ICC_PMR)", %x0" :: "r"(0xff)); 809d7e3db9cSkettenis __asm volatile("msr "STR(ICC_BPR1)", %x0" :: "r"(0)); 810d7e3db9cSkettenis __asm volatile("msr "STR(ICC_IGRPEN1)", %x0" :: "r"(1)); 811daf2e6ccSkettenis intr_enable(); 81266f727bcSkettenis } 81366f727bcSkettenis 81466f727bcSkettenis void 815163120c5Skettenis agintc_set_priority(struct agintc_softc *sc, int irq, int ipl) 81666f727bcSkettenis { 81766f727bcSkettenis struct cpu_info *ci = curcpu(); 81866f727bcSkettenis int hwcpu = sc->sc_cpuremap[ci->ci_cpuid]; 81966f727bcSkettenis uint32_t prival; 82066f727bcSkettenis 821163120c5Skettenis prival = ((0xff - ipl) << sc->sc_prio_shift) & 0xff; 82216b913cdSkettenis 823f0a2ac19Skettenis if (irq >= SPI_BASE) { 82466f727bcSkettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, 82566f727bcSkettenis GICD_IPRIORITYR(irq), prival); 82666f727bcSkettenis } else { 82766f727bcSkettenis /* only sets local redistributor */ 82866f727bcSkettenis bus_space_write_1(sc->sc_iot, sc->sc_r_ioh[hwcpu], 82966f727bcSkettenis GICR_IPRIORITYR(irq), prival); 83066f727bcSkettenis } 83166f727bcSkettenis } 83266f727bcSkettenis 83366f727bcSkettenis void 834163120c5Skettenis agintc_setipl(int ipl) 83566f727bcSkettenis { 836163120c5Skettenis struct agintc_softc *sc = agintc_sc; 83766f727bcSkettenis struct cpu_info *ci = curcpu(); 838daf2e6ccSkettenis u_long psw; 83966f727bcSkettenis uint32_t prival; 84066f727bcSkettenis 84166f727bcSkettenis /* disable here is only to keep hardware in sync with ci->ci_cpl */ 842daf2e6ccSkettenis psw = intr_disable(); 843163120c5Skettenis ci->ci_cpl = ipl; 84466f727bcSkettenis 845163120c5Skettenis prival = ((0xff - ipl) << sc->sc_pmr_shift) & 0xff; 84666f727bcSkettenis __asm volatile("msr "STR(ICC_PMR)", %x0" : : "r" (prival)); 847d7e3db9cSkettenis __isb(); 848d7e3db9cSkettenis 849daf2e6ccSkettenis intr_restore(psw); 85066f727bcSkettenis } 85166f727bcSkettenis 85266f727bcSkettenis void 85325da8179Spatrick agintc_enable_wakeup(void) 85425da8179Spatrick { 85525da8179Spatrick struct agintc_softc *sc = agintc_sc; 85625da8179Spatrick struct intrhand *ih; 85725da8179Spatrick uint8_t *prop; 85825da8179Spatrick int irq, wakeup; 85925da8179Spatrick 86025da8179Spatrick for (irq = 0; irq < sc->sc_nintr; irq++) { 86125da8179Spatrick /* No handler? Disabled already. */ 86225da8179Spatrick if (TAILQ_EMPTY(&sc->sc_handler[irq].iq_list)) 86325da8179Spatrick continue; 86425da8179Spatrick /* Unless we're WAKEUP, disable. */ 86525da8179Spatrick wakeup = 0; 86625da8179Spatrick TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) { 86725da8179Spatrick if (ih->ih_flags & IPL_WAKEUP) { 86825da8179Spatrick wakeup = 1; 86925da8179Spatrick break; 87025da8179Spatrick } 87125da8179Spatrick } 87225da8179Spatrick if (!wakeup) 87325da8179Spatrick agintc_intr_disable(sc, irq); 87425da8179Spatrick } 87525da8179Spatrick 87625da8179Spatrick for (irq = 0; irq < sc->sc_nlpi; irq++) { 877f83e6fb8Spatrick if (sc->sc_lpi[irq] == NULL) 878f83e6fb8Spatrick continue; 879f83e6fb8Spatrick ih = sc->sc_lpi[irq]->li_ih; 880f83e6fb8Spatrick KASSERT(ih != NULL); 881f83e6fb8Spatrick if (ih->ih_flags & IPL_WAKEUP) 88225da8179Spatrick continue; 88325da8179Spatrick prop = AGINTC_DMA_KVA(sc->sc_prop); 88425da8179Spatrick prop[irq] &= ~GICR_PROP_ENABLE; 88525da8179Spatrick /* Make globally visible. */ 88625da8179Spatrick cpu_dcache_wb_range((vaddr_t)&prop[irq], 88725da8179Spatrick sizeof(*prop)); 88825da8179Spatrick __asm volatile("dsb sy"); 889f83e6fb8Spatrick /* Invalidate cache */ 890f83e6fb8Spatrick agintc_msi_inv(sc->sc_lpi[irq]); 89125da8179Spatrick } 89225da8179Spatrick } 89325da8179Spatrick 89425da8179Spatrick void 89525da8179Spatrick agintc_disable_wakeup(void) 89625da8179Spatrick { 89725da8179Spatrick struct agintc_softc *sc = agintc_sc; 89825da8179Spatrick struct intrhand *ih; 89925da8179Spatrick uint8_t *prop; 90025da8179Spatrick int irq, wakeup; 90125da8179Spatrick 90225da8179Spatrick for (irq = 0; irq < sc->sc_nintr; irq++) { 90325da8179Spatrick /* No handler? Keep disabled. */ 90425da8179Spatrick if (TAILQ_EMPTY(&sc->sc_handler[irq].iq_list)) 90525da8179Spatrick continue; 90625da8179Spatrick /* WAKEUPs are already enabled. */ 90725da8179Spatrick wakeup = 0; 90825da8179Spatrick TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) { 90925da8179Spatrick if (ih->ih_flags & IPL_WAKEUP) { 91025da8179Spatrick wakeup = 1; 91125da8179Spatrick break; 91225da8179Spatrick } 91325da8179Spatrick } 91425da8179Spatrick if (!wakeup) 91525da8179Spatrick agintc_intr_enable(sc, irq); 91625da8179Spatrick } 91725da8179Spatrick 91825da8179Spatrick for (irq = 0; irq < sc->sc_nlpi; irq++) { 919f83e6fb8Spatrick if (sc->sc_lpi[irq] == NULL) 920f83e6fb8Spatrick continue; 921f83e6fb8Spatrick ih = sc->sc_lpi[irq]->li_ih; 922f83e6fb8Spatrick KASSERT(ih != NULL); 923f83e6fb8Spatrick if (ih->ih_flags & IPL_WAKEUP) 92425da8179Spatrick continue; 92525da8179Spatrick prop = AGINTC_DMA_KVA(sc->sc_prop); 92625da8179Spatrick prop[irq] |= GICR_PROP_ENABLE; 92725da8179Spatrick /* Make globally visible. */ 92825da8179Spatrick cpu_dcache_wb_range((vaddr_t)&prop[irq], 92925da8179Spatrick sizeof(*prop)); 93025da8179Spatrick __asm volatile("dsb sy"); 931f83e6fb8Spatrick /* Invalidate cache */ 932f83e6fb8Spatrick agintc_msi_inv(sc->sc_lpi[irq]); 93325da8179Spatrick } 93425da8179Spatrick } 93525da8179Spatrick 93625da8179Spatrick void 93766f727bcSkettenis agintc_intr_enable(struct agintc_softc *sc, int irq) 93866f727bcSkettenis { 93966f727bcSkettenis struct cpu_info *ci = curcpu(); 94066f727bcSkettenis int hwcpu = sc->sc_cpuremap[ci->ci_cpuid]; 94166f727bcSkettenis int bit = 1 << IRQ_TO_REG32BIT(irq); 94266f727bcSkettenis 94366f727bcSkettenis if (irq >= 32) { 94466f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 94566f727bcSkettenis GICD_ISENABLER(irq), bit); 94666f727bcSkettenis } else { 94766f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 94866f727bcSkettenis GICR_ISENABLE0, bit); 94966f727bcSkettenis } 95066f727bcSkettenis } 95166f727bcSkettenis 95266f727bcSkettenis void 95366f727bcSkettenis agintc_intr_disable(struct agintc_softc *sc, int irq) 95466f727bcSkettenis { 95566f727bcSkettenis struct cpu_info *ci = curcpu(); 95666f727bcSkettenis int hwcpu = sc->sc_cpuremap[ci->ci_cpuid]; 95766f727bcSkettenis 95866f727bcSkettenis if (irq >= 32) { 95966f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 96066f727bcSkettenis GICD_ICENABLER(irq), 1 << IRQ_TO_REG32BIT(irq)); 96166f727bcSkettenis } else { 96266f727bcSkettenis bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 96366f727bcSkettenis GICR_ICENABLE0, 1 << IRQ_TO_REG32BIT(irq)); 96466f727bcSkettenis } 96566f727bcSkettenis } 96666f727bcSkettenis 96766f727bcSkettenis void 9686214007cSpatrick agintc_intr_config(struct agintc_softc *sc, int irq, int type) 9696214007cSpatrick { 9706214007cSpatrick uint32_t reg; 9716214007cSpatrick 9726214007cSpatrick /* Don't dare to change SGIs or PPIs (yet) */ 9736214007cSpatrick if (irq < 32) 9746214007cSpatrick return; 9756214007cSpatrick 9766214007cSpatrick reg = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_ICFGR(irq)); 9776214007cSpatrick reg &= ~GICD_ICFGR_TRIG_MASK(irq); 9786214007cSpatrick if (type == IST_EDGE_RISING) 9796214007cSpatrick reg |= GICD_ICFGR_TRIG_EDGE(irq); 9806214007cSpatrick else 9816214007cSpatrick reg |= GICD_ICFGR_TRIG_LEVEL(irq); 9826214007cSpatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_ICFGR(irq), reg); 9836214007cSpatrick } 9846214007cSpatrick 9856214007cSpatrick void 98666f727bcSkettenis agintc_calc_mask(void) 98766f727bcSkettenis { 98866f727bcSkettenis struct agintc_softc *sc = agintc_sc; 98966f727bcSkettenis int irq; 99066f727bcSkettenis 99166f727bcSkettenis for (irq = 0; irq < sc->sc_nintr; irq++) 99266f727bcSkettenis agintc_calc_irq(sc, irq); 99366f727bcSkettenis } 99466f727bcSkettenis 99566f727bcSkettenis void 99666f727bcSkettenis agintc_calc_irq(struct agintc_softc *sc, int irq) 99766f727bcSkettenis { 9987c59a772Spatrick struct cpu_info *ci = sc->sc_handler[irq].iq_ci; 99966f727bcSkettenis struct intrhand *ih; 100066f727bcSkettenis int max = IPL_NONE; 100166f727bcSkettenis int min = IPL_HIGH; 100266f727bcSkettenis 1003f0a2ac19Skettenis TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) { 100466f727bcSkettenis if (ih->ih_ipl > max) 100566f727bcSkettenis max = ih->ih_ipl; 100666f727bcSkettenis 100766f727bcSkettenis if (ih->ih_ipl < min) 100866f727bcSkettenis min = ih->ih_ipl; 100966f727bcSkettenis } 101066f727bcSkettenis 101166f727bcSkettenis if (max == IPL_NONE) 101266f727bcSkettenis min = IPL_NONE; 101366f727bcSkettenis 1014eb0c9b3cSpatrick if (sc->sc_handler[irq].iq_irq_max == max && 1015eb0c9b3cSpatrick sc->sc_handler[irq].iq_irq_min == min) 1016eb0c9b3cSpatrick return; 1017eb0c9b3cSpatrick 1018eb0c9b3cSpatrick sc->sc_handler[irq].iq_irq_max = max; 1019eb0c9b3cSpatrick sc->sc_handler[irq].iq_irq_min = min; 1020eb0c9b3cSpatrick 102166f727bcSkettenis #ifdef DEBUG_AGINTC 102266f727bcSkettenis if (min != IPL_NONE) 102366f727bcSkettenis printf("irq %d to block at %d %d \n", irq, max, min ); 102466f727bcSkettenis #endif 102566f727bcSkettenis /* Enable interrupts at lower levels, clear -> enable */ 102666f727bcSkettenis /* Set interrupt priority/enable */ 102766f727bcSkettenis if (min != IPL_NONE) { 102866f727bcSkettenis agintc_set_priority(sc, irq, min); 102966f727bcSkettenis agintc_route(sc, irq, IRQ_ENABLE, ci); 103066f727bcSkettenis agintc_intr_enable(sc, irq); 103166f727bcSkettenis } else { 103266f727bcSkettenis agintc_intr_disable(sc, irq); 103366f727bcSkettenis agintc_route(sc, irq, IRQ_DISABLE, ci); 103466f727bcSkettenis } 103566f727bcSkettenis } 103666f727bcSkettenis 103766f727bcSkettenis void 103866f727bcSkettenis agintc_splx(int new) 103966f727bcSkettenis { 104066f727bcSkettenis struct cpu_info *ci = curcpu(); 104166f727bcSkettenis 104266f727bcSkettenis if (ci->ci_ipending & arm_smask[new]) 104366f727bcSkettenis arm_do_pending_intr(new); 104466f727bcSkettenis 104566f727bcSkettenis agintc_setipl(new); 104666f727bcSkettenis } 104766f727bcSkettenis 104866f727bcSkettenis int 104966f727bcSkettenis agintc_spllower(int new) 105066f727bcSkettenis { 105166f727bcSkettenis struct cpu_info *ci = curcpu(); 105266f727bcSkettenis int old = ci->ci_cpl; 105366f727bcSkettenis 105466f727bcSkettenis agintc_splx(new); 105566f727bcSkettenis return (old); 105666f727bcSkettenis } 105766f727bcSkettenis 105866f727bcSkettenis int 105966f727bcSkettenis agintc_splraise(int new) 106066f727bcSkettenis { 106166f727bcSkettenis struct cpu_info *ci = curcpu(); 106266f727bcSkettenis int old = ci->ci_cpl; 106366f727bcSkettenis 106466f727bcSkettenis /* 106566f727bcSkettenis * setipl must always be called because there is a race window 106666f727bcSkettenis * where the variable is updated before the mask is set 106766f727bcSkettenis * an interrupt occurs in that window without the mask always 106866f727bcSkettenis * being set, the hardware might not get updated on the next 106966f727bcSkettenis * splraise completely messing up spl protection. 107066f727bcSkettenis */ 107166f727bcSkettenis if (old > new) 107266f727bcSkettenis new = old; 107366f727bcSkettenis 107466f727bcSkettenis agintc_setipl(new); 107566f727bcSkettenis return (old); 107666f727bcSkettenis } 107766f727bcSkettenis 107866f727bcSkettenis uint32_t 107966f727bcSkettenis agintc_iack(void) 108066f727bcSkettenis { 108166f727bcSkettenis int irq; 108266f727bcSkettenis 108366f727bcSkettenis __asm volatile("mrs %x0, "STR(ICC_IAR1) : "=r" (irq)); 108466f727bcSkettenis __asm volatile("dsb sy"); 108566f727bcSkettenis return irq; 108666f727bcSkettenis } 108766f727bcSkettenis 108866f727bcSkettenis void 1089d7e3db9cSkettenis agintc_route_irq(void *v, int enable, struct cpu_info *ci) 1090d7e3db9cSkettenis { 1091d7e3db9cSkettenis struct agintc_softc *sc = agintc_sc; 1092d7e3db9cSkettenis struct intrhand *ih = v; 1093d7e3db9cSkettenis 1094d7e3db9cSkettenis if (enable) { 1095d7e3db9cSkettenis agintc_set_priority(sc, ih->ih_irq, 1096eb0c9b3cSpatrick sc->sc_handler[ih->ih_irq].iq_irq_min); 1097d7e3db9cSkettenis agintc_route(sc, ih->ih_irq, IRQ_ENABLE, ci); 1098d7e3db9cSkettenis agintc_intr_enable(sc, ih->ih_irq); 1099d7e3db9cSkettenis } 1100d7e3db9cSkettenis } 1101d7e3db9cSkettenis 1102d7e3db9cSkettenis void 110366f727bcSkettenis agintc_route(struct agintc_softc *sc, int irq, int enable, struct cpu_info *ci) 110466f727bcSkettenis { 110566f727bcSkettenis /* XXX does not yet support 'participating node' */ 110666f727bcSkettenis if (irq >= 32) { 110766f727bcSkettenis #ifdef DEBUG_AGINTC 11085ce1efbdSkettenis printf("router %x irq %d val %016llx\n", GICD_IROUTER(irq), 11095ce1efbdSkettenis irq, ci->ci_mpidr & MPIDR_AFF); 111066f727bcSkettenis #endif 111166f727bcSkettenis bus_space_write_8(sc->sc_iot, sc->sc_d_ioh, 11125ce1efbdSkettenis GICD_IROUTER(irq), ci->ci_mpidr & MPIDR_AFF); 111366f727bcSkettenis } 111466f727bcSkettenis } 111566f727bcSkettenis 111666f727bcSkettenis void 1117452daaedSpatrick agintc_intr_barrier(void *cookie) 1118452daaedSpatrick { 1119452daaedSpatrick struct intrhand *ih = cookie; 1120452daaedSpatrick 1121452daaedSpatrick sched_barrier(ih->ih_ci); 1122452daaedSpatrick } 1123452daaedSpatrick 1124452daaedSpatrick void 1125f0a2ac19Skettenis agintc_run_handler(struct intrhand *ih, void *frame, int s) 112666f727bcSkettenis { 112766f727bcSkettenis void *arg; 1128f0a2ac19Skettenis int handled; 112966f727bcSkettenis 11307a7b3facSkettenis #ifdef MULTIPROCESSOR 11317a7b3facSkettenis int need_lock; 11327a7b3facSkettenis 11337a7b3facSkettenis if (ih->ih_flags & IPL_MPSAFE) 11347a7b3facSkettenis need_lock = 0; 11357a7b3facSkettenis else 11367a7b3facSkettenis need_lock = s < IPL_SCHED; 11377a7b3facSkettenis 11387a7b3facSkettenis if (need_lock) 11397a7b3facSkettenis KERNEL_LOCK(); 11407a7b3facSkettenis #endif 11417a7b3facSkettenis 1142f9b35d7eSkettenis if (ih->ih_arg) 114366f727bcSkettenis arg = ih->ih_arg; 114466f727bcSkettenis else 114566f727bcSkettenis arg = frame; 114666f727bcSkettenis 114766f727bcSkettenis handled = ih->ih_func(arg); 114866f727bcSkettenis if (handled) 114966f727bcSkettenis ih->ih_count.ec_count++; 115066f727bcSkettenis 11517a7b3facSkettenis #ifdef MULTIPROCESSOR 11527a7b3facSkettenis if (need_lock) 11537a7b3facSkettenis KERNEL_UNLOCK(); 11547a7b3facSkettenis #endif 115566f727bcSkettenis } 1156f0a2ac19Skettenis 1157f0a2ac19Skettenis void 1158f0a2ac19Skettenis agintc_irq_handler(void *frame) 1159f0a2ac19Skettenis { 1160f0a2ac19Skettenis struct agintc_softc *sc = agintc_sc; 1161f0a2ac19Skettenis struct intrhand *ih; 1162f0a2ac19Skettenis int irq, pri, s; 1163f0a2ac19Skettenis 1164f0a2ac19Skettenis irq = agintc_iack(); 1165f0a2ac19Skettenis 1166f0a2ac19Skettenis #ifdef DEBUG_AGINTC 1167f0a2ac19Skettenis if (irq != 30) 1168f0a2ac19Skettenis printf("irq %d fired\n", irq); 1169f0a2ac19Skettenis else { 1170f0a2ac19Skettenis static int cnt = 0; 1171f0a2ac19Skettenis if ((cnt++ % 100) == 0) { 1172f0a2ac19Skettenis printf("irq %d fired * _100\n", irq); 1173f0a2ac19Skettenis #ifdef DDB 1174f0a2ac19Skettenis db_enter(); 1175f0a2ac19Skettenis #endif 1176f0a2ac19Skettenis } 1177f0a2ac19Skettenis } 1178f0a2ac19Skettenis #endif 1179f0a2ac19Skettenis 1180f0a2ac19Skettenis if (irq == 1023) { 1181f0a2ac19Skettenis sc->sc_spur.ec_count++; 1182f0a2ac19Skettenis return; 1183f0a2ac19Skettenis } 1184f0a2ac19Skettenis 1185f0a2ac19Skettenis if ((irq >= sc->sc_nintr && irq < LPI_BASE) || 1186f0a2ac19Skettenis irq >= LPI_BASE + sc->sc_nlpi) { 1187f0a2ac19Skettenis return; 1188f0a2ac19Skettenis } 1189f0a2ac19Skettenis 1190f0a2ac19Skettenis if (irq >= LPI_BASE) { 1191f83e6fb8Spatrick if (sc->sc_lpi[irq - LPI_BASE] == NULL) 1192f0a2ac19Skettenis return; 1193f83e6fb8Spatrick ih = sc->sc_lpi[irq - LPI_BASE]->li_ih; 1194f83e6fb8Spatrick KASSERT(ih != NULL); 1195f0a2ac19Skettenis 1196f0a2ac19Skettenis s = agintc_splraise(ih->ih_ipl); 1197666951e3Skettenis intr_enable(); 1198f0a2ac19Skettenis agintc_run_handler(ih, frame, s); 1199666951e3Skettenis intr_disable(); 1200f0a2ac19Skettenis agintc_eoi(irq); 1201f0a2ac19Skettenis 1202f0a2ac19Skettenis agintc_splx(s); 1203f0a2ac19Skettenis return; 1204f0a2ac19Skettenis } 1205f0a2ac19Skettenis 1206eb0c9b3cSpatrick pri = sc->sc_handler[irq].iq_irq_max; 1207f0a2ac19Skettenis s = agintc_splraise(pri); 1208666951e3Skettenis intr_enable(); 1209f0a2ac19Skettenis TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) { 1210f0a2ac19Skettenis agintc_run_handler(ih, frame, s); 1211f0a2ac19Skettenis } 1212666951e3Skettenis intr_disable(); 121366f727bcSkettenis agintc_eoi(irq); 121466f727bcSkettenis 121566f727bcSkettenis agintc_splx(s); 121666f727bcSkettenis } 121766f727bcSkettenis 121866f727bcSkettenis void * 121966f727bcSkettenis agintc_intr_establish_fdt(void *cookie, int *cell, int level, 1220789e88a4Spatrick struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 122166f727bcSkettenis { 122266f727bcSkettenis struct agintc_softc *sc = agintc_sc; 122366f727bcSkettenis int irq; 12246214007cSpatrick int type; 122566f727bcSkettenis 122666f727bcSkettenis /* 2nd cell contains the interrupt number */ 122766f727bcSkettenis irq = cell[1]; 122866f727bcSkettenis 122966f727bcSkettenis /* 1st cell contains type: 0 SPI (32-X), 1 PPI (16-31) */ 123066f727bcSkettenis if (cell[0] == 0) 1231f0a2ac19Skettenis irq += SPI_BASE; 123266f727bcSkettenis else if (cell[0] == 1) 1233f0a2ac19Skettenis irq += PPI_BASE; 123466f727bcSkettenis else 1235f0a2ac19Skettenis panic("%s: bogus interrupt type", sc->sc_sbus.sc_dev.dv_xname); 123666f727bcSkettenis 12376214007cSpatrick /* SPIs are only active-high level or low-to-high edge */ 12386214007cSpatrick if (cell[2] & 0x3) 12396214007cSpatrick type = IST_EDGE_RISING; 12406214007cSpatrick else 12416214007cSpatrick type = IST_LEVEL_HIGH; 12426214007cSpatrick 12436214007cSpatrick return agintc_intr_establish(irq, type, level, ci, func, arg, name); 124466f727bcSkettenis } 124566f727bcSkettenis 124666f727bcSkettenis void * 12476214007cSpatrick agintc_intr_establish(int irqno, int type, int level, struct cpu_info *ci, 1248789e88a4Spatrick int (*func)(void *), void *arg, char *name) 124966f727bcSkettenis { 125066f727bcSkettenis struct agintc_softc *sc = agintc_sc; 125166f727bcSkettenis struct intrhand *ih; 1252daf2e6ccSkettenis u_long psw; 125366f727bcSkettenis 1254f0a2ac19Skettenis if (irqno < 0 || (irqno >= sc->sc_nintr && irqno < LPI_BASE) || 1255f0a2ac19Skettenis irqno >= LPI_BASE + sc->sc_nlpi) 125666f727bcSkettenis panic("agintc_intr_establish: bogus irqnumber %d: %s", 125766f727bcSkettenis irqno, name); 125866f727bcSkettenis 1259789e88a4Spatrick if (ci == NULL) 1260789e88a4Spatrick ci = &cpu_info_primary; 1261789e88a4Spatrick 126266f727bcSkettenis ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK); 126366f727bcSkettenis ih->ih_func = func; 126466f727bcSkettenis ih->ih_arg = arg; 1265d7e3db9cSkettenis ih->ih_ipl = level & IPL_IRQMASK; 1266d7e3db9cSkettenis ih->ih_flags = level & IPL_FLAGMASK; 126766f727bcSkettenis ih->ih_irq = irqno; 126866f727bcSkettenis ih->ih_name = name; 1269452daaedSpatrick ih->ih_ci = ci; 127066f727bcSkettenis 1271daf2e6ccSkettenis psw = intr_disable(); 127266f727bcSkettenis 12737c59a772Spatrick if (irqno < LPI_BASE) { 12747c59a772Spatrick if (!TAILQ_EMPTY(&sc->sc_handler[irqno].iq_list) && 12757c59a772Spatrick sc->sc_handler[irqno].iq_ci != ci) { 1276daf2e6ccSkettenis intr_restore(psw); 12777c59a772Spatrick free(ih, M_DEVBUF, sizeof *ih); 12787c59a772Spatrick return NULL; 12797c59a772Spatrick } 1280f0a2ac19Skettenis TAILQ_INSERT_TAIL(&sc->sc_handler[irqno].iq_list, ih, ih_list); 12817c59a772Spatrick sc->sc_handler[irqno].iq_ci = ci; 1282f83e6fb8Spatrick } 128366f727bcSkettenis 128466f727bcSkettenis if (name != NULL) 128566f727bcSkettenis evcount_attach(&ih->ih_count, name, &ih->ih_irq); 128666f727bcSkettenis 128766f727bcSkettenis #ifdef DEBUG_AGINTC 128866f727bcSkettenis printf("%s: irq %d level %d [%s]\n", __func__, irqno, level, name); 128966f727bcSkettenis #endif 129066f727bcSkettenis 1291f0a2ac19Skettenis if (irqno < LPI_BASE) { 12926214007cSpatrick agintc_intr_config(sc, irqno, type); 129366f727bcSkettenis agintc_calc_irq(sc, irqno); 1294f0a2ac19Skettenis } else { 12952896618dSkettenis uint8_t *prop = AGINTC_DMA_KVA(sc->sc_prop); 12962896618dSkettenis 12973346ccb8Skettenis prop[irqno - LPI_BASE] = (((0xff - ih->ih_ipl) << 4) & 0xff) | 129816b913cdSkettenis GICR_PROP_GROUP1 | GICR_PROP_ENABLE; 12992896618dSkettenis 13002896618dSkettenis /* Make globally visible. */ 130135175b85Skettenis cpu_dcache_wb_range((vaddr_t)&prop[irqno - LPI_BASE], 130235175b85Skettenis sizeof(*prop)); 13032896618dSkettenis __asm volatile("dsb sy"); 1304f0a2ac19Skettenis } 130566f727bcSkettenis 1306daf2e6ccSkettenis intr_restore(psw); 130766f727bcSkettenis return (ih); 130866f727bcSkettenis } 130966f727bcSkettenis 131066f727bcSkettenis void 131166f727bcSkettenis agintc_intr_disestablish(void *cookie) 131266f727bcSkettenis { 131366f727bcSkettenis struct agintc_softc *sc = agintc_sc; 131466f727bcSkettenis struct intrhand *ih = cookie; 131566f727bcSkettenis int irqno = ih->ih_irq; 1316daf2e6ccSkettenis u_long psw; 13178b7bc089Skettenis struct agintc_mbi_range *mr; 13188b7bc089Skettenis int i; 131966f727bcSkettenis 1320daf2e6ccSkettenis psw = intr_disable(); 132166f727bcSkettenis 132235175b85Skettenis if (irqno < LPI_BASE) { 1323f0a2ac19Skettenis TAILQ_REMOVE(&sc->sc_handler[irqno].iq_list, ih, ih_list); 132466f727bcSkettenis agintc_calc_irq(sc, irqno); 132566f727bcSkettenis 13268b7bc089Skettenis /* In case this is an MBI, free it */ 13278b7bc089Skettenis for (i = 0; i < sc->sc_mbi_nranges; i++) { 13288b7bc089Skettenis mr = &sc->sc_mbi_ranges[i]; 13298b7bc089Skettenis if (irqno < mr->mr_base) 13308b7bc089Skettenis continue; 13318b7bc089Skettenis if (irqno >= mr->mr_base + mr->mr_span) 13328b7bc089Skettenis break; 13338b7bc089Skettenis if (mr->mr_mbi[irqno - mr->mr_base] != NULL) 13348b7bc089Skettenis mr->mr_mbi[irqno - mr->mr_base] = NULL; 13358b7bc089Skettenis } 133635175b85Skettenis } else { 133735175b85Skettenis uint8_t *prop = AGINTC_DMA_KVA(sc->sc_prop); 133835175b85Skettenis 133935175b85Skettenis prop[irqno - LPI_BASE] = 0; 134035175b85Skettenis 134135175b85Skettenis /* Make globally visible. */ 134235175b85Skettenis cpu_dcache_wb_range((vaddr_t)&prop[irqno - LPI_BASE], 134335175b85Skettenis sizeof(*prop)); 134435175b85Skettenis __asm volatile("dsb sy"); 134535175b85Skettenis } 134635175b85Skettenis 134735175b85Skettenis if (ih->ih_name != NULL) 134835175b85Skettenis evcount_detach(&ih->ih_count); 13498b7bc089Skettenis 1350daf2e6ccSkettenis intr_restore(psw); 135166f727bcSkettenis 135266f727bcSkettenis free(ih, M_DEVBUF, 0); 135366f727bcSkettenis } 135466f727bcSkettenis 135525da8179Spatrick void 135625da8179Spatrick agintc_intr_set_wakeup(void *cookie) 135725da8179Spatrick { 135825da8179Spatrick struct intrhand *ih = cookie; 135925da8179Spatrick 136025da8179Spatrick ih->ih_flags |= IPL_WAKEUP; 136125da8179Spatrick } 136225da8179Spatrick 13638b7bc089Skettenis void * 13648b7bc089Skettenis agintc_intr_establish_mbi(void *self, uint64_t *addr, uint64_t *data, 13658b7bc089Skettenis int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 13668b7bc089Skettenis { 13678b7bc089Skettenis struct agintc_softc *sc = agintc_sc; 13688b7bc089Skettenis struct agintc_mbi_range *mr; 13698b7bc089Skettenis void *cookie; 13708b7bc089Skettenis int i, j, hwcpu; 13718b7bc089Skettenis 13728b7bc089Skettenis if (ci == NULL) 13738b7bc089Skettenis ci = &cpu_info_primary; 13748b7bc089Skettenis hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid]; 13758b7bc089Skettenis 13768b7bc089Skettenis for (i = 0; i < sc->sc_mbi_nranges; i++) { 13778b7bc089Skettenis mr = &sc->sc_mbi_ranges[i]; 13788b7bc089Skettenis for (j = 0; j < mr->mr_span; j++) { 13798b7bc089Skettenis if (mr->mr_mbi[j] != NULL) 13808b7bc089Skettenis continue; 13818b7bc089Skettenis 13828b7bc089Skettenis cookie = agintc_intr_establish(mr->mr_base + j, 13838b7bc089Skettenis IST_EDGE_RISING, level, ci, func, arg, name); 13848b7bc089Skettenis if (cookie == NULL) 13858b7bc089Skettenis return NULL; 13868b7bc089Skettenis 13878b7bc089Skettenis *addr = sc->sc_mbi_addr; 13888b7bc089Skettenis *data = mr->mr_base + j; 13898b7bc089Skettenis 13908b7bc089Skettenis mr->mr_mbi[j] = cookie; 13918b7bc089Skettenis return cookie; 13928b7bc089Skettenis } 13938b7bc089Skettenis } 13948b7bc089Skettenis 13958b7bc089Skettenis return NULL; 13968b7bc089Skettenis } 13978b7bc089Skettenis 139866f727bcSkettenis void 139966f727bcSkettenis agintc_eoi(uint32_t eoi) 140066f727bcSkettenis { 140166f727bcSkettenis __asm volatile("msr "STR(ICC_EOIR1)", %x0" :: "r" (eoi)); 140266f727bcSkettenis __isb(); 140366f727bcSkettenis } 140466f727bcSkettenis 140566f727bcSkettenis void 140666f727bcSkettenis agintc_d_wait_rwp(struct agintc_softc *sc) 140766f727bcSkettenis { 140866f727bcSkettenis int count = 100000; 140966f727bcSkettenis uint32_t v; 141066f727bcSkettenis 141166f727bcSkettenis do { 141266f727bcSkettenis v = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR); 141366f727bcSkettenis } while (--count && (v & GICD_CTLR_RWP)); 141466f727bcSkettenis 141566f727bcSkettenis if (count == 0) 1416d7e3db9cSkettenis panic("%s: RWP timed out 0x08%x", __func__, v); 141766f727bcSkettenis } 141866f727bcSkettenis 141966f727bcSkettenis void 142066f727bcSkettenis agintc_r_wait_rwp(struct agintc_softc *sc) 142166f727bcSkettenis { 142266f727bcSkettenis struct cpu_info *ci = curcpu(); 142366f727bcSkettenis int hwcpu = sc->sc_cpuremap[ci->ci_cpuid]; 142466f727bcSkettenis int count = 100000; 142566f727bcSkettenis uint32_t v; 142666f727bcSkettenis 142766f727bcSkettenis do { 142866f727bcSkettenis v = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], 142966f727bcSkettenis GICR_CTLR); 143066f727bcSkettenis } while (--count && (v & GICR_CTLR_RWP)); 143166f727bcSkettenis 143266f727bcSkettenis if (count == 0) 1433d7e3db9cSkettenis panic("%s: RWP timed out 0x08%x", __func__, v); 1434d7e3db9cSkettenis } 1435d7e3db9cSkettenis 1436d7e3db9cSkettenis #ifdef MULTIPROCESSOR 1437d7e3db9cSkettenis int 1438d7e3db9cSkettenis agintc_ipi_ddb(void *v) 1439d7e3db9cSkettenis { 1440d7e3db9cSkettenis /* XXX */ 1441a9a6a9f2Sderaadt #ifdef DDB 1442d7e3db9cSkettenis db_enter(); 1443a9a6a9f2Sderaadt #endif 1444d7e3db9cSkettenis return 1; 1445d7e3db9cSkettenis } 1446d7e3db9cSkettenis 1447d7e3db9cSkettenis int 14484002e08dSkettenis agintc_ipi_halt(void *v) 14494002e08dSkettenis { 1450aa08aab7Skettenis struct agintc_softc *sc = v; 1451aa08aab7Skettenis int old = curcpu()->ci_cpl; 1452aa08aab7Skettenis 1453aa08aab7Skettenis intr_disable(); 1454aa08aab7Skettenis agintc_eoi(sc->sc_ipi_num[ARM_IPI_HALT]); 1455aa08aab7Skettenis agintc_setipl(IPL_NONE); 1456aa08aab7Skettenis 14574002e08dSkettenis cpu_halt(); 1458aa08aab7Skettenis 1459aa08aab7Skettenis agintc_setipl(old); 1460aa08aab7Skettenis intr_enable(); 14614002e08dSkettenis return 1; 14624002e08dSkettenis } 14634002e08dSkettenis 14644002e08dSkettenis int 1465d7e3db9cSkettenis agintc_ipi_nop(void *v) 1466d7e3db9cSkettenis { 1467d7e3db9cSkettenis /* Nothing to do here, just enough to wake up from WFI */ 1468d7e3db9cSkettenis return 1; 1469d7e3db9cSkettenis } 1470d7e3db9cSkettenis 1471d7e3db9cSkettenis int 1472d7e3db9cSkettenis agintc_ipi_combined(void *v) 1473d7e3db9cSkettenis { 1474d7e3db9cSkettenis struct agintc_softc *sc = v; 1475d7e3db9cSkettenis 1476d7e3db9cSkettenis if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_DDB) { 1477d7e3db9cSkettenis sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP; 1478d7e3db9cSkettenis return agintc_ipi_ddb(v); 14794002e08dSkettenis } else if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_HALT) { 14804002e08dSkettenis sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP; 14814002e08dSkettenis return agintc_ipi_halt(v); 1482d7e3db9cSkettenis } else { 1483d7e3db9cSkettenis return agintc_ipi_nop(v); 1484d7e3db9cSkettenis } 148566f727bcSkettenis } 148666f727bcSkettenis 148766f727bcSkettenis void 1488d7e3db9cSkettenis agintc_send_ipi(struct cpu_info *ci, int id) 148966f727bcSkettenis { 1490d7e3db9cSkettenis struct agintc_softc *sc = agintc_sc; 1491d7e3db9cSkettenis uint64_t sendmask; 149266f727bcSkettenis 1493d7e3db9cSkettenis if (ci == curcpu() && id == ARM_IPI_NOP) 1494d7e3db9cSkettenis return; 1495d7e3db9cSkettenis 14964002e08dSkettenis /* never overwrite IPI_DDB or IPI_HALT with IPI_NOP */ 14974002e08dSkettenis if (id == ARM_IPI_DDB || id == ARM_IPI_HALT) 1498d7e3db9cSkettenis sc->sc_ipi_reason[ci->ci_cpuid] = id; 1499d7e3db9cSkettenis 1500d7e3db9cSkettenis /* will only send 1 cpu */ 15015ce1efbdSkettenis sendmask = (ci->ci_mpidr & MPIDR_AFF3) << 16; 15025ce1efbdSkettenis sendmask |= (ci->ci_mpidr & MPIDR_AFF2) << 16; 15035ce1efbdSkettenis sendmask |= (ci->ci_mpidr & MPIDR_AFF1) << 8; 15045ce1efbdSkettenis sendmask |= 1 << (ci->ci_mpidr & 0x0f); 1505d7e3db9cSkettenis sendmask |= (sc->sc_ipi_num[id] << 24); 1506d7e3db9cSkettenis 1507d7e3db9cSkettenis __asm volatile ("msr " STR(ICC_SGI1R)", %x0" ::"r"(sendmask)); 150866f727bcSkettenis } 1509d7e3db9cSkettenis #endif 1510f0a2ac19Skettenis 1511f0a2ac19Skettenis /* 1512f0a2ac19Skettenis * GICv3 ITS controller for MSI interrupts. 1513f0a2ac19Skettenis */ 1514f0a2ac19Skettenis #define GITS_CTLR 0x0000 1515f0a2ac19Skettenis #define GITS_CTLR_ENABLED (1UL << 0) 1516f0a2ac19Skettenis #define GITS_TYPER 0x0008 1517f0a2ac19Skettenis #define GITS_TYPER_CIL (1ULL << 36) 151848352e7bSkettenis #define GITS_TYPER_CIDBITS(x) (((x) >> 32) & 0xf) 1519f0a2ac19Skettenis #define GITS_TYPER_HCC(x) (((x) >> 24) & 0xff) 1520f0a2ac19Skettenis #define GITS_TYPER_PTA (1ULL << 19) 152191fec423Skettenis #define GITS_TYPER_DEVBITS(x) (((x) >> 13) & 0x1f) 1522f0a2ac19Skettenis #define GITS_TYPER_ITE_SZ(x) (((x) >> 4) & 0xf) 1523f0a2ac19Skettenis #define GITS_TYPER_PHYS (1ULL << 0) 1524f0a2ac19Skettenis #define GITS_CBASER 0x0080 1525f0a2ac19Skettenis #define GITS_CBASER_VALID (1ULL << 63) 1526f0a2ac19Skettenis #define GITS_CBASER_IC_NORM_NC (1ULL << 59) 1527f0a2ac19Skettenis #define GITS_CBASER_MASK 0x1ffffffffff000ULL 1528f0a2ac19Skettenis #define GITS_CWRITER 0x0088 1529f0a2ac19Skettenis #define GITS_CREADR 0x0090 1530f0a2ac19Skettenis #define GITS_BASER(i) (0x0100 + ((i) * 8)) 1531f0a2ac19Skettenis #define GITS_BASER_VALID (1ULL << 63) 1532f0a2ac19Skettenis #define GITS_BASER_INDIRECT (1ULL << 62) 1533f0a2ac19Skettenis #define GITS_BASER_IC_NORM_NC (1ULL << 59) 1534f0a2ac19Skettenis #define GITS_BASER_TYPE_MASK (7ULL << 56) 1535f0a2ac19Skettenis #define GITS_BASER_TYPE_DEVICE (1ULL << 56) 153648352e7bSkettenis #define GITS_BASER_TYPE_COLL (4ULL << 56) 153748352e7bSkettenis #define GITS_BASER_TTE_SZ(x) (((x) >> 48) & 0x1f) 153891fec423Skettenis #define GITS_BASER_PGSZ_MASK (3ULL << 8) 153991fec423Skettenis #define GITS_BASER_PGSZ_4K (0ULL << 8) 154091fec423Skettenis #define GITS_BASER_PGSZ_16K (1ULL << 8) 154191fec423Skettenis #define GITS_BASER_PGSZ_64K (2ULL << 8) 15422679ff7fSpatrick #define GITS_BASER_SZ_MASK (0xffULL) 154391fec423Skettenis #define GITS_BASER_PA_MASK 0x7ffffffff000ULL 1544f0a2ac19Skettenis #define GITS_TRANSLATER 0x10040 1545f0a2ac19Skettenis 154691fec423Skettenis #define GITS_NUM_BASER 8 154791fec423Skettenis 1548f0a2ac19Skettenis struct gits_cmd { 1549f0a2ac19Skettenis uint8_t cmd; 1550f0a2ac19Skettenis uint32_t deviceid; 1551f0a2ac19Skettenis uint32_t eventid; 1552f0a2ac19Skettenis uint32_t intid; 1553f0a2ac19Skettenis uint64_t dw2; 1554f0a2ac19Skettenis uint64_t dw3; 1555f0a2ac19Skettenis }; 1556f0a2ac19Skettenis 1557f0a2ac19Skettenis #define GITS_CMD_VALID (1ULL << 63) 1558f0a2ac19Skettenis 1559f0a2ac19Skettenis /* ITS commands */ 1560f0a2ac19Skettenis #define SYNC 0x05 1561f0a2ac19Skettenis #define MAPD 0x08 1562f0a2ac19Skettenis #define MAPC 0x09 1563f0a2ac19Skettenis #define MAPTI 0x0a 1564c60fe750Spatrick #define INV 0x0c 1565c60fe750Spatrick #define INVALL 0x0d 1566f83e6fb8Spatrick #define DISCARD 0x0f 1567f0a2ac19Skettenis 1568f0a2ac19Skettenis #define GITS_CMDQ_SIZE (64 * 1024) 1569f0a2ac19Skettenis #define GITS_CMDQ_NENTRIES (GITS_CMDQ_SIZE / sizeof(struct gits_cmd)) 1570f0a2ac19Skettenis 1571f0a2ac19Skettenis struct agintc_msi_device { 1572f0a2ac19Skettenis LIST_ENTRY(agintc_msi_device) md_list; 1573f0a2ac19Skettenis 1574f0a2ac19Skettenis uint32_t md_deviceid; 157556d02c00Skettenis uint32_t md_events; 1576f0a2ac19Skettenis struct agintc_dmamem *md_itt; 1577f0a2ac19Skettenis }; 1578f0a2ac19Skettenis 1579f0a2ac19Skettenis int agintc_msi_match(struct device *, void *, void *); 1580f0a2ac19Skettenis void agintc_msi_attach(struct device *, struct device *, void *); 1581f0a2ac19Skettenis void *agintc_intr_establish_msi(void *, uint64_t *, uint64_t *, 1582789e88a4Spatrick int , struct cpu_info *, int (*)(void *), void *, char *); 1583f0a2ac19Skettenis void agintc_intr_disestablish_msi(void *); 1584452daaedSpatrick void agintc_intr_barrier_msi(void *); 1585f0a2ac19Skettenis 1586f0a2ac19Skettenis struct agintc_msi_softc { 1587f0a2ac19Skettenis struct device sc_dev; 1588f0a2ac19Skettenis bus_space_tag_t sc_iot; 1589f0a2ac19Skettenis bus_space_handle_t sc_ioh; 1590f0a2ac19Skettenis bus_dma_tag_t sc_dmat; 1591f0a2ac19Skettenis 15922896618dSkettenis bus_addr_t sc_msi_addr; 15932896618dSkettenis int sc_msi_delta; 15942896618dSkettenis 1595f0a2ac19Skettenis struct agintc_dmamem *sc_cmdq; 1596f0a2ac19Skettenis uint16_t sc_cmdidx; 159791fec423Skettenis 159891fec423Skettenis int sc_devbits; 15992679ff7fSpatrick uint32_t sc_deviceid_max; 1600f0a2ac19Skettenis struct agintc_dmamem *sc_dtt; 160191fec423Skettenis size_t sc_dtt_pgsz; 160291fec423Skettenis uint8_t sc_dte_sz; 16037afc621aSpatrick int sc_dtt_indirect; 160448352e7bSkettenis int sc_cidbits; 160548352e7bSkettenis struct agintc_dmamem *sc_ctt; 160648352e7bSkettenis size_t sc_ctt_pgsz; 160748352e7bSkettenis uint8_t sc_cte_sz; 1608f0a2ac19Skettenis uint8_t sc_ite_sz; 1609f0a2ac19Skettenis 1610f0a2ac19Skettenis LIST_HEAD(, agintc_msi_device) sc_msi_devices; 1611f0a2ac19Skettenis 1612f0a2ac19Skettenis struct interrupt_controller sc_ic; 1613f0a2ac19Skettenis }; 1614f0a2ac19Skettenis 16159fdf0c62Smpi const struct cfattach agintcmsi_ca = { 1616f0a2ac19Skettenis sizeof (struct agintc_msi_softc), agintc_msi_match, agintc_msi_attach 1617f0a2ac19Skettenis }; 1618f0a2ac19Skettenis 1619f0a2ac19Skettenis struct cfdriver agintcmsi_cd = { 1620f0a2ac19Skettenis NULL, "agintcmsi", DV_DULL 1621f0a2ac19Skettenis }; 1622f0a2ac19Skettenis 1623f0a2ac19Skettenis void agintc_msi_send_cmd(struct agintc_msi_softc *, struct gits_cmd *); 1624f0a2ac19Skettenis void agintc_msi_wait_cmd(struct agintc_msi_softc *); 1625f0a2ac19Skettenis 162618d3f3c5Skettenis #define CPU_IMPL(midr) (((midr) >> 24) & 0xff) 162718d3f3c5Skettenis #define CPU_PART(midr) (((midr) >> 4) & 0xfff) 162818d3f3c5Skettenis 162918d3f3c5Skettenis #define CPU_IMPL_QCOM 0x51 163018d3f3c5Skettenis #define CPU_PART_ORYON 0x001 163118d3f3c5Skettenis 1632f0a2ac19Skettenis int 1633f0a2ac19Skettenis agintc_msi_match(struct device *parent, void *cfdata, void *aux) 1634f0a2ac19Skettenis { 1635f0a2ac19Skettenis struct fdt_attach_args *faa = aux; 1636f0a2ac19Skettenis 163718d3f3c5Skettenis /* 163818d3f3c5Skettenis * XXX For some reason MSIs don't work on Qualcomm X1E SoCs in 163918d3f3c5Skettenis * ACPI mode. So skip attaching the ITS in that case. MSIs 164018d3f3c5Skettenis * work fine when booting with a DTB. 164118d3f3c5Skettenis */ 164218d3f3c5Skettenis if (OF_is_compatible(OF_peer(0), "openbsd,acpi") && 164318d3f3c5Skettenis CPU_IMPL(curcpu()->ci_midr) == CPU_IMPL_QCOM && 164418d3f3c5Skettenis CPU_PART(curcpu()->ci_midr) == CPU_PART_ORYON) 164518d3f3c5Skettenis return 0; 164618d3f3c5Skettenis 1647f0a2ac19Skettenis return OF_is_compatible(faa->fa_node, "arm,gic-v3-its"); 1648f0a2ac19Skettenis } 1649f0a2ac19Skettenis 1650f0a2ac19Skettenis void 1651f0a2ac19Skettenis agintc_msi_attach(struct device *parent, struct device *self, void *aux) 1652f0a2ac19Skettenis { 1653f0a2ac19Skettenis struct agintc_msi_softc *sc = (struct agintc_msi_softc *)self; 1654f0a2ac19Skettenis struct fdt_attach_args *faa = aux; 1655f0a2ac19Skettenis struct gits_cmd cmd; 16562896618dSkettenis uint32_t pre_its[2]; 1657f0a2ac19Skettenis uint64_t typer; 16587c59a772Spatrick int i, hwcpu; 1659f0a2ac19Skettenis 1660f0a2ac19Skettenis if (faa->fa_nreg < 1) { 1661f0a2ac19Skettenis printf(": no registers\n"); 1662f0a2ac19Skettenis return; 1663f0a2ac19Skettenis } 1664f0a2ac19Skettenis 1665f0a2ac19Skettenis sc->sc_iot = faa->fa_iot; 1666f0a2ac19Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 1667f0a2ac19Skettenis faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 1668f0a2ac19Skettenis printf(": can't map registers\n"); 1669f0a2ac19Skettenis return; 1670f0a2ac19Skettenis } 1671f0a2ac19Skettenis sc->sc_dmat = faa->fa_dmat; 1672f0a2ac19Skettenis 16732896618dSkettenis sc->sc_msi_addr = faa->fa_reg[0].addr + GITS_TRANSLATER; 16742896618dSkettenis if (OF_getpropintarray(faa->fa_node, "socionext,synquacer-pre-its", 16752896618dSkettenis pre_its, sizeof(pre_its)) == sizeof(pre_its)) { 16762896618dSkettenis sc->sc_msi_addr = pre_its[0]; 16772896618dSkettenis sc->sc_msi_delta = 4; 16782896618dSkettenis } 16792896618dSkettenis 1680f0a2ac19Skettenis typer = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_TYPER); 168148352e7bSkettenis if ((typer & GITS_TYPER_PHYS) == 0 || typer & GITS_TYPER_PTA) { 1682f0a2ac19Skettenis printf(": unsupported type 0x%016llx\n", typer); 1683f0a2ac19Skettenis goto unmap; 1684f0a2ac19Skettenis } 1685f0a2ac19Skettenis sc->sc_ite_sz = GITS_TYPER_ITE_SZ(typer) + 1; 168691fec423Skettenis sc->sc_devbits = GITS_TYPER_DEVBITS(typer) + 1; 168748352e7bSkettenis if (typer & GITS_TYPER_CIL) 168848352e7bSkettenis sc->sc_cidbits = GITS_TYPER_CIDBITS(typer) + 1; 168948352e7bSkettenis else 169048352e7bSkettenis sc->sc_cidbits = 16; 1691f0a2ac19Skettenis 1692f0a2ac19Skettenis /* Set up command queue. */ 1693f0a2ac19Skettenis sc->sc_cmdq = agintc_dmamem_alloc(sc->sc_dmat, 1694f0a2ac19Skettenis GITS_CMDQ_SIZE, GITS_CMDQ_SIZE); 1695f0a2ac19Skettenis if (sc->sc_cmdq == NULL) { 1696f0a2ac19Skettenis printf(": can't alloc command queue\n"); 1697f0a2ac19Skettenis goto unmap; 1698f0a2ac19Skettenis } 1699f0a2ac19Skettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_CBASER, 1700f0a2ac19Skettenis AGINTC_DMA_DVA(sc->sc_cmdq) | GITS_CBASER_IC_NORM_NC | 1701f0a2ac19Skettenis (GITS_CMDQ_SIZE / PAGE_SIZE) - 1 | GITS_CBASER_VALID); 1702f0a2ac19Skettenis 1703f0a2ac19Skettenis /* Set up device translation table. */ 170491fec423Skettenis for (i = 0; i < GITS_NUM_BASER; i++) { 1705f0a2ac19Skettenis uint64_t baser; 170691fec423Skettenis paddr_t dtt_pa; 170791fec423Skettenis size_t size; 1708f0a2ac19Skettenis 1709f0a2ac19Skettenis baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i)); 1710f0a2ac19Skettenis if ((baser & GITS_BASER_TYPE_MASK) != GITS_BASER_TYPE_DEVICE) 1711f0a2ac19Skettenis continue; 1712f0a2ac19Skettenis 171391fec423Skettenis /* Determine the maximum supported page size. */ 171491fec423Skettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 171591fec423Skettenis (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_64K); 171691fec423Skettenis baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i)); 171791fec423Skettenis if ((baser & GITS_BASER_PGSZ_MASK) == GITS_BASER_PGSZ_64K) 171848352e7bSkettenis goto dfound; 171991fec423Skettenis 172091fec423Skettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 172191fec423Skettenis (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_16K); 172291fec423Skettenis baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i)); 172391fec423Skettenis if ((baser & GITS_BASER_PGSZ_MASK) == GITS_BASER_PGSZ_16K) 172448352e7bSkettenis goto dfound; 172591fec423Skettenis 172691fec423Skettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 172791fec423Skettenis (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_4K); 172891fec423Skettenis baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i)); 172991fec423Skettenis 173048352e7bSkettenis dfound: 173191fec423Skettenis switch (baser & GITS_BASER_PGSZ_MASK) { 173291fec423Skettenis case GITS_BASER_PGSZ_4K: 173391fec423Skettenis sc->sc_dtt_pgsz = PAGE_SIZE; 173491fec423Skettenis break; 173591fec423Skettenis case GITS_BASER_PGSZ_16K: 173691fec423Skettenis sc->sc_dtt_pgsz = 4 * PAGE_SIZE; 173791fec423Skettenis break; 173891fec423Skettenis case GITS_BASER_PGSZ_64K: 173991fec423Skettenis sc->sc_dtt_pgsz = 16 * PAGE_SIZE; 174091fec423Skettenis break; 174191fec423Skettenis } 174291fec423Skettenis 174391fec423Skettenis /* Calculate table size. */ 174448352e7bSkettenis sc->sc_dte_sz = GITS_BASER_TTE_SZ(baser) + 1; 174591fec423Skettenis size = (1ULL << sc->sc_devbits) * sc->sc_dte_sz; 174691fec423Skettenis size = roundup(size, sc->sc_dtt_pgsz); 174791fec423Skettenis 17487afc621aSpatrick /* Might make sense to go indirect */ 17497afc621aSpatrick if (size > 2 * sc->sc_dtt_pgsz) { 17507afc621aSpatrick bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 17517afc621aSpatrick baser | GITS_BASER_INDIRECT); 17527afc621aSpatrick if (bus_space_read_8(sc->sc_iot, sc->sc_ioh, 17537afc621aSpatrick GITS_BASER(i)) & GITS_BASER_INDIRECT) 17547afc621aSpatrick sc->sc_dtt_indirect = 1; 17557afc621aSpatrick } 17567afc621aSpatrick if (sc->sc_dtt_indirect) { 17577afc621aSpatrick size = (1ULL << sc->sc_devbits); 17587afc621aSpatrick size /= (sc->sc_dtt_pgsz / sc->sc_dte_sz); 17597afc621aSpatrick size *= sizeof(uint64_t); 17607afc621aSpatrick size = roundup(size, sc->sc_dtt_pgsz); 1761df30041bSpatrick } 1762df30041bSpatrick 17632679ff7fSpatrick /* Clamp down to maximum configurable num pages */ 17642679ff7fSpatrick if (size / sc->sc_dtt_pgsz > GITS_BASER_SZ_MASK + 1) 17652679ff7fSpatrick size = (GITS_BASER_SZ_MASK + 1) * sc->sc_dtt_pgsz; 17662679ff7fSpatrick 17672679ff7fSpatrick /* Calculate max deviceid based off configured size */ 17682679ff7fSpatrick sc->sc_deviceid_max = (size / sc->sc_dte_sz) - 1; 17697afc621aSpatrick if (sc->sc_dtt_indirect) 17707afc621aSpatrick sc->sc_deviceid_max = ((size / sizeof(uint64_t)) * 17717afc621aSpatrick (sc->sc_dtt_pgsz / sc->sc_dte_sz)) - 1; 17722679ff7fSpatrick 177391fec423Skettenis /* Allocate table. */ 1774f0a2ac19Skettenis sc->sc_dtt = agintc_dmamem_alloc(sc->sc_dmat, 177591fec423Skettenis size, sc->sc_dtt_pgsz); 1776f0a2ac19Skettenis if (sc->sc_dtt == NULL) { 1777f0a2ac19Skettenis printf(": can't alloc translation table\n"); 1778f0a2ac19Skettenis goto unmap; 1779f0a2ac19Skettenis } 178091fec423Skettenis 178191fec423Skettenis /* Configure table. */ 178291fec423Skettenis dtt_pa = AGINTC_DMA_DVA(sc->sc_dtt); 178391fec423Skettenis KASSERT((dtt_pa & GITS_BASER_PA_MASK) == dtt_pa); 17842896618dSkettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 178591fec423Skettenis GITS_BASER_IC_NORM_NC | baser & GITS_BASER_PGSZ_MASK | 17867afc621aSpatrick dtt_pa | (size / sc->sc_dtt_pgsz) - 1 | 17877afc621aSpatrick (sc->sc_dtt_indirect ? GITS_BASER_INDIRECT : 0) | 17887afc621aSpatrick GITS_BASER_VALID); 1789f0a2ac19Skettenis } 1790f0a2ac19Skettenis 179148352e7bSkettenis /* Set up collection translation table. */ 179248352e7bSkettenis for (i = 0; i < GITS_NUM_BASER; i++) { 179348352e7bSkettenis uint64_t baser; 179448352e7bSkettenis paddr_t ctt_pa; 179548352e7bSkettenis size_t size; 179648352e7bSkettenis 179748352e7bSkettenis baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i)); 179848352e7bSkettenis if ((baser & GITS_BASER_TYPE_MASK) != GITS_BASER_TYPE_COLL) 179948352e7bSkettenis continue; 180048352e7bSkettenis 180148352e7bSkettenis /* Determine the maximum supported page size. */ 180248352e7bSkettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 180348352e7bSkettenis (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_64K); 180448352e7bSkettenis baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i)); 180548352e7bSkettenis if ((baser & GITS_BASER_PGSZ_MASK) == GITS_BASER_PGSZ_64K) 180648352e7bSkettenis goto cfound; 180748352e7bSkettenis 180848352e7bSkettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 180948352e7bSkettenis (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_16K); 181048352e7bSkettenis baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i)); 181148352e7bSkettenis if ((baser & GITS_BASER_PGSZ_MASK) == GITS_BASER_PGSZ_16K) 181248352e7bSkettenis goto cfound; 181348352e7bSkettenis 181448352e7bSkettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 181548352e7bSkettenis (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_4K); 181648352e7bSkettenis baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i)); 181748352e7bSkettenis 181848352e7bSkettenis cfound: 181948352e7bSkettenis switch (baser & GITS_BASER_PGSZ_MASK) { 182048352e7bSkettenis case GITS_BASER_PGSZ_4K: 182148352e7bSkettenis sc->sc_ctt_pgsz = PAGE_SIZE; 182248352e7bSkettenis break; 182348352e7bSkettenis case GITS_BASER_PGSZ_16K: 182448352e7bSkettenis sc->sc_ctt_pgsz = 4 * PAGE_SIZE; 182548352e7bSkettenis break; 182648352e7bSkettenis case GITS_BASER_PGSZ_64K: 182748352e7bSkettenis sc->sc_ctt_pgsz = 16 * PAGE_SIZE; 182848352e7bSkettenis break; 182948352e7bSkettenis } 183048352e7bSkettenis 183148352e7bSkettenis /* Calculate table size. */ 183248352e7bSkettenis sc->sc_cte_sz = GITS_BASER_TTE_SZ(baser) + 1; 183348352e7bSkettenis size = (1ULL << sc->sc_cidbits) * sc->sc_cte_sz; 183448352e7bSkettenis size = roundup(size, sc->sc_ctt_pgsz); 183548352e7bSkettenis 183648352e7bSkettenis /* Allocate table. */ 183748352e7bSkettenis sc->sc_ctt = agintc_dmamem_alloc(sc->sc_dmat, 183848352e7bSkettenis size, sc->sc_ctt_pgsz); 183948352e7bSkettenis if (sc->sc_ctt == NULL) { 184048352e7bSkettenis printf(": can't alloc translation table\n"); 184148352e7bSkettenis goto unmap; 184248352e7bSkettenis } 184348352e7bSkettenis 184448352e7bSkettenis /* Configure table. */ 184548352e7bSkettenis ctt_pa = AGINTC_DMA_DVA(sc->sc_ctt); 184648352e7bSkettenis KASSERT((ctt_pa & GITS_BASER_PA_MASK) == ctt_pa); 184748352e7bSkettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i), 184848352e7bSkettenis GITS_BASER_IC_NORM_NC | baser & GITS_BASER_PGSZ_MASK | 184948352e7bSkettenis ctt_pa | (size / sc->sc_ctt_pgsz) - 1 | GITS_BASER_VALID); 185048352e7bSkettenis } 185148352e7bSkettenis 1852f0a2ac19Skettenis /* Enable ITS. */ 1853f0a2ac19Skettenis bus_space_write_4(sc->sc_iot, sc->sc_ioh, GITS_CTLR, 1854f0a2ac19Skettenis GITS_CTLR_ENABLED); 1855f0a2ac19Skettenis 1856f0a2ac19Skettenis LIST_INIT(&sc->sc_msi_devices); 1857f0a2ac19Skettenis 18587c59a772Spatrick /* Create one collection per core. */ 18597c59a772Spatrick KASSERT(ncpus <= agintc_sc->sc_num_redist); 18607c59a772Spatrick for (i = 0; i < ncpus; i++) { 18617c59a772Spatrick hwcpu = agintc_sc->sc_cpuremap[i]; 1862f0a2ac19Skettenis memset(&cmd, 0, sizeof(cmd)); 1863f0a2ac19Skettenis cmd.cmd = MAPC; 18647c59a772Spatrick cmd.dw2 = GITS_CMD_VALID | 18657c59a772Spatrick (agintc_sc->sc_processor[hwcpu] << 16) | i; 1866f0a2ac19Skettenis agintc_msi_send_cmd(sc, &cmd); 1867f0a2ac19Skettenis agintc_msi_wait_cmd(sc); 18687c59a772Spatrick } 1869f0a2ac19Skettenis 1870f0a2ac19Skettenis printf("\n"); 1871f0a2ac19Skettenis 1872f0a2ac19Skettenis sc->sc_ic.ic_node = faa->fa_node; 1873f0a2ac19Skettenis sc->sc_ic.ic_cookie = sc; 1874f0a2ac19Skettenis sc->sc_ic.ic_establish_msi = agintc_intr_establish_msi; 1875f0a2ac19Skettenis sc->sc_ic.ic_disestablish = agintc_intr_disestablish_msi; 1876452daaedSpatrick sc->sc_ic.ic_barrier = agintc_intr_barrier_msi; 1877550856c9Sjmatthew sc->sc_ic.ic_gic_its_id = OF_getpropint(faa->fa_node, 1878550856c9Sjmatthew "openbsd,gic-its-id", 0); 1879f0a2ac19Skettenis arm_intr_register_fdt(&sc->sc_ic); 1880f0a2ac19Skettenis return; 1881f0a2ac19Skettenis 1882f0a2ac19Skettenis unmap: 1883f0a2ac19Skettenis if (sc->sc_dtt) 1884f0a2ac19Skettenis agintc_dmamem_free(sc->sc_dmat, sc->sc_dtt); 1885f0a2ac19Skettenis if (sc->sc_cmdq) 1886f0a2ac19Skettenis agintc_dmamem_free(sc->sc_dmat, sc->sc_cmdq); 1887f0a2ac19Skettenis 1888f0a2ac19Skettenis bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); 1889f0a2ac19Skettenis } 1890f0a2ac19Skettenis 1891f0a2ac19Skettenis void 1892f0a2ac19Skettenis agintc_msi_send_cmd(struct agintc_msi_softc *sc, struct gits_cmd *cmd) 1893f0a2ac19Skettenis { 1894f0a2ac19Skettenis struct gits_cmd *queue = AGINTC_DMA_KVA(sc->sc_cmdq); 1895f0a2ac19Skettenis 18962896618dSkettenis memcpy(&queue[sc->sc_cmdidx], cmd, sizeof(*cmd)); 18972896618dSkettenis 18982896618dSkettenis /* Make globally visible. */ 18992896618dSkettenis cpu_dcache_wb_range((vaddr_t)&queue[sc->sc_cmdidx], sizeof(*cmd)); 1900f0a2ac19Skettenis __asm volatile("dsb sy"); 1901f0a2ac19Skettenis 19022896618dSkettenis sc->sc_cmdidx++; 1903f0a2ac19Skettenis sc->sc_cmdidx %= GITS_CMDQ_NENTRIES; 1904f0a2ac19Skettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_CWRITER, 1905f0a2ac19Skettenis sc->sc_cmdidx * sizeof(*cmd)); 1906f0a2ac19Skettenis } 1907f0a2ac19Skettenis 1908f0a2ac19Skettenis void 1909f0a2ac19Skettenis agintc_msi_wait_cmd(struct agintc_msi_softc *sc) 1910f0a2ac19Skettenis { 1911f0a2ac19Skettenis uint64_t creadr; 1912f0a2ac19Skettenis int timo; 1913f0a2ac19Skettenis 1914f0a2ac19Skettenis for (timo = 1000; timo > 0; timo--) { 1915f0a2ac19Skettenis creadr = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_CREADR); 1916f0a2ac19Skettenis if (creadr == sc->sc_cmdidx * sizeof(struct gits_cmd)) 1917f0a2ac19Skettenis break; 1918f0a2ac19Skettenis delay(1); 1919f0a2ac19Skettenis } 1920f0a2ac19Skettenis if (timo == 0) 1921f0a2ac19Skettenis printf("%s: command queue timeout\n", sc->sc_dev.dv_xname); 1922f0a2ac19Skettenis } 1923f0a2ac19Skettenis 19247afc621aSpatrick int 19257afc621aSpatrick agintc_msi_create_device_table(struct agintc_msi_softc *sc, uint32_t deviceid) 19267afc621aSpatrick { 19277afc621aSpatrick uint64_t *table = AGINTC_DMA_KVA(sc->sc_dtt); 19287afc621aSpatrick uint32_t idx = deviceid / (sc->sc_dtt_pgsz / sc->sc_dte_sz); 19297afc621aSpatrick struct agintc_dmamem *dtt; 19307afc621aSpatrick paddr_t dtt_pa; 19317afc621aSpatrick 19327afc621aSpatrick /* Out of bounds */ 19337afc621aSpatrick if (deviceid > sc->sc_deviceid_max) 19347afc621aSpatrick return ENXIO; 19357afc621aSpatrick 19367afc621aSpatrick /* No need to adjust */ 19377afc621aSpatrick if (!sc->sc_dtt_indirect) 19387afc621aSpatrick return 0; 19397afc621aSpatrick 19407afc621aSpatrick /* Table already allocated */ 19417afc621aSpatrick if (table[idx]) 19427afc621aSpatrick return 0; 19437afc621aSpatrick 19447afc621aSpatrick /* FIXME: leaks */ 19457afc621aSpatrick dtt = agintc_dmamem_alloc(sc->sc_dmat, 19467afc621aSpatrick sc->sc_dtt_pgsz, sc->sc_dtt_pgsz); 19477afc621aSpatrick if (dtt == NULL) 19487afc621aSpatrick return ENOMEM; 19497afc621aSpatrick 19507afc621aSpatrick dtt_pa = AGINTC_DMA_DVA(dtt); 19517afc621aSpatrick KASSERT((dtt_pa & GITS_BASER_PA_MASK) == dtt_pa); 19527afc621aSpatrick table[idx] = dtt_pa | GITS_BASER_VALID; 19537afc621aSpatrick cpu_dcache_wb_range((vaddr_t)&table[idx], sizeof(table[idx])); 19547afc621aSpatrick __asm volatile("dsb sy"); 19557afc621aSpatrick return 0; 19567afc621aSpatrick } 19577afc621aSpatrick 1958f0a2ac19Skettenis struct agintc_msi_device * 1959f0a2ac19Skettenis agintc_msi_create_device(struct agintc_msi_softc *sc, uint32_t deviceid) 1960f0a2ac19Skettenis { 1961f0a2ac19Skettenis struct agintc_msi_device *md; 1962f0a2ac19Skettenis struct gits_cmd cmd; 1963f0a2ac19Skettenis 19642679ff7fSpatrick if (deviceid > sc->sc_deviceid_max) 19652679ff7fSpatrick return NULL; 19662679ff7fSpatrick 19677afc621aSpatrick if (agintc_msi_create_device_table(sc, deviceid) != 0) 19687afc621aSpatrick return NULL; 19697afc621aSpatrick 1970f0a2ac19Skettenis md = malloc(sizeof(*md), M_DEVBUF, M_ZERO | M_WAITOK); 1971f0a2ac19Skettenis md->md_deviceid = deviceid; 1972f0a2ac19Skettenis md->md_itt = agintc_dmamem_alloc(sc->sc_dmat, 1973f0a2ac19Skettenis 32 * sc->sc_ite_sz, PAGE_SIZE); 1974f0a2ac19Skettenis LIST_INSERT_HEAD(&sc->sc_msi_devices, md, md_list); 1975f0a2ac19Skettenis 1976f0a2ac19Skettenis memset(&cmd, 0, sizeof(cmd)); 1977f0a2ac19Skettenis cmd.cmd = MAPD; 1978f0a2ac19Skettenis cmd.deviceid = deviceid; 1979f0a2ac19Skettenis cmd.eventid = 4; /* size */ 1980f0a2ac19Skettenis cmd.dw2 = AGINTC_DMA_DVA(md->md_itt) | GITS_CMD_VALID; 1981f0a2ac19Skettenis agintc_msi_send_cmd(sc, &cmd); 1982f0a2ac19Skettenis agintc_msi_wait_cmd(sc); 1983f0a2ac19Skettenis 1984f0a2ac19Skettenis return md; 1985f0a2ac19Skettenis } 1986f0a2ac19Skettenis 1987f0a2ac19Skettenis struct agintc_msi_device * 1988f0a2ac19Skettenis agintc_msi_find_device(struct agintc_msi_softc *sc, uint32_t deviceid) 1989f0a2ac19Skettenis { 1990f0a2ac19Skettenis struct agintc_msi_device *md; 1991f0a2ac19Skettenis 1992f0a2ac19Skettenis LIST_FOREACH(md, &sc->sc_msi_devices, md_list) { 1993f0a2ac19Skettenis if (md->md_deviceid == deviceid) 1994f0a2ac19Skettenis return md; 1995f0a2ac19Skettenis } 1996f0a2ac19Skettenis 1997f0a2ac19Skettenis return agintc_msi_create_device(sc, deviceid); 1998f0a2ac19Skettenis } 1999f0a2ac19Skettenis 2000c60fe750Spatrick void 2001f83e6fb8Spatrick agintc_msi_discard(struct agintc_lpi_info *li) 2002c60fe750Spatrick { 2003c60fe750Spatrick struct agintc_msi_softc *sc; 2004f83e6fb8Spatrick struct cpu_info *ci; 2005c60fe750Spatrick struct gits_cmd cmd; 2006f83e6fb8Spatrick int hwcpu; 2007c60fe750Spatrick 2008f83e6fb8Spatrick sc = li->li_msic; 2009f83e6fb8Spatrick ci = li->li_ci; 2010f83e6fb8Spatrick hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid]; 2011f83e6fb8Spatrick 2012c60fe750Spatrick memset(&cmd, 0, sizeof(cmd)); 2013f83e6fb8Spatrick cmd.cmd = DISCARD; 2014f83e6fb8Spatrick cmd.deviceid = li->li_deviceid; 2015f83e6fb8Spatrick cmd.eventid = li->li_eventid; 2016f83e6fb8Spatrick agintc_msi_send_cmd(sc, &cmd); 2017f83e6fb8Spatrick 2018f83e6fb8Spatrick memset(&cmd, 0, sizeof(cmd)); 2019f83e6fb8Spatrick cmd.cmd = SYNC; 2020f83e6fb8Spatrick cmd.dw2 = agintc_sc->sc_processor[hwcpu] << 16; 2021c60fe750Spatrick agintc_msi_send_cmd(sc, &cmd); 2022c60fe750Spatrick agintc_msi_wait_cmd(sc); 2023c60fe750Spatrick } 2024f83e6fb8Spatrick 2025f83e6fb8Spatrick void 2026f83e6fb8Spatrick agintc_msi_inv(struct agintc_lpi_info *li) 2027f83e6fb8Spatrick { 2028f83e6fb8Spatrick struct agintc_msi_softc *sc; 2029f83e6fb8Spatrick struct cpu_info *ci; 2030f83e6fb8Spatrick struct gits_cmd cmd; 2031f83e6fb8Spatrick int hwcpu; 2032f83e6fb8Spatrick 2033f83e6fb8Spatrick sc = li->li_msic; 2034f83e6fb8Spatrick ci = li->li_ci; 2035f83e6fb8Spatrick hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid]; 2036f83e6fb8Spatrick 2037f83e6fb8Spatrick memset(&cmd, 0, sizeof(cmd)); 2038f83e6fb8Spatrick cmd.cmd = INV; 2039f83e6fb8Spatrick cmd.deviceid = li->li_deviceid; 2040f83e6fb8Spatrick cmd.eventid = li->li_eventid; 2041f83e6fb8Spatrick agintc_msi_send_cmd(sc, &cmd); 2042f83e6fb8Spatrick 2043f83e6fb8Spatrick memset(&cmd, 0, sizeof(cmd)); 2044f83e6fb8Spatrick cmd.cmd = SYNC; 2045f83e6fb8Spatrick cmd.dw2 = agintc_sc->sc_processor[hwcpu] << 16; 2046f83e6fb8Spatrick agintc_msi_send_cmd(sc, &cmd); 2047f83e6fb8Spatrick agintc_msi_wait_cmd(sc); 2048c60fe750Spatrick } 2049c60fe750Spatrick 2050f0a2ac19Skettenis void * 2051f0a2ac19Skettenis agintc_intr_establish_msi(void *self, uint64_t *addr, uint64_t *data, 2052789e88a4Spatrick int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 2053f0a2ac19Skettenis { 2054f0a2ac19Skettenis struct agintc_msi_softc *sc = (struct agintc_msi_softc *)self; 2055f0a2ac19Skettenis struct agintc_msi_device *md; 2056f0a2ac19Skettenis struct gits_cmd cmd; 2057f0a2ac19Skettenis uint32_t deviceid = *data; 2058f0a2ac19Skettenis uint32_t eventid; 20597c59a772Spatrick int i, hwcpu; 2060f0a2ac19Skettenis 2061789e88a4Spatrick if (ci == NULL) 2062789e88a4Spatrick ci = &cpu_info_primary; 20637c59a772Spatrick hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid]; 2064789e88a4Spatrick 2065f0a2ac19Skettenis md = agintc_msi_find_device(sc, deviceid); 2066f0a2ac19Skettenis if (md == NULL) 2067f0a2ac19Skettenis return NULL; 2068f0a2ac19Skettenis 206956d02c00Skettenis eventid = *addr; 207056d02c00Skettenis if (eventid > 0 && (md->md_events & (1U << eventid))) 207156d02c00Skettenis return NULL; 207256d02c00Skettenis for (; eventid < 32; eventid++) { 207356d02c00Skettenis if ((md->md_events & (1U << eventid)) == 0) { 207456d02c00Skettenis md->md_events |= (1U << eventid); 207556d02c00Skettenis break; 207656d02c00Skettenis } 207756d02c00Skettenis } 2078f0a2ac19Skettenis if (eventid >= 32) 2079f0a2ac19Skettenis return NULL; 2080f0a2ac19Skettenis 2081f83e6fb8Spatrick for (i = 0; i < agintc_sc->sc_nlpi; i++) { 2082f83e6fb8Spatrick if (agintc_sc->sc_lpi[i] != NULL) 2083f0a2ac19Skettenis continue; 2084f0a2ac19Skettenis 2085f83e6fb8Spatrick agintc_sc->sc_lpi[i] = malloc(sizeof(struct agintc_lpi_info), 2086f83e6fb8Spatrick M_DEVBUF, M_WAITOK | M_ZERO); 2087f83e6fb8Spatrick agintc_sc->sc_lpi[i]->li_msic = sc; 2088f83e6fb8Spatrick agintc_sc->sc_lpi[i]->li_ci = ci; 2089f83e6fb8Spatrick agintc_sc->sc_lpi[i]->li_deviceid = deviceid; 2090f83e6fb8Spatrick agintc_sc->sc_lpi[i]->li_eventid = eventid; 2091f83e6fb8Spatrick agintc_sc->sc_lpi[i]->li_ih = 2092f83e6fb8Spatrick agintc_intr_establish(LPI_BASE + i, 20936214007cSpatrick IST_EDGE_RISING, level, ci, func, arg, name); 2094f83e6fb8Spatrick if (agintc_sc->sc_lpi[i]->li_ih == NULL) { 2095f83e6fb8Spatrick free(agintc_sc->sc_lpi[i], M_DEVBUF, 2096f83e6fb8Spatrick sizeof(struct agintc_lpi_info)); 2097f83e6fb8Spatrick agintc_sc->sc_lpi[i] = NULL; 2098f0a2ac19Skettenis return NULL; 2099f83e6fb8Spatrick } 2100f0a2ac19Skettenis 2101f0a2ac19Skettenis memset(&cmd, 0, sizeof(cmd)); 2102f0a2ac19Skettenis cmd.cmd = MAPTI; 2103f0a2ac19Skettenis cmd.deviceid = deviceid; 2104f0a2ac19Skettenis cmd.eventid = eventid; 2105f0a2ac19Skettenis cmd.intid = LPI_BASE + i; 21067c59a772Spatrick cmd.dw2 = ci->ci_cpuid; 2107f0a2ac19Skettenis agintc_msi_send_cmd(sc, &cmd); 2108f0a2ac19Skettenis 2109f0a2ac19Skettenis memset(&cmd, 0, sizeof(cmd)); 2110f0a2ac19Skettenis cmd.cmd = SYNC; 21117c59a772Spatrick cmd.dw2 = agintc_sc->sc_processor[hwcpu] << 16; 2112f0a2ac19Skettenis agintc_msi_send_cmd(sc, &cmd); 2113f0a2ac19Skettenis agintc_msi_wait_cmd(sc); 2114f0a2ac19Skettenis 21152896618dSkettenis *addr = sc->sc_msi_addr + deviceid * sc->sc_msi_delta; 2116f0a2ac19Skettenis *data = eventid; 2117f83e6fb8Spatrick return &agintc_sc->sc_lpi[i]; 2118f0a2ac19Skettenis } 2119f0a2ac19Skettenis 2120f0a2ac19Skettenis return NULL; 2121f0a2ac19Skettenis } 2122f0a2ac19Skettenis 2123f0a2ac19Skettenis void 2124f0a2ac19Skettenis agintc_intr_disestablish_msi(void *cookie) 2125f0a2ac19Skettenis { 2126f83e6fb8Spatrick struct agintc_lpi_info *li = *(void **)cookie; 21270bf07e22Skettenis 2128f83e6fb8Spatrick agintc_intr_disestablish(li->li_ih); 2129f83e6fb8Spatrick agintc_msi_discard(li); 2130f83e6fb8Spatrick agintc_msi_inv(li); 2131f83e6fb8Spatrick 2132f83e6fb8Spatrick free(li, M_DEVBUF, sizeof(*li)); 2133f83e6fb8Spatrick *(void **)cookie = NULL; 2134f0a2ac19Skettenis } 2135f0a2ac19Skettenis 2136452daaedSpatrick void 2137452daaedSpatrick agintc_intr_barrier_msi(void *cookie) 2138452daaedSpatrick { 2139f83e6fb8Spatrick struct agintc_lpi_info *li = *(void **)cookie; 2140f83e6fb8Spatrick 2141f83e6fb8Spatrick agintc_intr_barrier(li->li_ih); 2142452daaedSpatrick } 2143452daaedSpatrick 2144f0a2ac19Skettenis struct agintc_dmamem * 2145f0a2ac19Skettenis agintc_dmamem_alloc(bus_dma_tag_t dmat, bus_size_t size, bus_size_t align) 2146f0a2ac19Skettenis { 2147f0a2ac19Skettenis struct agintc_dmamem *adm; 2148f0a2ac19Skettenis int nsegs; 2149f0a2ac19Skettenis 2150f0a2ac19Skettenis adm = malloc(sizeof(*adm), M_DEVBUF, M_WAITOK | M_ZERO); 2151f0a2ac19Skettenis adm->adm_size = size; 2152f0a2ac19Skettenis 2153f0a2ac19Skettenis if (bus_dmamap_create(dmat, size, 1, size, 0, 2154f0a2ac19Skettenis BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &adm->adm_map) != 0) 2155f0a2ac19Skettenis goto admfree; 2156f0a2ac19Skettenis 2157f0a2ac19Skettenis if (bus_dmamem_alloc(dmat, size, align, 0, &adm->adm_seg, 1, 2158f0a2ac19Skettenis &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO) != 0) 2159f0a2ac19Skettenis goto destroy; 2160f0a2ac19Skettenis 2161f0a2ac19Skettenis if (bus_dmamem_map(dmat, &adm->adm_seg, nsegs, size, 2162f0a2ac19Skettenis &adm->adm_kva, BUS_DMA_WAITOK | BUS_DMA_NOCACHE) != 0) 2163f0a2ac19Skettenis goto free; 2164f0a2ac19Skettenis 2165f0a2ac19Skettenis if (bus_dmamap_load_raw(dmat, adm->adm_map, &adm->adm_seg, 2166f0a2ac19Skettenis nsegs, size, BUS_DMA_WAITOK) != 0) 2167f0a2ac19Skettenis goto unmap; 2168f0a2ac19Skettenis 21692896618dSkettenis /* Make globally visible. */ 21702896618dSkettenis cpu_dcache_wb_range((vaddr_t)adm->adm_kva, size); 21712896618dSkettenis __asm volatile("dsb sy"); 2172f0a2ac19Skettenis return adm; 2173f0a2ac19Skettenis 2174f0a2ac19Skettenis unmap: 2175f0a2ac19Skettenis bus_dmamem_unmap(dmat, adm->adm_kva, size); 2176f0a2ac19Skettenis free: 2177f0a2ac19Skettenis bus_dmamem_free(dmat, &adm->adm_seg, 1); 2178f0a2ac19Skettenis destroy: 2179f0a2ac19Skettenis bus_dmamap_destroy(dmat, adm->adm_map); 2180f0a2ac19Skettenis admfree: 2181f0a2ac19Skettenis free(adm, M_DEVBUF, sizeof(*adm)); 2182f0a2ac19Skettenis 2183f0a2ac19Skettenis return NULL; 2184f0a2ac19Skettenis } 2185f0a2ac19Skettenis 2186f0a2ac19Skettenis void 2187f0a2ac19Skettenis agintc_dmamem_free(bus_dma_tag_t dmat, struct agintc_dmamem *adm) 2188f0a2ac19Skettenis { 2189f0a2ac19Skettenis bus_dmamem_unmap(dmat, adm->adm_kva, adm->adm_size); 2190f0a2ac19Skettenis bus_dmamem_free(dmat, &adm->adm_seg, 1); 2191f0a2ac19Skettenis bus_dmamap_destroy(dmat, adm->adm_map); 2192f0a2ac19Skettenis free(adm, M_DEVBUF, sizeof(*adm)); 2193f0a2ac19Skettenis } 2194