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