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