au_icu.c revision 1.9 1 /* $NetBSD: au_icu.c,v 1.9 2004/02/13 11:36:15 wiz 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 <sys/cdefs.h>
47 __KERNEL_RCSID(0, "$NetBSD: au_icu.c,v 1.9 2004/02/13 11:36:15 wiz Exp $");
48
49 #include "opt_ddb.h"
50
51 #include <sys/param.h>
52 #include <sys/queue.h>
53 #include <sys/malloc.h>
54 #include <sys/systm.h>
55 #include <sys/device.h>
56 #include <sys/kernel.h>
57
58 #include <machine/bus.h>
59 #include <machine/intr.h>
60
61 #include <mips/locore.h>
62 #include <mips/alchemy/include/aureg.h>
63 #include <mips/alchemy/include/auvar.h>
64
65 #define REGVAL(x) *((__volatile u_int32_t *)(MIPS_PHYS_TO_KSEG1((x))))
66
67 /*
68 * This is a mask of bits to clear in the SR when we go to a
69 * given hardware interrupt priority level.
70 */
71
72 const u_int32_t ipl_sr_bits[_IPL_N] = {
73 0, /* 0: IPL_NONE */
74
75 MIPS_SOFT_INT_MASK_0, /* 1: IPL_SOFT */
76
77 MIPS_SOFT_INT_MASK_0, /* 2: IPL_SOFTCLOCK */
78
79 MIPS_SOFT_INT_MASK_0, /* 3: IPL_SOFTNET */
80
81 MIPS_SOFT_INT_MASK_0, /* 4: IPL_SOFTSERIAL */
82
83 MIPS_SOFT_INT_MASK_0|
84 MIPS_SOFT_INT_MASK_1|
85 MIPS_INT_MASK_0, /* 5: IPL_BIO */
86
87 MIPS_SOFT_INT_MASK_0|
88 MIPS_SOFT_INT_MASK_1|
89 MIPS_INT_MASK_0, /* 6: IPL_NET */
90
91 MIPS_SOFT_INT_MASK_0|
92 MIPS_SOFT_INT_MASK_1|
93 MIPS_INT_MASK_0, /* 7: IPL_{SERIAL,TTY} */
94
95 MIPS_SOFT_INT_MASK_0|
96 MIPS_SOFT_INT_MASK_1|
97 MIPS_INT_MASK_0|
98 MIPS_INT_MASK_1|
99 MIPS_INT_MASK_2|
100 MIPS_INT_MASK_3|
101 MIPS_INT_MASK_4|
102 MIPS_INT_MASK_5, /* 8: IPL_{CLOCK,HIGH} */
103 };
104
105 /*
106 * This is a mask of bits to clear in the SR when we go to a
107 * given software interrupt priority level.
108 * Hardware ipls are port/board specific.
109 */
110 const u_int32_t mips_ipl_si_to_sr[_IPL_NSOFT] = {
111 MIPS_SOFT_INT_MASK_0, /* IPL_SOFT */
112 MIPS_SOFT_INT_MASK_0, /* IPL_SOFTCLOCK */
113 MIPS_SOFT_INT_MASK_0, /* IPL_SOFTNET */
114 MIPS_SOFT_INT_MASK_0, /* IPL_SOFTSERIAL */
115 };
116
117 #define NIRQS 64
118
119 const char *au1000_intrnames[NIRQS] = {
120 "uart0",
121 "uart1",
122 "uart2",
123 "uart3",
124 "ssi0",
125 "ssi1",
126 "dma0",
127 "dma1",
128 "dma2",
129 "dma3",
130 "dma4",
131 "dma5",
132 "dma6",
133 "dma7",
134 "pc0",
135 "pc0 match1",
136 "pc0 match2",
137 "pc0 match3",
138 "pc1",
139 "pc1 match1",
140 "pc1 match2",
141 "pc1 match3",
142 "irda tx",
143 "irda rx",
144 "usb intr",
145 "usb suspend",
146 "usb host",
147 "ac97",
148 "mac0",
149 "mac1",
150 "i2s",
151 "ac97 cmd",
152
153 "gpio 0",
154 "gpio 1",
155 "gpio 2",
156 "gpio 3",
157 "gpio 4",
158 "gpio 5",
159 "gpio 6",
160 "gpio 7",
161 "gpio 8",
162 "gpio 9",
163 "gpio 10",
164 "gpio 11",
165 "gpio 12",
166 "gpio 13",
167 "gpio 14",
168 "gpio 15",
169 "gpio 16",
170 "gpio 17",
171 "gpio 18",
172 "gpio 19",
173 "gpio 20",
174 "gpio 21",
175 "gpio 22",
176 "gpio 23",
177 "gpio 24",
178 "gpio 25",
179 "gpio 26",
180 "gpio 27",
181 "gpio 28",
182 "gpio 29",
183 "gpio 30",
184 "gpio 31",
185 };
186
187 struct au1000_intrhead {
188 struct evcnt intr_count;
189 int intr_refcnt;
190 };
191 struct au1000_intrhead au1000_intrtab[NIRQS];
192
193 #define NINTRS 4 /* MIPS INT0 - INT3 */
194
195 struct au1000_cpuintr {
196 LIST_HEAD(, evbmips_intrhand) cintr_list;
197 struct evcnt cintr_count;
198 };
199
200 struct au1000_cpuintr au1000_cpuintrs[NINTRS];
201 const char *au1000_cpuintrnames[NINTRS] = {
202 "icu 0, req 0",
203 "icu 0, req 1",
204 "icu 1, req 0",
205 "icu 1, req 1",
206 };
207
208 void
209 au_intr_init(void)
210 {
211 int i;
212
213 for (i = 0; i < NINTRS; i++) {
214 LIST_INIT(&au1000_cpuintrs[i].cintr_list);
215 evcnt_attach_dynamic(&au1000_cpuintrs[i].cintr_count,
216 EVCNT_TYPE_INTR, NULL, "mips", au1000_cpuintrnames[i]);
217 }
218
219 for (i = 0; i < NIRQS; i++) {
220 /* XXX steering - use an irqmap array? */
221
222 au1000_intrtab[i].intr_refcnt = 0;
223 evcnt_attach_dynamic(&au1000_intrtab[i].intr_count,
224 EVCNT_TYPE_INTR, NULL, "au1000", au1000_intrnames[i]);
225 }
226 }
227
228 void *
229 au_intr_establish(int irq, int req, int level, int type,
230 int (*func)(void *), void *arg)
231 {
232 struct evbmips_intrhand *ih;
233 uint32_t icu_base;
234 int cpu_intr, s;
235
236 if (irq >= NIRQS)
237 panic("au_intr_establish: bogus IRQ %d", irq);
238 if (req > 1)
239 panic("au_intr_establish: bogus request %d", req);
240
241 ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
242 if (ih == NULL)
243 return (NULL);
244
245 ih->ih_func = func;
246 ih->ih_arg = arg;
247 ih->ih_irq = irq;
248
249 s = splhigh();
250
251 /*
252 * First, link it into the tables.
253 * XXX do we want a separate list (really, should only be one item, not
254 * a list anyway) per irq, not per CPU interrupt?
255 */
256 cpu_intr = (irq < 32 ? 0 : 2);
257 LIST_INSERT_HEAD(&au1000_cpuintrs[cpu_intr].cintr_list, ih, ih_q);
258
259 /*
260 * Now enable it.
261 */
262 if (au1000_intrtab[irq].intr_refcnt++ == 0) {
263 icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
264
265 irq &= 31; /* throw away high bit if set */
266 irq = 1 << irq; /* only used as a mask from here on */
267
268 /* XXX Only high-level interrupts for now */
269 switch (type) {
270 case IST_NONE:
271 case IST_PULSE:
272 case IST_EDGE:
273 panic("unsupported irq type %d", type);
274 /* NOTREACHED */
275 case IST_LEVEL:
276 case IST_LEVEL_HIGH:
277 REGVAL(icu_base + IC_CONFIG2_SET) = irq;
278 REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
279 REGVAL(icu_base + IC_CONFIG0_SET) = irq;
280 break;
281 case IST_LEVEL_LOW:
282 REGVAL(icu_base + IC_CONFIG2_SET) = irq;
283 REGVAL(icu_base + IC_CONFIG1_SET) = irq;
284 REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq;
285 break;
286 }
287
288 /* XXX handle GPIO interrupts - not done at all yet */
289 if (cpu_intr & 0x1)
290 REGVAL(icu_base + IC_ASSIGN_REQUEST_CLEAR) = irq;
291 else
292 REGVAL(icu_base + IC_ASSIGN_REQUEST_SET) = irq;
293
294 /* Associate interrupt with peripheral */
295 REGVAL(icu_base + IC_SOURCE_SET) = irq;
296
297 /* Actually enable the interrupt */
298 REGVAL(icu_base + IC_MASK_SET) = irq;
299
300 /* And allow the interrupt to interrupt idle */
301 REGVAL(icu_base + IC_WAKEUP_SET) = irq;
302 }
303 splx(s);
304
305 return (ih);
306 }
307
308 void
309 au_intr_disestablish(void *cookie)
310 {
311 struct evbmips_intrhand *ih = cookie;
312 uint32_t icu_base;
313 int irq, s;
314
315 irq = ih->ih_irq;
316
317 s = splhigh();
318
319 /*
320 * First, remove it from the table.
321 */
322 LIST_REMOVE(ih, ih_q);
323
324 /*
325 * Now, disable it, if there is nothing remaining on the
326 * list.
327 */
328 if (au1000_intrtab[irq].intr_refcnt-- == 1) {
329 icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
330
331 irq &= 31; /* throw away high bit if set */
332 irq = 1 << irq; /* only used as a mask from here on */
333
334 REGVAL(icu_base + IC_CONFIG2_CLEAR) = irq;
335 REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
336 REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq;
337
338 /* XXX disable with MASK_CLEAR and WAKEUP_CLEAR */
339 }
340
341 splx(s);
342
343 free(ih, M_DEVBUF);
344 }
345
346 void
347 au_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc, u_int32_t ipending)
348 {
349 struct evbmips_intrhand *ih;
350 int level;
351 u_int32_t icu_base = 0, irqmask = 0; /* Both XXX gcc */
352
353 for (level = 3; level >= 0; level--) {
354 if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
355 continue;
356
357 /*
358 * XXX the following may well be slow to execute.
359 * investigate and possibly speed up.
360 *
361 * is something like:
362 *
363 * irqmask = REGVAL(
364 * (level & 4 == 0) ? IC0_BASE ? IC1_BASE +
365 * (level & 2 == 0) ? IC_REQUEST0_INT : IC_REQUEST1_INT);
366 *
367 * be any better?
368 *
369 */
370 switch (level) {
371 case 0:
372 icu_base = IC0_BASE;
373 irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
374 break;
375 case 1:
376 icu_base = IC0_BASE;
377 irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
378 break;
379 case 2:
380 icu_base = IC1_BASE;
381 irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
382 break;
383 case 3:
384 icu_base = IC1_BASE;
385 irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
386 break;
387 }
388 au1000_cpuintrs[level].cintr_count.ev_count++;
389 LIST_FOREACH(ih, &au1000_cpuintrs[level].cintr_list, ih_q) {
390 /* XXX should check is see if interrupt is masked? */
391 if (1 << ih->ih_irq & irqmask) {
392 au1000_intrtab[ih->ih_irq].intr_count.ev_count++;
393 (*ih->ih_func)(ih->ih_arg);
394
395 REGVAL(icu_base + IC_MASK_CLEAR) = 1 << ih->ih_irq;
396 REGVAL(icu_base + IC_MASK_SET) = 1 << ih->ih_irq;
397 }
398 }
399 cause &= ~(MIPS_INT_MASK_0 << level);
400 }
401
402 /* Re-enable anything that we have processed. */
403 _splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
404 }
405