xref: /openbsd-src/sys/arch/arm/cortex/ampintc.c (revision 94673892b7b28179327a9d4cb100f53993b0bd92)
1*94673892Sjsg /* $OpenBSD: ampintc.c,v 1.32 2023/09/22 01:10:43 jsg Exp $ */
2027018efSpatrick /*
3027018efSpatrick  * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
4027018efSpatrick  *
5027018efSpatrick  * Permission to use, copy, modify, and distribute this software for any
6027018efSpatrick  * purpose with or without fee is hereby granted, provided that the above
7027018efSpatrick  * copyright notice and this permission notice appear in all copies.
8027018efSpatrick  *
9027018efSpatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10027018efSpatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11027018efSpatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12027018efSpatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13027018efSpatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14027018efSpatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15027018efSpatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16027018efSpatrick  */
17027018efSpatrick 
18027018efSpatrick /*
19027018efSpatrick  * This driver implements the interrupt controller as specified in
20027018efSpatrick  * DDI0407E_cortex_a9_mpcore_r2p0_trm with the
21027018efSpatrick  * IHI0048A_gic_architecture_spec_v1_0 underlying specification
22027018efSpatrick  */
23027018efSpatrick #include <sys/param.h>
24027018efSpatrick #include <sys/systm.h>
25027018efSpatrick #include <sys/queue.h>
26027018efSpatrick #include <sys/malloc.h>
27027018efSpatrick #include <sys/device.h>
28027018efSpatrick #include <sys/evcount.h>
296fc522b1Skettenis 
3023a4b636Spatrick #include <uvm/uvm_extern.h>
3123a4b636Spatrick 
32027018efSpatrick #include <machine/bus.h>
336fc522b1Skettenis #include <machine/fdt.h>
346fc522b1Skettenis 
356fc522b1Skettenis #include <arm/cpufunc.h>
36027018efSpatrick 
376fc522b1Skettenis #include <dev/ofw/fdt.h>
386fc522b1Skettenis #include <dev/ofw/openfirm.h>
39d730c68aSrapha 
40*94673892Sjsg #include <machine/simplebusvar.h>
4123a4b636Spatrick 
42027018efSpatrick /* registers */
43027018efSpatrick #define	ICD_DCR			0x000
44027018efSpatrick #define		ICD_DCR_ES		0x00000001
45027018efSpatrick #define		ICD_DCR_ENS		0x00000002
46027018efSpatrick 
47027018efSpatrick #define ICD_ICTR			0x004
48027018efSpatrick #define		ICD_ICTR_LSPI_SH	11
49027018efSpatrick #define		ICD_ICTR_LSPI_M		0x1f
50027018efSpatrick #define		ICD_ICTR_CPU_SH		5
51027018efSpatrick #define		ICD_ICTR_CPU_M		0x07
52027018efSpatrick #define		ICD_ICTR_ITL_SH		0
53027018efSpatrick #define		ICD_ICTR_ITL_M		0x1f
54027018efSpatrick #define ICD_IDIR			0x008
55027018efSpatrick #define 	ICD_DIR_PROD_SH		24
56027018efSpatrick #define 	ICD_DIR_PROD_M		0xff
57027018efSpatrick #define 	ICD_DIR_REV_SH		12
58027018efSpatrick #define 	ICD_DIR_REV_M		0xfff
59027018efSpatrick #define 	ICD_DIR_IMP_SH		0
60027018efSpatrick #define 	ICD_DIR_IMP_M		0xfff
61027018efSpatrick 
6218a1c566Spatrick #define IRQ_TO_REG32(i)		(((i) >> 5) & 0x1f)
63027018efSpatrick #define IRQ_TO_REG32BIT(i)	((i) & 0x1f)
6418a1c566Spatrick #define IRQ_TO_REG4(i)		(((i) >> 2) & 0xff)
65027018efSpatrick #define IRQ_TO_REG4BIT(i)	((i) & 0x3)
6618a1c566Spatrick #define IRQ_TO_REG16(i)		(((i) >> 4) & 0x3f)
6723a4b636Spatrick #define IRQ_TO_REG16BIT(i)	((i) & 0xf)
68027018efSpatrick #define IRQ_TO_REGBIT_S(i)	8
69027018efSpatrick #define IRQ_TO_REG4BIT_M(i)	8
70027018efSpatrick 
71027018efSpatrick #define ICD_ISRn(i)		(0x080 + (IRQ_TO_REG32(i) * 4))
72027018efSpatrick #define ICD_ISERn(i)		(0x100 + (IRQ_TO_REG32(i) * 4))
73027018efSpatrick #define ICD_ICERn(i)		(0x180 + (IRQ_TO_REG32(i) * 4))
74027018efSpatrick #define ICD_ISPRn(i)		(0x200 + (IRQ_TO_REG32(i) * 4))
75027018efSpatrick #define ICD_ICPRn(i)		(0x280 + (IRQ_TO_REG32(i) * 4))
76027018efSpatrick #define ICD_ABRn(i)		(0x300 + (IRQ_TO_REG32(i) * 4))
77027018efSpatrick #define ICD_IPRn(i)		(0x400 + (i))
78027018efSpatrick #define ICD_IPTRn(i)		(0x800 + (i))
79027018efSpatrick #define ICD_ICRn(i)		(0xC00 + (IRQ_TO_REG16(i) * 4))
8023a4b636Spatrick #define 	ICD_ICR_TRIG_LEVEL(i)	(0x0 << (IRQ_TO_REG16BIT(i) * 2))
8123a4b636Spatrick #define 	ICD_ICR_TRIG_EDGE(i)	(0x2 << (IRQ_TO_REG16BIT(i) * 2))
8223a4b636Spatrick #define 	ICD_ICR_TRIG_MASK(i)	(0x2 << (IRQ_TO_REG16BIT(i) * 2))
8323a4b636Spatrick 
84027018efSpatrick /*
85027018efSpatrick  * what about (ppi|spi)_status
86027018efSpatrick  */
87027018efSpatrick #define ICD_PPI			0xD00
88027018efSpatrick #define 	ICD_PPI_GTIMER	(1 << 11)
89027018efSpatrick #define 	ICD_PPI_FIQ		(1 << 12)
90027018efSpatrick #define 	ICD_PPI_PTIMER	(1 << 13)
91027018efSpatrick #define 	ICD_PPI_PWDOG	(1 << 14)
92027018efSpatrick #define 	ICD_PPI_IRQ		(1 << 15)
93027018efSpatrick #define ICD_SPI_BASE		0xD04
94027018efSpatrick #define ICD_SPIn(i)			(ICD_SPI_BASE + ((i) * 4))
95027018efSpatrick 
96027018efSpatrick 
97027018efSpatrick #define ICD_SGIR			0xF00
98027018efSpatrick 
99027018efSpatrick #define ICD_PERIPH_ID_0			0xFD0
100027018efSpatrick #define ICD_PERIPH_ID_1			0xFD4
101027018efSpatrick #define ICD_PERIPH_ID_2			0xFD8
102027018efSpatrick #define ICD_PERIPH_ID_3			0xFDC
103027018efSpatrick #define ICD_PERIPH_ID_4			0xFE0
104027018efSpatrick #define ICD_PERIPH_ID_5			0xFE4
105027018efSpatrick #define ICD_PERIPH_ID_6			0xFE8
106027018efSpatrick #define ICD_PERIPH_ID_7			0xFEC
107027018efSpatrick 
108027018efSpatrick #define ICD_COMP_ID_0			0xFEC
109027018efSpatrick #define ICD_COMP_ID_1			0xFEC
110027018efSpatrick #define ICD_COMP_ID_2			0xFEC
111027018efSpatrick #define ICD_COMP_ID_3			0xFEC
112027018efSpatrick 
113027018efSpatrick 
114027018efSpatrick #define ICPICR				0x00
115027018efSpatrick #define ICPIPMR				0x04
116027018efSpatrick /* XXX - must left justify bits to  0 - 7  */
117027018efSpatrick #define 	ICMIPMR_SH 		4
118027018efSpatrick #define ICPBPR				0x08
119027018efSpatrick #define ICPIAR				0x0C
120027018efSpatrick #define 	ICPIAR_IRQ_SH		0
121027018efSpatrick #define 	ICPIAR_IRQ_M		0x3ff
122027018efSpatrick #define 	ICPIAR_CPUID_SH		10
123027018efSpatrick #define 	ICPIAR_CPUID_M		0x7
124027018efSpatrick #define 	ICPIAR_NO_PENDING_IRQ	ICPIAR_IRQ_M
125027018efSpatrick #define ICPEOIR				0x10
126027018efSpatrick #define ICPPRP				0x14
127027018efSpatrick #define ICPHPIR				0x18
128027018efSpatrick #define ICPIIR				0xFC
1296fc522b1Skettenis 
130027018efSpatrick /*
131027018efSpatrick  * what about periph_id and component_id
132027018efSpatrick  */
133027018efSpatrick 
134027018efSpatrick #define IRQ_ENABLE	1
135027018efSpatrick #define IRQ_DISABLE	0
136027018efSpatrick 
137027018efSpatrick struct ampintc_softc {
13823a4b636Spatrick 	struct simplebus_softc	 sc_sbus;
1390b5401c0Skettenis 	struct intrq 		*sc_handler;
140027018efSpatrick 	int			 sc_nintr;
141027018efSpatrick 	bus_space_tag_t		 sc_iot;
142027018efSpatrick 	bus_space_handle_t	 sc_d_ioh, sc_p_ioh;
1436b5838f8Skettenis 	uint8_t			 sc_cpu_mask[ICD_ICTR_CPU_M + 1];
144c7189a0dSpatrick 	struct evcount		 sc_spur;
1456fc522b1Skettenis 	struct interrupt_controller sc_ic;
146ad0d549bSkettenis 	int			 sc_ipi_reason[ICD_ICTR_CPU_M + 1];
147ad0d549bSkettenis 	int			 sc_ipi_num[2];
148027018efSpatrick };
149027018efSpatrick struct ampintc_softc *ampintc;
150027018efSpatrick 
151027018efSpatrick 
152027018efSpatrick struct intrhand {
153027018efSpatrick 	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
154027018efSpatrick 	int (*ih_func)(void *);		/* handler */
155027018efSpatrick 	void *ih_arg;			/* arg for handler */
156027018efSpatrick 	int ih_ipl;			/* IPL_* */
157abff443dSkettenis 	int ih_flags;
158027018efSpatrick 	int ih_irq;			/* IRQ number */
159027018efSpatrick 	struct evcount	ih_count;
160027018efSpatrick 	char *ih_name;
161027018efSpatrick };
162027018efSpatrick 
163027018efSpatrick struct intrq {
164027018efSpatrick 	TAILQ_HEAD(, intrhand) iq_list;	/* handler list */
165eb0c9b3cSpatrick 	int iq_irq_max;			/* IRQ to mask while handling */
166eb0c9b3cSpatrick 	int iq_irq_min;			/* lowest IRQ when shared */
167027018efSpatrick 	int iq_ist;			/* share type */
168027018efSpatrick };
169027018efSpatrick 
170027018efSpatrick 
171027018efSpatrick int		 ampintc_match(struct device *, void *, void *);
172027018efSpatrick void		 ampintc_attach(struct device *, struct device *, void *);
173ad0d549bSkettenis void		 ampintc_cpuinit(void);
174027018efSpatrick int		 ampintc_spllower(int);
175027018efSpatrick void		 ampintc_splx(int);
176027018efSpatrick int		 ampintc_splraise(int);
177027018efSpatrick void		 ampintc_setipl(int);
178027018efSpatrick void		 ampintc_calc_mask(void);
179789e88a4Spatrick void		*ampintc_intr_establish(int, int, int, struct cpu_info *,
1806fc522b1Skettenis 		    int (*)(void *), void *, char *);
181789e88a4Spatrick void		*ampintc_intr_establish_ext(int, int, struct cpu_info *,
182789e88a4Spatrick 		    int (*)(void *), void *, char *);
183789e88a4Spatrick void		*ampintc_intr_establish_fdt(void *, int *, int,
184789e88a4Spatrick 		    struct cpu_info *, int (*)(void *), void *, char *);
185027018efSpatrick void		 ampintc_intr_disestablish(void *);
186027018efSpatrick void		 ampintc_irq_handler(void *);
187027018efSpatrick const char	*ampintc_intr_string(void *);
188027018efSpatrick uint32_t	 ampintc_iack(void);
189027018efSpatrick void		 ampintc_eoi(uint32_t);
190027018efSpatrick void		 ampintc_set_priority(int, int);
191027018efSpatrick void		 ampintc_intr_enable(int);
192027018efSpatrick void		 ampintc_intr_disable(int);
19323a4b636Spatrick void		 ampintc_intr_config(int, int);
1946b5838f8Skettenis void		 ampintc_route(int, int, struct cpu_info *);
195ad0d549bSkettenis void		 ampintc_route_irq(void *, int, struct cpu_info *);
196ad0d549bSkettenis 
197ad0d549bSkettenis int		 ampintc_ipi_combined(void *);
198ad0d549bSkettenis int		 ampintc_ipi_nop(void *);
199ad0d549bSkettenis int		 ampintc_ipi_ddb(void *);
200ad0d549bSkettenis void		 ampintc_send_ipi(struct cpu_info *, int);
201027018efSpatrick 
202e3ee5e84Smpi const struct cfattach	ampintc_ca = {
203027018efSpatrick 	sizeof (struct ampintc_softc), ampintc_match, ampintc_attach
204027018efSpatrick };
205027018efSpatrick 
206027018efSpatrick struct cfdriver ampintc_cd = {
207027018efSpatrick 	NULL, "ampintc", DV_DULL
208027018efSpatrick };
209027018efSpatrick 
2106fc522b1Skettenis static char *ampintc_compatibles[] = {
2116fc522b1Skettenis 	"arm,cortex-a7-gic",
2126fc522b1Skettenis 	"arm,cortex-a9-gic",
2136fc522b1Skettenis 	"arm,cortex-a15-gic",
214b78d0bc7Sjsg 	"arm,gic-400",
2156fc522b1Skettenis 	NULL
2166fc522b1Skettenis };
2176fc522b1Skettenis 
218027018efSpatrick int
ampintc_match(struct device * parent,void * cfdata,void * aux)219027018efSpatrick ampintc_match(struct device *parent, void *cfdata, void *aux)
220027018efSpatrick {
2216fc522b1Skettenis 	struct fdt_attach_args *faa = aux;
2226fc522b1Skettenis 	int i;
2236fc522b1Skettenis 
2246fc522b1Skettenis 	for (i = 0; ampintc_compatibles[i]; i++)
2256fc522b1Skettenis 		if (OF_is_compatible(faa->fa_node, ampintc_compatibles[i]))
226027018efSpatrick 			return (1);
2276fc522b1Skettenis 
2286fc522b1Skettenis 	return (0);
229027018efSpatrick }
230027018efSpatrick 
231027018efSpatrick void
ampintc_attach(struct device * parent,struct device * self,void * aux)2326fc522b1Skettenis ampintc_attach(struct device *parent, struct device *self, void *aux)
233027018efSpatrick {
234027018efSpatrick 	struct ampintc_softc *sc = (struct ampintc_softc *)self;
2356fc522b1Skettenis 	struct fdt_attach_args *faa = aux;
2366b5838f8Skettenis 	int i, nintr, ncpu;
2376b5838f8Skettenis 	uint32_t ictr;
238ad0d549bSkettenis #ifdef MULTIPROCESSOR
239ad0d549bSkettenis 	int nipi, ipiirq[2];
240ad0d549bSkettenis #endif
241027018efSpatrick 
242027018efSpatrick 	ampintc = sc;
243027018efSpatrick 
244027018efSpatrick 	arm_init_smask();
245027018efSpatrick 
2466fc522b1Skettenis 	sc->sc_iot = faa->fa_iot;
247027018efSpatrick 
2486fc522b1Skettenis 	/* First row: ICD */
2496fc522b1Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
2506fc522b1Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_d_ioh))
2516fc522b1Skettenis 		panic("%s: ICD bus_space_map failed!", __func__);
252d730c68aSrapha 
2536fc522b1Skettenis 	/* Second row: ICP */
2546fc522b1Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
2556fc522b1Skettenis 	    faa->fa_reg[1].size, 0, &sc->sc_p_ioh))
2566fc522b1Skettenis 		panic("%s: ICP bus_space_map failed!", __func__);
257027018efSpatrick 
258c7189a0dSpatrick 	evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
259c7189a0dSpatrick 
2606b5838f8Skettenis 	ictr = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICTR);
2616b5838f8Skettenis 	nintr = 32 * ((ictr >> ICD_ICTR_ITL_SH) & ICD_ICTR_ITL_M);
262027018efSpatrick 	nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
263027018efSpatrick 	sc->sc_nintr = nintr;
2646b5838f8Skettenis 	ncpu = ((ictr >> ICD_ICTR_CPU_SH) & ICD_ICTR_CPU_M) + 1;
26523a4b636Spatrick 	printf(" nirq %d, ncpu %d", nintr, ncpu);
266027018efSpatrick 
2676b5838f8Skettenis 	KASSERT(curcpu()->ci_cpuid <= ICD_ICTR_CPU_M);
2686b5838f8Skettenis 	sc->sc_cpu_mask[curcpu()->ci_cpuid] =
2696b5838f8Skettenis 	    bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(0));
270e851b7eaSkettenis 
271027018efSpatrick 	/* Disable all interrupts, clear all pending */
272027018efSpatrick 	for (i = 0; i < nintr/32; i++) {
2736fc522b1Skettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
2746fc522b1Skettenis 		    ICD_ICERn(i*32), ~0);
2756fc522b1Skettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
2766fc522b1Skettenis 		    ICD_ICPRn(i*32), ~0);
277027018efSpatrick 	}
278027018efSpatrick 	for (i = 0; i < nintr; i++) {
279027018efSpatrick 		/* lowest priority ?? */
2806fc522b1Skettenis 		bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i), 0xff);
281027018efSpatrick 		/* target no cpus */
2826fc522b1Skettenis 		bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(i), 0);
283027018efSpatrick 	}
284027018efSpatrick 	for (i = 2; i < nintr/16; i++) {
285027018efSpatrick 		/* irq 32 - N */
2866fc522b1Skettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(i*16), 0);
287027018efSpatrick 	}
288027018efSpatrick 
289027018efSpatrick 	/* software reset of the part? */
290027018efSpatrick 	/* set protection bit (kernel only)? */
291027018efSpatrick 
292027018efSpatrick 	/* XXX - check power saving bit */
293027018efSpatrick 
2940b5401c0Skettenis 	sc->sc_handler = mallocarray(nintr, sizeof(*sc->sc_handler), M_DEVBUF,
2950b5401c0Skettenis 	    M_ZERO | M_NOWAIT);
296027018efSpatrick 	for (i = 0; i < nintr; i++) {
2970b5401c0Skettenis 		TAILQ_INIT(&sc->sc_handler[i].iq_list);
298027018efSpatrick 	}
299027018efSpatrick 
300027018efSpatrick 	ampintc_setipl(IPL_HIGH);  /* XXX ??? */
301027018efSpatrick 	ampintc_calc_mask();
302027018efSpatrick 
303027018efSpatrick 	/* insert self as interrupt handler */
304027018efSpatrick 	arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx,
305027018efSpatrick 	    ampintc_setipl, ampintc_intr_establish_ext,
306027018efSpatrick 	    ampintc_intr_disestablish, ampintc_intr_string, ampintc_irq_handler);
307027018efSpatrick 
308ad0d549bSkettenis #ifdef MULTIPROCESSOR
309ad0d549bSkettenis 	/* setup IPI interrupts */
310ad0d549bSkettenis 
311ad0d549bSkettenis 	/*
312ad0d549bSkettenis 	 * Ideally we want two IPI interrupts, one for NOP and one for
313ad0d549bSkettenis 	 * DDB, however we can survive if only one is available it is
314ad0d549bSkettenis 	 * possible that most are not available to the non-secure OS.
315ad0d549bSkettenis 	 */
316ad0d549bSkettenis 	nipi = 0;
317ad0d549bSkettenis 	for (i = 0; i < 16; i++) {
318ad0d549bSkettenis 		int reg, oldreg;
319ad0d549bSkettenis 
320ad0d549bSkettenis 		oldreg = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh,
321ad0d549bSkettenis 		    ICD_IPRn(i));
322ad0d549bSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i),
323ad0d549bSkettenis 		    oldreg ^ 0x20);
324ad0d549bSkettenis 
325ad0d549bSkettenis 		/* if this interrupt is not usable, route will be zero */
326ad0d549bSkettenis 		reg = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i));
327ad0d549bSkettenis 		if (reg == oldreg)
328ad0d549bSkettenis 			continue;
329ad0d549bSkettenis 
330ad0d549bSkettenis 		/* return to original value, will be set when used */
331ad0d549bSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i),
332ad0d549bSkettenis 		    oldreg);
333ad0d549bSkettenis 
334ad0d549bSkettenis 		if (nipi == 0)
335ad0d549bSkettenis 			printf(" ipi: %d", i);
336ad0d549bSkettenis 		else
337ad0d549bSkettenis 			printf(", %d", i);
338ad0d549bSkettenis 		ipiirq[nipi++] = i;
339ad0d549bSkettenis 		if (nipi == 2)
340ad0d549bSkettenis 			break;
341ad0d549bSkettenis 	}
342ad0d549bSkettenis 
343ad0d549bSkettenis 	if (nipi == 0)
344ad0d549bSkettenis 		panic ("no irq available for IPI");
345ad0d549bSkettenis 
346ad0d549bSkettenis 	switch (nipi) {
347ad0d549bSkettenis 	case 1:
348ad0d549bSkettenis 		ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING,
349ad0d549bSkettenis 		    IPL_IPI|IPL_MPSAFE, ampintc_ipi_combined, sc, "ipi");
350ad0d549bSkettenis 		sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
351ad0d549bSkettenis 		sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[0];
352ad0d549bSkettenis 		break;
353ad0d549bSkettenis 	case 2:
354ad0d549bSkettenis 		ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING,
355ad0d549bSkettenis 		    IPL_IPI|IPL_MPSAFE, ampintc_ipi_nop, sc, "ipinop");
356ad0d549bSkettenis 		sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
357ad0d549bSkettenis 		ampintc_intr_establish(ipiirq[1], IST_EDGE_RISING,
358ad0d549bSkettenis 		    IPL_IPI|IPL_MPSAFE, ampintc_ipi_ddb, sc, "ipiddb");
359ad0d549bSkettenis 		sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1];
360ad0d549bSkettenis 		break;
361ad0d549bSkettenis 	default:
362ad0d549bSkettenis 		panic("nipi unexpected number %d", nipi);
363ad0d549bSkettenis 	}
364ad0d549bSkettenis 
365ad0d549bSkettenis 	intr_send_ipi_func = ampintc_send_ipi;
366ad0d549bSkettenis #endif
367ad0d549bSkettenis 
368027018efSpatrick 	/* enable interrupts */
3696fc522b1Skettenis 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_DCR, 3);
3706fc522b1Skettenis 	bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1);
371ebd24745Sjsg 	enable_interrupts(PSR_I);
3726fc522b1Skettenis 
3736fc522b1Skettenis 	sc->sc_ic.ic_node = faa->fa_node;
3746fc522b1Skettenis 	sc->sc_ic.ic_cookie = self;
3756fc522b1Skettenis 	sc->sc_ic.ic_establish = ampintc_intr_establish_fdt;
3762b8e4081Spatrick 	sc->sc_ic.ic_disestablish = ampintc_intr_disestablish;
377ad0d549bSkettenis 	sc->sc_ic.ic_route = ampintc_route_irq;
378ad0d549bSkettenis 	sc->sc_ic.ic_cpu_enable = ampintc_cpuinit;
3796fc522b1Skettenis 	arm_intr_register_fdt(&sc->sc_ic);
38023a4b636Spatrick 
38123a4b636Spatrick 	/* attach GICv2M frame controller */
38223a4b636Spatrick 	simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
383027018efSpatrick }
384027018efSpatrick 
385027018efSpatrick void
ampintc_set_priority(int irq,int pri)386027018efSpatrick ampintc_set_priority(int irq, int pri)
387027018efSpatrick {
388027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
389027018efSpatrick 	uint32_t		 prival;
390027018efSpatrick 
391027018efSpatrick 	/*
392027018efSpatrick 	 * We only use 16 (13 really) interrupt priorities,
393027018efSpatrick 	 * and a CPU is only required to implement bit 4-7 of each field
394027018efSpatrick 	 * so shift into the top bits.
395027018efSpatrick 	 * also low values are higher priority thus IPL_HIGH - pri
396027018efSpatrick 	 */
397027018efSpatrick 	prival = (IPL_HIGH - pri) << ICMIPMR_SH;
398027018efSpatrick 	bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival);
399027018efSpatrick }
400027018efSpatrick 
401027018efSpatrick void
ampintc_setipl(int new)402027018efSpatrick ampintc_setipl(int new)
403027018efSpatrick {
404027018efSpatrick 	struct cpu_info		*ci = curcpu();
405027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
406027018efSpatrick 	int			 psw;
407027018efSpatrick 
408027018efSpatrick 	/* disable here is only to keep hardware in sync with ci->ci_cpl */
409ebd24745Sjsg 	psw = disable_interrupts(PSR_I);
410027018efSpatrick 	ci->ci_cpl = new;
411027018efSpatrick 
412027018efSpatrick 	/* low values are higher priority thus IPL_HIGH - pri */
413027018efSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR,
414027018efSpatrick 	    (IPL_HIGH - new) << ICMIPMR_SH);
415027018efSpatrick 	restore_interrupts(psw);
416027018efSpatrick }
417027018efSpatrick 
418027018efSpatrick void
ampintc_intr_enable(int irq)419027018efSpatrick ampintc_intr_enable(int irq)
420027018efSpatrick {
421027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
422027018efSpatrick 
423027018efSpatrick #ifdef DEBUG
424027018efSpatrick 	printf("enable irq %d register %x bitmask %08x\n",
425027018efSpatrick 	    irq, ICD_ISERn(irq), 1 << IRQ_TO_REG32BIT(irq));
426027018efSpatrick #endif
427027018efSpatrick 
428027018efSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq),
429027018efSpatrick 	    1 << IRQ_TO_REG32BIT(irq));
430027018efSpatrick }
431027018efSpatrick 
432027018efSpatrick void
ampintc_intr_disable(int irq)433027018efSpatrick ampintc_intr_disable(int irq)
434027018efSpatrick {
435027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
436027018efSpatrick 
437027018efSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq),
438027018efSpatrick 	    1 << IRQ_TO_REG32BIT(irq));
439027018efSpatrick }
440027018efSpatrick 
44123a4b636Spatrick void
ampintc_intr_config(int irqno,int type)44223a4b636Spatrick ampintc_intr_config(int irqno, int type)
44323a4b636Spatrick {
44423a4b636Spatrick 	struct ampintc_softc	*sc = ampintc;
44523a4b636Spatrick 	uint32_t		 ctrl;
44623a4b636Spatrick 
44723a4b636Spatrick 	ctrl = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(irqno));
44823a4b636Spatrick 
44923a4b636Spatrick 	ctrl &= ~ICD_ICR_TRIG_MASK(irqno);
45023a4b636Spatrick 	if (type == IST_EDGE_RISING)
45123a4b636Spatrick 		ctrl |= ICD_ICR_TRIG_EDGE(irqno);
45223a4b636Spatrick 	else
45323a4b636Spatrick 		ctrl |= ICD_ICR_TRIG_LEVEL(irqno);
45423a4b636Spatrick 
45523a4b636Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(irqno), ctrl);
45623a4b636Spatrick }
457027018efSpatrick 
458027018efSpatrick void
ampintc_calc_mask(void)459027018efSpatrick ampintc_calc_mask(void)
460027018efSpatrick {
461027018efSpatrick 	struct cpu_info		*ci = curcpu();
462027018efSpatrick         struct ampintc_softc	*sc = ampintc;
463027018efSpatrick 	struct intrhand		*ih;
4646b5838f8Skettenis 	int			 irq;
465027018efSpatrick 
466027018efSpatrick 	for (irq = 0; irq < sc->sc_nintr; irq++) {
467027018efSpatrick 		int max = IPL_NONE;
468027018efSpatrick 		int min = IPL_HIGH;
4690b5401c0Skettenis 		TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
470027018efSpatrick 			if (ih->ih_ipl > max)
471027018efSpatrick 				max = ih->ih_ipl;
472027018efSpatrick 
473027018efSpatrick 			if (ih->ih_ipl < min)
474027018efSpatrick 				min = ih->ih_ipl;
475027018efSpatrick 		}
476027018efSpatrick 
477738a7e0eSkettenis 		if (max == IPL_NONE)
478738a7e0eSkettenis 			min = IPL_NONE;
479738a7e0eSkettenis 
4800b5401c0Skettenis 		if (sc->sc_handler[irq].iq_irq_max == max &&
4810b5401c0Skettenis 		    sc->sc_handler[irq].iq_irq_min == min)
482027018efSpatrick 			continue;
483eb0c9b3cSpatrick 
4840b5401c0Skettenis 		sc->sc_handler[irq].iq_irq_max = max;
4850b5401c0Skettenis 		sc->sc_handler[irq].iq_irq_min = min;
486027018efSpatrick 
487027018efSpatrick 		/* Enable interrupts at lower levels, clear -> enable */
488027018efSpatrick 		/* Set interrupt priority/enable */
489027018efSpatrick 		if (min != IPL_NONE) {
490027018efSpatrick 			ampintc_set_priority(irq, min);
491027018efSpatrick 			ampintc_intr_enable(irq);
4926b5838f8Skettenis 			ampintc_route(irq, IRQ_ENABLE, ci);
493027018efSpatrick 		} else {
494027018efSpatrick 			ampintc_intr_disable(irq);
4956b5838f8Skettenis 			ampintc_route(irq, IRQ_DISABLE, ci);
496027018efSpatrick 		}
497027018efSpatrick 	}
498027018efSpatrick 	ampintc_setipl(ci->ci_cpl);
499027018efSpatrick }
500027018efSpatrick 
501027018efSpatrick void
ampintc_splx(int new)502027018efSpatrick ampintc_splx(int new)
503027018efSpatrick {
504027018efSpatrick 	struct cpu_info *ci = curcpu();
505027018efSpatrick 
506027018efSpatrick 	if (ci->ci_ipending & arm_smask[new])
507027018efSpatrick 		arm_do_pending_intr(new);
508027018efSpatrick 
509027018efSpatrick 	ampintc_setipl(new);
510027018efSpatrick }
511027018efSpatrick 
512027018efSpatrick int
ampintc_spllower(int new)513027018efSpatrick ampintc_spllower(int new)
514027018efSpatrick {
515027018efSpatrick 	struct cpu_info *ci = curcpu();
516027018efSpatrick 	int old = ci->ci_cpl;
517027018efSpatrick 	ampintc_splx(new);
518027018efSpatrick 	return (old);
519027018efSpatrick }
520027018efSpatrick 
521027018efSpatrick int
ampintc_splraise(int new)522027018efSpatrick ampintc_splraise(int new)
523027018efSpatrick {
524027018efSpatrick 	struct cpu_info *ci = curcpu();
525027018efSpatrick 	int old;
526027018efSpatrick 	old = ci->ci_cpl;
527027018efSpatrick 
528027018efSpatrick 	/*
529027018efSpatrick 	 * setipl must always be called because there is a race window
530027018efSpatrick 	 * where the variable is updated before the mask is set
531027018efSpatrick 	 * an interrupt occurs in that window without the mask always
532027018efSpatrick 	 * being set, the hardware might not get updated on the next
533027018efSpatrick 	 * splraise completely messing up spl protection.
534027018efSpatrick 	 */
535027018efSpatrick 	if (old > new)
536027018efSpatrick 		new = old;
537027018efSpatrick 
538027018efSpatrick 	ampintc_setipl(new);
539027018efSpatrick 
540027018efSpatrick 	return (old);
541027018efSpatrick }
542027018efSpatrick 
543027018efSpatrick 
544027018efSpatrick uint32_t
ampintc_iack(void)545027018efSpatrick ampintc_iack(void)
546027018efSpatrick {
547027018efSpatrick 	uint32_t intid;
548027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
549027018efSpatrick 
550027018efSpatrick 	intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR);
551027018efSpatrick 
552027018efSpatrick 	return (intid);
553027018efSpatrick }
554027018efSpatrick 
555027018efSpatrick void
ampintc_eoi(uint32_t eoi)556027018efSpatrick ampintc_eoi(uint32_t eoi)
557027018efSpatrick {
558027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
559027018efSpatrick 
560027018efSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPEOIR, eoi);
561027018efSpatrick }
562027018efSpatrick 
563027018efSpatrick void
ampintc_route(int irq,int enable,struct cpu_info * ci)5646b5838f8Skettenis ampintc_route(int irq, int enable, struct cpu_info *ci)
565027018efSpatrick {
566027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
5676b5838f8Skettenis 	uint8_t			 mask, val;
5686b5838f8Skettenis 
5696b5838f8Skettenis 	KASSERT(ci->ci_cpuid <= ICD_ICTR_CPU_M);
5706b5838f8Skettenis 	mask = sc->sc_cpu_mask[ci->ci_cpuid];
571027018efSpatrick 
572027018efSpatrick 	val = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq));
573027018efSpatrick 	if (enable == IRQ_ENABLE)
5746b5838f8Skettenis 		val |= mask;
575027018efSpatrick 	else
5766b5838f8Skettenis 		val &= ~mask;
577027018efSpatrick 	bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq), val);
578027018efSpatrick }
579027018efSpatrick 
580027018efSpatrick void
ampintc_cpuinit(void)581ad0d549bSkettenis ampintc_cpuinit(void)
582ad0d549bSkettenis {
583ad0d549bSkettenis 	struct ampintc_softc    *sc = ampintc;
584ad0d549bSkettenis 	int			 i;
585ad0d549bSkettenis 
586ad0d549bSkettenis 	/* XXX - this is the only cpu specific call to set this */
587ad0d549bSkettenis 	if (sc->sc_cpu_mask[cpu_number()] == 0) {
588ad0d549bSkettenis 		for (i = 0; i < 32; i++) {
589ad0d549bSkettenis 			int cpumask =
590ad0d549bSkettenis 			    bus_space_read_1(sc->sc_iot, sc->sc_d_ioh,
591ad0d549bSkettenis 			        ICD_IPTRn(i));
592ad0d549bSkettenis 
593ad0d549bSkettenis 			if (cpumask != 0) {
594ad0d549bSkettenis 				sc->sc_cpu_mask[cpu_number()] = cpumask;
595ad0d549bSkettenis 				break;
596ad0d549bSkettenis 			}
597ad0d549bSkettenis 		}
598ad0d549bSkettenis 	}
599ad0d549bSkettenis 
600ad0d549bSkettenis 	if (sc->sc_cpu_mask[cpu_number()] == 0)
601ad0d549bSkettenis 		panic("could not determine cpu target mask");
602ad0d549bSkettenis }
603ad0d549bSkettenis 
604ad0d549bSkettenis void
ampintc_route_irq(void * v,int enable,struct cpu_info * ci)605ad0d549bSkettenis ampintc_route_irq(void *v, int enable, struct cpu_info *ci)
606ad0d549bSkettenis {
607ad0d549bSkettenis 	struct ampintc_softc    *sc = ampintc;
608ad0d549bSkettenis 	struct intrhand         *ih = v;
609ad0d549bSkettenis 
610ad0d549bSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1);
611ad0d549bSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(ih->ih_irq), 0);
612ad0d549bSkettenis 	if (enable) {
613ad0d549bSkettenis 		ampintc_set_priority(ih->ih_irq,
614ad0d549bSkettenis 		    sc->sc_handler[ih->ih_irq].iq_irq_min);
615ad0d549bSkettenis 		ampintc_intr_enable(ih->ih_irq);
616ad0d549bSkettenis 	}
617ad0d549bSkettenis 
618ad0d549bSkettenis 	ampintc_route(ih->ih_irq, enable, ci);
619ad0d549bSkettenis }
620ad0d549bSkettenis 
621ad0d549bSkettenis void
ampintc_irq_handler(void * frame)622027018efSpatrick ampintc_irq_handler(void *frame)
623027018efSpatrick {
624027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
625027018efSpatrick 	struct intrhand		*ih;
626027018efSpatrick 	void			*arg;
627027018efSpatrick 	uint32_t		 iack_val;
6287e841682Skettenis 	int			 irq, pri, s, handled;
629027018efSpatrick 
630027018efSpatrick 	iack_val = ampintc_iack();
631027018efSpatrick #ifdef DEBUG_INTC
632027018efSpatrick 	if (iack_val != 27)
633027018efSpatrick 		printf("irq  %d fired\n", iack_val);
634027018efSpatrick 	else {
635027018efSpatrick 		static int cnt = 0;
636027018efSpatrick 		if ((cnt++ % 100) == 0) {
637027018efSpatrick 			printf("irq  %d fired * _100\n", iack_val);
638e08d6af1Smiod #ifdef DDB
639e97088d6Smpi 			db_enter();
640e08d6af1Smiod #endif
641027018efSpatrick 		}
642027018efSpatrick 
643027018efSpatrick 	}
644027018efSpatrick #endif
645027018efSpatrick 
6468daa750dSpatrick 	irq = iack_val & ICPIAR_IRQ_M;
6478daa750dSpatrick 
6488daa750dSpatrick 	if (irq == 1023) {
649c7189a0dSpatrick 		sc->sc_spur.ec_count++;
650027018efSpatrick 		return;
651027018efSpatrick 	}
6528daa750dSpatrick 
6538daa750dSpatrick 	if (irq >= sc->sc_nintr)
6548daa750dSpatrick 		return;
655027018efSpatrick 
6560b5401c0Skettenis 	pri = sc->sc_handler[irq].iq_irq_max;
657027018efSpatrick 	s = ampintc_splraise(pri);
6580b5401c0Skettenis 	TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
659abff443dSkettenis #ifdef MULTIPROCESSOR
660abff443dSkettenis 		int need_lock;
661abff443dSkettenis 
662abff443dSkettenis 		if (ih->ih_flags & IPL_MPSAFE)
663abff443dSkettenis 			need_lock = 0;
664abff443dSkettenis 		else
665abff443dSkettenis 			need_lock = s < IPL_SCHED;
666abff443dSkettenis 
667abff443dSkettenis 		if (need_lock)
668abff443dSkettenis 			KERNEL_LOCK();
669abff443dSkettenis #endif
670abff443dSkettenis 
671fe0d117cSjsg 		if (ih->ih_arg)
672027018efSpatrick 			arg = ih->ih_arg;
673027018efSpatrick 		else
674027018efSpatrick 			arg = frame;
675027018efSpatrick 
6767e841682Skettenis 		enable_interrupts(PSR_I);
6777e841682Skettenis 		handled = ih->ih_func(arg);
6787e841682Skettenis 		disable_interrupts(PSR_I);
6797e841682Skettenis 		if (handled)
680027018efSpatrick 			ih->ih_count.ec_count++;
681027018efSpatrick 
682abff443dSkettenis #ifdef MULTIPROCESSOR
683abff443dSkettenis 		if (need_lock)
684abff443dSkettenis 			KERNEL_UNLOCK();
685abff443dSkettenis #endif
686027018efSpatrick 	}
687027018efSpatrick 	ampintc_eoi(iack_val);
688027018efSpatrick 
689027018efSpatrick 	ampintc_splx(s);
690027018efSpatrick }
691027018efSpatrick 
692027018efSpatrick void *
ampintc_intr_establish_ext(int irqno,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)693789e88a4Spatrick ampintc_intr_establish_ext(int irqno, int level, struct cpu_info *ci,
694789e88a4Spatrick     int (*func)(void *), void *arg, char *name)
695027018efSpatrick {
69623a4b636Spatrick 	return ampintc_intr_establish(irqno+32, IST_LEVEL_HIGH, level,
697789e88a4Spatrick 	    ci, func, arg, name);
698027018efSpatrick }
699027018efSpatrick 
700027018efSpatrick void *
ampintc_intr_establish_fdt(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)7016fc522b1Skettenis ampintc_intr_establish_fdt(void *cookie, int *cell, int level,
702789e88a4Spatrick     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
7036fc522b1Skettenis {
7046fc522b1Skettenis 	struct ampintc_softc	*sc = (struct ampintc_softc *)cookie;
7056fc522b1Skettenis 	int			 irq;
70623a4b636Spatrick 	int			 type;
7076fc522b1Skettenis 
7086fc522b1Skettenis 	/* 2nd cell contains the interrupt number */
7096fc522b1Skettenis 	irq = cell[1];
7106fc522b1Skettenis 
7116fc522b1Skettenis 	/* 1st cell contains type: 0 SPI (32-X), 1 PPI (16-31) */
7126fc522b1Skettenis 	if (cell[0] == 0)
7136fc522b1Skettenis 		irq += 32;
7146fc522b1Skettenis 	else if (cell[0] == 1)
7156fc522b1Skettenis 		irq += 16;
7166fc522b1Skettenis 	else
71723a4b636Spatrick 		panic("%s: bogus interrupt type", sc->sc_sbus.sc_dev.dv_xname);
7186fc522b1Skettenis 
71923a4b636Spatrick 	/* SPIs are only active-high level or low-to-high edge */
72023a4b636Spatrick 	if (cell[2] & 0x3)
72123a4b636Spatrick 		type = IST_EDGE_RISING;
72223a4b636Spatrick 	else
72323a4b636Spatrick 		type = IST_LEVEL_HIGH;
72423a4b636Spatrick 
725789e88a4Spatrick 	return ampintc_intr_establish(irq, type, level, ci, func, arg, name);
7266fc522b1Skettenis }
7276fc522b1Skettenis 
7286fc522b1Skettenis void *
ampintc_intr_establish(int irqno,int type,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)729789e88a4Spatrick ampintc_intr_establish(int irqno, int type, int level, struct cpu_info *ci,
730789e88a4Spatrick     int (*func)(void *), void *arg, char *name)
731027018efSpatrick {
732027018efSpatrick 	struct ampintc_softc	*sc = ampintc;
733027018efSpatrick 	struct intrhand		*ih;
734027018efSpatrick 	int			 psw;
735027018efSpatrick 
736027018efSpatrick 	if (irqno < 0 || irqno >= sc->sc_nintr)
737027018efSpatrick 		panic("ampintc_intr_establish: bogus irqnumber %d: %s",
738027018efSpatrick 		     irqno, name);
739027018efSpatrick 
740789e88a4Spatrick 	if (ci == NULL)
741789e88a4Spatrick 		ci = &cpu_info_primary;
742789e88a4Spatrick 	else if (!CPU_IS_PRIMARY(ci))
743789e88a4Spatrick 		return NULL;
744789e88a4Spatrick 
74523a4b636Spatrick 	if (irqno < 16) {
74623a4b636Spatrick 		/* SGI are only EDGE */
74723a4b636Spatrick 		type = IST_EDGE_RISING;
74823a4b636Spatrick 	} else if (irqno < 32) {
74923a4b636Spatrick 		/* PPI are only LEVEL */
75023a4b636Spatrick 		type = IST_LEVEL_HIGH;
75123a4b636Spatrick 	}
75223a4b636Spatrick 
753c88bb1b8Spatrick 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
754027018efSpatrick 	ih->ih_func = func;
755027018efSpatrick 	ih->ih_arg = arg;
756ad0d549bSkettenis 	ih->ih_ipl = level & IPL_IRQMASK;
757ad0d549bSkettenis 	ih->ih_flags = level & IPL_FLAGMASK;
758027018efSpatrick 	ih->ih_irq = irqno;
759027018efSpatrick 	ih->ih_name = name;
760027018efSpatrick 
761ebd24745Sjsg 	psw = disable_interrupts(PSR_I);
762027018efSpatrick 
7630b5401c0Skettenis 	TAILQ_INSERT_TAIL(&sc->sc_handler[irqno].iq_list, ih, ih_list);
764027018efSpatrick 
765027018efSpatrick 	if (name != NULL)
766027018efSpatrick 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
767027018efSpatrick 
768027018efSpatrick #ifdef DEBUG_INTC
769027018efSpatrick 	printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level,
770027018efSpatrick 	    name);
771027018efSpatrick #endif
77223a4b636Spatrick 
77323a4b636Spatrick 	ampintc_intr_config(irqno, type);
774027018efSpatrick 	ampintc_calc_mask();
775027018efSpatrick 
776027018efSpatrick 	restore_interrupts(psw);
777027018efSpatrick 	return (ih);
778027018efSpatrick }
779027018efSpatrick 
780027018efSpatrick void
ampintc_intr_disestablish(void * cookie)781027018efSpatrick ampintc_intr_disestablish(void *cookie)
782027018efSpatrick {
7837c01c46aSpatrick 	struct ampintc_softc	*sc = ampintc;
784027018efSpatrick 	struct intrhand		*ih = cookie;
7857c01c46aSpatrick 	int			 psw;
7867c01c46aSpatrick 
7877c01c46aSpatrick #ifdef DEBUG_INTC
7887c01c46aSpatrick 	printf("ampintc_intr_disestablish irq %d level %d [%s]\n",
7897c01c46aSpatrick 	    ih->ih_irq, ih->ih_ipl, ih->ih_name);
7907c01c46aSpatrick #endif
7917c01c46aSpatrick 
792ebd24745Sjsg 	psw = disable_interrupts(PSR_I);
7937c01c46aSpatrick 
7940b5401c0Skettenis 	TAILQ_REMOVE(&sc->sc_handler[ih->ih_irq].iq_list, ih, ih_list);
795027018efSpatrick 	if (ih->ih_name != NULL)
796027018efSpatrick 		evcount_detach(&ih->ih_count);
7977c01c46aSpatrick 	free(ih, M_DEVBUF, sizeof(*ih));
7987c01c46aSpatrick 
7997c01c46aSpatrick 	ampintc_calc_mask();
8007c01c46aSpatrick 
801027018efSpatrick 	restore_interrupts(psw);
802027018efSpatrick }
803027018efSpatrick 
804027018efSpatrick const char *
ampintc_intr_string(void * cookie)805027018efSpatrick ampintc_intr_string(void *cookie)
806027018efSpatrick {
807027018efSpatrick 	struct intrhand *ih = (struct intrhand *)cookie;
808027018efSpatrick 	static char irqstr[1 + sizeof("ampintc irq ") + 4];
809027018efSpatrick 
810027018efSpatrick 	snprintf(irqstr, sizeof irqstr, "ampintc irq %d", ih->ih_irq);
811027018efSpatrick 	return irqstr;
812027018efSpatrick }
81323a4b636Spatrick 
81423a4b636Spatrick /*
81523a4b636Spatrick  * GICv2m frame controller for MSI interrupts.
81623a4b636Spatrick  */
81723a4b636Spatrick #define GICV2M_TYPER		0x008
81823a4b636Spatrick #define  GICV2M_TYPER_SPI_BASE(x)	(((x) >> 16) & 0x3ff)
81923a4b636Spatrick #define  GICV2M_TYPER_SPI_COUNT(x)	(((x) >> 0) & 0x3ff)
82023a4b636Spatrick #define GICV2M_SETSPI_NS	0x040
82123a4b636Spatrick 
82223a4b636Spatrick int	 ampintc_msi_match(struct device *, void *, void *);
82323a4b636Spatrick void	 ampintc_msi_attach(struct device *, struct device *, void *);
82423a4b636Spatrick void	*ampintc_intr_establish_msi(void *, uint64_t *, uint64_t *,
825789e88a4Spatrick 	    int , struct cpu_info *, int (*)(void *), void *, char *);
82623a4b636Spatrick void	 ampintc_intr_disestablish_msi(void *);
82723a4b636Spatrick 
82823a4b636Spatrick struct ampintc_msi_softc {
82923a4b636Spatrick 	struct device			 sc_dev;
83023a4b636Spatrick 	bus_space_tag_t			 sc_iot;
83123a4b636Spatrick 	bus_space_handle_t		 sc_ioh;
83223a4b636Spatrick 	paddr_t				 sc_addr;
83323a4b636Spatrick 	int				 sc_bspi;
83423a4b636Spatrick 	int				 sc_nspi;
83523a4b636Spatrick 	void				**sc_spi;
83623a4b636Spatrick 	struct interrupt_controller	 sc_ic;
83723a4b636Spatrick };
83823a4b636Spatrick 
839e3ee5e84Smpi const struct cfattach	ampintcmsi_ca = {
84023a4b636Spatrick 	sizeof (struct ampintc_msi_softc), ampintc_msi_match, ampintc_msi_attach
84123a4b636Spatrick };
84223a4b636Spatrick 
84323a4b636Spatrick struct cfdriver ampintcmsi_cd = {
84423a4b636Spatrick 	NULL, "ampintcmsi", DV_DULL
84523a4b636Spatrick };
84623a4b636Spatrick 
84723a4b636Spatrick int
ampintc_msi_match(struct device * parent,void * cfdata,void * aux)84823a4b636Spatrick ampintc_msi_match(struct device *parent, void *cfdata, void *aux)
84923a4b636Spatrick {
85023a4b636Spatrick 	struct fdt_attach_args *faa = aux;
85123a4b636Spatrick 
85223a4b636Spatrick 	return OF_is_compatible(faa->fa_node, "arm,gic-v2m-frame");
85323a4b636Spatrick }
85423a4b636Spatrick 
85523a4b636Spatrick void
ampintc_msi_attach(struct device * parent,struct device * self,void * aux)85623a4b636Spatrick ampintc_msi_attach(struct device *parent, struct device *self, void *aux)
85723a4b636Spatrick {
85823a4b636Spatrick 	struct ampintc_msi_softc *sc = (struct ampintc_msi_softc *)self;
85923a4b636Spatrick 	struct fdt_attach_args *faa = aux;
86023a4b636Spatrick 	uint32_t typer;
86123a4b636Spatrick 
86223a4b636Spatrick 	sc->sc_iot = faa->fa_iot;
86323a4b636Spatrick 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
86423a4b636Spatrick 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
86523a4b636Spatrick 		panic("%s: bus_space_map failed!", __func__);
86623a4b636Spatrick 
86723a4b636Spatrick 	/* XXX: Hack to retrieve the physical address (from a CPU PoV). */
86823a4b636Spatrick 	if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) {
86923a4b636Spatrick 		printf(": cannot retrieve msi addr\n");
87023a4b636Spatrick 		return;
87123a4b636Spatrick 	}
87223a4b636Spatrick 
87323a4b636Spatrick 	typer = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GICV2M_TYPER);
87423a4b636Spatrick 	sc->sc_bspi = GICV2M_TYPER_SPI_BASE(typer);
87523a4b636Spatrick 	sc->sc_nspi = GICV2M_TYPER_SPI_COUNT(typer);
87623a4b636Spatrick 
87723a4b636Spatrick 	sc->sc_bspi = OF_getpropint(faa->fa_node,
87823a4b636Spatrick 	    "arm,msi-base-spi", sc->sc_bspi);
87923a4b636Spatrick 	sc->sc_nspi = OF_getpropint(faa->fa_node,
88023a4b636Spatrick 	    "arm,msi-num-spis", sc->sc_nspi);
88123a4b636Spatrick 
88223a4b636Spatrick 	printf(": nspi %d\n", sc->sc_nspi);
88323a4b636Spatrick 
88423a4b636Spatrick 	sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *), M_DEVBUF,
88523a4b636Spatrick 	    M_WAITOK|M_ZERO);
88623a4b636Spatrick 
88723a4b636Spatrick 	sc->sc_ic.ic_node = faa->fa_node;
88823a4b636Spatrick 	sc->sc_ic.ic_cookie = sc;
88923a4b636Spatrick 	sc->sc_ic.ic_establish_msi = ampintc_intr_establish_msi;
89023a4b636Spatrick 	sc->sc_ic.ic_disestablish = ampintc_intr_disestablish_msi;
89123a4b636Spatrick 	arm_intr_register_fdt(&sc->sc_ic);
89223a4b636Spatrick }
89323a4b636Spatrick 
89423a4b636Spatrick void *
ampintc_intr_establish_msi(void * self,uint64_t * addr,uint64_t * data,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)89523a4b636Spatrick ampintc_intr_establish_msi(void *self, uint64_t *addr, uint64_t *data,
896789e88a4Spatrick     int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
89723a4b636Spatrick {
89823a4b636Spatrick 	struct ampintc_msi_softc *sc = (struct ampintc_msi_softc *)self;
89923a4b636Spatrick 	void *cookie;
90023a4b636Spatrick 	int i;
90123a4b636Spatrick 
90223a4b636Spatrick 	for (i = 0; i < sc->sc_nspi; i++) {
90323a4b636Spatrick 		if (sc->sc_spi[i] != NULL)
90423a4b636Spatrick 			continue;
90523a4b636Spatrick 
90623a4b636Spatrick 		cookie = ampintc_intr_establish(sc->sc_bspi + i,
907789e88a4Spatrick 		    IST_EDGE_RISING, level, ci, func, arg, name);
90823a4b636Spatrick 		if (cookie == NULL)
90923a4b636Spatrick 			return NULL;
91023a4b636Spatrick 
91123a4b636Spatrick 		*addr = sc->sc_addr + GICV2M_SETSPI_NS;
91223a4b636Spatrick 		*data = sc->sc_bspi + i;
91323a4b636Spatrick 		sc->sc_spi[i] = cookie;
91423a4b636Spatrick 		return &sc->sc_spi[i];
91523a4b636Spatrick 	}
91623a4b636Spatrick 
91723a4b636Spatrick 	return NULL;
91823a4b636Spatrick }
91923a4b636Spatrick 
92023a4b636Spatrick void
ampintc_intr_disestablish_msi(void * cookie)92123a4b636Spatrick ampintc_intr_disestablish_msi(void *cookie)
92223a4b636Spatrick {
92323a4b636Spatrick 	ampintc_intr_disestablish(*(void **)cookie);
92423a4b636Spatrick 	*(void **)cookie = NULL;
92523a4b636Spatrick }
926ad0d549bSkettenis 
927ad0d549bSkettenis #ifdef MULTIPROCESSOR
928ad0d549bSkettenis int
ampintc_ipi_ddb(void * v)929ad0d549bSkettenis ampintc_ipi_ddb(void *v)
930ad0d549bSkettenis {
931ad0d549bSkettenis 	/* XXX */
932ad0d549bSkettenis 	db_enter();
933ad0d549bSkettenis 	return 1;
934ad0d549bSkettenis }
935ad0d549bSkettenis 
936ad0d549bSkettenis int
ampintc_ipi_nop(void * v)937ad0d549bSkettenis ampintc_ipi_nop(void *v)
938ad0d549bSkettenis {
939ad0d549bSkettenis 	/* Nothing to do here, just enough to wake up from WFI */
940ad0d549bSkettenis 	return 1;
941ad0d549bSkettenis }
942ad0d549bSkettenis 
943ad0d549bSkettenis int
ampintc_ipi_combined(void * v)944ad0d549bSkettenis ampintc_ipi_combined(void *v)
945ad0d549bSkettenis {
946ad0d549bSkettenis 	struct ampintc_softc *sc = (struct ampintc_softc *)v;
947ad0d549bSkettenis 
948ad0d549bSkettenis 	if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_DDB) {
949ad0d549bSkettenis 		sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP;
950ad0d549bSkettenis 		return ampintc_ipi_ddb(v);
951ad0d549bSkettenis 	} else {
952ad0d549bSkettenis 		return ampintc_ipi_nop(v);
953ad0d549bSkettenis 	}
954ad0d549bSkettenis }
955ad0d549bSkettenis 
956ad0d549bSkettenis void
ampintc_send_ipi(struct cpu_info * ci,int id)957ad0d549bSkettenis ampintc_send_ipi(struct cpu_info *ci, int id)
958ad0d549bSkettenis {
959ad0d549bSkettenis 	struct ampintc_softc	*sc = ampintc;
960ad0d549bSkettenis 	int sendmask;
961ad0d549bSkettenis 
962ad0d549bSkettenis 	if (ci == curcpu() && id == ARM_IPI_NOP)
963ad0d549bSkettenis 		return;
964ad0d549bSkettenis 
965ad0d549bSkettenis 	/* never overwrite IPI_DDB with IPI_NOP */
966ad0d549bSkettenis 	if (id == ARM_IPI_DDB)
967ad0d549bSkettenis 		sc->sc_ipi_reason[ci->ci_cpuid] = id;
968ad0d549bSkettenis 
969ad0d549bSkettenis 	/* currently will only send to one cpu */
970ad0d549bSkettenis 	sendmask = 1 << (16 + ci->ci_cpuid);
971ad0d549bSkettenis 	sendmask |= sc->sc_ipi_num[id];
972ad0d549bSkettenis 
973ad0d549bSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_SGIR, sendmask);
974ad0d549bSkettenis }
975ad0d549bSkettenis #endif
976