initial commit, pull in sys/arch/armv7/omap
[bbb-pru.git] / intc.c
... / ...
CommitLineData
1/* $OpenBSD: intc.c,v 1.4 2016/01/31 00:14:50 jsg 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
70struct 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
80struct 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
87volatile int softint_pending;
88
89struct intrq intc_handler[INTC_MAX_IRQ];
90u_int32_t intc_smask[NIPL];
91u_int32_t intc_imask[INTC_MAX_BANKS][NIPL];
92
93bus_space_tag_t intc_iot;
94bus_space_handle_t intc_ioh;
95int intc_nirq;
96
97void intc_attach(struct device *, struct device *, void *);
98int intc_spllower(int new);
99int intc_splraise(int new);
100void intc_setipl(int new);
101void intc_calc_mask(void);
102
103struct cfattach intc_ca = {
104 sizeof (struct device), NULL, intc_attach
105};
106
107struct cfdriver intc_cd = {
108 NULL, "intc", DV_DULL
109};
110
111int intc_attached = 0;
112
113void
114intc_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(PSR_I);
174}
175
176void
177intc_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
222void
223intc_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
232int
233intc_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
241int
242intc_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
263void
264intc_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(PSR_I);
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
293void
294intc_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
305void
306intc_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
335void *
336intc_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(PSR_I);
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
373void
374intc_intr_disestablish(void *cookie)
375{
376 int psw;
377 struct intrhand *ih = cookie;
378 int irqno = ih->ih_irq;
379 psw = disable_interrupts(PSR_I);
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
387const char *
388intc_intr_string(void *cookie)
389{
390 return "huh?";
391}
392
393
394#if 0
395int intc_tst(void *a);
396
397int
398intc_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
405void intc_test(void);
406void 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