1 /* $OpenBSD: bcm2836_intr.c,v 1.7 2021/10/24 17:52:27 mpi Exp $ */ 2 /* 3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> 4 * Copyright (c) 2015 Patrick Wildt <patrick@blueri.se> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/queue.h> 22 #include <sys/malloc.h> 23 #include <sys/device.h> 24 #include <sys/evcount.h> 25 26 #include <machine/bus.h> 27 #include <machine/fdt.h> 28 29 #include <arm/cpufunc.h> 30 31 #include <dev/ofw/openfirm.h> 32 #include <dev/ofw/fdt.h> 33 34 /* registers */ 35 #define INTC_PENDING_BANK0 0x00 36 #define INTC_PENDING_BANK1 0x04 37 #define INTC_PENDING_BANK2 0x08 38 #define INTC_FIQ_CONTROL 0x0C 39 #define INTC_ENABLE_BANK1 0x10 40 #define INTC_ENABLE_BANK2 0x14 41 #define INTC_ENABLE_BANK0 0x18 42 #define INTC_DISABLE_BANK1 0x1C 43 #define INTC_DISABLE_BANK2 0x20 44 #define INTC_DISABLE_BANK0 0x24 45 46 /* arm local */ 47 #define ARM_LOCAL_CONTROL 0x00 48 #define ARM_LOCAL_PRESCALER 0x08 49 #define PRESCALER_19_2 0x80000000 /* 19.2 MHz */ 50 #define ARM_LOCAL_INT_TIMER(n) (0x40 + (n) * 4) 51 #define ARM_LOCAL_INT_MAILBOX(n) (0x50 + (n) * 4) 52 #define ARM_LOCAL_INT_PENDING(n) (0x60 + (n) * 4) 53 #define ARM_LOCAL_INT_PENDING_MASK 0x0f 54 55 #define BANK0_START 64 56 #define BANK0_END (BANK0_START + 32 - 1) 57 #define BANK1_START 0 58 #define BANK1_END (BANK1_START + 32 - 1) 59 #define BANK2_START 32 60 #define BANK2_END (BANK2_START + 32 - 1) 61 #define LOCAL_START 96 62 #define LOCAL_END (LOCAL_START + 32 - 1) 63 64 #define IS_IRQ_BANK0(n) (((n) >= BANK0_START) && ((n) <= BANK0_END)) 65 #define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END)) 66 #define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END)) 67 #define IS_IRQ_LOCAL(n) (((n) >= LOCAL_START) && ((n) <= LOCAL_END)) 68 #define IRQ_BANK0(n) ((n) - BANK0_START) 69 #define IRQ_BANK1(n) ((n) - BANK1_START) 70 #define IRQ_BANK2(n) ((n) - BANK2_START) 71 #define IRQ_LOCAL(n) ((n) - LOCAL_START) 72 73 #define INTC_NIRQ 128 74 #define INTC_NBANK 4 75 76 #define INTC_IRQ_TO_REG(i) (((i) >> 5) & 0x3) 77 #define INTC_IRQ_TO_REGi(i) ((i) & 0x1f) 78 79 struct intrhand { 80 TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ 81 int (*ih_fun)(void *); /* handler */ 82 void *ih_arg; /* arg for handler */ 83 int ih_ipl; /* IPL_* */ 84 int ih_irq; /* IRQ number */ 85 struct evcount ih_count; /* interrupt counter */ 86 char *ih_name; /* device name */ 87 }; 88 89 struct intrsource { 90 TAILQ_HEAD(, intrhand) is_list; /* handler list */ 91 int is_irq; /* IRQ to mask while handling */ 92 }; 93 94 struct bcm_intc_softc { 95 struct device sc_dev; 96 struct intrsource sc_bcm_intc_handler[INTC_NIRQ]; 97 uint32_t sc_bcm_intc_imask[INTC_NBANK][NIPL]; 98 bus_space_tag_t sc_iot; 99 bus_space_handle_t sc_ioh; 100 bus_space_handle_t sc_lioh; 101 struct interrupt_controller sc_intc; 102 struct interrupt_controller sc_l1_intc; 103 }; 104 struct bcm_intc_softc *bcm_intc; 105 106 int bcm_intc_match(struct device *, void *, void *); 107 void bcm_intc_attach(struct device *, struct device *, void *); 108 void bcm_intc_splx(int new); 109 int bcm_intc_spllower(int new); 110 int bcm_intc_splraise(int new); 111 void bcm_intc_setipl(int new); 112 void bcm_intc_calc_mask(void); 113 void *bcm_intc_intr_establish(int, int, struct cpu_info *, 114 int (*)(void *), void *, char *); 115 void *bcm_intc_intr_establish_fdt(void *, int *, int, struct cpu_info *, 116 int (*)(void *), void *, char *); 117 void *l1_intc_intr_establish_fdt(void *, int *, int, struct cpu_info *, 118 int (*)(void *), void *, char *); 119 void bcm_intc_intr_disestablish(void *); 120 const char *bcm_intc_intr_string(void *); 121 void bcm_intc_irq_handler(void *); 122 123 const struct cfattach bcmintc_ca = { 124 sizeof (struct bcm_intc_softc), bcm_intc_match, bcm_intc_attach 125 }; 126 127 struct cfdriver bcmintc_cd = { 128 NULL, "bcmintc", DV_DULL 129 }; 130 131 int 132 bcm_intc_match(struct device *parent, void *cfdata, void *aux) 133 { 134 struct fdt_attach_args *faa = aux; 135 136 if (OF_is_compatible(faa->fa_node, "brcm,bcm2836-armctrl-ic")) 137 return 1; 138 139 return 0; 140 } 141 142 void 143 bcm_intc_attach(struct device *parent, struct device *self, void *aux) 144 { 145 struct bcm_intc_softc *sc = (struct bcm_intc_softc *)self; 146 struct fdt_attach_args *faa = aux; 147 uint32_t reg[2]; 148 int node; 149 int i; 150 151 if (faa->fa_nreg < 1) 152 return; 153 154 bcm_intc = sc; 155 156 sc->sc_iot = faa->fa_iot; 157 158 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 159 faa->fa_reg[0].size, 0, &sc->sc_ioh)) 160 panic("%s: bus_space_map failed!", __func__); 161 162 /* 163 * ARM control logic. 164 * 165 * XXX Should really be implemented as a separate interrupt 166 * controller, but for now it is easier to handle it together 167 * with its BCM2835 partner. 168 */ 169 node = OF_finddevice("/soc/local_intc"); 170 if (node == -1) 171 panic("%s: can't find ARM control logic", __func__); 172 173 if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg)) 174 panic("%s: can't map ARM control logic", __func__); 175 176 if (bus_space_map(sc->sc_iot, reg[0], reg[1], 0, &sc->sc_lioh)) 177 panic("%s: bus_space_map failed!", __func__); 178 179 printf("\n"); 180 181 /* mask all interrupts */ 182 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK0, 183 0xffffffff); 184 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK1, 185 0xffffffff); 186 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK2, 187 0xffffffff); 188 189 /* ARM control specific */ 190 bus_space_write_4(sc->sc_iot, sc->sc_lioh, ARM_LOCAL_CONTROL, 0); 191 bus_space_write_4(sc->sc_iot, sc->sc_lioh, ARM_LOCAL_PRESCALER, 192 PRESCALER_19_2); 193 for (i = 0; i < 4; i++) 194 bus_space_write_4(sc->sc_iot, sc->sc_lioh, 195 ARM_LOCAL_INT_TIMER(i), 0); 196 for (i = 0; i < 4; i++) 197 bus_space_write_4(sc->sc_iot, sc->sc_lioh, 198 ARM_LOCAL_INT_MAILBOX(i), 1); 199 200 for (i = 0; i < INTC_NIRQ; i++) { 201 TAILQ_INIT(&sc->sc_bcm_intc_handler[i].is_list); 202 } 203 204 bcm_intc_calc_mask(); 205 206 /* insert self as interrupt handler */ 207 arm_set_intr_handler(bcm_intc_splraise, bcm_intc_spllower, bcm_intc_splx, 208 bcm_intc_setipl, 209 bcm_intc_intr_establish, bcm_intc_intr_disestablish, bcm_intc_intr_string, 210 bcm_intc_irq_handler); 211 212 sc->sc_intc.ic_node = faa->fa_node; 213 sc->sc_intc.ic_cookie = sc; 214 sc->sc_intc.ic_establish = bcm_intc_intr_establish_fdt; 215 sc->sc_intc.ic_disestablish = bcm_intc_intr_disestablish; 216 arm_intr_register_fdt(&sc->sc_intc); 217 218 sc->sc_l1_intc.ic_node = node; 219 sc->sc_l1_intc.ic_cookie = sc; 220 sc->sc_l1_intc.ic_establish = l1_intc_intr_establish_fdt; 221 sc->sc_l1_intc.ic_disestablish = bcm_intc_intr_disestablish; 222 arm_intr_register_fdt(&sc->sc_l1_intc); 223 224 bcm_intc_setipl(IPL_HIGH); /* XXX ??? */ 225 enable_interrupts(PSR_I); 226 } 227 228 void 229 bcm_intc_intr_enable(int irq, int ipl) 230 { 231 struct bcm_intc_softc *sc = bcm_intc; 232 233 if (IS_IRQ_BANK0(irq)) 234 sc->sc_bcm_intc_imask[0][ipl] |= (1 << IRQ_BANK0(irq)); 235 else if (IS_IRQ_BANK1(irq)) 236 sc->sc_bcm_intc_imask[1][ipl] |= (1 << IRQ_BANK1(irq)); 237 else if (IS_IRQ_BANK2(irq)) 238 sc->sc_bcm_intc_imask[2][ipl] |= (1 << IRQ_BANK2(irq)); 239 else if (IS_IRQ_LOCAL(irq)) 240 sc->sc_bcm_intc_imask[3][ipl] |= (1 << IRQ_LOCAL(irq)); 241 else 242 printf("%s: invalid irq number: %d\n", __func__, irq); 243 } 244 245 void 246 bcm_intc_intr_disable(int irq, int ipl) 247 { 248 struct bcm_intc_softc *sc = bcm_intc; 249 250 if (IS_IRQ_BANK0(irq)) 251 sc->sc_bcm_intc_imask[0][ipl] &= ~(1 << IRQ_BANK0(irq)); 252 else if (IS_IRQ_BANK1(irq)) 253 sc->sc_bcm_intc_imask[1][ipl] &= ~(1 << IRQ_BANK1(irq)); 254 else if (IS_IRQ_BANK2(irq)) 255 sc->sc_bcm_intc_imask[2][ipl] &= ~(1 << IRQ_BANK2(irq)); 256 else if (IS_IRQ_LOCAL(irq)) 257 sc->sc_bcm_intc_imask[3][ipl] &= ~(1 << IRQ_LOCAL(irq)); 258 else 259 printf("%s: invalid irq number: %d\n", __func__, irq); 260 } 261 262 void 263 bcm_intc_calc_mask(void) 264 { 265 struct cpu_info *ci = curcpu(); 266 struct bcm_intc_softc *sc = bcm_intc; 267 int irq; 268 struct intrhand *ih; 269 int i; 270 271 for (irq = 0; irq < INTC_NIRQ; irq++) { 272 int max = IPL_NONE; 273 int min = IPL_HIGH; 274 TAILQ_FOREACH(ih, &sc->sc_bcm_intc_handler[irq].is_list, 275 ih_list) { 276 if (ih->ih_ipl > max) 277 max = ih->ih_ipl; 278 279 if (ih->ih_ipl < min) 280 min = ih->ih_ipl; 281 } 282 283 sc->sc_bcm_intc_handler[irq].is_irq = max; 284 285 if (max == IPL_NONE) 286 min = IPL_NONE; 287 288 #ifdef DEBUG_INTC 289 if (min != IPL_NONE) { 290 printf("irq %d to block at %d %d reg %d bit %d\n", 291 irq, max, min, INTC_IRQ_TO_REG(irq), 292 INTC_IRQ_TO_REGi(irq)); 293 } 294 #endif 295 /* Enable interrupts at lower levels, clear -> enable */ 296 for (i = 0; i < min; i++) 297 bcm_intc_intr_enable(irq, i); 298 for (; i <= IPL_HIGH; i++) 299 bcm_intc_intr_disable(irq, i); 300 } 301 arm_init_smask(); 302 bcm_intc_setipl(ci->ci_cpl); 303 } 304 305 void 306 bcm_intc_splx(int new) 307 { 308 struct cpu_info *ci = curcpu(); 309 310 if (ci->ci_ipending & arm_smask[new]) 311 arm_do_pending_intr(new); 312 313 bcm_intc_setipl(new); 314 } 315 316 int 317 bcm_intc_spllower(int new) 318 { 319 struct cpu_info *ci = curcpu(); 320 int old = ci->ci_cpl; 321 bcm_intc_splx(new); 322 return (old); 323 } 324 325 int 326 bcm_intc_splraise(int new) 327 { 328 struct cpu_info *ci = curcpu(); 329 int old; 330 old = ci->ci_cpl; 331 332 /* 333 * setipl must always be called because there is a race window 334 * where the variable is updated before the mask is set 335 * an interrupt occurs in that window without the mask always 336 * being set, the hardware might not get updated on the next 337 * splraise completely messing up spl protection. 338 */ 339 if (old > new) 340 new = old; 341 342 bcm_intc_setipl(new); 343 344 return (old); 345 } 346 347 void 348 bcm_intc_setipl(int new) 349 { 350 struct cpu_info *ci = curcpu(); 351 struct bcm_intc_softc *sc = bcm_intc; 352 int i, psw; 353 354 psw = disable_interrupts(PSR_I); 355 ci->ci_cpl = new; 356 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK0, 357 0xffffffff); 358 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK1, 359 0xffffffff); 360 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK2, 361 0xffffffff); 362 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK0, 363 sc->sc_bcm_intc_imask[0][new]); 364 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK1, 365 sc->sc_bcm_intc_imask[1][new]); 366 bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK2, 367 sc->sc_bcm_intc_imask[2][new]); 368 /* XXX: SMP */ 369 for (i = 0; i < 4; i++) 370 bus_space_write_4(sc->sc_iot, sc->sc_lioh, 371 ARM_LOCAL_INT_TIMER(i), sc->sc_bcm_intc_imask[3][new]); 372 restore_interrupts(psw); 373 } 374 375 int 376 bcm_intc_get_next_irq(int last_irq) 377 { 378 struct bcm_intc_softc *sc = bcm_intc; 379 uint32_t pending; 380 int32_t irq = last_irq + 1; 381 382 /* Sanity check */ 383 if (irq < 0) 384 irq = 0; 385 386 /* We need to keep this order. */ 387 /* TODO: should we mask last_irq? */ 388 if (IS_IRQ_BANK1(irq)) { 389 pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 390 INTC_PENDING_BANK1); 391 if (pending == 0) { 392 irq = BANK2_START; /* skip to next bank */ 393 } else do { 394 if (pending & (1 << IRQ_BANK1(irq))) 395 return irq; 396 irq++; 397 } while (IS_IRQ_BANK1(irq)); 398 } 399 if (IS_IRQ_BANK2(irq)) { 400 pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 401 INTC_PENDING_BANK2); 402 if (pending == 0) { 403 irq = BANK0_START; /* skip to next bank */ 404 } else do { 405 if (pending & (1 << IRQ_BANK2(irq))) 406 return irq; 407 irq++; 408 } while (IS_IRQ_BANK2(irq)); 409 } 410 if (IS_IRQ_BANK0(irq)) { 411 pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 412 INTC_PENDING_BANK0); 413 if (pending == 0) { 414 irq = LOCAL_START; /* skip to next bank */ 415 } else do { 416 if (pending & (1 << IRQ_BANK0(irq))) 417 return irq; 418 irq++; 419 } while (IS_IRQ_BANK0(irq)); 420 } 421 if (IS_IRQ_LOCAL(irq)) { 422 /* XXX: SMP */ 423 pending = bus_space_read_4(sc->sc_iot, sc->sc_lioh, 424 ARM_LOCAL_INT_PENDING(0)); 425 pending &= ARM_LOCAL_INT_PENDING_MASK; 426 if (pending != 0) do { 427 if (pending & (1 << IRQ_LOCAL(irq))) 428 return irq; 429 irq++; 430 } while (IS_IRQ_LOCAL(irq)); 431 } 432 return (-1); 433 } 434 435 static void 436 bcm_intc_call_handler(int irq, void *frame) 437 { 438 struct bcm_intc_softc *sc = bcm_intc; 439 struct intrhand *ih; 440 int pri, s; 441 void *arg; 442 443 #ifdef DEBUG_INTC 444 if (irq != 99) 445 printf("irq %d fired\n", irq); 446 else { 447 static int cnt = 0; 448 if ((cnt++ % 100) == 0) { 449 printf("irq %d fired * _100\n", irq); 450 db_enter(); 451 } 452 } 453 #endif 454 455 pri = sc->sc_bcm_intc_handler[irq].is_irq; 456 s = bcm_intc_splraise(pri); 457 TAILQ_FOREACH(ih, &sc->sc_bcm_intc_handler[irq].is_list, ih_list) { 458 if (ih->ih_arg != 0) 459 arg = ih->ih_arg; 460 else 461 arg = frame; 462 463 if (ih->ih_fun(arg)) 464 ih->ih_count.ec_count++; 465 466 } 467 468 bcm_intc_splx(s); 469 } 470 471 void 472 bcm_intc_irq_handler(void *frame) 473 { 474 int irq = -1; 475 476 while ((irq = bcm_intc_get_next_irq(irq)) != -1) 477 bcm_intc_call_handler(irq, frame); 478 } 479 480 void * 481 bcm_intc_intr_establish_fdt(void *cookie, int *cell, int level, 482 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 483 { 484 struct bcm_intc_softc *sc = (struct bcm_intc_softc *)cookie; 485 int irq; 486 487 irq = cell[1]; 488 if (cell[0] == 0) 489 irq += BANK0_START; 490 else if (cell[0] == 1) 491 irq += BANK1_START; 492 else if (cell[0] == 2) 493 irq += BANK2_START; 494 else if (cell[0] == 3) 495 irq += LOCAL_START; 496 else 497 panic("%s: bogus interrupt type", sc->sc_dev.dv_xname); 498 499 return bcm_intc_intr_establish(irq, level, ci, func, arg, name); 500 } 501 502 void * 503 l1_intc_intr_establish_fdt(void *cookie, int *cell, int level, 504 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 505 { 506 int irq; 507 508 irq = cell[0] + LOCAL_START; 509 return bcm_intc_intr_establish(irq, level, ci, func, arg, name); 510 } 511 512 void * 513 bcm_intc_intr_establish(int irqno, int level, struct cpu_info *ci, 514 int (*func)(void *), void *arg, char *name) 515 { 516 struct bcm_intc_softc *sc = bcm_intc; 517 struct intrhand *ih; 518 int psw; 519 520 if (irqno < 0 || irqno >= INTC_NIRQ) 521 panic("bcm_intc_intr_establish: bogus irqnumber %d: %s", 522 irqno, name); 523 524 if (ci == NULL) 525 ci = &cpu_info_primary; 526 else if (!CPU_IS_PRIMARY(ci)) 527 return NULL; 528 529 psw = disable_interrupts(PSR_I); 530 531 ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK); 532 ih->ih_fun = func; 533 ih->ih_arg = arg; 534 ih->ih_ipl = level & IPL_IRQMASK; 535 ih->ih_irq = irqno; 536 ih->ih_name = name; 537 538 TAILQ_INSERT_TAIL(&sc->sc_bcm_intc_handler[irqno].is_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("%s irq %d level %d [%s]\n", __func__, irqno, level, 545 name); 546 #endif 547 bcm_intc_calc_mask(); 548 549 restore_interrupts(psw); 550 return (ih); 551 } 552 553 void 554 bcm_intc_intr_disestablish(void *cookie) 555 { 556 struct bcm_intc_softc *sc = bcm_intc; 557 struct intrhand *ih = cookie; 558 int irqno = ih->ih_irq; 559 int psw; 560 psw = disable_interrupts(PSR_I); 561 TAILQ_REMOVE(&sc->sc_bcm_intc_handler[irqno].is_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 } 567 568 const char * 569 bcm_intc_intr_string(void *cookie) 570 { 571 return "huh?"; 572 } 573