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