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