1*5dee5702Skettenis /* $OpenBSD: aplintc.c,v 1.18 2022/12/21 22:30:42 kettenis Exp $ */
27bc868b9Skettenis /*
37bc868b9Skettenis * Copyright (c) 2021 Mark Kettenis
47bc868b9Skettenis *
57bc868b9Skettenis * Permission to use, copy, modify, and distribute this software for any
67bc868b9Skettenis * purpose with or without fee is hereby granted, provided that the above
77bc868b9Skettenis * copyright notice and this permission notice appear in all copies.
87bc868b9Skettenis *
97bc868b9Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
107bc868b9Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
117bc868b9Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
127bc868b9Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
137bc868b9Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
147bc868b9Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
157bc868b9Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167bc868b9Skettenis */
177bc868b9Skettenis
187bc868b9Skettenis #include <sys/param.h>
19f8505842Skettenis #include <sys/atomic.h>
207bc868b9Skettenis #include <sys/device.h>
217bc868b9Skettenis #include <sys/evcount.h>
227bc868b9Skettenis #include <sys/malloc.h>
237bc868b9Skettenis #include <sys/systm.h>
247bc868b9Skettenis
25f48e0365Skettenis #include <machine/armreg.h>
267bc868b9Skettenis #include <machine/bus.h>
277bc868b9Skettenis #include <machine/fdt.h>
287bc868b9Skettenis #include <machine/intr.h>
297bc868b9Skettenis
307bc868b9Skettenis #include <dev/ofw/openfirm.h>
317bc868b9Skettenis #include <dev/ofw/fdt.h>
327bc868b9Skettenis
337bc868b9Skettenis #include <ddb/db_output.h>
347bc868b9Skettenis
35defbb6a1Skettenis #define APL_IRQ_CR_EL1 s3_4_c15_c10_4
36defbb6a1Skettenis #define APL_IRQ_CR_EL1_DISABLE (3 << 0)
37defbb6a1Skettenis
38f4647fb9Skettenis #define APL_IPI_LOCAL_RR_EL1 s3_5_c15_c0_0
3925b00589Skettenis #define APL_IPI_GLOBAL_RR_EL1 s3_5_c15_c0_1
4025b00589Skettenis #define APL_IPI_SR_EL1 s3_5_c15_c1_1
4125b00589Skettenis #define APL_IPI_SR_EL1_PENDING (1 << 0)
4225b00589Skettenis
437bc868b9Skettenis #define AIC_INFO 0x0004
44d0056b06Skettenis #define AIC_INFO_NDIE(val) (((val) >> 24) & 0xf)
457bc868b9Skettenis #define AIC_INFO_NIRQ(val) ((val) & 0xffff)
467bc868b9Skettenis #define AIC_WHOAMI 0x2000
477bc868b9Skettenis #define AIC_EVENT 0x2004
48d0056b06Skettenis #define AIC_EVENT_DIE(val) (((val) >> 24) & 0xff)
49d0056b06Skettenis #define AIC_EVENT_TYPE(val) (((val) >> 16) & 0xff)
50defbb6a1Skettenis #define AIC_EVENT_TYPE_NONE 0
517bc868b9Skettenis #define AIC_EVENT_TYPE_IRQ 1
52f8505842Skettenis #define AIC_EVENT_TYPE_IPI 4
537bc868b9Skettenis #define AIC_EVENT_IRQ(val) ((val) & 0xffff)
54f8505842Skettenis #define AIC_EVENT_IPI_OTHER 1
55f8505842Skettenis #define AIC_EVENT_IPI_SELF 2
56f8505842Skettenis #define AIC_IPI_SEND 0x2008
57f8505842Skettenis #define AIC_IPI_ACK 0x200c
58f8505842Skettenis #define AIC_IPI_MASK_SET 0x2024
59f8505842Skettenis #define AIC_IPI_MASK_CLR 0x2028
60f8505842Skettenis #define AIC_IPI_OTHER (1U << 0)
61f8505842Skettenis #define AIC_IPI_SELF (1U << 31)
627bc868b9Skettenis #define AIC_TARGET_CPU(irq) (0x3000 + ((irq) << 2))
637bc868b9Skettenis #define AIC_SW_SET(irq) (0x4000 + (((irq) >> 5) << 2))
647bc868b9Skettenis #define AIC_SW_CLR(irq) (0x4080 + (((irq) >> 5) << 2))
657bc868b9Skettenis #define AIC_SW_BIT(irq) (1U << ((irq) & 0x1f))
667bc868b9Skettenis #define AIC_MASK_SET(irq) (0x4100 + (((irq) >> 5) << 2))
677bc868b9Skettenis #define AIC_MASK_CLR(irq) (0x4180 + (((irq) >> 5) << 2))
687bc868b9Skettenis #define AIC_MASK_BIT(irq) (1U << ((irq) & 0x1f))
697bc868b9Skettenis
70defbb6a1Skettenis #define AIC2_CONFIG 0x0014
71defbb6a1Skettenis #define AIC2_CONFIG_ENABLE (1 << 0)
72d0056b06Skettenis #define AIC2_SW_SET(die, irq) (0x6000 + (die) * 0x4a00 + (((irq) >> 5) << 2))
73d0056b06Skettenis #define AIC2_SW_CLR(die, irq) (0x6200 + (die) * 0x4a00 + (((irq) >> 5) << 2))
74d0056b06Skettenis #define AIC2_MASK_SET(die, irq) (0x6400 + (die) * 0x4a00 + (((irq) >> 5) << 2))
75d0056b06Skettenis #define AIC2_MASK_CLR(die, irq) (0x6600 + (die) * 0x4a00 + (((irq) >> 5) << 2))
76defbb6a1Skettenis #define AIC2_EVENT 0xc000
77defbb6a1Skettenis
78f8505842Skettenis #define AIC_MAXCPUS 32
79d0056b06Skettenis #define AIC_MAXDIES 4
80f8505842Skettenis
817bc868b9Skettenis #define HREAD4(sc, reg) \
827bc868b9Skettenis (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
837bc868b9Skettenis #define HWRITE4(sc, reg, val) \
847bc868b9Skettenis bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
85defbb6a1Skettenis #define HSET4(sc, reg, bits) \
86defbb6a1Skettenis HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
87defbb6a1Skettenis #define HCLR4(sc, reg, bits) \
88defbb6a1Skettenis HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
897bc868b9Skettenis
907bc868b9Skettenis struct intrhand {
917bc868b9Skettenis TAILQ_ENTRY(intrhand) ih_list;
927bc868b9Skettenis int (*ih_func)(void *);
937bc868b9Skettenis void *ih_arg;
947bc868b9Skettenis int ih_ipl;
957bc868b9Skettenis int ih_flags;
96d0056b06Skettenis int ih_die;
977bc868b9Skettenis int ih_irq;
987bc868b9Skettenis struct evcount ih_count;
997bc868b9Skettenis const char *ih_name;
1007bc868b9Skettenis struct cpu_info *ih_ci;
1017bc868b9Skettenis };
1027bc868b9Skettenis
1037bc868b9Skettenis struct aplintc_softc {
1047bc868b9Skettenis struct device sc_dev;
1057bc868b9Skettenis bus_space_tag_t sc_iot;
1067bc868b9Skettenis bus_space_handle_t sc_ioh;
10761a8eb80Skettenis bus_space_handle_t sc_event_ioh;
1087bc868b9Skettenis
109defbb6a1Skettenis int sc_version;
110defbb6a1Skettenis
1117bc868b9Skettenis struct interrupt_controller sc_ic;
1127bc868b9Skettenis
1137bc868b9Skettenis struct intrhand *sc_fiq_handler;
114f8505842Skettenis int sc_fiq_pending[AIC_MAXCPUS];
115d0056b06Skettenis struct intrhand **sc_irq_handler[AIC_MAXDIES];
1167bc868b9Skettenis int sc_nirq;
117d0056b06Skettenis int sc_ndie;
11816f2ec56Stobhe int sc_ncells;
1197bc868b9Skettenis TAILQ_HEAD(, intrhand) sc_irq_list[NIPL];
120f8505842Skettenis
121f8505842Skettenis uint32_t sc_cpuremap[AIC_MAXCPUS];
122f8505842Skettenis u_int sc_ipi_reason[AIC_MAXCPUS];
12325b00589Skettenis struct evcount sc_ipi_count;
1247bc868b9Skettenis };
1257bc868b9Skettenis
1263b6109e0Skettenis static inline void
aplintc_sw_clr(struct aplintc_softc * sc,int die,int irq)1273b6109e0Skettenis aplintc_sw_clr(struct aplintc_softc *sc, int die, int irq)
1283b6109e0Skettenis {
1293b6109e0Skettenis if (sc->sc_version == 1)
1303b6109e0Skettenis HWRITE4(sc, AIC_SW_CLR(irq), AIC_SW_BIT(irq));
1313b6109e0Skettenis else
1323b6109e0Skettenis HWRITE4(sc, AIC2_SW_CLR(die, irq), AIC_SW_BIT(irq));
1333b6109e0Skettenis }
1343b6109e0Skettenis
1353b6109e0Skettenis static inline void
aplintc_sw_set(struct aplintc_softc * sc,int die,int irq)1363b6109e0Skettenis aplintc_sw_set(struct aplintc_softc *sc, int die, int irq)
1373b6109e0Skettenis {
1383b6109e0Skettenis if (sc->sc_version == 1)
1393b6109e0Skettenis HWRITE4(sc, AIC_SW_SET(irq), AIC_SW_BIT(irq));
1403b6109e0Skettenis else
1413b6109e0Skettenis HWRITE4(sc, AIC2_SW_SET(die, irq), AIC_SW_BIT(irq));
1423b6109e0Skettenis }
1433b6109e0Skettenis
1443b6109e0Skettenis static inline void
aplintc_mask_clr(struct aplintc_softc * sc,int die,int irq)1453b6109e0Skettenis aplintc_mask_clr(struct aplintc_softc *sc, int die, int irq)
1463b6109e0Skettenis {
1473b6109e0Skettenis if (sc->sc_version == 1)
1483b6109e0Skettenis HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq));
1493b6109e0Skettenis else
1503b6109e0Skettenis HWRITE4(sc, AIC2_MASK_CLR(die, irq), AIC_MASK_BIT(irq));
1513b6109e0Skettenis }
1523b6109e0Skettenis
1533b6109e0Skettenis static inline void
aplintc_mask_set(struct aplintc_softc * sc,int die,int irq)1543b6109e0Skettenis aplintc_mask_set(struct aplintc_softc *sc, int die, int irq)
1553b6109e0Skettenis {
1563b6109e0Skettenis if (sc->sc_version == 1)
1573b6109e0Skettenis HWRITE4(sc, AIC_MASK_SET(irq), AIC_MASK_BIT(irq));
1583b6109e0Skettenis else
1593b6109e0Skettenis HWRITE4(sc, AIC2_MASK_SET(die, irq), AIC_MASK_BIT(irq));
1603b6109e0Skettenis }
1613b6109e0Skettenis
1627bc868b9Skettenis struct aplintc_softc *aplintc_sc;
1637bc868b9Skettenis
1647bc868b9Skettenis int aplintc_match(struct device *, void *, void *);
1657bc868b9Skettenis void aplintc_attach(struct device *, struct device *, void *);
1667bc868b9Skettenis
167471aeecfSnaddy const struct cfattach aplintc_ca = {
168*5dee5702Skettenis sizeof (struct aplintc_softc), aplintc_match, aplintc_attach
1697bc868b9Skettenis };
1707bc868b9Skettenis
1717bc868b9Skettenis struct cfdriver aplintc_cd = {
1727bc868b9Skettenis NULL, "aplintc", DV_DULL
1737bc868b9Skettenis };
1747bc868b9Skettenis
175f8505842Skettenis void aplintc_cpuinit(void);
1767bc868b9Skettenis void aplintc_irq_handler(void *);
1777bc868b9Skettenis void aplintc_fiq_handler(void *);
1787bc868b9Skettenis void aplintc_intr_barrier(void *);
1797bc868b9Skettenis int aplintc_splraise(int);
1807bc868b9Skettenis int aplintc_spllower(int);
1817bc868b9Skettenis void aplintc_splx(int);
1827bc868b9Skettenis void aplintc_setipl(int);
183*5dee5702Skettenis void aplintc_enable_wakeup(void);
184*5dee5702Skettenis void aplintc_disable_wakeup(void);
1857bc868b9Skettenis
1867bc868b9Skettenis void *aplintc_intr_establish(void *, int *, int, struct cpu_info *,
1877bc868b9Skettenis int (*)(void *), void *, char *);
1887bc868b9Skettenis void aplintc_intr_disestablish(void *);
189*5dee5702Skettenis void aplintc_intr_set_wakeup(void *);
1907bc868b9Skettenis
191f8505842Skettenis void aplintc_send_ipi(struct cpu_info *, int);
19225b00589Skettenis void aplintc_handle_ipi(struct aplintc_softc *);
193f8505842Skettenis
1947bc868b9Skettenis int
aplintc_match(struct device * parent,void * match,void * aux)1957bc868b9Skettenis aplintc_match(struct device *parent, void *match, void *aux)
1967bc868b9Skettenis {
1977bc868b9Skettenis struct fdt_attach_args *faa = aux;
1987bc868b9Skettenis
199defbb6a1Skettenis return OF_is_compatible(faa->fa_node, "apple,aic") ||
200defbb6a1Skettenis OF_is_compatible(faa->fa_node, "apple,aic2");
2017bc868b9Skettenis }
2027bc868b9Skettenis
2037bc868b9Skettenis void
aplintc_attach(struct device * parent,struct device * self,void * aux)2047bc868b9Skettenis aplintc_attach(struct device *parent, struct device *self, void *aux)
2057bc868b9Skettenis {
2067bc868b9Skettenis struct aplintc_softc *sc = (struct aplintc_softc *)self;
2077bc868b9Skettenis struct fdt_attach_args *faa = aux;
2087bc868b9Skettenis uint32_t info;
209d0056b06Skettenis int die, ipl;
2107bc868b9Skettenis
2117bc868b9Skettenis if (faa->fa_nreg < 1) {
2127bc868b9Skettenis printf(": no registers\n");
2137bc868b9Skettenis return;
2147bc868b9Skettenis }
2157bc868b9Skettenis
2167bc868b9Skettenis sc->sc_iot = faa->fa_iot;
2177bc868b9Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
2187bc868b9Skettenis faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
2197bc868b9Skettenis printf(": can't map registers\n");
2207bc868b9Skettenis return;
2217bc868b9Skettenis }
2227bc868b9Skettenis
223defbb6a1Skettenis if (OF_is_compatible(faa->fa_node, "apple,aic2"))
224defbb6a1Skettenis sc->sc_version = 2;
225defbb6a1Skettenis else
226defbb6a1Skettenis sc->sc_version = 1;
227defbb6a1Skettenis
22816f2ec56Stobhe sc->sc_ncells = OF_getpropint(faa->fa_node, "#interrupt-cells", 3);
22916f2ec56Stobhe if (sc->sc_ncells < 3 || sc->sc_ncells > 4) {
23016f2ec56Stobhe printf(": invalid number of cells\n");
23116f2ec56Stobhe return;
23216f2ec56Stobhe }
23316f2ec56Stobhe
23461a8eb80Skettenis /*
23561a8eb80Skettenis * AIC2 has the event register specified separately. However
23661a8eb80Skettenis * a preliminary device tree binding for AIC2 had it included
23761a8eb80Skettenis * in the main register area, like with AIC1. Support both
23861a8eb80Skettenis * for now.
23961a8eb80Skettenis */
24061a8eb80Skettenis if (faa->fa_nreg > 1) {
24161a8eb80Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
24261a8eb80Skettenis faa->fa_reg[1].size, 0, &sc->sc_event_ioh)) {
24361a8eb80Skettenis printf(": can't map event register\n");
24461a8eb80Skettenis return;
24561a8eb80Skettenis }
24661a8eb80Skettenis } else {
24761a8eb80Skettenis if (sc->sc_version == 1) {
24861a8eb80Skettenis bus_space_subregion(sc->sc_iot, sc->sc_ioh,
24961a8eb80Skettenis AIC_EVENT, 4, &sc->sc_event_ioh);
25061a8eb80Skettenis } else {
25161a8eb80Skettenis bus_space_subregion(sc->sc_iot, sc->sc_ioh,
25261a8eb80Skettenis AIC2_EVENT, 4, &sc->sc_event_ioh);
25361a8eb80Skettenis }
25461a8eb80Skettenis }
25561a8eb80Skettenis
2567bc868b9Skettenis info = HREAD4(sc, AIC_INFO);
2577bc868b9Skettenis sc->sc_nirq = AIC_INFO_NIRQ(info);
258d0056b06Skettenis sc->sc_ndie = AIC_INFO_NDIE(info) + 1;
259d0056b06Skettenis for (die = 0; die < sc->sc_ndie; die++) {
260d0056b06Skettenis sc->sc_irq_handler[die] = mallocarray(sc->sc_nirq,
261d0056b06Skettenis sizeof(struct intrhand), M_DEVBUF, M_WAITOK | M_ZERO);
262d0056b06Skettenis }
2637bc868b9Skettenis for (ipl = 0; ipl < NIPL; ipl++)
2647bc868b9Skettenis TAILQ_INIT(&sc->sc_irq_list[ipl]);
2657bc868b9Skettenis
266d0056b06Skettenis printf(" nirq %d ndie %d\n", sc->sc_nirq, sc->sc_ndie);
2677bc868b9Skettenis
2687bc868b9Skettenis arm_init_smask();
2697bc868b9Skettenis
2707bc868b9Skettenis aplintc_sc = sc;
271f8505842Skettenis aplintc_cpuinit();
272f8505842Skettenis
27325b00589Skettenis evcount_attach(&sc->sc_ipi_count, "ipi", NULL);
2747bc868b9Skettenis arm_set_intr_handler(aplintc_splraise, aplintc_spllower, aplintc_splx,
275*5dee5702Skettenis aplintc_setipl, aplintc_irq_handler, aplintc_fiq_handler,
276*5dee5702Skettenis aplintc_enable_wakeup, aplintc_disable_wakeup);
2777bc868b9Skettenis
2787bc868b9Skettenis sc->sc_ic.ic_node = faa->fa_node;
2797bc868b9Skettenis sc->sc_ic.ic_cookie = self;
2807bc868b9Skettenis sc->sc_ic.ic_establish = aplintc_intr_establish;
2817bc868b9Skettenis sc->sc_ic.ic_disestablish = aplintc_intr_disestablish;
282f8505842Skettenis sc->sc_ic.ic_cpu_enable = aplintc_cpuinit;
2837bc868b9Skettenis sc->sc_ic.ic_barrier = aplintc_intr_barrier;
284*5dee5702Skettenis sc->sc_ic.ic_set_wakeup = aplintc_intr_set_wakeup;
2857bc868b9Skettenis arm_intr_register_fdt(&sc->sc_ic);
286f8505842Skettenis
28799391a55Skettenis #ifdef MULTIPROCESSOR
288f8505842Skettenis intr_send_ipi_func = aplintc_send_ipi;
28999391a55Skettenis #endif
290defbb6a1Skettenis
291defbb6a1Skettenis if (sc->sc_version == 2)
292defbb6a1Skettenis HSET4(sc, AIC2_CONFIG, AIC2_CONFIG_ENABLE);
293f8505842Skettenis }
294f8505842Skettenis
295f8505842Skettenis void
aplintc_cpuinit(void)296f8505842Skettenis aplintc_cpuinit(void)
297f8505842Skettenis {
298f8505842Skettenis struct aplintc_softc *sc = aplintc_sc;
299f8505842Skettenis struct cpu_info *ci = curcpu();
300f8505842Skettenis uint32_t hwid;
301f8505842Skettenis
302f8505842Skettenis KASSERT(ci->ci_cpuid < AIC_MAXCPUS);
303f8505842Skettenis
304defbb6a1Skettenis /*
305defbb6a1Skettenis * AIC2 does not provide us with a way to target external
306defbb6a1Skettenis * interrupts to a particular core. Therefore, disable IRQ
307defbb6a1Skettenis * delivery to the secondary CPUs which makes sure all
308defbb6a1Skettenis * external interrupts are delivered to the primary CPU.
309defbb6a1Skettenis */
310defbb6a1Skettenis if (!CPU_IS_PRIMARY(ci))
311defbb6a1Skettenis WRITE_SPECIALREG(APL_IRQ_CR_EL1, APL_IRQ_CR_EL1_DISABLE);
312defbb6a1Skettenis
313defbb6a1Skettenis if (sc->sc_version == 1) {
314f8505842Skettenis hwid = HREAD4(sc, AIC_WHOAMI);
315f8505842Skettenis KASSERT(hwid < AIC_MAXCPUS);
316f8505842Skettenis sc->sc_cpuremap[ci->ci_cpuid] = hwid;
3177bc868b9Skettenis }
318defbb6a1Skettenis }
3197bc868b9Skettenis
3207bc868b9Skettenis void
aplintc_run_handler(struct intrhand * ih,void * frame,int s)3212fef1223Skettenis aplintc_run_handler(struct intrhand *ih, void *frame, int s)
3222fef1223Skettenis {
3232fef1223Skettenis void *arg;
3242fef1223Skettenis int handled;
3252fef1223Skettenis
3262fef1223Skettenis #ifdef MULTIPROCESSOR
3272fef1223Skettenis int need_lock;
3282fef1223Skettenis
3292fef1223Skettenis if (ih->ih_flags & IPL_MPSAFE)
3302fef1223Skettenis need_lock = 0;
3312fef1223Skettenis else
3322fef1223Skettenis need_lock = s < IPL_SCHED;
3332fef1223Skettenis
3342fef1223Skettenis if (need_lock)
3352fef1223Skettenis KERNEL_LOCK();
3362fef1223Skettenis #endif
3372fef1223Skettenis
3382fef1223Skettenis if (ih->ih_arg)
3392fef1223Skettenis arg = ih->ih_arg;
3402fef1223Skettenis else
3412fef1223Skettenis arg = frame;
3422fef1223Skettenis
3432fef1223Skettenis handled = ih->ih_func(arg);
3442fef1223Skettenis if (handled)
3452fef1223Skettenis ih->ih_count.ec_count++;
3462fef1223Skettenis
3472fef1223Skettenis #ifdef MULTIPROCESSOR
3482fef1223Skettenis if (need_lock)
3492fef1223Skettenis KERNEL_UNLOCK();
3502fef1223Skettenis #endif
3512fef1223Skettenis }
3522fef1223Skettenis
3532fef1223Skettenis void
aplintc_irq_handler(void * frame)3547bc868b9Skettenis aplintc_irq_handler(void *frame)
3557bc868b9Skettenis {
3567bc868b9Skettenis struct aplintc_softc *sc = aplintc_sc;
3577bc868b9Skettenis struct cpu_info *ci = curcpu();
3587bc868b9Skettenis struct intrhand *ih;
3597bc868b9Skettenis uint32_t event;
360d0056b06Skettenis uint32_t die, irq, type;
3617bc868b9Skettenis int s;
3627bc868b9Skettenis
36361a8eb80Skettenis event = bus_space_read_4(sc->sc_iot, sc->sc_event_ioh, 0);
364d0056b06Skettenis die = AIC_EVENT_DIE(event);
3657bc868b9Skettenis irq = AIC_EVENT_IRQ(event);
3667bc868b9Skettenis type = AIC_EVENT_TYPE(event);
3677bc868b9Skettenis
368ca3decdaSkettenis if (type != AIC_EVENT_TYPE_IRQ) {
369defbb6a1Skettenis if (type != AIC_EVENT_TYPE_NONE) {
370defbb6a1Skettenis printf("%s: unexpected event type %d\n",
371defbb6a1Skettenis __func__, type);
372defbb6a1Skettenis }
373ca3decdaSkettenis return;
374ca3decdaSkettenis }
3757bc868b9Skettenis
376d0056b06Skettenis if (die >= sc->sc_ndie)
377d0056b06Skettenis panic("%s: unexpected die %d", __func__, die);
3787bc868b9Skettenis if (irq >= sc->sc_nirq)
3794123b6a7Sderaadt panic("%s: unexpected irq %d", __func__, irq);
3807bc868b9Skettenis
381d0056b06Skettenis if (sc->sc_irq_handler[die][irq] == NULL)
3827bc868b9Skettenis return;
3837bc868b9Skettenis
384d0056b06Skettenis aplintc_sw_clr(sc, die, irq);
385d0056b06Skettenis ih = sc->sc_irq_handler[die][irq];
3867bc868b9Skettenis
3877bc868b9Skettenis if (ci->ci_cpl >= ih->ih_ipl) {
3887bc868b9Skettenis /* Queue interrupt as pending. */
3897bc868b9Skettenis TAILQ_INSERT_TAIL(&sc->sc_irq_list[ih->ih_ipl], ih, ih_list);
3907bc868b9Skettenis } else {
3917bc868b9Skettenis s = aplintc_splraise(ih->ih_ipl);
3927bc868b9Skettenis intr_enable();
3932fef1223Skettenis aplintc_run_handler(ih, frame, s);
3947bc868b9Skettenis intr_disable();
3957bc868b9Skettenis aplintc_splx(s);
3967bc868b9Skettenis
397d0056b06Skettenis aplintc_mask_clr(sc, die, irq);
3987bc868b9Skettenis }
3997bc868b9Skettenis }
4007bc868b9Skettenis
4017bc868b9Skettenis void
aplintc_fiq_handler(void * frame)4027bc868b9Skettenis aplintc_fiq_handler(void *frame)
4037bc868b9Skettenis {
4047bc868b9Skettenis struct aplintc_softc *sc = aplintc_sc;
4057bc868b9Skettenis struct cpu_info *ci = curcpu();
4067bc868b9Skettenis uint64_t reg;
4077bc868b9Skettenis int s;
4087bc868b9Skettenis
40999391a55Skettenis #ifdef MULTIPROCESSOR
41025b00589Skettenis /* Handle IPIs. */
41125b00589Skettenis reg = READ_SPECIALREG(APL_IPI_SR_EL1);
41225b00589Skettenis if (reg & APL_IPI_SR_EL1_PENDING) {
41325b00589Skettenis WRITE_SPECIALREG(APL_IPI_SR_EL1, APL_IPI_SR_EL1_PENDING);
41425b00589Skettenis aplintc_handle_ipi(sc);
4157bc868b9Skettenis }
41699391a55Skettenis #endif
4177bc868b9Skettenis
41825b00589Skettenis /* Handle timer interrupts. */
41925b00589Skettenis reg = READ_SPECIALREG(cntv_ctl_el0);
42025b00589Skettenis if ((reg & (CNTV_CTL_ENABLE | CNTV_CTL_IMASK | CNTV_CTL_ISTATUS)) ==
42125b00589Skettenis (CNTV_CTL_ENABLE | CNTV_CTL_ISTATUS)) {
42225b00589Skettenis if (ci->ci_cpl >= IPL_CLOCK) {
42325b00589Skettenis /* Mask timer interrupt and mark as pending. */
42425b00589Skettenis WRITE_SPECIALREG(cntv_ctl_el0, reg | CNTV_CTL_IMASK);
42525b00589Skettenis sc->sc_fiq_pending[ci->ci_cpuid] = 1;
42625b00589Skettenis } else {
4277bc868b9Skettenis s = aplintc_splraise(IPL_CLOCK);
4287bc868b9Skettenis sc->sc_fiq_handler->ih_func(frame);
4297bc868b9Skettenis sc->sc_fiq_handler->ih_count.ec_count++;
4307bc868b9Skettenis aplintc_splx(s);
4317bc868b9Skettenis }
43225b00589Skettenis }
43325b00589Skettenis }
4347bc868b9Skettenis
4357bc868b9Skettenis void
aplintc_intr_barrier(void * cookie)4367bc868b9Skettenis aplintc_intr_barrier(void *cookie)
4377bc868b9Skettenis {
4387bc868b9Skettenis struct intrhand *ih = cookie;
4397bc868b9Skettenis
4407bc868b9Skettenis sched_barrier(ih->ih_ci);
4417bc868b9Skettenis }
4427bc868b9Skettenis
4437bc868b9Skettenis int
aplintc_splraise(int new)4447bc868b9Skettenis aplintc_splraise(int new)
4457bc868b9Skettenis {
4467bc868b9Skettenis struct cpu_info *ci = curcpu();
4477bc868b9Skettenis int old = ci->ci_cpl;
4487bc868b9Skettenis
4497bc868b9Skettenis if (old > new)
4507bc868b9Skettenis new = old;
4517bc868b9Skettenis
4527bc868b9Skettenis aplintc_setipl(new);
4537bc868b9Skettenis return old;
4547bc868b9Skettenis }
4557bc868b9Skettenis
4567bc868b9Skettenis int
aplintc_spllower(int new)4577bc868b9Skettenis aplintc_spllower(int new)
4587bc868b9Skettenis {
4597bc868b9Skettenis struct cpu_info *ci = curcpu();
4607bc868b9Skettenis int old = ci->ci_cpl;
4617bc868b9Skettenis
4627bc868b9Skettenis aplintc_splx(new);
4637bc868b9Skettenis return old;
4647bc868b9Skettenis }
4657bc868b9Skettenis
4667bc868b9Skettenis void
aplintc_splx(int new)4677bc868b9Skettenis aplintc_splx(int new)
4687bc868b9Skettenis {
4697bc868b9Skettenis struct aplintc_softc *sc = aplintc_sc;
4707bc868b9Skettenis struct cpu_info *ci = curcpu();
4717bc868b9Skettenis struct intrhand *ih;
4727bc868b9Skettenis uint64_t reg;
4737bc868b9Skettenis u_long daif;
4747bc868b9Skettenis int ipl;
4757bc868b9Skettenis
4767bc868b9Skettenis daif = intr_disable();
4777bc868b9Skettenis
4787bc868b9Skettenis /* Process pending FIQs. */
479f8505842Skettenis if (sc->sc_fiq_pending[ci->ci_cpuid] && new < IPL_CLOCK) {
480f8505842Skettenis sc->sc_fiq_pending[ci->ci_cpuid] = 0;
4817bc868b9Skettenis reg = READ_SPECIALREG(cntv_ctl_el0);
4827bc868b9Skettenis WRITE_SPECIALREG(cntv_ctl_el0, reg & ~CNTV_CTL_IMASK);
4837bc868b9Skettenis }
4847bc868b9Skettenis
4857bc868b9Skettenis /* Process pending IRQs. */
486f8505842Skettenis if (CPU_IS_PRIMARY(ci)) {
4877bc868b9Skettenis for (ipl = ci->ci_cpl; ipl > new; ipl--) {
4887bc868b9Skettenis while (!TAILQ_EMPTY(&sc->sc_irq_list[ipl])) {
4897bc868b9Skettenis ih = TAILQ_FIRST(&sc->sc_irq_list[ipl]);
490f8505842Skettenis TAILQ_REMOVE(&sc->sc_irq_list[ipl],
491f8505842Skettenis ih, ih_list);
4927bc868b9Skettenis
493d0056b06Skettenis aplintc_sw_set(sc, ih->ih_die, ih->ih_irq);
494d0056b06Skettenis aplintc_mask_clr(sc, ih->ih_die, ih->ih_irq);
4957bc868b9Skettenis }
4967bc868b9Skettenis }
497f8505842Skettenis }
4987bc868b9Skettenis
4997bc868b9Skettenis aplintc_setipl(new);
5007bc868b9Skettenis intr_restore(daif);
5017bc868b9Skettenis
5027bc868b9Skettenis if (ci->ci_ipending & arm_smask[new])
5037bc868b9Skettenis arm_do_pending_intr(new);
5047bc868b9Skettenis }
5057bc868b9Skettenis
5067bc868b9Skettenis void
aplintc_setipl(int ipl)5077bc868b9Skettenis aplintc_setipl(int ipl)
5087bc868b9Skettenis {
5097bc868b9Skettenis struct cpu_info *ci = curcpu();
5107bc868b9Skettenis
5117bc868b9Skettenis ci->ci_cpl = ipl;
5127bc868b9Skettenis }
5137bc868b9Skettenis
514*5dee5702Skettenis void
aplintc_enable_wakeup(void)515*5dee5702Skettenis aplintc_enable_wakeup(void)
516*5dee5702Skettenis {
517*5dee5702Skettenis struct aplintc_softc *sc = aplintc_sc;
518*5dee5702Skettenis struct intrhand *ih;
519*5dee5702Skettenis int die, irq;
520*5dee5702Skettenis
521*5dee5702Skettenis for (die = 0; die < sc->sc_ndie; die++) {
522*5dee5702Skettenis for (irq = 0; irq < sc->sc_nirq; irq++) {
523*5dee5702Skettenis ih = sc->sc_irq_handler[die][irq];
524*5dee5702Skettenis if (ih == NULL || (ih->ih_flags & IPL_WAKEUP))
525*5dee5702Skettenis continue;
526*5dee5702Skettenis aplintc_mask_set(sc, die, irq);
527*5dee5702Skettenis }
528*5dee5702Skettenis }
529*5dee5702Skettenis }
530*5dee5702Skettenis
531*5dee5702Skettenis void
aplintc_disable_wakeup(void)532*5dee5702Skettenis aplintc_disable_wakeup(void)
533*5dee5702Skettenis {
534*5dee5702Skettenis struct aplintc_softc *sc = aplintc_sc;
535*5dee5702Skettenis struct intrhand *ih;
536*5dee5702Skettenis int die, irq;
537*5dee5702Skettenis
538*5dee5702Skettenis for (die = 0; die < sc->sc_ndie; die++) {
539*5dee5702Skettenis for (irq = 0; irq < sc->sc_nirq; irq++) {
540*5dee5702Skettenis ih = sc->sc_irq_handler[die][irq];
541*5dee5702Skettenis if (ih == NULL || (ih->ih_flags & IPL_WAKEUP))
542*5dee5702Skettenis continue;
543*5dee5702Skettenis aplintc_mask_clr(sc, die, irq);
544*5dee5702Skettenis }
545*5dee5702Skettenis }
546*5dee5702Skettenis }
547*5dee5702Skettenis
5487bc868b9Skettenis void *
aplintc_intr_establish(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)5497bc868b9Skettenis aplintc_intr_establish(void *cookie, int *cell, int level,
5507bc868b9Skettenis struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
5517bc868b9Skettenis {
5527bc868b9Skettenis struct aplintc_softc *sc = cookie;
5537bc868b9Skettenis struct intrhand *ih;
5547bc868b9Skettenis uint32_t type = cell[0];
555d0056b06Skettenis uint32_t die, irq;
556defbb6a1Skettenis
55716f2ec56Stobhe if (sc->sc_ncells == 3) {
558d0056b06Skettenis die = 0;
559defbb6a1Skettenis irq = cell[1];
560d0056b06Skettenis } else {
561d0056b06Skettenis die = cell[1];
562defbb6a1Skettenis irq = cell[2];
563d0056b06Skettenis }
5647bc868b9Skettenis
5657bc868b9Skettenis if (type == 0) {
5667bc868b9Skettenis KASSERT(level != (IPL_CLOCK | IPL_MPSAFE));
567d0056b06Skettenis if (die >= sc->sc_ndie) {
568d0056b06Skettenis panic("%s: bogus die number %d",
569d0056b06Skettenis sc->sc_dev.dv_xname, die);
570d0056b06Skettenis }
5717bc868b9Skettenis if (irq >= sc->sc_nirq) {
5724123b6a7Sderaadt panic("%s: bogus irq number %d",
5737bc868b9Skettenis sc->sc_dev.dv_xname, irq);
5747bc868b9Skettenis }
5757bc868b9Skettenis } else if (type == 1) {
5767bc868b9Skettenis KASSERT(level == (IPL_CLOCK | IPL_MPSAFE));
5777bc868b9Skettenis if (irq >= 4)
5784123b6a7Sderaadt panic("%s: bogus fiq number %d",
5797bc868b9Skettenis sc->sc_dev.dv_xname, irq);
5807bc868b9Skettenis } else {
5817bc868b9Skettenis panic("%s: bogus irq type %d",
5827bc868b9Skettenis sc->sc_dev.dv_xname, cell[0]);
5837bc868b9Skettenis }
5847bc868b9Skettenis
5857bc868b9Skettenis ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
5867bc868b9Skettenis ih->ih_func = func;
5877bc868b9Skettenis ih->ih_arg = arg;
5887bc868b9Skettenis ih->ih_ipl = level & IPL_IRQMASK;
5897bc868b9Skettenis ih->ih_flags = level & IPL_FLAGMASK;
590d0056b06Skettenis ih->ih_die = die;
5917bc868b9Skettenis ih->ih_irq = irq;
5927bc868b9Skettenis ih->ih_name = name;
5937bc868b9Skettenis ih->ih_ci = ci;
5947bc868b9Skettenis
5957bc868b9Skettenis if (name != NULL)
5967bc868b9Skettenis evcount_attach(&ih->ih_count, name, &ih->ih_irq);
5977bc868b9Skettenis
5987bc868b9Skettenis if (type == 0) {
599d0056b06Skettenis sc->sc_irq_handler[die][irq] = ih;
600d0056b06Skettenis if (sc->sc_version == 1)
6017bc868b9Skettenis HWRITE4(sc, AIC_TARGET_CPU(irq), 1);
602d0056b06Skettenis aplintc_mask_clr(sc, die, irq);
6037bc868b9Skettenis } else
6047bc868b9Skettenis sc->sc_fiq_handler = ih;
6057bc868b9Skettenis
6067bc868b9Skettenis return ih;
6077bc868b9Skettenis }
6087bc868b9Skettenis
6097bc868b9Skettenis void
aplintc_intr_disestablish(void * cookie)6107bc868b9Skettenis aplintc_intr_disestablish(void *cookie)
6117bc868b9Skettenis {
6127bc868b9Skettenis struct aplintc_softc *sc = aplintc_sc;
6137bc868b9Skettenis struct intrhand *ih = cookie;
6147bc868b9Skettenis struct intrhand *tmp;
6157bc868b9Skettenis u_long daif;
6167bc868b9Skettenis
6177bc868b9Skettenis KASSERT(ih->ih_ipl < IPL_CLOCK);
6187bc868b9Skettenis
6197bc868b9Skettenis daif = intr_disable();
6207bc868b9Skettenis
621d0056b06Skettenis aplintc_sw_clr(sc, ih->ih_die, ih->ih_irq);
622d0056b06Skettenis aplintc_mask_set(sc, ih->ih_die, ih->ih_irq);
6237bc868b9Skettenis
6247bc868b9Skettenis /* Remove ourselves from the list of pending IRQs. */
6257bc868b9Skettenis TAILQ_FOREACH(tmp, &sc->sc_irq_list[ih->ih_ipl], ih_list) {
6267bc868b9Skettenis if (tmp == ih) {
6277bc868b9Skettenis TAILQ_REMOVE(&sc->sc_irq_list[ih->ih_ipl],
6287bc868b9Skettenis ih, ih_list);
6297bc868b9Skettenis break;
6307bc868b9Skettenis }
6317bc868b9Skettenis }
6327bc868b9Skettenis
633d0056b06Skettenis sc->sc_irq_handler[ih->ih_die][ih->ih_irq] = NULL;
6347bc868b9Skettenis if (ih->ih_name)
6357bc868b9Skettenis evcount_detach(&ih->ih_count);
6367bc868b9Skettenis
6377bc868b9Skettenis intr_restore(daif);
6387bc868b9Skettenis
6397bc868b9Skettenis free(ih, M_DEVBUF, sizeof(*ih));
6407bc868b9Skettenis }
641f8505842Skettenis
6423b6109e0Skettenis void
aplintc_intr_set_wakeup(void * cookie)643*5dee5702Skettenis aplintc_intr_set_wakeup(void *cookie)
6443b6109e0Skettenis {
6453b6109e0Skettenis struct intrhand *ih = cookie;
6463b6109e0Skettenis
6473b6109e0Skettenis ih->ih_flags |= IPL_WAKEUP;
6483b6109e0Skettenis }
6493b6109e0Skettenis
65099391a55Skettenis #ifdef MULTIPROCESSOR
65199391a55Skettenis
652f8505842Skettenis void
aplintc_send_ipi(struct cpu_info * ci,int reason)653f8505842Skettenis aplintc_send_ipi(struct cpu_info *ci, int reason)
654f8505842Skettenis {
655f8505842Skettenis struct aplintc_softc *sc = aplintc_sc;
65625b00589Skettenis uint64_t sendmask;
657f8505842Skettenis
658f8505842Skettenis if (ci == curcpu() && reason == ARM_IPI_NOP)
659f8505842Skettenis return;
660f8505842Skettenis
6614002e08dSkettenis /* never overwrite IPI_DDB or IPI_HALT with IPI_NOP */
6624002e08dSkettenis if (reason == ARM_IPI_DDB || reason == ARM_IPI_HALT)
663f8505842Skettenis sc->sc_ipi_reason[ci->ci_cpuid] = reason;
664f8505842Skettenis membar_producer();
665f8505842Skettenis
66625b00589Skettenis sendmask = (ci->ci_mpidr & MPIDR_AFF0);
66725b00589Skettenis if ((curcpu()->ci_mpidr & MPIDR_AFF1) == (ci->ci_mpidr & MPIDR_AFF1)) {
66825b00589Skettenis /* Same cluster, so request local delivery. */
66925b00589Skettenis WRITE_SPECIALREG(APL_IPI_LOCAL_RR_EL1, sendmask);
67025b00589Skettenis } else {
67125b00589Skettenis /* Different cluster, so request global delivery. */
67225b00589Skettenis sendmask |= (ci->ci_mpidr & MPIDR_AFF1) << 8;
67325b00589Skettenis WRITE_SPECIALREG(APL_IPI_GLOBAL_RR_EL1, sendmask);
67425b00589Skettenis }
675f8505842Skettenis }
676f8505842Skettenis
677f8505842Skettenis void
aplintc_handle_ipi(struct aplintc_softc * sc)67825b00589Skettenis aplintc_handle_ipi(struct aplintc_softc *sc)
679f8505842Skettenis {
680f8505842Skettenis struct cpu_info *ci = curcpu();
681f8505842Skettenis
682f8505842Skettenis membar_consumer();
683f8505842Skettenis if (sc->sc_ipi_reason[ci->ci_cpuid] == ARM_IPI_DDB) {
684f8505842Skettenis sc->sc_ipi_reason[ci->ci_cpuid] = ARM_IPI_NOP;
685f8505842Skettenis #ifdef DDB
686f8505842Skettenis db_enter();
687f8505842Skettenis #endif
6886a89557eSkettenis } else if (sc->sc_ipi_reason[ci->ci_cpuid] == ARM_IPI_HALT) {
6896a89557eSkettenis sc->sc_ipi_reason[ci->ci_cpuid] = ARM_IPI_NOP;
6906a89557eSkettenis cpu_halt();
691f8505842Skettenis }
692f8505842Skettenis
69325b00589Skettenis sc->sc_ipi_count.ec_count++;
694f8505842Skettenis }
69599391a55Skettenis
69699391a55Skettenis #endif
697