1/*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1991 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * William Jolitz. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34/*- 35 * Copyright (c) 2002 Benno Rice. 36 * All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 57 * SUCH DAMAGE. 58 * 59 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 60 * form: src/sys/i386/isa/intr_machdep.c,v 1.57 2001/07/20 61 * 62 * $FreeBSD$ 63 */ 64 65#include "opt_isa.h" 66 67#include <sys/param.h> 68#include <sys/systm.h> 69#include <sys/kernel.h> 70#include <sys/queue.h> 71#include <sys/bus.h> 72#include <sys/cpuset.h> 73#include <sys/interrupt.h> 74#include <sys/ktr.h> 75#include <sys/lock.h> 76#include <sys/malloc.h> 77#include <sys/mutex.h> 78#include <sys/pcpu.h> 79#include <sys/smp.h> 80#include <sys/syslog.h> 81#include <sys/vmmeter.h> 82#include <sys/proc.h> 83 84#include <machine/frame.h> 85#include <machine/intr_machdep.h> 86#include <machine/md_var.h> 87#include <machine/smp.h> 88#include <machine/trap.h> 89 90#include "pic_if.h" 91 92#define MAX_STRAY_LOG 5 93 94static MALLOC_DEFINE(M_INTR, "intr", "interrupt handler data"); 95 96struct powerpc_intr { 97 struct intr_event *event; 98 long *cntp; 99 u_int irq; 100 device_t pic; 101 u_int intline; 102 u_int vector; 103 u_int cntindex; 104 cpuset_t cpu; 105 enum intr_trigger trig; 106 enum intr_polarity pol; 107 int fwcode; 108 int ipi; 109}; 110 111struct pic { 112 device_t dev; 113 uint32_t node; 114 u_int irqs; 115 u_int ipis; 116 int base; 117}; 118 119static u_int intrcnt_index = 0; 120static struct mtx intr_table_lock; 121static struct powerpc_intr *powerpc_intrs[INTR_VECTORS]; 122static struct pic piclist[MAX_PICS]; 123static u_int nvectors; /* Allocated vectors */ 124static u_int npics; /* PICs registered */ 125#ifdef DEV_ISA 126static u_int nirqs = 16; /* Allocated IRQS (ISA pre-allocated). */ 127#else 128static u_int nirqs = 0; /* Allocated IRQs. */ 129#endif 130static u_int stray_count; 131 132u_long intrcnt[INTR_VECTORS]; 133char intrnames[INTR_VECTORS * (MAXCOMLEN + 1)]; 134size_t sintrcnt = sizeof(intrcnt); 135size_t sintrnames = sizeof(intrnames); 136 137device_t root_pic; 138 139#ifdef SMP 140static void *ipi_cookie; 141#endif 142 143static void 144intr_init(void *dummy __unused) 145{ 146 147 mtx_init(&intr_table_lock, "intr sources lock", NULL, MTX_DEF); 148} 149SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); 150 151#ifdef SMP 152static void 153smp_intr_init(void *dummy __unused) 154{ 155 struct powerpc_intr *i; 156 int vector; 157 158 for (vector = 0; vector < nvectors; vector++) { 159 i = powerpc_intrs[vector]; 160 if (i != NULL && i->event != NULL && i->pic == root_pic) 161 PIC_BIND(i->pic, i->intline, i->cpu); 162 } 163} 164SYSINIT(smp_intr_init, SI_SUB_SMP, SI_ORDER_ANY, smp_intr_init, NULL); 165#endif 166 167static void 168intrcnt_setname(const char *name, int index) 169{ 170 171 snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s", 172 MAXCOMLEN, name); 173} 174 175void 176intrcnt_add(const char *name, u_long **countp) 177{ 178 int idx; 179 180 idx = atomic_fetchadd_int(&intrcnt_index, 1); 181 KASSERT(idx < INTR_VECTORS, ("intrcnt_add: Interrupt counter index " 182 "reached INTR_VECTORS")); 183 *countp = &intrcnt[idx]; 184 intrcnt_setname(name, idx); 185} 186 187static struct powerpc_intr * 188intr_lookup(u_int irq) 189{ 190 char intrname[16]; 191 struct powerpc_intr *i, *iscan; 192 int vector; 193 194 mtx_lock(&intr_table_lock); 195 for (vector = 0; vector < nvectors; vector++) { 196 i = powerpc_intrs[vector]; 197 if (i != NULL && i->irq == irq) { 198 mtx_unlock(&intr_table_lock); 199 return (i); 200 } 201 } 202 203 i = malloc(sizeof(*i), M_INTR, M_NOWAIT); 204 if (i == NULL) { 205 mtx_unlock(&intr_table_lock); 206 return (NULL); 207 } 208 209 i->event = NULL; 210 i->cntp = NULL; 211 i->trig = INTR_TRIGGER_CONFORM; 212 i->pol = INTR_POLARITY_CONFORM; 213 i->irq = irq; 214 i->pic = NULL; 215 i->vector = -1; 216 i->fwcode = 0; 217 i->ipi = 0; 218 219#ifdef SMP 220 i->cpu = all_cpus; 221#else 222 CPU_SETOF(0, &i->cpu); 223#endif 224 225 for (vector = 0; vector < INTR_VECTORS && vector <= nvectors; 226 vector++) { 227 iscan = powerpc_intrs[vector]; 228 if (iscan != NULL && iscan->irq == irq) 229 break; 230 if (iscan == NULL && i->vector == -1) 231 i->vector = vector; 232 iscan = NULL; 233 } 234 235 if (iscan == NULL && i->vector != -1) { 236 powerpc_intrs[i->vector] = i; 237 i->cntindex = atomic_fetchadd_int(&intrcnt_index, 1); 238 i->cntp = &intrcnt[i->cntindex]; 239 sprintf(intrname, "irq%u:", i->irq); 240 intrcnt_setname(intrname, i->cntindex); 241 nvectors++; 242 } 243 mtx_unlock(&intr_table_lock); 244 245 if (iscan != NULL || i->vector == -1) { 246 free(i, M_INTR); 247 i = iscan; 248 } 249 250 return (i); 251} 252 253static int 254powerpc_map_irq(struct powerpc_intr *i) 255{ 256 struct pic *p; 257 u_int cnt; 258 int idx; 259 260 for (idx = 0; idx < npics; idx++) { 261 p = &piclist[idx]; 262 cnt = p->irqs + p->ipis; 263 if (i->irq >= p->base && i->irq < p->base + cnt) 264 break; 265 } 266 if (idx == npics) 267 return (EINVAL); 268 269 i->intline = i->irq - p->base; 270 i->pic = p->dev; 271 272 /* Try a best guess if that failed */ 273 if (i->pic == NULL) 274 i->pic = root_pic; 275 276 return (0); 277} 278 279static void 280powerpc_intr_eoi(void *arg) 281{ 282 struct powerpc_intr *i = arg; 283 284 PIC_EOI(i->pic, i->intline); 285} 286 287static void 288powerpc_intr_pre_ithread(void *arg) 289{ 290 struct powerpc_intr *i = arg; 291 292 PIC_MASK(i->pic, i->intline); 293 PIC_EOI(i->pic, i->intline); 294} 295 296static void 297powerpc_intr_post_ithread(void *arg) 298{ 299 struct powerpc_intr *i = arg; 300 301 PIC_UNMASK(i->pic, i->intline); 302} 303 304static int 305powerpc_assign_intr_cpu(void *arg, int cpu) 306{ 307#ifdef SMP 308 struct powerpc_intr *i = arg; 309 310 if (cpu == NOCPU) 311 i->cpu = all_cpus; 312 else 313 CPU_SETOF(cpu, &i->cpu); 314 315 if (!cold && i->pic != NULL && i->pic == root_pic) 316 PIC_BIND(i->pic, i->intline, i->cpu); 317 318 return (0); 319#else 320 return (EOPNOTSUPP); 321#endif 322} 323 324u_int 325powerpc_register_pic(device_t dev, uint32_t node, u_int irqs, u_int ipis, 326 u_int atpic) 327{ 328 struct pic *p; 329 u_int irq; 330 int idx; 331 332 mtx_lock(&intr_table_lock); 333 334 /* XXX see powerpc_get_irq(). */ 335 for (idx = 0; idx < npics; idx++) { 336 p = &piclist[idx]; 337 if (p->node != node) 338 continue; 339 if (node != 0 || p->dev == dev) 340 break; 341 } 342 p = &piclist[idx]; 343 344 p->dev = dev; 345 p->node = node; 346 p->irqs = irqs; 347 p->ipis = ipis; 348 if (idx == npics) { 349#ifdef DEV_ISA 350 p->base = (atpic) ? 0 : nirqs; 351#else 352 p->base = nirqs; 353#endif 354 irq = p->base + irqs + ipis; 355 nirqs = MAX(nirqs, irq); 356 npics++; 357 } 358 359 KASSERT(npics < MAX_PICS, 360 ("Number of PICs exceeds maximum (%d)", MAX_PICS)); 361 362 mtx_unlock(&intr_table_lock); 363 364 return (p->base); 365} 366 367u_int 368powerpc_get_irq(uint32_t node, u_int pin) 369{ 370 int idx; 371 372 if (node == 0) 373 return (pin); 374 375 mtx_lock(&intr_table_lock); 376 for (idx = 0; idx < npics; idx++) { 377 if (piclist[idx].node == node) { 378 mtx_unlock(&intr_table_lock); 379 return (piclist[idx].base + pin); 380 } 381 } 382 383 /* 384 * XXX we should never encounter an unregistered PIC, but that 385 * can only be done when we properly support bus enumeration 386 * using multiple passes. Until then, fake an entry and give it 387 * some adhoc maximum number of IRQs and IPIs. 388 */ 389 piclist[idx].dev = NULL; 390 piclist[idx].node = node; 391 piclist[idx].irqs = 124; 392 piclist[idx].ipis = 4; 393 piclist[idx].base = nirqs; 394 nirqs += (1 << 25); 395 npics++; 396 397 KASSERT(npics < MAX_PICS, 398 ("Number of PICs exceeds maximum (%d)", MAX_PICS)); 399 400 mtx_unlock(&intr_table_lock); 401 402 return (piclist[idx].base + pin); 403} 404 405int 406powerpc_enable_intr(void) 407{ 408 struct powerpc_intr *i; 409 int error, vector; 410#ifdef SMP 411 int n; 412#endif 413 414 if (npics == 0) 415 panic("no PIC detected\n"); 416 417 if (root_pic == NULL) 418 root_pic = piclist[0].dev; 419 420#ifdef SMP 421 /* Install an IPI handler. */ 422 if (mp_ncpus > 1) { 423 for (n = 0; n < npics; n++) { 424 if (piclist[n].dev != root_pic) 425 continue; 426 427 KASSERT(piclist[n].ipis != 0, 428 ("%s: SMP root PIC does not supply any IPIs", 429 __func__)); 430 error = powerpc_setup_intr("IPI", 431 MAP_IRQ(piclist[n].node, piclist[n].irqs), 432 powerpc_ipi_handler, NULL, NULL, 433 INTR_TYPE_MISC | INTR_EXCL, &ipi_cookie); 434 if (error) { 435 printf("unable to setup IPI handler\n"); 436 return (error); 437 } 438 439 /* 440 * Some subterfuge: disable late EOI and mark this 441 * as an IPI to the dispatch layer. 442 */ 443 i = intr_lookup(MAP_IRQ(piclist[n].node, 444 piclist[n].irqs)); 445 i->event->ie_post_filter = NULL; 446 i->ipi = 1; 447 } 448 } 449#endif 450 451 for (vector = 0; vector < nvectors; vector++) { 452 i = powerpc_intrs[vector]; 453 if (i == NULL) 454 continue; 455 456 error = powerpc_map_irq(i); 457 if (error) 458 continue; 459 460 if (i->trig == INTR_TRIGGER_INVALID) 461 PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, 462 &i->trig, &i->pol); 463 if (i->trig != INTR_TRIGGER_CONFORM || 464 i->pol != INTR_POLARITY_CONFORM) 465 PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); 466 467 if (i->event != NULL) 468 PIC_ENABLE(i->pic, i->intline, vector); 469 } 470 471 return (0); 472} 473 474int 475powerpc_setup_intr(const char *name, u_int irq, driver_filter_t filter, 476 driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep) 477{ 478 struct powerpc_intr *i; 479 int error, enable = 0; 480 481 i = intr_lookup(irq); 482 if (i == NULL) 483 return (ENOMEM); 484 485 if (i->event == NULL) { 486 error = intr_event_create(&i->event, (void *)i, 0, irq, 487 powerpc_intr_pre_ithread, powerpc_intr_post_ithread, 488 powerpc_intr_eoi, powerpc_assign_intr_cpu, "irq%u:", irq); 489 if (error) 490 return (error); 491 492 enable = 1; 493 } 494 495 error = intr_event_add_handler(i->event, name, filter, handler, arg, 496 intr_priority(flags), flags, cookiep); 497 498 mtx_lock(&intr_table_lock); 499 intrcnt_setname(i->event->ie_fullname, i->cntindex); 500 mtx_unlock(&intr_table_lock); 501 502 if (!cold) { 503 error = powerpc_map_irq(i); 504 505 if (!error) { 506 if (i->trig == INTR_TRIGGER_INVALID) 507 PIC_TRANSLATE_CODE(i->pic, i->intline, 508 i->fwcode, &i->trig, &i->pol); 509 510 if (i->trig != INTR_TRIGGER_CONFORM || 511 i->pol != INTR_POLARITY_CONFORM) 512 PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); 513 514 if (i->pic == root_pic) 515 PIC_BIND(i->pic, i->intline, i->cpu); 516 517 if (enable) 518 PIC_ENABLE(i->pic, i->intline, i->vector); 519 } 520 } 521 return (error); 522} 523 524int 525powerpc_teardown_intr(void *cookie) 526{ 527 528 return (intr_event_remove_handler(cookie)); 529} 530 531#ifdef SMP 532int 533powerpc_bind_intr(u_int irq, u_char cpu) 534{ 535 struct powerpc_intr *i; 536 537 i = intr_lookup(irq); 538 if (i == NULL) 539 return (ENOMEM); 540 541 return (intr_event_bind(i->event, cpu)); 542} 543#endif 544 545int 546powerpc_fw_config_intr(int irq, int sense_code) 547{ 548 struct powerpc_intr *i; 549 550 i = intr_lookup(irq); 551 if (i == NULL) 552 return (ENOMEM); 553 554 i->trig = INTR_TRIGGER_INVALID; 555 i->pol = INTR_POLARITY_CONFORM; 556 i->fwcode = sense_code; 557 558 if (!cold && i->pic != NULL) { 559 PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig, 560 &i->pol); 561 PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); 562 } 563 564 return (0); 565} 566 567int 568powerpc_config_intr(int irq, enum intr_trigger trig, enum intr_polarity pol) 569{ 570 struct powerpc_intr *i; 571 572 i = intr_lookup(irq); 573 if (i == NULL) 574 return (ENOMEM); 575 576 i->trig = trig; 577 i->pol = pol; 578 579 if (!cold && i->pic != NULL) 580 PIC_CONFIG(i->pic, i->intline, trig, pol); 581 582 return (0); 583} 584 585void 586powerpc_dispatch_intr(u_int vector, struct trapframe *tf) 587{ 588 struct powerpc_intr *i; 589 struct intr_event *ie; 590 591 i = powerpc_intrs[vector]; 592 if (i == NULL) 593 goto stray; 594 595 (*i->cntp)++; 596 597 ie = i->event; 598 KASSERT(ie != NULL, ("%s: interrupt without an event", __func__)); 599 600 /* 601 * IPIs are magical and need to be EOI'ed before filtering. 602 * This prevents races in IPI handling. 603 */ 604 if (i->ipi) 605 PIC_EOI(i->pic, i->intline); 606 607 if (intr_event_handle(ie, tf) != 0) { 608 goto stray; 609 } 610 return; 611 612stray: 613 stray_count++; 614 if (stray_count <= MAX_STRAY_LOG) { 615 printf("stray irq %d\n", i ? i->irq : -1); 616 if (stray_count >= MAX_STRAY_LOG) { 617 printf("got %d stray interrupts, not logging anymore\n", 618 MAX_STRAY_LOG); 619 } 620 } 621 if (i != NULL) 622 PIC_MASK(i->pic, i->intline); 623} 624 625void 626powerpc_intr_mask(u_int irq) 627{ 628 struct powerpc_intr *i; 629 630 i = intr_lookup(irq); 631 if (i == NULL || i->pic == NULL) 632 return; 633 634 PIC_MASK(i->pic, i->intline); 635} 636 637void 638powerpc_intr_unmask(u_int irq) 639{ 640 struct powerpc_intr *i; 641 642 i = intr_lookup(irq); 643 if (i == NULL || i->pic == NULL) 644 return; 645 646 PIC_UNMASK(i->pic, i->intline); 647} 648