1 /* $OpenBSD: intc.c,v 1.15 2024/06/26 01:40:49 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
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 struct intrq intc_handler[INTC_MAX_IRQ];
95 u_int32_t intc_smask[NIPL];
96 u_int32_t intc_imask[INTC_MAX_BANKS][NIPL];
97 struct interrupt_controller intc_ic;
98
99 bus_space_tag_t intc_iot;
100 bus_space_handle_t intc_ioh;
101 int intc_nirq;
102
103 int intc_match(struct device *, void *, void *);
104 void intc_attach(struct device *, struct device *, void *);
105 int intc_spllower(int new);
106 int intc_splraise(int new);
107 void intc_setipl(int new);
108 void intc_calc_mask(void);
109 void *intc_intr_establish_fdt(void *, int *, int, struct cpu_info *,
110 int (*)(void *), void *, char *);
111
112 const struct cfattach intc_ca = {
113 sizeof (struct device), intc_match, intc_attach
114 };
115
116 struct cfdriver intc_cd = {
117 NULL, "intc", DV_DULL
118 };
119
120 int intc_attached = 0;
121
122 int
intc_match(struct device * parent,void * match,void * aux)123 intc_match(struct device *parent, void *match, void *aux)
124 {
125 struct fdt_attach_args *faa = aux;
126
127 return (OF_is_compatible(faa->fa_node, "ti,omap3-intc") ||
128 OF_is_compatible(faa->fa_node, "ti,am33xx-intc"));
129 }
130
131 void
intc_attach(struct device * parent,struct device * self,void * aux)132 intc_attach(struct device *parent, struct device *self, void *aux)
133 {
134 struct fdt_attach_args *faa = aux;
135 int i;
136 u_int32_t rev;
137
138 intc_iot = faa->fa_iot;
139 if (bus_space_map(intc_iot, faa->fa_reg[0].addr,
140 faa->fa_reg[0].size, 0, &intc_ioh))
141 panic("intc_attach: bus_space_map failed!");
142
143 rev = bus_space_read_4(intc_iot, intc_ioh, INTC_REVISION);
144
145 printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf);
146
147 /* software reset of the part? */
148 /* set protection bit (kernel only)? */
149 #if 0
150 bus_space_write_4(intc_iot, intc_ioh, INTC_PROTECTION,
151 INTC_PROTECTION_PROT);
152 #endif
153
154 /* enable interface clock power saving mode */
155 bus_space_write_4(intc_iot, intc_ioh, INTC_SYSCONFIG,
156 INTC_SYSCONFIG_AUTOIDLE);
157
158 if (OF_is_compatible(faa->fa_node, "ti,am33xx-intc"))
159 intc_nirq = 128;
160 else
161 intc_nirq = 96;
162
163 /* mask all interrupts */
164 for (i = 0; i < INTC_NUM_BANKS; i++)
165 bus_space_write_4(intc_iot, intc_ioh, INTC_MIRn(i), 0xffffffff);
166
167 for (i = 0; i < INTC_NUM_IRQ; i++) {
168 bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(i),
169 INTC_ILR_PRIs(INTC_MIN_PRI)|INTC_ILR_IRQ);
170
171 TAILQ_INIT(&intc_handler[i].iq_list);
172 }
173
174 intc_calc_mask();
175 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
176 INTC_CONTROL_NEWIRQ);
177
178 intc_attached = 1;
179
180 /* insert self as interrupt handler */
181 arm_set_intr_handler(intc_splraise, intc_spllower, intc_splx,
182 intc_setipl,
183 intc_intr_establish, intc_intr_disestablish, intc_intr_string,
184 intc_irq_handler);
185
186 intc_setipl(IPL_HIGH); /* XXX ??? */
187 enable_interrupts(PSR_I);
188
189 intc_ic.ic_node = faa->fa_node;
190 intc_ic.ic_establish = intc_intr_establish_fdt;
191 arm_intr_register_fdt(&intc_ic);
192 }
193
194 void
intc_calc_mask(void)195 intc_calc_mask(void)
196 {
197 struct cpu_info *ci = curcpu();
198 int irq;
199 struct intrhand *ih;
200 int i;
201
202 for (irq = 0; irq < INTC_NUM_IRQ; irq++) {
203 int max = IPL_NONE;
204 int min = IPL_HIGH;
205 TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) {
206 if (ih->ih_ipl > max)
207 max = ih->ih_ipl;
208
209 if (ih->ih_ipl < min)
210 min = ih->ih_ipl;
211 }
212
213 intc_handler[irq].iq_irq = max;
214
215 if (max == IPL_NONE)
216 min = IPL_NONE;
217
218 #ifdef DEBUG_INTC
219 if (min != IPL_NONE) {
220 printf("irq %d to block at %d %d reg %d bit %d\n",
221 irq, max, min, INTC_IRQ_TO_REG(irq),
222 INTC_IRQ_TO_REGi(irq));
223 }
224 #endif
225 /* Enable interrupts at lower levels, clear -> enable */
226 for (i = 0; i < min; i++)
227 intc_imask[INTC_IRQ_TO_REG(irq)][i] &=
228 ~(1 << INTC_IRQ_TO_REGi(irq));
229 for (; i <= IPL_HIGH; i++)
230 intc_imask[INTC_IRQ_TO_REG(irq)][i] |=
231 1 << INTC_IRQ_TO_REGi(irq);
232 /* XXX - set enable/disable, priority */
233 bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(irq),
234 INTC_ILR_PRIs(NIPL-max)|INTC_ILR_IRQ);
235 }
236 arm_init_smask();
237 intc_setipl(ci->ci_cpl);
238 }
239
240 void
intc_splx(int new)241 intc_splx(int new)
242 {
243 struct cpu_info *ci = curcpu();
244 intc_setipl(new);
245
246 if (ci->ci_ipending & arm_smask[ci->ci_cpl])
247 arm_do_pending_intr(ci->ci_cpl);
248 }
249
250 int
intc_spllower(int new)251 intc_spllower(int new)
252 {
253 struct cpu_info *ci = curcpu();
254 int old = ci->ci_cpl;
255 intc_splx(new);
256 return (old);
257 }
258
259 int
intc_splraise(int new)260 intc_splraise(int new)
261 {
262 struct cpu_info *ci = curcpu();
263 int old;
264 old = ci->ci_cpl;
265
266 /*
267 * setipl must always be called because there is a race window
268 * where the variable is updated before the mask is set
269 * an interrupt occurs in that window without the mask always
270 * being set, the hardware might not get updated on the next
271 * splraise completely messing up spl protection.
272 */
273 if (old > new)
274 new = old;
275
276 intc_setipl(new);
277
278 return (old);
279 }
280
281 void
intc_setipl(int new)282 intc_setipl(int new)
283 {
284 struct cpu_info *ci = curcpu();
285 int i;
286 int psw;
287 if (intc_attached == 0)
288 return;
289
290 psw = disable_interrupts(PSR_I);
291 #if 0
292 {
293 volatile static int recursed = 0;
294 if (recursed == 0) {
295 recursed = 1;
296 if (new != 12)
297 printf("setipl %d\n", new);
298 recursed = 0;
299 }
300 }
301 #endif
302 ci->ci_cpl = new;
303 for (i = 0; i < INTC_NUM_BANKS; i++)
304 bus_space_write_4(intc_iot, intc_ioh,
305 INTC_MIRn(i), intc_imask[i][new]);
306 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
307 INTC_CONTROL_NEWIRQ);
308 restore_interrupts(psw);
309 }
310
311 void
intc_irq_handler(void * frame)312 intc_irq_handler(void *frame)
313 {
314 int irq, pri, s;
315 struct intrhand *ih;
316 void *arg;
317
318 irq = bus_space_read_4(intc_iot, intc_ioh, INTC_SIR_IRQ);
319 #ifdef DEBUG_INTC
320 printf("irq %d fired\n", irq);
321 #endif
322
323 pri = intc_handler[irq].iq_irq;
324 s = intc_splraise(pri);
325 TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) {
326 if (ih->ih_arg)
327 arg = ih->ih_arg;
328 else
329 arg = frame;
330
331 if (ih->ih_func(arg))
332 ih->ih_count.ec_count++;
333
334 }
335 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
336 INTC_CONTROL_NEWIRQ);
337
338 intc_splx(s);
339 }
340
341 void *
intc_intr_establish(int irqno,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)342 intc_intr_establish(int irqno, int level, struct cpu_info *ci,
343 int (*func)(void *), void *arg, char *name)
344 {
345 int psw;
346 struct intrhand *ih;
347
348 if (irqno < 0 || irqno >= INTC_NUM_IRQ)
349 panic("intc_intr_establish: bogus irqnumber %d: %s",
350 irqno, name);
351
352 if (ci == NULL)
353 ci = &cpu_info_primary;
354 else if (!CPU_IS_PRIMARY(ci))
355 return NULL;
356
357 psw = disable_interrupts(PSR_I);
358
359 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
360 ih->ih_func = func;
361 ih->ih_arg = arg;
362 ih->ih_ipl = level & IPL_IRQMASK;
363 ih->ih_irq = irqno;
364 ih->ih_name = name;
365
366 TAILQ_INSERT_TAIL(&intc_handler[irqno].iq_list, ih, ih_list);
367
368 if (name != NULL)
369 evcount_attach(&ih->ih_count, name, &ih->ih_irq);
370
371 #ifdef DEBUG_INTC
372 printf("intc_intr_establish irq %d level %d [%s]\n", irqno, level,
373 name);
374 #endif
375 intc_calc_mask();
376
377 restore_interrupts(psw);
378 return (ih);
379 }
380
381 void *
intc_intr_establish_fdt(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)382 intc_intr_establish_fdt(void *cookie, int *cell, int level,
383 struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
384 {
385 return intc_intr_establish(cell[0], level, ci, func, arg, name);
386 }
387
388 void
intc_intr_disestablish(void * cookie)389 intc_intr_disestablish(void *cookie)
390 {
391 int psw;
392 struct intrhand *ih = cookie;
393 int irqno = ih->ih_irq;
394 psw = disable_interrupts(PSR_I);
395 TAILQ_REMOVE(&intc_handler[irqno].iq_list, ih, ih_list);
396 if (ih->ih_name != NULL)
397 evcount_detach(&ih->ih_count);
398 free(ih, M_DEVBUF, 0);
399 restore_interrupts(psw);
400 }
401
402 const char *
intc_intr_string(void * cookie)403 intc_intr_string(void *cookie)
404 {
405 return "huh?";
406 }
407
408
409 #if 0
410 int intc_tst(void *a);
411
412 int
413 intc_tst(void *a)
414 {
415 printf("inct_tst called\n");
416 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2);
417 return 1;
418 }
419
420 void intc_test(void);
421 void
422 intc_test(void)
423 {
424 void * ih;
425 printf("about to register handler\n");
426 ih = intc_intr_establish(1, IPL_BIO, intc_tst, NULL, "intctst");
427
428 printf("about to set bit\n");
429 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_SETn(0), 2);
430
431 printf("about to clear bit\n");
432 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2);
433
434 printf("about to remove handler\n");
435 intc_intr_disestablish(ih);
436
437 printf("done\n");
438 }
439 #endif
440