xref: /openbsd-src/sys/arch/arm/cortex/ampintc.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: ampintc.c,v 1.14 2016/08/21 06:47:47 jsg Exp $ */
2 /*
3  * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*
19  * This driver implements the interrupt controller as specified in
20  * DDI0407E_cortex_a9_mpcore_r2p0_trm with the
21  * IHI0048A_gic_architecture_spec_v1_0 underlying specification
22  */
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/queue.h>
26 #include <sys/malloc.h>
27 #include <sys/device.h>
28 #include <sys/evcount.h>
29 
30 #include <machine/bus.h>
31 #include <machine/fdt.h>
32 
33 #include <arm/cpufunc.h>
34 #include <arm/cortex/cortex.h>
35 
36 #include <dev/ofw/fdt.h>
37 #include <dev/ofw/openfirm.h>
38 
39 /* registers */
40 #define	ICD_DCR			0x000
41 #define		ICD_DCR_ES		0x00000001
42 #define		ICD_DCR_ENS		0x00000002
43 
44 #define ICD_ICTR			0x004
45 #define		ICD_ICTR_LSPI_SH	11
46 #define		ICD_ICTR_LSPI_M		0x1f
47 #define		ICD_ICTR_CPU_SH		5
48 #define		ICD_ICTR_CPU_M		0x07
49 #define		ICD_ICTR_ITL_SH		0
50 #define		ICD_ICTR_ITL_M		0x1f
51 #define ICD_IDIR			0x008
52 #define 	ICD_DIR_PROD_SH		24
53 #define 	ICD_DIR_PROD_M		0xff
54 #define 	ICD_DIR_REV_SH		12
55 #define 	ICD_DIR_REV_M		0xfff
56 #define 	ICD_DIR_IMP_SH		0
57 #define 	ICD_DIR_IMP_M		0xfff
58 
59 #define IRQ_TO_REG32(i)		(((i) >> 5) & 0x7)
60 #define IRQ_TO_REG32BIT(i)	((i) & 0x1f)
61 #define IRQ_TO_REG4(i)		(((i) >> 2) & 0x3f)
62 #define IRQ_TO_REG4BIT(i)	((i) & 0x3)
63 #define IRQ_TO_REG16(i)		(((i) >> 4) & 0xf)
64 #define IRQ_TO_REGBIT_S(i)	8
65 #define IRQ_TO_REG4BIT_M(i)	8
66 
67 #define ICD_ISRn(i)		(0x080 + (IRQ_TO_REG32(i) * 4))
68 #define ICD_ISERn(i)		(0x100 + (IRQ_TO_REG32(i) * 4))
69 #define ICD_ICERn(i)		(0x180 + (IRQ_TO_REG32(i) * 4))
70 #define ICD_ISPRn(i)		(0x200 + (IRQ_TO_REG32(i) * 4))
71 #define ICD_ICPRn(i)		(0x280 + (IRQ_TO_REG32(i) * 4))
72 #define ICD_ABRn(i)		(0x300 + (IRQ_TO_REG32(i) * 4))
73 #define ICD_IPRn(i)		(0x400 + (i))
74 #define ICD_IPTRn(i)		(0x800 + (i))
75 #define ICD_ICRn(i)		(0xC00 + (IRQ_TO_REG16(i) * 4))
76 /*
77  * what about (ppi|spi)_status
78  */
79 #define ICD_PPI			0xD00
80 #define 	ICD_PPI_GTIMER	(1 << 11)
81 #define 	ICD_PPI_FIQ		(1 << 12)
82 #define 	ICD_PPI_PTIMER	(1 << 13)
83 #define 	ICD_PPI_PWDOG	(1 << 14)
84 #define 	ICD_PPI_IRQ		(1 << 15)
85 #define ICD_SPI_BASE		0xD04
86 #define ICD_SPIn(i)			(ICD_SPI_BASE + ((i) * 4))
87 
88 
89 #define ICD_SGIR			0xF00
90 
91 #define ICD_PERIPH_ID_0			0xFD0
92 #define ICD_PERIPH_ID_1			0xFD4
93 #define ICD_PERIPH_ID_2			0xFD8
94 #define ICD_PERIPH_ID_3			0xFDC
95 #define ICD_PERIPH_ID_4			0xFE0
96 #define ICD_PERIPH_ID_5			0xFE4
97 #define ICD_PERIPH_ID_6			0xFE8
98 #define ICD_PERIPH_ID_7			0xFEC
99 
100 #define ICD_COMP_ID_0			0xFEC
101 #define ICD_COMP_ID_1			0xFEC
102 #define ICD_COMP_ID_2			0xFEC
103 #define ICD_COMP_ID_3			0xFEC
104 
105 
106 #define ICPICR				0x00
107 #define ICPIPMR				0x04
108 /* XXX - must left justify bits to  0 - 7  */
109 #define 	ICMIPMR_SH 		4
110 #define ICPBPR				0x08
111 #define ICPIAR				0x0C
112 #define 	ICPIAR_IRQ_SH		0
113 #define 	ICPIAR_IRQ_M		0x3ff
114 #define 	ICPIAR_CPUID_SH		10
115 #define 	ICPIAR_CPUID_M		0x7
116 #define 	ICPIAR_NO_PENDING_IRQ	ICPIAR_IRQ_M
117 #define ICPEOIR				0x10
118 #define ICPPRP				0x14
119 #define ICPHPIR				0x18
120 #define ICPIIR				0xFC
121 
122 /*
123  * what about periph_id and component_id
124  */
125 
126 #define IRQ_ENABLE	1
127 #define IRQ_DISABLE	0
128 
129 struct ampintc_softc {
130 	struct device		 sc_dev;
131 	struct intrq 		*sc_ampintc_handler;
132 	int			 sc_nintr;
133 	bus_space_tag_t		 sc_iot;
134 	bus_space_handle_t	 sc_d_ioh, sc_p_ioh;
135 	struct evcount		 sc_spur;
136 	struct interrupt_controller sc_ic;
137 };
138 struct ampintc_softc *ampintc;
139 
140 
141 struct intrhand {
142 	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
143 	int (*ih_func)(void *);		/* handler */
144 	void *ih_arg;			/* arg for handler */
145 	int ih_ipl;			/* IPL_* */
146 	int ih_irq;			/* IRQ number */
147 	struct evcount	ih_count;
148 	char *ih_name;
149 };
150 
151 struct intrq {
152 	TAILQ_HEAD(, intrhand) iq_list;	/* handler list */
153 	int iq_irq;			/* IRQ to mask while handling */
154 	int iq_levels;			/* IPL_*'s this IRQ has */
155 	int iq_ist;			/* share type */
156 };
157 
158 
159 int		 ampintc_match(struct device *, void *, void *);
160 void		 ampintc_attach(struct device *, struct device *, void *);
161 int		 ampintc_spllower(int);
162 void		 ampintc_splx(int);
163 int		 ampintc_splraise(int);
164 void		 ampintc_setipl(int);
165 void		 ampintc_calc_mask(void);
166 void		*ampintc_intr_establish(int, int, int (*)(void *), void *,
167 		    char *);
168 void		*ampintc_intr_establish_ext(int, int, int (*)(void *), void *,
169 		    char *);
170 void		*ampintc_intr_establish_fdt(void *, int *, int,
171 		    int (*)(void *), void *, char *);
172 void		 ampintc_intr_disestablish(void *);
173 void		 ampintc_irq_handler(void *);
174 const char	*ampintc_intr_string(void *);
175 uint32_t	 ampintc_iack(void);
176 void		 ampintc_eoi(uint32_t);
177 void		 ampintc_set_priority(int, int);
178 void		 ampintc_intr_enable(int);
179 void		 ampintc_intr_disable(int);
180 void		 ampintc_route(int, int , int);
181 
182 struct cfattach	ampintc_ca = {
183 	sizeof (struct ampintc_softc), ampintc_match, ampintc_attach
184 };
185 
186 struct cfdriver ampintc_cd = {
187 	NULL, "ampintc", DV_DULL
188 };
189 
190 static char *ampintc_compatibles[] = {
191 	"arm,cortex-a7-gic",
192 	"arm,cortex-a9-gic",
193 	"arm,cortex-a15-gic",
194 	NULL
195 };
196 
197 int
198 ampintc_match(struct device *parent, void *cfdata, void *aux)
199 {
200 	struct fdt_attach_args *faa = aux;
201 	int i;
202 
203 	for (i = 0; ampintc_compatibles[i]; i++)
204 		if (OF_is_compatible(faa->fa_node, ampintc_compatibles[i]))
205 			return (1);
206 
207 	return (0);
208 }
209 
210 void
211 ampintc_attach(struct device *parent, struct device *self, void *aux)
212 {
213 	struct ampintc_softc *sc = (struct ampintc_softc *)self;
214 	struct fdt_attach_args *faa = aux;
215 	int i, nintr;
216 
217 	ampintc = sc;
218 
219 	arm_init_smask();
220 
221 	sc->sc_iot = faa->fa_iot;
222 
223 	/* First row: ICD */
224 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
225 	    faa->fa_reg[0].size, 0, &sc->sc_d_ioh))
226 		panic("%s: ICD bus_space_map failed!", __func__);
227 
228 	/* Second row: ICP */
229 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
230 	    faa->fa_reg[1].size, 0, &sc->sc_p_ioh))
231 		panic("%s: ICP bus_space_map failed!", __func__);
232 
233 	evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
234 
235 	nintr = 32 * (bus_space_read_4(sc->sc_iot, sc->sc_d_ioh,
236 	    ICD_ICTR) & ICD_ICTR_ITL_M);
237 	nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
238 	sc->sc_nintr = nintr;
239 	printf(" nirq %d\n", nintr);
240 
241 	/* Disable all interrupts, clear all pending */
242 	for (i = 0; i < nintr/32; i++) {
243 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
244 		    ICD_ICERn(i*32), ~0);
245 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
246 		    ICD_ICPRn(i*32), ~0);
247 	}
248 	for (i = 0; i < nintr; i++) {
249 		/* lowest priority ?? */
250 		bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i), 0xff);
251 		/* target no cpus */
252 		bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(i), 0);
253 	}
254 	for (i = 2; i < nintr/16; i++) {
255 		/* irq 32 - N */
256 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(i*16), 0);
257 	}
258 
259 	/* software reset of the part? */
260 	/* set protection bit (kernel only)? */
261 
262 	/* XXX - check power saving bit */
263 
264 	sc->sc_ampintc_handler = mallocarray(nintr,
265 	    sizeof(*sc->sc_ampintc_handler), M_DEVBUF, M_ZERO | M_NOWAIT);
266 	for (i = 0; i < nintr; i++) {
267 		TAILQ_INIT(&sc->sc_ampintc_handler[i].iq_list);
268 	}
269 
270 	ampintc_setipl(IPL_HIGH);  /* XXX ??? */
271 	ampintc_calc_mask();
272 
273 	/* insert self as interrupt handler */
274 	arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx,
275 	    ampintc_setipl, ampintc_intr_establish_ext,
276 	    ampintc_intr_disestablish, ampintc_intr_string, ampintc_irq_handler);
277 
278 	/* enable interrupts */
279 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_DCR, 3);
280 	bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1);
281 	enable_interrupts(PSR_I);
282 
283 	sc->sc_ic.ic_node = faa->fa_node;
284 	sc->sc_ic.ic_cookie = self;
285 	sc->sc_ic.ic_establish = ampintc_intr_establish_fdt;
286 	sc->sc_ic.ic_disestablish = ampintc_intr_disestablish;
287 	arm_intr_register_fdt(&sc->sc_ic);
288 }
289 
290 void
291 ampintc_set_priority(int irq, int pri)
292 {
293 	struct ampintc_softc	*sc = ampintc;
294 	uint32_t		 prival;
295 
296 	/*
297 	 * We only use 16 (13 really) interrupt priorities,
298 	 * and a CPU is only required to implement bit 4-7 of each field
299 	 * so shift into the top bits.
300 	 * also low values are higher priority thus IPL_HIGH - pri
301 	 */
302 	prival = (IPL_HIGH - pri) << ICMIPMR_SH;
303 	bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival);
304 }
305 
306 void
307 ampintc_setipl(int new)
308 {
309 	struct cpu_info		*ci = curcpu();
310 	struct ampintc_softc	*sc = ampintc;
311 	int			 psw;
312 
313 	/* disable here is only to keep hardware in sync with ci->ci_cpl */
314 	psw = disable_interrupts(PSR_I);
315 	ci->ci_cpl = new;
316 
317 	/* low values are higher priority thus IPL_HIGH - pri */
318 	bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR,
319 	    (IPL_HIGH - new) << ICMIPMR_SH);
320 	restore_interrupts(psw);
321 }
322 
323 void
324 ampintc_intr_enable(int irq)
325 {
326 	struct ampintc_softc	*sc = ampintc;
327 
328 #ifdef DEBUG
329 	printf("enable irq %d register %x bitmask %08x\n",
330 	    irq, ICD_ISERn(irq), 1 << IRQ_TO_REG32BIT(irq));
331 #endif
332 
333 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq),
334 	    1 << IRQ_TO_REG32BIT(irq));
335 }
336 
337 void
338 ampintc_intr_disable(int irq)
339 {
340 	struct ampintc_softc	*sc = ampintc;
341 
342 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq),
343 	    1 << IRQ_TO_REG32BIT(irq));
344 }
345 
346 
347 void
348 ampintc_calc_mask(void)
349 {
350 	struct cpu_info		*ci = curcpu();
351         struct ampintc_softc	*sc = ampintc;
352 	struct intrhand		*ih;
353 	int			 irq;
354 
355 	for (irq = 0; irq < sc->sc_nintr; irq++) {
356 		int max = IPL_NONE;
357 		int min = IPL_HIGH;
358 		TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list,
359 		    ih_list) {
360 			if (ih->ih_ipl > max)
361 				max = ih->ih_ipl;
362 
363 			if (ih->ih_ipl < min)
364 				min = ih->ih_ipl;
365 		}
366 
367 		if (sc->sc_ampintc_handler[irq].iq_irq == max) {
368 			continue;
369 		}
370 		sc->sc_ampintc_handler[irq].iq_irq = max;
371 
372 		if (max == IPL_NONE)
373 			min = IPL_NONE;
374 
375 		/* Enable interrupts at lower levels, clear -> enable */
376 		/* Set interrupt priority/enable */
377 		if (min != IPL_NONE) {
378 			ampintc_set_priority(irq, min);
379 			ampintc_intr_enable(irq);
380 			ampintc_route(irq, IRQ_ENABLE, 0);
381 		} else {
382 			ampintc_intr_disable(irq);
383 			ampintc_route(irq, IRQ_DISABLE, 0);
384 
385 		}
386 	}
387 	ampintc_setipl(ci->ci_cpl);
388 }
389 
390 void
391 ampintc_splx(int new)
392 {
393 	struct cpu_info *ci = curcpu();
394 
395 	if (ci->ci_ipending & arm_smask[new])
396 		arm_do_pending_intr(new);
397 
398 	ampintc_setipl(new);
399 }
400 
401 int
402 ampintc_spllower(int new)
403 {
404 	struct cpu_info *ci = curcpu();
405 	int old = ci->ci_cpl;
406 	ampintc_splx(new);
407 	return (old);
408 }
409 
410 int
411 ampintc_splraise(int new)
412 {
413 	struct cpu_info *ci = curcpu();
414 	int old;
415 	old = ci->ci_cpl;
416 
417 	/*
418 	 * setipl must always be called because there is a race window
419 	 * where the variable is updated before the mask is set
420 	 * an interrupt occurs in that window without the mask always
421 	 * being set, the hardware might not get updated on the next
422 	 * splraise completely messing up spl protection.
423 	 */
424 	if (old > new)
425 		new = old;
426 
427 	ampintc_setipl(new);
428 
429 	return (old);
430 }
431 
432 
433 uint32_t
434 ampintc_iack(void)
435 {
436 	uint32_t intid;
437 	struct ampintc_softc	*sc = ampintc;
438 
439 	intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR);
440 
441 	return (intid);
442 }
443 
444 void
445 ampintc_eoi(uint32_t eoi)
446 {
447 	struct ampintc_softc	*sc = ampintc;
448 
449 	bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPEOIR, eoi);
450 }
451 
452 void
453 ampintc_route(int irq, int enable, int cpu)
454 {
455 	uint8_t  val;
456 	struct ampintc_softc	*sc = ampintc;
457 
458 	val = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq));
459 	if (enable == IRQ_ENABLE)
460 		val |= (1 << cpu);
461 	else
462 		val &= ~(1 << cpu);
463 	bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq), val);
464 }
465 
466 void
467 ampintc_irq_handler(void *frame)
468 {
469 	struct ampintc_softc	*sc = ampintc;
470 	struct intrhand		*ih;
471 	void			*arg;
472 	uint32_t		 iack_val;
473 	int			 irq, pri, s;
474 
475 	iack_val = ampintc_iack();
476 #ifdef DEBUG_INTC
477 	if (iack_val != 27)
478 		printf("irq  %d fired\n", iack_val);
479 	else {
480 		static int cnt = 0;
481 		if ((cnt++ % 100) == 0) {
482 			printf("irq  %d fired * _100\n", iack_val);
483 #ifdef DDB
484 			Debugger();
485 #endif
486 		}
487 
488 	}
489 #endif
490 
491 	if (iack_val == 1023) {
492 		sc->sc_spur.ec_count++;
493 		return;
494 	}
495 	irq = iack_val & ((1 << sc->sc_nintr) - 1);
496 
497 	pri = sc->sc_ampintc_handler[irq].iq_irq;
498 	s = ampintc_splraise(pri);
499 	TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, ih_list) {
500 		if (ih->ih_arg != 0)
501 			arg = ih->ih_arg;
502 		else
503 			arg = frame;
504 
505 		if (ih->ih_func(arg))
506 			ih->ih_count.ec_count++;
507 
508 	}
509 	ampintc_eoi(iack_val);
510 
511 	ampintc_splx(s);
512 }
513 
514 void *
515 ampintc_intr_establish_ext(int irqno, int level, int (*func)(void *),
516     void *arg, char *name)
517 {
518 	return ampintc_intr_establish(irqno+32, level, func, arg, name);
519 }
520 
521 void *
522 ampintc_intr_establish_fdt(void *cookie, int *cell, int level,
523     int (*func)(void *), void *arg, char *name)
524 {
525 	struct ampintc_softc	*sc = (struct ampintc_softc *)cookie;
526 	int			 irq;
527 
528 	/* 2nd cell contains the interrupt number */
529 	irq = cell[1];
530 
531 	/* 1st cell contains type: 0 SPI (32-X), 1 PPI (16-31) */
532 	if (cell[0] == 0)
533 		irq += 32;
534 	else if (cell[0] == 1)
535 		irq += 16;
536 	else
537 		panic("%s: bogus interrupt type", sc->sc_dev.dv_xname);
538 
539 	return ampintc_intr_establish(irq, level, func, arg, name);
540 }
541 
542 void *
543 ampintc_intr_establish(int irqno, int level, int (*func)(void *),
544     void *arg, char *name)
545 {
546 	struct ampintc_softc	*sc = ampintc;
547 	struct intrhand		*ih;
548 	int			 psw;
549 
550 	if (irqno < 0 || irqno >= sc->sc_nintr)
551 		panic("ampintc_intr_establish: bogus irqnumber %d: %s",
552 		     irqno, name);
553 
554 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
555 	ih->ih_func = func;
556 	ih->ih_arg = arg;
557 	ih->ih_ipl = level;
558 	ih->ih_irq = irqno;
559 	ih->ih_name = name;
560 
561 	psw = disable_interrupts(PSR_I);
562 
563 	TAILQ_INSERT_TAIL(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list);
564 
565 	if (name != NULL)
566 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
567 
568 #ifdef DEBUG_INTC
569 	printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level,
570 	    name);
571 #endif
572 	ampintc_calc_mask();
573 
574 	restore_interrupts(psw);
575 	return (ih);
576 }
577 
578 void
579 ampintc_intr_disestablish(void *cookie)
580 {
581 	struct ampintc_softc	*sc = ampintc;
582 	struct intrhand		*ih = cookie;
583 	int			 psw;
584 
585 #ifdef DEBUG_INTC
586 	printf("ampintc_intr_disestablish irq %d level %d [%s]\n",
587 	    ih->ih_irq, ih->ih_ipl, ih->ih_name);
588 #endif
589 
590 	psw = disable_interrupts(PSR_I);
591 
592 	TAILQ_REMOVE(&sc->sc_ampintc_handler[ih->ih_irq].iq_list, ih, ih_list);
593 	if (ih->ih_name != NULL)
594 		evcount_detach(&ih->ih_count);
595 	free(ih, M_DEVBUF, sizeof(*ih));
596 
597 	ampintc_calc_mask();
598 
599 	restore_interrupts(psw);
600 }
601 
602 const char *
603 ampintc_intr_string(void *cookie)
604 {
605 	struct intrhand *ih = (struct intrhand *)cookie;
606 	static char irqstr[1 + sizeof("ampintc irq ") + 4];
607 
608 	snprintf(irqstr, sizeof irqstr, "ampintc irq %d", ih->ih_irq);
609 	return irqstr;
610 }
611