ar_intr.c revision 1.4
1/* $NetBSD: ar_intr.c,v 1.4 2015/06/09 22:50:50 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.4 2015/06/09 22:50:50 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 68struct atheros_intrhand { 69 LIST_ENTRY(atheros_intrhand) ih_q; 70 int (*ih_func)(void *); 71 void *ih_arg; 72 int ih_irq; 73}; 74 75struct atheros_intr { 76 LIST_HEAD(, atheros_intrhand) intr_qh; 77 struct evcnt intr_count; 78}; 79 80static struct atheros_intr cpu_intrs[NINTRS]; 81static struct atheros_intr misc_intrs[NIRQS]; 82 83static uint32_t 84misc_intstat_get(void) 85{ 86 return REGVAL(platformsw->apsw_misc_intstat); 87} 88 89static void 90misc_intstat_put(uint32_t v) 91{ 92 REGVAL(platformsw->apsw_misc_intstat) = v; 93} 94 95static uint32_t 96misc_intmask_get(void) 97{ 98 return REGVAL(platformsw->apsw_misc_intmask); 99} 100 101static void 102misc_intmask_put(uint32_t v) 103{ 104 REGVAL(platformsw->apsw_misc_intmask) = v; 105} 106 107 108static void * 109genath_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 136static void 137genath_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 149static void * 150genath_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 182static void 183genath_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 202static int 203genath_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 232static void 233genath_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 257static void 258genath_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 294const 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