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