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