au_icu.c revision 1.1 1 1.1 simonb /* $NetBSD: au_icu.c,v 1.1 2002/07/29 15:39:11 simonb Exp $ */
2 1.1 simonb
3 1.1 simonb /*-
4 1.1 simonb * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 1.1 simonb * All rights reserved.
6 1.1 simonb *
7 1.1 simonb * This code is derived from software contributed to The NetBSD Foundation
8 1.1 simonb * by Jason R. Thorpe.
9 1.1 simonb *
10 1.1 simonb * Redistribution and use in source and binary forms, with or without
11 1.1 simonb * modification, are permitted provided that the following conditions
12 1.1 simonb * are met:
13 1.1 simonb * 1. Redistributions of source code must retain the above copyright
14 1.1 simonb * notice, this list of conditions and the following disclaimer.
15 1.1 simonb * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 simonb * notice, this list of conditions and the following disclaimer in the
17 1.1 simonb * documentation and/or other materials provided with the distribution.
18 1.1 simonb * 3. All advertising materials mentioning features or use of this software
19 1.1 simonb * must display the following acknowledgement:
20 1.1 simonb * This product includes software developed by the NetBSD
21 1.1 simonb * Foundation, Inc. and its contributors.
22 1.1 simonb * 4. Neither the name of The NetBSD Foundation nor the names of its
23 1.1 simonb * contributors may be used to endorse or promote products derived
24 1.1 simonb * from this software without specific prior written permission.
25 1.1 simonb *
26 1.1 simonb * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 1.1 simonb * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 1.1 simonb * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 1.1 simonb * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 1.1 simonb * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 1.1 simonb * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 1.1 simonb * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 1.1 simonb * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 1.1 simonb * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 1.1 simonb * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 1.1 simonb * POSSIBILITY OF SUCH DAMAGE.
37 1.1 simonb */
38 1.1 simonb
39 1.1 simonb /*
40 1.1 simonb * Interrupt support for the Alchemy Semiconductor Au1x00 CPUs.
41 1.1 simonb *
42 1.1 simonb * The Alchemy Semiconductor Au1x00's interrupts are wired to two internal
43 1.1 simonb * interrupt controllers.
44 1.1 simonb */
45 1.1 simonb
46 1.1 simonb #include "opt_ddb.h"
47 1.1 simonb
48 1.1 simonb #include <sys/param.h>
49 1.1 simonb #include <sys/queue.h>
50 1.1 simonb #include <sys/malloc.h>
51 1.1 simonb #include <sys/systm.h>
52 1.1 simonb #include <sys/device.h>
53 1.1 simonb #include <sys/kernel.h>
54 1.1 simonb
55 1.1 simonb #include <machine/bus.h>
56 1.1 simonb #include <machine/intr.h>
57 1.1 simonb
58 1.1 simonb #include <mips/locore.h>
59 1.1 simonb #include <mips/alchemy/include/aureg.h>
60 1.1 simonb #include <mips/alchemy/include/auvar.h>
61 1.1 simonb
62 1.1 simonb #if 0
63 1.1 simonb #include <evbmips/evbmips/clockvar.h>
64 1.1 simonb #include <mips/alchemy/include/aubusvar.h>
65 1.1 simonb #endif
66 1.1 simonb
67 1.1 simonb #include <dev/ic/mc146818reg.h>
68 1.1 simonb
69 1.1 simonb #define REGVAL(x) *((__volatile u_int32_t *)(MIPS_PHYS_TO_KSEG1((x))))
70 1.1 simonb
71 1.1 simonb /*
72 1.1 simonb * This is a mask of bits to clear in the SR when we go to a
73 1.1 simonb * given hardware interrupt priority level.
74 1.1 simonb */
75 1.1 simonb
76 1.1 simonb // #define ENABLE_AUMAC_HW1 /* XXX */
77 1.1 simonb #undef ENABLE_AUMAC_HW1 /* XXX */
78 1.1 simonb
79 1.1 simonb const u_int32_t ipl_sr_bits[_IPL_N] = {
80 1.1 simonb 0, /* 0: IPL_NONE */
81 1.1 simonb
82 1.1 simonb MIPS_SOFT_INT_MASK_0, /* 1: IPL_SOFT */
83 1.1 simonb
84 1.1 simonb MIPS_SOFT_INT_MASK_0, /* 2: IPL_SOFTCLOCK */
85 1.1 simonb
86 1.1 simonb MIPS_SOFT_INT_MASK_0, /* 3: IPL_SOFTNET */
87 1.1 simonb
88 1.1 simonb MIPS_SOFT_INT_MASK_0, /* 4: IPL_SOFTSERIAL */
89 1.1 simonb
90 1.1 simonb MIPS_SOFT_INT_MASK_0|
91 1.1 simonb MIPS_SOFT_INT_MASK_1|
92 1.1 simonb
93 1.1 simonb MIPS_SOFT_INT_MASK_0|
94 1.1 simonb MIPS_SOFT_INT_MASK_1|
95 1.1 simonb MIPS_INT_MASK_0, /* 5: IPL_BIO */
96 1.1 simonb
97 1.1 simonb MIPS_SOFT_INT_MASK_0|
98 1.1 simonb MIPS_SOFT_INT_MASK_1|
99 1.1 simonb MIPS_INT_MASK_0, /* 6: IPL_NET */
100 1.1 simonb
101 1.1 simonb MIPS_SOFT_INT_MASK_0|
102 1.1 simonb MIPS_SOFT_INT_MASK_1|
103 1.1 simonb MIPS_INT_MASK_0|
104 1.1 simonb #ifdef ENABLE_AUMAC_HW1
105 1.1 simonb MIPS_INT_MASK_1, /* 7: IPL_AUMAC */
106 1.1 simonb #else
107 1.1 simonb 0, /* 7: IPL_AUMAC */
108 1.1 simonb #endif
109 1.1 simonb
110 1.1 simonb MIPS_SOFT_INT_MASK_0|
111 1.1 simonb MIPS_SOFT_INT_MASK_1|
112 1.1 simonb MIPS_INT_MASK_0, /* 8: IPL_{SERIAL,TTY} */
113 1.1 simonb
114 1.1 simonb MIPS_SOFT_INT_MASK_0|
115 1.1 simonb MIPS_SOFT_INT_MASK_1|
116 1.1 simonb MIPS_INT_MASK_0|
117 1.1 simonb #ifdef ENABLE_AUMAC_HW1
118 1.1 simonb /* nothing */
119 1.1 simonb #else
120 1.1 simonb MIPS_INT_MASK_1| /* XXX */
121 1.1 simonb #endif
122 1.1 simonb MIPS_INT_MASK_2|
123 1.1 simonb MIPS_INT_MASK_3|
124 1.1 simonb MIPS_INT_MASK_4|
125 1.1 simonb MIPS_INT_MASK_5, /* 9: IPL_{CLOCK,HIGH} */
126 1.1 simonb
127 1.1 simonb MIPS_SOFT_INT_MASK_0|
128 1.1 simonb MIPS_SOFT_INT_MASK_1|
129 1.1 simonb MIPS_INT_MASK_0|
130 1.1 simonb MIPS_INT_MASK_1|
131 1.1 simonb MIPS_INT_MASK_2|
132 1.1 simonb MIPS_INT_MASK_3|
133 1.1 simonb MIPS_INT_MASK_4|
134 1.1 simonb MIPS_INT_MASK_5, /* 10: IPL_EXTREME */
135 1.1 simonb };
136 1.1 simonb
137 1.1 simonb /*
138 1.1 simonb * This is a mask of bits to clear in the SR when we go to a
139 1.1 simonb * given software interrupt priority level.
140 1.1 simonb * Hardware ipls are port/board specific.
141 1.1 simonb */
142 1.1 simonb const u_int32_t ipl_si_to_sr[_IPL_NSOFT] = {
143 1.1 simonb MIPS_SOFT_INT_MASK_0, /* IPL_SOFT */
144 1.1 simonb MIPS_SOFT_INT_MASK_0, /* IPL_SOFTCLOCK */
145 1.1 simonb MIPS_SOFT_INT_MASK_0, /* IPL_SOFTNET */
146 1.1 simonb MIPS_SOFT_INT_MASK_0, /* IPL_SOFTSERIAL */
147 1.1 simonb };
148 1.1 simonb
149 1.1 simonb #define NIRQS 64
150 1.1 simonb
151 1.1 simonb const char *au1000_intrnames[NIRQS] = {
152 1.1 simonb "uart0",
153 1.1 simonb "uart1",
154 1.1 simonb "uart2",
155 1.1 simonb "uart3",
156 1.1 simonb "ssi0",
157 1.1 simonb "ssi1",
158 1.1 simonb "dma0",
159 1.1 simonb "dma1",
160 1.1 simonb "dma2",
161 1.1 simonb "dma3",
162 1.1 simonb "dma4",
163 1.1 simonb "dma5",
164 1.1 simonb "dma6",
165 1.1 simonb "dma7",
166 1.1 simonb "pc0",
167 1.1 simonb "pc0 match1",
168 1.1 simonb "pc0 match2",
169 1.1 simonb "pc0 match3",
170 1.1 simonb "pc1",
171 1.1 simonb "pc1 match1",
172 1.1 simonb "pc1 match2",
173 1.1 simonb "pc1 match3",
174 1.1 simonb "irda tx",
175 1.1 simonb "irda rx",
176 1.1 simonb "usb intr",
177 1.1 simonb "usb suspend",
178 1.1 simonb "usb host",
179 1.1 simonb "ac97",
180 1.1 simonb "mac0",
181 1.1 simonb "mac1",
182 1.1 simonb "i2s",
183 1.1 simonb "ac97 cmd",
184 1.1 simonb
185 1.1 simonb "gpio 0",
186 1.1 simonb "gpio 1",
187 1.1 simonb "gpio 2",
188 1.1 simonb "gpio 3",
189 1.1 simonb "gpio 4",
190 1.1 simonb "gpio 5",
191 1.1 simonb "gpio 6",
192 1.1 simonb "gpio 7",
193 1.1 simonb "gpio 8",
194 1.1 simonb "gpio 9",
195 1.1 simonb "gpio 10",
196 1.1 simonb "gpio 11",
197 1.1 simonb "gpio 12",
198 1.1 simonb "gpio 13",
199 1.1 simonb "gpio 14",
200 1.1 simonb "gpio 15",
201 1.1 simonb "gpio 16",
202 1.1 simonb "gpio 17",
203 1.1 simonb "gpio 18",
204 1.1 simonb "gpio 19",
205 1.1 simonb "gpio 20",
206 1.1 simonb "gpio 21",
207 1.1 simonb "gpio 22",
208 1.1 simonb "gpio 23",
209 1.1 simonb "gpio 24",
210 1.1 simonb "gpio 25",
211 1.1 simonb "gpio 26",
212 1.1 simonb "gpio 27",
213 1.1 simonb "gpio 28",
214 1.1 simonb "gpio 29",
215 1.1 simonb "gpio 30",
216 1.1 simonb "gpio 31",
217 1.1 simonb };
218 1.1 simonb
219 1.1 simonb struct au1000_intrhead {
220 1.1 simonb struct evcnt intr_count;
221 1.1 simonb int intr_refcnt;
222 1.1 simonb };
223 1.1 simonb struct au1000_intrhead au1000_intrtab[NIRQS];
224 1.1 simonb
225 1.1 simonb #define NINTRS 4 /* MIPS INT0 - INT3 */
226 1.1 simonb
227 1.1 simonb struct au1000_cpuintr {
228 1.1 simonb LIST_HEAD(, evbmips_intrhand) cintr_list;
229 1.1 simonb struct evcnt cintr_count;
230 1.1 simonb };
231 1.1 simonb
232 1.1 simonb struct au1000_cpuintr au1000_cpuintrs[NINTRS];
233 1.1 simonb const char *au1000_cpuintrnames[NINTRS] = {
234 1.1 simonb "icu 0, req 0",
235 1.1 simonb "icu 0, req 1",
236 1.1 simonb "icu 1, req 0",
237 1.1 simonb "icu 1, req 1",
238 1.1 simonb };
239 1.1 simonb
240 1.1 simonb void
241 1.1 simonb au_intr_init(void)
242 1.1 simonb {
243 1.1 simonb int i;
244 1.1 simonb
245 1.1 simonb for (i = 0; i < NINTRS; i++) {
246 1.1 simonb LIST_INIT(&au1000_cpuintrs[i].cintr_list);
247 1.1 simonb evcnt_attach_dynamic(&au1000_cpuintrs[i].cintr_count,
248 1.1 simonb EVCNT_TYPE_INTR, NULL, "mips", au1000_cpuintrnames[i]);
249 1.1 simonb }
250 1.1 simonb
251 1.1 simonb evcnt_attach_static(&mips_int5_evcnt);
252 1.1 simonb
253 1.1 simonb for (i = 0; i < NIRQS; i++) {
254 1.1 simonb /* XXX steering - use an irqmap array? */
255 1.1 simonb
256 1.1 simonb au1000_intrtab[i].intr_refcnt = 0;
257 1.1 simonb evcnt_attach_dynamic(&au1000_intrtab[i].intr_count,
258 1.1 simonb EVCNT_TYPE_INTR, NULL, "au1000", au1000_intrnames[i]);
259 1.1 simonb }
260 1.1 simonb }
261 1.1 simonb
262 1.1 simonb void *
263 1.1 simonb au_intr_establish(int irq, int req, int level, int type,
264 1.1 simonb int (*func)(void *), void *arg)
265 1.1 simonb {
266 1.1 simonb struct evbmips_intrhand *ih;
267 1.1 simonb uint32_t icu_base;
268 1.1 simonb int cpu_intr, s;
269 1.1 simonb
270 1.1 simonb if (irq >= NIRQS)
271 1.1 simonb panic("au_intr_establish: bogus IRQ %d\n", irq);
272 1.1 simonb if (req > 1)
273 1.1 simonb panic("au_intr_establish: bogus request %d\n", req);
274 1.1 simonb
275 1.1 simonb ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
276 1.1 simonb if (ih == NULL)
277 1.1 simonb return (NULL);
278 1.1 simonb
279 1.1 simonb ih->ih_func = func;
280 1.1 simonb ih->ih_arg = arg;
281 1.1 simonb ih->ih_irq = irq;
282 1.1 simonb
283 1.1 simonb s = splhigh();
284 1.1 simonb
285 1.1 simonb /*
286 1.1 simonb * First, link it into the tables.
287 1.1 simonb * XXX do we want a separate list (really, should only be one item, not
288 1.1 simonb * a list anyway) per irq, not per cpu interrupt?
289 1.1 simonb */
290 1.1 simonb #ifdef ENABLE_AUMAC_HW1
291 1.1 simonb cpu_intr = (irq < 32 ? 0 : 2) + req;
292 1.1 simonb #else
293 1.1 simonb cpu_intr = (irq < 32 ? 0 : 2);
294 1.1 simonb #endif
295 1.1 simonb LIST_INSERT_HEAD(&au1000_cpuintrs[cpu_intr].cintr_list, ih, ih_q);
296 1.1 simonb
297 1.1 simonb /*
298 1.1 simonb * Now enable it.
299 1.1 simonb */
300 1.1 simonb if (au1000_intrtab[irq].intr_refcnt++ == 0) {
301 1.1 simonb icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
302 1.1 simonb
303 1.1 simonb irq &= 31; /* throw away high bit if set */
304 1.1 simonb irq = 1 << irq; /* only used as a mask from here on */
305 1.1 simonb
306 1.1 simonb /* XXX Only high-level interrupts for now */
307 1.1 simonb switch (type) {
308 1.1 simonb case IST_NONE:
309 1.1 simonb case IST_PULSE:
310 1.1 simonb case IST_EDGE:
311 1.1 simonb panic("unsupported irq type %d", type);
312 1.1 simonb case IST_LEVEL:
313 1.1 simonb REGVAL(icu_base + IC_CONFIG2_SET) = irq;
314 1.1 simonb REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
315 1.1 simonb REGVAL(icu_base + IC_CONFIG0_SET) = irq;
316 1.1 simonb }
317 1.1 simonb
318 1.1 simonb /* XXX handle GPIO interrupts - not done at all yet */
319 1.1 simonb if (cpu_intr & 0x1)
320 1.1 simonb REGVAL(icu_base + IC_ASSIGN_REQUEST_CLEAR) = irq;
321 1.1 simonb else
322 1.1 simonb REGVAL(icu_base + IC_ASSIGN_REQUEST_SET) = irq;
323 1.1 simonb
324 1.1 simonb /* Associate interrupt with peripheral */
325 1.1 simonb REGVAL(icu_base + IC_SOURCE_SET) = irq;
326 1.1 simonb
327 1.1 simonb /* Actually enable the interrupt */
328 1.1 simonb REGVAL(icu_base + IC_MASK_SET) = irq;
329 1.1 simonb
330 1.1 simonb /* And allow the interrupt to interrupt idle */
331 1.1 simonb REGVAL(icu_base + IC_WAKEUP_SET) = irq;
332 1.1 simonb }
333 1.1 simonb splx(s);
334 1.1 simonb
335 1.1 simonb return (ih);
336 1.1 simonb }
337 1.1 simonb
338 1.1 simonb void
339 1.1 simonb au_intr_disestablish(void *cookie)
340 1.1 simonb {
341 1.1 simonb struct evbmips_intrhand *ih = cookie;
342 1.1 simonb uint32_t icu_base;
343 1.1 simonb int irq, s;
344 1.1 simonb
345 1.1 simonb irq = ih->ih_irq;
346 1.1 simonb
347 1.1 simonb s = splhigh();
348 1.1 simonb
349 1.1 simonb /*
350 1.1 simonb * First, remove it from the table.
351 1.1 simonb */
352 1.1 simonb LIST_REMOVE(ih, ih_q);
353 1.1 simonb
354 1.1 simonb /*
355 1.1 simonb * Now, disable it, if there is nothing remaining on the
356 1.1 simonb * list.
357 1.1 simonb */
358 1.1 simonb if (au1000_intrtab[irq].intr_refcnt-- == 1) {
359 1.1 simonb icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
360 1.1 simonb
361 1.1 simonb irq &= 31; /* throw away high bit if set */
362 1.1 simonb irq = 1 << irq; /* only used as a mask from here on */
363 1.1 simonb
364 1.1 simonb REGVAL(icu_base + IC_CONFIG2_CLEAR) = irq;
365 1.1 simonb REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
366 1.1 simonb REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq;
367 1.1 simonb
368 1.1 simonb /* XXX disable with MASK_CLEAR and WAKEUP_CLEAR */
369 1.1 simonb }
370 1.1 simonb
371 1.1 simonb splx(s);
372 1.1 simonb
373 1.1 simonb free(ih, M_DEVBUF);
374 1.1 simonb }
375 1.1 simonb
376 1.1 simonb void
377 1.1 simonb au_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc, u_int32_t ipending)
378 1.1 simonb {
379 1.1 simonb struct evbmips_intrhand *ih;
380 1.1 simonb int level;
381 1.1 simonb u_int32_t icu_base, irqmask;
382 1.1 simonb
383 1.1 simonb for (level = 3; level >= 0; level--) {
384 1.1 simonb if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
385 1.1 simonb continue;
386 1.1 simonb
387 1.1 simonb /*
388 1.1 simonb * XXX the following may well be slow to execute.
389 1.1 simonb * investigate and possibly speed up.
390 1.1 simonb *
391 1.1 simonb * is something like:
392 1.1 simonb *
393 1.1 simonb * irqmask = REGVAL(
394 1.1 simonb * (level & 4 == 0) ? IC0_BASE ? IC1_BASE +
395 1.1 simonb * (level & 2 == 0) ? IC_REQUEST0_INT : IC_REQUEST1_INT);
396 1.1 simonb *
397 1.1 simonb * be any better?
398 1.1 simonb *
399 1.1 simonb */
400 1.1 simonb switch (level) {
401 1.1 simonb case 0:
402 1.1 simonb icu_base = IC0_BASE;
403 1.1 simonb irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
404 1.1 simonb break;
405 1.1 simonb case 1:
406 1.1 simonb icu_base = IC0_BASE;
407 1.1 simonb irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
408 1.1 simonb break;
409 1.1 simonb case 2:
410 1.1 simonb icu_base = IC1_BASE;
411 1.1 simonb irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
412 1.1 simonb break;
413 1.1 simonb case 3:
414 1.1 simonb icu_base = IC1_BASE;
415 1.1 simonb irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
416 1.1 simonb break;
417 1.1 simonb }
418 1.1 simonb au1000_cpuintrs[level].cintr_count.ev_count++;
419 1.1 simonb LIST_FOREACH(ih, &au1000_cpuintrs[level].cintr_list, ih_q) {
420 1.1 simonb /* XXX should check is see if interrupt is masked? */
421 1.1 simonb if (1 << ih->ih_irq & irqmask) {
422 1.1 simonb au1000_intrtab[ih->ih_irq].intr_count.ev_count++;
423 1.1 simonb (*ih->ih_func)(ih->ih_arg);
424 1.1 simonb
425 1.1 simonb REGVAL(icu_base + IC_MASK_CLEAR) = 1 << ih->ih_irq;
426 1.1 simonb REGVAL(icu_base + IC_MASK_SET) = 1 << ih->ih_irq;
427 1.1 simonb }
428 1.1 simonb }
429 1.1 simonb cause &= ~(MIPS_INT_MASK_0 << level);
430 1.1 simonb }
431 1.1 simonb
432 1.1 simonb /* Re-enable anything that we have processed. */
433 1.1 simonb _splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
434 1.1 simonb }
435