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