Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / arch / mips / atheros / ar5312_intr.c
blob69b72f3e489f6f7586b15546e52a2c2355b1fccb
1 /* $Id: ar5312_intr.c,v 1.6 2008/01/07 07:28:14 dyoung Exp $ */
2 /*
3 * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
4 * Copyright (c) 2006 Garrett D'Amore.
5 * All rights reserved.
7 * This code was written by Garrett D'Amore for the Champaign-Urbana
8 * Community Wireless Network Project.
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
12 * conditions 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
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 * 3. All advertising materials mentioning features or use of this
20 * software must display the following acknowledgements:
21 * This product includes software developed by the Urbana-Champaign
22 * Independent Media Center.
23 * This product includes software developed by Garrett D'Amore.
24 * 4. Urbana-Champaign Independent Media Center's name and Garrett
25 * D'Amore's name may not be used to endorse or promote products
26 * derived from this software without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
29 * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR
30 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
33 * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
35 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
37 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
38 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
40 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: ar5312_intr.c,v 1.5 2008/01/07 07:14:37 dyoung Exp $");
46 #include <sys/param.h>
47 #include <sys/queue.h>
48 #include <sys/malloc.h>
49 #include <sys/systm.h>
50 #include <sys/device.h>
51 #include <sys/kernel.h>
53 #include <machine/bus.h>
54 #include <machine/intr.h>
56 #include <mips/locore.h>
57 #include <mips/atheros/include/ar5312reg.h>
58 #include <mips/atheros/include/ar531xvar.h>
61 * Here's a little tidbit that can only be gleaned from Linux sources.
63 * IP2: (INT0) WLAN0
64 * IP3: (INT1) ENET0
65 * IP4: (INT2) ENET1
66 * IP5: (INT3) WLAN1
67 * IP6: (INT4) MISC
68 * IP7: (INT5) CPU CLOCK
70 * Only MISC interrupts are easily masked at the interrupt controller.
71 * The others have to be masked at the source.
74 #define REGVAL(x) *((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x))))
75 #define GETREG(x) REGVAL((x) + AR5312_SYSREG_BASE)
76 #define PUTREG(x,v) (REGVAL((x) + AR5312_SYSREG_BASE)) = (v)
78 #define NINTRS 5 /* MIPS INT0-INT6 (7 is clock interrupt) */
79 #define NIRQS 7 /* bits in Miscellaneous Interrupt Status Register */
81 struct ar531x_intrhand {
82 LIST_ENTRY(ar531x_intrhand) ih_q;
83 int (*ih_func)(void *);
84 void *ih_arg;
85 int ih_irq;
88 struct ar531x_intr {
89 LIST_HEAD(, ar531x_intrhand) intr_l;
90 struct evcnt intr_count;
93 const uint32_t ipl_sr_bits[_IPL_N] = {
94 0, /* 0: IPL_NONE */
95 MIPS_SOFT_INT_MASK_0, /* 1: IPL_SOFTCLOCK */
96 MIPS_SOFT_INT_MASK_0, /* 2: IPL_SOFTNET */
98 MIPS_SOFT_INT_MASK_0 |
99 MIPS_SOFT_INT_MASK_1 |
100 MIPS_INT_MASK_0 |
101 MIPS_INT_MASK_1 |
102 MIPS_INT_MASK_2 |
103 MIPS_INT_MASK_3, /* 3: IPL_VM */
105 MIPS_INT_MASK, /* 4: IPL_{SCHED,HIGH} */
108 static const char *ar5312_cpuintrnames[NINTRS] = {
109 "int 2 (wlan0)",
110 "int 3 (enet0)",
111 "int 4 (enet1)",
112 "int 5 (wlan1)",
113 "int 6 (misc)",
116 static const char *ar5312_miscintrnames[NIRQS] = {
117 "misc 0 (timer)",
118 "misc 1 (AHBproc error)",
119 "misc 2 (AHBdma error)",
120 "misc 3 (gpio)",
121 "misc 4 (uart)",
122 "misc 5 (uart dma)",
123 "misc 6 (watchdog)"
126 static struct ar531x_intr ar5312_cpuintrs[NINTRS];
127 static struct ar531x_intr ar5312_miscintrs[NIRQS];
129 static int ar531x_miscintr(void *);
131 void
132 ar531x_intr_init(void)
134 int i;
136 for (i = 0; i < NINTRS; i++) {
137 LIST_INIT(&ar5312_cpuintrs[i].intr_l);
138 evcnt_attach_dynamic(&ar5312_cpuintrs[i].intr_count,
139 EVCNT_TYPE_INTR, NULL, "mips", ar5312_cpuintrnames[i]);
142 for (i = 0; i < NIRQS; i++) {
143 LIST_INIT(&ar5312_miscintrs[i].intr_l);
144 evcnt_attach_dynamic(&ar5312_miscintrs[i].intr_count,
145 EVCNT_TYPE_INTR, NULL, "ar5312", ar5312_miscintrnames[i]);
148 /* make sure we start without any misc interrupts enabled */
149 GETREG(AR5312_SYSREG_MISC_INTSTAT);
150 PUTREG(AR5312_SYSREG_MISC_INTMASK, 0);
152 /* make sure we register the MISC interrupt handler */
153 ar531x_cpu_intr_establish(NINTRS - 1, ar531x_miscintr, NULL);
157 void *
158 ar531x_cpu_intr_establish(int intr, int (*func)(void *), void *arg)
160 struct ar531x_intrhand *ih;
161 int s;
163 if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL)
164 return NULL;
166 ih->ih_func = func;
167 ih->ih_arg = arg;
168 ih->ih_irq = intr;
170 if (ih == NULL)
171 return NULL;
173 s = splhigh();
175 LIST_INSERT_HEAD(&ar5312_cpuintrs[intr].intr_l, ih, ih_q);
178 * The MIPS CPU interrupts are enabled at boot time, so they
179 * should pretty much always be ready to go.
182 splx(s);
183 return (ih);
186 void
187 ar531x_cpu_intr_disestablish(void *arg)
189 struct ar531x_intrhand *ih = arg;
190 int s;
192 s = splhigh();
194 LIST_REMOVE(ih, ih_q);
196 splx(s);
197 free(ih, M_DEVBUF);
200 void *
201 ar531x_misc_intr_establish(int irq, int (*func)(void *), void *arg)
203 struct ar531x_intrhand *ih;
204 int first;
205 int s;
207 if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL)
208 return NULL;
210 ih->ih_func = func;
211 ih->ih_arg = arg;
212 ih->ih_irq = irq;
214 if (ih == NULL)
215 return NULL;
217 s = splhigh();
219 first = LIST_EMPTY(&ar5312_miscintrs[irq].intr_l);
221 LIST_INSERT_HEAD(&ar5312_miscintrs[irq].intr_l, ih, ih_q);
223 if (first) {
224 uint32_t mask;
225 mask = GETREG(AR5312_SYSREG_MISC_INTMASK);
226 mask |= (1 << irq);
227 PUTREG(AR5312_SYSREG_MISC_INTMASK, mask);
228 GETREG(AR5312_SYSREG_MISC_INTMASK); /* flush wbuffer */
231 splx(s);
233 return ih;
236 void
237 ar531x_misc_intr_disestablish(void *arg)
239 struct ar531x_intrhand *ih = arg;
240 int s;
242 s = splhigh();
244 LIST_REMOVE(ih, ih_q);
245 if (LIST_EMPTY(&ar5312_miscintrs[ih->ih_irq].intr_l)) {
246 uint32_t mask;
247 mask = GETREG(AR5312_SYSREG_MISC_INTMASK);
248 mask &= ~(1 << ih->ih_irq);
249 PUTREG(AR5312_SYSREG_MISC_INTMASK, mask);
250 GETREG(AR5312_SYSREG_MISC_INTMASK); /* flush wbuffer */
253 splx(s);
254 free(ih, M_DEVBUF);
259 ar531x_miscintr(void *arg)
261 uint32_t isr;
262 int mask;
263 int index;
264 int rv = 0;
265 struct ar531x_intrhand *ih;
267 isr = GETREG(AR5312_SYSREG_MISC_INTSTAT);
268 mask = GETREG(AR5312_SYSREG_MISC_INTMASK);
270 for (index = 0; index < NIRQS; index++) {
272 if (isr & mask & (1 << index)) {
273 ar5312_miscintrs[index].intr_count.ev_count++;
274 LIST_FOREACH(ih, &ar5312_miscintrs[index].intr_l, ih_q)
275 rv |= (*ih->ih_func)(ih->ih_arg);
279 return rv;
282 void
283 ar531x_cpuintr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending)
285 uint32_t mask;
286 int index;
287 struct ar531x_intrhand *ih;
289 /* all others get normal handling */
290 for (index = NINTRS - 1; index >= 0; index--) {
291 mask = MIPS_INT_MASK_0 << index;
293 if (ipending & mask) {
294 ar5312_cpuintrs[index].intr_count.ev_count++;
295 LIST_FOREACH(ih, &ar5312_cpuintrs[index].intr_l, ih_q)
296 (*ih->ih_func)(ih->ih_arg);
297 cause &= ~mask;
301 /* re-enable the stuff we processed */
302 _splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));