1 /* $NetBSD: au_icu.c,v 1.12 2005/12/24 20:07:19 perry 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Interrupt support for the Alchemy Semiconductor Au1x00 CPUs. 41 * 42 * The Alchemy Semiconductor Au1x00's interrupts are wired to two internal 43 * interrupt controllers. 44 */ 45 46 #include <sys/cdefs.h> 47 __KERNEL_RCSID(0, "$NetBSD: au_icu.c,v 1.12 2005/12/24 20:07:19 perry Exp $"); 48 49 #include "opt_ddb.h" 50 51 #include <sys/param.h> 52 #include <sys/queue.h> 53 #include <sys/malloc.h> 54 #include <sys/systm.h> 55 #include <sys/device.h> 56 #include <sys/kernel.h> 57 58 #include <machine/bus.h> 59 #include <machine/intr.h> 60 61 #include <mips/locore.h> 62 #include <mips/alchemy/include/aureg.h> 63 #include <mips/alchemy/include/auvar.h> 64 65 #define REGVAL(x) *((volatile u_int32_t *)(MIPS_PHYS_TO_KSEG1((x)))) 66 67 /* 68 * This is a mask of bits to clear in the SR when we go to a 69 * given hardware interrupt priority level. 70 */ 71 72 const u_int32_t ipl_sr_bits[_IPL_N] = { 73 0, /* 0: IPL_NONE */ 74 75 MIPS_SOFT_INT_MASK_0, /* 1: IPL_SOFT */ 76 77 MIPS_SOFT_INT_MASK_0, /* 2: IPL_SOFTCLOCK */ 78 79 MIPS_SOFT_INT_MASK_0, /* 3: IPL_SOFTNET */ 80 81 MIPS_SOFT_INT_MASK_0, /* 4: IPL_SOFTSERIAL */ 82 83 MIPS_SOFT_INT_MASK_0| 84 MIPS_SOFT_INT_MASK_1| 85 MIPS_INT_MASK_0, /* 5: IPL_BIO */ 86 87 MIPS_SOFT_INT_MASK_0| 88 MIPS_SOFT_INT_MASK_1| 89 MIPS_INT_MASK_0, /* 6: IPL_NET */ 90 91 MIPS_SOFT_INT_MASK_0| 92 MIPS_SOFT_INT_MASK_1| 93 MIPS_INT_MASK_0, /* 7: IPL_{SERIAL,TTY} */ 94 95 MIPS_SOFT_INT_MASK_0| 96 MIPS_SOFT_INT_MASK_1| 97 MIPS_INT_MASK_0| 98 MIPS_INT_MASK_1| 99 MIPS_INT_MASK_2| 100 MIPS_INT_MASK_3| 101 MIPS_INT_MASK_4| 102 MIPS_INT_MASK_5, /* 8: IPL_{CLOCK,HIGH} */ 103 }; 104 105 /* 106 * This is a mask of bits to clear in the SR when we go to a 107 * given software interrupt priority level. 108 * Hardware ipls are port/board specific. 109 */ 110 const u_int32_t mips_ipl_si_to_sr[_IPL_NSOFT] = { 111 MIPS_SOFT_INT_MASK_0, /* IPL_SOFT */ 112 MIPS_SOFT_INT_MASK_0, /* IPL_SOFTCLOCK */ 113 MIPS_SOFT_INT_MASK_0, /* IPL_SOFTNET */ 114 MIPS_SOFT_INT_MASK_0, /* IPL_SOFTSERIAL */ 115 }; 116 117 #define NIRQS 64 118 119 const char *au1000_intrnames[NIRQS] = { 120 "uart0", 121 "uart1", 122 "uart2", 123 "uart3", 124 "ssi0", 125 "ssi1", 126 "dma0", 127 "dma1", 128 "dma2", 129 "dma3", 130 "dma4", 131 "dma5", 132 "dma6", 133 "dma7", 134 "pc0", 135 "pc0 match1", 136 "pc0 match2", 137 "pc0 match3", 138 "pc1", 139 "pc1 match1", 140 "pc1 match2", 141 "pc1 match3", 142 "irda tx", 143 "irda rx", 144 "usb intr", 145 "usb suspend", 146 "usb host", 147 "ac97", 148 "mac0", 149 "mac1", 150 "i2s", 151 "ac97 cmd", 152 153 "gpio 0", 154 "gpio 1", 155 "gpio 2", 156 "gpio 3", 157 "gpio 4", 158 "gpio 5", 159 "gpio 6", 160 "gpio 7", 161 "gpio 8", 162 "gpio 9", 163 "gpio 10", 164 "gpio 11", 165 "gpio 12", 166 "gpio 13", 167 "gpio 14", 168 "gpio 15", 169 "gpio 16", 170 "gpio 17", 171 "gpio 18", 172 "gpio 19", 173 "gpio 20", 174 "gpio 21", 175 "gpio 22", 176 "gpio 23", 177 "gpio 24", 178 "gpio 25", 179 "gpio 26", 180 "gpio 27", 181 "gpio 28", 182 "gpio 29", 183 "gpio 30", 184 "gpio 31", 185 }; 186 187 struct au1000_intrhead { 188 struct evcnt intr_count; 189 int intr_refcnt; 190 }; 191 struct au1000_intrhead au1000_intrtab[NIRQS]; 192 193 #define NINTRS 4 /* MIPS INT0 - INT3 */ 194 195 struct au1000_cpuintr { 196 LIST_HEAD(, evbmips_intrhand) cintr_list; 197 struct evcnt cintr_count; 198 }; 199 200 struct au1000_cpuintr au1000_cpuintrs[NINTRS]; 201 const char *au1000_cpuintrnames[NINTRS] = { 202 "icu 0, req 0", 203 "icu 0, req 1", 204 "icu 1, req 0", 205 "icu 1, req 1", 206 }; 207 208 void 209 au_intr_init(void) 210 { 211 int i; 212 213 for (i = 0; i < NINTRS; i++) { 214 LIST_INIT(&au1000_cpuintrs[i].cintr_list); 215 evcnt_attach_dynamic(&au1000_cpuintrs[i].cintr_count, 216 EVCNT_TYPE_INTR, NULL, "mips", au1000_cpuintrnames[i]); 217 } 218 219 for (i = 0; i < NIRQS; i++) { 220 /* XXX steering - use an irqmap array? */ 221 222 au1000_intrtab[i].intr_refcnt = 0; 223 evcnt_attach_dynamic(&au1000_intrtab[i].intr_count, 224 EVCNT_TYPE_INTR, NULL, "au1000", au1000_intrnames[i]); 225 } 226 } 227 228 void * 229 au_intr_establish(int irq, int req, int level, int type, 230 int (*func)(void *), void *arg) 231 { 232 struct evbmips_intrhand *ih; 233 uint32_t icu_base; 234 int cpu_int, s; 235 236 if (irq >= NIRQS) 237 panic("au_intr_establish: bogus IRQ %d", irq); 238 if (req > 1) 239 panic("au_intr_establish: bogus request %d", req); 240 241 ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT); 242 if (ih == NULL) 243 return (NULL); 244 245 ih->ih_func = func; 246 ih->ih_arg = arg; 247 ih->ih_irq = irq; 248 249 s = splhigh(); 250 251 /* 252 * First, link it into the tables. 253 * XXX do we want a separate list (really, should only be one item, not 254 * a list anyway) per irq, not per CPU interrupt? 255 */ 256 cpu_int = (irq < 32 ? 0 : 2); 257 LIST_INSERT_HEAD(&au1000_cpuintrs[cpu_int].cintr_list, ih, ih_q); 258 259 /* 260 * Now enable it. 261 */ 262 if (au1000_intrtab[irq].intr_refcnt++ == 0) { 263 icu_base = (irq < 32) ? IC0_BASE : IC1_BASE; 264 265 irq &= 31; /* throw away high bit if set */ 266 irq = 1 << irq; /* only used as a mask from here on */ 267 268 /* XXX Only high-level interrupts for now */ 269 switch (type) { 270 case IST_NONE: 271 case IST_PULSE: 272 case IST_EDGE: 273 panic("unsupported irq type %d", type); 274 /* NOTREACHED */ 275 case IST_LEVEL: 276 case IST_LEVEL_HIGH: 277 REGVAL(icu_base + IC_CONFIG2_SET) = irq; 278 REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq; 279 REGVAL(icu_base + IC_CONFIG0_SET) = irq; 280 break; 281 case IST_LEVEL_LOW: 282 REGVAL(icu_base + IC_CONFIG2_SET) = irq; 283 REGVAL(icu_base + IC_CONFIG1_SET) = irq; 284 REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq; 285 break; 286 } 287 288 /* XXX handle GPIO interrupts - not done at all yet */ 289 if (cpu_int & 0x1) 290 REGVAL(icu_base + IC_ASSIGN_REQUEST_CLEAR) = irq; 291 else 292 REGVAL(icu_base + IC_ASSIGN_REQUEST_SET) = irq; 293 294 /* Associate interrupt with peripheral */ 295 REGVAL(icu_base + IC_SOURCE_SET) = irq; 296 297 /* Actually enable the interrupt */ 298 REGVAL(icu_base + IC_MASK_SET) = irq; 299 300 /* And allow the interrupt to interrupt idle */ 301 REGVAL(icu_base + IC_WAKEUP_SET) = irq; 302 } 303 splx(s); 304 305 return (ih); 306 } 307 308 void 309 au_intr_disestablish(void *cookie) 310 { 311 struct evbmips_intrhand *ih = cookie; 312 uint32_t icu_base; 313 int irq, s; 314 315 irq = ih->ih_irq; 316 317 s = splhigh(); 318 319 /* 320 * First, remove it from the table. 321 */ 322 LIST_REMOVE(ih, ih_q); 323 324 /* 325 * Now, disable it, if there is nothing remaining on the 326 * list. 327 */ 328 if (au1000_intrtab[irq].intr_refcnt-- == 1) { 329 icu_base = (irq < 32) ? IC0_BASE : IC1_BASE; 330 331 irq &= 31; /* throw away high bit if set */ 332 irq = 1 << irq; /* only used as a mask from here on */ 333 334 REGVAL(icu_base + IC_CONFIG2_CLEAR) = irq; 335 REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq; 336 REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq; 337 338 /* XXX disable with MASK_CLEAR and WAKEUP_CLEAR */ 339 } 340 341 splx(s); 342 343 free(ih, M_DEVBUF); 344 } 345 346 void 347 au_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc, u_int32_t ipending) 348 { 349 struct evbmips_intrhand *ih; 350 int level; 351 u_int32_t icu_base = 0, irqmask = 0; /* Both XXX gcc */ 352 353 for (level = 3; level >= 0; level--) { 354 if ((ipending & (MIPS_INT_MASK_0 << level)) == 0) 355 continue; 356 357 /* 358 * XXX the following may well be slow to execute. 359 * investigate and possibly speed up. 360 * 361 * is something like: 362 * 363 * irqmask = REGVAL( 364 * (level & 4 == 0) ? IC0_BASE ? IC1_BASE + 365 * (level & 2 == 0) ? IC_REQUEST0_INT : IC_REQUEST1_INT); 366 * 367 * be any better? 368 * 369 */ 370 switch (level) { 371 case 0: 372 icu_base = IC0_BASE; 373 irqmask = REGVAL(icu_base + IC_REQUEST0_INT); 374 break; 375 case 1: 376 icu_base = IC0_BASE; 377 irqmask = REGVAL(icu_base + IC_REQUEST1_INT); 378 break; 379 case 2: 380 icu_base = IC1_BASE; 381 irqmask = REGVAL(icu_base + IC_REQUEST0_INT); 382 break; 383 case 3: 384 icu_base = IC1_BASE; 385 irqmask = REGVAL(icu_base + IC_REQUEST1_INT); 386 break; 387 } 388 au1000_cpuintrs[level].cintr_count.ev_count++; 389 LIST_FOREACH(ih, &au1000_cpuintrs[level].cintr_list, ih_q) { 390 /* XXX should check is see if interrupt is masked? */ 391 if (1 << ih->ih_irq & irqmask) { 392 au1000_intrtab[ih->ih_irq].intr_count.ev_count++; 393 (*ih->ih_func)(ih->ih_arg); 394 395 REGVAL(icu_base + IC_MASK_CLEAR) = 1 << ih->ih_irq; 396 REGVAL(icu_base + IC_MASK_SET) = 1 << ih->ih_irq; 397 } 398 } 399 cause &= ~(MIPS_INT_MASK_0 << level); 400 } 401 402 /* Re-enable anything that we have processed. */ 403 _splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK)); 404 } 405