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