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