xref: /openbsd-src/sys/arch/arm64/dev/aplintc.c (revision 5dee57024e6d59a6ce03549ce37fbdea3e9df090)
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