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