ar_intr.c revision 1.3 1 /* $NetBSD: ar_intr.c,v 1.3 2011/07/10 06:24:18 matt Exp $ */
2 /*
3 * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
4 * Copyright (c) 2006 Garrett D'Amore.
5 * All rights reserved.
6 *
7 * This code was written by Garrett D'Amore for the Champaign-Urbana
8 * Community Wireless Network Project.
9 *
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.
27 *
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.
41 */
42
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: ar_intr.c,v 1.3 2011/07/10 06:24:18 matt Exp $");
45
46 #define __INTR_PRIVATE
47
48 #include <sys/param.h>
49 #include <sys/intr.h>
50 #include <sys/kernel.h>
51 #include <sys/malloc.h>
52
53 #include <mips/locore.h>
54 #include <mips/atheros/include/platform.h>
55
56 #define REGVAL(x) *((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x))))
57
58 /*
59 * Only MISC interrupts are easily masked at the interrupt controller.
60 * The others have to be masked at the source.
61 */
62
63 #define NINTRS 7 /* MIPS INT2-INT4 (7 is clock interrupt) */
64 #define NIRQS 32 /* bits in Miscellaneous Interrupt Status Register */
65
66 struct atheros_intrhand {
67 LIST_ENTRY(atheros_intrhand) ih_q;
68 int (*ih_func)(void *);
69 void *ih_arg;
70 int ih_irq;
71 };
72
73 struct atheros_intr {
74 LIST_HEAD(, atheros_intrhand) intr_qh;
75 struct evcnt intr_count;
76 };
77
78 static struct atheros_intr cpu_intrs[NINTRS];
79 static struct atheros_intr misc_intrs[NIRQS];
80
81 static uint32_t
82 misc_intstat_get(void)
83 {
84 return REGVAL(platformsw->apsw_misc_intstat);
85 }
86
87 static void
88 misc_intstat_put(uint32_t v)
89 {
90 REGVAL(platformsw->apsw_misc_intstat) = v;
91 }
92
93 static uint32_t
94 misc_intmask_get(void)
95 {
96 return REGVAL(platformsw->apsw_misc_intmask);
97 }
98
99 static void
100 misc_intmask_put(uint32_t v)
101 {
102 REGVAL(platformsw->apsw_misc_intmask) = v;
103 }
104
105
106 static void *
107 genath_cpu_intr_establish(int intr, int (*func)(void *), void *arg)
108 {
109 struct atheros_intrhand *ih;
110
111 if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL)
112 return NULL;
113
114 ih->ih_func = func;
115 ih->ih_arg = arg;
116 ih->ih_irq = intr;
117
118 if (ih == NULL)
119 return NULL;
120
121 const int s = splhigh();
122
123 LIST_INSERT_HEAD(&cpu_intrs[intr].intr_qh, ih, ih_q);
124
125 /*
126 * The MIPS CPU interrupts are enabled at boot time, so they
127 * should pretty much always be ready to go.
128 */
129
130 splx(s);
131 return (ih);
132 }
133
134 static void
135 genath_cpu_intr_disestablish(void *arg)
136 {
137 struct atheros_intrhand * const ih = arg;
138
139 const int s = splhigh();
140
141 LIST_REMOVE(ih, ih_q);
142
143 splx(s);
144 free(ih, M_DEVBUF);
145 }
146
147 static void *
148 genath_misc_intr_establish(int irq, int (*func)(void *), void *arg)
149 {
150 struct atheros_intr * const intr = &misc_intrs[irq];
151 struct atheros_intrhand *ih;
152 bool first;
153 int s;
154
155
156 if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL)
157 return NULL;
158
159 ih->ih_func = func;
160 ih->ih_arg = arg;
161 ih->ih_irq = irq;
162
163 s = splhigh();
164
165 first = LIST_EMPTY(&intr->intr_qh);
166
167 LIST_INSERT_HEAD(&intr->intr_qh, ih, ih_q);
168
169 if (first) {
170 const uint32_t mask = misc_intmask_get() | __BIT(irq);
171 misc_intmask_put(mask);
172 (void) misc_intmask_get(); /* flush wbuffer */
173 }
174
175 splx(s);
176
177 return ih;
178 }
179
180 static void
181 genath_misc_intr_disestablish(void *arg)
182 {
183 struct atheros_intrhand *ih = arg;
184 struct atheros_intr * const intr = &misc_intrs[ih->ih_irq];
185
186 const int s = splhigh();
187
188 LIST_REMOVE(ih, ih_q);
189 if (LIST_EMPTY(&intr->intr_qh)) {
190 const uint32_t mask = misc_intmask_get() & ~__BIT(ih->ih_irq);
191 misc_intmask_put(mask);
192 (void) misc_intmask_get(); /* flush wbuffer */
193 }
194
195 splx(s);
196 free(ih, M_DEVBUF);
197 }
198
199
200 static int
201 genath_misc_intr(void *arg)
202 {
203 uint32_t isr;
204 uint32_t mask;
205 int rv = 0;
206 struct atheros_intr *intr = arg;
207
208 isr = misc_intstat_get();
209 mask = misc_intmask_get();
210
211 misc_intstat_put(isr & ~mask);
212
213 isr &= mask;
214 while (isr != 0) {
215 struct atheros_intrhand *ih;
216 int index = 31 - __builtin_clz(isr & -isr); /* ffs */
217 intr += index;
218
219 intr->intr_count.ev_count++;
220 LIST_FOREACH(ih, &intr->intr_qh, ih_q) {
221 rv |= (*ih->ih_func)(ih->ih_arg);
222 }
223 isr >>= index + 1;
224 intr++;
225 }
226
227 return rv;
228 }
229
230 static void
231 genath_iointr(int cpl, vaddr_t pc, uint32_t ipending)
232 {
233 struct atheros_intr *intr = &cpu_intrs[NINTRS-1];
234
235 /* move ipending to the most significant bits */
236 ipending *= __BIT(31) / (MIPS_INT_MASK_0 << (NINTRS-1));
237 while (ipending != 0) {
238 struct atheros_intrhand *ih;
239 int index = __builtin_clz(ipending);
240
241 intr -= index;
242 ipending <<= index;
243 KASSERT(ipending & __BIT(31));
244 KASSERT(intr >= cpu_intrs);
245
246 intr->intr_count.ev_count++;
247 LIST_FOREACH(ih, &intr->intr_qh, ih_q) {
248 (*ih->ih_func)(ih->ih_arg);
249 }
250 ipending <<= 1;
251 intr--;
252 }
253 }
254
255 static void
256 genath_intr_init(void)
257 {
258 const struct atheros_platformsw * const apsw = platformsw;
259
260 KASSERT(apsw->apsw_ipl_sr_map != NULL);
261 ipl_sr_map = *apsw->apsw_ipl_sr_map;
262
263 for (size_t i = 0; i < apsw->apsw_cpu_nintrs; i++) {
264 if (apsw->apsw_cpu_intrnames[i] != NULL) {
265 LIST_INIT(&cpu_intrs[i].intr_qh);
266 evcnt_attach_dynamic(&cpu_intrs[i].intr_count,
267 EVCNT_TYPE_INTR, NULL, "cpu",
268 apsw->apsw_cpu_intrnames[i]);
269 }
270 }
271
272 for (size_t i = 0; i < apsw->apsw_misc_nintrs; i++) {
273 if (apsw->apsw_misc_intrnames[i] != NULL) {
274 LIST_INIT(&misc_intrs[i].intr_qh);
275 evcnt_attach_dynamic(&misc_intrs[i].intr_count,
276 EVCNT_TYPE_INTR, NULL, "misc",
277 apsw->apsw_misc_intrnames[i]);
278 }
279 }
280
281 /* make sure we start without any misc interrupts enabled */
282 (void) misc_intstat_get();
283 misc_intmask_put(0);
284 misc_intstat_put(0);
285
286 /* make sure we register the MISC interrupt handler */
287 genath_cpu_intr_establish(apsw->apsw_cpuirq_misc,
288 genath_misc_intr, misc_intrs);
289 }
290
291
292 const struct atheros_intrsw atheros_intrsw = {
293 .aisw_init = genath_intr_init,
294 .aisw_cpu_establish = genath_cpu_intr_establish,
295 .aisw_cpu_disestablish = genath_cpu_intr_disestablish,
296 .aisw_misc_establish = genath_misc_intr_establish,
297 .aisw_misc_disestablish = genath_misc_intr_disestablish,
298 .aisw_iointr = genath_iointr,
299 };
300
301