12b3ad188SAdrian Chadd /*- 2bff6be3eSSvatopluk Kraus * Copyright (c) 2015-2016 Svatopluk Kraus 3bff6be3eSSvatopluk Kraus * Copyright (c) 2015-2016 Michal Meloun 42b3ad188SAdrian Chadd * All rights reserved. 52b3ad188SAdrian Chadd * 62b3ad188SAdrian Chadd * Redistribution and use in source and binary forms, with or without 72b3ad188SAdrian Chadd * modification, are permitted provided that the following conditions 82b3ad188SAdrian Chadd * are met: 92b3ad188SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 102b3ad188SAdrian Chadd * notice, this list of conditions and the following disclaimer. 112b3ad188SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 122b3ad188SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 132b3ad188SAdrian Chadd * documentation and/or other materials provided with the distribution. 142b3ad188SAdrian Chadd * 152b3ad188SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 162b3ad188SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 172b3ad188SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 182b3ad188SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 192b3ad188SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 202b3ad188SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 212b3ad188SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 222b3ad188SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 232b3ad188SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 242b3ad188SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 252b3ad188SAdrian Chadd * SUCH DAMAGE. 262b3ad188SAdrian Chadd */ 272b3ad188SAdrian Chadd 282b3ad188SAdrian Chadd #include <sys/cdefs.h> 292b3ad188SAdrian Chadd __FBSDID("$FreeBSD$"); 302b3ad188SAdrian Chadd 312b3ad188SAdrian Chadd /* 322b3ad188SAdrian Chadd * New-style Interrupt Framework 332b3ad188SAdrian Chadd * 342b3ad188SAdrian Chadd * TODO: - to support IPI (PPI) enabling on other CPUs if already started 352b3ad188SAdrian Chadd * - to complete things for removable PICs 362b3ad188SAdrian Chadd */ 372b3ad188SAdrian Chadd 38bff6be3eSSvatopluk Kraus #include "opt_acpi.h" 392b3ad188SAdrian Chadd #include "opt_ddb.h" 40df7a2251SAndrew Turner #include "opt_hwpmc_hooks.h" 412b3ad188SAdrian Chadd #include "opt_platform.h" 422b3ad188SAdrian Chadd 432b3ad188SAdrian Chadd #include <sys/param.h> 442b3ad188SAdrian Chadd #include <sys/systm.h> 452b3ad188SAdrian Chadd #include <sys/kernel.h> 462b3ad188SAdrian Chadd #include <sys/syslog.h> 472b3ad188SAdrian Chadd #include <sys/malloc.h> 482b3ad188SAdrian Chadd #include <sys/proc.h> 492b3ad188SAdrian Chadd #include <sys/queue.h> 502b3ad188SAdrian Chadd #include <sys/bus.h> 512b3ad188SAdrian Chadd #include <sys/interrupt.h> 522b3ad188SAdrian Chadd #include <sys/conf.h> 532b3ad188SAdrian Chadd #include <sys/cpuset.h> 546b42a1f4SAndrew Turner #include <sys/rman.h> 552b3ad188SAdrian Chadd #include <sys/sched.h> 562b3ad188SAdrian Chadd #include <sys/smp.h> 57df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 58df7a2251SAndrew Turner #include <sys/pmckern.h> 59df7a2251SAndrew Turner #endif 60df7a2251SAndrew Turner 612b3ad188SAdrian Chadd #include <machine/atomic.h> 622b3ad188SAdrian Chadd #include <machine/intr.h> 632b3ad188SAdrian Chadd #include <machine/cpu.h> 642b3ad188SAdrian Chadd #include <machine/smp.h> 652b3ad188SAdrian Chadd #include <machine/stdarg.h> 662b3ad188SAdrian Chadd 670cc5515aSAdrian Chadd #ifdef FDT 682b3ad188SAdrian Chadd #include <dev/ofw/openfirm.h> 692b3ad188SAdrian Chadd #include <dev/ofw/ofw_bus.h> 702b3ad188SAdrian Chadd #include <dev/ofw/ofw_bus_subr.h> 710cc5515aSAdrian Chadd #endif 722b3ad188SAdrian Chadd 732b3ad188SAdrian Chadd #ifdef DDB 742b3ad188SAdrian Chadd #include <ddb/ddb.h> 752b3ad188SAdrian Chadd #endif 762b3ad188SAdrian Chadd 772b3ad188SAdrian Chadd #include "pic_if.h" 783fc155dcSAndrew Turner #include "msi_if.h" 792b3ad188SAdrian Chadd 802b3ad188SAdrian Chadd #define INTRNAME_LEN (2*MAXCOMLEN + 1) 812b3ad188SAdrian Chadd 822b3ad188SAdrian Chadd #ifdef DEBUG 832b3ad188SAdrian Chadd #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 842b3ad188SAdrian Chadd printf(fmt,##args); } while (0) 852b3ad188SAdrian Chadd #else 862b3ad188SAdrian Chadd #define debugf(fmt, args...) 872b3ad188SAdrian Chadd #endif 882b3ad188SAdrian Chadd 892b3ad188SAdrian Chadd MALLOC_DECLARE(M_INTRNG); 902b3ad188SAdrian Chadd MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); 912b3ad188SAdrian Chadd 922b3ad188SAdrian Chadd /* Main interrupt handler called from assembler -> 'hidden' for C code. */ 932b3ad188SAdrian Chadd void intr_irq_handler(struct trapframe *tf); 942b3ad188SAdrian Chadd 952b3ad188SAdrian Chadd /* Root interrupt controller stuff. */ 965b70c08cSSvatopluk Kraus device_t intr_irq_root_dev; 972b3ad188SAdrian Chadd static intr_irq_filter_t *irq_root_filter; 982b3ad188SAdrian Chadd static void *irq_root_arg; 992b3ad188SAdrian Chadd static u_int irq_root_ipicount; 1002b3ad188SAdrian Chadd 101*d1605cdaSAndrew Turner struct intr_pic_child { 102*d1605cdaSAndrew Turner SLIST_ENTRY(intr_pic_child) pc_next; 103*d1605cdaSAndrew Turner struct intr_pic *pc_pic; 104*d1605cdaSAndrew Turner intr_child_irq_filter_t *pc_filter; 105*d1605cdaSAndrew Turner void *pc_filter_arg; 106*d1605cdaSAndrew Turner uintptr_t pc_start; 107*d1605cdaSAndrew Turner uintptr_t pc_length; 108*d1605cdaSAndrew Turner }; 109*d1605cdaSAndrew Turner 1102b3ad188SAdrian Chadd /* Interrupt controller definition. */ 1112b3ad188SAdrian Chadd struct intr_pic { 1122b3ad188SAdrian Chadd SLIST_ENTRY(intr_pic) pic_next; 1132b3ad188SAdrian Chadd intptr_t pic_xref; /* hardware identification */ 1142b3ad188SAdrian Chadd device_t pic_dev; 1153fc155dcSAndrew Turner #define FLAG_PIC (1 << 0) 1163fc155dcSAndrew Turner #define FLAG_MSI (1 << 1) 1173fc155dcSAndrew Turner u_int pic_flags; 118*d1605cdaSAndrew Turner struct mtx pic_child_lock; 119*d1605cdaSAndrew Turner SLIST_HEAD(, intr_pic_child) pic_children; 1202b3ad188SAdrian Chadd }; 1212b3ad188SAdrian Chadd 1222b3ad188SAdrian Chadd static struct mtx pic_list_lock; 1232b3ad188SAdrian Chadd static SLIST_HEAD(, intr_pic) pic_list; 1242b3ad188SAdrian Chadd 1252b3ad188SAdrian Chadd static struct intr_pic *pic_lookup(device_t dev, intptr_t xref); 1262b3ad188SAdrian Chadd 1272b3ad188SAdrian Chadd /* Interrupt source definition. */ 1282b3ad188SAdrian Chadd static struct mtx isrc_table_lock; 1292b3ad188SAdrian Chadd static struct intr_irqsrc *irq_sources[NIRQ]; 1302b3ad188SAdrian Chadd u_int irq_next_free; 1312b3ad188SAdrian Chadd 132bff6be3eSSvatopluk Kraus /* 133bff6be3eSSvatopluk Kraus * XXX - All stuff around struct intr_dev_data is considered as temporary 134bff6be3eSSvatopluk Kraus * until better place for storing struct intr_map_data will be find. 135bff6be3eSSvatopluk Kraus * 136bff6be3eSSvatopluk Kraus * For now, there are two global interrupt numbers spaces: 137bff6be3eSSvatopluk Kraus * <0, NIRQ) ... interrupts without config data 138bff6be3eSSvatopluk Kraus * managed in irq_sources[] 139bff6be3eSSvatopluk Kraus * IRQ_DDATA_BASE + <0, 2 * NIRQ) ... interrupts with config data 140bff6be3eSSvatopluk Kraus * managed in intr_ddata_tab[] 141bff6be3eSSvatopluk Kraus * 142bff6be3eSSvatopluk Kraus * Read intr_ddata_lookup() to see how these spaces are worked with. 143bff6be3eSSvatopluk Kraus * Note that each interrupt number from second space duplicates some number 144bff6be3eSSvatopluk Kraus * from first space at this moment. An interrupt number from first space can 145bff6be3eSSvatopluk Kraus * be duplicated even multiple times in second space. 146bff6be3eSSvatopluk Kraus */ 147bff6be3eSSvatopluk Kraus struct intr_dev_data { 148bff6be3eSSvatopluk Kraus device_t idd_dev; 149bff6be3eSSvatopluk Kraus intptr_t idd_xref; 150bff6be3eSSvatopluk Kraus u_int idd_irq; 151cd642c88SSvatopluk Kraus struct intr_map_data * idd_data; 152bff6be3eSSvatopluk Kraus struct intr_irqsrc * idd_isrc; 153bff6be3eSSvatopluk Kraus }; 154bff6be3eSSvatopluk Kraus 155bff6be3eSSvatopluk Kraus static struct intr_dev_data *intr_ddata_tab[2 * NIRQ]; 156bff6be3eSSvatopluk Kraus static u_int intr_ddata_first_unused; 157bff6be3eSSvatopluk Kraus 158bff6be3eSSvatopluk Kraus #define IRQ_DDATA_BASE 10000 1598442087fSMichal Meloun CTASSERT(IRQ_DDATA_BASE > nitems(irq_sources)); 160bff6be3eSSvatopluk Kraus 1612b3ad188SAdrian Chadd #ifdef SMP 1622b3ad188SAdrian Chadd static boolean_t irq_assign_cpu = FALSE; 1632b3ad188SAdrian Chadd #endif 1642b3ad188SAdrian Chadd 1652b3ad188SAdrian Chadd /* 1662b3ad188SAdrian Chadd * - 2 counters for each I/O interrupt. 1672b3ad188SAdrian Chadd * - MAXCPU counters for each IPI counters for SMP. 1682b3ad188SAdrian Chadd */ 1692b3ad188SAdrian Chadd #ifdef SMP 1702b3ad188SAdrian Chadd #define INTRCNT_COUNT (NIRQ * 2 + INTR_IPI_COUNT * MAXCPU) 1712b3ad188SAdrian Chadd #else 1722b3ad188SAdrian Chadd #define INTRCNT_COUNT (NIRQ * 2) 1732b3ad188SAdrian Chadd #endif 1742b3ad188SAdrian Chadd 1752b3ad188SAdrian Chadd /* Data for MI statistics reporting. */ 1762b3ad188SAdrian Chadd u_long intrcnt[INTRCNT_COUNT]; 1772b3ad188SAdrian Chadd char intrnames[INTRCNT_COUNT * INTRNAME_LEN]; 1782b3ad188SAdrian Chadd size_t sintrcnt = sizeof(intrcnt); 1792b3ad188SAdrian Chadd size_t sintrnames = sizeof(intrnames); 1802b3ad188SAdrian Chadd static u_int intrcnt_index; 1812b3ad188SAdrian Chadd 1822b3ad188SAdrian Chadd /* 1832b3ad188SAdrian Chadd * Interrupt framework initialization routine. 1842b3ad188SAdrian Chadd */ 1852b3ad188SAdrian Chadd static void 1862b3ad188SAdrian Chadd intr_irq_init(void *dummy __unused) 1872b3ad188SAdrian Chadd { 1882b3ad188SAdrian Chadd 1892b3ad188SAdrian Chadd SLIST_INIT(&pic_list); 1902b3ad188SAdrian Chadd mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); 1913fc155dcSAndrew Turner 1922b3ad188SAdrian Chadd mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); 1932b3ad188SAdrian Chadd } 1942b3ad188SAdrian Chadd SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); 1952b3ad188SAdrian Chadd 1962b3ad188SAdrian Chadd static void 1972b3ad188SAdrian Chadd intrcnt_setname(const char *name, int index) 1982b3ad188SAdrian Chadd { 1992b3ad188SAdrian Chadd 2002b3ad188SAdrian Chadd snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s", 2012b3ad188SAdrian Chadd INTRNAME_LEN - 1, name); 2022b3ad188SAdrian Chadd } 2032b3ad188SAdrian Chadd 2042b3ad188SAdrian Chadd /* 2052b3ad188SAdrian Chadd * Update name for interrupt source with interrupt event. 2062b3ad188SAdrian Chadd */ 2072b3ad188SAdrian Chadd static void 2082b3ad188SAdrian Chadd intrcnt_updatename(struct intr_irqsrc *isrc) 2092b3ad188SAdrian Chadd { 2102b3ad188SAdrian Chadd 2112b3ad188SAdrian Chadd /* QQQ: What about stray counter name? */ 2122b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2132b3ad188SAdrian Chadd intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index); 2142b3ad188SAdrian Chadd } 2152b3ad188SAdrian Chadd 2162b3ad188SAdrian Chadd /* 2172b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counter increment. 2182b3ad188SAdrian Chadd */ 2192b3ad188SAdrian Chadd static inline void 2202b3ad188SAdrian Chadd isrc_increment_count(struct intr_irqsrc *isrc) 2212b3ad188SAdrian Chadd { 2222b3ad188SAdrian Chadd 223bff6be3eSSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_PPI) 224bff6be3eSSvatopluk Kraus atomic_add_long(&isrc->isrc_count[0], 1); 225bff6be3eSSvatopluk Kraus else 2262b3ad188SAdrian Chadd isrc->isrc_count[0]++; 2272b3ad188SAdrian Chadd } 2282b3ad188SAdrian Chadd 2292b3ad188SAdrian Chadd /* 2302b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt stray counter increment. 2312b3ad188SAdrian Chadd */ 2322b3ad188SAdrian Chadd static inline void 2332b3ad188SAdrian Chadd isrc_increment_straycount(struct intr_irqsrc *isrc) 2342b3ad188SAdrian Chadd { 2352b3ad188SAdrian Chadd 2362b3ad188SAdrian Chadd isrc->isrc_count[1]++; 2372b3ad188SAdrian Chadd } 2382b3ad188SAdrian Chadd 2392b3ad188SAdrian Chadd /* 2402b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt name update. 2412b3ad188SAdrian Chadd */ 2422b3ad188SAdrian Chadd static void 2432b3ad188SAdrian Chadd isrc_update_name(struct intr_irqsrc *isrc, const char *name) 2442b3ad188SAdrian Chadd { 2452b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2462b3ad188SAdrian Chadd 2472b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2482b3ad188SAdrian Chadd 2492b3ad188SAdrian Chadd if (name != NULL) { 2502b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); 2512b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2522b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, 2532b3ad188SAdrian Chadd name); 2542b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2552b3ad188SAdrian Chadd } else { 2562b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); 2572b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2582b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); 2592b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2602b3ad188SAdrian Chadd } 2612b3ad188SAdrian Chadd } 2622b3ad188SAdrian Chadd 2632b3ad188SAdrian Chadd /* 2642b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counters setup. 2652b3ad188SAdrian Chadd */ 2662b3ad188SAdrian Chadd static void 2672b3ad188SAdrian Chadd isrc_setup_counters(struct intr_irqsrc *isrc) 2682b3ad188SAdrian Chadd { 2692b3ad188SAdrian Chadd u_int index; 2702b3ad188SAdrian Chadd 2712b3ad188SAdrian Chadd /* 2722b3ad188SAdrian Chadd * XXX - it does not work well with removable controllers and 2732b3ad188SAdrian Chadd * interrupt sources !!! 2742b3ad188SAdrian Chadd */ 2752b3ad188SAdrian Chadd index = atomic_fetchadd_int(&intrcnt_index, 2); 2762b3ad188SAdrian Chadd isrc->isrc_index = index; 2772b3ad188SAdrian Chadd isrc->isrc_count = &intrcnt[index]; 2782b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 2792b3ad188SAdrian Chadd } 2802b3ad188SAdrian Chadd 281bff6be3eSSvatopluk Kraus /* 282bff6be3eSSvatopluk Kraus * Virtualization for interrupt source interrupt counters release. 283bff6be3eSSvatopluk Kraus */ 284bff6be3eSSvatopluk Kraus static void 285bff6be3eSSvatopluk Kraus isrc_release_counters(struct intr_irqsrc *isrc) 286bff6be3eSSvatopluk Kraus { 287bff6be3eSSvatopluk Kraus 288bff6be3eSSvatopluk Kraus panic("%s: not implemented", __func__); 289bff6be3eSSvatopluk Kraus } 290bff6be3eSSvatopluk Kraus 2912b3ad188SAdrian Chadd #ifdef SMP 2922b3ad188SAdrian Chadd /* 2932b3ad188SAdrian Chadd * Virtualization for interrupt source IPI counters setup. 2942b3ad188SAdrian Chadd */ 2955b70c08cSSvatopluk Kraus u_long * 2965b70c08cSSvatopluk Kraus intr_ipi_setup_counters(const char *name) 2972b3ad188SAdrian Chadd { 2982b3ad188SAdrian Chadd u_int index, i; 2992b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 3002b3ad188SAdrian Chadd 3012b3ad188SAdrian Chadd index = atomic_fetchadd_int(&intrcnt_index, MAXCPU); 3022b3ad188SAdrian Chadd for (i = 0; i < MAXCPU; i++) { 3032b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); 3042b3ad188SAdrian Chadd intrcnt_setname(str, index + i); 3052b3ad188SAdrian Chadd } 3065b70c08cSSvatopluk Kraus return (&intrcnt[index]); 3072b3ad188SAdrian Chadd } 3082b3ad188SAdrian Chadd #endif 3092b3ad188SAdrian Chadd 3102b3ad188SAdrian Chadd /* 3112b3ad188SAdrian Chadd * Main interrupt dispatch handler. It's called straight 3122b3ad188SAdrian Chadd * from the assembler, where CPU interrupt is served. 3132b3ad188SAdrian Chadd */ 3142b3ad188SAdrian Chadd void 3152b3ad188SAdrian Chadd intr_irq_handler(struct trapframe *tf) 3162b3ad188SAdrian Chadd { 3172b3ad188SAdrian Chadd struct trapframe * oldframe; 3182b3ad188SAdrian Chadd struct thread * td; 3192b3ad188SAdrian Chadd 3202b3ad188SAdrian Chadd KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__)); 3212b3ad188SAdrian Chadd 3222b3ad188SAdrian Chadd PCPU_INC(cnt.v_intr); 3232b3ad188SAdrian Chadd critical_enter(); 3242b3ad188SAdrian Chadd td = curthread; 3252b3ad188SAdrian Chadd oldframe = td->td_intr_frame; 3262b3ad188SAdrian Chadd td->td_intr_frame = tf; 3272b3ad188SAdrian Chadd irq_root_filter(irq_root_arg); 3282b3ad188SAdrian Chadd td->td_intr_frame = oldframe; 3292b3ad188SAdrian Chadd critical_exit(); 330df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 331974692e3SAndrew Turner if (pmc_hook && TRAPF_USERMODE(tf) && 332974692e3SAndrew Turner (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) 333df7a2251SAndrew Turner pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf); 334df7a2251SAndrew Turner #endif 3352b3ad188SAdrian Chadd } 3362b3ad188SAdrian Chadd 337*d1605cdaSAndrew Turner int 338*d1605cdaSAndrew Turner intr_child_irq_handler(struct intr_pic *parent, uintptr_t irq) 339*d1605cdaSAndrew Turner { 340*d1605cdaSAndrew Turner struct intr_pic_child *child; 341*d1605cdaSAndrew Turner bool found; 342*d1605cdaSAndrew Turner 343*d1605cdaSAndrew Turner found = false; 344*d1605cdaSAndrew Turner mtx_lock_spin(&parent->pic_child_lock); 345*d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent->pic_children, pc_next) { 346*d1605cdaSAndrew Turner if (child->pc_start <= irq && 347*d1605cdaSAndrew Turner irq < (child->pc_start + child->pc_length)) { 348*d1605cdaSAndrew Turner found = true; 349*d1605cdaSAndrew Turner break; 350*d1605cdaSAndrew Turner } 351*d1605cdaSAndrew Turner } 352*d1605cdaSAndrew Turner mtx_unlock_spin(&parent->pic_child_lock); 353*d1605cdaSAndrew Turner 354*d1605cdaSAndrew Turner if (found) 355*d1605cdaSAndrew Turner return (child->pc_filter(child->pc_filter_arg, irq)); 356*d1605cdaSAndrew Turner 357*d1605cdaSAndrew Turner return (FILTER_STRAY); 358*d1605cdaSAndrew Turner } 359*d1605cdaSAndrew Turner 3602b3ad188SAdrian Chadd /* 3612b3ad188SAdrian Chadd * interrupt controller dispatch function for interrupts. It should 3622b3ad188SAdrian Chadd * be called straight from the interrupt controller, when associated interrupt 3632b3ad188SAdrian Chadd * source is learned. 3642b3ad188SAdrian Chadd */ 365bff6be3eSSvatopluk Kraus int 366bff6be3eSSvatopluk Kraus intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) 3672b3ad188SAdrian Chadd { 3682b3ad188SAdrian Chadd 3692b3ad188SAdrian Chadd KASSERT(isrc != NULL, ("%s: no source", __func__)); 3702b3ad188SAdrian Chadd 3712b3ad188SAdrian Chadd isrc_increment_count(isrc); 3722b3ad188SAdrian Chadd 3732b3ad188SAdrian Chadd #ifdef INTR_SOLO 3742b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 3752b3ad188SAdrian Chadd int error; 3762b3ad188SAdrian Chadd error = isrc->isrc_filter(isrc->isrc_arg, tf); 3772b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 3782b3ad188SAdrian Chadd if (error == FILTER_HANDLED) 379bff6be3eSSvatopluk Kraus return (0); 3802b3ad188SAdrian Chadd } else 3812b3ad188SAdrian Chadd #endif 3822b3ad188SAdrian Chadd if (isrc->isrc_event != NULL) { 3832b3ad188SAdrian Chadd if (intr_event_handle(isrc->isrc_event, tf) == 0) 384bff6be3eSSvatopluk Kraus return (0); 3852b3ad188SAdrian Chadd } 3862b3ad188SAdrian Chadd 3872b3ad188SAdrian Chadd isrc_increment_straycount(isrc); 388bff6be3eSSvatopluk Kraus return (EINVAL); 3892b3ad188SAdrian Chadd } 3902b3ad188SAdrian Chadd 3912b3ad188SAdrian Chadd /* 3922b3ad188SAdrian Chadd * Alloc unique interrupt number (resource handle) for interrupt source. 3932b3ad188SAdrian Chadd * 3942b3ad188SAdrian Chadd * There could be various strategies how to allocate free interrupt number 3952b3ad188SAdrian Chadd * (resource handle) for new interrupt source. 3962b3ad188SAdrian Chadd * 3972b3ad188SAdrian Chadd * 1. Handles are always allocated forward, so handles are not recycled 3982b3ad188SAdrian Chadd * immediately. However, if only one free handle left which is reused 3992b3ad188SAdrian Chadd * constantly... 4002b3ad188SAdrian Chadd */ 401bff6be3eSSvatopluk Kraus static inline int 402bff6be3eSSvatopluk Kraus isrc_alloc_irq(struct intr_irqsrc *isrc) 4032b3ad188SAdrian Chadd { 4042b3ad188SAdrian Chadd u_int maxirqs, irq; 4052b3ad188SAdrian Chadd 4062b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 4072b3ad188SAdrian Chadd 4082b3ad188SAdrian Chadd maxirqs = nitems(irq_sources); 4092b3ad188SAdrian Chadd if (irq_next_free >= maxirqs) 4102b3ad188SAdrian Chadd return (ENOSPC); 4112b3ad188SAdrian Chadd 4122b3ad188SAdrian Chadd for (irq = irq_next_free; irq < maxirqs; irq++) { 4132b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 4142b3ad188SAdrian Chadd goto found; 4152b3ad188SAdrian Chadd } 4162b3ad188SAdrian Chadd for (irq = 0; irq < irq_next_free; irq++) { 4172b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 4182b3ad188SAdrian Chadd goto found; 4192b3ad188SAdrian Chadd } 4202b3ad188SAdrian Chadd 4212b3ad188SAdrian Chadd irq_next_free = maxirqs; 4222b3ad188SAdrian Chadd return (ENOSPC); 4232b3ad188SAdrian Chadd 4242b3ad188SAdrian Chadd found: 4252b3ad188SAdrian Chadd isrc->isrc_irq = irq; 4262b3ad188SAdrian Chadd irq_sources[irq] = isrc; 4272b3ad188SAdrian Chadd 4282b3ad188SAdrian Chadd irq_next_free = irq + 1; 4292b3ad188SAdrian Chadd if (irq_next_free >= maxirqs) 4302b3ad188SAdrian Chadd irq_next_free = 0; 4312b3ad188SAdrian Chadd return (0); 4322b3ad188SAdrian Chadd } 433bff6be3eSSvatopluk Kraus 4342b3ad188SAdrian Chadd /* 4352b3ad188SAdrian Chadd * Free unique interrupt number (resource handle) from interrupt source. 4362b3ad188SAdrian Chadd */ 437bff6be3eSSvatopluk Kraus static inline int 4382b3ad188SAdrian Chadd isrc_free_irq(struct intr_irqsrc *isrc) 4392b3ad188SAdrian Chadd { 4402b3ad188SAdrian Chadd 441bff6be3eSSvatopluk Kraus mtx_assert(&isrc_table_lock, MA_OWNED); 4422b3ad188SAdrian Chadd 443bff6be3eSSvatopluk Kraus if (isrc->isrc_irq >= nitems(irq_sources)) 4442b3ad188SAdrian Chadd return (EINVAL); 445bff6be3eSSvatopluk Kraus if (irq_sources[isrc->isrc_irq] != isrc) 4462b3ad188SAdrian Chadd return (EINVAL); 4472b3ad188SAdrian Chadd 4482b3ad188SAdrian Chadd irq_sources[isrc->isrc_irq] = NULL; 4498442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 4502b3ad188SAdrian Chadd return (0); 4512b3ad188SAdrian Chadd } 452bff6be3eSSvatopluk Kraus 4532b3ad188SAdrian Chadd /* 4542b3ad188SAdrian Chadd * Lookup interrupt source by interrupt number (resource handle). 4552b3ad188SAdrian Chadd */ 456bff6be3eSSvatopluk Kraus static inline struct intr_irqsrc * 4572b3ad188SAdrian Chadd isrc_lookup(u_int irq) 4582b3ad188SAdrian Chadd { 4592b3ad188SAdrian Chadd 4602b3ad188SAdrian Chadd if (irq < nitems(irq_sources)) 4612b3ad188SAdrian Chadd return (irq_sources[irq]); 4622b3ad188SAdrian Chadd return (NULL); 4632b3ad188SAdrian Chadd } 4642b3ad188SAdrian Chadd 4652b3ad188SAdrian Chadd /* 466bff6be3eSSvatopluk Kraus * Initialize interrupt source and register it into global interrupt table. 4672b3ad188SAdrian Chadd */ 468bff6be3eSSvatopluk Kraus int 469bff6be3eSSvatopluk Kraus intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, 470bff6be3eSSvatopluk Kraus const char *fmt, ...) 4712b3ad188SAdrian Chadd { 472bff6be3eSSvatopluk Kraus int error; 473bff6be3eSSvatopluk Kraus va_list ap; 4742b3ad188SAdrian Chadd 475bff6be3eSSvatopluk Kraus bzero(isrc, sizeof(struct intr_irqsrc)); 476bff6be3eSSvatopluk Kraus isrc->isrc_dev = dev; 4778442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 478bff6be3eSSvatopluk Kraus isrc->isrc_flags = flags; 4792b3ad188SAdrian Chadd 480bff6be3eSSvatopluk Kraus va_start(ap, fmt); 481bff6be3eSSvatopluk Kraus vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); 482bff6be3eSSvatopluk Kraus va_end(ap); 483bff6be3eSSvatopluk Kraus 484bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 485bff6be3eSSvatopluk Kraus error = isrc_alloc_irq(isrc); 486bff6be3eSSvatopluk Kraus if (error != 0) { 487bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 488bff6be3eSSvatopluk Kraus return (error); 4892b3ad188SAdrian Chadd } 490bff6be3eSSvatopluk Kraus /* 491bff6be3eSSvatopluk Kraus * Setup interrupt counters, but not for IPI sources. Those are setup 492bff6be3eSSvatopluk Kraus * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust 493bff6be3eSSvatopluk Kraus * our counter pool. 494bff6be3eSSvatopluk Kraus */ 495bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 496bff6be3eSSvatopluk Kraus isrc_setup_counters(isrc); 497bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 498bff6be3eSSvatopluk Kraus return (0); 4992b3ad188SAdrian Chadd } 5002b3ad188SAdrian Chadd 5012b3ad188SAdrian Chadd /* 502bff6be3eSSvatopluk Kraus * Deregister interrupt source from global interrupt table. 503bff6be3eSSvatopluk Kraus */ 504bff6be3eSSvatopluk Kraus int 505bff6be3eSSvatopluk Kraus intr_isrc_deregister(struct intr_irqsrc *isrc) 506bff6be3eSSvatopluk Kraus { 507bff6be3eSSvatopluk Kraus int error; 508bff6be3eSSvatopluk Kraus 509bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 510bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 511bff6be3eSSvatopluk Kraus isrc_release_counters(isrc); 512bff6be3eSSvatopluk Kraus error = isrc_free_irq(isrc); 513bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 514bff6be3eSSvatopluk Kraus return (error); 515bff6be3eSSvatopluk Kraus } 516bff6be3eSSvatopluk Kraus 5175b613c19SSvatopluk Kraus #ifdef SMP 5185b613c19SSvatopluk Kraus /* 5195b613c19SSvatopluk Kraus * A support function for a PIC to decide if provided ISRC should be inited 5205b613c19SSvatopluk Kraus * on given cpu. The logic of INTR_ISRCF_BOUND flag and isrc_cpu member of 5215b613c19SSvatopluk Kraus * struct intr_irqsrc is the following: 5225b613c19SSvatopluk Kraus * 5235b613c19SSvatopluk Kraus * If INTR_ISRCF_BOUND is set, the ISRC should be inited only on cpus 5245b613c19SSvatopluk Kraus * set in isrc_cpu. If not, the ISRC should be inited on every cpu and 5255b613c19SSvatopluk Kraus * isrc_cpu is kept consistent with it. Thus isrc_cpu is always correct. 5265b613c19SSvatopluk Kraus */ 5275b613c19SSvatopluk Kraus bool 5285b613c19SSvatopluk Kraus intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu) 5295b613c19SSvatopluk Kraus { 5305b613c19SSvatopluk Kraus 5315b613c19SSvatopluk Kraus if (isrc->isrc_handlers == 0) 5325b613c19SSvatopluk Kraus return (false); 5335b613c19SSvatopluk Kraus if ((isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) == 0) 5345b613c19SSvatopluk Kraus return (false); 5355b613c19SSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_BOUND) 5365b613c19SSvatopluk Kraus return (CPU_ISSET(cpu, &isrc->isrc_cpu)); 5375b613c19SSvatopluk Kraus 5385b613c19SSvatopluk Kraus CPU_SET(cpu, &isrc->isrc_cpu); 5395b613c19SSvatopluk Kraus return (true); 5405b613c19SSvatopluk Kraus } 5415b613c19SSvatopluk Kraus #endif 5425b613c19SSvatopluk Kraus 543bff6be3eSSvatopluk Kraus static struct intr_dev_data * 544bff6be3eSSvatopluk Kraus intr_ddata_alloc(u_int extsize) 545bff6be3eSSvatopluk Kraus { 546bff6be3eSSvatopluk Kraus struct intr_dev_data *ddata; 547cd642c88SSvatopluk Kraus size_t size; 548bff6be3eSSvatopluk Kraus 549cd642c88SSvatopluk Kraus size = sizeof(*ddata); 550cd642c88SSvatopluk Kraus ddata = malloc(size + extsize, M_INTRNG, M_WAITOK | M_ZERO); 551bff6be3eSSvatopluk Kraus 552bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 553bff6be3eSSvatopluk Kraus if (intr_ddata_first_unused >= nitems(intr_ddata_tab)) { 554bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 555bff6be3eSSvatopluk Kraus free(ddata, M_INTRNG); 556bff6be3eSSvatopluk Kraus return (NULL); 557bff6be3eSSvatopluk Kraus } 558bff6be3eSSvatopluk Kraus intr_ddata_tab[intr_ddata_first_unused] = ddata; 559bff6be3eSSvatopluk Kraus ddata->idd_irq = IRQ_DDATA_BASE + intr_ddata_first_unused++; 560bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 561cd642c88SSvatopluk Kraus 562cd642c88SSvatopluk Kraus ddata->idd_data = (struct intr_map_data *)((uintptr_t)ddata + size); 563a100280eSSvatopluk Kraus ddata->idd_data->size = extsize; 564bff6be3eSSvatopluk Kraus return (ddata); 565bff6be3eSSvatopluk Kraus } 566bff6be3eSSvatopluk Kraus 567bff6be3eSSvatopluk Kraus static struct intr_irqsrc * 568bff6be3eSSvatopluk Kraus intr_ddata_lookup(u_int irq, struct intr_map_data **datap) 569bff6be3eSSvatopluk Kraus { 570bff6be3eSSvatopluk Kraus int error; 571bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 572bff6be3eSSvatopluk Kraus struct intr_dev_data *ddata; 573bff6be3eSSvatopluk Kraus 574bff6be3eSSvatopluk Kraus isrc = isrc_lookup(irq); 575bff6be3eSSvatopluk Kraus if (isrc != NULL) { 576bff6be3eSSvatopluk Kraus if (datap != NULL) 577bff6be3eSSvatopluk Kraus *datap = NULL; 578bff6be3eSSvatopluk Kraus return (isrc); 579bff6be3eSSvatopluk Kraus } 580bff6be3eSSvatopluk Kraus 581bff6be3eSSvatopluk Kraus if (irq < IRQ_DDATA_BASE) 582bff6be3eSSvatopluk Kraus return (NULL); 583bff6be3eSSvatopluk Kraus 584bff6be3eSSvatopluk Kraus irq -= IRQ_DDATA_BASE; 585bff6be3eSSvatopluk Kraus if (irq >= nitems(intr_ddata_tab)) 586bff6be3eSSvatopluk Kraus return (NULL); 587bff6be3eSSvatopluk Kraus 588bff6be3eSSvatopluk Kraus ddata = intr_ddata_tab[irq]; 589bff6be3eSSvatopluk Kraus if (ddata->idd_isrc == NULL) { 590bff6be3eSSvatopluk Kraus error = intr_map_irq(ddata->idd_dev, ddata->idd_xref, 591cd642c88SSvatopluk Kraus ddata->idd_data, &irq); 592bff6be3eSSvatopluk Kraus if (error != 0) 593bff6be3eSSvatopluk Kraus return (NULL); 594bff6be3eSSvatopluk Kraus ddata->idd_isrc = isrc_lookup(irq); 595bff6be3eSSvatopluk Kraus } 596bff6be3eSSvatopluk Kraus if (datap != NULL) 597cd642c88SSvatopluk Kraus *datap = ddata->idd_data; 598bff6be3eSSvatopluk Kraus return (ddata->idd_isrc); 599bff6be3eSSvatopluk Kraus } 600bff6be3eSSvatopluk Kraus 601bff6be3eSSvatopluk Kraus #ifdef DEV_ACPI 602bff6be3eSSvatopluk Kraus /* 603bff6be3eSSvatopluk Kraus * Map interrupt source according to ACPI info into framework. If such mapping 6042b3ad188SAdrian Chadd * does not exist, create it. Return unique interrupt number (resource handle) 6052b3ad188SAdrian Chadd * associated with mapped interrupt source. 6062b3ad188SAdrian Chadd */ 6072b3ad188SAdrian Chadd u_int 608bff6be3eSSvatopluk Kraus intr_acpi_map_irq(device_t dev, u_int irq, enum intr_polarity pol, 609bff6be3eSSvatopluk Kraus enum intr_trigger trig) 6102b3ad188SAdrian Chadd { 611cd642c88SSvatopluk Kraus struct intr_map_data_acpi *daa; 612bff6be3eSSvatopluk Kraus struct intr_dev_data *ddata; 6132b3ad188SAdrian Chadd 614cd642c88SSvatopluk Kraus ddata = intr_ddata_alloc(sizeof(struct intr_map_data_acpi)); 615bff6be3eSSvatopluk Kraus if (ddata == NULL) 6168442087fSMichal Meloun return (INTR_IRQ_INVALID); /* no space left */ 6172b3ad188SAdrian Chadd 618bff6be3eSSvatopluk Kraus ddata->idd_dev = dev; 619cd642c88SSvatopluk Kraus ddata->idd_data->type = INTR_MAP_DATA_ACPI; 620cd642c88SSvatopluk Kraus 621cd642c88SSvatopluk Kraus daa = (struct intr_map_data_acpi *)ddata->idd_data; 622cd642c88SSvatopluk Kraus daa->irq = irq; 623cd642c88SSvatopluk Kraus daa->pol = pol; 624cd642c88SSvatopluk Kraus daa->trig = trig; 625cd642c88SSvatopluk Kraus 626bff6be3eSSvatopluk Kraus return (ddata->idd_irq); 6272b3ad188SAdrian Chadd } 628bff6be3eSSvatopluk Kraus #endif 6292b3ad188SAdrian Chadd #ifdef FDT 6302b3ad188SAdrian Chadd /* 6312b3ad188SAdrian Chadd * Map interrupt source according to FDT data into framework. If such mapping 6322b3ad188SAdrian Chadd * does not exist, create it. Return unique interrupt number (resource handle) 6332b3ad188SAdrian Chadd * associated with mapped interrupt source. 6342b3ad188SAdrian Chadd */ 6352b3ad188SAdrian Chadd u_int 6362b3ad188SAdrian Chadd intr_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells) 6372b3ad188SAdrian Chadd { 638cd642c88SSvatopluk Kraus size_t cellsize; 639bff6be3eSSvatopluk Kraus struct intr_dev_data *ddata; 640cd642c88SSvatopluk Kraus struct intr_map_data_fdt *daf; 6412b3ad188SAdrian Chadd 6422b3ad188SAdrian Chadd cellsize = ncells * sizeof(*cells); 643cd642c88SSvatopluk Kraus ddata = intr_ddata_alloc(sizeof(struct intr_map_data_fdt) + cellsize); 644bff6be3eSSvatopluk Kraus if (ddata == NULL) 6458442087fSMichal Meloun return (INTR_IRQ_INVALID); /* no space left */ 6462b3ad188SAdrian Chadd 647bff6be3eSSvatopluk Kraus ddata->idd_xref = (intptr_t)node; 648cd642c88SSvatopluk Kraus ddata->idd_data->type = INTR_MAP_DATA_FDT; 649cd642c88SSvatopluk Kraus 650cd642c88SSvatopluk Kraus daf = (struct intr_map_data_fdt *)ddata->idd_data; 651cd642c88SSvatopluk Kraus daf->ncells = ncells; 652cd642c88SSvatopluk Kraus memcpy(daf->cells, cells, cellsize); 653bff6be3eSSvatopluk Kraus return (ddata->idd_irq); 6542b3ad188SAdrian Chadd } 6552b3ad188SAdrian Chadd #endif 6562b3ad188SAdrian Chadd 65739f6c1bdSMichal Meloun /* 65839f6c1bdSMichal Meloun * Store GPIO interrupt decription in framework and return unique interrupt 65939f6c1bdSMichal Meloun * number (resource handle) associated with it. 66039f6c1bdSMichal Meloun */ 66139f6c1bdSMichal Meloun u_int 66239f6c1bdSMichal Meloun intr_gpio_map_irq(device_t dev, u_int pin_num, u_int pin_flags, u_int intr_mode) 66339f6c1bdSMichal Meloun { 66439f6c1bdSMichal Meloun struct intr_dev_data *ddata; 665cd642c88SSvatopluk Kraus struct intr_map_data_gpio *dag; 66639f6c1bdSMichal Meloun 667cd642c88SSvatopluk Kraus ddata = intr_ddata_alloc(sizeof(struct intr_map_data_gpio)); 66839f6c1bdSMichal Meloun if (ddata == NULL) 6698442087fSMichal Meloun return (INTR_IRQ_INVALID); /* no space left */ 67039f6c1bdSMichal Meloun 67139f6c1bdSMichal Meloun ddata->idd_dev = dev; 672cd642c88SSvatopluk Kraus ddata->idd_data->type = INTR_MAP_DATA_GPIO; 673cd642c88SSvatopluk Kraus 674cd642c88SSvatopluk Kraus dag = (struct intr_map_data_gpio *)ddata->idd_data; 675cd642c88SSvatopluk Kraus dag->gpio_pin_num = pin_num; 676cd642c88SSvatopluk Kraus dag->gpio_pin_flags = pin_flags; 677cd642c88SSvatopluk Kraus dag->gpio_intr_mode = intr_mode; 67839f6c1bdSMichal Meloun return (ddata->idd_irq); 67939f6c1bdSMichal Meloun } 68039f6c1bdSMichal Meloun 6812b3ad188SAdrian Chadd #ifdef INTR_SOLO 6822b3ad188SAdrian Chadd /* 6832b3ad188SAdrian Chadd * Setup filter into interrupt source. 6842b3ad188SAdrian Chadd */ 6852b3ad188SAdrian Chadd static int 6862b3ad188SAdrian Chadd iscr_setup_filter(struct intr_irqsrc *isrc, const char *name, 6872b3ad188SAdrian Chadd intr_irq_filter_t *filter, void *arg, void **cookiep) 6882b3ad188SAdrian Chadd { 6892b3ad188SAdrian Chadd 6902b3ad188SAdrian Chadd if (filter == NULL) 6912b3ad188SAdrian Chadd return (EINVAL); 6922b3ad188SAdrian Chadd 6932b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6942b3ad188SAdrian Chadd /* 6952b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 6962b3ad188SAdrian Chadd * how we handle interrupt sources. 6972b3ad188SAdrian Chadd */ 6982b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 6992b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7002b3ad188SAdrian Chadd return (EBUSY); 7012b3ad188SAdrian Chadd } 7022b3ad188SAdrian Chadd isrc->isrc_filter = filter; 7032b3ad188SAdrian Chadd isrc->isrc_arg = arg; 7042b3ad188SAdrian Chadd isrc_update_name(isrc, name); 7052b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7062b3ad188SAdrian Chadd 7072b3ad188SAdrian Chadd *cookiep = isrc; 7082b3ad188SAdrian Chadd return (0); 7092b3ad188SAdrian Chadd } 7102b3ad188SAdrian Chadd #endif 7112b3ad188SAdrian Chadd 7122b3ad188SAdrian Chadd /* 7132b3ad188SAdrian Chadd * Interrupt source pre_ithread method for MI interrupt framework. 7142b3ad188SAdrian Chadd */ 7152b3ad188SAdrian Chadd static void 7162b3ad188SAdrian Chadd intr_isrc_pre_ithread(void *arg) 7172b3ad188SAdrian Chadd { 7182b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 7192b3ad188SAdrian Chadd 7202b3ad188SAdrian Chadd PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); 7212b3ad188SAdrian Chadd } 7222b3ad188SAdrian Chadd 7232b3ad188SAdrian Chadd /* 7242b3ad188SAdrian Chadd * Interrupt source post_ithread method for MI interrupt framework. 7252b3ad188SAdrian Chadd */ 7262b3ad188SAdrian Chadd static void 7272b3ad188SAdrian Chadd intr_isrc_post_ithread(void *arg) 7282b3ad188SAdrian Chadd { 7292b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 7302b3ad188SAdrian Chadd 7312b3ad188SAdrian Chadd PIC_POST_ITHREAD(isrc->isrc_dev, isrc); 7322b3ad188SAdrian Chadd } 7332b3ad188SAdrian Chadd 7342b3ad188SAdrian Chadd /* 7352b3ad188SAdrian Chadd * Interrupt source post_filter method for MI interrupt framework. 7362b3ad188SAdrian Chadd */ 7372b3ad188SAdrian Chadd static void 7382b3ad188SAdrian Chadd intr_isrc_post_filter(void *arg) 7392b3ad188SAdrian Chadd { 7402b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 7412b3ad188SAdrian Chadd 7422b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 7432b3ad188SAdrian Chadd } 7442b3ad188SAdrian Chadd 7452b3ad188SAdrian Chadd /* 7462b3ad188SAdrian Chadd * Interrupt source assign_cpu method for MI interrupt framework. 7472b3ad188SAdrian Chadd */ 7482b3ad188SAdrian Chadd static int 7492b3ad188SAdrian Chadd intr_isrc_assign_cpu(void *arg, int cpu) 7502b3ad188SAdrian Chadd { 7512b3ad188SAdrian Chadd #ifdef SMP 7522b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 7532b3ad188SAdrian Chadd int error; 7542b3ad188SAdrian Chadd 7555b70c08cSSvatopluk Kraus if (isrc->isrc_dev != intr_irq_root_dev) 7562b3ad188SAdrian Chadd return (EINVAL); 7572b3ad188SAdrian Chadd 7582b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7592b3ad188SAdrian Chadd if (cpu == NOCPU) { 7602b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 7612b3ad188SAdrian Chadd isrc->isrc_flags &= ~INTR_ISRCF_BOUND; 7622b3ad188SAdrian Chadd } else { 7632b3ad188SAdrian Chadd CPU_SETOF(cpu, &isrc->isrc_cpu); 7642b3ad188SAdrian Chadd isrc->isrc_flags |= INTR_ISRCF_BOUND; 7652b3ad188SAdrian Chadd } 7662b3ad188SAdrian Chadd 7672b3ad188SAdrian Chadd /* 7682b3ad188SAdrian Chadd * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or 7692b3ad188SAdrian Chadd * re-balance it to another CPU or enable it on more CPUs. However, 7702b3ad188SAdrian Chadd * PIC is expected to change isrc_cpu appropriately to keep us well 771e3043798SPedro F. Giffuni * informed if the call is successful. 7722b3ad188SAdrian Chadd */ 7732b3ad188SAdrian Chadd if (irq_assign_cpu) { 774bff6be3eSSvatopluk Kraus error = PIC_BIND_INTR(isrc->isrc_dev, isrc); 7752b3ad188SAdrian Chadd if (error) { 7762b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 7772b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7782b3ad188SAdrian Chadd return (error); 7792b3ad188SAdrian Chadd } 7802b3ad188SAdrian Chadd } 7812b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7822b3ad188SAdrian Chadd return (0); 7832b3ad188SAdrian Chadd #else 7842b3ad188SAdrian Chadd return (EOPNOTSUPP); 7852b3ad188SAdrian Chadd #endif 7862b3ad188SAdrian Chadd } 7872b3ad188SAdrian Chadd 7882b3ad188SAdrian Chadd /* 7892b3ad188SAdrian Chadd * Create interrupt event for interrupt source. 7902b3ad188SAdrian Chadd */ 7912b3ad188SAdrian Chadd static int 7922b3ad188SAdrian Chadd isrc_event_create(struct intr_irqsrc *isrc) 7932b3ad188SAdrian Chadd { 7942b3ad188SAdrian Chadd struct intr_event *ie; 7952b3ad188SAdrian Chadd int error; 7962b3ad188SAdrian Chadd 7972b3ad188SAdrian Chadd error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq, 7982b3ad188SAdrian Chadd intr_isrc_pre_ithread, intr_isrc_post_ithread, intr_isrc_post_filter, 7992b3ad188SAdrian Chadd intr_isrc_assign_cpu, "%s:", isrc->isrc_name); 8002b3ad188SAdrian Chadd if (error) 8012b3ad188SAdrian Chadd return (error); 8022b3ad188SAdrian Chadd 8032b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 8042b3ad188SAdrian Chadd /* 8052b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 8062b3ad188SAdrian Chadd * how we handle interrupt sources. Let contested event wins. 8072b3ad188SAdrian Chadd */ 808169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 8092b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 810169e6abdSSvatopluk Kraus #else 811169e6abdSSvatopluk Kraus if (isrc->isrc_event != NULL) { 812169e6abdSSvatopluk Kraus #endif 8132b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 8142b3ad188SAdrian Chadd intr_event_destroy(ie); 8152b3ad188SAdrian Chadd return (isrc->isrc_event != NULL ? EBUSY : 0); 8162b3ad188SAdrian Chadd } 8172b3ad188SAdrian Chadd isrc->isrc_event = ie; 8182b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 8192b3ad188SAdrian Chadd 8202b3ad188SAdrian Chadd return (0); 8212b3ad188SAdrian Chadd } 8222b3ad188SAdrian Chadd #ifdef notyet 8232b3ad188SAdrian Chadd /* 8242b3ad188SAdrian Chadd * Destroy interrupt event for interrupt source. 8252b3ad188SAdrian Chadd */ 8262b3ad188SAdrian Chadd static void 8272b3ad188SAdrian Chadd isrc_event_destroy(struct intr_irqsrc *isrc) 8282b3ad188SAdrian Chadd { 8292b3ad188SAdrian Chadd struct intr_event *ie; 8302b3ad188SAdrian Chadd 8312b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 8322b3ad188SAdrian Chadd ie = isrc->isrc_event; 8332b3ad188SAdrian Chadd isrc->isrc_event = NULL; 8342b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 8352b3ad188SAdrian Chadd 8362b3ad188SAdrian Chadd if (ie != NULL) 8372b3ad188SAdrian Chadd intr_event_destroy(ie); 8382b3ad188SAdrian Chadd } 8392b3ad188SAdrian Chadd #endif 8402b3ad188SAdrian Chadd /* 8412b3ad188SAdrian Chadd * Add handler to interrupt source. 8422b3ad188SAdrian Chadd */ 8432b3ad188SAdrian Chadd static int 8442b3ad188SAdrian Chadd isrc_add_handler(struct intr_irqsrc *isrc, const char *name, 8452b3ad188SAdrian Chadd driver_filter_t filter, driver_intr_t handler, void *arg, 8462b3ad188SAdrian Chadd enum intr_type flags, void **cookiep) 8472b3ad188SAdrian Chadd { 8482b3ad188SAdrian Chadd int error; 8492b3ad188SAdrian Chadd 8502b3ad188SAdrian Chadd if (isrc->isrc_event == NULL) { 8512b3ad188SAdrian Chadd error = isrc_event_create(isrc); 8522b3ad188SAdrian Chadd if (error) 8532b3ad188SAdrian Chadd return (error); 8542b3ad188SAdrian Chadd } 8552b3ad188SAdrian Chadd 8562b3ad188SAdrian Chadd error = intr_event_add_handler(isrc->isrc_event, name, filter, handler, 8572b3ad188SAdrian Chadd arg, intr_priority(flags), flags, cookiep); 8582b3ad188SAdrian Chadd if (error == 0) { 8592b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 8602b3ad188SAdrian Chadd intrcnt_updatename(isrc); 8612b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 8622b3ad188SAdrian Chadd } 8632b3ad188SAdrian Chadd 8642b3ad188SAdrian Chadd return (error); 8652b3ad188SAdrian Chadd } 8662b3ad188SAdrian Chadd 8672b3ad188SAdrian Chadd /* 8682b3ad188SAdrian Chadd * Lookup interrupt controller locked. 8692b3ad188SAdrian Chadd */ 870bff6be3eSSvatopluk Kraus static inline struct intr_pic * 8712b3ad188SAdrian Chadd pic_lookup_locked(device_t dev, intptr_t xref) 8722b3ad188SAdrian Chadd { 8732b3ad188SAdrian Chadd struct intr_pic *pic; 8742b3ad188SAdrian Chadd 8752b3ad188SAdrian Chadd mtx_assert(&pic_list_lock, MA_OWNED); 8762b3ad188SAdrian Chadd 8774be58cbaSSvatopluk Kraus if (dev == NULL && xref == 0) 8784be58cbaSSvatopluk Kraus return (NULL); 8794be58cbaSSvatopluk Kraus 8804be58cbaSSvatopluk Kraus /* Note that pic->pic_dev is never NULL on registered PIC. */ 8812b3ad188SAdrian Chadd SLIST_FOREACH(pic, &pic_list, pic_next) { 8824be58cbaSSvatopluk Kraus if (dev == NULL) { 8834be58cbaSSvatopluk Kraus if (xref == pic->pic_xref) 8844be58cbaSSvatopluk Kraus return (pic); 8854be58cbaSSvatopluk Kraus } else if (xref == 0 || pic->pic_xref == 0) { 8864be58cbaSSvatopluk Kraus if (dev == pic->pic_dev) 8874be58cbaSSvatopluk Kraus return (pic); 8884be58cbaSSvatopluk Kraus } else if (xref == pic->pic_xref && dev == pic->pic_dev) 8892b3ad188SAdrian Chadd return (pic); 8902b3ad188SAdrian Chadd } 8912b3ad188SAdrian Chadd return (NULL); 8922b3ad188SAdrian Chadd } 8932b3ad188SAdrian Chadd 8942b3ad188SAdrian Chadd /* 8952b3ad188SAdrian Chadd * Lookup interrupt controller. 8962b3ad188SAdrian Chadd */ 8972b3ad188SAdrian Chadd static struct intr_pic * 8982b3ad188SAdrian Chadd pic_lookup(device_t dev, intptr_t xref) 8992b3ad188SAdrian Chadd { 9002b3ad188SAdrian Chadd struct intr_pic *pic; 9012b3ad188SAdrian Chadd 9022b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 9032b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 9042b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 9052b3ad188SAdrian Chadd return (pic); 9062b3ad188SAdrian Chadd } 9072b3ad188SAdrian Chadd 9082b3ad188SAdrian Chadd /* 9092b3ad188SAdrian Chadd * Create interrupt controller. 9102b3ad188SAdrian Chadd */ 9112b3ad188SAdrian Chadd static struct intr_pic * 9122b3ad188SAdrian Chadd pic_create(device_t dev, intptr_t xref) 9132b3ad188SAdrian Chadd { 9142b3ad188SAdrian Chadd struct intr_pic *pic; 9152b3ad188SAdrian Chadd 9162b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 9172b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 9182b3ad188SAdrian Chadd if (pic != NULL) { 9192b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 9202b3ad188SAdrian Chadd return (pic); 9212b3ad188SAdrian Chadd } 9222b3ad188SAdrian Chadd pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO); 923b48c6083SAndrew Turner if (pic == NULL) { 924b48c6083SAndrew Turner mtx_unlock(&pic_list_lock); 925b48c6083SAndrew Turner return (NULL); 926b48c6083SAndrew Turner } 9272b3ad188SAdrian Chadd pic->pic_xref = xref; 9282b3ad188SAdrian Chadd pic->pic_dev = dev; 929*d1605cdaSAndrew Turner mtx_init(&pic->pic_child_lock, "pic child lock", NULL, MTX_SPIN); 9302b3ad188SAdrian Chadd SLIST_INSERT_HEAD(&pic_list, pic, pic_next); 9312b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 9322b3ad188SAdrian Chadd 9332b3ad188SAdrian Chadd return (pic); 9342b3ad188SAdrian Chadd } 9352b3ad188SAdrian Chadd #ifdef notyet 9362b3ad188SAdrian Chadd /* 9372b3ad188SAdrian Chadd * Destroy interrupt controller. 9382b3ad188SAdrian Chadd */ 9392b3ad188SAdrian Chadd static void 9402b3ad188SAdrian Chadd pic_destroy(device_t dev, intptr_t xref) 9412b3ad188SAdrian Chadd { 9422b3ad188SAdrian Chadd struct intr_pic *pic; 9432b3ad188SAdrian Chadd 9442b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 9452b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 9462b3ad188SAdrian Chadd if (pic == NULL) { 9472b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 9482b3ad188SAdrian Chadd return; 9492b3ad188SAdrian Chadd } 9502b3ad188SAdrian Chadd SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next); 9512b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 9522b3ad188SAdrian Chadd 9532b3ad188SAdrian Chadd free(pic, M_INTRNG); 9542b3ad188SAdrian Chadd } 9552b3ad188SAdrian Chadd #endif 9562b3ad188SAdrian Chadd /* 9572b3ad188SAdrian Chadd * Register interrupt controller. 9582b3ad188SAdrian Chadd */ 9599346e913SAndrew Turner struct intr_pic * 9602b3ad188SAdrian Chadd intr_pic_register(device_t dev, intptr_t xref) 9612b3ad188SAdrian Chadd { 9622b3ad188SAdrian Chadd struct intr_pic *pic; 9632b3ad188SAdrian Chadd 9644be58cbaSSvatopluk Kraus if (dev == NULL) 9659346e913SAndrew Turner return (NULL); 9662b3ad188SAdrian Chadd pic = pic_create(dev, xref); 9672b3ad188SAdrian Chadd if (pic == NULL) 9689346e913SAndrew Turner return (NULL); 9692b3ad188SAdrian Chadd 9703fc155dcSAndrew Turner pic->pic_flags |= FLAG_PIC; 9713fc155dcSAndrew Turner 9724be58cbaSSvatopluk Kraus debugf("PIC %p registered for %s <dev %p, xref %x>\n", pic, 9734be58cbaSSvatopluk Kraus device_get_nameunit(dev), dev, xref); 9749346e913SAndrew Turner return (pic); 9752b3ad188SAdrian Chadd } 9762b3ad188SAdrian Chadd 9772b3ad188SAdrian Chadd /* 9782b3ad188SAdrian Chadd * Unregister interrupt controller. 9792b3ad188SAdrian Chadd */ 9802b3ad188SAdrian Chadd int 981bff6be3eSSvatopluk Kraus intr_pic_deregister(device_t dev, intptr_t xref) 9822b3ad188SAdrian Chadd { 9832b3ad188SAdrian Chadd 9842b3ad188SAdrian Chadd panic("%s: not implemented", __func__); 9852b3ad188SAdrian Chadd } 9862b3ad188SAdrian Chadd 9872b3ad188SAdrian Chadd /* 9882b3ad188SAdrian Chadd * Mark interrupt controller (itself) as a root one. 9892b3ad188SAdrian Chadd * 9902b3ad188SAdrian Chadd * Note that only an interrupt controller can really know its position 9912b3ad188SAdrian Chadd * in interrupt controller's tree. So root PIC must claim itself as a root. 9922b3ad188SAdrian Chadd * 9932b3ad188SAdrian Chadd * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, 9942b3ad188SAdrian Chadd * page 30: 9952b3ad188SAdrian Chadd * "The root of the interrupt tree is determined when traversal 9962b3ad188SAdrian Chadd * of the interrupt tree reaches an interrupt controller node without 9972b3ad188SAdrian Chadd * an interrupts property and thus no explicit interrupt parent." 9982b3ad188SAdrian Chadd */ 9992b3ad188SAdrian Chadd int 10002b3ad188SAdrian Chadd intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, 10012b3ad188SAdrian Chadd void *arg, u_int ipicount) 10022b3ad188SAdrian Chadd { 10033fc155dcSAndrew Turner struct intr_pic *pic; 10042b3ad188SAdrian Chadd 10053fc155dcSAndrew Turner pic = pic_lookup(dev, xref); 10063fc155dcSAndrew Turner if (pic == NULL) { 10072b3ad188SAdrian Chadd device_printf(dev, "not registered\n"); 10082b3ad188SAdrian Chadd return (EINVAL); 10092b3ad188SAdrian Chadd } 10103fc155dcSAndrew Turner 10113fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_PIC) != 0, 10123fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 10133fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 10143fc155dcSAndrew Turner 10152b3ad188SAdrian Chadd if (filter == NULL) { 10162b3ad188SAdrian Chadd device_printf(dev, "filter missing\n"); 10172b3ad188SAdrian Chadd return (EINVAL); 10182b3ad188SAdrian Chadd } 10192b3ad188SAdrian Chadd 10202b3ad188SAdrian Chadd /* 10212b3ad188SAdrian Chadd * Only one interrupt controllers could be on the root for now. 10222b3ad188SAdrian Chadd * Note that we further suppose that there is not threaded interrupt 10232b3ad188SAdrian Chadd * routine (handler) on the root. See intr_irq_handler(). 10242b3ad188SAdrian Chadd */ 10255b70c08cSSvatopluk Kraus if (intr_irq_root_dev != NULL) { 10262b3ad188SAdrian Chadd device_printf(dev, "another root already set\n"); 10272b3ad188SAdrian Chadd return (EBUSY); 10282b3ad188SAdrian Chadd } 10292b3ad188SAdrian Chadd 10305b70c08cSSvatopluk Kraus intr_irq_root_dev = dev; 10312b3ad188SAdrian Chadd irq_root_filter = filter; 10322b3ad188SAdrian Chadd irq_root_arg = arg; 10332b3ad188SAdrian Chadd irq_root_ipicount = ipicount; 10342b3ad188SAdrian Chadd 10352b3ad188SAdrian Chadd debugf("irq root set to %s\n", device_get_nameunit(dev)); 10362b3ad188SAdrian Chadd return (0); 10372b3ad188SAdrian Chadd } 10382b3ad188SAdrian Chadd 1039*d1605cdaSAndrew Turner /* 1040*d1605cdaSAndrew Turner * Add a handler to manage a sub range of a parents interrupts. 1041*d1605cdaSAndrew Turner */ 1042*d1605cdaSAndrew Turner struct intr_pic * 1043*d1605cdaSAndrew Turner intr_pic_add_handler(device_t parent, struct intr_pic *pic, 1044*d1605cdaSAndrew Turner intr_child_irq_filter_t *filter, void *arg, uintptr_t start, 1045*d1605cdaSAndrew Turner uintptr_t length) 1046*d1605cdaSAndrew Turner { 1047*d1605cdaSAndrew Turner struct intr_pic *parent_pic; 1048*d1605cdaSAndrew Turner struct intr_pic_child *newchild; 1049*d1605cdaSAndrew Turner #ifdef INVARIANTS 1050*d1605cdaSAndrew Turner struct intr_pic_child *child; 1051*d1605cdaSAndrew Turner #endif 1052*d1605cdaSAndrew Turner 1053*d1605cdaSAndrew Turner parent_pic = pic_lookup(parent, 0); 1054*d1605cdaSAndrew Turner if (parent_pic == NULL) 1055*d1605cdaSAndrew Turner return (NULL); 1056*d1605cdaSAndrew Turner 1057*d1605cdaSAndrew Turner newchild = malloc(sizeof(*newchild), M_INTRNG, M_WAITOK | M_ZERO); 1058*d1605cdaSAndrew Turner newchild->pc_pic = pic; 1059*d1605cdaSAndrew Turner newchild->pc_filter = filter; 1060*d1605cdaSAndrew Turner newchild->pc_filter_arg = arg; 1061*d1605cdaSAndrew Turner newchild->pc_start = start; 1062*d1605cdaSAndrew Turner newchild->pc_length = length; 1063*d1605cdaSAndrew Turner 1064*d1605cdaSAndrew Turner mtx_lock_spin(&parent_pic->pic_child_lock); 1065*d1605cdaSAndrew Turner #ifdef INVARIANTS 1066*d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent_pic->pic_children, pc_next) { 1067*d1605cdaSAndrew Turner KASSERT(child->pc_pic != pic, ("%s: Adding a child PIC twice", 1068*d1605cdaSAndrew Turner __func__)); 1069*d1605cdaSAndrew Turner } 1070*d1605cdaSAndrew Turner #endif 1071*d1605cdaSAndrew Turner SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next); 1072*d1605cdaSAndrew Turner mtx_unlock_spin(&parent_pic->pic_child_lock); 1073*d1605cdaSAndrew Turner 1074*d1605cdaSAndrew Turner return (pic); 1075*d1605cdaSAndrew Turner } 1076*d1605cdaSAndrew Turner 10772b3ad188SAdrian Chadd int 1078bff6be3eSSvatopluk Kraus intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data, 1079bff6be3eSSvatopluk Kraus u_int *irqp) 10802b3ad188SAdrian Chadd { 10812b3ad188SAdrian Chadd int error; 1082bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1083bff6be3eSSvatopluk Kraus struct intr_pic *pic; 1084bff6be3eSSvatopluk Kraus 1085bff6be3eSSvatopluk Kraus if (data == NULL) 1086bff6be3eSSvatopluk Kraus return (EINVAL); 1087bff6be3eSSvatopluk Kraus 1088bff6be3eSSvatopluk Kraus pic = pic_lookup(dev, xref); 108915adccc6SSvatopluk Kraus if (pic == NULL) 1090bff6be3eSSvatopluk Kraus return (ESRCH); 1091bff6be3eSSvatopluk Kraus 10923fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_PIC) != 0, 10933fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 10943fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 10953fc155dcSAndrew Turner 1096bff6be3eSSvatopluk Kraus error = PIC_MAP_INTR(pic->pic_dev, data, &isrc); 1097bff6be3eSSvatopluk Kraus if (error == 0) 1098bff6be3eSSvatopluk Kraus *irqp = isrc->isrc_irq; 1099bff6be3eSSvatopluk Kraus return (error); 1100bff6be3eSSvatopluk Kraus } 1101bff6be3eSSvatopluk Kraus 1102bff6be3eSSvatopluk Kraus int 1103bff6be3eSSvatopluk Kraus intr_alloc_irq(device_t dev, struct resource *res) 1104bff6be3eSSvatopluk Kraus { 1105bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1106bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1107bff6be3eSSvatopluk Kraus 1108bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1109bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1110bff6be3eSSvatopluk Kraus 1111bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), &data); 1112bff6be3eSSvatopluk Kraus if (isrc == NULL) 1113bff6be3eSSvatopluk Kraus return (EINVAL); 1114bff6be3eSSvatopluk Kraus 1115bff6be3eSSvatopluk Kraus return (PIC_ALLOC_INTR(isrc->isrc_dev, isrc, res, data)); 1116bff6be3eSSvatopluk Kraus } 1117bff6be3eSSvatopluk Kraus 1118bff6be3eSSvatopluk Kraus int 1119bff6be3eSSvatopluk Kraus intr_release_irq(device_t dev, struct resource *res) 1120bff6be3eSSvatopluk Kraus { 1121bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1122bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1123bff6be3eSSvatopluk Kraus 1124bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1125bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1126bff6be3eSSvatopluk Kraus 1127bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), &data); 1128bff6be3eSSvatopluk Kraus if (isrc == NULL) 1129bff6be3eSSvatopluk Kraus return (EINVAL); 1130bff6be3eSSvatopluk Kraus 1131bff6be3eSSvatopluk Kraus return (PIC_RELEASE_INTR(isrc->isrc_dev, isrc, res, data)); 1132bff6be3eSSvatopluk Kraus } 1133bff6be3eSSvatopluk Kraus 1134bff6be3eSSvatopluk Kraus int 1135bff6be3eSSvatopluk Kraus intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, 1136bff6be3eSSvatopluk Kraus driver_intr_t hand, void *arg, int flags, void **cookiep) 1137bff6be3eSSvatopluk Kraus { 1138bff6be3eSSvatopluk Kraus int error; 1139bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1140bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1141bff6be3eSSvatopluk Kraus const char *name; 1142bff6be3eSSvatopluk Kraus 1143bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1144bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1145bff6be3eSSvatopluk Kraus 1146bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), &data); 1147bff6be3eSSvatopluk Kraus if (isrc == NULL) 1148bff6be3eSSvatopluk Kraus return (EINVAL); 11492b3ad188SAdrian Chadd 11502b3ad188SAdrian Chadd name = device_get_nameunit(dev); 11512b3ad188SAdrian Chadd 11522b3ad188SAdrian Chadd #ifdef INTR_SOLO 11532b3ad188SAdrian Chadd /* 1154e3043798SPedro F. Giffuni * Standard handling is done through MI interrupt framework. However, 11552b3ad188SAdrian Chadd * some interrupts could request solely own special handling. This 11562b3ad188SAdrian Chadd * non standard handling can be used for interrupt controllers without 11572b3ad188SAdrian Chadd * handler (filter only), so in case that interrupt controllers are 11582b3ad188SAdrian Chadd * chained, MI interrupt framework is called only in leaf controller. 11592b3ad188SAdrian Chadd * 11602b3ad188SAdrian Chadd * Note that root interrupt controller routine is served as well, 11612b3ad188SAdrian Chadd * however in intr_irq_handler(), i.e. main system dispatch routine. 11622b3ad188SAdrian Chadd */ 11632b3ad188SAdrian Chadd if (flags & INTR_SOLO && hand != NULL) { 11642b3ad188SAdrian Chadd debugf("irq %u cannot solo on %s\n", irq, name); 11652b3ad188SAdrian Chadd return (EINVAL); 11662b3ad188SAdrian Chadd } 11672b3ad188SAdrian Chadd 11682b3ad188SAdrian Chadd if (flags & INTR_SOLO) { 11692b3ad188SAdrian Chadd error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, 11702b3ad188SAdrian Chadd arg, cookiep); 11712b3ad188SAdrian Chadd debugf("irq %u setup filter error %d on %s\n", irq, error, 11722b3ad188SAdrian Chadd name); 11732b3ad188SAdrian Chadd } else 11742b3ad188SAdrian Chadd #endif 11752b3ad188SAdrian Chadd { 11762b3ad188SAdrian Chadd error = isrc_add_handler(isrc, name, filt, hand, arg, flags, 11772b3ad188SAdrian Chadd cookiep); 11782b3ad188SAdrian Chadd debugf("irq %u add handler error %d on %s\n", irq, error, name); 11792b3ad188SAdrian Chadd } 11802b3ad188SAdrian Chadd if (error != 0) 11812b3ad188SAdrian Chadd return (error); 11822b3ad188SAdrian Chadd 11832b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1184bff6be3eSSvatopluk Kraus error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); 1185bff6be3eSSvatopluk Kraus if (error == 0) { 11862b3ad188SAdrian Chadd isrc->isrc_handlers++; 1187bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 1) 11882b3ad188SAdrian Chadd PIC_ENABLE_INTR(isrc->isrc_dev, isrc); 11892b3ad188SAdrian Chadd } 11902b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 1191bff6be3eSSvatopluk Kraus if (error != 0) 1192bff6be3eSSvatopluk Kraus intr_event_remove_handler(*cookiep); 1193bff6be3eSSvatopluk Kraus return (error); 11942b3ad188SAdrian Chadd } 11952b3ad188SAdrian Chadd 11962b3ad188SAdrian Chadd int 1197bff6be3eSSvatopluk Kraus intr_teardown_irq(device_t dev, struct resource *res, void *cookie) 11982b3ad188SAdrian Chadd { 11992b3ad188SAdrian Chadd int error; 1200bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1201bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 12022b3ad188SAdrian Chadd 1203bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1204bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1205bff6be3eSSvatopluk Kraus 1206bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), &data); 12072b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 12082b3ad188SAdrian Chadd return (EINVAL); 1209bff6be3eSSvatopluk Kraus 1210169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 12112b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 12122b3ad188SAdrian Chadd if (isrc != cookie) 12132b3ad188SAdrian Chadd return (EINVAL); 12142b3ad188SAdrian Chadd 12152b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12162b3ad188SAdrian Chadd isrc->isrc_filter = NULL; 12172b3ad188SAdrian Chadd isrc->isrc_arg = NULL; 12182b3ad188SAdrian Chadd isrc->isrc_handlers = 0; 12192b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1220bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 12212b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 12222b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12232b3ad188SAdrian Chadd return (0); 12242b3ad188SAdrian Chadd } 1225169e6abdSSvatopluk Kraus #endif 12262b3ad188SAdrian Chadd if (isrc != intr_handler_source(cookie)) 12272b3ad188SAdrian Chadd return (EINVAL); 12282b3ad188SAdrian Chadd 12292b3ad188SAdrian Chadd error = intr_event_remove_handler(cookie); 12302b3ad188SAdrian Chadd if (error == 0) { 12312b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12322b3ad188SAdrian Chadd isrc->isrc_handlers--; 1233bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 0) 12342b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1235bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 12362b3ad188SAdrian Chadd intrcnt_updatename(isrc); 12372b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12382b3ad188SAdrian Chadd } 12392b3ad188SAdrian Chadd return (error); 12402b3ad188SAdrian Chadd } 12412b3ad188SAdrian Chadd 12422b3ad188SAdrian Chadd int 1243bff6be3eSSvatopluk Kraus intr_describe_irq(device_t dev, struct resource *res, void *cookie, 1244bff6be3eSSvatopluk Kraus const char *descr) 12452b3ad188SAdrian Chadd { 12462b3ad188SAdrian Chadd int error; 1247bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 12482b3ad188SAdrian Chadd 1249bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1250bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1251bff6be3eSSvatopluk Kraus 1252bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), NULL); 12532b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 12542b3ad188SAdrian Chadd return (EINVAL); 1255169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 12562b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 12572b3ad188SAdrian Chadd if (isrc != cookie) 12582b3ad188SAdrian Chadd return (EINVAL); 12592b3ad188SAdrian Chadd 12602b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12612b3ad188SAdrian Chadd isrc_update_name(isrc, descr); 12622b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12632b3ad188SAdrian Chadd return (0); 12642b3ad188SAdrian Chadd } 1265169e6abdSSvatopluk Kraus #endif 12662b3ad188SAdrian Chadd error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); 12672b3ad188SAdrian Chadd if (error == 0) { 12682b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12692b3ad188SAdrian Chadd intrcnt_updatename(isrc); 12702b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12712b3ad188SAdrian Chadd } 12722b3ad188SAdrian Chadd return (error); 12732b3ad188SAdrian Chadd } 12742b3ad188SAdrian Chadd 12752b3ad188SAdrian Chadd #ifdef SMP 12762b3ad188SAdrian Chadd int 1277bff6be3eSSvatopluk Kraus intr_bind_irq(device_t dev, struct resource *res, int cpu) 12782b3ad188SAdrian Chadd { 12792b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 12802b3ad188SAdrian Chadd 1281bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1282bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1283bff6be3eSSvatopluk Kraus 1284bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), NULL); 12852b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 12862b3ad188SAdrian Chadd return (EINVAL); 1287169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 12882b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) 12892b3ad188SAdrian Chadd return (intr_isrc_assign_cpu(isrc, cpu)); 1290169e6abdSSvatopluk Kraus #endif 12912b3ad188SAdrian Chadd return (intr_event_bind(isrc->isrc_event, cpu)); 12922b3ad188SAdrian Chadd } 12932b3ad188SAdrian Chadd 12942b3ad188SAdrian Chadd /* 12952b3ad188SAdrian Chadd * Return the CPU that the next interrupt source should use. 12962b3ad188SAdrian Chadd * For now just returns the next CPU according to round-robin. 12972b3ad188SAdrian Chadd */ 12982b3ad188SAdrian Chadd u_int 12992b3ad188SAdrian Chadd intr_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask) 13002b3ad188SAdrian Chadd { 13012b3ad188SAdrian Chadd 13022b3ad188SAdrian Chadd if (!irq_assign_cpu || mp_ncpus == 1) 13032b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 13042b3ad188SAdrian Chadd 13052b3ad188SAdrian Chadd do { 13062b3ad188SAdrian Chadd last_cpu++; 13072b3ad188SAdrian Chadd if (last_cpu > mp_maxid) 13082b3ad188SAdrian Chadd last_cpu = 0; 13092b3ad188SAdrian Chadd } while (!CPU_ISSET(last_cpu, cpumask)); 13102b3ad188SAdrian Chadd return (last_cpu); 13112b3ad188SAdrian Chadd } 13122b3ad188SAdrian Chadd 13132b3ad188SAdrian Chadd /* 13142b3ad188SAdrian Chadd * Distribute all the interrupt sources among the available 13152b3ad188SAdrian Chadd * CPUs once the AP's have been launched. 13162b3ad188SAdrian Chadd */ 13172b3ad188SAdrian Chadd static void 13182b3ad188SAdrian Chadd intr_irq_shuffle(void *arg __unused) 13192b3ad188SAdrian Chadd { 13202b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 13212b3ad188SAdrian Chadd u_int i; 13222b3ad188SAdrian Chadd 13232b3ad188SAdrian Chadd if (mp_ncpus == 1) 13242b3ad188SAdrian Chadd return; 13252b3ad188SAdrian Chadd 13262b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 13272b3ad188SAdrian Chadd irq_assign_cpu = TRUE; 13282b3ad188SAdrian Chadd for (i = 0; i < NIRQ; i++) { 13292b3ad188SAdrian Chadd isrc = irq_sources[i]; 13302b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0 || 1331cf55df9fSSvatopluk Kraus isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) 13322b3ad188SAdrian Chadd continue; 13332b3ad188SAdrian Chadd 13342b3ad188SAdrian Chadd if (isrc->isrc_event != NULL && 13352b3ad188SAdrian Chadd isrc->isrc_flags & INTR_ISRCF_BOUND && 13362b3ad188SAdrian Chadd isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1) 13372b3ad188SAdrian Chadd panic("%s: CPU inconsistency", __func__); 13382b3ad188SAdrian Chadd 13392b3ad188SAdrian Chadd if ((isrc->isrc_flags & INTR_ISRCF_BOUND) == 0) 13402b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); /* start again */ 13412b3ad188SAdrian Chadd 13422b3ad188SAdrian Chadd /* 13432b3ad188SAdrian Chadd * We are in wicked position here if the following call fails 13442b3ad188SAdrian Chadd * for bound ISRC. The best thing we can do is to clear 13452b3ad188SAdrian Chadd * isrc_cpu so inconsistency with ie_cpu will be detectable. 13462b3ad188SAdrian Chadd */ 1347bff6be3eSSvatopluk Kraus if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0) 13482b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 13492b3ad188SAdrian Chadd } 13502b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 13512b3ad188SAdrian Chadd } 13522b3ad188SAdrian Chadd SYSINIT(intr_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, intr_irq_shuffle, NULL); 13532b3ad188SAdrian Chadd 13542b3ad188SAdrian Chadd #else 13552b3ad188SAdrian Chadd u_int 13562b3ad188SAdrian Chadd intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) 13572b3ad188SAdrian Chadd { 13582b3ad188SAdrian Chadd 13592b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 13602b3ad188SAdrian Chadd } 13612b3ad188SAdrian Chadd #endif 13622b3ad188SAdrian Chadd 13633fc155dcSAndrew Turner /* 13643fc155dcSAndrew Turner * Register a MSI/MSI-X interrupt controller 13653fc155dcSAndrew Turner */ 13663fc155dcSAndrew Turner int 13673fc155dcSAndrew Turner intr_msi_register(device_t dev, intptr_t xref) 13683fc155dcSAndrew Turner { 13693fc155dcSAndrew Turner struct intr_pic *pic; 13703fc155dcSAndrew Turner 13713fc155dcSAndrew Turner if (dev == NULL) 13723fc155dcSAndrew Turner return (EINVAL); 13733fc155dcSAndrew Turner pic = pic_create(dev, xref); 13743fc155dcSAndrew Turner if (pic == NULL) 13753fc155dcSAndrew Turner return (ENOMEM); 13763fc155dcSAndrew Turner 13773fc155dcSAndrew Turner pic->pic_flags |= FLAG_MSI; 13783fc155dcSAndrew Turner 13793fc155dcSAndrew Turner debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic, 13803fc155dcSAndrew Turner device_get_nameunit(dev), dev, (uintmax_t)xref); 13813fc155dcSAndrew Turner return (0); 13823fc155dcSAndrew Turner } 13833fc155dcSAndrew Turner 13843fc155dcSAndrew Turner int 13853fc155dcSAndrew Turner intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count, 13863fc155dcSAndrew Turner int maxcount, int *irqs) 13873fc155dcSAndrew Turner { 13883fc155dcSAndrew Turner struct intr_irqsrc **isrc; 13893fc155dcSAndrew Turner struct intr_pic *pic; 13903fc155dcSAndrew Turner device_t pdev; 13913fc155dcSAndrew Turner int err, i; 13923fc155dcSAndrew Turner 13933fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 13943fc155dcSAndrew Turner if (pic == NULL) 13953fc155dcSAndrew Turner return (ESRCH); 13963fc155dcSAndrew Turner 13973fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 13983fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 13993fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14003fc155dcSAndrew Turner 14013fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 14023fc155dcSAndrew Turner err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc); 14033fc155dcSAndrew Turner if (err == 0) { 14043fc155dcSAndrew Turner for (i = 0; i < count; i++) { 14053fc155dcSAndrew Turner irqs[i] = isrc[i]->isrc_irq; 14063fc155dcSAndrew Turner } 14073fc155dcSAndrew Turner } 14083fc155dcSAndrew Turner 14093fc155dcSAndrew Turner free(isrc, M_INTRNG); 14103fc155dcSAndrew Turner 14113fc155dcSAndrew Turner return (err); 14123fc155dcSAndrew Turner } 14133fc155dcSAndrew Turner 14143fc155dcSAndrew Turner int 14153fc155dcSAndrew Turner intr_release_msi(device_t pci, device_t child, intptr_t xref, int count, 14163fc155dcSAndrew Turner int *irqs) 14173fc155dcSAndrew Turner { 14183fc155dcSAndrew Turner struct intr_irqsrc **isrc; 14193fc155dcSAndrew Turner struct intr_pic *pic; 14203fc155dcSAndrew Turner int i, err; 14213fc155dcSAndrew Turner 14223fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 14233fc155dcSAndrew Turner if (pic == NULL) 14243fc155dcSAndrew Turner return (ESRCH); 14253fc155dcSAndrew Turner 14263fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 14273fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14283fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14293fc155dcSAndrew Turner 14303fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 14313fc155dcSAndrew Turner 14323fc155dcSAndrew Turner for (i = 0; i < count; i++) { 14333fc155dcSAndrew Turner isrc[i] = isrc_lookup(irqs[i]); 14343fc155dcSAndrew Turner if (isrc == NULL) { 14353fc155dcSAndrew Turner free(isrc, M_INTRNG); 14363fc155dcSAndrew Turner return (EINVAL); 14373fc155dcSAndrew Turner } 14383fc155dcSAndrew Turner } 14393fc155dcSAndrew Turner 14403fc155dcSAndrew Turner err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc); 14413fc155dcSAndrew Turner free(isrc, M_INTRNG); 14423fc155dcSAndrew Turner return (err); 14433fc155dcSAndrew Turner } 14443fc155dcSAndrew Turner 14453fc155dcSAndrew Turner int 14463fc155dcSAndrew Turner intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq) 14473fc155dcSAndrew Turner { 14483fc155dcSAndrew Turner struct intr_irqsrc *isrc; 14493fc155dcSAndrew Turner struct intr_pic *pic; 14503fc155dcSAndrew Turner device_t pdev; 14513fc155dcSAndrew Turner int err; 14523fc155dcSAndrew Turner 14533fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 14543fc155dcSAndrew Turner if (pic == NULL) 14553fc155dcSAndrew Turner return (ESRCH); 14563fc155dcSAndrew Turner 14573fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 14583fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14593fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14603fc155dcSAndrew Turner 14613fc155dcSAndrew Turner err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc); 14623fc155dcSAndrew Turner if (err != 0) 14633fc155dcSAndrew Turner return (err); 14643fc155dcSAndrew Turner 14653fc155dcSAndrew Turner *irq = isrc->isrc_irq; 14663fc155dcSAndrew Turner return (0); 14673fc155dcSAndrew Turner } 14683fc155dcSAndrew Turner 14693fc155dcSAndrew Turner int 14703fc155dcSAndrew Turner intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq) 14713fc155dcSAndrew Turner { 14723fc155dcSAndrew Turner struct intr_irqsrc *isrc; 14733fc155dcSAndrew Turner struct intr_pic *pic; 14743fc155dcSAndrew Turner int err; 14753fc155dcSAndrew Turner 14763fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 14773fc155dcSAndrew Turner if (pic == NULL) 14783fc155dcSAndrew Turner return (ESRCH); 14793fc155dcSAndrew Turner 14803fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 14813fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14823fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14833fc155dcSAndrew Turner 14843fc155dcSAndrew Turner isrc = isrc_lookup(irq); 14853fc155dcSAndrew Turner if (isrc == NULL) 14863fc155dcSAndrew Turner return (EINVAL); 14873fc155dcSAndrew Turner 14883fc155dcSAndrew Turner err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc); 14893fc155dcSAndrew Turner return (err); 14903fc155dcSAndrew Turner } 14913fc155dcSAndrew Turner 14923fc155dcSAndrew Turner int 14933fc155dcSAndrew Turner intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq, 14943fc155dcSAndrew Turner uint64_t *addr, uint32_t *data) 14953fc155dcSAndrew Turner { 14963fc155dcSAndrew Turner struct intr_irqsrc *isrc; 14973fc155dcSAndrew Turner struct intr_pic *pic; 14983fc155dcSAndrew Turner int err; 14993fc155dcSAndrew Turner 15003fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 15013fc155dcSAndrew Turner if (pic == NULL) 15023fc155dcSAndrew Turner return (ESRCH); 15033fc155dcSAndrew Turner 15043fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 15053fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 15063fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 15073fc155dcSAndrew Turner 15083fc155dcSAndrew Turner isrc = isrc_lookup(irq); 15093fc155dcSAndrew Turner if (isrc == NULL) 15103fc155dcSAndrew Turner return (EINVAL); 15113fc155dcSAndrew Turner 15123fc155dcSAndrew Turner err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data); 15133fc155dcSAndrew Turner return (err); 15143fc155dcSAndrew Turner } 15153fc155dcSAndrew Turner 15163fc155dcSAndrew Turner 15172b3ad188SAdrian Chadd void dosoftints(void); 15182b3ad188SAdrian Chadd void 15192b3ad188SAdrian Chadd dosoftints(void) 15202b3ad188SAdrian Chadd { 15212b3ad188SAdrian Chadd } 15222b3ad188SAdrian Chadd 15232b3ad188SAdrian Chadd #ifdef SMP 15242b3ad188SAdrian Chadd /* 15252b3ad188SAdrian Chadd * Init interrupt controller on another CPU. 15262b3ad188SAdrian Chadd */ 15272b3ad188SAdrian Chadd void 15282b3ad188SAdrian Chadd intr_pic_init_secondary(void) 15292b3ad188SAdrian Chadd { 15302b3ad188SAdrian Chadd 15312b3ad188SAdrian Chadd /* 15322b3ad188SAdrian Chadd * QQQ: Only root PIC is aware of other CPUs ??? 15332b3ad188SAdrian Chadd */ 15345b70c08cSSvatopluk Kraus KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); 15352b3ad188SAdrian Chadd 15362b3ad188SAdrian Chadd //mtx_lock(&isrc_table_lock); 15375b70c08cSSvatopluk Kraus PIC_INIT_SECONDARY(intr_irq_root_dev); 15382b3ad188SAdrian Chadd //mtx_unlock(&isrc_table_lock); 15392b3ad188SAdrian Chadd } 15402b3ad188SAdrian Chadd #endif 15412b3ad188SAdrian Chadd 15422b3ad188SAdrian Chadd #ifdef DDB 15432b3ad188SAdrian Chadd DB_SHOW_COMMAND(irqs, db_show_irqs) 15442b3ad188SAdrian Chadd { 15452b3ad188SAdrian Chadd u_int i, irqsum; 1546bff6be3eSSvatopluk Kraus u_long num; 15472b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 15482b3ad188SAdrian Chadd 15492b3ad188SAdrian Chadd for (irqsum = 0, i = 0; i < NIRQ; i++) { 15502b3ad188SAdrian Chadd isrc = irq_sources[i]; 15512b3ad188SAdrian Chadd if (isrc == NULL) 15522b3ad188SAdrian Chadd continue; 15532b3ad188SAdrian Chadd 1554bff6be3eSSvatopluk Kraus num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; 15552b3ad188SAdrian Chadd db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, 15562b3ad188SAdrian Chadd isrc->isrc_name, isrc->isrc_cpu.__bits[0], 1557bff6be3eSSvatopluk Kraus isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); 1558bff6be3eSSvatopluk Kraus irqsum += num; 15592b3ad188SAdrian Chadd } 15602b3ad188SAdrian Chadd db_printf("irq total %u\n", irqsum); 15612b3ad188SAdrian Chadd } 15622b3ad188SAdrian Chadd #endif 1563