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/kernel.h> 51 #include <sys/malloc.h> 52 53 #include <mips/locore.h> 54 #include <mips/atheros/include/platform.h> 55 56 #define REGVAL(x) *((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x)))) 57 58 /* 59 * Only MISC interrupts are easily masked at the interrupt controller. 60 * The others have to be masked at the source. 61 */ 62 63 #define NINTRS 7 /* MIPS INT2-INT4 (7 is clock interrupt) */ 64 #define NIRQS 32 /* bits in Miscellaneous Interrupt Status Register */ 65 66 struct atheros_intrhand { 67 LIST_ENTRY(atheros_intrhand) ih_q; 68 int (*ih_func)(void *); 69 void *ih_arg; 70 int ih_irq; 71 }; 72 73 struct atheros_intr { 74 LIST_HEAD(, atheros_intrhand) intr_qh; 75 struct evcnt intr_count; 76 }; 77 78 static struct atheros_intr cpu_intrs[NINTRS]; 79 static struct atheros_intr misc_intrs[NIRQS]; 80 81 static uint32_t 82 misc_intstat_get(void) 83 { 84 return REGVAL(platformsw->apsw_misc_intstat); 85 } 86 87 static void 88 misc_intstat_put(uint32_t v) 89 { 90 REGVAL(platformsw->apsw_misc_intstat) = v; 91 } 92 93 static uint32_t 94 misc_intmask_get(void) 95 { 96 return REGVAL(platformsw->apsw_misc_intmask); 97 } 98 99 static void 100 misc_intmask_put(uint32_t v) 101 { 102 REGVAL(platformsw->apsw_misc_intmask) = v; 103 } 104 105 106 static void * 107 genath_cpu_intr_establish(int intr, int (*func)(void *), void *arg) 108 { 109 struct atheros_intrhand *ih; 110 111 if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL) 112 return NULL; 113 114 ih->ih_func = func; 115 ih->ih_arg = arg; 116 ih->ih_irq = intr; 117 118 if (ih == NULL) 119 return NULL; 120 121 const int s = splhigh(); 122 123 LIST_INSERT_HEAD(&cpu_intrs[intr].intr_qh, ih, ih_q); 124 125 /* 126 * The MIPS CPU interrupts are enabled at boot time, so they 127 * should pretty much always be ready to go. 128 */ 129 130 splx(s); 131 return (ih); 132 } 133 134 static void 135 genath_cpu_intr_disestablish(void *arg) 136 { 137 struct atheros_intrhand * const ih = arg; 138 139 const int s = splhigh(); 140 141 LIST_REMOVE(ih, ih_q); 142 143 splx(s); 144 free(ih, M_DEVBUF); 145 } 146 147 static void * 148 genath_misc_intr_establish(int irq, int (*func)(void *), void *arg) 149 { 150 struct atheros_intr * const intr = &misc_intrs[irq]; 151 struct atheros_intrhand *ih; 152 bool first; 153 int s; 154 155 156 if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL) 157 return NULL; 158 159 ih->ih_func = func; 160 ih->ih_arg = arg; 161 ih->ih_irq = irq; 162 163 s = splhigh(); 164 165 first = LIST_EMPTY(&intr->intr_qh); 166 167 LIST_INSERT_HEAD(&intr->intr_qh, ih, ih_q); 168 169 if (first) { 170 const uint32_t mask = misc_intmask_get() | __BIT(irq); 171 misc_intmask_put(mask); 172 (void) misc_intmask_get(); /* flush wbuffer */ 173 } 174 175 splx(s); 176 177 return ih; 178 } 179 180 static void 181 genath_misc_intr_disestablish(void *arg) 182 { 183 struct atheros_intrhand *ih = arg; 184 struct atheros_intr * const intr = &misc_intrs[ih->ih_irq]; 185 186 const int s = splhigh(); 187 188 LIST_REMOVE(ih, ih_q); 189 if (LIST_EMPTY(&intr->intr_qh)) { 190 const uint32_t mask = misc_intmask_get() & ~__BIT(ih->ih_irq); 191 misc_intmask_put(mask); 192 (void) misc_intmask_get(); /* flush wbuffer */ 193 } 194 195 splx(s); 196 free(ih, M_DEVBUF); 197 } 198 199 200 static int 201 genath_misc_intr(void *arg) 202 { 203 uint32_t isr; 204 uint32_t mask; 205 int rv = 0; 206 struct atheros_intr *intr = arg; 207 208 isr = misc_intstat_get(); 209 mask = misc_intmask_get(); 210 211 misc_intstat_put(isr & ~mask); 212 213 isr &= mask; 214 while (isr != 0) { 215 struct atheros_intrhand *ih; 216 int index = 31 - __builtin_clz(isr & -isr); /* ffs */ 217 intr += index; 218 219 intr->intr_count.ev_count++; 220 LIST_FOREACH(ih, &intr->intr_qh, ih_q) { 221 rv |= (*ih->ih_func)(ih->ih_arg); 222 } 223 isr >>= index + 1; 224 intr++; 225 } 226 227 return rv; 228 } 229 230 static void 231 genath_iointr(int cpl, vaddr_t pc, uint32_t ipending) 232 { 233 struct atheros_intr *intr = &cpu_intrs[NINTRS-1]; 234 235 /* move ipending to the most significant bits */ 236 ipending *= __BIT(31) / (MIPS_INT_MASK_0 << (NINTRS-1)); 237 while (ipending != 0) { 238 struct atheros_intrhand *ih; 239 int index = __builtin_clz(ipending); 240 241 intr -= index; 242 ipending <<= index; 243 KASSERT(ipending & __BIT(31)); 244 KASSERT(intr >= cpu_intrs); 245 246 intr->intr_count.ev_count++; 247 LIST_FOREACH(ih, &intr->intr_qh, ih_q) { 248 (*ih->ih_func)(ih->ih_arg); 249 } 250 ipending <<= 1; 251 intr--; 252 } 253 } 254 255 static void 256 genath_intr_init(void) 257 { 258 const struct atheros_platformsw * const apsw = platformsw; 259 260 KASSERT(apsw->apsw_ipl_sr_map != NULL); 261 ipl_sr_map = *apsw->apsw_ipl_sr_map; 262 263 for (size_t i = 0; i < apsw->apsw_cpu_nintrs; i++) { 264 if (apsw->apsw_cpu_intrnames[i] != NULL) { 265 LIST_INIT(&cpu_intrs[i].intr_qh); 266 evcnt_attach_dynamic(&cpu_intrs[i].intr_count, 267 EVCNT_TYPE_INTR, NULL, "cpu", 268 apsw->apsw_cpu_intrnames[i]); 269 } 270 } 271 272 for (size_t i = 0; i < apsw->apsw_misc_nintrs; i++) { 273 if (apsw->apsw_misc_intrnames[i] != NULL) { 274 LIST_INIT(&misc_intrs[i].intr_qh); 275 evcnt_attach_dynamic(&misc_intrs[i].intr_count, 276 EVCNT_TYPE_INTR, NULL, "misc", 277 apsw->apsw_misc_intrnames[i]); 278 } 279 } 280 281 /* make sure we start without any misc interrupts enabled */ 282 (void) misc_intstat_get(); 283 misc_intmask_put(0); 284 misc_intstat_put(0); 285 286 /* make sure we register the MISC interrupt handler */ 287 genath_cpu_intr_establish(apsw->apsw_cpuirq_misc, 288 genath_misc_intr, misc_intrs); 289 } 290 291 292 const struct atheros_intrsw atheros_intrsw = { 293 .aisw_init = genath_intr_init, 294 .aisw_cpu_establish = genath_cpu_intr_establish, 295 .aisw_cpu_disestablish = genath_cpu_intr_disestablish, 296 .aisw_misc_establish = genath_misc_intr_establish, 297 .aisw_misc_disestablish = genath_misc_intr_disestablish, 298 .aisw_iointr = genath_iointr, 299 }; 300 301