xref: /openbsd/sys/arch/armv7/omap/intc.c (revision 9b7c3dbb)
1 /* $OpenBSD: intc.c,v 1.7 2016/08/06 18:21:34 patrick Exp $ */
2 /*
3  * Copyright (c) 2007,2009 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 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/queue.h>
21 #include <sys/malloc.h>
22 #include <sys/device.h>
23 #include <sys/evcount.h>
24 
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27 
28 #include <armv7/armv7/armv7var.h>
29 
30 #include <dev/ofw/openfirm.h>
31 #include <dev/ofw/fdt.h>
32 
33 #include "intc.h"
34 
35 #define INTC_NUM_IRQ intc_nirq
36 #define INTC_NUM_BANKS (intc_nirq/32)
37 #define INTC_MAX_IRQ 128
38 #define INTC_MAX_BANKS (INTC_MAX_IRQ/32)
39 
40 /* registers */
41 #define	INTC_REVISION		0x00	/* R */
42 #define	INTC_SYSCONFIG		0x10	/* RW */
43 #define		INTC_SYSCONFIG_AUTOIDLE		0x1
44 #define		INTC_SYSCONFIG_SOFTRESET	0x2
45 #define	INTC_SYSSTATUS		0x14	/* R */
46 #define		INTC_SYSSYSTATUS_RESETDONE	0x1
47 #define	INTC_SIR_IRQ		0x40	/* R */
48 #define	INTC_SIR_FIQ		0x44	/* R */
49 #define	INTC_CONTROL		0x48	/* RW */
50 #define		INTC_CONTROL_NEWIRQ	0x1
51 #define		INTC_CONTROL_NEWFIQ	0x2
52 #define		INTC_CONTROL_GLOBALMASK	0x1
53 #define	INTC_PROTECTION		0x4c	/* RW */
54 #define		INTC_PROTECTION_PROT 1	/* only privileged mode */
55 #define	INTC_IDLE		0x50	/* RW */
56 
57 #define INTC_IRQ_TO_REG(i)	(((i) >> 5) & 0x3)
58 #define INTC_IRQ_TO_REGi(i)	((i) & 0x1f)
59 #define	INTC_ITRn(i)		0x80+(0x20*i)+0x00	/* R */
60 #define	INTC_MIRn(i)		0x80+(0x20*i)+0x04	/* RW */
61 #define	INTC_CLEARn(i)		0x80+(0x20*i)+0x08	/* RW */
62 #define	INTC_SETn(i)		0x80+(0x20*i)+0x0c	/* RW */
63 #define	INTC_ISR_SETn(i)	0x80+(0x20*i)+0x10	/* RW */
64 #define	INTC_ISR_CLEARn(i)	0x80+(0x20*i)+0x14	/* RW */
65 #define INTC_PENDING_IRQn(i)	0x80+(0x20*i)+0x18	/* R */
66 #define INTC_PENDING_FIQn(i)	0x80+(0x20*i)+0x1c	/* R */
67 
68 #define INTC_ILRn(i)		0x100+(4*i)
69 #define		INTC_ILR_IRQ	0x0		/* not of FIQ */
70 #define		INTC_ILR_FIQ	0x1
71 #define		INTC_ILR_PRIs(pri)	((pri) << 2)
72 #define		INTC_ILR_PRI(reg)	(((reg) >> 2) & 0x2f)
73 #define		INTC_MIN_PRI	63
74 #define		INTC_STD_PRI	32
75 #define		INTC_MAX_PRI	0
76 
77 struct intrhand {
78 	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
79 	int (*ih_func)(void *);		/* handler */
80 	void *ih_arg;			/* arg for handler */
81 	int ih_ipl;			/* IPL_* */
82 	int ih_irq;			/* IRQ number */
83 	struct evcount	ih_count;
84 	char *ih_name;
85 };
86 
87 struct intrq {
88 	TAILQ_HEAD(, intrhand) iq_list;	/* handler list */
89 	int iq_irq;			/* IRQ to mask while handling */
90 	int iq_levels;			/* IPL_*'s this IRQ has */
91 	int iq_ist;			/* share type */
92 };
93 
94 volatile int softint_pending;
95 
96 struct intrq intc_handler[INTC_MAX_IRQ];
97 u_int32_t intc_smask[NIPL];
98 u_int32_t intc_imask[INTC_MAX_BANKS][NIPL];
99 struct interrupt_controller intc_ic;
100 
101 bus_space_tag_t		intc_iot;
102 bus_space_handle_t	intc_ioh;
103 int			intc_nirq;
104 
105 int	intc_match(struct device *, void *, void *);
106 void	intc_attach(struct device *, struct device *, void *);
107 int	intc_spllower(int new);
108 int	intc_splraise(int new);
109 void	intc_setipl(int new);
110 void	intc_calc_mask(void);
111 void	*intc_intr_establish_fdt(void *, int *, int, int (*)(void *),
112 	    void *, char *);
113 
114 struct cfattach	intc_ca = {
115 	sizeof (struct device), intc_match, intc_attach
116 };
117 
118 struct cfdriver intc_cd = {
119 	NULL, "intc", DV_DULL
120 };
121 
122 int intc_attached = 0;
123 
124 int
125 intc_match(struct device *parent, void *match, void *aux)
126 {
127 	struct fdt_attach_args *faa = aux;
128 
129 	return (OF_is_compatible(faa->fa_node, "ti,omap3-intc") ||
130 	    OF_is_compatible(faa->fa_node, "ti,am33xx-intc"));
131 }
132 
133 void
134 intc_attach(struct device *parent, struct device *self, void *aux)
135 {
136 	struct fdt_attach_args *faa = aux;
137 	int i;
138 	u_int32_t rev;
139 
140 	intc_iot = faa->fa_iot;
141 	if (bus_space_map(intc_iot, faa->fa_reg[0].addr,
142 	    faa->fa_reg[0].size, 0, &intc_ioh))
143 		panic("intc_attach: bus_space_map failed!");
144 
145 	rev = bus_space_read_4(intc_iot, intc_ioh, INTC_REVISION);
146 
147 	printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf);
148 
149 	/* software reset of the part? */
150 	/* set protection bit (kernel only)? */
151 #if 0
152 	bus_space_write_4(intc_iot, intc_ioh, INTC_PROTECTION,
153 	     INTC_PROTECTION_PROT);
154 #endif
155 
156 	/* enable interface clock power saving mode */
157 	bus_space_write_4(intc_iot, intc_ioh, INTC_SYSCONFIG,
158 	    INTC_SYSCONFIG_AUTOIDLE);
159 
160 	if (OF_is_compatible(faa->fa_node, "ti,am33xx-intc"))
161 		intc_nirq = 128;
162 	else
163 		intc_nirq = 96;
164 
165 	/* mask all interrupts */
166 	for (i = 0; i < INTC_NUM_BANKS; i++)
167 		bus_space_write_4(intc_iot, intc_ioh, INTC_MIRn(i), 0xffffffff);
168 
169 	for (i = 0; i < INTC_NUM_IRQ; i++) {
170 		bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(i),
171 		    INTC_ILR_PRIs(INTC_MIN_PRI)|INTC_ILR_IRQ);
172 
173 		TAILQ_INIT(&intc_handler[i].iq_list);
174 	}
175 
176 	intc_calc_mask();
177 	bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
178 	    INTC_CONTROL_NEWIRQ);
179 
180 	intc_attached = 1;
181 
182 	/* insert self as interrupt handler */
183 	arm_set_intr_handler(intc_splraise, intc_spllower, intc_splx,
184 	    intc_setipl,
185 	    intc_intr_establish, intc_intr_disestablish, intc_intr_string,
186 	    intc_irq_handler);
187 
188 	intc_setipl(IPL_HIGH);  /* XXX ??? */
189 	enable_interrupts(PSR_I);
190 
191 	intc_ic.ic_node = faa->fa_node;
192 	intc_ic.ic_establish = intc_intr_establish_fdt;
193 	arm_intr_register_fdt(&intc_ic);
194 }
195 
196 void
197 intc_calc_mask(void)
198 {
199 	struct cpu_info *ci = curcpu();
200 	int irq;
201 	struct intrhand *ih;
202 	int i;
203 
204 	for (irq = 0; irq < INTC_NUM_IRQ; irq++) {
205 		int max = IPL_NONE;
206 		int min = IPL_HIGH;
207 		TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) {
208 			if (ih->ih_ipl > max)
209 				max = ih->ih_ipl;
210 
211 			if (ih->ih_ipl < min)
212 				min = ih->ih_ipl;
213 		}
214 
215 		intc_handler[irq].iq_irq = max;
216 
217 		if (max == IPL_NONE)
218 			min = IPL_NONE;
219 
220 #ifdef DEBUG_INTC
221 		if (min != IPL_NONE) {
222 			printf("irq %d to block at %d %d reg %d bit %d\n",
223 			    irq, max, min, INTC_IRQ_TO_REG(irq),
224 			    INTC_IRQ_TO_REGi(irq));
225 		}
226 #endif
227 		/* Enable interrupts at lower levels, clear -> enable */
228 		for (i = 0; i < min; i++)
229 			intc_imask[INTC_IRQ_TO_REG(irq)][i] &=
230 			    ~(1 << INTC_IRQ_TO_REGi(irq));
231 		for (; i <= IPL_HIGH; i++)
232 			intc_imask[INTC_IRQ_TO_REG(irq)][i] |=
233 			    1 << INTC_IRQ_TO_REGi(irq);
234 		/* XXX - set enable/disable, priority */
235 		bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(irq),
236 		    INTC_ILR_PRIs(NIPL-max)|INTC_ILR_IRQ);
237 	}
238 	arm_init_smask();
239 	intc_setipl(ci->ci_cpl);
240 }
241 
242 void
243 intc_splx(int new)
244 {
245 	struct cpu_info *ci = curcpu();
246 	intc_setipl(new);
247 
248 	if (ci->ci_ipending & arm_smask[ci->ci_cpl])
249 		arm_do_pending_intr(ci->ci_cpl);
250 }
251 
252 int
253 intc_spllower(int new)
254 {
255 	struct cpu_info *ci = curcpu();
256 	int old = ci->ci_cpl;
257 	intc_splx(new);
258 	return (old);
259 }
260 
261 int
262 intc_splraise(int new)
263 {
264 	struct cpu_info *ci = curcpu();
265 	int old;
266 	old = ci->ci_cpl;
267 
268 	/*
269 	 * setipl must always be called because there is a race window
270 	 * where the variable is updated before the mask is set
271 	 * an interrupt occurs in that window without the mask always
272 	 * being set, the hardware might not get updated on the next
273 	 * splraise completely messing up spl protection.
274 	 */
275 	if (old > new)
276 		new = old;
277 
278 	intc_setipl(new);
279 
280 	return (old);
281 }
282 
283 void
284 intc_setipl(int new)
285 {
286 	struct cpu_info *ci = curcpu();
287 	int i;
288 	int psw;
289 	if (intc_attached == 0)
290 		return;
291 
292 	psw = disable_interrupts(PSR_I);
293 #if 0
294 	{
295 		volatile static int recursed = 0;
296 		if (recursed == 0) {
297 			recursed = 1;
298 			if (new != 12)
299 				printf("setipl %d\n", new);
300 			recursed = 0;
301 		}
302 	}
303 #endif
304 	ci->ci_cpl = new;
305 	for (i = 0; i < INTC_NUM_BANKS; i++)
306 		bus_space_write_4(intc_iot, intc_ioh,
307 		    INTC_MIRn(i), intc_imask[i][new]);
308 	bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
309 	    INTC_CONTROL_NEWIRQ);
310 	restore_interrupts(psw);
311 }
312 
313 void
314 intc_intr_bootstrap(vaddr_t addr)
315 {
316 	int i, j;
317 	extern struct bus_space armv7_bs_tag;
318 	intc_iot = &armv7_bs_tag;
319 	intc_ioh = addr;
320 	for (i = 0; i < INTC_NUM_BANKS; i++)
321 		for (j = 0; j < NIPL; j++)
322 			intc_imask[i][j] = 0xffffffff;
323 }
324 
325 void
326 intc_irq_handler(void *frame)
327 {
328 	int irq, pri, s;
329 	struct intrhand *ih;
330 	void *arg;
331 
332 	irq = bus_space_read_4(intc_iot, intc_ioh, INTC_SIR_IRQ);
333 #ifdef DEBUG_INTC
334 	printf("irq %d fired\n", irq);
335 #endif
336 
337 	pri = intc_handler[irq].iq_irq;
338 	s = intc_splraise(pri);
339 	TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) {
340 		if (ih->ih_arg != 0)
341 			arg = ih->ih_arg;
342 		else
343 			arg = frame;
344 
345 		if (ih->ih_func(arg))
346 			ih->ih_count.ec_count++;
347 
348 	}
349 	bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
350 	    INTC_CONTROL_NEWIRQ);
351 
352 	intc_splx(s);
353 }
354 
355 void *
356 intc_intr_establish(int irqno, int level, int (*func)(void *),
357     void *arg, char *name)
358 {
359 	int psw;
360 	struct intrhand *ih;
361 
362 	if (irqno < 0 || irqno >= INTC_NUM_IRQ)
363 		panic("intc_intr_establish: bogus irqnumber %d: %s",
364 		     irqno, name);
365 	psw = disable_interrupts(PSR_I);
366 
367 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
368 	ih->ih_func = func;
369 	ih->ih_arg = arg;
370 	ih->ih_ipl = level;
371 	ih->ih_irq = irqno;
372 	ih->ih_name = name;
373 
374 	TAILQ_INSERT_TAIL(&intc_handler[irqno].iq_list, ih, ih_list);
375 
376 	if (name != NULL)
377 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
378 
379 #ifdef DEBUG_INTC
380 	printf("intc_intr_establish irq %d level %d [%s]\n", irqno, level,
381 	    name);
382 #endif
383 	intc_calc_mask();
384 
385 	restore_interrupts(psw);
386 	return (ih);
387 }
388 
389 void *
390 intc_intr_establish_fdt(void *cookie, int *cell, int level,
391     int (*func)(void *), void *arg, char *name)
392 {
393 	return intc_intr_establish(cell[0], level, func, arg, name);
394 }
395 
396 void
397 intc_intr_disestablish(void *cookie)
398 {
399 	int psw;
400 	struct intrhand *ih = cookie;
401 	int irqno = ih->ih_irq;
402 	psw = disable_interrupts(PSR_I);
403 	TAILQ_REMOVE(&intc_handler[irqno].iq_list, ih, ih_list);
404 	if (ih->ih_name != NULL)
405 		evcount_detach(&ih->ih_count);
406 	free(ih, M_DEVBUF, 0);
407 	restore_interrupts(psw);
408 }
409 
410 const char *
411 intc_intr_string(void *cookie)
412 {
413 	return "huh?";
414 }
415 
416 
417 #if 0
418 int intc_tst(void *a);
419 
420 int
421 intc_tst(void *a)
422 {
423 	printf("inct_tst called\n");
424 	bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2);
425 	return 1;
426 }
427 
428 void intc_test(void);
429 void intc_test(void)
430 {
431 	void * ih;
432 	printf("about to register handler\n");
433 	ih = intc_intr_establish(1, IPL_BIO, intc_tst, NULL, "intctst");
434 
435 	printf("about to set bit\n");
436 	bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_SETn(0), 2);
437 
438 	printf("about to clear bit\n");
439 	bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2);
440 
441 	printf("about to remove handler\n");
442 	intc_intr_disestablish(ih);
443 
444 	printf("done\n");
445 }
446 #endif
447