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 /* 302b3ad188SAdrian Chadd * New-style Interrupt Framework 312b3ad188SAdrian Chadd * 32895c8b1cSMichal Meloun * TODO: - add support for disconnected PICs. 33895c8b1cSMichal Meloun * - to support IPI (PPI) enabling on other CPUs if already started. 34895c8b1cSMichal Meloun * - to complete things for removable PICs. 352b3ad188SAdrian Chadd */ 362b3ad188SAdrian Chadd 372b3ad188SAdrian Chadd #include "opt_ddb.h" 38df7a2251SAndrew Turner #include "opt_hwpmc_hooks.h" 39e707c8beSRuslan Bukin #include "opt_iommu.h" 402b3ad188SAdrian Chadd 412b3ad188SAdrian Chadd #include <sys/param.h> 422b3ad188SAdrian Chadd #include <sys/systm.h> 4389c52f9dSKyle Evans #include <sys/asan.h> 4428137bdbSMitchell Horne #include <sys/bitstring.h> 452b3ad188SAdrian Chadd #include <sys/bus.h> 462b3ad188SAdrian Chadd #include <sys/conf.h> 472b3ad188SAdrian Chadd #include <sys/cpuset.h> 4882e846dfSMitchell Horne #include <sys/interrupt.h> 4982e846dfSMitchell Horne #include <sys/kernel.h> 5082e846dfSMitchell Horne #include <sys/lock.h> 5182e846dfSMitchell Horne #include <sys/malloc.h> 5282e846dfSMitchell Horne #include <sys/mutex.h> 5382e846dfSMitchell Horne #include <sys/proc.h> 5482e846dfSMitchell Horne #include <sys/queue.h> 556b42a1f4SAndrew Turner #include <sys/rman.h> 562b3ad188SAdrian Chadd #include <sys/sched.h> 572b3ad188SAdrian Chadd #include <sys/smp.h> 58248f0cabSOleksandr Tymoshenko #include <sys/sysctl.h> 5982e846dfSMitchell Horne #include <sys/syslog.h> 6082e846dfSMitchell Horne #include <sys/taskqueue.h> 6182e846dfSMitchell Horne #include <sys/tree.h> 629ed01c32SGleb Smirnoff #include <sys/vmmeter.h> 63df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 64df7a2251SAndrew Turner #include <sys/pmckern.h> 65df7a2251SAndrew Turner #endif 66df7a2251SAndrew Turner 672b3ad188SAdrian Chadd #include <machine/atomic.h> 682b3ad188SAdrian Chadd #include <machine/cpu.h> 6982e846dfSMitchell Horne #include <machine/intr.h> 702b3ad188SAdrian Chadd #include <machine/smp.h> 712b3ad188SAdrian Chadd #include <machine/stdarg.h> 722b3ad188SAdrian Chadd 732b3ad188SAdrian Chadd #ifdef DDB 742b3ad188SAdrian Chadd #include <ddb/ddb.h> 752b3ad188SAdrian Chadd #endif 762b3ad188SAdrian Chadd 77e707c8beSRuslan Bukin #ifdef IOMMU 78e707c8beSRuslan Bukin #include <dev/iommu/iommu_msi.h> 79e707c8beSRuslan Bukin #endif 80e707c8beSRuslan Bukin 812b3ad188SAdrian Chadd #include "pic_if.h" 823fc155dcSAndrew Turner #include "msi_if.h" 832b3ad188SAdrian Chadd 842b3ad188SAdrian Chadd #define INTRNAME_LEN (2*MAXCOMLEN + 1) 852b3ad188SAdrian Chadd 862b3ad188SAdrian Chadd #ifdef DEBUG 872b3ad188SAdrian Chadd #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 882b3ad188SAdrian Chadd printf(fmt,##args); } while (0) 892b3ad188SAdrian Chadd #else 902b3ad188SAdrian Chadd #define debugf(fmt, args...) 912b3ad188SAdrian Chadd #endif 922b3ad188SAdrian Chadd 932b3ad188SAdrian Chadd MALLOC_DECLARE(M_INTRNG); 942b3ad188SAdrian Chadd MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); 952b3ad188SAdrian Chadd 962b3ad188SAdrian Chadd /* Main interrupt handler called from assembler -> 'hidden' for C code. */ 972b3ad188SAdrian Chadd void intr_irq_handler(struct trapframe *tf); 982b3ad188SAdrian Chadd 992b3ad188SAdrian Chadd /* Root interrupt controller stuff. */ 1005b70c08cSSvatopluk Kraus device_t intr_irq_root_dev; 1012b3ad188SAdrian Chadd static intr_irq_filter_t *irq_root_filter; 1022b3ad188SAdrian Chadd static void *irq_root_arg; 1032b3ad188SAdrian Chadd static u_int irq_root_ipicount; 1042b3ad188SAdrian Chadd 105d1605cdaSAndrew Turner struct intr_pic_child { 106d1605cdaSAndrew Turner SLIST_ENTRY(intr_pic_child) pc_next; 107d1605cdaSAndrew Turner struct intr_pic *pc_pic; 108d1605cdaSAndrew Turner intr_child_irq_filter_t *pc_filter; 109d1605cdaSAndrew Turner void *pc_filter_arg; 110d1605cdaSAndrew Turner uintptr_t pc_start; 111d1605cdaSAndrew Turner uintptr_t pc_length; 112d1605cdaSAndrew Turner }; 113d1605cdaSAndrew Turner 1142b3ad188SAdrian Chadd /* Interrupt controller definition. */ 1152b3ad188SAdrian Chadd struct intr_pic { 1162b3ad188SAdrian Chadd SLIST_ENTRY(intr_pic) pic_next; 1172b3ad188SAdrian Chadd intptr_t pic_xref; /* hardware identification */ 1182b3ad188SAdrian Chadd device_t pic_dev; 119c0d52370SAndrew Turner /* Only one of FLAG_PIC or FLAG_MSI may be set */ 1203fc155dcSAndrew Turner #define FLAG_PIC (1 << 0) 1213fc155dcSAndrew Turner #define FLAG_MSI (1 << 1) 122c0d52370SAndrew Turner #define FLAG_TYPE_MASK (FLAG_PIC | FLAG_MSI) 1233fc155dcSAndrew Turner u_int pic_flags; 124d1605cdaSAndrew Turner struct mtx pic_child_lock; 125d1605cdaSAndrew Turner SLIST_HEAD(, intr_pic_child) pic_children; 1262b3ad188SAdrian Chadd }; 1272b3ad188SAdrian Chadd 1282b3ad188SAdrian Chadd static struct mtx pic_list_lock; 1292b3ad188SAdrian Chadd static SLIST_HEAD(, intr_pic) pic_list; 1302b3ad188SAdrian Chadd 131c0d52370SAndrew Turner static struct intr_pic *pic_lookup(device_t dev, intptr_t xref, int flags); 1322b3ad188SAdrian Chadd 1332b3ad188SAdrian Chadd /* Interrupt source definition. */ 1342b3ad188SAdrian Chadd static struct mtx isrc_table_lock; 135248f0cabSOleksandr Tymoshenko static struct intr_irqsrc **irq_sources; 1361e0ba9d4SAndrew Turner static u_int irq_next_free; 1372b3ad188SAdrian Chadd 1382b3ad188SAdrian Chadd #ifdef SMP 139dc425090SMitchell Horne #ifdef EARLY_AP_STARTUP 140dc425090SMitchell Horne static bool irq_assign_cpu = true; 141dc425090SMitchell Horne #else 142dc425090SMitchell Horne static bool irq_assign_cpu = false; 143dc425090SMitchell Horne #endif 1442b3ad188SAdrian Chadd #endif 1452b3ad188SAdrian Chadd 146a3c7da3dSElliott Mitchell u_int intr_nirq = NIRQ; 147248f0cabSOleksandr Tymoshenko SYSCTL_UINT(_machdep, OID_AUTO, nirq, CTLFLAG_RDTUN, &intr_nirq, 0, 148248f0cabSOleksandr Tymoshenko "Number of IRQs"); 1492b3ad188SAdrian Chadd 1502b3ad188SAdrian Chadd /* Data for MI statistics reporting. */ 151248f0cabSOleksandr Tymoshenko u_long *intrcnt; 152248f0cabSOleksandr Tymoshenko char *intrnames; 153248f0cabSOleksandr Tymoshenko size_t sintrcnt; 154248f0cabSOleksandr Tymoshenko size_t sintrnames; 15528137bdbSMitchell Horne int nintrcnt; 15628137bdbSMitchell Horne static bitstr_t *intrcnt_bitmap; 1572b3ad188SAdrian Chadd 158895c8b1cSMichal Meloun static struct intr_irqsrc *intr_map_get_isrc(u_int res_id); 159895c8b1cSMichal Meloun static void intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc); 160609b0fe9SOleksandr Tymoshenko static struct intr_map_data * intr_map_get_map_data(u_int res_id); 161895c8b1cSMichal Meloun static void intr_map_copy_map_data(u_int res_id, device_t *dev, intptr_t *xref, 162895c8b1cSMichal Meloun struct intr_map_data **data); 163895c8b1cSMichal Meloun 1642b3ad188SAdrian Chadd /* 1652b3ad188SAdrian Chadd * Interrupt framework initialization routine. 1662b3ad188SAdrian Chadd */ 1672b3ad188SAdrian Chadd static void 1682b3ad188SAdrian Chadd intr_irq_init(void *dummy __unused) 1692b3ad188SAdrian Chadd { 1702b3ad188SAdrian Chadd 1712b3ad188SAdrian Chadd SLIST_INIT(&pic_list); 1722b3ad188SAdrian Chadd mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); 1733fc155dcSAndrew Turner 1742b3ad188SAdrian Chadd mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); 175248f0cabSOleksandr Tymoshenko 176248f0cabSOleksandr Tymoshenko /* 177248f0cabSOleksandr Tymoshenko * - 2 counters for each I/O interrupt. 178*2f0b059eSElliott Mitchell * - mp_maxid + 1 counters for each IPI counters for SMP. 179248f0cabSOleksandr Tymoshenko */ 18028137bdbSMitchell Horne nintrcnt = intr_nirq * 2; 181248f0cabSOleksandr Tymoshenko #ifdef SMP 182*2f0b059eSElliott Mitchell nintrcnt += INTR_IPI_COUNT * (mp_maxid + 1); 183248f0cabSOleksandr Tymoshenko #endif 184248f0cabSOleksandr Tymoshenko 18528137bdbSMitchell Horne intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTRNG, 186248f0cabSOleksandr Tymoshenko M_WAITOK | M_ZERO); 18728137bdbSMitchell Horne intrnames = mallocarray(nintrcnt, INTRNAME_LEN, M_INTRNG, 188248f0cabSOleksandr Tymoshenko M_WAITOK | M_ZERO); 18928137bdbSMitchell Horne sintrcnt = nintrcnt * sizeof(u_long); 19028137bdbSMitchell Horne sintrnames = nintrcnt * INTRNAME_LEN; 19128137bdbSMitchell Horne 19228137bdbSMitchell Horne /* Allocate the bitmap tracking counter allocations. */ 19328137bdbSMitchell Horne intrcnt_bitmap = bit_alloc(nintrcnt, M_INTRNG, M_WAITOK | M_ZERO); 19428137bdbSMitchell Horne 195248f0cabSOleksandr Tymoshenko irq_sources = mallocarray(intr_nirq, sizeof(struct intr_irqsrc*), 196248f0cabSOleksandr Tymoshenko M_INTRNG, M_WAITOK | M_ZERO); 1972b3ad188SAdrian Chadd } 1982b3ad188SAdrian Chadd SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); 1992b3ad188SAdrian Chadd 2002b3ad188SAdrian Chadd static void 2012b3ad188SAdrian Chadd intrcnt_setname(const char *name, int index) 2022b3ad188SAdrian Chadd { 2032b3ad188SAdrian Chadd 2042b3ad188SAdrian Chadd snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s", 2052b3ad188SAdrian Chadd INTRNAME_LEN - 1, name); 2062b3ad188SAdrian Chadd } 2072b3ad188SAdrian Chadd 2082b3ad188SAdrian Chadd /* 2092b3ad188SAdrian Chadd * Update name for interrupt source with interrupt event. 2102b3ad188SAdrian Chadd */ 2112b3ad188SAdrian Chadd static void 2122b3ad188SAdrian Chadd intrcnt_updatename(struct intr_irqsrc *isrc) 2132b3ad188SAdrian Chadd { 2142b3ad188SAdrian Chadd 2152b3ad188SAdrian Chadd /* QQQ: What about stray counter name? */ 2162b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2172b3ad188SAdrian Chadd intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index); 2182b3ad188SAdrian Chadd } 2192b3ad188SAdrian Chadd 2202b3ad188SAdrian Chadd /* 2212b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counter increment. 2222b3ad188SAdrian Chadd */ 2232b3ad188SAdrian Chadd static inline void 2242b3ad188SAdrian Chadd isrc_increment_count(struct intr_irqsrc *isrc) 2252b3ad188SAdrian Chadd { 2262b3ad188SAdrian Chadd 227bff6be3eSSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_PPI) 228bff6be3eSSvatopluk Kraus atomic_add_long(&isrc->isrc_count[0], 1); 229bff6be3eSSvatopluk Kraus else 2302b3ad188SAdrian Chadd isrc->isrc_count[0]++; 2312b3ad188SAdrian Chadd } 2322b3ad188SAdrian Chadd 2332b3ad188SAdrian Chadd /* 2342b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt stray counter increment. 2352b3ad188SAdrian Chadd */ 2362b3ad188SAdrian Chadd static inline void 2372b3ad188SAdrian Chadd isrc_increment_straycount(struct intr_irqsrc *isrc) 2382b3ad188SAdrian Chadd { 2392b3ad188SAdrian Chadd 2402b3ad188SAdrian Chadd isrc->isrc_count[1]++; 2412b3ad188SAdrian Chadd } 2422b3ad188SAdrian Chadd 2432b3ad188SAdrian Chadd /* 2442b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt name update. 2452b3ad188SAdrian Chadd */ 2462b3ad188SAdrian Chadd static void 2472b3ad188SAdrian Chadd isrc_update_name(struct intr_irqsrc *isrc, const char *name) 2482b3ad188SAdrian Chadd { 2492b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2502b3ad188SAdrian Chadd 2512b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2522b3ad188SAdrian Chadd 2532b3ad188SAdrian Chadd if (name != NULL) { 2542b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); 2552b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2562b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, 2572b3ad188SAdrian Chadd name); 2582b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2592b3ad188SAdrian Chadd } else { 2602b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); 2612b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2622b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); 2632b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2642b3ad188SAdrian Chadd } 2652b3ad188SAdrian Chadd } 2662b3ad188SAdrian Chadd 2672b3ad188SAdrian Chadd /* 2682b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counters setup. 2692b3ad188SAdrian Chadd */ 2702b3ad188SAdrian Chadd static void 2712b3ad188SAdrian Chadd isrc_setup_counters(struct intr_irqsrc *isrc) 2722b3ad188SAdrian Chadd { 27328137bdbSMitchell Horne int index; 27428137bdbSMitchell Horne 27528137bdbSMitchell Horne mtx_assert(&isrc_table_lock, MA_OWNED); 2762b3ad188SAdrian Chadd 2772b3ad188SAdrian Chadd /* 27828137bdbSMitchell Horne * Allocate two counter values, the second tracking "stray" interrupts. 2792b3ad188SAdrian Chadd */ 28028137bdbSMitchell Horne bit_ffc_area(intrcnt_bitmap, nintrcnt, 2, &index); 28128137bdbSMitchell Horne if (index == -1) 28228137bdbSMitchell Horne panic("Failed to allocate 2 counters. Array exhausted?"); 28328137bdbSMitchell Horne bit_nset(intrcnt_bitmap, index, index + 1); 2842b3ad188SAdrian Chadd isrc->isrc_index = index; 2852b3ad188SAdrian Chadd isrc->isrc_count = &intrcnt[index]; 2862b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 2872b3ad188SAdrian Chadd } 2882b3ad188SAdrian Chadd 289bff6be3eSSvatopluk Kraus /* 290bff6be3eSSvatopluk Kraus * Virtualization for interrupt source interrupt counters release. 291bff6be3eSSvatopluk Kraus */ 292bff6be3eSSvatopluk Kraus static void 293bff6be3eSSvatopluk Kraus isrc_release_counters(struct intr_irqsrc *isrc) 294bff6be3eSSvatopluk Kraus { 29528137bdbSMitchell Horne int idx = isrc->isrc_index; 296bff6be3eSSvatopluk Kraus 29728137bdbSMitchell Horne mtx_assert(&isrc_table_lock, MA_OWNED); 29828137bdbSMitchell Horne 29928137bdbSMitchell Horne bit_nclear(intrcnt_bitmap, idx, idx + 1); 300bff6be3eSSvatopluk Kraus } 301bff6be3eSSvatopluk Kraus 3022b3ad188SAdrian Chadd #ifdef SMP 3032b3ad188SAdrian Chadd /* 3042b3ad188SAdrian Chadd * Virtualization for interrupt source IPI counters setup. 3052b3ad188SAdrian Chadd */ 3065b70c08cSSvatopluk Kraus u_long * 3075b70c08cSSvatopluk Kraus intr_ipi_setup_counters(const char *name) 3082b3ad188SAdrian Chadd { 3092b3ad188SAdrian Chadd u_int index, i; 3102b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 3112b3ad188SAdrian Chadd 31228137bdbSMitchell Horne mtx_lock(&isrc_table_lock); 31328137bdbSMitchell Horne 31428137bdbSMitchell Horne /* 315*2f0b059eSElliott Mitchell * We should never have a problem finding mp_maxid + 1 contiguous 316*2f0b059eSElliott Mitchell * counters, in practice. Interrupts will be allocated sequentially 317*2f0b059eSElliott Mitchell * during boot, so the array should fill from low to high index. Once 318*2f0b059eSElliott Mitchell * reserved, the IPI counters will never be released. Similarly, we 319*2f0b059eSElliott Mitchell * will not need to allocate more IPIs once the system is running. 32028137bdbSMitchell Horne */ 321*2f0b059eSElliott Mitchell bit_ffc_area(intrcnt_bitmap, nintrcnt, mp_maxid + 1, &index); 32228137bdbSMitchell Horne if (index == -1) 32328137bdbSMitchell Horne panic("Failed to allocate %d counters. Array exhausted?", 324*2f0b059eSElliott Mitchell mp_maxid + 1); 325*2f0b059eSElliott Mitchell bit_nset(intrcnt_bitmap, index, index + mp_maxid); 326*2f0b059eSElliott Mitchell for (i = 0; i < mp_maxid + 1; i++) { 3272b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); 3282b3ad188SAdrian Chadd intrcnt_setname(str, index + i); 3292b3ad188SAdrian Chadd } 33028137bdbSMitchell Horne mtx_unlock(&isrc_table_lock); 3315b70c08cSSvatopluk Kraus return (&intrcnt[index]); 3322b3ad188SAdrian Chadd } 3332b3ad188SAdrian Chadd #endif 3342b3ad188SAdrian Chadd 3352b3ad188SAdrian Chadd /* 3362b3ad188SAdrian Chadd * Main interrupt dispatch handler. It's called straight 3372b3ad188SAdrian Chadd * from the assembler, where CPU interrupt is served. 3382b3ad188SAdrian Chadd */ 3392b3ad188SAdrian Chadd void 3402b3ad188SAdrian Chadd intr_irq_handler(struct trapframe *tf) 3412b3ad188SAdrian Chadd { 3422b3ad188SAdrian Chadd struct trapframe * oldframe; 3432b3ad188SAdrian Chadd struct thread * td; 3442b3ad188SAdrian Chadd 3452b3ad188SAdrian Chadd KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__)); 3462b3ad188SAdrian Chadd 34789c52f9dSKyle Evans kasan_mark(tf, sizeof(*tf), sizeof(*tf), 0); 34889c52f9dSKyle Evans 34983c9dea1SGleb Smirnoff VM_CNT_INC(v_intr); 3502b3ad188SAdrian Chadd critical_enter(); 3512b3ad188SAdrian Chadd td = curthread; 3522b3ad188SAdrian Chadd oldframe = td->td_intr_frame; 3532b3ad188SAdrian Chadd td->td_intr_frame = tf; 3542b3ad188SAdrian Chadd irq_root_filter(irq_root_arg); 3552b3ad188SAdrian Chadd td->td_intr_frame = oldframe; 3562b3ad188SAdrian Chadd critical_exit(); 357df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 358974692e3SAndrew Turner if (pmc_hook && TRAPF_USERMODE(tf) && 359974692e3SAndrew Turner (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) 360df7a2251SAndrew Turner pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf); 361df7a2251SAndrew Turner #endif 3622b3ad188SAdrian Chadd } 3632b3ad188SAdrian Chadd 364d1605cdaSAndrew Turner int 365d1605cdaSAndrew Turner intr_child_irq_handler(struct intr_pic *parent, uintptr_t irq) 366d1605cdaSAndrew Turner { 367d1605cdaSAndrew Turner struct intr_pic_child *child; 368d1605cdaSAndrew Turner bool found; 369d1605cdaSAndrew Turner 370d1605cdaSAndrew Turner found = false; 371d1605cdaSAndrew Turner mtx_lock_spin(&parent->pic_child_lock); 372d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent->pic_children, pc_next) { 373d1605cdaSAndrew Turner if (child->pc_start <= irq && 374d1605cdaSAndrew Turner irq < (child->pc_start + child->pc_length)) { 375d1605cdaSAndrew Turner found = true; 376d1605cdaSAndrew Turner break; 377d1605cdaSAndrew Turner } 378d1605cdaSAndrew Turner } 379d1605cdaSAndrew Turner mtx_unlock_spin(&parent->pic_child_lock); 380d1605cdaSAndrew Turner 381d1605cdaSAndrew Turner if (found) 382d1605cdaSAndrew Turner return (child->pc_filter(child->pc_filter_arg, irq)); 383d1605cdaSAndrew Turner 384d1605cdaSAndrew Turner return (FILTER_STRAY); 385d1605cdaSAndrew Turner } 386d1605cdaSAndrew Turner 3872b3ad188SAdrian Chadd /* 3882b3ad188SAdrian Chadd * interrupt controller dispatch function for interrupts. It should 3892b3ad188SAdrian Chadd * be called straight from the interrupt controller, when associated interrupt 3902b3ad188SAdrian Chadd * source is learned. 3912b3ad188SAdrian Chadd */ 392bff6be3eSSvatopluk Kraus int 393bff6be3eSSvatopluk Kraus intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) 3942b3ad188SAdrian Chadd { 3952b3ad188SAdrian Chadd 3962b3ad188SAdrian Chadd KASSERT(isrc != NULL, ("%s: no source", __func__)); 3972b3ad188SAdrian Chadd 3982b3ad188SAdrian Chadd isrc_increment_count(isrc); 3992b3ad188SAdrian Chadd 4002b3ad188SAdrian Chadd #ifdef INTR_SOLO 4012b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 4022b3ad188SAdrian Chadd int error; 4032b3ad188SAdrian Chadd error = isrc->isrc_filter(isrc->isrc_arg, tf); 4042b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 4052b3ad188SAdrian Chadd if (error == FILTER_HANDLED) 406bff6be3eSSvatopluk Kraus return (0); 4072b3ad188SAdrian Chadd } else 4082b3ad188SAdrian Chadd #endif 4092b3ad188SAdrian Chadd if (isrc->isrc_event != NULL) { 4102b3ad188SAdrian Chadd if (intr_event_handle(isrc->isrc_event, tf) == 0) 411bff6be3eSSvatopluk Kraus return (0); 4122b3ad188SAdrian Chadd } 4132b3ad188SAdrian Chadd 4142b3ad188SAdrian Chadd isrc_increment_straycount(isrc); 415bff6be3eSSvatopluk Kraus return (EINVAL); 4162b3ad188SAdrian Chadd } 4172b3ad188SAdrian Chadd 4182b3ad188SAdrian Chadd /* 4192b3ad188SAdrian Chadd * Alloc unique interrupt number (resource handle) for interrupt source. 4202b3ad188SAdrian Chadd * 4212b3ad188SAdrian Chadd * There could be various strategies how to allocate free interrupt number 4222b3ad188SAdrian Chadd * (resource handle) for new interrupt source. 4232b3ad188SAdrian Chadd * 4242b3ad188SAdrian Chadd * 1. Handles are always allocated forward, so handles are not recycled 4252b3ad188SAdrian Chadd * immediately. However, if only one free handle left which is reused 4262b3ad188SAdrian Chadd * constantly... 4272b3ad188SAdrian Chadd */ 428bff6be3eSSvatopluk Kraus static inline int 429bff6be3eSSvatopluk Kraus isrc_alloc_irq(struct intr_irqsrc *isrc) 4302b3ad188SAdrian Chadd { 431e88c3b1bSMichal Meloun u_int irq; 4322b3ad188SAdrian Chadd 4332b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 4342b3ad188SAdrian Chadd 435e88c3b1bSMichal Meloun if (irq_next_free >= intr_nirq) 4362b3ad188SAdrian Chadd return (ENOSPC); 4372b3ad188SAdrian Chadd 438e88c3b1bSMichal Meloun for (irq = irq_next_free; irq < intr_nirq; irq++) { 4392b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 4402b3ad188SAdrian Chadd goto found; 4412b3ad188SAdrian Chadd } 4422b3ad188SAdrian Chadd for (irq = 0; irq < irq_next_free; irq++) { 4432b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 4442b3ad188SAdrian Chadd goto found; 4452b3ad188SAdrian Chadd } 4462b3ad188SAdrian Chadd 447e88c3b1bSMichal Meloun irq_next_free = intr_nirq; 4482b3ad188SAdrian Chadd return (ENOSPC); 4492b3ad188SAdrian Chadd 4502b3ad188SAdrian Chadd found: 4512b3ad188SAdrian Chadd isrc->isrc_irq = irq; 4522b3ad188SAdrian Chadd irq_sources[irq] = isrc; 4532b3ad188SAdrian Chadd 4542b3ad188SAdrian Chadd irq_next_free = irq + 1; 455e88c3b1bSMichal Meloun if (irq_next_free >= intr_nirq) 4562b3ad188SAdrian Chadd irq_next_free = 0; 4572b3ad188SAdrian Chadd return (0); 4582b3ad188SAdrian Chadd } 459bff6be3eSSvatopluk Kraus 4602b3ad188SAdrian Chadd /* 4612b3ad188SAdrian Chadd * Free unique interrupt number (resource handle) from interrupt source. 4622b3ad188SAdrian Chadd */ 463bff6be3eSSvatopluk Kraus static inline int 4642b3ad188SAdrian Chadd isrc_free_irq(struct intr_irqsrc *isrc) 4652b3ad188SAdrian Chadd { 4662b3ad188SAdrian Chadd 467bff6be3eSSvatopluk Kraus mtx_assert(&isrc_table_lock, MA_OWNED); 4682b3ad188SAdrian Chadd 469248f0cabSOleksandr Tymoshenko if (isrc->isrc_irq >= intr_nirq) 4702b3ad188SAdrian Chadd return (EINVAL); 471bff6be3eSSvatopluk Kraus if (irq_sources[isrc->isrc_irq] != isrc) 4722b3ad188SAdrian Chadd return (EINVAL); 4732b3ad188SAdrian Chadd 4742b3ad188SAdrian Chadd irq_sources[isrc->isrc_irq] = NULL; 4758442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 476a49f208dSMichal Meloun 477a49f208dSMichal Meloun /* 478a49f208dSMichal Meloun * If we are recovering from the state irq_sources table is full, 479a49f208dSMichal Meloun * then the following allocation should check the entire table. This 480a49f208dSMichal Meloun * will ensure maximum separation of allocation order from release 481a49f208dSMichal Meloun * order. 482a49f208dSMichal Meloun */ 483a49f208dSMichal Meloun if (irq_next_free >= intr_nirq) 484a49f208dSMichal Meloun irq_next_free = 0; 485a49f208dSMichal Meloun 4862b3ad188SAdrian Chadd return (0); 4872b3ad188SAdrian Chadd } 488bff6be3eSSvatopluk Kraus 4892b3ad188SAdrian Chadd /* 490bff6be3eSSvatopluk Kraus * Initialize interrupt source and register it into global interrupt table. 4912b3ad188SAdrian Chadd */ 492bff6be3eSSvatopluk Kraus int 493bff6be3eSSvatopluk Kraus intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, 494bff6be3eSSvatopluk Kraus const char *fmt, ...) 4952b3ad188SAdrian Chadd { 496bff6be3eSSvatopluk Kraus int error; 497bff6be3eSSvatopluk Kraus va_list ap; 4982b3ad188SAdrian Chadd 499bff6be3eSSvatopluk Kraus bzero(isrc, sizeof(struct intr_irqsrc)); 500bff6be3eSSvatopluk Kraus isrc->isrc_dev = dev; 5018442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 502bff6be3eSSvatopluk Kraus isrc->isrc_flags = flags; 5032b3ad188SAdrian Chadd 504bff6be3eSSvatopluk Kraus va_start(ap, fmt); 505bff6be3eSSvatopluk Kraus vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); 506bff6be3eSSvatopluk Kraus va_end(ap); 507bff6be3eSSvatopluk Kraus 508bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 509bff6be3eSSvatopluk Kraus error = isrc_alloc_irq(isrc); 510bff6be3eSSvatopluk Kraus if (error != 0) { 511bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 512bff6be3eSSvatopluk Kraus return (error); 5132b3ad188SAdrian Chadd } 514bff6be3eSSvatopluk Kraus /* 515bff6be3eSSvatopluk Kraus * Setup interrupt counters, but not for IPI sources. Those are setup 516bff6be3eSSvatopluk Kraus * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust 517bff6be3eSSvatopluk Kraus * our counter pool. 518bff6be3eSSvatopluk Kraus */ 519bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 520bff6be3eSSvatopluk Kraus isrc_setup_counters(isrc); 521bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 522bff6be3eSSvatopluk Kraus return (0); 5232b3ad188SAdrian Chadd } 5242b3ad188SAdrian Chadd 5252b3ad188SAdrian Chadd /* 526bff6be3eSSvatopluk Kraus * Deregister interrupt source from global interrupt table. 527bff6be3eSSvatopluk Kraus */ 528bff6be3eSSvatopluk Kraus int 529bff6be3eSSvatopluk Kraus intr_isrc_deregister(struct intr_irqsrc *isrc) 530bff6be3eSSvatopluk Kraus { 531bff6be3eSSvatopluk Kraus int error; 532bff6be3eSSvatopluk Kraus 533bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 534bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 535bff6be3eSSvatopluk Kraus isrc_release_counters(isrc); 536bff6be3eSSvatopluk Kraus error = isrc_free_irq(isrc); 537bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 538bff6be3eSSvatopluk Kraus return (error); 539bff6be3eSSvatopluk Kraus } 540bff6be3eSSvatopluk Kraus 5415b613c19SSvatopluk Kraus #ifdef SMP 5425b613c19SSvatopluk Kraus /* 5435b613c19SSvatopluk Kraus * A support function for a PIC to decide if provided ISRC should be inited 5445b613c19SSvatopluk Kraus * on given cpu. The logic of INTR_ISRCF_BOUND flag and isrc_cpu member of 5455b613c19SSvatopluk Kraus * struct intr_irqsrc is the following: 5465b613c19SSvatopluk Kraus * 5475b613c19SSvatopluk Kraus * If INTR_ISRCF_BOUND is set, the ISRC should be inited only on cpus 5485b613c19SSvatopluk Kraus * set in isrc_cpu. If not, the ISRC should be inited on every cpu and 5495b613c19SSvatopluk Kraus * isrc_cpu is kept consistent with it. Thus isrc_cpu is always correct. 5505b613c19SSvatopluk Kraus */ 5515b613c19SSvatopluk Kraus bool 5525b613c19SSvatopluk Kraus intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu) 5535b613c19SSvatopluk Kraus { 5545b613c19SSvatopluk Kraus 5555b613c19SSvatopluk Kraus if (isrc->isrc_handlers == 0) 5565b613c19SSvatopluk Kraus return (false); 5575b613c19SSvatopluk Kraus if ((isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) == 0) 5585b613c19SSvatopluk Kraus return (false); 5595b613c19SSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_BOUND) 5605b613c19SSvatopluk Kraus return (CPU_ISSET(cpu, &isrc->isrc_cpu)); 5615b613c19SSvatopluk Kraus 5625b613c19SSvatopluk Kraus CPU_SET(cpu, &isrc->isrc_cpu); 5635b613c19SSvatopluk Kraus return (true); 5645b613c19SSvatopluk Kraus } 5655b613c19SSvatopluk Kraus #endif 5665b613c19SSvatopluk Kraus 5672b3ad188SAdrian Chadd #ifdef INTR_SOLO 5682b3ad188SAdrian Chadd /* 5692b3ad188SAdrian Chadd * Setup filter into interrupt source. 5702b3ad188SAdrian Chadd */ 5712b3ad188SAdrian Chadd static int 5722b3ad188SAdrian Chadd iscr_setup_filter(struct intr_irqsrc *isrc, const char *name, 5732b3ad188SAdrian Chadd intr_irq_filter_t *filter, void *arg, void **cookiep) 5742b3ad188SAdrian Chadd { 5752b3ad188SAdrian Chadd 5762b3ad188SAdrian Chadd if (filter == NULL) 5772b3ad188SAdrian Chadd return (EINVAL); 5782b3ad188SAdrian Chadd 5792b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 5802b3ad188SAdrian Chadd /* 5812b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 5822b3ad188SAdrian Chadd * how we handle interrupt sources. 5832b3ad188SAdrian Chadd */ 5842b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 5852b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 5862b3ad188SAdrian Chadd return (EBUSY); 5872b3ad188SAdrian Chadd } 5882b3ad188SAdrian Chadd isrc->isrc_filter = filter; 5892b3ad188SAdrian Chadd isrc->isrc_arg = arg; 5902b3ad188SAdrian Chadd isrc_update_name(isrc, name); 5912b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 5922b3ad188SAdrian Chadd 5932b3ad188SAdrian Chadd *cookiep = isrc; 5942b3ad188SAdrian Chadd return (0); 5952b3ad188SAdrian Chadd } 5962b3ad188SAdrian Chadd #endif 5972b3ad188SAdrian Chadd 5982b3ad188SAdrian Chadd /* 5992b3ad188SAdrian Chadd * Interrupt source pre_ithread method for MI interrupt framework. 6002b3ad188SAdrian Chadd */ 6012b3ad188SAdrian Chadd static void 6022b3ad188SAdrian Chadd intr_isrc_pre_ithread(void *arg) 6032b3ad188SAdrian Chadd { 6042b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6052b3ad188SAdrian Chadd 6062b3ad188SAdrian Chadd PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); 6072b3ad188SAdrian Chadd } 6082b3ad188SAdrian Chadd 6092b3ad188SAdrian Chadd /* 6102b3ad188SAdrian Chadd * Interrupt source post_ithread method for MI interrupt framework. 6112b3ad188SAdrian Chadd */ 6122b3ad188SAdrian Chadd static void 6132b3ad188SAdrian Chadd intr_isrc_post_ithread(void *arg) 6142b3ad188SAdrian Chadd { 6152b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6162b3ad188SAdrian Chadd 6172b3ad188SAdrian Chadd PIC_POST_ITHREAD(isrc->isrc_dev, isrc); 6182b3ad188SAdrian Chadd } 6192b3ad188SAdrian Chadd 6202b3ad188SAdrian Chadd /* 6212b3ad188SAdrian Chadd * Interrupt source post_filter method for MI interrupt framework. 6222b3ad188SAdrian Chadd */ 6232b3ad188SAdrian Chadd static void 6242b3ad188SAdrian Chadd intr_isrc_post_filter(void *arg) 6252b3ad188SAdrian Chadd { 6262b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6272b3ad188SAdrian Chadd 6282b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 6292b3ad188SAdrian Chadd } 6302b3ad188SAdrian Chadd 6312b3ad188SAdrian Chadd /* 6322b3ad188SAdrian Chadd * Interrupt source assign_cpu method for MI interrupt framework. 6332b3ad188SAdrian Chadd */ 6342b3ad188SAdrian Chadd static int 6352b3ad188SAdrian Chadd intr_isrc_assign_cpu(void *arg, int cpu) 6362b3ad188SAdrian Chadd { 6372b3ad188SAdrian Chadd #ifdef SMP 6382b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6392b3ad188SAdrian Chadd int error; 6402b3ad188SAdrian Chadd 6412b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6422b3ad188SAdrian Chadd if (cpu == NOCPU) { 6432b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 6442b3ad188SAdrian Chadd isrc->isrc_flags &= ~INTR_ISRCF_BOUND; 6452b3ad188SAdrian Chadd } else { 6462b3ad188SAdrian Chadd CPU_SETOF(cpu, &isrc->isrc_cpu); 6472b3ad188SAdrian Chadd isrc->isrc_flags |= INTR_ISRCF_BOUND; 6482b3ad188SAdrian Chadd } 6492b3ad188SAdrian Chadd 6502b3ad188SAdrian Chadd /* 6512b3ad188SAdrian Chadd * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or 6522b3ad188SAdrian Chadd * re-balance it to another CPU or enable it on more CPUs. However, 6532b3ad188SAdrian Chadd * PIC is expected to change isrc_cpu appropriately to keep us well 654e3043798SPedro F. Giffuni * informed if the call is successful. 6552b3ad188SAdrian Chadd */ 6562b3ad188SAdrian Chadd if (irq_assign_cpu) { 657bff6be3eSSvatopluk Kraus error = PIC_BIND_INTR(isrc->isrc_dev, isrc); 6582b3ad188SAdrian Chadd if (error) { 6592b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 6602b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6612b3ad188SAdrian Chadd return (error); 6622b3ad188SAdrian Chadd } 6632b3ad188SAdrian Chadd } 6642b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6652b3ad188SAdrian Chadd return (0); 6662b3ad188SAdrian Chadd #else 6672b3ad188SAdrian Chadd return (EOPNOTSUPP); 6682b3ad188SAdrian Chadd #endif 6692b3ad188SAdrian Chadd } 6702b3ad188SAdrian Chadd 6712b3ad188SAdrian Chadd /* 6722b3ad188SAdrian Chadd * Create interrupt event for interrupt source. 6732b3ad188SAdrian Chadd */ 6742b3ad188SAdrian Chadd static int 6752b3ad188SAdrian Chadd isrc_event_create(struct intr_irqsrc *isrc) 6762b3ad188SAdrian Chadd { 6772b3ad188SAdrian Chadd struct intr_event *ie; 6782b3ad188SAdrian Chadd int error; 6792b3ad188SAdrian Chadd 6802b3ad188SAdrian Chadd error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq, 6812b3ad188SAdrian Chadd intr_isrc_pre_ithread, intr_isrc_post_ithread, intr_isrc_post_filter, 6822b3ad188SAdrian Chadd intr_isrc_assign_cpu, "%s:", isrc->isrc_name); 6832b3ad188SAdrian Chadd if (error) 6842b3ad188SAdrian Chadd return (error); 6852b3ad188SAdrian Chadd 6862b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6872b3ad188SAdrian Chadd /* 6882b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 6892b3ad188SAdrian Chadd * how we handle interrupt sources. Let contested event wins. 6902b3ad188SAdrian Chadd */ 691169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 6922b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 693169e6abdSSvatopluk Kraus #else 694169e6abdSSvatopluk Kraus if (isrc->isrc_event != NULL) { 695169e6abdSSvatopluk Kraus #endif 6962b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6972b3ad188SAdrian Chadd intr_event_destroy(ie); 6982b3ad188SAdrian Chadd return (isrc->isrc_event != NULL ? EBUSY : 0); 6992b3ad188SAdrian Chadd } 7002b3ad188SAdrian Chadd isrc->isrc_event = ie; 7012b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7022b3ad188SAdrian Chadd 7032b3ad188SAdrian Chadd return (0); 7042b3ad188SAdrian Chadd } 7052b3ad188SAdrian Chadd #ifdef notyet 7062b3ad188SAdrian Chadd /* 7072b3ad188SAdrian Chadd * Destroy interrupt event for interrupt source. 7082b3ad188SAdrian Chadd */ 7092b3ad188SAdrian Chadd static void 7102b3ad188SAdrian Chadd isrc_event_destroy(struct intr_irqsrc *isrc) 7112b3ad188SAdrian Chadd { 7122b3ad188SAdrian Chadd struct intr_event *ie; 7132b3ad188SAdrian Chadd 7142b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7152b3ad188SAdrian Chadd ie = isrc->isrc_event; 7162b3ad188SAdrian Chadd isrc->isrc_event = NULL; 7172b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7182b3ad188SAdrian Chadd 7192b3ad188SAdrian Chadd if (ie != NULL) 7202b3ad188SAdrian Chadd intr_event_destroy(ie); 7212b3ad188SAdrian Chadd } 7222b3ad188SAdrian Chadd #endif 7232b3ad188SAdrian Chadd /* 7242b3ad188SAdrian Chadd * Add handler to interrupt source. 7252b3ad188SAdrian Chadd */ 7262b3ad188SAdrian Chadd static int 7272b3ad188SAdrian Chadd isrc_add_handler(struct intr_irqsrc *isrc, const char *name, 7282b3ad188SAdrian Chadd driver_filter_t filter, driver_intr_t handler, void *arg, 7292b3ad188SAdrian Chadd enum intr_type flags, void **cookiep) 7302b3ad188SAdrian Chadd { 7312b3ad188SAdrian Chadd int error; 7322b3ad188SAdrian Chadd 7332b3ad188SAdrian Chadd if (isrc->isrc_event == NULL) { 7342b3ad188SAdrian Chadd error = isrc_event_create(isrc); 7352b3ad188SAdrian Chadd if (error) 7362b3ad188SAdrian Chadd return (error); 7372b3ad188SAdrian Chadd } 7382b3ad188SAdrian Chadd 7392b3ad188SAdrian Chadd error = intr_event_add_handler(isrc->isrc_event, name, filter, handler, 7402b3ad188SAdrian Chadd arg, intr_priority(flags), flags, cookiep); 7412b3ad188SAdrian Chadd if (error == 0) { 7422b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7432b3ad188SAdrian Chadd intrcnt_updatename(isrc); 7442b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7452b3ad188SAdrian Chadd } 7462b3ad188SAdrian Chadd 7472b3ad188SAdrian Chadd return (error); 7482b3ad188SAdrian Chadd } 7492b3ad188SAdrian Chadd 7502b3ad188SAdrian Chadd /* 7512b3ad188SAdrian Chadd * Lookup interrupt controller locked. 7522b3ad188SAdrian Chadd */ 753bff6be3eSSvatopluk Kraus static inline struct intr_pic * 754c0d52370SAndrew Turner pic_lookup_locked(device_t dev, intptr_t xref, int flags) 7552b3ad188SAdrian Chadd { 7562b3ad188SAdrian Chadd struct intr_pic *pic; 7572b3ad188SAdrian Chadd 7582b3ad188SAdrian Chadd mtx_assert(&pic_list_lock, MA_OWNED); 7592b3ad188SAdrian Chadd 7604be58cbaSSvatopluk Kraus if (dev == NULL && xref == 0) 7614be58cbaSSvatopluk Kraus return (NULL); 7624be58cbaSSvatopluk Kraus 7634be58cbaSSvatopluk Kraus /* Note that pic->pic_dev is never NULL on registered PIC. */ 7642b3ad188SAdrian Chadd SLIST_FOREACH(pic, &pic_list, pic_next) { 765c0d52370SAndrew Turner if ((pic->pic_flags & FLAG_TYPE_MASK) != 766c0d52370SAndrew Turner (flags & FLAG_TYPE_MASK)) 767c0d52370SAndrew Turner continue; 768c0d52370SAndrew Turner 7694be58cbaSSvatopluk Kraus if (dev == NULL) { 7704be58cbaSSvatopluk Kraus if (xref == pic->pic_xref) 7714be58cbaSSvatopluk Kraus return (pic); 7724be58cbaSSvatopluk Kraus } else if (xref == 0 || pic->pic_xref == 0) { 7734be58cbaSSvatopluk Kraus if (dev == pic->pic_dev) 7744be58cbaSSvatopluk Kraus return (pic); 7754be58cbaSSvatopluk Kraus } else if (xref == pic->pic_xref && dev == pic->pic_dev) 7762b3ad188SAdrian Chadd return (pic); 7772b3ad188SAdrian Chadd } 7782b3ad188SAdrian Chadd return (NULL); 7792b3ad188SAdrian Chadd } 7802b3ad188SAdrian Chadd 7812b3ad188SAdrian Chadd /* 7822b3ad188SAdrian Chadd * Lookup interrupt controller. 7832b3ad188SAdrian Chadd */ 7842b3ad188SAdrian Chadd static struct intr_pic * 785c0d52370SAndrew Turner pic_lookup(device_t dev, intptr_t xref, int flags) 7862b3ad188SAdrian Chadd { 7872b3ad188SAdrian Chadd struct intr_pic *pic; 7882b3ad188SAdrian Chadd 7892b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 790c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 7912b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7922b3ad188SAdrian Chadd return (pic); 7932b3ad188SAdrian Chadd } 7942b3ad188SAdrian Chadd 7952b3ad188SAdrian Chadd /* 7962b3ad188SAdrian Chadd * Create interrupt controller. 7972b3ad188SAdrian Chadd */ 7982b3ad188SAdrian Chadd static struct intr_pic * 799c0d52370SAndrew Turner pic_create(device_t dev, intptr_t xref, int flags) 8002b3ad188SAdrian Chadd { 8012b3ad188SAdrian Chadd struct intr_pic *pic; 8022b3ad188SAdrian Chadd 8032b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 804c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 8052b3ad188SAdrian Chadd if (pic != NULL) { 8062b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8072b3ad188SAdrian Chadd return (pic); 8082b3ad188SAdrian Chadd } 8092b3ad188SAdrian Chadd pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO); 810b48c6083SAndrew Turner if (pic == NULL) { 811b48c6083SAndrew Turner mtx_unlock(&pic_list_lock); 812b48c6083SAndrew Turner return (NULL); 813b48c6083SAndrew Turner } 8142b3ad188SAdrian Chadd pic->pic_xref = xref; 8152b3ad188SAdrian Chadd pic->pic_dev = dev; 816c0d52370SAndrew Turner pic->pic_flags = flags; 817d1605cdaSAndrew Turner mtx_init(&pic->pic_child_lock, "pic child lock", NULL, MTX_SPIN); 8182b3ad188SAdrian Chadd SLIST_INSERT_HEAD(&pic_list, pic, pic_next); 8192b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8202b3ad188SAdrian Chadd 8212b3ad188SAdrian Chadd return (pic); 8222b3ad188SAdrian Chadd } 8232b3ad188SAdrian Chadd #ifdef notyet 8242b3ad188SAdrian Chadd /* 8252b3ad188SAdrian Chadd * Destroy interrupt controller. 8262b3ad188SAdrian Chadd */ 8272b3ad188SAdrian Chadd static void 828c0d52370SAndrew Turner pic_destroy(device_t dev, intptr_t xref, int flags) 8292b3ad188SAdrian Chadd { 8302b3ad188SAdrian Chadd struct intr_pic *pic; 8312b3ad188SAdrian Chadd 8322b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 833c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 8342b3ad188SAdrian Chadd if (pic == NULL) { 8352b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8362b3ad188SAdrian Chadd return; 8372b3ad188SAdrian Chadd } 8382b3ad188SAdrian Chadd SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next); 8392b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8402b3ad188SAdrian Chadd 8412b3ad188SAdrian Chadd free(pic, M_INTRNG); 8422b3ad188SAdrian Chadd } 8432b3ad188SAdrian Chadd #endif 8442b3ad188SAdrian Chadd /* 8452b3ad188SAdrian Chadd * Register interrupt controller. 8462b3ad188SAdrian Chadd */ 8479346e913SAndrew Turner struct intr_pic * 8482b3ad188SAdrian Chadd intr_pic_register(device_t dev, intptr_t xref) 8492b3ad188SAdrian Chadd { 8502b3ad188SAdrian Chadd struct intr_pic *pic; 8512b3ad188SAdrian Chadd 8524be58cbaSSvatopluk Kraus if (dev == NULL) 8539346e913SAndrew Turner return (NULL); 854c0d52370SAndrew Turner pic = pic_create(dev, xref, FLAG_PIC); 8552b3ad188SAdrian Chadd if (pic == NULL) 8569346e913SAndrew Turner return (NULL); 8572b3ad188SAdrian Chadd 858cff33fa8SEd Maste debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic, 859cff33fa8SEd Maste device_get_nameunit(dev), dev, (uintmax_t)xref); 8609346e913SAndrew Turner return (pic); 8612b3ad188SAdrian Chadd } 8622b3ad188SAdrian Chadd 8632b3ad188SAdrian Chadd /* 8642b3ad188SAdrian Chadd * Unregister interrupt controller. 8652b3ad188SAdrian Chadd */ 8662b3ad188SAdrian Chadd int 867bff6be3eSSvatopluk Kraus intr_pic_deregister(device_t dev, intptr_t xref) 8682b3ad188SAdrian Chadd { 8692b3ad188SAdrian Chadd 8702b3ad188SAdrian Chadd panic("%s: not implemented", __func__); 8712b3ad188SAdrian Chadd } 8722b3ad188SAdrian Chadd 8732b3ad188SAdrian Chadd /* 8742b3ad188SAdrian Chadd * Mark interrupt controller (itself) as a root one. 8752b3ad188SAdrian Chadd * 8762b3ad188SAdrian Chadd * Note that only an interrupt controller can really know its position 8772b3ad188SAdrian Chadd * in interrupt controller's tree. So root PIC must claim itself as a root. 8782b3ad188SAdrian Chadd * 8792b3ad188SAdrian Chadd * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, 8802b3ad188SAdrian Chadd * page 30: 8812b3ad188SAdrian Chadd * "The root of the interrupt tree is determined when traversal 8822b3ad188SAdrian Chadd * of the interrupt tree reaches an interrupt controller node without 8832b3ad188SAdrian Chadd * an interrupts property and thus no explicit interrupt parent." 8842b3ad188SAdrian Chadd */ 8852b3ad188SAdrian Chadd int 8862b3ad188SAdrian Chadd intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, 8872b3ad188SAdrian Chadd void *arg, u_int ipicount) 8882b3ad188SAdrian Chadd { 8893fc155dcSAndrew Turner struct intr_pic *pic; 8902b3ad188SAdrian Chadd 891c0d52370SAndrew Turner pic = pic_lookup(dev, xref, FLAG_PIC); 8923fc155dcSAndrew Turner if (pic == NULL) { 8932b3ad188SAdrian Chadd device_printf(dev, "not registered\n"); 8942b3ad188SAdrian Chadd return (EINVAL); 8952b3ad188SAdrian Chadd } 8963fc155dcSAndrew Turner 897c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_PIC, 8983fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 8993fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 9003fc155dcSAndrew Turner 9012b3ad188SAdrian Chadd if (filter == NULL) { 9022b3ad188SAdrian Chadd device_printf(dev, "filter missing\n"); 9032b3ad188SAdrian Chadd return (EINVAL); 9042b3ad188SAdrian Chadd } 9052b3ad188SAdrian Chadd 9062b3ad188SAdrian Chadd /* 9072b3ad188SAdrian Chadd * Only one interrupt controllers could be on the root for now. 9082b3ad188SAdrian Chadd * Note that we further suppose that there is not threaded interrupt 9092b3ad188SAdrian Chadd * routine (handler) on the root. See intr_irq_handler(). 9102b3ad188SAdrian Chadd */ 9115b70c08cSSvatopluk Kraus if (intr_irq_root_dev != NULL) { 9122b3ad188SAdrian Chadd device_printf(dev, "another root already set\n"); 9132b3ad188SAdrian Chadd return (EBUSY); 9142b3ad188SAdrian Chadd } 9152b3ad188SAdrian Chadd 9165b70c08cSSvatopluk Kraus intr_irq_root_dev = dev; 9172b3ad188SAdrian Chadd irq_root_filter = filter; 9182b3ad188SAdrian Chadd irq_root_arg = arg; 9192b3ad188SAdrian Chadd irq_root_ipicount = ipicount; 9202b3ad188SAdrian Chadd 9212b3ad188SAdrian Chadd debugf("irq root set to %s\n", device_get_nameunit(dev)); 9222b3ad188SAdrian Chadd return (0); 9232b3ad188SAdrian Chadd } 9242b3ad188SAdrian Chadd 925d1605cdaSAndrew Turner /* 926d1605cdaSAndrew Turner * Add a handler to manage a sub range of a parents interrupts. 927d1605cdaSAndrew Turner */ 928a3e828c9SJessica Clarke int 929d1605cdaSAndrew Turner intr_pic_add_handler(device_t parent, struct intr_pic *pic, 930d1605cdaSAndrew Turner intr_child_irq_filter_t *filter, void *arg, uintptr_t start, 931d1605cdaSAndrew Turner uintptr_t length) 932d1605cdaSAndrew Turner { 933d1605cdaSAndrew Turner struct intr_pic *parent_pic; 934d1605cdaSAndrew Turner struct intr_pic_child *newchild; 935d1605cdaSAndrew Turner #ifdef INVARIANTS 936d1605cdaSAndrew Turner struct intr_pic_child *child; 937d1605cdaSAndrew Turner #endif 938d1605cdaSAndrew Turner 939c0d52370SAndrew Turner /* Find the parent PIC */ 940c0d52370SAndrew Turner parent_pic = pic_lookup(parent, 0, FLAG_PIC); 941d1605cdaSAndrew Turner if (parent_pic == NULL) 942a3e828c9SJessica Clarke return (ENXIO); 943d1605cdaSAndrew Turner 944d1605cdaSAndrew Turner newchild = malloc(sizeof(*newchild), M_INTRNG, M_WAITOK | M_ZERO); 945d1605cdaSAndrew Turner newchild->pc_pic = pic; 946d1605cdaSAndrew Turner newchild->pc_filter = filter; 947d1605cdaSAndrew Turner newchild->pc_filter_arg = arg; 948d1605cdaSAndrew Turner newchild->pc_start = start; 949d1605cdaSAndrew Turner newchild->pc_length = length; 950d1605cdaSAndrew Turner 951d1605cdaSAndrew Turner mtx_lock_spin(&parent_pic->pic_child_lock); 952d1605cdaSAndrew Turner #ifdef INVARIANTS 953d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent_pic->pic_children, pc_next) { 954d1605cdaSAndrew Turner KASSERT(child->pc_pic != pic, ("%s: Adding a child PIC twice", 955d1605cdaSAndrew Turner __func__)); 956d1605cdaSAndrew Turner } 957d1605cdaSAndrew Turner #endif 958d1605cdaSAndrew Turner SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next); 959d1605cdaSAndrew Turner mtx_unlock_spin(&parent_pic->pic_child_lock); 960d1605cdaSAndrew Turner 961a3e828c9SJessica Clarke return (0); 962d1605cdaSAndrew Turner } 963d1605cdaSAndrew Turner 964895c8b1cSMichal Meloun static int 965895c8b1cSMichal Meloun intr_resolve_irq(device_t dev, intptr_t xref, struct intr_map_data *data, 966895c8b1cSMichal Meloun struct intr_irqsrc **isrc) 9672b3ad188SAdrian Chadd { 968bff6be3eSSvatopluk Kraus struct intr_pic *pic; 969895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 970bff6be3eSSvatopluk Kraus 971bff6be3eSSvatopluk Kraus if (data == NULL) 972bff6be3eSSvatopluk Kraus return (EINVAL); 973bff6be3eSSvatopluk Kraus 974c0d52370SAndrew Turner pic = pic_lookup(dev, xref, 975c0d52370SAndrew Turner (data->type == INTR_MAP_DATA_MSI) ? FLAG_MSI : FLAG_PIC); 97615adccc6SSvatopluk Kraus if (pic == NULL) 977bff6be3eSSvatopluk Kraus return (ESRCH); 978bff6be3eSSvatopluk Kraus 979895c8b1cSMichal Meloun switch (data->type) { 980895c8b1cSMichal Meloun case INTR_MAP_DATA_MSI: 981c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 982895c8b1cSMichal Meloun ("%s: Found a non-MSI controller: %s", __func__, 983895c8b1cSMichal Meloun device_get_name(pic->pic_dev))); 984895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)data; 985895c8b1cSMichal Meloun *isrc = msi->isrc; 986895c8b1cSMichal Meloun return (0); 987895c8b1cSMichal Meloun 988895c8b1cSMichal Meloun default: 989c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_PIC, 9903fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 9913fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 992895c8b1cSMichal Meloun return (PIC_MAP_INTR(pic->pic_dev, data, isrc)); 993895c8b1cSMichal Meloun } 994895c8b1cSMichal Meloun } 995895c8b1cSMichal Meloun 996eb20867fSMichal Meloun bool 997eb20867fSMichal Meloun intr_is_per_cpu(struct resource *res) 998eb20867fSMichal Meloun { 999eb20867fSMichal Meloun u_int res_id; 1000eb20867fSMichal Meloun struct intr_irqsrc *isrc; 1001eb20867fSMichal Meloun 1002eb20867fSMichal Meloun res_id = (u_int)rman_get_start(res); 1003eb20867fSMichal Meloun isrc = intr_map_get_isrc(res_id); 1004eb20867fSMichal Meloun 1005eb20867fSMichal Meloun if (isrc == NULL) 1006eb20867fSMichal Meloun panic("Attempt to get isrc for non-active resource id: %u\n", 1007eb20867fSMichal Meloun res_id); 1008eb20867fSMichal Meloun return ((isrc->isrc_flags & INTR_ISRCF_PPI) != 0); 1009eb20867fSMichal Meloun } 1010eb20867fSMichal Meloun 1011895c8b1cSMichal Meloun int 1012895c8b1cSMichal Meloun intr_activate_irq(device_t dev, struct resource *res) 1013895c8b1cSMichal Meloun { 1014895c8b1cSMichal Meloun device_t map_dev; 1015895c8b1cSMichal Meloun intptr_t map_xref; 1016895c8b1cSMichal Meloun struct intr_map_data *data; 1017895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 1018895c8b1cSMichal Meloun u_int res_id; 1019895c8b1cSMichal Meloun int error; 1020895c8b1cSMichal Meloun 1021895c8b1cSMichal Meloun KASSERT(rman_get_start(res) == rman_get_end(res), 1022895c8b1cSMichal Meloun ("%s: more interrupts in resource", __func__)); 1023895c8b1cSMichal Meloun 1024895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1025895c8b1cSMichal Meloun if (intr_map_get_isrc(res_id) != NULL) 1026895c8b1cSMichal Meloun panic("Attempt to double activation of resource id: %u\n", 1027895c8b1cSMichal Meloun res_id); 1028895c8b1cSMichal Meloun intr_map_copy_map_data(res_id, &map_dev, &map_xref, &data); 1029895c8b1cSMichal Meloun error = intr_resolve_irq(map_dev, map_xref, data, &isrc); 1030895c8b1cSMichal Meloun if (error != 0) { 1031895c8b1cSMichal Meloun free(data, M_INTRNG); 1032895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 1033895c8b1cSMichal Meloun /* if (error == EINVAL) return(0); */ 1034bff6be3eSSvatopluk Kraus return (error); 1035bff6be3eSSvatopluk Kraus } 1036895c8b1cSMichal Meloun intr_map_set_isrc(res_id, isrc); 1037895c8b1cSMichal Meloun rman_set_virtual(res, data); 1038895c8b1cSMichal Meloun return (PIC_ACTIVATE_INTR(isrc->isrc_dev, isrc, res, data)); 1039bff6be3eSSvatopluk Kraus } 1040bff6be3eSSvatopluk Kraus 1041bff6be3eSSvatopluk Kraus int 1042895c8b1cSMichal Meloun intr_deactivate_irq(device_t dev, struct resource *res) 1043bff6be3eSSvatopluk Kraus { 1044bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1045bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1046895c8b1cSMichal Meloun u_int res_id; 1047895c8b1cSMichal Meloun int error; 1048bff6be3eSSvatopluk Kraus 1049bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1050bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1051bff6be3eSSvatopluk Kraus 1052895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1053895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 1054bff6be3eSSvatopluk Kraus if (isrc == NULL) 1055895c8b1cSMichal Meloun panic("Attempt to deactivate non-active resource id: %u\n", 1056895c8b1cSMichal Meloun res_id); 1057bff6be3eSSvatopluk Kraus 1058c4263292SSvatopluk Kraus data = rman_get_virtual(res); 1059895c8b1cSMichal Meloun error = PIC_DEACTIVATE_INTR(isrc->isrc_dev, isrc, res, data); 1060895c8b1cSMichal Meloun intr_map_set_isrc(res_id, NULL); 1061895c8b1cSMichal Meloun rman_set_virtual(res, NULL); 1062895c8b1cSMichal Meloun free(data, M_INTRNG); 1063895c8b1cSMichal Meloun return (error); 1064bff6be3eSSvatopluk Kraus } 1065bff6be3eSSvatopluk Kraus 1066bff6be3eSSvatopluk Kraus int 1067bff6be3eSSvatopluk Kraus intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, 1068bff6be3eSSvatopluk Kraus driver_intr_t hand, void *arg, int flags, void **cookiep) 1069bff6be3eSSvatopluk Kraus { 1070bff6be3eSSvatopluk Kraus int error; 1071bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1072bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1073bff6be3eSSvatopluk Kraus const char *name; 1074895c8b1cSMichal Meloun u_int res_id; 1075bff6be3eSSvatopluk Kraus 1076bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1077bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1078bff6be3eSSvatopluk Kraus 1079895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1080895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 1081895c8b1cSMichal Meloun if (isrc == NULL) { 1082895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 1083bff6be3eSSvatopluk Kraus return (EINVAL); 1084895c8b1cSMichal Meloun } 10852b3ad188SAdrian Chadd 1086c4263292SSvatopluk Kraus data = rman_get_virtual(res); 10872b3ad188SAdrian Chadd name = device_get_nameunit(dev); 10882b3ad188SAdrian Chadd 10892b3ad188SAdrian Chadd #ifdef INTR_SOLO 10902b3ad188SAdrian Chadd /* 1091e3043798SPedro F. Giffuni * Standard handling is done through MI interrupt framework. However, 10922b3ad188SAdrian Chadd * some interrupts could request solely own special handling. This 10932b3ad188SAdrian Chadd * non standard handling can be used for interrupt controllers without 10942b3ad188SAdrian Chadd * handler (filter only), so in case that interrupt controllers are 10952b3ad188SAdrian Chadd * chained, MI interrupt framework is called only in leaf controller. 10962b3ad188SAdrian Chadd * 10972b3ad188SAdrian Chadd * Note that root interrupt controller routine is served as well, 10982b3ad188SAdrian Chadd * however in intr_irq_handler(), i.e. main system dispatch routine. 10992b3ad188SAdrian Chadd */ 11002b3ad188SAdrian Chadd if (flags & INTR_SOLO && hand != NULL) { 11012b3ad188SAdrian Chadd debugf("irq %u cannot solo on %s\n", irq, name); 11022b3ad188SAdrian Chadd return (EINVAL); 11032b3ad188SAdrian Chadd } 11042b3ad188SAdrian Chadd 11052b3ad188SAdrian Chadd if (flags & INTR_SOLO) { 11062b3ad188SAdrian Chadd error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, 11072b3ad188SAdrian Chadd arg, cookiep); 1108ce44a736SIan Lepore debugf("irq %u setup filter error %d on %s\n", isrc->isrc_irq, error, 11092b3ad188SAdrian Chadd name); 11102b3ad188SAdrian Chadd } else 11112b3ad188SAdrian Chadd #endif 11122b3ad188SAdrian Chadd { 11132b3ad188SAdrian Chadd error = isrc_add_handler(isrc, name, filt, hand, arg, flags, 11142b3ad188SAdrian Chadd cookiep); 1115ce44a736SIan Lepore debugf("irq %u add handler error %d on %s\n", isrc->isrc_irq, error, name); 11162b3ad188SAdrian Chadd } 11172b3ad188SAdrian Chadd if (error != 0) 11182b3ad188SAdrian Chadd return (error); 11192b3ad188SAdrian Chadd 11202b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1121bff6be3eSSvatopluk Kraus error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); 1122bff6be3eSSvatopluk Kraus if (error == 0) { 11232b3ad188SAdrian Chadd isrc->isrc_handlers++; 1124bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 1) 11252b3ad188SAdrian Chadd PIC_ENABLE_INTR(isrc->isrc_dev, isrc); 11262b3ad188SAdrian Chadd } 11272b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 1128bff6be3eSSvatopluk Kraus if (error != 0) 1129bff6be3eSSvatopluk Kraus intr_event_remove_handler(*cookiep); 1130bff6be3eSSvatopluk Kraus return (error); 11312b3ad188SAdrian Chadd } 11322b3ad188SAdrian Chadd 11332b3ad188SAdrian Chadd int 1134bff6be3eSSvatopluk Kraus intr_teardown_irq(device_t dev, struct resource *res, void *cookie) 11352b3ad188SAdrian Chadd { 11362b3ad188SAdrian Chadd int error; 1137bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1138bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1139895c8b1cSMichal Meloun u_int res_id; 11402b3ad188SAdrian Chadd 1141bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1142bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1143bff6be3eSSvatopluk Kraus 1144895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1145895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 11462b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 11472b3ad188SAdrian Chadd return (EINVAL); 1148bff6be3eSSvatopluk Kraus 1149c4263292SSvatopluk Kraus data = rman_get_virtual(res); 1150c4263292SSvatopluk Kraus 1151169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 11522b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 11532b3ad188SAdrian Chadd if (isrc != cookie) 11542b3ad188SAdrian Chadd return (EINVAL); 11552b3ad188SAdrian Chadd 11562b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11572b3ad188SAdrian Chadd isrc->isrc_filter = NULL; 11582b3ad188SAdrian Chadd isrc->isrc_arg = NULL; 11592b3ad188SAdrian Chadd isrc->isrc_handlers = 0; 11602b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1161bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 11622b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 11632b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11642b3ad188SAdrian Chadd return (0); 11652b3ad188SAdrian Chadd } 1166169e6abdSSvatopluk Kraus #endif 11672b3ad188SAdrian Chadd if (isrc != intr_handler_source(cookie)) 11682b3ad188SAdrian Chadd return (EINVAL); 11692b3ad188SAdrian Chadd 11702b3ad188SAdrian Chadd error = intr_event_remove_handler(cookie); 11712b3ad188SAdrian Chadd if (error == 0) { 11722b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11732b3ad188SAdrian Chadd isrc->isrc_handlers--; 1174bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 0) 11752b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1176bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 11772b3ad188SAdrian Chadd intrcnt_updatename(isrc); 11782b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11792b3ad188SAdrian Chadd } 11802b3ad188SAdrian Chadd return (error); 11812b3ad188SAdrian Chadd } 11822b3ad188SAdrian Chadd 11832b3ad188SAdrian Chadd int 1184bff6be3eSSvatopluk Kraus intr_describe_irq(device_t dev, struct resource *res, void *cookie, 1185bff6be3eSSvatopluk Kraus const char *descr) 11862b3ad188SAdrian Chadd { 11872b3ad188SAdrian Chadd int error; 1188bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1189895c8b1cSMichal Meloun u_int res_id; 11902b3ad188SAdrian Chadd 1191bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1192bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1193bff6be3eSSvatopluk Kraus 1194895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1195895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 11962b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 11972b3ad188SAdrian Chadd return (EINVAL); 1198169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 11992b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 12002b3ad188SAdrian Chadd if (isrc != cookie) 12012b3ad188SAdrian Chadd return (EINVAL); 12022b3ad188SAdrian Chadd 12032b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12042b3ad188SAdrian Chadd isrc_update_name(isrc, descr); 12052b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12062b3ad188SAdrian Chadd return (0); 12072b3ad188SAdrian Chadd } 1208169e6abdSSvatopluk Kraus #endif 12092b3ad188SAdrian Chadd error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); 12102b3ad188SAdrian Chadd if (error == 0) { 12112b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12122b3ad188SAdrian Chadd intrcnt_updatename(isrc); 12132b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12142b3ad188SAdrian Chadd } 12152b3ad188SAdrian Chadd return (error); 12162b3ad188SAdrian Chadd } 12172b3ad188SAdrian Chadd 12182b3ad188SAdrian Chadd #ifdef SMP 12192b3ad188SAdrian Chadd int 1220bff6be3eSSvatopluk Kraus intr_bind_irq(device_t dev, struct resource *res, int cpu) 12212b3ad188SAdrian Chadd { 12222b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 1223895c8b1cSMichal Meloun u_int res_id; 12242b3ad188SAdrian Chadd 1225bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1226bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1227bff6be3eSSvatopluk Kraus 1228895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1229895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 12302b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 12312b3ad188SAdrian Chadd return (EINVAL); 1232169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 12332b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) 12342b3ad188SAdrian Chadd return (intr_isrc_assign_cpu(isrc, cpu)); 1235169e6abdSSvatopluk Kraus #endif 12362b3ad188SAdrian Chadd return (intr_event_bind(isrc->isrc_event, cpu)); 12372b3ad188SAdrian Chadd } 12382b3ad188SAdrian Chadd 12392b3ad188SAdrian Chadd /* 12402b3ad188SAdrian Chadd * Return the CPU that the next interrupt source should use. 12412b3ad188SAdrian Chadd * For now just returns the next CPU according to round-robin. 12422b3ad188SAdrian Chadd */ 12432b3ad188SAdrian Chadd u_int 12442b3ad188SAdrian Chadd intr_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask) 12452b3ad188SAdrian Chadd { 1246a92a2f00SAndrew Turner u_int cpu; 12472b3ad188SAdrian Chadd 1248a92a2f00SAndrew Turner KASSERT(!CPU_EMPTY(cpumask), ("%s: Empty CPU mask", __func__)); 1249a92a2f00SAndrew Turner if (!irq_assign_cpu || mp_ncpus == 1) { 1250a92a2f00SAndrew Turner cpu = PCPU_GET(cpuid); 1251a92a2f00SAndrew Turner 1252a92a2f00SAndrew Turner if (CPU_ISSET(cpu, cpumask)) 1253a92a2f00SAndrew Turner return (curcpu); 1254a92a2f00SAndrew Turner 1255a92a2f00SAndrew Turner return (CPU_FFS(cpumask) - 1); 1256a92a2f00SAndrew Turner } 12572b3ad188SAdrian Chadd 12582b3ad188SAdrian Chadd do { 12592b3ad188SAdrian Chadd last_cpu++; 12602b3ad188SAdrian Chadd if (last_cpu > mp_maxid) 12612b3ad188SAdrian Chadd last_cpu = 0; 12622b3ad188SAdrian Chadd } while (!CPU_ISSET(last_cpu, cpumask)); 12632b3ad188SAdrian Chadd return (last_cpu); 12642b3ad188SAdrian Chadd } 12652b3ad188SAdrian Chadd 1266dc425090SMitchell Horne #ifndef EARLY_AP_STARTUP 12672b3ad188SAdrian Chadd /* 12682b3ad188SAdrian Chadd * Distribute all the interrupt sources among the available 12692b3ad188SAdrian Chadd * CPUs once the AP's have been launched. 12702b3ad188SAdrian Chadd */ 12712b3ad188SAdrian Chadd static void 12722b3ad188SAdrian Chadd intr_irq_shuffle(void *arg __unused) 12732b3ad188SAdrian Chadd { 12742b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 12752b3ad188SAdrian Chadd u_int i; 12762b3ad188SAdrian Chadd 12772b3ad188SAdrian Chadd if (mp_ncpus == 1) 12782b3ad188SAdrian Chadd return; 12792b3ad188SAdrian Chadd 12802b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1281dc425090SMitchell Horne irq_assign_cpu = true; 1282248f0cabSOleksandr Tymoshenko for (i = 0; i < intr_nirq; i++) { 12832b3ad188SAdrian Chadd isrc = irq_sources[i]; 12842b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0 || 1285cf55df9fSSvatopluk Kraus isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) 12862b3ad188SAdrian Chadd continue; 12872b3ad188SAdrian Chadd 12882b3ad188SAdrian Chadd if (isrc->isrc_event != NULL && 12892b3ad188SAdrian Chadd isrc->isrc_flags & INTR_ISRCF_BOUND && 12902b3ad188SAdrian Chadd isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1) 12912b3ad188SAdrian Chadd panic("%s: CPU inconsistency", __func__); 12922b3ad188SAdrian Chadd 12932b3ad188SAdrian Chadd if ((isrc->isrc_flags & INTR_ISRCF_BOUND) == 0) 12942b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); /* start again */ 12952b3ad188SAdrian Chadd 12962b3ad188SAdrian Chadd /* 12972b3ad188SAdrian Chadd * We are in wicked position here if the following call fails 12982b3ad188SAdrian Chadd * for bound ISRC. The best thing we can do is to clear 12992b3ad188SAdrian Chadd * isrc_cpu so inconsistency with ie_cpu will be detectable. 13002b3ad188SAdrian Chadd */ 1301bff6be3eSSvatopluk Kraus if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0) 13022b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 13032b3ad188SAdrian Chadd } 13042b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 13052b3ad188SAdrian Chadd } 13062b3ad188SAdrian Chadd SYSINIT(intr_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, intr_irq_shuffle, NULL); 1307dc425090SMitchell Horne #endif /* !EARLY_AP_STARTUP */ 13082b3ad188SAdrian Chadd 13092b3ad188SAdrian Chadd #else 13102b3ad188SAdrian Chadd u_int 13112b3ad188SAdrian Chadd intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) 13122b3ad188SAdrian Chadd { 13132b3ad188SAdrian Chadd 13142b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 13152b3ad188SAdrian Chadd } 1316dc425090SMitchell Horne #endif /* SMP */ 13172b3ad188SAdrian Chadd 13183fc155dcSAndrew Turner /* 1319895c8b1cSMichal Meloun * Allocate memory for new intr_map_data structure. 1320895c8b1cSMichal Meloun * Initialize common fields. 1321895c8b1cSMichal Meloun */ 1322895c8b1cSMichal Meloun struct intr_map_data * 1323895c8b1cSMichal Meloun intr_alloc_map_data(enum intr_map_data_type type, size_t len, int flags) 1324895c8b1cSMichal Meloun { 1325895c8b1cSMichal Meloun struct intr_map_data *data; 1326895c8b1cSMichal Meloun 1327895c8b1cSMichal Meloun data = malloc(len, M_INTRNG, flags); 1328895c8b1cSMichal Meloun data->type = type; 1329895c8b1cSMichal Meloun data->len = len; 1330895c8b1cSMichal Meloun return (data); 1331895c8b1cSMichal Meloun } 1332895c8b1cSMichal Meloun 1333895c8b1cSMichal Meloun void intr_free_intr_map_data(struct intr_map_data *data) 1334895c8b1cSMichal Meloun { 1335895c8b1cSMichal Meloun 1336895c8b1cSMichal Meloun free(data, M_INTRNG); 1337895c8b1cSMichal Meloun } 1338895c8b1cSMichal Meloun 1339895c8b1cSMichal Meloun /* 13403fc155dcSAndrew Turner * Register a MSI/MSI-X interrupt controller 13413fc155dcSAndrew Turner */ 13423fc155dcSAndrew Turner int 13433fc155dcSAndrew Turner intr_msi_register(device_t dev, intptr_t xref) 13443fc155dcSAndrew Turner { 13453fc155dcSAndrew Turner struct intr_pic *pic; 13463fc155dcSAndrew Turner 13473fc155dcSAndrew Turner if (dev == NULL) 13483fc155dcSAndrew Turner return (EINVAL); 1349c0d52370SAndrew Turner pic = pic_create(dev, xref, FLAG_MSI); 13503fc155dcSAndrew Turner if (pic == NULL) 13513fc155dcSAndrew Turner return (ENOMEM); 13523fc155dcSAndrew Turner 13533fc155dcSAndrew Turner debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic, 13543fc155dcSAndrew Turner device_get_nameunit(dev), dev, (uintmax_t)xref); 13553fc155dcSAndrew Turner return (0); 13563fc155dcSAndrew Turner } 13573fc155dcSAndrew Turner 13583fc155dcSAndrew Turner int 13593fc155dcSAndrew Turner intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count, 13603fc155dcSAndrew Turner int maxcount, int *irqs) 13613fc155dcSAndrew Turner { 1362e707c8beSRuslan Bukin struct iommu_domain *domain; 13633fc155dcSAndrew Turner struct intr_irqsrc **isrc; 13643fc155dcSAndrew Turner struct intr_pic *pic; 13653fc155dcSAndrew Turner device_t pdev; 1366895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 13673fc155dcSAndrew Turner int err, i; 13683fc155dcSAndrew Turner 1369c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 13703fc155dcSAndrew Turner if (pic == NULL) 13713fc155dcSAndrew Turner return (ESRCH); 13723fc155dcSAndrew Turner 1373c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 13743fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 13753fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 13763fc155dcSAndrew Turner 1377e707c8beSRuslan Bukin /* 1378e707c8beSRuslan Bukin * If this is the first time we have used this context ask the 1379e707c8beSRuslan Bukin * interrupt controller to map memory the msi source will need. 1380e707c8beSRuslan Bukin */ 1381e707c8beSRuslan Bukin err = MSI_IOMMU_INIT(pic->pic_dev, child, &domain); 1382e707c8beSRuslan Bukin if (err != 0) 1383e707c8beSRuslan Bukin return (err); 1384e707c8beSRuslan Bukin 13853fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 13863fc155dcSAndrew Turner err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc); 1387895c8b1cSMichal Meloun if (err != 0) { 1388895c8b1cSMichal Meloun free(isrc, M_INTRNG); 1389895c8b1cSMichal Meloun return (err); 13903fc155dcSAndrew Turner } 13913fc155dcSAndrew Turner 1392895c8b1cSMichal Meloun for (i = 0; i < count; i++) { 1393e707c8beSRuslan Bukin isrc[i]->isrc_iommu = domain; 1394895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)intr_alloc_map_data( 1395895c8b1cSMichal Meloun INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO); 1396895c8b1cSMichal Meloun msi-> isrc = isrc[i]; 1397e707c8beSRuslan Bukin 1398895c8b1cSMichal Meloun irqs[i] = intr_map_irq(pic->pic_dev, xref, 1399895c8b1cSMichal Meloun (struct intr_map_data *)msi); 1400895c8b1cSMichal Meloun } 14013fc155dcSAndrew Turner free(isrc, M_INTRNG); 14023fc155dcSAndrew Turner 14033fc155dcSAndrew Turner return (err); 14043fc155dcSAndrew Turner } 14053fc155dcSAndrew Turner 14063fc155dcSAndrew Turner int 14073fc155dcSAndrew Turner intr_release_msi(device_t pci, device_t child, intptr_t xref, int count, 14083fc155dcSAndrew Turner int *irqs) 14093fc155dcSAndrew Turner { 14103fc155dcSAndrew Turner struct intr_irqsrc **isrc; 14113fc155dcSAndrew Turner struct intr_pic *pic; 1412609b0fe9SOleksandr Tymoshenko struct intr_map_data_msi *msi; 14133fc155dcSAndrew Turner int i, err; 14143fc155dcSAndrew Turner 1415c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 14163fc155dcSAndrew Turner if (pic == NULL) 14173fc155dcSAndrew Turner return (ESRCH); 14183fc155dcSAndrew Turner 1419c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 14203fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14213fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14223fc155dcSAndrew Turner 14233fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 14243fc155dcSAndrew Turner 1425609b0fe9SOleksandr Tymoshenko for (i = 0; i < count; i++) { 1426609b0fe9SOleksandr Tymoshenko msi = (struct intr_map_data_msi *) 1427609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(irqs[i]); 1428609b0fe9SOleksandr Tymoshenko KASSERT(msi->hdr.type == INTR_MAP_DATA_MSI, 1429609b0fe9SOleksandr Tymoshenko ("%s: irq %d map data is not MSI", __func__, 1430609b0fe9SOleksandr Tymoshenko irqs[i])); 1431609b0fe9SOleksandr Tymoshenko isrc[i] = msi->isrc; 1432609b0fe9SOleksandr Tymoshenko } 14333fc155dcSAndrew Turner 1434f32f0095SRuslan Bukin MSI_IOMMU_DEINIT(pic->pic_dev, child); 1435f32f0095SRuslan Bukin 14363fc155dcSAndrew Turner err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc); 1437895c8b1cSMichal Meloun 1438895c8b1cSMichal Meloun for (i = 0; i < count; i++) { 1439895c8b1cSMichal Meloun if (isrc[i] != NULL) 1440895c8b1cSMichal Meloun intr_unmap_irq(irqs[i]); 1441895c8b1cSMichal Meloun } 1442895c8b1cSMichal Meloun 14433fc155dcSAndrew Turner free(isrc, M_INTRNG); 14443fc155dcSAndrew Turner return (err); 14453fc155dcSAndrew Turner } 14463fc155dcSAndrew Turner 14473fc155dcSAndrew Turner int 14483fc155dcSAndrew Turner intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq) 14493fc155dcSAndrew Turner { 1450e707c8beSRuslan Bukin struct iommu_domain *domain; 14513fc155dcSAndrew Turner struct intr_irqsrc *isrc; 14523fc155dcSAndrew Turner struct intr_pic *pic; 14533fc155dcSAndrew Turner device_t pdev; 1454895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 14553fc155dcSAndrew Turner int err; 14563fc155dcSAndrew Turner 1457c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 14583fc155dcSAndrew Turner if (pic == NULL) 14593fc155dcSAndrew Turner return (ESRCH); 14603fc155dcSAndrew Turner 1461c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 14623fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14633fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14643fc155dcSAndrew Turner 1465e707c8beSRuslan Bukin /* 1466e707c8beSRuslan Bukin * If this is the first time we have used this context ask the 1467e707c8beSRuslan Bukin * interrupt controller to map memory the msi source will need. 1468e707c8beSRuslan Bukin */ 1469e707c8beSRuslan Bukin err = MSI_IOMMU_INIT(pic->pic_dev, child, &domain); 1470e707c8beSRuslan Bukin if (err != 0) 1471e707c8beSRuslan Bukin return (err); 1472e707c8beSRuslan Bukin 14733fc155dcSAndrew Turner err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc); 14743fc155dcSAndrew Turner if (err != 0) 14753fc155dcSAndrew Turner return (err); 14763fc155dcSAndrew Turner 1477e707c8beSRuslan Bukin isrc->isrc_iommu = domain; 1478895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)intr_alloc_map_data( 1479895c8b1cSMichal Meloun INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO); 1480895c8b1cSMichal Meloun msi->isrc = isrc; 1481895c8b1cSMichal Meloun *irq = intr_map_irq(pic->pic_dev, xref, (struct intr_map_data *)msi); 14823fc155dcSAndrew Turner return (0); 14833fc155dcSAndrew Turner } 14843fc155dcSAndrew Turner 14853fc155dcSAndrew Turner int 14863fc155dcSAndrew Turner intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq) 14873fc155dcSAndrew Turner { 14883fc155dcSAndrew Turner struct intr_irqsrc *isrc; 14893fc155dcSAndrew Turner struct intr_pic *pic; 1490609b0fe9SOleksandr Tymoshenko struct intr_map_data_msi *msi; 14913fc155dcSAndrew Turner int err; 14923fc155dcSAndrew Turner 1493c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 14943fc155dcSAndrew Turner if (pic == NULL) 14953fc155dcSAndrew Turner return (ESRCH); 14963fc155dcSAndrew Turner 1497c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 14983fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14993fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 15003fc155dcSAndrew Turner 1501609b0fe9SOleksandr Tymoshenko msi = (struct intr_map_data_msi *) 1502609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(irq); 1503609b0fe9SOleksandr Tymoshenko KASSERT(msi->hdr.type == INTR_MAP_DATA_MSI, 1504609b0fe9SOleksandr Tymoshenko ("%s: irq %d map data is not MSI", __func__, 1505609b0fe9SOleksandr Tymoshenko irq)); 1506609b0fe9SOleksandr Tymoshenko isrc = msi->isrc; 1507895c8b1cSMichal Meloun if (isrc == NULL) { 1508895c8b1cSMichal Meloun intr_unmap_irq(irq); 15093fc155dcSAndrew Turner return (EINVAL); 1510895c8b1cSMichal Meloun } 15113fc155dcSAndrew Turner 1512f32f0095SRuslan Bukin MSI_IOMMU_DEINIT(pic->pic_dev, child); 1513f32f0095SRuslan Bukin 15143fc155dcSAndrew Turner err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc); 1515895c8b1cSMichal Meloun intr_unmap_irq(irq); 1516895c8b1cSMichal Meloun 15173fc155dcSAndrew Turner return (err); 15183fc155dcSAndrew Turner } 15193fc155dcSAndrew Turner 15203fc155dcSAndrew Turner int 15213fc155dcSAndrew Turner intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq, 15223fc155dcSAndrew Turner uint64_t *addr, uint32_t *data) 15233fc155dcSAndrew Turner { 15243fc155dcSAndrew Turner struct intr_irqsrc *isrc; 15253fc155dcSAndrew Turner struct intr_pic *pic; 15263fc155dcSAndrew Turner int err; 15273fc155dcSAndrew Turner 1528c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 15293fc155dcSAndrew Turner if (pic == NULL) 15303fc155dcSAndrew Turner return (ESRCH); 15313fc155dcSAndrew Turner 1532c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 15333fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 15343fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 15353fc155dcSAndrew Turner 1536895c8b1cSMichal Meloun isrc = intr_map_get_isrc(irq); 15373fc155dcSAndrew Turner if (isrc == NULL) 15383fc155dcSAndrew Turner return (EINVAL); 15393fc155dcSAndrew Turner 15403fc155dcSAndrew Turner err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data); 1541e707c8beSRuslan Bukin 1542e707c8beSRuslan Bukin #ifdef IOMMU 1543e707c8beSRuslan Bukin if (isrc->isrc_iommu != NULL) 1544e707c8beSRuslan Bukin iommu_translate_msi(isrc->isrc_iommu, addr); 1545e707c8beSRuslan Bukin #endif 1546e707c8beSRuslan Bukin 15473fc155dcSAndrew Turner return (err); 15483fc155dcSAndrew Turner } 15493fc155dcSAndrew Turner 15502b3ad188SAdrian Chadd void dosoftints(void); 15512b3ad188SAdrian Chadd void 15522b3ad188SAdrian Chadd dosoftints(void) 15532b3ad188SAdrian Chadd { 15542b3ad188SAdrian Chadd } 15552b3ad188SAdrian Chadd 15562b3ad188SAdrian Chadd #ifdef SMP 15572b3ad188SAdrian Chadd /* 15582b3ad188SAdrian Chadd * Init interrupt controller on another CPU. 15592b3ad188SAdrian Chadd */ 15602b3ad188SAdrian Chadd void 15612b3ad188SAdrian Chadd intr_pic_init_secondary(void) 15622b3ad188SAdrian Chadd { 15632b3ad188SAdrian Chadd 15642b3ad188SAdrian Chadd /* 15652b3ad188SAdrian Chadd * QQQ: Only root PIC is aware of other CPUs ??? 15662b3ad188SAdrian Chadd */ 15675b70c08cSSvatopluk Kraus KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); 15682b3ad188SAdrian Chadd 15692b3ad188SAdrian Chadd //mtx_lock(&isrc_table_lock); 15705b70c08cSSvatopluk Kraus PIC_INIT_SECONDARY(intr_irq_root_dev); 15712b3ad188SAdrian Chadd //mtx_unlock(&isrc_table_lock); 15722b3ad188SAdrian Chadd } 15732b3ad188SAdrian Chadd #endif 15742b3ad188SAdrian Chadd 15752b3ad188SAdrian Chadd #ifdef DDB 1576c84c5e00SMitchell Horne DB_SHOW_COMMAND_FLAGS(irqs, db_show_irqs, DB_CMD_MEMSAFE) 15772b3ad188SAdrian Chadd { 15782b3ad188SAdrian Chadd u_int i, irqsum; 1579bff6be3eSSvatopluk Kraus u_long num; 15802b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 15812b3ad188SAdrian Chadd 1582248f0cabSOleksandr Tymoshenko for (irqsum = 0, i = 0; i < intr_nirq; i++) { 15832b3ad188SAdrian Chadd isrc = irq_sources[i]; 15842b3ad188SAdrian Chadd if (isrc == NULL) 15852b3ad188SAdrian Chadd continue; 15862b3ad188SAdrian Chadd 1587bff6be3eSSvatopluk Kraus num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; 15882b3ad188SAdrian Chadd db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, 15892b3ad188SAdrian Chadd isrc->isrc_name, isrc->isrc_cpu.__bits[0], 1590bff6be3eSSvatopluk Kraus isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); 1591bff6be3eSSvatopluk Kraus irqsum += num; 15922b3ad188SAdrian Chadd } 15932b3ad188SAdrian Chadd db_printf("irq total %u\n", irqsum); 15942b3ad188SAdrian Chadd } 15952b3ad188SAdrian Chadd #endif 1596895c8b1cSMichal Meloun 1597895c8b1cSMichal Meloun /* 1598895c8b1cSMichal Meloun * Interrupt mapping table functions. 1599895c8b1cSMichal Meloun * 1600895c8b1cSMichal Meloun * Please, keep this part separately, it can be transformed to 1601895c8b1cSMichal Meloun * extension of standard resources. 1602895c8b1cSMichal Meloun */ 1603895c8b1cSMichal Meloun struct intr_map_entry 1604895c8b1cSMichal Meloun { 1605895c8b1cSMichal Meloun device_t dev; 1606895c8b1cSMichal Meloun intptr_t xref; 1607895c8b1cSMichal Meloun struct intr_map_data *map_data; 1608895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 1609895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 1610895c8b1cSMichal Meloun /*int flags */ 1611895c8b1cSMichal Meloun }; 1612895c8b1cSMichal Meloun 1613895c8b1cSMichal Meloun /* XXX Convert irq_map[] to dynamicaly expandable one. */ 1614248f0cabSOleksandr Tymoshenko static struct intr_map_entry **irq_map; 1615a3c7da3dSElliott Mitchell static u_int irq_map_count; 1616a3c7da3dSElliott Mitchell static u_int irq_map_first_free_idx; 1617895c8b1cSMichal Meloun static struct mtx irq_map_lock; 1618895c8b1cSMichal Meloun 1619895c8b1cSMichal Meloun static struct intr_irqsrc * 1620895c8b1cSMichal Meloun intr_map_get_isrc(u_int res_id) 1621895c8b1cSMichal Meloun { 1622895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 1623895c8b1cSMichal Meloun 1624ecc8ccb4SAndrew Turner isrc = NULL; 1625895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1626ecc8ccb4SAndrew Turner if (res_id < irq_map_count && irq_map[res_id] != NULL) 1627895c8b1cSMichal Meloun isrc = irq_map[res_id]->isrc; 1628895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1629ecc8ccb4SAndrew Turner 1630895c8b1cSMichal Meloun return (isrc); 1631895c8b1cSMichal Meloun } 1632895c8b1cSMichal Meloun 1633895c8b1cSMichal Meloun static void 1634895c8b1cSMichal Meloun intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc) 1635895c8b1cSMichal Meloun { 1636895c8b1cSMichal Meloun 1637895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1638ecc8ccb4SAndrew Turner if (res_id < irq_map_count && irq_map[res_id] != NULL) 1639895c8b1cSMichal Meloun irq_map[res_id]->isrc = isrc; 1640895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1641895c8b1cSMichal Meloun } 1642895c8b1cSMichal Meloun 1643895c8b1cSMichal Meloun /* 1644895c8b1cSMichal Meloun * Get a copy of intr_map_entry data 1645895c8b1cSMichal Meloun */ 1646609b0fe9SOleksandr Tymoshenko static struct intr_map_data * 1647609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(u_int res_id) 1648609b0fe9SOleksandr Tymoshenko { 1649609b0fe9SOleksandr Tymoshenko struct intr_map_data *data; 1650609b0fe9SOleksandr Tymoshenko 1651609b0fe9SOleksandr Tymoshenko data = NULL; 1652609b0fe9SOleksandr Tymoshenko mtx_lock(&irq_map_lock); 1653609b0fe9SOleksandr Tymoshenko if (res_id >= irq_map_count || irq_map[res_id] == NULL) 1654609b0fe9SOleksandr Tymoshenko panic("Attempt to copy invalid resource id: %u\n", res_id); 1655609b0fe9SOleksandr Tymoshenko data = irq_map[res_id]->map_data; 1656609b0fe9SOleksandr Tymoshenko mtx_unlock(&irq_map_lock); 1657609b0fe9SOleksandr Tymoshenko 1658609b0fe9SOleksandr Tymoshenko return (data); 1659609b0fe9SOleksandr Tymoshenko } 1660609b0fe9SOleksandr Tymoshenko 1661609b0fe9SOleksandr Tymoshenko /* 1662609b0fe9SOleksandr Tymoshenko * Get a copy of intr_map_entry data 1663609b0fe9SOleksandr Tymoshenko */ 1664895c8b1cSMichal Meloun static void 1665895c8b1cSMichal Meloun intr_map_copy_map_data(u_int res_id, device_t *map_dev, intptr_t *map_xref, 1666895c8b1cSMichal Meloun struct intr_map_data **data) 1667895c8b1cSMichal Meloun { 1668895c8b1cSMichal Meloun size_t len; 1669895c8b1cSMichal Meloun 1670895c8b1cSMichal Meloun len = 0; 1671895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1672895c8b1cSMichal Meloun if (res_id >= irq_map_count || irq_map[res_id] == NULL) 1673895c8b1cSMichal Meloun panic("Attempt to copy invalid resource id: %u\n", res_id); 1674895c8b1cSMichal Meloun if (irq_map[res_id]->map_data != NULL) 1675895c8b1cSMichal Meloun len = irq_map[res_id]->map_data->len; 1676895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1677895c8b1cSMichal Meloun 1678895c8b1cSMichal Meloun if (len == 0) 1679895c8b1cSMichal Meloun *data = NULL; 1680895c8b1cSMichal Meloun else 1681895c8b1cSMichal Meloun *data = malloc(len, M_INTRNG, M_WAITOK | M_ZERO); 1682895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1683895c8b1cSMichal Meloun if (irq_map[res_id] == NULL) 1684895c8b1cSMichal Meloun panic("Attempt to copy invalid resource id: %u\n", res_id); 1685895c8b1cSMichal Meloun if (len != 0) { 1686895c8b1cSMichal Meloun if (len != irq_map[res_id]->map_data->len) 1687895c8b1cSMichal Meloun panic("Resource id: %u has changed.\n", res_id); 1688895c8b1cSMichal Meloun memcpy(*data, irq_map[res_id]->map_data, len); 1689895c8b1cSMichal Meloun } 1690895c8b1cSMichal Meloun *map_dev = irq_map[res_id]->dev; 1691895c8b1cSMichal Meloun *map_xref = irq_map[res_id]->xref; 1692895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1693895c8b1cSMichal Meloun } 1694895c8b1cSMichal Meloun 1695895c8b1cSMichal Meloun /* 1696895c8b1cSMichal Meloun * Allocate and fill new entry in irq_map table. 1697895c8b1cSMichal Meloun */ 1698895c8b1cSMichal Meloun u_int 1699895c8b1cSMichal Meloun intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data) 1700895c8b1cSMichal Meloun { 1701895c8b1cSMichal Meloun u_int i; 1702895c8b1cSMichal Meloun struct intr_map_entry *entry; 1703895c8b1cSMichal Meloun 1704895c8b1cSMichal Meloun /* Prepare new entry first. */ 1705895c8b1cSMichal Meloun entry = malloc(sizeof(*entry), M_INTRNG, M_WAITOK | M_ZERO); 1706895c8b1cSMichal Meloun 1707895c8b1cSMichal Meloun entry->dev = dev; 1708895c8b1cSMichal Meloun entry->xref = xref; 1709895c8b1cSMichal Meloun entry->map_data = data; 1710895c8b1cSMichal Meloun entry->isrc = NULL; 1711895c8b1cSMichal Meloun 1712895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1713895c8b1cSMichal Meloun for (i = irq_map_first_free_idx; i < irq_map_count; i++) { 1714895c8b1cSMichal Meloun if (irq_map[i] == NULL) { 1715895c8b1cSMichal Meloun irq_map[i] = entry; 1716895c8b1cSMichal Meloun irq_map_first_free_idx = i + 1; 1717895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1718895c8b1cSMichal Meloun return (i); 1719895c8b1cSMichal Meloun } 1720895c8b1cSMichal Meloun } 17219beb195fSAndrew Turner for (i = 0; i < irq_map_first_free_idx; i++) { 17229beb195fSAndrew Turner if (irq_map[i] == NULL) { 17239beb195fSAndrew Turner irq_map[i] = entry; 17249beb195fSAndrew Turner irq_map_first_free_idx = i + 1; 17259beb195fSAndrew Turner mtx_unlock(&irq_map_lock); 17269beb195fSAndrew Turner return (i); 17279beb195fSAndrew Turner } 17289beb195fSAndrew Turner } 1729895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1730895c8b1cSMichal Meloun 1731895c8b1cSMichal Meloun /* XXX Expand irq_map table */ 1732895c8b1cSMichal Meloun panic("IRQ mapping table is full."); 1733895c8b1cSMichal Meloun } 1734895c8b1cSMichal Meloun 1735895c8b1cSMichal Meloun /* 1736895c8b1cSMichal Meloun * Remove and free mapping entry. 1737895c8b1cSMichal Meloun */ 1738895c8b1cSMichal Meloun void 1739895c8b1cSMichal Meloun intr_unmap_irq(u_int res_id) 1740895c8b1cSMichal Meloun { 1741895c8b1cSMichal Meloun struct intr_map_entry *entry; 1742895c8b1cSMichal Meloun 1743895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1744895c8b1cSMichal Meloun if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL)) 1745895c8b1cSMichal Meloun panic("Attempt to unmap invalid resource id: %u\n", res_id); 1746895c8b1cSMichal Meloun entry = irq_map[res_id]; 1747895c8b1cSMichal Meloun irq_map[res_id] = NULL; 1748895c8b1cSMichal Meloun irq_map_first_free_idx = res_id; 1749895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1750895c8b1cSMichal Meloun intr_free_intr_map_data(entry->map_data); 1751895c8b1cSMichal Meloun free(entry, M_INTRNG); 1752895c8b1cSMichal Meloun } 1753895c8b1cSMichal Meloun 1754895c8b1cSMichal Meloun /* 1755895c8b1cSMichal Meloun * Clone mapping entry. 1756895c8b1cSMichal Meloun */ 1757895c8b1cSMichal Meloun u_int 1758895c8b1cSMichal Meloun intr_map_clone_irq(u_int old_res_id) 1759895c8b1cSMichal Meloun { 1760895c8b1cSMichal Meloun device_t map_dev; 1761895c8b1cSMichal Meloun intptr_t map_xref; 1762895c8b1cSMichal Meloun struct intr_map_data *data; 1763895c8b1cSMichal Meloun 1764895c8b1cSMichal Meloun intr_map_copy_map_data(old_res_id, &map_dev, &map_xref, &data); 1765895c8b1cSMichal Meloun return (intr_map_irq(map_dev, map_xref, data)); 1766895c8b1cSMichal Meloun } 1767895c8b1cSMichal Meloun 1768895c8b1cSMichal Meloun static void 1769895c8b1cSMichal Meloun intr_map_init(void *dummy __unused) 1770895c8b1cSMichal Meloun { 1771895c8b1cSMichal Meloun 1772895c8b1cSMichal Meloun mtx_init(&irq_map_lock, "intr map table", NULL, MTX_DEF); 1773248f0cabSOleksandr Tymoshenko 1774248f0cabSOleksandr Tymoshenko irq_map_count = 2 * intr_nirq; 1775248f0cabSOleksandr Tymoshenko irq_map = mallocarray(irq_map_count, sizeof(struct intr_map_entry*), 1776248f0cabSOleksandr Tymoshenko M_INTRNG, M_WAITOK | M_ZERO); 1777895c8b1cSMichal Meloun } 1778895c8b1cSMichal Meloun SYSINIT(intr_map_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_map_init, NULL); 1779