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