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