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