1 /* $OpenBSD: ampintc.c,v 1.3 2014/07/12 18:44:41 tedu Exp $ */ 2 /* 3 * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* 19 * This driver implements the interrupt controller as specified in 20 * DDI0407E_cortex_a9_mpcore_r2p0_trm with the 21 * IHI0048A_gic_architecture_spec_v1_0 underlying specification 22 */ 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/queue.h> 26 #include <sys/malloc.h> 27 #include <sys/device.h> 28 #include <sys/evcount.h> 29 #include <arm/cpufunc.h> 30 #include <machine/bus.h> 31 #include <arm/cortex/cortex.h> 32 33 /* offset from periphbase */ 34 #define ICP_ADDR 0x100 35 #define ICP_SIZE 0x100 36 #define ICD_ADDR 0x1000 37 #define ICD_SIZE 0x1000 38 39 /* registers */ 40 #define ICD_DCR 0x000 41 #define ICD_DCR_ES 0x00000001 42 #define ICD_DCR_ENS 0x00000002 43 44 #define ICD_ICTR 0x004 45 #define ICD_ICTR_LSPI_SH 11 46 #define ICD_ICTR_LSPI_M 0x1f 47 #define ICD_ICTR_CPU_SH 5 48 #define ICD_ICTR_CPU_M 0x07 49 #define ICD_ICTR_ITL_SH 0 50 #define ICD_ICTR_ITL_M 0x1f 51 #define ICD_IDIR 0x008 52 #define ICD_DIR_PROD_SH 24 53 #define ICD_DIR_PROD_M 0xff 54 #define ICD_DIR_REV_SH 12 55 #define ICD_DIR_REV_M 0xfff 56 #define ICD_DIR_IMP_SH 0 57 #define ICD_DIR_IMP_M 0xfff 58 59 #define IRQ_TO_REG32(i) (((i) >> 5) & 0x7) 60 #define IRQ_TO_REG32BIT(i) ((i) & 0x1f) 61 #define IRQ_TO_REG4(i) (((i) >> 2) & 0x3f) 62 #define IRQ_TO_REG4BIT(i) ((i) & 0x3) 63 #define IRQ_TO_REG16(i) (((i) >> 4) & 0xf) 64 #define IRQ_TO_REGBIT_S(i) 8 65 #define IRQ_TO_REG4BIT_M(i) 8 66 67 #define ICD_ISRn(i) (0x080 + (IRQ_TO_REG32(i) * 4)) 68 #define ICD_ISERn(i) (0x100 + (IRQ_TO_REG32(i) * 4)) 69 #define ICD_ICERn(i) (0x180 + (IRQ_TO_REG32(i) * 4)) 70 #define ICD_ISPRn(i) (0x200 + (IRQ_TO_REG32(i) * 4)) 71 #define ICD_ICPRn(i) (0x280 + (IRQ_TO_REG32(i) * 4)) 72 #define ICD_ABRn(i) (0x300 + (IRQ_TO_REG32(i) * 4)) 73 #define ICD_IPRn(i) (0x400 + (i)) 74 #define ICD_IPTRn(i) (0x800 + (i)) 75 #define ICD_ICRn(i) (0xC00 + (IRQ_TO_REG16(i) * 4)) 76 /* 77 * what about (ppi|spi)_status 78 */ 79 #define ICD_PPI 0xD00 80 #define ICD_PPI_GTIMER (1 << 11) 81 #define ICD_PPI_FIQ (1 << 12) 82 #define ICD_PPI_PTIMER (1 << 13) 83 #define ICD_PPI_PWDOG (1 << 14) 84 #define ICD_PPI_IRQ (1 << 15) 85 #define ICD_SPI_BASE 0xD04 86 #define ICD_SPIn(i) (ICD_SPI_BASE + ((i) * 4)) 87 88 89 #define ICD_SGIR 0xF00 90 91 #define ICD_PERIPH_ID_0 0xFD0 92 #define ICD_PERIPH_ID_1 0xFD4 93 #define ICD_PERIPH_ID_2 0xFD8 94 #define ICD_PERIPH_ID_3 0xFDC 95 #define ICD_PERIPH_ID_4 0xFE0 96 #define ICD_PERIPH_ID_5 0xFE4 97 #define ICD_PERIPH_ID_6 0xFE8 98 #define ICD_PERIPH_ID_7 0xFEC 99 100 #define ICD_COMP_ID_0 0xFEC 101 #define ICD_COMP_ID_1 0xFEC 102 #define ICD_COMP_ID_2 0xFEC 103 #define ICD_COMP_ID_3 0xFEC 104 105 #define ICD_SIZE 0x1000 106 107 108 #define ICPICR 0x00 109 #define ICPIPMR 0x04 110 /* XXX - must left justify bits to 0 - 7 */ 111 #define ICMIPMR_SH 4 112 #define ICPBPR 0x08 113 #define ICPIAR 0x0C 114 #define ICPIAR_IRQ_SH 0 115 #define ICPIAR_IRQ_M 0x3ff 116 #define ICPIAR_CPUID_SH 10 117 #define ICPIAR_CPUID_M 0x7 118 #define ICPIAR_NO_PENDING_IRQ ICPIAR_IRQ_M 119 #define ICPEOIR 0x10 120 #define ICPPRP 0x14 121 #define ICPHPIR 0x18 122 #define ICPIIR 0xFC 123 #define ICP_SIZE 0x100 124 /* 125 * what about periph_id and component_id 126 */ 127 128 #define AMPAMPINTC_SIZE 0x1000 129 130 131 #define IRQ_ENABLE 1 132 #define IRQ_DISABLE 0 133 134 struct ampintc_softc { 135 struct device sc_dev; 136 struct intrq *sc_ampintc_handler; 137 int sc_nintr; 138 bus_space_tag_t sc_iot; 139 bus_space_handle_t sc_d_ioh, sc_p_ioh; 140 struct evcount sc_spur; 141 }; 142 struct ampintc_softc *ampintc; 143 144 145 struct intrhand { 146 TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ 147 int (*ih_func)(void *); /* handler */ 148 void *ih_arg; /* arg for handler */ 149 int ih_ipl; /* IPL_* */ 150 int ih_irq; /* IRQ number */ 151 struct evcount ih_count; 152 char *ih_name; 153 }; 154 155 struct intrq { 156 TAILQ_HEAD(, intrhand) iq_list; /* handler list */ 157 int iq_irq; /* IRQ to mask while handling */ 158 int iq_levels; /* IPL_*'s this IRQ has */ 159 int iq_ist; /* share type */ 160 }; 161 162 163 int ampintc_match(struct device *, void *, void *); 164 void ampintc_attach(struct device *, struct device *, void *); 165 int ampintc_spllower(int); 166 void ampintc_splx(int); 167 int ampintc_splraise(int); 168 void ampintc_setipl(int); 169 void ampintc_calc_mask(void); 170 void *ampintc_intr_establish(int, int, int (*)(void *), void *, 171 char *); 172 void *ampintc_intr_establish_ext(int, int, int (*)(void *), void *, 173 char *); 174 void ampintc_intr_disestablish(void *); 175 void ampintc_irq_handler(void *); 176 const char *ampintc_intr_string(void *); 177 uint32_t ampintc_iack(void); 178 void ampintc_eoi(uint32_t); 179 void ampintc_set_priority(int, int); 180 void ampintc_intr_enable(int); 181 void ampintc_intr_disable(int); 182 void ampintc_route(int, int , int); 183 184 struct cfattach ampintc_ca = { 185 sizeof (struct ampintc_softc), ampintc_match, ampintc_attach 186 }; 187 188 struct cfdriver ampintc_cd = { 189 NULL, "ampintc", DV_DULL 190 }; 191 192 int 193 ampintc_match(struct device *parent, void *cfdata, void *aux) 194 { 195 return (1); 196 } 197 198 void 199 ampintc_attach(struct device *parent, struct device *self, void *args) 200 { 201 struct ampintc_softc *sc = (struct ampintc_softc *)self; 202 struct cortex_attach_args *ia = args; 203 int i, nintr; 204 bus_space_tag_t iot; 205 bus_space_handle_t d_ioh, p_ioh; 206 207 ampintc = sc; 208 209 arm_init_smask(); 210 211 iot = ia->ca_iot; 212 213 if (bus_space_map(iot, ia->ca_periphbase + ICP_ADDR, 214 ICP_SIZE, 0, &p_ioh)) 215 panic("ampintc_attach: ICP bus_space_map failed!"); 216 217 if (bus_space_map(iot, ia->ca_periphbase + ICD_ADDR, 218 ICD_SIZE, 0, &d_ioh)) 219 panic("ampintc_attach: ICD bus_space_map failed!"); 220 221 sc->sc_iot = iot; 222 sc->sc_d_ioh = d_ioh; 223 sc->sc_p_ioh = p_ioh; 224 225 evcount_attach(&sc->sc_spur, "irq1023/spur", NULL); 226 227 nintr = 32 * (bus_space_read_4(iot, d_ioh, ICD_ICTR) & ICD_ICTR_ITL_M); 228 nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */ 229 sc->sc_nintr = nintr; 230 printf(" nirq %d\n", nintr); 231 232 233 /* Disable all interrupts, clear all pending */ 234 for (i = 0; i < nintr/32; i++) { 235 bus_space_write_4(iot, d_ioh, ICD_ICERn(i*32), ~0); 236 bus_space_write_4(iot, d_ioh, ICD_ICPRn(i*32), ~0); 237 } 238 for (i = 0; i < nintr; i++) { 239 /* lowest priority ?? */ 240 bus_space_write_1(iot, d_ioh, ICD_IPRn(i), 0xff); 241 /* target no cpus */ 242 bus_space_write_1(iot, d_ioh, ICD_IPTRn(i), 0); 243 } 244 for (i = 2; i < nintr/16; i++) { 245 /* irq 32 - N */ 246 bus_space_write_4(iot, d_ioh, ICD_ICRn(i*16), 0); 247 } 248 249 /* software reset of the part? */ 250 /* set protection bit (kernel only)? */ 251 252 /* XXX - check power saving bit */ 253 254 255 sc->sc_ampintc_handler = malloc( 256 (sizeof (*sc->sc_ampintc_handler) * nintr), 257 M_DEVBUF, M_ZERO | M_NOWAIT); 258 for (i = 0; i < nintr; i++) { 259 TAILQ_INIT(&sc->sc_ampintc_handler[i].iq_list); 260 } 261 262 ampintc_setipl(IPL_HIGH); /* XXX ??? */ 263 ampintc_calc_mask(); 264 265 /* insert self as interrupt handler */ 266 arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx, 267 ampintc_setipl, ampintc_intr_establish_ext, 268 ampintc_intr_disestablish, ampintc_intr_string, ampintc_irq_handler); 269 270 /* enable interrupts */ 271 bus_space_write_4(iot, d_ioh, ICD_DCR, 3); 272 bus_space_write_4(iot, p_ioh, ICPICR, 1); 273 enable_interrupts(I32_bit); 274 } 275 276 void 277 ampintc_set_priority(int irq, int pri) 278 { 279 struct ampintc_softc *sc = ampintc; 280 uint32_t prival; 281 282 /* 283 * We only use 16 (13 really) interrupt priorities, 284 * and a CPU is only required to implement bit 4-7 of each field 285 * so shift into the top bits. 286 * also low values are higher priority thus IPL_HIGH - pri 287 */ 288 prival = (IPL_HIGH - pri) << ICMIPMR_SH; 289 bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival); 290 } 291 292 void 293 ampintc_setipl(int new) 294 { 295 struct cpu_info *ci = curcpu(); 296 struct ampintc_softc *sc = ampintc; 297 int psw; 298 299 /* disable here is only to keep hardware in sync with ci->ci_cpl */ 300 psw = disable_interrupts(I32_bit); 301 ci->ci_cpl = new; 302 303 /* low values are higher priority thus IPL_HIGH - pri */ 304 bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR, 305 (IPL_HIGH - new) << ICMIPMR_SH); 306 restore_interrupts(psw); 307 } 308 309 void 310 ampintc_intr_enable(int irq) 311 { 312 struct ampintc_softc *sc = ampintc; 313 314 #ifdef DEBUG 315 printf("enable irq %d register %x bitmask %08x\n", 316 irq, ICD_ISERn(irq), 1 << IRQ_TO_REG32BIT(irq)); 317 #endif 318 319 bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq), 320 1 << IRQ_TO_REG32BIT(irq)); 321 } 322 323 void 324 ampintc_intr_disable(int irq) 325 { 326 struct ampintc_softc *sc = ampintc; 327 328 bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq), 329 1 << IRQ_TO_REG32BIT(irq)); 330 } 331 332 333 void 334 ampintc_calc_mask(void) 335 { 336 struct cpu_info *ci = curcpu(); 337 struct ampintc_softc *sc = ampintc; 338 struct intrhand *ih; 339 int irq; 340 341 for (irq = 0; irq < sc->sc_nintr; irq++) { 342 int max = IPL_NONE; 343 int min = IPL_HIGH; 344 TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, 345 ih_list) { 346 if (ih->ih_ipl > max) 347 max = ih->ih_ipl; 348 349 if (ih->ih_ipl < min) 350 min = ih->ih_ipl; 351 } 352 353 if (sc->sc_ampintc_handler[irq].iq_irq == max) { 354 continue; 355 } 356 sc->sc_ampintc_handler[irq].iq_irq = max; 357 358 if (max == IPL_NONE) 359 min = IPL_NONE; 360 361 #ifdef DEBUG_INTC 362 if (min != IPL_NONE) { 363 printf("irq %d to block at %d %d reg %d bit %d\n", 364 irq, max, min, AMPINTC_IRQ_TO_REG(irq), 365 AMPINTC_IRQ_TO_REGi(irq)); 366 } 367 #endif 368 /* Enable interrupts at lower levels, clear -> enable */ 369 /* Set interrupt priority/enable */ 370 if (min != IPL_NONE) { 371 ampintc_set_priority(irq, min); 372 ampintc_intr_enable(irq); 373 ampintc_route(irq, IRQ_ENABLE, 0); 374 } else { 375 ampintc_intr_disable(irq); 376 ampintc_route(irq, IRQ_DISABLE, 0); 377 378 } 379 } 380 ampintc_setipl(ci->ci_cpl); 381 } 382 383 void 384 ampintc_splx(int new) 385 { 386 struct cpu_info *ci = curcpu(); 387 388 if (ci->ci_ipending & arm_smask[new]) 389 arm_do_pending_intr(new); 390 391 ampintc_setipl(new); 392 } 393 394 int 395 ampintc_spllower(int new) 396 { 397 struct cpu_info *ci = curcpu(); 398 int old = ci->ci_cpl; 399 ampintc_splx(new); 400 return (old); 401 } 402 403 int 404 ampintc_splraise(int new) 405 { 406 struct cpu_info *ci = curcpu(); 407 int old; 408 old = ci->ci_cpl; 409 410 /* 411 * setipl must always be called because there is a race window 412 * where the variable is updated before the mask is set 413 * an interrupt occurs in that window without the mask always 414 * being set, the hardware might not get updated on the next 415 * splraise completely messing up spl protection. 416 */ 417 if (old > new) 418 new = old; 419 420 ampintc_setipl(new); 421 422 return (old); 423 } 424 425 426 uint32_t 427 ampintc_iack(void) 428 { 429 uint32_t intid; 430 struct ampintc_softc *sc = ampintc; 431 432 intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR); 433 434 return (intid); 435 } 436 437 void 438 ampintc_eoi(uint32_t eoi) 439 { 440 struct ampintc_softc *sc = ampintc; 441 442 bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPEOIR, eoi); 443 } 444 445 void 446 ampintc_route(int irq, int enable, int cpu) 447 { 448 uint8_t val; 449 struct ampintc_softc *sc = ampintc; 450 451 val = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq)); 452 if (enable == IRQ_ENABLE) 453 val |= (1 << cpu); 454 else 455 val &= ~(1 << cpu); 456 bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq), val); 457 } 458 459 void 460 ampintc_irq_handler(void *frame) 461 { 462 struct ampintc_softc *sc = ampintc; 463 struct intrhand *ih; 464 void *arg; 465 uint32_t iack_val; 466 int irq, pri, s; 467 468 iack_val = ampintc_iack(); 469 //#define DEBUG_INTC 470 #ifdef DEBUG_INTC 471 if (iack_val != 27) 472 printf("irq %d fired\n", iack_val); 473 else { 474 static int cnt = 0; 475 if ((cnt++ % 100) == 0) { 476 printf("irq %d fired * _100\n", iack_val); 477 Debugger(); 478 } 479 480 } 481 #endif 482 483 if (iack_val == 1023) { 484 sc->sc_spur.ec_count++; 485 return; 486 } 487 irq = iack_val & ((1 << sc->sc_nintr) - 1); 488 489 pri = sc->sc_ampintc_handler[irq].iq_irq; 490 s = ampintc_splraise(pri); 491 TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, ih_list) { 492 if (ih->ih_arg != 0) 493 arg = ih->ih_arg; 494 else 495 arg = frame; 496 497 if (ih->ih_func(arg)) 498 ih->ih_count.ec_count++; 499 500 } 501 ampintc_eoi(iack_val); 502 503 ampintc_splx(s); 504 } 505 506 void * 507 ampintc_intr_establish_ext(int irqno, int level, int (*func)(void *), 508 void *arg, char *name) 509 { 510 return ampintc_intr_establish(irqno+32, level, func, arg, name); 511 } 512 513 void * 514 ampintc_intr_establish(int irqno, int level, int (*func)(void *), 515 void *arg, char *name) 516 { 517 struct ampintc_softc *sc = ampintc; 518 struct intrhand *ih; 519 int psw; 520 521 if (irqno < 0 || irqno >= sc->sc_nintr) 522 panic("ampintc_intr_establish: bogus irqnumber %d: %s", 523 irqno, name); 524 525 /* no point in sleeping unless someone can free memory. */ 526 ih = (struct intrhand *)malloc (sizeof *ih, M_DEVBUF, 527 cold ? M_NOWAIT : M_WAITOK); 528 if (ih == NULL) 529 panic("intr_establish: can't malloc handler info"); 530 ih->ih_func = func; 531 ih->ih_arg = arg; 532 ih->ih_ipl = level; 533 ih->ih_irq = irqno; 534 ih->ih_name = name; 535 536 psw = disable_interrupts(I32_bit); 537 538 TAILQ_INSERT_TAIL(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list); 539 540 if (name != NULL) 541 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 542 543 #ifdef DEBUG_INTC 544 printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level, 545 name); 546 #endif 547 ampintc_calc_mask(); 548 549 restore_interrupts(psw); 550 return (ih); 551 } 552 553 void 554 ampintc_intr_disestablish(void *cookie) 555 { 556 #if 0 557 int psw; 558 struct intrhand *ih = cookie; 559 int irqno = ih->ih_irq; 560 psw = disable_interrupts(I32_bit); 561 TAILQ_REMOVE(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list); 562 if (ih->ih_name != NULL) 563 evcount_detach(&ih->ih_count); 564 free(ih, M_DEVBUF, 0); 565 restore_interrupts(psw); 566 #endif 567 } 568 569 const char * 570 ampintc_intr_string(void *cookie) 571 { 572 struct intrhand *ih = (struct intrhand *)cookie; 573 static char irqstr[1 + sizeof("ampintc irq ") + 4]; 574 575 snprintf(irqstr, sizeof irqstr, "ampintc irq %d", ih->ih_irq); 576 return irqstr; 577 } 578