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