1 /* $NetBSD: loongson_intr.c,v 1.8 2020/11/21 15:36:36 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * 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 copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: loongson_intr.c,v 1.8 2020/11/21 15:36:36 thorpej Exp $"); 34 35 #define __INTR_PRIVATE 36 37 #include <sys/param.h> 38 #include <sys/device.h> 39 #include <sys/cpu.h> 40 #include <sys/intr.h> 41 #include <sys/bus.h> 42 #include <sys/kmem.h> 43 44 #include <mips/mips3_clock.h> 45 #include <machine/locore.h> 46 47 #include <evbmips/loongson/autoconf.h> 48 #include <evbmips/loongson/loongson_intr.h> 49 50 #include <mips/locore.h> 51 52 #include <mips/bonito/bonitoreg.h> 53 #include <mips/bonito/bonitovar.h> 54 55 #include <dev/pci/pciidereg.h> 56 #include <dev/isa/isavar.h> 57 58 #include "isa.h" 59 60 #ifdef INTR_DEBUG 61 #define DPRINTF(x) printf x 62 #else 63 #define DPRINTF(x) 64 #endif 65 66 struct bonito_intrhead bonito_intrhead[BONITO_NINTS]; 67 68 /* 69 * This is a mask of bits to clear in the SR when we go to a 70 * given hardware interrupt priority level. 71 */ 72 static const struct ipl_sr_map loongson_ipl_sr_map = { 73 .sr_bits = { 74 [IPL_NONE] = 0, 75 [IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0, 76 [IPL_SOFTNET] = MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1, 77 [IPL_VM] = 78 MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 | 79 MIPS_INT_MASK_0 | 80 MIPS_INT_MASK_1 | 81 MIPS_INT_MASK_2 | 82 MIPS_INT_MASK_3 | 83 MIPS_INT_MASK_4, 84 [IPL_SCHED] = 85 MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 | 86 MIPS_INT_MASK_0 | 87 MIPS_INT_MASK_1 | 88 MIPS_INT_MASK_2 | 89 MIPS_INT_MASK_3 | 90 MIPS_INT_MASK_4 | 91 MIPS_INT_MASK_5, 92 [IPL_DDB] = MIPS_INT_MASK, 93 [IPL_HIGH] = MIPS_INT_MASK, 94 }, 95 }; 96 97 98 void 99 evbmips_intr_init(void) 100 { 101 const struct bonito_config * const bc = sys_platform->bonito_config; 102 const struct bonito_irqmap *irqmap; 103 int i; 104 105 ipl_sr_map = loongson_ipl_sr_map; 106 107 for (i = 0; i < BONITO_NDIRECT; i++) { 108 irqmap = &sys_platform->irq_map[i]; 109 if (irqmap->name == NULL) 110 continue; 111 DPRINTF(("attach %d %s\n", i, irqmap->name)); 112 evcnt_attach_dynamic(&bonito_intrhead[i].intr_count, 113 EVCNT_TYPE_INTR, NULL, "bonito", irqmap->name); 114 LIST_INIT(&bonito_intrhead[i].intrhand_head); 115 } 116 117 REGVAL(BONITO_GPIOIE) = bc->bc_gpioIE; 118 REGVAL(BONITO_INTEDGE) = bc->bc_intEdge; 119 /* REGVAL(BONITO_INTSTEER) = bc->bc_intSteer; XXX */ 120 REGVAL(BONITO_INTPOL) = bc->bc_intPol; 121 REGVAL(BONITO_INTENCLR) = 0xffffffff; 122 (void)REGVAL(BONITO_INTENCLR); 123 124 if (sys_platform->isa_chipset != NULL) { 125 int irq; 126 static char irqstr[8]; 127 for (irq = 0; irq < BONITO_NISA; irq++) { 128 i = BONITO_ISA_IRQ(irq); 129 snprintf(irqstr, sizeof(irqstr), "irq %d", irq); 130 DPRINTF(("attach %d %d %s\n", i, irq, irqstr)); 131 evcnt_attach_dynamic(&bonito_intrhead[i].intr_count, 132 EVCNT_TYPE_INTR, NULL, "isa", irqstr); 133 LIST_INIT(&bonito_intrhead[i].intrhand_head); 134 } 135 } 136 } 137 138 void 139 evbmips_iointr(int ppl, uint32_t ipending, struct clockframe *cf) 140 { 141 struct evbmips_intrhand *ih; 142 int irq; 143 uint32_t isr0, isr, imr; 144 145 /* 146 * Read the interrupt pending registers, mask them with the 147 * ones we have enabled, and service them in order of decreasing 148 * priority. 149 */ 150 isr0 = REGVAL(BONITO_INTISR); 151 imr = REGVAL(BONITO_INTEN); 152 153 if (ipending & sys_platform->bonito_mips_intr) { 154 isr = isr0 & imr & LOONGSON_INTRMASK_LVL4; 155 156 REGVAL(BONITO_INTENCLR) = isr; 157 (void)REGVAL(BONITO_INTENCLR); 158 159 for (irq = 0; irq < BONITO_NINTS; irq++) { 160 if ((isr & (1 << irq)) == 0) 161 continue; 162 bonito_intrhead[irq].intr_count.ev_count++; 163 LIST_FOREACH (ih, 164 &bonito_intrhead[irq].intrhand_head, ih_q) { 165 if (ih->ih_arg) 166 (*ih->ih_func)(ih->ih_arg); 167 else 168 (*ih->ih_func)(cf); 169 } 170 } 171 REGVAL(BONITO_INTENSET) = isr; 172 (void)REGVAL(BONITO_INTENSET); 173 } 174 if (isr0 & LOONGSON_INTRMASK_INT0) 175 sys_platform->isa_intr(ppl, cf->pc, ipending); 176 } 177 178 void * 179 loongson_pciide_compat_intr_establish(void *v, device_t dev, 180 const struct pci_attach_args *pa, int chan, int (*func)(void *), void *arg) 181 { 182 pci_chipset_tag_t pc = pa->pa_pc; 183 void *cookie; 184 int bus, irq; 185 char buf[PCI_INTRSTR_LEN]; 186 187 pci_decompose_tag(pc, pa->pa_tag, &bus, NULL, NULL); 188 189 /* 190 * If this isn't PCI bus #0, all bets are off. 191 */ 192 if (bus != 0) 193 return (NULL); 194 195 irq = PCIIDE_COMPAT_IRQ(chan); 196 #if NISA > 0 197 if (sys_platform->isa_chipset != NULL) 198 cookie = isa_intr_establish(sys_platform->isa_chipset, irq, 199 IST_EDGE, IPL_BIO, func, arg); 200 else 201 #endif 202 cookie = NULL; 203 if (cookie == NULL) 204 return (NULL); 205 printf("%s: %s channel interrupting at %s\n", device_xname(dev), 206 PCIIDE_CHANNEL_NAME(chan), 207 isa_intr_string(sys_platform->isa_chipset, irq, buf, sizeof(buf))); 208 return (cookie); 209 } 210 211 int 212 loongson_pci_intr_map(const struct pci_attach_args *pa, 213 pci_intr_handle_t *ihp) 214 { 215 pcitag_t bustag = pa->pa_intrtag; 216 int buspin = pa->pa_intrpin; 217 pci_chipset_tag_t pc = pa->pa_pc; 218 int device, function; 219 220 if (buspin == 0) { 221 /* No IRQ used. */ 222 return (1); 223 } 224 225 if (buspin > 4) { 226 printf("loongson_pci_intr_map: bad interrupt pin %d\n", 227 buspin); 228 return (1); 229 } 230 231 pci_decompose_tag(pc, bustag, NULL, &device, &function); 232 return (sys_platform->p_pci_intr_map(device, function, buspin, ihp)); 233 } 234 235 const char * 236 loongson_pci_intr_string(void *v, pci_intr_handle_t ih, char *buf, size_t len) 237 { 238 239 const struct bonito_config *bc = v; 240 return loongson_intr_string(bc, ih, buf, len); 241 } 242 243 const struct evcnt * 244 loongson_pci_intr_evcnt(void *v, pci_intr_handle_t ih) 245 { 246 247 return &bonito_intrhead[ih].intr_count; 248 } 249 250 void * 251 loongson_pci_intr_establish(void *v, pci_intr_handle_t ih, int level, 252 int (*func)(void *), void *arg) 253 { 254 if (BONITO_IRQ_IS_ISA(ih)) { 255 if (sys_platform->isa_chipset == NULL) 256 panic("ISA interrupt on non-ISA platform"); 257 return sys_platform->isa_chipset->ic_intr_establish(v, 258 BONITO_IRQ_TO_ISA(ih), IST_EDGE, level, func, arg); 259 } 260 return evbmips_intr_establish(ih, func, arg); 261 } 262 263 void 264 loongson_pci_intr_disestablish(void *v, void *cookie) 265 { 266 struct evbmips_intrhand *ih = cookie; 267 if (BONITO_IRQ_IS_ISA(ih->ih_irq)) { 268 if (sys_platform->isa_chipset == NULL) 269 panic("ISA interrupt on non-ISA platform"); 270 sys_platform->isa_chipset->ic_intr_disestablish(v, ih); 271 return; 272 } 273 return (evbmips_intr_disestablish(cookie)); 274 } 275 276 void 277 loongson_pci_conf_interrupt(void *v, int bus, int dev, int pin, int swiz, 278 int *iline) 279 { 280 281 /* 282 * We actually don't need to do anything; everything is handled 283 * in pci_intr_map(). 284 */ 285 *iline = 0; 286 } 287 288 289 void * 290 evbmips_intr_establish(int irq, int (*func)(void *), void *arg) 291 { 292 struct evbmips_intrhand *ih; 293 int s; 294 295 KASSERT(irq < BONITO_NINTS); 296 DPRINTF(("loongson_intr_establish %d %p", irq, func)); 297 298 ih = kmem_zalloc(sizeof(*ih), KM_SLEEP); 299 ih->ih_func = func; 300 ih->ih_arg = arg; 301 ih->ih_irq = irq; 302 DPRINTF((" ih %p", ih)); 303 304 /* link it into tables */ 305 s = splhigh(); 306 LIST_INSERT_HEAD(&bonito_intrhead[irq].intrhand_head, ih, ih_q); 307 /* and enable it */ 308 DPRINTF((" inten 0x%x", REGVAL(BONITO_INTEN))); 309 if (bonito_intrhead[irq].refcnt++ == 0 && !BONITO_IRQ_IS_ISA(irq)) 310 REGVAL(BONITO_INTENSET) = (1 << ih->ih_irq); 311 DPRINTF((" now 0x%x\n", REGVAL(BONITO_INTEN))); 312 splx(s); 313 314 return (ih); 315 } 316 317 void 318 evbmips_intr_disestablish(void *cookie) 319 { 320 struct evbmips_intrhand *ih = cookie; 321 int s; 322 323 s = splhigh(); 324 LIST_REMOVE(ih, ih_q); 325 bonito_intrhead[ih->ih_irq].refcnt--; 326 if (bonito_intrhead[ih->ih_irq].refcnt == 0 && 327 !BONITO_IRQ_IS_ISA(ih->ih_irq)) 328 REGVAL(BONITO_INTENCLR) = (1 << ih->ih_irq); 329 splx(s); 330 kmem_free(ih, sizeof(*ih)); 331 } 332 333 const char * 334 loongson_intr_string(const struct bonito_config *bc, int irq, char *buf, size_t len) 335 { 336 if (BONITO_IRQ_IS_ISA(irq)) 337 snprintf(buf, len, "isa irq %d", BONITO_IRQ_TO_ISA(irq)); 338 else 339 strlcpy(buf, sys_platform->irq_map[irq].name, len); 340 return buf; 341 } 342