1 /* $Id: ar5312_intr.c,v 1.6 2008/01/07 07:28:14 dyoung Exp $ */
3 * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
4 * Copyright (c) 2006 Garrett D'Amore.
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
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.
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 *);
89 LIST_HEAD(, ar531x_intrhand
) intr_l
;
90 struct evcnt intr_count
;
93 const uint32_t ipl_sr_bits
[_IPL_N
] = {
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
|
103 MIPS_INT_MASK_3
, /* 3: IPL_VM */
105 MIPS_INT_MASK
, /* 4: IPL_{SCHED,HIGH} */
108 static const char *ar5312_cpuintrnames
[NINTRS
] = {
116 static const char *ar5312_miscintrnames
[NIRQS
] = {
118 "misc 1 (AHBproc error)",
119 "misc 2 (AHBdma error)",
126 static struct ar531x_intr ar5312_cpuintrs
[NINTRS
];
127 static struct ar531x_intr ar5312_miscintrs
[NIRQS
];
129 static int ar531x_miscintr(void *);
132 ar531x_intr_init(void)
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
);
158 ar531x_cpu_intr_establish(int intr
, int (*func
)(void *), void *arg
)
160 struct ar531x_intrhand
*ih
;
163 if ((ih
= malloc(sizeof(*ih
), M_DEVBUF
, M_NOWAIT
)) == NULL
)
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.
187 ar531x_cpu_intr_disestablish(void *arg
)
189 struct ar531x_intrhand
*ih
= arg
;
194 LIST_REMOVE(ih
, ih_q
);
201 ar531x_misc_intr_establish(int irq
, int (*func
)(void *), void *arg
)
203 struct ar531x_intrhand
*ih
;
207 if ((ih
= malloc(sizeof(*ih
), M_DEVBUF
, M_NOWAIT
)) == NULL
)
219 first
= LIST_EMPTY(&ar5312_miscintrs
[irq
].intr_l
);
221 LIST_INSERT_HEAD(&ar5312_miscintrs
[irq
].intr_l
, ih
, ih_q
);
225 mask
= GETREG(AR5312_SYSREG_MISC_INTMASK
);
227 PUTREG(AR5312_SYSREG_MISC_INTMASK
, mask
);
228 GETREG(AR5312_SYSREG_MISC_INTMASK
); /* flush wbuffer */
237 ar531x_misc_intr_disestablish(void *arg
)
239 struct ar531x_intrhand
*ih
= arg
;
244 LIST_REMOVE(ih
, ih_q
);
245 if (LIST_EMPTY(&ar5312_miscintrs
[ih
->ih_irq
].intr_l
)) {
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 */
259 ar531x_miscintr(void *arg
)
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
);
283 ar531x_cpuintr(uint32_t status
, uint32_t cause
, uint32_t pc
, uint32_t ipending
)
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
);
301 /* re-enable the stuff we processed */
302 _splset(MIPS_SR_INT_IE
| ((status
& ~cause
) & MIPS_HARD_INT_MASK
));