Home | History | Annotate | Line # | Download | only in atheros
ar_intr.c revision 1.4
      1 /* $NetBSD: ar_intr.c,v 1.3 2011/07/10 06:24:18 matt Exp $ */
      2 /*
      3  * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
      4  * Copyright (c) 2006 Garrett D'Amore.
      5  * All rights reserved.
      6  *
      7  * This code was written by Garrett D'Amore for the Champaign-Urbana
      8  * Community Wireless Network Project.
      9  *
     10  * Redistribution and use in source and binary forms, with or
     11  * without modification, are permitted provided that the following
     12  * conditions 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
     16  *    copyright notice, this list of conditions and the following
     17  *    disclaimer in the documentation and/or other materials provided
     18  *    with the distribution.
     19  * 3. All advertising materials mentioning features or use of this
     20  *    software must display the following acknowledgements:
     21  *      This product includes software developed by the Urbana-Champaign
     22  *      Independent Media Center.
     23  *	This product includes software developed by Garrett D'Amore.
     24  * 4. Urbana-Champaign Independent Media Center's name and Garrett
     25  *    D'Amore's name may not be used to endorse or promote products
     26  *    derived from this software without specific prior written permission.
     27  *
     28  * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
     29  * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR
     30  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     31  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
     33  * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT,
     34  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     35  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     36  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     37  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     38  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     39  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     40  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     41  */
     42 
     43 #include <sys/cdefs.h>
     44 __KERNEL_RCSID(0, "$NetBSD: ar_intr.c,v 1.3 2011/07/10 06:24:18 matt Exp $");
     45 
     46 #define __INTR_PRIVATE
     47 
     48 #include <sys/param.h>
     49 #include <sys/intr.h>
     50 #include <sys/cpu.h>
     51 #include <sys/kernel.h>
     52 #include <sys/malloc.h>
     53 
     54 #include <mips/cpuregs.h>
     55 #include <mips/locore.h>
     56 #include <mips/atheros/include/platform.h>
     57 
     58 #define	REGVAL(x)	*((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x))))
     59 
     60 /*
     61  * Only MISC interrupts are easily masked at the interrupt controller.
     62  * The others have to be masked at the source.
     63  */
     64 
     65 #define	NINTRS	7	/* MIPS INT2-INT4 (7 is clock interrupt) */
     66 #define	NIRQS	32	/* bits in Miscellaneous Interrupt Status Register */
     67 
     68 struct atheros_intrhand {
     69 	LIST_ENTRY(atheros_intrhand) ih_q;
     70 	int (*ih_func)(void *);
     71 	void *ih_arg;
     72 	int ih_irq;
     73 };
     74 
     75 struct atheros_intr {
     76 	LIST_HEAD(, atheros_intrhand) intr_qh;
     77 	struct evcnt	intr_count;
     78 };
     79 
     80 static struct atheros_intr cpu_intrs[NINTRS];
     81 static struct atheros_intr misc_intrs[NIRQS];
     82 
     83 static uint32_t
     84 misc_intstat_get(void)
     85 {
     86 	return REGVAL(platformsw->apsw_misc_intstat);
     87 }
     88 
     89 static void
     90 misc_intstat_put(uint32_t v)
     91 {
     92 	REGVAL(platformsw->apsw_misc_intstat) = v;
     93 }
     94 
     95 static uint32_t
     96 misc_intmask_get(void)
     97 {
     98 	return REGVAL(platformsw->apsw_misc_intmask);
     99 }
    100 
    101 static void
    102 misc_intmask_put(uint32_t v)
    103 {
    104 	REGVAL(platformsw->apsw_misc_intmask) = v;
    105 }
    106 
    107 
    108 static void *
    109 genath_cpu_intr_establish(int intr, int (*func)(void *), void *arg)
    110 {
    111 	struct atheros_intrhand	*ih;
    112 
    113 	if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL)
    114 		return NULL;
    115 
    116 	ih->ih_func = func;
    117 	ih->ih_arg = arg;
    118 	ih->ih_irq = intr;
    119 
    120 	if (ih == NULL)
    121 		return NULL;
    122 
    123 	const int s = splhigh();
    124 
    125 	LIST_INSERT_HEAD(&cpu_intrs[intr].intr_qh, ih, ih_q);
    126 
    127 	/*
    128 	 * The MIPS CPU interrupts are enabled at boot time, so they
    129 	 * should pretty much always be ready to go.
    130 	 */
    131 
    132 	splx(s);
    133 	return (ih);
    134 }
    135 
    136 static void
    137 genath_cpu_intr_disestablish(void *arg)
    138 {
    139 	struct atheros_intrhand	* const ih = arg;
    140 
    141 	const int s = splhigh();
    142 
    143 	LIST_REMOVE(ih, ih_q);
    144 
    145 	splx(s);
    146 	free(ih, M_DEVBUF);
    147 }
    148 
    149 static void *
    150 genath_misc_intr_establish(int irq, int (*func)(void *), void *arg)
    151 {
    152 	struct atheros_intr * const intr = &misc_intrs[irq];
    153 	struct atheros_intrhand	*ih;
    154 	bool first;
    155 	int s;
    156 
    157 
    158 	if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL)
    159 		return NULL;
    160 
    161 	ih->ih_func = func;
    162 	ih->ih_arg = arg;
    163 	ih->ih_irq = irq;
    164 
    165 	s = splhigh();
    166 
    167 	first = LIST_EMPTY(&intr->intr_qh);
    168 
    169 	LIST_INSERT_HEAD(&intr->intr_qh, ih, ih_q);
    170 
    171 	if (first) {
    172 		const uint32_t mask = misc_intmask_get() | __BIT(irq);
    173 		misc_intmask_put(mask);
    174 		(void) misc_intmask_get();	/* flush wbuffer */
    175 	}
    176 
    177 	splx(s);
    178 
    179 	return ih;
    180 }
    181 
    182 static void
    183 genath_misc_intr_disestablish(void *arg)
    184 {
    185 	struct atheros_intrhand	*ih = arg;
    186 	struct atheros_intr * const intr = &misc_intrs[ih->ih_irq];
    187 
    188 	const int s = splhigh();
    189 
    190 	LIST_REMOVE(ih, ih_q);
    191 	if (LIST_EMPTY(&intr->intr_qh)) {
    192 		const uint32_t mask = misc_intmask_get() & ~__BIT(ih->ih_irq);
    193 		misc_intmask_put(mask);
    194 		(void) misc_intmask_get();	/* flush wbuffer */
    195 	}
    196 
    197 	splx(s);
    198 	free(ih, M_DEVBUF);
    199 }
    200 
    201 
    202 static int
    203 genath_misc_intr(void *arg)
    204 {
    205 	uint32_t		isr;
    206 	uint32_t		mask;
    207 	int			rv = 0;
    208 	struct atheros_intr	*intr = arg;
    209 
    210 	isr = misc_intstat_get();
    211 	mask = misc_intmask_get();
    212 
    213 	misc_intstat_put(isr & ~mask);
    214 
    215 	isr &= mask;
    216 	while (isr != 0) {
    217 		struct atheros_intrhand	*ih;
    218 		int index = 31 - __builtin_clz(isr & -isr); /* ffs */
    219 		intr += index;
    220 
    221 		intr->intr_count.ev_count++;
    222 		LIST_FOREACH(ih, &intr->intr_qh, ih_q) {
    223 			rv |= (*ih->ih_func)(ih->ih_arg);
    224 		}
    225 		isr >>= index + 1;
    226 		intr++;
    227 	}
    228 
    229 	return rv;
    230 }
    231 
    232 static void
    233 genath_iointr(int cpl, vaddr_t pc, uint32_t ipending)
    234 {
    235 	struct atheros_intr *intr = &cpu_intrs[NINTRS-1];
    236 
    237 	/* move ipending to the most significant bits */
    238 	ipending *= __BIT(31) / (MIPS_INT_MASK_0 << (NINTRS-1));
    239 	while (ipending != 0) {
    240 		struct atheros_intrhand	*ih;
    241 		int index = __builtin_clz(ipending);
    242 
    243 		intr -= index;
    244 		ipending <<= index;
    245 		KASSERT(ipending & __BIT(31));
    246 		KASSERT(intr >= cpu_intrs);
    247 
    248 		intr->intr_count.ev_count++;
    249 		LIST_FOREACH(ih, &intr->intr_qh, ih_q) {
    250 			(*ih->ih_func)(ih->ih_arg);
    251 		}
    252 		ipending <<= 1;
    253 		intr--;
    254 	}
    255 }
    256 
    257 static void
    258 genath_intr_init(void)
    259 {
    260 	const struct atheros_platformsw * const apsw = platformsw;
    261 
    262 	KASSERT(apsw->apsw_ipl_sr_map != NULL);
    263 	ipl_sr_map = *apsw->apsw_ipl_sr_map;
    264 
    265 	for (size_t i = 0; i < apsw->apsw_cpu_nintrs; i++) {
    266 		if (apsw->apsw_cpu_intrnames[i] != NULL) {
    267 			LIST_INIT(&cpu_intrs[i].intr_qh);
    268 			evcnt_attach_dynamic(&cpu_intrs[i].intr_count,
    269 			    EVCNT_TYPE_INTR, NULL, "cpu",
    270 			    apsw->apsw_cpu_intrnames[i]);
    271 		}
    272 	}
    273 
    274 	for (size_t i = 0; i < apsw->apsw_misc_nintrs; i++) {
    275 		if (apsw->apsw_misc_intrnames[i] != NULL) {
    276 			LIST_INIT(&misc_intrs[i].intr_qh);
    277 			evcnt_attach_dynamic(&misc_intrs[i].intr_count,
    278 			    EVCNT_TYPE_INTR, NULL, "misc",
    279 			    apsw->apsw_misc_intrnames[i]);
    280 		}
    281 	}
    282 
    283 	/* make sure we start without any misc interrupts enabled */
    284 	(void) misc_intstat_get();
    285 	misc_intmask_put(0);
    286 	misc_intstat_put(0);
    287 
    288 	/* make sure we register the MISC interrupt handler */
    289 	genath_cpu_intr_establish(apsw->apsw_cpuirq_misc,
    290 	    genath_misc_intr, misc_intrs);
    291 }
    292 
    293 
    294 const struct atheros_intrsw atheros_intrsw = {
    295 	.aisw_init = genath_intr_init,
    296 	.aisw_cpu_establish = genath_cpu_intr_establish,
    297 	.aisw_cpu_disestablish = genath_cpu_intr_disestablish,
    298 	.aisw_misc_establish = genath_misc_intr_establish,
    299 	.aisw_misc_disestablish = genath_misc_intr_disestablish,
    300 	.aisw_iointr = genath_iointr,
    301 };
    302 
    303