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 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 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 131static void 132genath_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 144static void * 145genath_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 175static void 176genath_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 195static int 196genath_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 225static void 226genath_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 250static void 251genath_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 287const 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