Home | History | Annotate | Line # | Download | only in alchemy
au_icu.c revision 1.2
      1 /*	$NetBSD: au_icu.c,v 1.2 2002/07/29 16:25:02 simonb 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 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 	evcnt_attach_static(&mips_int5_evcnt);
    217 
    218 	for (i = 0; i < NIRQS; i++) {
    219 		/* XXX steering - use an irqmap array? */
    220 
    221 		au1000_intrtab[i].intr_refcnt = 0;
    222 		evcnt_attach_dynamic(&au1000_intrtab[i].intr_count,
    223 		    EVCNT_TYPE_INTR, NULL, "au1000", au1000_intrnames[i]);
    224 	}
    225 }
    226 
    227 void *
    228 au_intr_establish(int irq, int req, int level, int type,
    229     int (*func)(void *), void *arg)
    230 {
    231 	struct evbmips_intrhand *ih;
    232 	uint32_t icu_base;
    233 	int cpu_intr, s;
    234 
    235 	if (irq >= NIRQS)
    236 		panic("au_intr_establish: bogus IRQ %d\n", irq);
    237 	if (req > 1)
    238 		panic("au_intr_establish: bogus request %d\n", req);
    239 
    240 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
    241 	if (ih == NULL)
    242 		return (NULL);
    243 
    244 	ih->ih_func = func;
    245 	ih->ih_arg = arg;
    246 	ih->ih_irq = irq;
    247 
    248 	s = splhigh();
    249 
    250 	/*
    251 	 * First, link it into the tables.
    252 	 * XXX do we want a separate list (really, should only be one item, not
    253 	 *     a list anyway) per irq, not per cpu interrupt?
    254 	 */
    255 	cpu_intr = (irq < 32 ? 0 : 2);
    256 	LIST_INSERT_HEAD(&au1000_cpuintrs[cpu_intr].cintr_list, ih, ih_q);
    257 
    258 	/*
    259 	 * Now enable it.
    260 	 */
    261 	if (au1000_intrtab[irq].intr_refcnt++ == 0) {
    262 		icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
    263 
    264 		irq &= 31;	/* throw away high bit if set */
    265 		irq = 1 << irq;	/* only used as a mask from here on */
    266 
    267 		/* XXX Only high-level interrupts for now */
    268 		switch (type) {
    269 		case IST_NONE:
    270 		case IST_PULSE:
    271 		case IST_EDGE:
    272 			panic("unsupported irq type %d", type);
    273 		case IST_LEVEL:
    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 		}
    278 
    279 		/* XXX handle GPIO interrupts - not done at all yet */
    280 		if (cpu_intr & 0x1)
    281 			REGVAL(icu_base + IC_ASSIGN_REQUEST_CLEAR) = irq;
    282 		else
    283 			REGVAL(icu_base + IC_ASSIGN_REQUEST_SET) = irq;
    284 
    285 		/* Associate interrupt with peripheral */
    286 		REGVAL(icu_base + IC_SOURCE_SET) = irq;
    287 
    288 		/* Actually enable the interrupt */
    289 		REGVAL(icu_base + IC_MASK_SET) = irq;
    290 
    291 		/* And allow the interrupt to interrupt idle */
    292 		REGVAL(icu_base + IC_WAKEUP_SET) = irq;
    293 	}
    294 	splx(s);
    295 
    296 	return (ih);
    297 }
    298 
    299 void
    300 au_intr_disestablish(void *cookie)
    301 {
    302 	struct evbmips_intrhand *ih = cookie;
    303 	uint32_t icu_base;
    304 	int irq, s;
    305 
    306 	irq = ih->ih_irq;
    307 
    308 	s = splhigh();
    309 
    310 	/*
    311 	 * First, remove it from the table.
    312 	 */
    313 	LIST_REMOVE(ih, ih_q);
    314 
    315 	/*
    316 	 * Now, disable it, if there is nothing remaining on the
    317 	 * list.
    318 	 */
    319 	if (au1000_intrtab[irq].intr_refcnt-- == 1) {
    320 		icu_base = (irq < 32) ? IC0_BASE : IC1_BASE;
    321 
    322 		irq &= 31;	/* throw away high bit if set */
    323 		irq = 1 << irq;	/* only used as a mask from here on */
    324 
    325 		REGVAL(icu_base + IC_CONFIG2_CLEAR) = irq;
    326 		REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq;
    327 		REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq;
    328 
    329 		/* XXX disable with MASK_CLEAR and WAKEUP_CLEAR */
    330 	}
    331 
    332 	splx(s);
    333 
    334 	free(ih, M_DEVBUF);
    335 }
    336 
    337 void
    338 au_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc, u_int32_t ipending)
    339 {
    340 	struct evbmips_intrhand *ih;
    341 	int level;
    342 	u_int32_t icu_base, irqmask;
    343 
    344 	for (level = 3; level >= 0; level--) {
    345 		if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
    346 			continue;
    347 
    348 		/*
    349 		 * XXX	the following may well be slow to execute.
    350 		 *	investigate and possibly speed up.
    351 		 *
    352 		 * is something like:
    353 		 *
    354 		 *    irqmask = REGVAL(
    355 		 *	 (level & 4 == 0) ? IC0_BASE ? IC1_BASE +
    356 		 *	 (level & 2 == 0) ? IC_REQUEST0_INT : IC_REQUEST1_INT);
    357 		 *
    358 		 * be any better?
    359 		 *
    360 		 */
    361 		switch (level) {
    362 		case 0:
    363 			icu_base = IC0_BASE;
    364 			irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
    365 			break;
    366 		case 1:
    367 			icu_base = IC0_BASE;
    368 			irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
    369 			break;
    370 		case 2:
    371 			icu_base = IC1_BASE;
    372 			irqmask = REGVAL(icu_base + IC_REQUEST0_INT);
    373 			break;
    374 		case 3:
    375 			icu_base = IC1_BASE;
    376 			irqmask = REGVAL(icu_base + IC_REQUEST1_INT);
    377 			break;
    378 		}
    379 		au1000_cpuintrs[level].cintr_count.ev_count++;
    380 		LIST_FOREACH(ih, &au1000_cpuintrs[level].cintr_list, ih_q) {
    381 			/* XXX should check is see if interrupt is masked? */
    382 			if (1 << ih->ih_irq & irqmask) {
    383 				au1000_intrtab[ih->ih_irq].intr_count.ev_count++;
    384 				(*ih->ih_func)(ih->ih_arg);
    385 
    386 				REGVAL(icu_base + IC_MASK_CLEAR) = 1 << ih->ih_irq;
    387 				REGVAL(icu_base + IC_MASK_SET) = 1 << ih->ih_irq;
    388 			}
    389 		}
    390 		cause &= ~(MIPS_INT_MASK_0 << level);
    391 	}
    392 
    393 	/* Re-enable anything that we have processed. */
    394 	_splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
    395 }
    396