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