1 /* $NetBSD: adm5120_intr.c,v 1.3 2008/04/28 20:23:27 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or 8 * without modification, are permitted provided that the following 9 * conditions are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following 14 * disclaimer in the documentation and/or other materials provided 15 * with the distribution. 16 * 3. The names of the authors may not be used to endorse or promote 17 * products derived from this software without specific prior 18 * written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY 21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 25 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 27 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 29 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 30 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 31 * OF SUCH DAMAGE. 32 */ 33 /*- 34 * Copyright (c) 2001 The NetBSD Foundation, Inc. 35 * All rights reserved. 36 * 37 * This code is derived from software contributed to The NetBSD Foundation 38 * by Jason R. Thorpe. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 50 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 51 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 52 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 53 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 54 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 55 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 56 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 57 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 58 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 59 * POSSIBILITY OF SUCH DAMAGE. 60 */ 61 62 /* 63 * Platform-specific interrupt support for the Alchemy Semiconductor Pb1000. 64 * 65 * The Alchemy Semiconductor Pb1000's interrupts are wired to two internal 66 * interrupt controllers. 67 */ 68 69 #include <sys/cdefs.h> 70 __KERNEL_RCSID(0, "$NetBSD: adm5120_intr.c,v 1.3 2008/04/28 20:23:27 martin Exp $"); 71 72 #include "opt_ddb.h" 73 74 #include <sys/param.h> 75 #include <sys/queue.h> 76 #include <sys/malloc.h> 77 #include <sys/systm.h> 78 #include <sys/device.h> 79 #include <sys/kernel.h> 80 81 #include <machine/bus.h> 82 #include <machine/intr.h> 83 84 #include <mips/locore.h> 85 #include <mips/adm5120/include/adm5120reg.h> 86 #include <mips/adm5120/include/adm5120var.h> 87 88 #include <dev/pci/pcireg.h> 89 #include <dev/pci/pcivar.h> 90 91 /* 92 * This is a mask of bits to clear in the SR when we go to a 93 * given hardware interrupt priority level. 94 */ 95 const uint32_t ipl_sr_bits[_IPL_N] = { 96 0, /* 0: IPL_NONE */ 97 MIPS_SOFT_INT_MASK_0, /* 1: IPL_SOFTCLOCK */ 98 MIPS_SOFT_INT_MASK_0, /* 2: IPL_SOFTNET */ 99 100 MIPS_SOFT_INT_MASK_0| 101 MIPS_SOFT_INT_MASK_1| 102 MIPS_INT_MASK_0, /* 3: IPL_VM */ 103 104 MIPS_SOFT_INT_MASK_0| 105 MIPS_SOFT_INT_MASK_1| 106 MIPS_INT_MASK_0| 107 MIPS_INT_MASK_1| 108 MIPS_INT_MASK_2| 109 MIPS_INT_MASK_3| 110 MIPS_INT_MASK_4| 111 MIPS_INT_MASK_5, /* 4: IPL_{SCHED,HIGH} */ 112 }; 113 114 #define NIRQS 32 115 const char *adm5120_intrnames[NIRQS] = { 116 "timer", /* 0 */ 117 "uart0", /* 1 */ 118 "uart1", /* 2 */ 119 "usb", /* 3 */ 120 "intx0/gpio2", /* 4 */ 121 "intx1/gpio4", /* 5 */ 122 "pci0", /* 6 */ 123 "pci1", /* 7 */ 124 "pci2", /* 8 */ 125 "switch",/* 9 */ 126 "res10", /* 10 */ 127 "res11", /* 11 */ 128 "res12", /* 12 */ 129 "res13", /* 13 */ 130 "res14", /* 14 */ 131 "res15", /* 15 */ 132 "res16", /* 16 */ 133 "res17", /* 17 */ 134 "res18", /* 18 */ 135 "res19", /* 19 */ 136 "res20", /* 20 */ 137 "res21", /* 21 */ 138 "res22", /* 22 */ 139 "res23", /* 23 */ 140 "res24", /* 24 */ 141 "res25", /* 25 */ 142 "res26", /* 26 */ 143 "res27", /* 27 */ 144 "res28", /* 28 */ 145 "res29", /* 29 */ 146 "res30", /* 30 */ 147 "res31", /* 31 */ 148 }; 149 150 struct adm5120_intrhead { 151 struct evcnt intr_count; 152 int intr_refcnt; 153 }; 154 struct adm5120_intrhead adm5120_intrtab[NIRQS]; 155 156 157 #define NINTRS 2 /* MIPS INT0 - INT1 */ 158 struct adm5120_cpuintr { 159 LIST_HEAD(, evbmips_intrhand) cintr_list; 160 struct evcnt cintr_count; 161 }; 162 struct adm5120_cpuintr adm5120_cpuintrs[NINTRS]; 163 164 const char *adm5120_cpuintrnames[NINTRS] = { 165 "int 0 (irq)", 166 "int 1 (fiq)", 167 }; 168 169 #define REG_READ(o) *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1(ADM5120_BASE_ICU + (o))) 170 #define REG_WRITE(o,v) (REG_READ(o)) = (v) 171 172 void 173 evbmips_intr_init(void) 174 { 175 int i; 176 177 for (i = 0; i < NINTRS; i++) { 178 LIST_INIT(&adm5120_cpuintrs[i].cintr_list); 179 evcnt_attach_dynamic(&adm5120_cpuintrs[i].cintr_count, 180 EVCNT_TYPE_INTR, NULL, "mips", adm5120_cpuintrnames[i]); 181 } 182 183 for (i = 0; i < NIRQS; i++) { 184 /* XXX steering - use an irqmap array? */ 185 186 adm5120_intrtab[i].intr_refcnt = 0; 187 evcnt_attach_dynamic(&adm5120_intrtab[i].intr_count, 188 EVCNT_TYPE_INTR, NULL, "adm5120", adm5120_intrnames[i]); 189 } 190 191 /* disable all interrupts */ 192 REG_WRITE(ICU_DISABLE_REG, ICU_INT_MASK); 193 } 194 195 void * 196 adm5120_intr_establish(int irq, int priority, int (*func)(void *), void *arg) 197 { 198 struct evbmips_intrhand *ih; 199 uint32_t irqmask; 200 int cpu_int, s; 201 202 if (irq < 0 || irq >= NIRQS) 203 panic("adm5120_intr_establish: bogus IRQ %d", irq); 204 205 ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT); 206 if (ih == NULL) 207 return NULL; 208 209 ih->ih_func = func; 210 ih->ih_arg = arg; 211 ih->ih_irq = irq; 212 213 s = splhigh(); 214 215 /* 216 * First, link it into the tables. 217 * XXX do we want a separate list (really, should only be one item, not 218 * a list anyway) per irq, not per CPU interrupt? 219 */ 220 221 cpu_int = (priority == INTR_FIQ) ? 1 : 0; 222 223 LIST_INSERT_HEAD(&adm5120_cpuintrs[cpu_int].cintr_list, ih, ih_q); 224 225 /* 226 * Now enable it. 227 */ 228 if (adm5120_intrtab[irq].intr_refcnt++ == 0) { 229 irqmask = 1 << irq; 230 231 /* configure as IRQ or FIQ */ 232 if (priority == INTR_FIQ) { 233 REG_WRITE(ICU_MODE_REG, 234 REG_READ(ICU_MODE_REG) | irqmask); 235 } else { 236 REG_WRITE(ICU_MODE_REG, 237 REG_READ(ICU_MODE_REG) & ~irqmask); 238 } 239 /* enable */ 240 REG_WRITE(ICU_ENABLE_REG, irqmask); 241 } 242 splx(s); 243 244 return ih; 245 } 246 247 void 248 adm5120_intr_disestablish(void *cookie) 249 { 250 struct evbmips_intrhand *ih = cookie; 251 int irq, s; 252 uint32_t irqmask; 253 254 irq = ih->ih_irq; 255 256 s = splhigh(); 257 258 /* 259 * First, remove it from the table. 260 */ 261 LIST_REMOVE(ih, ih_q); 262 263 /* 264 * Now, disable it, if there is nothing remaining on the 265 * list. 266 */ 267 if (adm5120_intrtab[irq].intr_refcnt-- == 1) { 268 irqmask = 1 << irq; /* only used as a mask from here on */ 269 270 /* disable this irq in HW */ 271 REG_WRITE(ICU_DISABLE_REG, irqmask); 272 } 273 274 splx(s); 275 276 free(ih, M_DEVBUF); 277 } 278 void 279 evbmips_iointr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending) 280 { 281 struct evbmips_intrhand *ih; 282 int level; 283 uint32_t irqmask, irqstat; 284 285 for (level = NINTRS - 1; level >= 0; level--) { 286 if ((ipending & (MIPS_INT_MASK_0 << level)) == 0) 287 continue; 288 289 if (level) 290 irqstat = REG_READ(ICU_FIQ_STATUS_REG); 291 else 292 irqstat = REG_READ(ICU_STATUS_REG); 293 294 adm5120_cpuintrs[level].cintr_count.ev_count++; 295 LIST_FOREACH(ih, &adm5120_cpuintrs[level].cintr_list, ih_q) { 296 irqmask = 1 << ih->ih_irq; 297 if (irqmask & irqstat) { 298 adm5120_intrtab[ih->ih_irq].intr_count.ev_count++; 299 (*ih->ih_func)(ih->ih_arg); 300 } 301 } 302 cause &= ~(MIPS_INT_MASK_0 << level); 303 } 304 305 /* Re-enable anything that we have processed. */ 306 _splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK)); 307 308 return; 309 } 310