1 /* $OpenBSD: ampintc.c,v 1.14 2016/08/21 06:47:47 jsg 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 30 #include <machine/bus.h> 31 #include <machine/fdt.h> 32 33 #include <arm/cpufunc.h> 34 #include <arm/cortex/cortex.h> 35 36 #include <dev/ofw/fdt.h> 37 #include <dev/ofw/openfirm.h> 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 106 #define ICPICR 0x00 107 #define ICPIPMR 0x04 108 /* XXX - must left justify bits to 0 - 7 */ 109 #define ICMIPMR_SH 4 110 #define ICPBPR 0x08 111 #define ICPIAR 0x0C 112 #define ICPIAR_IRQ_SH 0 113 #define ICPIAR_IRQ_M 0x3ff 114 #define ICPIAR_CPUID_SH 10 115 #define ICPIAR_CPUID_M 0x7 116 #define ICPIAR_NO_PENDING_IRQ ICPIAR_IRQ_M 117 #define ICPEOIR 0x10 118 #define ICPPRP 0x14 119 #define ICPHPIR 0x18 120 #define ICPIIR 0xFC 121 122 /* 123 * what about periph_id and component_id 124 */ 125 126 #define IRQ_ENABLE 1 127 #define IRQ_DISABLE 0 128 129 struct ampintc_softc { 130 struct device sc_dev; 131 struct intrq *sc_ampintc_handler; 132 int sc_nintr; 133 bus_space_tag_t sc_iot; 134 bus_space_handle_t sc_d_ioh, sc_p_ioh; 135 struct evcount sc_spur; 136 struct interrupt_controller sc_ic; 137 }; 138 struct ampintc_softc *ampintc; 139 140 141 struct intrhand { 142 TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ 143 int (*ih_func)(void *); /* handler */ 144 void *ih_arg; /* arg for handler */ 145 int ih_ipl; /* IPL_* */ 146 int ih_irq; /* IRQ number */ 147 struct evcount ih_count; 148 char *ih_name; 149 }; 150 151 struct intrq { 152 TAILQ_HEAD(, intrhand) iq_list; /* handler list */ 153 int iq_irq; /* IRQ to mask while handling */ 154 int iq_levels; /* IPL_*'s this IRQ has */ 155 int iq_ist; /* share type */ 156 }; 157 158 159 int ampintc_match(struct device *, void *, void *); 160 void ampintc_attach(struct device *, struct device *, void *); 161 int ampintc_spllower(int); 162 void ampintc_splx(int); 163 int ampintc_splraise(int); 164 void ampintc_setipl(int); 165 void ampintc_calc_mask(void); 166 void *ampintc_intr_establish(int, int, int (*)(void *), void *, 167 char *); 168 void *ampintc_intr_establish_ext(int, int, int (*)(void *), void *, 169 char *); 170 void *ampintc_intr_establish_fdt(void *, int *, int, 171 int (*)(void *), void *, char *); 172 void ampintc_intr_disestablish(void *); 173 void ampintc_irq_handler(void *); 174 const char *ampintc_intr_string(void *); 175 uint32_t ampintc_iack(void); 176 void ampintc_eoi(uint32_t); 177 void ampintc_set_priority(int, int); 178 void ampintc_intr_enable(int); 179 void ampintc_intr_disable(int); 180 void ampintc_route(int, int , int); 181 182 struct cfattach ampintc_ca = { 183 sizeof (struct ampintc_softc), ampintc_match, ampintc_attach 184 }; 185 186 struct cfdriver ampintc_cd = { 187 NULL, "ampintc", DV_DULL 188 }; 189 190 static char *ampintc_compatibles[] = { 191 "arm,cortex-a7-gic", 192 "arm,cortex-a9-gic", 193 "arm,cortex-a15-gic", 194 NULL 195 }; 196 197 int 198 ampintc_match(struct device *parent, void *cfdata, void *aux) 199 { 200 struct fdt_attach_args *faa = aux; 201 int i; 202 203 for (i = 0; ampintc_compatibles[i]; i++) 204 if (OF_is_compatible(faa->fa_node, ampintc_compatibles[i])) 205 return (1); 206 207 return (0); 208 } 209 210 void 211 ampintc_attach(struct device *parent, struct device *self, void *aux) 212 { 213 struct ampintc_softc *sc = (struct ampintc_softc *)self; 214 struct fdt_attach_args *faa = aux; 215 int i, nintr; 216 217 ampintc = sc; 218 219 arm_init_smask(); 220 221 sc->sc_iot = faa->fa_iot; 222 223 /* First row: ICD */ 224 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 225 faa->fa_reg[0].size, 0, &sc->sc_d_ioh)) 226 panic("%s: ICD bus_space_map failed!", __func__); 227 228 /* Second row: ICP */ 229 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, 230 faa->fa_reg[1].size, 0, &sc->sc_p_ioh)) 231 panic("%s: ICP bus_space_map failed!", __func__); 232 233 evcount_attach(&sc->sc_spur, "irq1023/spur", NULL); 234 235 nintr = 32 * (bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, 236 ICD_ICTR) & ICD_ICTR_ITL_M); 237 nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */ 238 sc->sc_nintr = nintr; 239 printf(" nirq %d\n", nintr); 240 241 /* Disable all interrupts, clear all pending */ 242 for (i = 0; i < nintr/32; i++) { 243 bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 244 ICD_ICERn(i*32), ~0); 245 bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, 246 ICD_ICPRn(i*32), ~0); 247 } 248 for (i = 0; i < nintr; i++) { 249 /* lowest priority ?? */ 250 bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i), 0xff); 251 /* target no cpus */ 252 bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(i), 0); 253 } 254 for (i = 2; i < nintr/16; i++) { 255 /* irq 32 - N */ 256 bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(i*16), 0); 257 } 258 259 /* software reset of the part? */ 260 /* set protection bit (kernel only)? */ 261 262 /* XXX - check power saving bit */ 263 264 sc->sc_ampintc_handler = mallocarray(nintr, 265 sizeof(*sc->sc_ampintc_handler), M_DEVBUF, M_ZERO | M_NOWAIT); 266 for (i = 0; i < nintr; i++) { 267 TAILQ_INIT(&sc->sc_ampintc_handler[i].iq_list); 268 } 269 270 ampintc_setipl(IPL_HIGH); /* XXX ??? */ 271 ampintc_calc_mask(); 272 273 /* insert self as interrupt handler */ 274 arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx, 275 ampintc_setipl, ampintc_intr_establish_ext, 276 ampintc_intr_disestablish, ampintc_intr_string, ampintc_irq_handler); 277 278 /* enable interrupts */ 279 bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_DCR, 3); 280 bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1); 281 enable_interrupts(PSR_I); 282 283 sc->sc_ic.ic_node = faa->fa_node; 284 sc->sc_ic.ic_cookie = self; 285 sc->sc_ic.ic_establish = ampintc_intr_establish_fdt; 286 sc->sc_ic.ic_disestablish = ampintc_intr_disestablish; 287 arm_intr_register_fdt(&sc->sc_ic); 288 } 289 290 void 291 ampintc_set_priority(int irq, int pri) 292 { 293 struct ampintc_softc *sc = ampintc; 294 uint32_t prival; 295 296 /* 297 * We only use 16 (13 really) interrupt priorities, 298 * and a CPU is only required to implement bit 4-7 of each field 299 * so shift into the top bits. 300 * also low values are higher priority thus IPL_HIGH - pri 301 */ 302 prival = (IPL_HIGH - pri) << ICMIPMR_SH; 303 bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival); 304 } 305 306 void 307 ampintc_setipl(int new) 308 { 309 struct cpu_info *ci = curcpu(); 310 struct ampintc_softc *sc = ampintc; 311 int psw; 312 313 /* disable here is only to keep hardware in sync with ci->ci_cpl */ 314 psw = disable_interrupts(PSR_I); 315 ci->ci_cpl = new; 316 317 /* low values are higher priority thus IPL_HIGH - pri */ 318 bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR, 319 (IPL_HIGH - new) << ICMIPMR_SH); 320 restore_interrupts(psw); 321 } 322 323 void 324 ampintc_intr_enable(int irq) 325 { 326 struct ampintc_softc *sc = ampintc; 327 328 #ifdef DEBUG 329 printf("enable irq %d register %x bitmask %08x\n", 330 irq, ICD_ISERn(irq), 1 << IRQ_TO_REG32BIT(irq)); 331 #endif 332 333 bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq), 334 1 << IRQ_TO_REG32BIT(irq)); 335 } 336 337 void 338 ampintc_intr_disable(int irq) 339 { 340 struct ampintc_softc *sc = ampintc; 341 342 bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq), 343 1 << IRQ_TO_REG32BIT(irq)); 344 } 345 346 347 void 348 ampintc_calc_mask(void) 349 { 350 struct cpu_info *ci = curcpu(); 351 struct ampintc_softc *sc = ampintc; 352 struct intrhand *ih; 353 int irq; 354 355 for (irq = 0; irq < sc->sc_nintr; irq++) { 356 int max = IPL_NONE; 357 int min = IPL_HIGH; 358 TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, 359 ih_list) { 360 if (ih->ih_ipl > max) 361 max = ih->ih_ipl; 362 363 if (ih->ih_ipl < min) 364 min = ih->ih_ipl; 365 } 366 367 if (sc->sc_ampintc_handler[irq].iq_irq == max) { 368 continue; 369 } 370 sc->sc_ampintc_handler[irq].iq_irq = max; 371 372 if (max == IPL_NONE) 373 min = IPL_NONE; 374 375 /* Enable interrupts at lower levels, clear -> enable */ 376 /* Set interrupt priority/enable */ 377 if (min != IPL_NONE) { 378 ampintc_set_priority(irq, min); 379 ampintc_intr_enable(irq); 380 ampintc_route(irq, IRQ_ENABLE, 0); 381 } else { 382 ampintc_intr_disable(irq); 383 ampintc_route(irq, IRQ_DISABLE, 0); 384 385 } 386 } 387 ampintc_setipl(ci->ci_cpl); 388 } 389 390 void 391 ampintc_splx(int new) 392 { 393 struct cpu_info *ci = curcpu(); 394 395 if (ci->ci_ipending & arm_smask[new]) 396 arm_do_pending_intr(new); 397 398 ampintc_setipl(new); 399 } 400 401 int 402 ampintc_spllower(int new) 403 { 404 struct cpu_info *ci = curcpu(); 405 int old = ci->ci_cpl; 406 ampintc_splx(new); 407 return (old); 408 } 409 410 int 411 ampintc_splraise(int new) 412 { 413 struct cpu_info *ci = curcpu(); 414 int old; 415 old = ci->ci_cpl; 416 417 /* 418 * setipl must always be called because there is a race window 419 * where the variable is updated before the mask is set 420 * an interrupt occurs in that window without the mask always 421 * being set, the hardware might not get updated on the next 422 * splraise completely messing up spl protection. 423 */ 424 if (old > new) 425 new = old; 426 427 ampintc_setipl(new); 428 429 return (old); 430 } 431 432 433 uint32_t 434 ampintc_iack(void) 435 { 436 uint32_t intid; 437 struct ampintc_softc *sc = ampintc; 438 439 intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR); 440 441 return (intid); 442 } 443 444 void 445 ampintc_eoi(uint32_t eoi) 446 { 447 struct ampintc_softc *sc = ampintc; 448 449 bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPEOIR, eoi); 450 } 451 452 void 453 ampintc_route(int irq, int enable, int cpu) 454 { 455 uint8_t val; 456 struct ampintc_softc *sc = ampintc; 457 458 val = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq)); 459 if (enable == IRQ_ENABLE) 460 val |= (1 << cpu); 461 else 462 val &= ~(1 << cpu); 463 bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq), val); 464 } 465 466 void 467 ampintc_irq_handler(void *frame) 468 { 469 struct ampintc_softc *sc = ampintc; 470 struct intrhand *ih; 471 void *arg; 472 uint32_t iack_val; 473 int irq, pri, s; 474 475 iack_val = ampintc_iack(); 476 #ifdef DEBUG_INTC 477 if (iack_val != 27) 478 printf("irq %d fired\n", iack_val); 479 else { 480 static int cnt = 0; 481 if ((cnt++ % 100) == 0) { 482 printf("irq %d fired * _100\n", iack_val); 483 #ifdef DDB 484 Debugger(); 485 #endif 486 } 487 488 } 489 #endif 490 491 if (iack_val == 1023) { 492 sc->sc_spur.ec_count++; 493 return; 494 } 495 irq = iack_val & ((1 << sc->sc_nintr) - 1); 496 497 pri = sc->sc_ampintc_handler[irq].iq_irq; 498 s = ampintc_splraise(pri); 499 TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, ih_list) { 500 if (ih->ih_arg != 0) 501 arg = ih->ih_arg; 502 else 503 arg = frame; 504 505 if (ih->ih_func(arg)) 506 ih->ih_count.ec_count++; 507 508 } 509 ampintc_eoi(iack_val); 510 511 ampintc_splx(s); 512 } 513 514 void * 515 ampintc_intr_establish_ext(int irqno, int level, int (*func)(void *), 516 void *arg, char *name) 517 { 518 return ampintc_intr_establish(irqno+32, level, func, arg, name); 519 } 520 521 void * 522 ampintc_intr_establish_fdt(void *cookie, int *cell, int level, 523 int (*func)(void *), void *arg, char *name) 524 { 525 struct ampintc_softc *sc = (struct ampintc_softc *)cookie; 526 int irq; 527 528 /* 2nd cell contains the interrupt number */ 529 irq = cell[1]; 530 531 /* 1st cell contains type: 0 SPI (32-X), 1 PPI (16-31) */ 532 if (cell[0] == 0) 533 irq += 32; 534 else if (cell[0] == 1) 535 irq += 16; 536 else 537 panic("%s: bogus interrupt type", sc->sc_dev.dv_xname); 538 539 return ampintc_intr_establish(irq, level, func, arg, name); 540 } 541 542 void * 543 ampintc_intr_establish(int irqno, int level, int (*func)(void *), 544 void *arg, char *name) 545 { 546 struct ampintc_softc *sc = ampintc; 547 struct intrhand *ih; 548 int psw; 549 550 if (irqno < 0 || irqno >= sc->sc_nintr) 551 panic("ampintc_intr_establish: bogus irqnumber %d: %s", 552 irqno, name); 553 554 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 555 ih->ih_func = func; 556 ih->ih_arg = arg; 557 ih->ih_ipl = level; 558 ih->ih_irq = irqno; 559 ih->ih_name = name; 560 561 psw = disable_interrupts(PSR_I); 562 563 TAILQ_INSERT_TAIL(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list); 564 565 if (name != NULL) 566 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 567 568 #ifdef DEBUG_INTC 569 printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level, 570 name); 571 #endif 572 ampintc_calc_mask(); 573 574 restore_interrupts(psw); 575 return (ih); 576 } 577 578 void 579 ampintc_intr_disestablish(void *cookie) 580 { 581 struct ampintc_softc *sc = ampintc; 582 struct intrhand *ih = cookie; 583 int psw; 584 585 #ifdef DEBUG_INTC 586 printf("ampintc_intr_disestablish irq %d level %d [%s]\n", 587 ih->ih_irq, ih->ih_ipl, ih->ih_name); 588 #endif 589 590 psw = disable_interrupts(PSR_I); 591 592 TAILQ_REMOVE(&sc->sc_ampintc_handler[ih->ih_irq].iq_list, ih, ih_list); 593 if (ih->ih_name != NULL) 594 evcount_detach(&ih->ih_count); 595 free(ih, M_DEVBUF, sizeof(*ih)); 596 597 ampintc_calc_mask(); 598 599 restore_interrupts(psw); 600 } 601 602 const char * 603 ampintc_intr_string(void *cookie) 604 { 605 struct intrhand *ih = (struct intrhand *)cookie; 606 static char irqstr[1 + sizeof("ampintc irq ") + 4]; 607 608 snprintf(irqstr, sizeof irqstr, "ampintc irq %d", ih->ih_irq); 609 return irqstr; 610 } 611