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