xref: /netbsd-src/sys/arch/mips/alchemy/au_icu.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: au_icu.c,v 1.6 2003/05/25 14:08:21 tsutsui Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * 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 copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Interrupt support for the Alchemy Semiconductor Au1x00 CPUs.
41  *
42  * The Alchemy Semiconductor Au1x00's interrupts are wired to two internal
43  * interrupt controllers.
44  */
45 
46 #include "opt_ddb.h"
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/alchemy/include/aureg.h>
60 #include <mips/alchemy/include/auvar.h>
61 
62 #define	REGVAL(x)	*((__volatile u_int32_t *)(MIPS_PHYS_TO_KSEG1((x))))
63 
64 /*
65  * This is a mask of bits to clear in the SR when we go to a
66  * given hardware interrupt priority level.
67  */
68 
69 const u_int32_t ipl_sr_bits[_IPL_N] = {
70 	0,					/*  0: IPL_NONE */
71 
72 	MIPS_SOFT_INT_MASK_0,			/*  1: IPL_SOFT */
73 
74 	MIPS_SOFT_INT_MASK_0,			/*  2: IPL_SOFTCLOCK */
75 
76 	MIPS_SOFT_INT_MASK_0,			/*  3: IPL_SOFTNET */
77 
78 	MIPS_SOFT_INT_MASK_0,			/*  4: IPL_SOFTSERIAL */
79 
80 	MIPS_SOFT_INT_MASK_0|
81 		MIPS_SOFT_INT_MASK_1|
82 		MIPS_INT_MASK_0,		/*  5: IPL_BIO */
83 
84 	MIPS_SOFT_INT_MASK_0|
85 		MIPS_SOFT_INT_MASK_1|
86 		MIPS_INT_MASK_0,		/*  6: IPL_NET */
87 
88 	MIPS_SOFT_INT_MASK_0|
89 		MIPS_SOFT_INT_MASK_1|
90 		MIPS_INT_MASK_0,		/*  7: IPL_{SERIAL,TTY} */
91 
92 	MIPS_SOFT_INT_MASK_0|
93 		MIPS_SOFT_INT_MASK_1|
94 		MIPS_INT_MASK_0|
95 		MIPS_INT_MASK_1|
96 		MIPS_INT_MASK_2|
97 		MIPS_INT_MASK_3|
98 		MIPS_INT_MASK_4|
99 		MIPS_INT_MASK_5,		/*  8: IPL_{CLOCK,HIGH} */
100 };
101 
102 /*
103  * This is a mask of bits to clear in the SR when we go to a
104  * given software interrupt priority level.
105  * Hardware ipls are port/board specific.
106  */
107 const u_int32_t mips_ipl_si_to_sr[_IPL_NSOFT] = {
108 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFT */
109 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFTCLOCK */
110 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFTNET */
111 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFTSERIAL */
112 };
113 
114 #define	NIRQS		64
115 
116 const char *au1000_intrnames[NIRQS] = {
117 	"uart0",
118 	"uart1",
119 	"uart2",
120 	"uart3",
121 	"ssi0",
122 	"ssi1",
123 	"dma0",
124 	"dma1",
125 	"dma2",
126 	"dma3",
127 	"dma4",
128 	"dma5",
129 	"dma6",
130 	"dma7",
131 	"pc0",
132 	"pc0 match1",
133 	"pc0 match2",
134 	"pc0 match3",
135 	"pc1",
136 	"pc1 match1",
137 	"pc1 match2",
138 	"pc1 match3",
139 	"irda tx",
140 	"irda rx",
141 	"usb intr",
142 	"usb suspend",
143 	"usb host",
144 	"ac97",
145 	"mac0",
146 	"mac1",
147 	"i2s",
148 	"ac97 cmd",
149 
150 	"gpio 0",
151 	"gpio 1",
152 	"gpio 2",
153 	"gpio 3",
154 	"gpio 4",
155 	"gpio 5",
156 	"gpio 6",
157 	"gpio 7",
158 	"gpio 8",
159 	"gpio 9",
160 	"gpio 10",
161 	"gpio 11",
162 	"gpio 12",
163 	"gpio 13",
164 	"gpio 14",
165 	"gpio 15",
166 	"gpio 16",
167 	"gpio 17",
168 	"gpio 18",
169 	"gpio 19",
170 	"gpio 20",
171 	"gpio 21",
172 	"gpio 22",
173 	"gpio 23",
174 	"gpio 24",
175 	"gpio 25",
176 	"gpio 26",
177 	"gpio 27",
178 	"gpio 28",
179 	"gpio 29",
180 	"gpio 30",
181 	"gpio 31",
182 };
183 
184 struct au1000_intrhead {
185 	struct evcnt intr_count;
186 	int intr_refcnt;
187 };
188 struct au1000_intrhead au1000_intrtab[NIRQS];
189 
190 #define	NINTRS			4	/* MIPS INT0 - INT3 */
191 
192 struct au1000_cpuintr {
193 	LIST_HEAD(, evbmips_intrhand) cintr_list;
194 	struct evcnt cintr_count;
195 };
196 
197 struct au1000_cpuintr au1000_cpuintrs[NINTRS];
198 const char *au1000_cpuintrnames[NINTRS] = {
199 	"icu 0, req 0",
200 	"icu 0, req 1",
201 	"icu 1, req 0",
202 	"icu 1, req 1",
203 };
204 
205 void
206 au_intr_init(void)
207 {
208 	int i;
209 
210 	for (i = 0; i < NINTRS; i++) {
211 		LIST_INIT(&au1000_cpuintrs[i].cintr_list);
212 		evcnt_attach_dynamic(&au1000_cpuintrs[i].cintr_count,
213 		    EVCNT_TYPE_INTR, NULL, "mips", au1000_cpuintrnames[i]);
214 	}
215 
216 	for (i = 0; i < NIRQS; i++) {
217 		/* XXX steering - use an irqmap array? */
218 
219 		au1000_intrtab[i].intr_refcnt = 0;
220 		evcnt_attach_dynamic(&au1000_intrtab[i].intr_count,
221 		    EVCNT_TYPE_INTR, NULL, "au1000", au1000_intrnames[i]);
222 	}
223 }
224 
225 void *
226 au_intr_establish(int irq, int req, int level, int type,
227     int (*func)(void *), void *arg)
228 {
229 	struct evbmips_intrhand *ih;
230 	uint32_t icu_base;
231 	int cpu_intr, s;
232 
233 	if (irq >= NIRQS)
234 		panic("au_intr_establish: bogus IRQ %d", irq);
235 	if (req > 1)
236 		panic("au_intr_establish: bogus request %d", req);
237 
238 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
239 	if (ih == NULL)
240 		return (NULL);
241 
242 	ih->ih_func = func;
243 	ih->ih_arg = arg;
244 	ih->ih_irq = irq;
245 
246 	s = splhigh();
247 
248 	/*
249 	 * First, link it into the tables.
250 	 * XXX do we want a separate list (really, should only be one item, not
251 	 *     a list anyway) per irq, not per cpu interrupt?
252 	 */
253 	cpu_intr = (irq < 32 ? 0 : 2);
254 	LIST_INSERT_HEAD(&au1000_cpuintrs[cpu_intr].cintr_list, ih, ih_q);
255 
256 	/*
257 	 * Now enable it.
258 	 */
259 	if (au1000_intrtab[irq].intr_refcnt++ == 0) {
260 		icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
261 
262 		irq &= 31;	/* throw away high bit if set */
263 		irq = 1 << irq;	/* only used as a mask from here on */
264 
265 		/* XXX Only high-level interrupts for now */
266 		switch (type) {
267 		case IST_NONE:
268 		case IST_PULSE:
269 		case IST_EDGE:
270 			panic("unsupported irq type %d", type);
271 			/* NOTREACHED */
272 		case IST_LEVEL:
273 		case IST_LEVEL_HIGH:
274 			REGVAL(icu_base + IC_CONFIG2_SET) = irq;
275 			REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
276 			REGVAL(icu_base + IC_CONFIG0_SET) = irq;
277 			break;
278 		case IST_LEVEL_LOW:
279 			REGVAL(icu_base + IC_CONFIG2_SET) = irq;
280 			REGVAL(icu_base + IC_CONFIG1_SET) = irq;
281 			REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq;
282 			break;
283 		}
284 
285 		/* XXX handle GPIO interrupts - not done at all yet */
286 		if (cpu_intr & 0x1)
287 			REGVAL(icu_base + IC_ASSIGN_REQUEST_CLEAR) = irq;
288 		else
289 			REGVAL(icu_base + IC_ASSIGN_REQUEST_SET) = irq;
290 
291 		/* Associate interrupt with peripheral */
292 		REGVAL(icu_base + IC_SOURCE_SET) = irq;
293 
294 		/* Actually enable the interrupt */
295 		REGVAL(icu_base + IC_MASK_SET) = irq;
296 
297 		/* And allow the interrupt to interrupt idle */
298 		REGVAL(icu_base + IC_WAKEUP_SET) = irq;
299 	}
300 	splx(s);
301 
302 	return (ih);
303 }
304 
305 void
306 au_intr_disestablish(void *cookie)
307 {
308 	struct evbmips_intrhand *ih = cookie;
309 	uint32_t icu_base;
310 	int irq, s;
311 
312 	irq = ih->ih_irq;
313 
314 	s = splhigh();
315 
316 	/*
317 	 * First, remove it from the table.
318 	 */
319 	LIST_REMOVE(ih, ih_q);
320 
321 	/*
322 	 * Now, disable it, if there is nothing remaining on the
323 	 * list.
324 	 */
325 	if (au1000_intrtab[irq].intr_refcnt-- == 1) {
326 		icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
327 
328 		irq &= 31;	/* throw away high bit if set */
329 		irq = 1 << irq;	/* only used as a mask from here on */
330 
331 		REGVAL(icu_base + IC_CONFIG2_CLEAR) = irq;
332 		REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
333 		REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq;
334 
335 		/* XXX disable with MASK_CLEAR and WAKEUP_CLEAR */
336 	}
337 
338 	splx(s);
339 
340 	free(ih, M_DEVBUF);
341 }
342 
343 void
344 au_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc, u_int32_t ipending)
345 {
346 	struct evbmips_intrhand *ih;
347 	int level;
348 	u_int32_t icu_base, irqmask;
349 
350 	for (level = 3; level >= 0; level--) {
351 		if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
352 			continue;
353 
354 		/*
355 		 * XXX	the following may well be slow to execute.
356 		 *	investigate and possibly speed up.
357 		 *
358 		 * is something like:
359 		 *
360 		 *    irqmask = REGVAL(
361 		 *	 (level & 4 == 0) ? IC0_BASE ? IC1_BASE +
362 		 *	 (level & 2 == 0) ? IC_REQUEST0_INT : IC_REQUEST1_INT);
363 		 *
364 		 * be any better?
365 		 *
366 		 */
367 		switch (level) {
368 		case 0:
369 			icu_base = IC0_BASE;
370 			irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
371 			break;
372 		case 1:
373 			icu_base = IC0_BASE;
374 			irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
375 			break;
376 		case 2:
377 			icu_base = IC1_BASE;
378 			irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
379 			break;
380 		case 3:
381 			icu_base = IC1_BASE;
382 			irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
383 			break;
384 		}
385 		au1000_cpuintrs[level].cintr_count.ev_count++;
386 		LIST_FOREACH(ih, &au1000_cpuintrs[level].cintr_list, ih_q) {
387 			/* XXX should check is see if interrupt is masked? */
388 			if (1 << ih->ih_irq & irqmask) {
389 				au1000_intrtab[ih->ih_irq].intr_count.ev_count++;
390 				(*ih->ih_func)(ih->ih_arg);
391 
392 				REGVAL(icu_base + IC_MASK_CLEAR) = 1 << ih->ih_irq;
393 				REGVAL(icu_base + IC_MASK_SET) = 1 << ih->ih_irq;
394 			}
395 		}
396 		cause &= ~(MIPS_INT_MASK_0 << level);
397 	}
398 
399 	/* Re-enable anything that we have processed. */
400 	_splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
401 }
402