ar_intr.c revision 1.5
1/* $NetBSD: ar_intr.c,v 1.5 2015/06/29 16:36:17 maxv 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.5 2015/06/29 16:36:17 maxv 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 const int s = splhigh(); 121 122 LIST_INSERT_HEAD(&cpu_intrs[intr].intr_qh, ih, ih_q); 123 124 /* 125 * The MIPS CPU interrupts are enabled at boot time, so they 126 * should pretty much always be ready to go. 127 */ 128 129 splx(s); 130 return (ih); 131} 132 133static void 134genath_cpu_intr_disestablish(void *arg) 135{ 136 struct atheros_intrhand * const ih = arg; 137 138 const int s = splhigh(); 139 140 LIST_REMOVE(ih, ih_q); 141 142 splx(s); 143 free(ih, M_DEVBUF); 144} 145 146static void * 147genath_misc_intr_establish(int irq, int (*func)(void *), void *arg) 148{ 149 struct atheros_intr * const intr = &misc_intrs[irq]; 150 struct atheros_intrhand *ih; 151 bool first; 152 int s; 153 154 155 if ((ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT)) == NULL) 156 return NULL; 157 158 ih->ih_func = func; 159 ih->ih_arg = arg; 160 ih->ih_irq = irq; 161 162 s = splhigh(); 163 164 first = LIST_EMPTY(&intr->intr_qh); 165 166 LIST_INSERT_HEAD(&intr->intr_qh, ih, ih_q); 167 168 if (first) { 169 const uint32_t mask = misc_intmask_get() | __BIT(irq); 170 misc_intmask_put(mask); 171 (void) misc_intmask_get(); /* flush wbuffer */ 172 } 173 174 splx(s); 175 176 return ih; 177} 178 179static void 180genath_misc_intr_disestablish(void *arg) 181{ 182 struct atheros_intrhand *ih = arg; 183 struct atheros_intr * const intr = &misc_intrs[ih->ih_irq]; 184 185 const int s = splhigh(); 186 187 LIST_REMOVE(ih, ih_q); 188 if (LIST_EMPTY(&intr->intr_qh)) { 189 const uint32_t mask = misc_intmask_get() & ~__BIT(ih->ih_irq); 190 misc_intmask_put(mask); 191 (void) misc_intmask_get(); /* flush wbuffer */ 192 } 193 194 splx(s); 195 free(ih, M_DEVBUF); 196} 197 198 199static int 200genath_misc_intr(void *arg) 201{ 202 uint32_t isr; 203 uint32_t mask; 204 int rv = 0; 205 struct atheros_intr *intr = arg; 206 207 isr = misc_intstat_get(); 208 mask = misc_intmask_get(); 209 210 misc_intstat_put(isr & ~mask); 211 212 isr &= mask; 213 while (isr != 0) { 214 struct atheros_intrhand *ih; 215 int index = 31 - __builtin_clz(isr & -isr); /* ffs */ 216 intr += index; 217 218 intr->intr_count.ev_count++; 219 LIST_FOREACH(ih, &intr->intr_qh, ih_q) { 220 rv |= (*ih->ih_func)(ih->ih_arg); 221 } 222 isr >>= index + 1; 223 intr++; 224 } 225 226 return rv; 227} 228 229static void 230genath_iointr(int cpl, vaddr_t pc, uint32_t ipending) 231{ 232 struct atheros_intr *intr = &cpu_intrs[NINTRS-1]; 233 234 /* move ipending to the most significant bits */ 235 ipending *= __BIT(31) / (MIPS_INT_MASK_0 << (NINTRS-1)); 236 while (ipending != 0) { 237 struct atheros_intrhand *ih; 238 int index = __builtin_clz(ipending); 239 240 intr -= index; 241 ipending <<= index; 242 KASSERT(ipending & __BIT(31)); 243 KASSERT(intr >= cpu_intrs); 244 245 intr->intr_count.ev_count++; 246 LIST_FOREACH(ih, &intr->intr_qh, ih_q) { 247 (*ih->ih_func)(ih->ih_arg); 248 } 249 ipending <<= 1; 250 intr--; 251 } 252} 253 254static void 255genath_intr_init(void) 256{ 257 const struct atheros_platformsw * const apsw = platformsw; 258 259 KASSERT(apsw->apsw_ipl_sr_map != NULL); 260 ipl_sr_map = *apsw->apsw_ipl_sr_map; 261 262 for (size_t i = 0; i < apsw->apsw_cpu_nintrs; i++) { 263 if (apsw->apsw_cpu_intrnames[i] != NULL) { 264 LIST_INIT(&cpu_intrs[i].intr_qh); 265 evcnt_attach_dynamic(&cpu_intrs[i].intr_count, 266 EVCNT_TYPE_INTR, NULL, "cpu", 267 apsw->apsw_cpu_intrnames[i]); 268 } 269 } 270 271 for (size_t i = 0; i < apsw->apsw_misc_nintrs; i++) { 272 if (apsw->apsw_misc_intrnames[i] != NULL) { 273 LIST_INIT(&misc_intrs[i].intr_qh); 274 evcnt_attach_dynamic(&misc_intrs[i].intr_count, 275 EVCNT_TYPE_INTR, NULL, "misc", 276 apsw->apsw_misc_intrnames[i]); 277 } 278 } 279 280 /* make sure we start without any misc interrupts enabled */ 281 (void) misc_intstat_get(); 282 misc_intmask_put(0); 283 misc_intstat_put(0); 284 285 /* make sure we register the MISC interrupt handler */ 286 genath_cpu_intr_establish(apsw->apsw_cpuirq_misc, 287 genath_misc_intr, misc_intrs); 288} 289 290 291const struct atheros_intrsw atheros_intrsw = { 292 .aisw_init = genath_intr_init, 293 .aisw_cpu_establish = genath_cpu_intr_establish, 294 .aisw_cpu_disestablish = genath_cpu_intr_disestablish, 295 .aisw_misc_establish = genath_misc_intr_establish, 296 .aisw_misc_disestablish = genath_misc_intr_disestablish, 297 .aisw_iointr = genath_iointr, 298}; 299 300