Home | History | Annotate | Line # | Download | only in alchemy
au_icu.c revision 1.9
      1 /*	$NetBSD: au_icu.c,v 1.9 2004/02/13 11:36:15 wiz 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 <sys/cdefs.h>
     47 __KERNEL_RCSID(0, "$NetBSD: au_icu.c,v 1.9 2004/02/13 11:36:15 wiz Exp $");
     48 
     49 #include "opt_ddb.h"
     50 
     51 #include <sys/param.h>
     52 #include <sys/queue.h>
     53 #include <sys/malloc.h>
     54 #include <sys/systm.h>
     55 #include <sys/device.h>
     56 #include <sys/kernel.h>
     57 
     58 #include <machine/bus.h>
     59 #include <machine/intr.h>
     60 
     61 #include <mips/locore.h>
     62 #include <mips/alchemy/include/aureg.h>
     63 #include <mips/alchemy/include/auvar.h>
     64 
     65 #define	REGVAL(x)	*((__volatile u_int32_t *)(MIPS_PHYS_TO_KSEG1((x))))
     66 
     67 /*
     68  * This is a mask of bits to clear in the SR when we go to a
     69  * given hardware interrupt priority level.
     70  */
     71 
     72 const u_int32_t ipl_sr_bits[_IPL_N] = {
     73 	0,					/*  0: IPL_NONE */
     74 
     75 	MIPS_SOFT_INT_MASK_0,			/*  1: IPL_SOFT */
     76 
     77 	MIPS_SOFT_INT_MASK_0,			/*  2: IPL_SOFTCLOCK */
     78 
     79 	MIPS_SOFT_INT_MASK_0,			/*  3: IPL_SOFTNET */
     80 
     81 	MIPS_SOFT_INT_MASK_0,			/*  4: IPL_SOFTSERIAL */
     82 
     83 	MIPS_SOFT_INT_MASK_0|
     84 		MIPS_SOFT_INT_MASK_1|
     85 		MIPS_INT_MASK_0,		/*  5: IPL_BIO */
     86 
     87 	MIPS_SOFT_INT_MASK_0|
     88 		MIPS_SOFT_INT_MASK_1|
     89 		MIPS_INT_MASK_0,		/*  6: IPL_NET */
     90 
     91 	MIPS_SOFT_INT_MASK_0|
     92 		MIPS_SOFT_INT_MASK_1|
     93 		MIPS_INT_MASK_0,		/*  7: IPL_{SERIAL,TTY} */
     94 
     95 	MIPS_SOFT_INT_MASK_0|
     96 		MIPS_SOFT_INT_MASK_1|
     97 		MIPS_INT_MASK_0|
     98 		MIPS_INT_MASK_1|
     99 		MIPS_INT_MASK_2|
    100 		MIPS_INT_MASK_3|
    101 		MIPS_INT_MASK_4|
    102 		MIPS_INT_MASK_5,		/*  8: IPL_{CLOCK,HIGH} */
    103 };
    104 
    105 /*
    106  * This is a mask of bits to clear in the SR when we go to a
    107  * given software interrupt priority level.
    108  * Hardware ipls are port/board specific.
    109  */
    110 const u_int32_t mips_ipl_si_to_sr[_IPL_NSOFT] = {
    111 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFT */
    112 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFTCLOCK */
    113 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFTNET */
    114 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFTSERIAL */
    115 };
    116 
    117 #define	NIRQS		64
    118 
    119 const char *au1000_intrnames[NIRQS] = {
    120 	"uart0",
    121 	"uart1",
    122 	"uart2",
    123 	"uart3",
    124 	"ssi0",
    125 	"ssi1",
    126 	"dma0",
    127 	"dma1",
    128 	"dma2",
    129 	"dma3",
    130 	"dma4",
    131 	"dma5",
    132 	"dma6",
    133 	"dma7",
    134 	"pc0",
    135 	"pc0 match1",
    136 	"pc0 match2",
    137 	"pc0 match3",
    138 	"pc1",
    139 	"pc1 match1",
    140 	"pc1 match2",
    141 	"pc1 match3",
    142 	"irda tx",
    143 	"irda rx",
    144 	"usb intr",
    145 	"usb suspend",
    146 	"usb host",
    147 	"ac97",
    148 	"mac0",
    149 	"mac1",
    150 	"i2s",
    151 	"ac97 cmd",
    152 
    153 	"gpio 0",
    154 	"gpio 1",
    155 	"gpio 2",
    156 	"gpio 3",
    157 	"gpio 4",
    158 	"gpio 5",
    159 	"gpio 6",
    160 	"gpio 7",
    161 	"gpio 8",
    162 	"gpio 9",
    163 	"gpio 10",
    164 	"gpio 11",
    165 	"gpio 12",
    166 	"gpio 13",
    167 	"gpio 14",
    168 	"gpio 15",
    169 	"gpio 16",
    170 	"gpio 17",
    171 	"gpio 18",
    172 	"gpio 19",
    173 	"gpio 20",
    174 	"gpio 21",
    175 	"gpio 22",
    176 	"gpio 23",
    177 	"gpio 24",
    178 	"gpio 25",
    179 	"gpio 26",
    180 	"gpio 27",
    181 	"gpio 28",
    182 	"gpio 29",
    183 	"gpio 30",
    184 	"gpio 31",
    185 };
    186 
    187 struct au1000_intrhead {
    188 	struct evcnt intr_count;
    189 	int intr_refcnt;
    190 };
    191 struct au1000_intrhead au1000_intrtab[NIRQS];
    192 
    193 #define	NINTRS			4	/* MIPS INT0 - INT3 */
    194 
    195 struct au1000_cpuintr {
    196 	LIST_HEAD(, evbmips_intrhand) cintr_list;
    197 	struct evcnt cintr_count;
    198 };
    199 
    200 struct au1000_cpuintr au1000_cpuintrs[NINTRS];
    201 const char *au1000_cpuintrnames[NINTRS] = {
    202 	"icu 0, req 0",
    203 	"icu 0, req 1",
    204 	"icu 1, req 0",
    205 	"icu 1, req 1",
    206 };
    207 
    208 void
    209 au_intr_init(void)
    210 {
    211 	int i;
    212 
    213 	for (i = 0; i < NINTRS; i++) {
    214 		LIST_INIT(&au1000_cpuintrs[i].cintr_list);
    215 		evcnt_attach_dynamic(&au1000_cpuintrs[i].cintr_count,
    216 		    EVCNT_TYPE_INTR, NULL, "mips", au1000_cpuintrnames[i]);
    217 	}
    218 
    219 	for (i = 0; i < NIRQS; i++) {
    220 		/* XXX steering - use an irqmap array? */
    221 
    222 		au1000_intrtab[i].intr_refcnt = 0;
    223 		evcnt_attach_dynamic(&au1000_intrtab[i].intr_count,
    224 		    EVCNT_TYPE_INTR, NULL, "au1000", au1000_intrnames[i]);
    225 	}
    226 }
    227 
    228 void *
    229 au_intr_establish(int irq, int req, int level, int type,
    230     int (*func)(void *), void *arg)
    231 {
    232 	struct evbmips_intrhand *ih;
    233 	uint32_t icu_base;
    234 	int cpu_intr, s;
    235 
    236 	if (irq >= NIRQS)
    237 		panic("au_intr_establish: bogus IRQ %d", irq);
    238 	if (req > 1)
    239 		panic("au_intr_establish: bogus request %d", req);
    240 
    241 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
    242 	if (ih == NULL)
    243 		return (NULL);
    244 
    245 	ih->ih_func = func;
    246 	ih->ih_arg = arg;
    247 	ih->ih_irq = irq;
    248 
    249 	s = splhigh();
    250 
    251 	/*
    252 	 * First, link it into the tables.
    253 	 * XXX do we want a separate list (really, should only be one item, not
    254 	 *     a list anyway) per irq, not per CPU interrupt?
    255 	 */
    256 	cpu_intr = (irq < 32 ? 0 : 2);
    257 	LIST_INSERT_HEAD(&au1000_cpuintrs[cpu_intr].cintr_list, ih, ih_q);
    258 
    259 	/*
    260 	 * Now enable it.
    261 	 */
    262 	if (au1000_intrtab[irq].intr_refcnt++ == 0) {
    263 		icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
    264 
    265 		irq &= 31;	/* throw away high bit if set */
    266 		irq = 1 << irq;	/* only used as a mask from here on */
    267 
    268 		/* XXX Only high-level interrupts for now */
    269 		switch (type) {
    270 		case IST_NONE:
    271 		case IST_PULSE:
    272 		case IST_EDGE:
    273 			panic("unsupported irq type %d", type);
    274 			/* NOTREACHED */
    275 		case IST_LEVEL:
    276 		case IST_LEVEL_HIGH:
    277 			REGVAL(icu_base + IC_CONFIG2_SET) = irq;
    278 			REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
    279 			REGVAL(icu_base + IC_CONFIG0_SET) = irq;
    280 			break;
    281 		case IST_LEVEL_LOW:
    282 			REGVAL(icu_base + IC_CONFIG2_SET) = irq;
    283 			REGVAL(icu_base + IC_CONFIG1_SET) = irq;
    284 			REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq;
    285 			break;
    286 		}
    287 
    288 		/* XXX handle GPIO interrupts - not done at all yet */
    289 		if (cpu_intr & 0x1)
    290 			REGVAL(icu_base + IC_ASSIGN_REQUEST_CLEAR) = irq;
    291 		else
    292 			REGVAL(icu_base + IC_ASSIGN_REQUEST_SET) = irq;
    293 
    294 		/* Associate interrupt with peripheral */
    295 		REGVAL(icu_base + IC_SOURCE_SET) = irq;
    296 
    297 		/* Actually enable the interrupt */
    298 		REGVAL(icu_base + IC_MASK_SET) = irq;
    299 
    300 		/* And allow the interrupt to interrupt idle */
    301 		REGVAL(icu_base + IC_WAKEUP_SET) = irq;
    302 	}
    303 	splx(s);
    304 
    305 	return (ih);
    306 }
    307 
    308 void
    309 au_intr_disestablish(void *cookie)
    310 {
    311 	struct evbmips_intrhand *ih = cookie;
    312 	uint32_t icu_base;
    313 	int irq, s;
    314 
    315 	irq = ih->ih_irq;
    316 
    317 	s = splhigh();
    318 
    319 	/*
    320 	 * First, remove it from the table.
    321 	 */
    322 	LIST_REMOVE(ih, ih_q);
    323 
    324 	/*
    325 	 * Now, disable it, if there is nothing remaining on the
    326 	 * list.
    327 	 */
    328 	if (au1000_intrtab[irq].intr_refcnt-- == 1) {
    329 		icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
    330 
    331 		irq &= 31;	/* throw away high bit if set */
    332 		irq = 1 << irq;	/* only used as a mask from here on */
    333 
    334 		REGVAL(icu_base + IC_CONFIG2_CLEAR) = irq;
    335 		REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
    336 		REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq;
    337 
    338 		/* XXX disable with MASK_CLEAR and WAKEUP_CLEAR */
    339 	}
    340 
    341 	splx(s);
    342 
    343 	free(ih, M_DEVBUF);
    344 }
    345 
    346 void
    347 au_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc, u_int32_t ipending)
    348 {
    349 	struct evbmips_intrhand *ih;
    350 	int level;
    351 	u_int32_t icu_base = 0, irqmask = 0;	/* Both XXX gcc */
    352 
    353 	for (level = 3; level >= 0; level--) {
    354 		if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
    355 			continue;
    356 
    357 		/*
    358 		 * XXX	the following may well be slow to execute.
    359 		 *	investigate and possibly speed up.
    360 		 *
    361 		 * is something like:
    362 		 *
    363 		 *    irqmask = REGVAL(
    364 		 *	 (level & 4 == 0) ? IC0_BASE ? IC1_BASE +
    365 		 *	 (level & 2 == 0) ? IC_REQUEST0_INT : IC_REQUEST1_INT);
    366 		 *
    367 		 * be any better?
    368 		 *
    369 		 */
    370 		switch (level) {
    371 		case 0:
    372 			icu_base = IC0_BASE;
    373 			irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
    374 			break;
    375 		case 1:
    376 			icu_base = IC0_BASE;
    377 			irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
    378 			break;
    379 		case 2:
    380 			icu_base = IC1_BASE;
    381 			irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
    382 			break;
    383 		case 3:
    384 			icu_base = IC1_BASE;
    385 			irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
    386 			break;
    387 		}
    388 		au1000_cpuintrs[level].cintr_count.ev_count++;
    389 		LIST_FOREACH(ih, &au1000_cpuintrs[level].cintr_list, ih_q) {
    390 			/* XXX should check is see if interrupt is masked? */
    391 			if (1 << ih->ih_irq & irqmask) {
    392 				au1000_intrtab[ih->ih_irq].intr_count.ev_count++;
    393 				(*ih->ih_func)(ih->ih_arg);
    394 
    395 				REGVAL(icu_base + IC_MASK_CLEAR) = 1 << ih->ih_irq;
    396 				REGVAL(icu_base + IC_MASK_SET) = 1 << ih->ih_irq;
    397 			}
    398 		}
    399 		cause &= ~(MIPS_INT_MASK_0 << level);
    400 	}
    401 
    402 	/* Re-enable anything that we have processed. */
    403 	_splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
    404 }
    405