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 * 34895c8b1cSMichal Meloun * TODO: - add support for disconnected PICs. 35895c8b1cSMichal Meloun * - to support IPI (PPI) enabling on other CPUs if already started. 36895c8b1cSMichal Meloun * - to complete things for removable PICs. 372b3ad188SAdrian Chadd */ 382b3ad188SAdrian Chadd 392b3ad188SAdrian Chadd #include "opt_ddb.h" 40df7a2251SAndrew Turner #include "opt_hwpmc_hooks.h" 41e707c8beSRuslan Bukin #include "opt_iommu.h" 422b3ad188SAdrian Chadd 432b3ad188SAdrian Chadd #include <sys/param.h> 442b3ad188SAdrian Chadd #include <sys/systm.h> 452b3ad188SAdrian Chadd #include <sys/kernel.h> 46e2e050c8SConrad Meyer #include <sys/lock.h> 47e2e050c8SConrad Meyer #include <sys/mutex.h> 482b3ad188SAdrian Chadd #include <sys/syslog.h> 492b3ad188SAdrian Chadd #include <sys/malloc.h> 502b3ad188SAdrian Chadd #include <sys/proc.h> 512b3ad188SAdrian Chadd #include <sys/queue.h> 522b3ad188SAdrian Chadd #include <sys/bus.h> 532b3ad188SAdrian Chadd #include <sys/interrupt.h> 54e707c8beSRuslan Bukin #include <sys/taskqueue.h> 55e707c8beSRuslan Bukin #include <sys/tree.h> 562b3ad188SAdrian Chadd #include <sys/conf.h> 572b3ad188SAdrian Chadd #include <sys/cpuset.h> 586b42a1f4SAndrew Turner #include <sys/rman.h> 592b3ad188SAdrian Chadd #include <sys/sched.h> 602b3ad188SAdrian Chadd #include <sys/smp.h> 619ed01c32SGleb Smirnoff #include <sys/vmmeter.h> 62df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 63df7a2251SAndrew Turner #include <sys/pmckern.h> 64df7a2251SAndrew Turner #endif 65df7a2251SAndrew Turner 662b3ad188SAdrian Chadd #include <machine/atomic.h> 672b3ad188SAdrian Chadd #include <machine/intr.h> 682b3ad188SAdrian Chadd #include <machine/cpu.h> 692b3ad188SAdrian Chadd #include <machine/smp.h> 702b3ad188SAdrian Chadd #include <machine/stdarg.h> 712b3ad188SAdrian Chadd 722b3ad188SAdrian Chadd #ifdef DDB 732b3ad188SAdrian Chadd #include <ddb/ddb.h> 742b3ad188SAdrian Chadd #endif 752b3ad188SAdrian Chadd 76e707c8beSRuslan Bukin #ifdef IOMMU 77e707c8beSRuslan Bukin #include <dev/iommu/iommu_msi.h> 78e707c8beSRuslan Bukin #endif 79e707c8beSRuslan Bukin 802b3ad188SAdrian Chadd #include "pic_if.h" 813fc155dcSAndrew Turner #include "msi_if.h" 822b3ad188SAdrian Chadd 832b3ad188SAdrian Chadd #define INTRNAME_LEN (2*MAXCOMLEN + 1) 842b3ad188SAdrian Chadd 852b3ad188SAdrian Chadd #ifdef DEBUG 862b3ad188SAdrian Chadd #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 872b3ad188SAdrian Chadd printf(fmt,##args); } while (0) 882b3ad188SAdrian Chadd #else 892b3ad188SAdrian Chadd #define debugf(fmt, args...) 902b3ad188SAdrian Chadd #endif 912b3ad188SAdrian Chadd 922b3ad188SAdrian Chadd MALLOC_DECLARE(M_INTRNG); 932b3ad188SAdrian Chadd MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); 942b3ad188SAdrian Chadd 952b3ad188SAdrian Chadd /* Main interrupt handler called from assembler -> 'hidden' for C code. */ 962b3ad188SAdrian Chadd void intr_irq_handler(struct trapframe *tf); 972b3ad188SAdrian Chadd 982b3ad188SAdrian Chadd /* Root interrupt controller stuff. */ 995b70c08cSSvatopluk Kraus device_t intr_irq_root_dev; 1002b3ad188SAdrian Chadd static intr_irq_filter_t *irq_root_filter; 1012b3ad188SAdrian Chadd static void *irq_root_arg; 1022b3ad188SAdrian Chadd static u_int irq_root_ipicount; 1032b3ad188SAdrian Chadd 104d1605cdaSAndrew Turner struct intr_pic_child { 105d1605cdaSAndrew Turner SLIST_ENTRY(intr_pic_child) pc_next; 106d1605cdaSAndrew Turner struct intr_pic *pc_pic; 107d1605cdaSAndrew Turner intr_child_irq_filter_t *pc_filter; 108d1605cdaSAndrew Turner void *pc_filter_arg; 109d1605cdaSAndrew Turner uintptr_t pc_start; 110d1605cdaSAndrew Turner uintptr_t pc_length; 111d1605cdaSAndrew Turner }; 112d1605cdaSAndrew Turner 1132b3ad188SAdrian Chadd /* Interrupt controller definition. */ 1142b3ad188SAdrian Chadd struct intr_pic { 1152b3ad188SAdrian Chadd SLIST_ENTRY(intr_pic) pic_next; 1162b3ad188SAdrian Chadd intptr_t pic_xref; /* hardware identification */ 1172b3ad188SAdrian Chadd device_t pic_dev; 118c0d52370SAndrew Turner /* Only one of FLAG_PIC or FLAG_MSI may be set */ 1193fc155dcSAndrew Turner #define FLAG_PIC (1 << 0) 1203fc155dcSAndrew Turner #define FLAG_MSI (1 << 1) 121c0d52370SAndrew Turner #define FLAG_TYPE_MASK (FLAG_PIC | FLAG_MSI) 1223fc155dcSAndrew Turner u_int pic_flags; 123d1605cdaSAndrew Turner struct mtx pic_child_lock; 124d1605cdaSAndrew Turner SLIST_HEAD(, intr_pic_child) pic_children; 1252b3ad188SAdrian Chadd }; 1262b3ad188SAdrian Chadd 1272b3ad188SAdrian Chadd static struct mtx pic_list_lock; 1282b3ad188SAdrian Chadd static SLIST_HEAD(, intr_pic) pic_list; 1292b3ad188SAdrian Chadd 130c0d52370SAndrew Turner static struct intr_pic *pic_lookup(device_t dev, intptr_t xref, int flags); 1312b3ad188SAdrian Chadd 1322b3ad188SAdrian Chadd /* Interrupt source definition. */ 1332b3ad188SAdrian Chadd static struct mtx isrc_table_lock; 1342b3ad188SAdrian Chadd static struct intr_irqsrc *irq_sources[NIRQ]; 1352b3ad188SAdrian Chadd u_int irq_next_free; 1362b3ad188SAdrian Chadd 1372b3ad188SAdrian Chadd #ifdef SMP 138dc425090SMitchell Horne #ifdef EARLY_AP_STARTUP 139dc425090SMitchell Horne static bool irq_assign_cpu = true; 140dc425090SMitchell Horne #else 141dc425090SMitchell Horne static bool irq_assign_cpu = false; 142dc425090SMitchell Horne #endif 1432b3ad188SAdrian Chadd #endif 1442b3ad188SAdrian Chadd 1452b3ad188SAdrian Chadd /* 1462b3ad188SAdrian Chadd * - 2 counters for each I/O interrupt. 1472b3ad188SAdrian Chadd * - MAXCPU counters for each IPI counters for SMP. 1482b3ad188SAdrian Chadd */ 1492b3ad188SAdrian Chadd #ifdef SMP 1502b3ad188SAdrian Chadd #define INTRCNT_COUNT (NIRQ * 2 + INTR_IPI_COUNT * MAXCPU) 1512b3ad188SAdrian Chadd #else 1522b3ad188SAdrian Chadd #define INTRCNT_COUNT (NIRQ * 2) 1532b3ad188SAdrian Chadd #endif 1542b3ad188SAdrian Chadd 1552b3ad188SAdrian Chadd /* Data for MI statistics reporting. */ 1562b3ad188SAdrian Chadd u_long intrcnt[INTRCNT_COUNT]; 1572b3ad188SAdrian Chadd char intrnames[INTRCNT_COUNT * INTRNAME_LEN]; 1582b3ad188SAdrian Chadd size_t sintrcnt = sizeof(intrcnt); 1592b3ad188SAdrian Chadd size_t sintrnames = sizeof(intrnames); 1602b3ad188SAdrian Chadd static u_int intrcnt_index; 1612b3ad188SAdrian Chadd 162895c8b1cSMichal Meloun static struct intr_irqsrc *intr_map_get_isrc(u_int res_id); 163895c8b1cSMichal Meloun static void intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc); 164609b0fe9SOleksandr Tymoshenko static struct intr_map_data * intr_map_get_map_data(u_int res_id); 165895c8b1cSMichal Meloun static void intr_map_copy_map_data(u_int res_id, device_t *dev, intptr_t *xref, 166895c8b1cSMichal Meloun struct intr_map_data **data); 167895c8b1cSMichal Meloun 1682b3ad188SAdrian Chadd /* 1692b3ad188SAdrian Chadd * Interrupt framework initialization routine. 1702b3ad188SAdrian Chadd */ 1712b3ad188SAdrian Chadd static void 1722b3ad188SAdrian Chadd intr_irq_init(void *dummy __unused) 1732b3ad188SAdrian Chadd { 1742b3ad188SAdrian Chadd 1752b3ad188SAdrian Chadd SLIST_INIT(&pic_list); 1762b3ad188SAdrian Chadd mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); 1773fc155dcSAndrew Turner 1782b3ad188SAdrian Chadd mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); 1792b3ad188SAdrian Chadd } 1802b3ad188SAdrian Chadd SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); 1812b3ad188SAdrian Chadd 1822b3ad188SAdrian Chadd static void 1832b3ad188SAdrian Chadd intrcnt_setname(const char *name, int index) 1842b3ad188SAdrian Chadd { 1852b3ad188SAdrian Chadd 1862b3ad188SAdrian Chadd snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s", 1872b3ad188SAdrian Chadd INTRNAME_LEN - 1, name); 1882b3ad188SAdrian Chadd } 1892b3ad188SAdrian Chadd 1902b3ad188SAdrian Chadd /* 1912b3ad188SAdrian Chadd * Update name for interrupt source with interrupt event. 1922b3ad188SAdrian Chadd */ 1932b3ad188SAdrian Chadd static void 1942b3ad188SAdrian Chadd intrcnt_updatename(struct intr_irqsrc *isrc) 1952b3ad188SAdrian Chadd { 1962b3ad188SAdrian Chadd 1972b3ad188SAdrian Chadd /* QQQ: What about stray counter name? */ 1982b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 1992b3ad188SAdrian Chadd intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index); 2002b3ad188SAdrian Chadd } 2012b3ad188SAdrian Chadd 2022b3ad188SAdrian Chadd /* 2032b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counter increment. 2042b3ad188SAdrian Chadd */ 2052b3ad188SAdrian Chadd static inline void 2062b3ad188SAdrian Chadd isrc_increment_count(struct intr_irqsrc *isrc) 2072b3ad188SAdrian Chadd { 2082b3ad188SAdrian Chadd 209bff6be3eSSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_PPI) 210bff6be3eSSvatopluk Kraus atomic_add_long(&isrc->isrc_count[0], 1); 211bff6be3eSSvatopluk Kraus else 2122b3ad188SAdrian Chadd isrc->isrc_count[0]++; 2132b3ad188SAdrian Chadd } 2142b3ad188SAdrian Chadd 2152b3ad188SAdrian Chadd /* 2162b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt stray counter increment. 2172b3ad188SAdrian Chadd */ 2182b3ad188SAdrian Chadd static inline void 2192b3ad188SAdrian Chadd isrc_increment_straycount(struct intr_irqsrc *isrc) 2202b3ad188SAdrian Chadd { 2212b3ad188SAdrian Chadd 2222b3ad188SAdrian Chadd isrc->isrc_count[1]++; 2232b3ad188SAdrian Chadd } 2242b3ad188SAdrian Chadd 2252b3ad188SAdrian Chadd /* 2262b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt name update. 2272b3ad188SAdrian Chadd */ 2282b3ad188SAdrian Chadd static void 2292b3ad188SAdrian Chadd isrc_update_name(struct intr_irqsrc *isrc, const char *name) 2302b3ad188SAdrian Chadd { 2312b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2322b3ad188SAdrian Chadd 2332b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2342b3ad188SAdrian Chadd 2352b3ad188SAdrian Chadd if (name != NULL) { 2362b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); 2372b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2382b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, 2392b3ad188SAdrian Chadd name); 2402b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2412b3ad188SAdrian Chadd } else { 2422b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); 2432b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2442b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); 2452b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2462b3ad188SAdrian Chadd } 2472b3ad188SAdrian Chadd } 2482b3ad188SAdrian Chadd 2492b3ad188SAdrian Chadd /* 2502b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counters setup. 2512b3ad188SAdrian Chadd */ 2522b3ad188SAdrian Chadd static void 2532b3ad188SAdrian Chadd isrc_setup_counters(struct intr_irqsrc *isrc) 2542b3ad188SAdrian Chadd { 2552b3ad188SAdrian Chadd u_int index; 2562b3ad188SAdrian Chadd 2572b3ad188SAdrian Chadd /* 2582b3ad188SAdrian Chadd * XXX - it does not work well with removable controllers and 2592b3ad188SAdrian Chadd * interrupt sources !!! 2602b3ad188SAdrian Chadd */ 2612b3ad188SAdrian Chadd index = atomic_fetchadd_int(&intrcnt_index, 2); 2622b3ad188SAdrian Chadd isrc->isrc_index = index; 2632b3ad188SAdrian Chadd isrc->isrc_count = &intrcnt[index]; 2642b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 2652b3ad188SAdrian Chadd } 2662b3ad188SAdrian Chadd 267bff6be3eSSvatopluk Kraus /* 268bff6be3eSSvatopluk Kraus * Virtualization for interrupt source interrupt counters release. 269bff6be3eSSvatopluk Kraus */ 270bff6be3eSSvatopluk Kraus static void 271bff6be3eSSvatopluk Kraus isrc_release_counters(struct intr_irqsrc *isrc) 272bff6be3eSSvatopluk Kraus { 273bff6be3eSSvatopluk Kraus 274bff6be3eSSvatopluk Kraus panic("%s: not implemented", __func__); 275bff6be3eSSvatopluk Kraus } 276bff6be3eSSvatopluk Kraus 2772b3ad188SAdrian Chadd #ifdef SMP 2782b3ad188SAdrian Chadd /* 2792b3ad188SAdrian Chadd * Virtualization for interrupt source IPI counters setup. 2802b3ad188SAdrian Chadd */ 2815b70c08cSSvatopluk Kraus u_long * 2825b70c08cSSvatopluk Kraus intr_ipi_setup_counters(const char *name) 2832b3ad188SAdrian Chadd { 2842b3ad188SAdrian Chadd u_int index, i; 2852b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2862b3ad188SAdrian Chadd 2872b3ad188SAdrian Chadd index = atomic_fetchadd_int(&intrcnt_index, MAXCPU); 2882b3ad188SAdrian Chadd for (i = 0; i < MAXCPU; i++) { 2892b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); 2902b3ad188SAdrian Chadd intrcnt_setname(str, index + i); 2912b3ad188SAdrian Chadd } 2925b70c08cSSvatopluk Kraus return (&intrcnt[index]); 2932b3ad188SAdrian Chadd } 2942b3ad188SAdrian Chadd #endif 2952b3ad188SAdrian Chadd 2962b3ad188SAdrian Chadd /* 2972b3ad188SAdrian Chadd * Main interrupt dispatch handler. It's called straight 2982b3ad188SAdrian Chadd * from the assembler, where CPU interrupt is served. 2992b3ad188SAdrian Chadd */ 3002b3ad188SAdrian Chadd void 3012b3ad188SAdrian Chadd intr_irq_handler(struct trapframe *tf) 3022b3ad188SAdrian Chadd { 3032b3ad188SAdrian Chadd struct trapframe * oldframe; 3042b3ad188SAdrian Chadd struct thread * td; 3052b3ad188SAdrian Chadd 3062b3ad188SAdrian Chadd KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__)); 3072b3ad188SAdrian Chadd 30883c9dea1SGleb Smirnoff VM_CNT_INC(v_intr); 3092b3ad188SAdrian Chadd critical_enter(); 3102b3ad188SAdrian Chadd td = curthread; 3112b3ad188SAdrian Chadd oldframe = td->td_intr_frame; 3122b3ad188SAdrian Chadd td->td_intr_frame = tf; 3132b3ad188SAdrian Chadd irq_root_filter(irq_root_arg); 3142b3ad188SAdrian Chadd td->td_intr_frame = oldframe; 3152b3ad188SAdrian Chadd critical_exit(); 316df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 317974692e3SAndrew Turner if (pmc_hook && TRAPF_USERMODE(tf) && 318974692e3SAndrew Turner (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) 319df7a2251SAndrew Turner pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf); 320df7a2251SAndrew Turner #endif 3212b3ad188SAdrian Chadd } 3222b3ad188SAdrian Chadd 323d1605cdaSAndrew Turner int 324d1605cdaSAndrew Turner intr_child_irq_handler(struct intr_pic *parent, uintptr_t irq) 325d1605cdaSAndrew Turner { 326d1605cdaSAndrew Turner struct intr_pic_child *child; 327d1605cdaSAndrew Turner bool found; 328d1605cdaSAndrew Turner 329d1605cdaSAndrew Turner found = false; 330d1605cdaSAndrew Turner mtx_lock_spin(&parent->pic_child_lock); 331d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent->pic_children, pc_next) { 332d1605cdaSAndrew Turner if (child->pc_start <= irq && 333d1605cdaSAndrew Turner irq < (child->pc_start + child->pc_length)) { 334d1605cdaSAndrew Turner found = true; 335d1605cdaSAndrew Turner break; 336d1605cdaSAndrew Turner } 337d1605cdaSAndrew Turner } 338d1605cdaSAndrew Turner mtx_unlock_spin(&parent->pic_child_lock); 339d1605cdaSAndrew Turner 340d1605cdaSAndrew Turner if (found) 341d1605cdaSAndrew Turner return (child->pc_filter(child->pc_filter_arg, irq)); 342d1605cdaSAndrew Turner 343d1605cdaSAndrew Turner return (FILTER_STRAY); 344d1605cdaSAndrew Turner } 345d1605cdaSAndrew Turner 3462b3ad188SAdrian Chadd /* 3472b3ad188SAdrian Chadd * interrupt controller dispatch function for interrupts. It should 3482b3ad188SAdrian Chadd * be called straight from the interrupt controller, when associated interrupt 3492b3ad188SAdrian Chadd * source is learned. 3502b3ad188SAdrian Chadd */ 351bff6be3eSSvatopluk Kraus int 352bff6be3eSSvatopluk Kraus intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) 3532b3ad188SAdrian Chadd { 3542b3ad188SAdrian Chadd 3552b3ad188SAdrian Chadd KASSERT(isrc != NULL, ("%s: no source", __func__)); 3562b3ad188SAdrian Chadd 3572b3ad188SAdrian Chadd isrc_increment_count(isrc); 3582b3ad188SAdrian Chadd 3592b3ad188SAdrian Chadd #ifdef INTR_SOLO 3602b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 3612b3ad188SAdrian Chadd int error; 3622b3ad188SAdrian Chadd error = isrc->isrc_filter(isrc->isrc_arg, tf); 3632b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 3642b3ad188SAdrian Chadd if (error == FILTER_HANDLED) 365bff6be3eSSvatopluk Kraus return (0); 3662b3ad188SAdrian Chadd } else 3672b3ad188SAdrian Chadd #endif 3682b3ad188SAdrian Chadd if (isrc->isrc_event != NULL) { 3692b3ad188SAdrian Chadd if (intr_event_handle(isrc->isrc_event, tf) == 0) 370bff6be3eSSvatopluk Kraus return (0); 3712b3ad188SAdrian Chadd } 3722b3ad188SAdrian Chadd 3732b3ad188SAdrian Chadd isrc_increment_straycount(isrc); 374bff6be3eSSvatopluk Kraus return (EINVAL); 3752b3ad188SAdrian Chadd } 3762b3ad188SAdrian Chadd 3772b3ad188SAdrian Chadd /* 3782b3ad188SAdrian Chadd * Alloc unique interrupt number (resource handle) for interrupt source. 3792b3ad188SAdrian Chadd * 3802b3ad188SAdrian Chadd * There could be various strategies how to allocate free interrupt number 3812b3ad188SAdrian Chadd * (resource handle) for new interrupt source. 3822b3ad188SAdrian Chadd * 3832b3ad188SAdrian Chadd * 1. Handles are always allocated forward, so handles are not recycled 3842b3ad188SAdrian Chadd * immediately. However, if only one free handle left which is reused 3852b3ad188SAdrian Chadd * constantly... 3862b3ad188SAdrian Chadd */ 387bff6be3eSSvatopluk Kraus static inline int 388bff6be3eSSvatopluk Kraus isrc_alloc_irq(struct intr_irqsrc *isrc) 3892b3ad188SAdrian Chadd { 3902b3ad188SAdrian Chadd u_int maxirqs, irq; 3912b3ad188SAdrian Chadd 3922b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 3932b3ad188SAdrian Chadd 3942b3ad188SAdrian Chadd maxirqs = nitems(irq_sources); 3952b3ad188SAdrian Chadd if (irq_next_free >= maxirqs) 3962b3ad188SAdrian Chadd return (ENOSPC); 3972b3ad188SAdrian Chadd 3982b3ad188SAdrian Chadd for (irq = irq_next_free; irq < maxirqs; irq++) { 3992b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 4002b3ad188SAdrian Chadd goto found; 4012b3ad188SAdrian Chadd } 4022b3ad188SAdrian Chadd for (irq = 0; irq < irq_next_free; irq++) { 4032b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 4042b3ad188SAdrian Chadd goto found; 4052b3ad188SAdrian Chadd } 4062b3ad188SAdrian Chadd 4072b3ad188SAdrian Chadd irq_next_free = maxirqs; 4082b3ad188SAdrian Chadd return (ENOSPC); 4092b3ad188SAdrian Chadd 4102b3ad188SAdrian Chadd found: 4112b3ad188SAdrian Chadd isrc->isrc_irq = irq; 4122b3ad188SAdrian Chadd irq_sources[irq] = isrc; 4132b3ad188SAdrian Chadd 4142b3ad188SAdrian Chadd irq_next_free = irq + 1; 4152b3ad188SAdrian Chadd if (irq_next_free >= maxirqs) 4162b3ad188SAdrian Chadd irq_next_free = 0; 4172b3ad188SAdrian Chadd return (0); 4182b3ad188SAdrian Chadd } 419bff6be3eSSvatopluk Kraus 4202b3ad188SAdrian Chadd /* 4212b3ad188SAdrian Chadd * Free unique interrupt number (resource handle) from interrupt source. 4222b3ad188SAdrian Chadd */ 423bff6be3eSSvatopluk Kraus static inline int 4242b3ad188SAdrian Chadd isrc_free_irq(struct intr_irqsrc *isrc) 4252b3ad188SAdrian Chadd { 4262b3ad188SAdrian Chadd 427bff6be3eSSvatopluk Kraus mtx_assert(&isrc_table_lock, MA_OWNED); 4282b3ad188SAdrian Chadd 429bff6be3eSSvatopluk Kraus if (isrc->isrc_irq >= nitems(irq_sources)) 4302b3ad188SAdrian Chadd return (EINVAL); 431bff6be3eSSvatopluk Kraus if (irq_sources[isrc->isrc_irq] != isrc) 4322b3ad188SAdrian Chadd return (EINVAL); 4332b3ad188SAdrian Chadd 4342b3ad188SAdrian Chadd irq_sources[isrc->isrc_irq] = NULL; 4358442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 4362b3ad188SAdrian Chadd return (0); 4372b3ad188SAdrian Chadd } 438bff6be3eSSvatopluk Kraus 4392b3ad188SAdrian Chadd /* 440bff6be3eSSvatopluk Kraus * Initialize interrupt source and register it into global interrupt table. 4412b3ad188SAdrian Chadd */ 442bff6be3eSSvatopluk Kraus int 443bff6be3eSSvatopluk Kraus intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, 444bff6be3eSSvatopluk Kraus const char *fmt, ...) 4452b3ad188SAdrian Chadd { 446bff6be3eSSvatopluk Kraus int error; 447bff6be3eSSvatopluk Kraus va_list ap; 4482b3ad188SAdrian Chadd 449bff6be3eSSvatopluk Kraus bzero(isrc, sizeof(struct intr_irqsrc)); 450bff6be3eSSvatopluk Kraus isrc->isrc_dev = dev; 4518442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 452bff6be3eSSvatopluk Kraus isrc->isrc_flags = flags; 4532b3ad188SAdrian Chadd 454bff6be3eSSvatopluk Kraus va_start(ap, fmt); 455bff6be3eSSvatopluk Kraus vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); 456bff6be3eSSvatopluk Kraus va_end(ap); 457bff6be3eSSvatopluk Kraus 458bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 459bff6be3eSSvatopluk Kraus error = isrc_alloc_irq(isrc); 460bff6be3eSSvatopluk Kraus if (error != 0) { 461bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 462bff6be3eSSvatopluk Kraus return (error); 4632b3ad188SAdrian Chadd } 464bff6be3eSSvatopluk Kraus /* 465bff6be3eSSvatopluk Kraus * Setup interrupt counters, but not for IPI sources. Those are setup 466bff6be3eSSvatopluk Kraus * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust 467bff6be3eSSvatopluk Kraus * our counter pool. 468bff6be3eSSvatopluk Kraus */ 469bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 470bff6be3eSSvatopluk Kraus isrc_setup_counters(isrc); 471bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 472bff6be3eSSvatopluk Kraus return (0); 4732b3ad188SAdrian Chadd } 4742b3ad188SAdrian Chadd 4752b3ad188SAdrian Chadd /* 476bff6be3eSSvatopluk Kraus * Deregister interrupt source from global interrupt table. 477bff6be3eSSvatopluk Kraus */ 478bff6be3eSSvatopluk Kraus int 479bff6be3eSSvatopluk Kraus intr_isrc_deregister(struct intr_irqsrc *isrc) 480bff6be3eSSvatopluk Kraus { 481bff6be3eSSvatopluk Kraus int error; 482bff6be3eSSvatopluk Kraus 483bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 484bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 485bff6be3eSSvatopluk Kraus isrc_release_counters(isrc); 486bff6be3eSSvatopluk Kraus error = isrc_free_irq(isrc); 487bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 488bff6be3eSSvatopluk Kraus return (error); 489bff6be3eSSvatopluk Kraus } 490bff6be3eSSvatopluk Kraus 4915b613c19SSvatopluk Kraus #ifdef SMP 4925b613c19SSvatopluk Kraus /* 4935b613c19SSvatopluk Kraus * A support function for a PIC to decide if provided ISRC should be inited 4945b613c19SSvatopluk Kraus * on given cpu. The logic of INTR_ISRCF_BOUND flag and isrc_cpu member of 4955b613c19SSvatopluk Kraus * struct intr_irqsrc is the following: 4965b613c19SSvatopluk Kraus * 4975b613c19SSvatopluk Kraus * If INTR_ISRCF_BOUND is set, the ISRC should be inited only on cpus 4985b613c19SSvatopluk Kraus * set in isrc_cpu. If not, the ISRC should be inited on every cpu and 4995b613c19SSvatopluk Kraus * isrc_cpu is kept consistent with it. Thus isrc_cpu is always correct. 5005b613c19SSvatopluk Kraus */ 5015b613c19SSvatopluk Kraus bool 5025b613c19SSvatopluk Kraus intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu) 5035b613c19SSvatopluk Kraus { 5045b613c19SSvatopluk Kraus 5055b613c19SSvatopluk Kraus if (isrc->isrc_handlers == 0) 5065b613c19SSvatopluk Kraus return (false); 5075b613c19SSvatopluk Kraus if ((isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) == 0) 5085b613c19SSvatopluk Kraus return (false); 5095b613c19SSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_BOUND) 5105b613c19SSvatopluk Kraus return (CPU_ISSET(cpu, &isrc->isrc_cpu)); 5115b613c19SSvatopluk Kraus 5125b613c19SSvatopluk Kraus CPU_SET(cpu, &isrc->isrc_cpu); 5135b613c19SSvatopluk Kraus return (true); 5145b613c19SSvatopluk Kraus } 5155b613c19SSvatopluk Kraus #endif 5165b613c19SSvatopluk Kraus 5172b3ad188SAdrian Chadd #ifdef INTR_SOLO 5182b3ad188SAdrian Chadd /* 5192b3ad188SAdrian Chadd * Setup filter into interrupt source. 5202b3ad188SAdrian Chadd */ 5212b3ad188SAdrian Chadd static int 5222b3ad188SAdrian Chadd iscr_setup_filter(struct intr_irqsrc *isrc, const char *name, 5232b3ad188SAdrian Chadd intr_irq_filter_t *filter, void *arg, void **cookiep) 5242b3ad188SAdrian Chadd { 5252b3ad188SAdrian Chadd 5262b3ad188SAdrian Chadd if (filter == NULL) 5272b3ad188SAdrian Chadd return (EINVAL); 5282b3ad188SAdrian Chadd 5292b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 5302b3ad188SAdrian Chadd /* 5312b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 5322b3ad188SAdrian Chadd * how we handle interrupt sources. 5332b3ad188SAdrian Chadd */ 5342b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 5352b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 5362b3ad188SAdrian Chadd return (EBUSY); 5372b3ad188SAdrian Chadd } 5382b3ad188SAdrian Chadd isrc->isrc_filter = filter; 5392b3ad188SAdrian Chadd isrc->isrc_arg = arg; 5402b3ad188SAdrian Chadd isrc_update_name(isrc, name); 5412b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 5422b3ad188SAdrian Chadd 5432b3ad188SAdrian Chadd *cookiep = isrc; 5442b3ad188SAdrian Chadd return (0); 5452b3ad188SAdrian Chadd } 5462b3ad188SAdrian Chadd #endif 5472b3ad188SAdrian Chadd 5482b3ad188SAdrian Chadd /* 5492b3ad188SAdrian Chadd * Interrupt source pre_ithread method for MI interrupt framework. 5502b3ad188SAdrian Chadd */ 5512b3ad188SAdrian Chadd static void 5522b3ad188SAdrian Chadd intr_isrc_pre_ithread(void *arg) 5532b3ad188SAdrian Chadd { 5542b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 5552b3ad188SAdrian Chadd 5562b3ad188SAdrian Chadd PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); 5572b3ad188SAdrian Chadd } 5582b3ad188SAdrian Chadd 5592b3ad188SAdrian Chadd /* 5602b3ad188SAdrian Chadd * Interrupt source post_ithread method for MI interrupt framework. 5612b3ad188SAdrian Chadd */ 5622b3ad188SAdrian Chadd static void 5632b3ad188SAdrian Chadd intr_isrc_post_ithread(void *arg) 5642b3ad188SAdrian Chadd { 5652b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 5662b3ad188SAdrian Chadd 5672b3ad188SAdrian Chadd PIC_POST_ITHREAD(isrc->isrc_dev, isrc); 5682b3ad188SAdrian Chadd } 5692b3ad188SAdrian Chadd 5702b3ad188SAdrian Chadd /* 5712b3ad188SAdrian Chadd * Interrupt source post_filter method for MI interrupt framework. 5722b3ad188SAdrian Chadd */ 5732b3ad188SAdrian Chadd static void 5742b3ad188SAdrian Chadd intr_isrc_post_filter(void *arg) 5752b3ad188SAdrian Chadd { 5762b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 5772b3ad188SAdrian Chadd 5782b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 5792b3ad188SAdrian Chadd } 5802b3ad188SAdrian Chadd 5812b3ad188SAdrian Chadd /* 5822b3ad188SAdrian Chadd * Interrupt source assign_cpu method for MI interrupt framework. 5832b3ad188SAdrian Chadd */ 5842b3ad188SAdrian Chadd static int 5852b3ad188SAdrian Chadd intr_isrc_assign_cpu(void *arg, int cpu) 5862b3ad188SAdrian Chadd { 5872b3ad188SAdrian Chadd #ifdef SMP 5882b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 5892b3ad188SAdrian Chadd int error; 5902b3ad188SAdrian Chadd 5915b70c08cSSvatopluk Kraus if (isrc->isrc_dev != intr_irq_root_dev) 5922b3ad188SAdrian Chadd return (EINVAL); 5932b3ad188SAdrian Chadd 5942b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 5952b3ad188SAdrian Chadd if (cpu == NOCPU) { 5962b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 5972b3ad188SAdrian Chadd isrc->isrc_flags &= ~INTR_ISRCF_BOUND; 5982b3ad188SAdrian Chadd } else { 5992b3ad188SAdrian Chadd CPU_SETOF(cpu, &isrc->isrc_cpu); 6002b3ad188SAdrian Chadd isrc->isrc_flags |= INTR_ISRCF_BOUND; 6012b3ad188SAdrian Chadd } 6022b3ad188SAdrian Chadd 6032b3ad188SAdrian Chadd /* 6042b3ad188SAdrian Chadd * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or 6052b3ad188SAdrian Chadd * re-balance it to another CPU or enable it on more CPUs. However, 6062b3ad188SAdrian Chadd * PIC is expected to change isrc_cpu appropriately to keep us well 607e3043798SPedro F. Giffuni * informed if the call is successful. 6082b3ad188SAdrian Chadd */ 6092b3ad188SAdrian Chadd if (irq_assign_cpu) { 610bff6be3eSSvatopluk Kraus error = PIC_BIND_INTR(isrc->isrc_dev, isrc); 6112b3ad188SAdrian Chadd if (error) { 6122b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 6132b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6142b3ad188SAdrian Chadd return (error); 6152b3ad188SAdrian Chadd } 6162b3ad188SAdrian Chadd } 6172b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6182b3ad188SAdrian Chadd return (0); 6192b3ad188SAdrian Chadd #else 6202b3ad188SAdrian Chadd return (EOPNOTSUPP); 6212b3ad188SAdrian Chadd #endif 6222b3ad188SAdrian Chadd } 6232b3ad188SAdrian Chadd 6242b3ad188SAdrian Chadd /* 6252b3ad188SAdrian Chadd * Create interrupt event for interrupt source. 6262b3ad188SAdrian Chadd */ 6272b3ad188SAdrian Chadd static int 6282b3ad188SAdrian Chadd isrc_event_create(struct intr_irqsrc *isrc) 6292b3ad188SAdrian Chadd { 6302b3ad188SAdrian Chadd struct intr_event *ie; 6312b3ad188SAdrian Chadd int error; 6322b3ad188SAdrian Chadd 6332b3ad188SAdrian Chadd error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq, 6342b3ad188SAdrian Chadd intr_isrc_pre_ithread, intr_isrc_post_ithread, intr_isrc_post_filter, 6352b3ad188SAdrian Chadd intr_isrc_assign_cpu, "%s:", isrc->isrc_name); 6362b3ad188SAdrian Chadd if (error) 6372b3ad188SAdrian Chadd return (error); 6382b3ad188SAdrian Chadd 6392b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6402b3ad188SAdrian Chadd /* 6412b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 6422b3ad188SAdrian Chadd * how we handle interrupt sources. Let contested event wins. 6432b3ad188SAdrian Chadd */ 644169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 6452b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 646169e6abdSSvatopluk Kraus #else 647169e6abdSSvatopluk Kraus if (isrc->isrc_event != NULL) { 648169e6abdSSvatopluk Kraus #endif 6492b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6502b3ad188SAdrian Chadd intr_event_destroy(ie); 6512b3ad188SAdrian Chadd return (isrc->isrc_event != NULL ? EBUSY : 0); 6522b3ad188SAdrian Chadd } 6532b3ad188SAdrian Chadd isrc->isrc_event = ie; 6542b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6552b3ad188SAdrian Chadd 6562b3ad188SAdrian Chadd return (0); 6572b3ad188SAdrian Chadd } 6582b3ad188SAdrian Chadd #ifdef notyet 6592b3ad188SAdrian Chadd /* 6602b3ad188SAdrian Chadd * Destroy interrupt event for interrupt source. 6612b3ad188SAdrian Chadd */ 6622b3ad188SAdrian Chadd static void 6632b3ad188SAdrian Chadd isrc_event_destroy(struct intr_irqsrc *isrc) 6642b3ad188SAdrian Chadd { 6652b3ad188SAdrian Chadd struct intr_event *ie; 6662b3ad188SAdrian Chadd 6672b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6682b3ad188SAdrian Chadd ie = isrc->isrc_event; 6692b3ad188SAdrian Chadd isrc->isrc_event = NULL; 6702b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6712b3ad188SAdrian Chadd 6722b3ad188SAdrian Chadd if (ie != NULL) 6732b3ad188SAdrian Chadd intr_event_destroy(ie); 6742b3ad188SAdrian Chadd } 6752b3ad188SAdrian Chadd #endif 6762b3ad188SAdrian Chadd /* 6772b3ad188SAdrian Chadd * Add handler to interrupt source. 6782b3ad188SAdrian Chadd */ 6792b3ad188SAdrian Chadd static int 6802b3ad188SAdrian Chadd isrc_add_handler(struct intr_irqsrc *isrc, const char *name, 6812b3ad188SAdrian Chadd driver_filter_t filter, driver_intr_t handler, void *arg, 6822b3ad188SAdrian Chadd enum intr_type flags, void **cookiep) 6832b3ad188SAdrian Chadd { 6842b3ad188SAdrian Chadd int error; 6852b3ad188SAdrian Chadd 6862b3ad188SAdrian Chadd if (isrc->isrc_event == NULL) { 6872b3ad188SAdrian Chadd error = isrc_event_create(isrc); 6882b3ad188SAdrian Chadd if (error) 6892b3ad188SAdrian Chadd return (error); 6902b3ad188SAdrian Chadd } 6912b3ad188SAdrian Chadd 6922b3ad188SAdrian Chadd error = intr_event_add_handler(isrc->isrc_event, name, filter, handler, 6932b3ad188SAdrian Chadd arg, intr_priority(flags), flags, cookiep); 6942b3ad188SAdrian Chadd if (error == 0) { 6952b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6962b3ad188SAdrian Chadd intrcnt_updatename(isrc); 6972b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6982b3ad188SAdrian Chadd } 6992b3ad188SAdrian Chadd 7002b3ad188SAdrian Chadd return (error); 7012b3ad188SAdrian Chadd } 7022b3ad188SAdrian Chadd 7032b3ad188SAdrian Chadd /* 7042b3ad188SAdrian Chadd * Lookup interrupt controller locked. 7052b3ad188SAdrian Chadd */ 706bff6be3eSSvatopluk Kraus static inline struct intr_pic * 707c0d52370SAndrew Turner pic_lookup_locked(device_t dev, intptr_t xref, int flags) 7082b3ad188SAdrian Chadd { 7092b3ad188SAdrian Chadd struct intr_pic *pic; 7102b3ad188SAdrian Chadd 7112b3ad188SAdrian Chadd mtx_assert(&pic_list_lock, MA_OWNED); 7122b3ad188SAdrian Chadd 7134be58cbaSSvatopluk Kraus if (dev == NULL && xref == 0) 7144be58cbaSSvatopluk Kraus return (NULL); 7154be58cbaSSvatopluk Kraus 7164be58cbaSSvatopluk Kraus /* Note that pic->pic_dev is never NULL on registered PIC. */ 7172b3ad188SAdrian Chadd SLIST_FOREACH(pic, &pic_list, pic_next) { 718c0d52370SAndrew Turner if ((pic->pic_flags & FLAG_TYPE_MASK) != 719c0d52370SAndrew Turner (flags & FLAG_TYPE_MASK)) 720c0d52370SAndrew Turner continue; 721c0d52370SAndrew Turner 7224be58cbaSSvatopluk Kraus if (dev == NULL) { 7234be58cbaSSvatopluk Kraus if (xref == pic->pic_xref) 7244be58cbaSSvatopluk Kraus return (pic); 7254be58cbaSSvatopluk Kraus } else if (xref == 0 || pic->pic_xref == 0) { 7264be58cbaSSvatopluk Kraus if (dev == pic->pic_dev) 7274be58cbaSSvatopluk Kraus return (pic); 7284be58cbaSSvatopluk Kraus } else if (xref == pic->pic_xref && dev == pic->pic_dev) 7292b3ad188SAdrian Chadd return (pic); 7302b3ad188SAdrian Chadd } 7312b3ad188SAdrian Chadd return (NULL); 7322b3ad188SAdrian Chadd } 7332b3ad188SAdrian Chadd 7342b3ad188SAdrian Chadd /* 7352b3ad188SAdrian Chadd * Lookup interrupt controller. 7362b3ad188SAdrian Chadd */ 7372b3ad188SAdrian Chadd static struct intr_pic * 738c0d52370SAndrew Turner pic_lookup(device_t dev, intptr_t xref, int flags) 7392b3ad188SAdrian Chadd { 7402b3ad188SAdrian Chadd struct intr_pic *pic; 7412b3ad188SAdrian Chadd 7422b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 743c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 7442b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7452b3ad188SAdrian Chadd return (pic); 7462b3ad188SAdrian Chadd } 7472b3ad188SAdrian Chadd 7482b3ad188SAdrian Chadd /* 7492b3ad188SAdrian Chadd * Create interrupt controller. 7502b3ad188SAdrian Chadd */ 7512b3ad188SAdrian Chadd static struct intr_pic * 752c0d52370SAndrew Turner pic_create(device_t dev, intptr_t xref, int flags) 7532b3ad188SAdrian Chadd { 7542b3ad188SAdrian Chadd struct intr_pic *pic; 7552b3ad188SAdrian Chadd 7562b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 757c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 7582b3ad188SAdrian Chadd if (pic != NULL) { 7592b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7602b3ad188SAdrian Chadd return (pic); 7612b3ad188SAdrian Chadd } 7622b3ad188SAdrian Chadd pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO); 763b48c6083SAndrew Turner if (pic == NULL) { 764b48c6083SAndrew Turner mtx_unlock(&pic_list_lock); 765b48c6083SAndrew Turner return (NULL); 766b48c6083SAndrew Turner } 7672b3ad188SAdrian Chadd pic->pic_xref = xref; 7682b3ad188SAdrian Chadd pic->pic_dev = dev; 769c0d52370SAndrew Turner pic->pic_flags = flags; 770d1605cdaSAndrew Turner mtx_init(&pic->pic_child_lock, "pic child lock", NULL, MTX_SPIN); 7712b3ad188SAdrian Chadd SLIST_INSERT_HEAD(&pic_list, pic, pic_next); 7722b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7732b3ad188SAdrian Chadd 7742b3ad188SAdrian Chadd return (pic); 7752b3ad188SAdrian Chadd } 7762b3ad188SAdrian Chadd #ifdef notyet 7772b3ad188SAdrian Chadd /* 7782b3ad188SAdrian Chadd * Destroy interrupt controller. 7792b3ad188SAdrian Chadd */ 7802b3ad188SAdrian Chadd static void 781c0d52370SAndrew Turner pic_destroy(device_t dev, intptr_t xref, int flags) 7822b3ad188SAdrian Chadd { 7832b3ad188SAdrian Chadd struct intr_pic *pic; 7842b3ad188SAdrian Chadd 7852b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 786c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 7872b3ad188SAdrian Chadd if (pic == NULL) { 7882b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7892b3ad188SAdrian Chadd return; 7902b3ad188SAdrian Chadd } 7912b3ad188SAdrian Chadd SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next); 7922b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7932b3ad188SAdrian Chadd 7942b3ad188SAdrian Chadd free(pic, M_INTRNG); 7952b3ad188SAdrian Chadd } 7962b3ad188SAdrian Chadd #endif 7972b3ad188SAdrian Chadd /* 7982b3ad188SAdrian Chadd * Register interrupt controller. 7992b3ad188SAdrian Chadd */ 8009346e913SAndrew Turner struct intr_pic * 8012b3ad188SAdrian Chadd intr_pic_register(device_t dev, intptr_t xref) 8022b3ad188SAdrian Chadd { 8032b3ad188SAdrian Chadd struct intr_pic *pic; 8042b3ad188SAdrian Chadd 8054be58cbaSSvatopluk Kraus if (dev == NULL) 8069346e913SAndrew Turner return (NULL); 807c0d52370SAndrew Turner pic = pic_create(dev, xref, FLAG_PIC); 8082b3ad188SAdrian Chadd if (pic == NULL) 8099346e913SAndrew Turner return (NULL); 8102b3ad188SAdrian Chadd 811cff33fa8SEd Maste debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic, 812cff33fa8SEd Maste device_get_nameunit(dev), dev, (uintmax_t)xref); 8139346e913SAndrew Turner return (pic); 8142b3ad188SAdrian Chadd } 8152b3ad188SAdrian Chadd 8162b3ad188SAdrian Chadd /* 8172b3ad188SAdrian Chadd * Unregister interrupt controller. 8182b3ad188SAdrian Chadd */ 8192b3ad188SAdrian Chadd int 820bff6be3eSSvatopluk Kraus intr_pic_deregister(device_t dev, intptr_t xref) 8212b3ad188SAdrian Chadd { 8222b3ad188SAdrian Chadd 8232b3ad188SAdrian Chadd panic("%s: not implemented", __func__); 8242b3ad188SAdrian Chadd } 8252b3ad188SAdrian Chadd 8262b3ad188SAdrian Chadd /* 8272b3ad188SAdrian Chadd * Mark interrupt controller (itself) as a root one. 8282b3ad188SAdrian Chadd * 8292b3ad188SAdrian Chadd * Note that only an interrupt controller can really know its position 8302b3ad188SAdrian Chadd * in interrupt controller's tree. So root PIC must claim itself as a root. 8312b3ad188SAdrian Chadd * 8322b3ad188SAdrian Chadd * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, 8332b3ad188SAdrian Chadd * page 30: 8342b3ad188SAdrian Chadd * "The root of the interrupt tree is determined when traversal 8352b3ad188SAdrian Chadd * of the interrupt tree reaches an interrupt controller node without 8362b3ad188SAdrian Chadd * an interrupts property and thus no explicit interrupt parent." 8372b3ad188SAdrian Chadd */ 8382b3ad188SAdrian Chadd int 8392b3ad188SAdrian Chadd intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, 8402b3ad188SAdrian Chadd void *arg, u_int ipicount) 8412b3ad188SAdrian Chadd { 8423fc155dcSAndrew Turner struct intr_pic *pic; 8432b3ad188SAdrian Chadd 844c0d52370SAndrew Turner pic = pic_lookup(dev, xref, FLAG_PIC); 8453fc155dcSAndrew Turner if (pic == NULL) { 8462b3ad188SAdrian Chadd device_printf(dev, "not registered\n"); 8472b3ad188SAdrian Chadd return (EINVAL); 8482b3ad188SAdrian Chadd } 8493fc155dcSAndrew Turner 850c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_PIC, 8513fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 8523fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 8533fc155dcSAndrew Turner 8542b3ad188SAdrian Chadd if (filter == NULL) { 8552b3ad188SAdrian Chadd device_printf(dev, "filter missing\n"); 8562b3ad188SAdrian Chadd return (EINVAL); 8572b3ad188SAdrian Chadd } 8582b3ad188SAdrian Chadd 8592b3ad188SAdrian Chadd /* 8602b3ad188SAdrian Chadd * Only one interrupt controllers could be on the root for now. 8612b3ad188SAdrian Chadd * Note that we further suppose that there is not threaded interrupt 8622b3ad188SAdrian Chadd * routine (handler) on the root. See intr_irq_handler(). 8632b3ad188SAdrian Chadd */ 8645b70c08cSSvatopluk Kraus if (intr_irq_root_dev != NULL) { 8652b3ad188SAdrian Chadd device_printf(dev, "another root already set\n"); 8662b3ad188SAdrian Chadd return (EBUSY); 8672b3ad188SAdrian Chadd } 8682b3ad188SAdrian Chadd 8695b70c08cSSvatopluk Kraus intr_irq_root_dev = dev; 8702b3ad188SAdrian Chadd irq_root_filter = filter; 8712b3ad188SAdrian Chadd irq_root_arg = arg; 8722b3ad188SAdrian Chadd irq_root_ipicount = ipicount; 8732b3ad188SAdrian Chadd 8742b3ad188SAdrian Chadd debugf("irq root set to %s\n", device_get_nameunit(dev)); 8752b3ad188SAdrian Chadd return (0); 8762b3ad188SAdrian Chadd } 8772b3ad188SAdrian Chadd 878d1605cdaSAndrew Turner /* 879d1605cdaSAndrew Turner * Add a handler to manage a sub range of a parents interrupts. 880d1605cdaSAndrew Turner */ 881d1605cdaSAndrew Turner struct intr_pic * 882d1605cdaSAndrew Turner intr_pic_add_handler(device_t parent, struct intr_pic *pic, 883d1605cdaSAndrew Turner intr_child_irq_filter_t *filter, void *arg, uintptr_t start, 884d1605cdaSAndrew Turner uintptr_t length) 885d1605cdaSAndrew Turner { 886d1605cdaSAndrew Turner struct intr_pic *parent_pic; 887d1605cdaSAndrew Turner struct intr_pic_child *newchild; 888d1605cdaSAndrew Turner #ifdef INVARIANTS 889d1605cdaSAndrew Turner struct intr_pic_child *child; 890d1605cdaSAndrew Turner #endif 891d1605cdaSAndrew Turner 892c0d52370SAndrew Turner /* Find the parent PIC */ 893c0d52370SAndrew Turner parent_pic = pic_lookup(parent, 0, FLAG_PIC); 894d1605cdaSAndrew Turner if (parent_pic == NULL) 895d1605cdaSAndrew Turner return (NULL); 896d1605cdaSAndrew Turner 897d1605cdaSAndrew Turner newchild = malloc(sizeof(*newchild), M_INTRNG, M_WAITOK | M_ZERO); 898d1605cdaSAndrew Turner newchild->pc_pic = pic; 899d1605cdaSAndrew Turner newchild->pc_filter = filter; 900d1605cdaSAndrew Turner newchild->pc_filter_arg = arg; 901d1605cdaSAndrew Turner newchild->pc_start = start; 902d1605cdaSAndrew Turner newchild->pc_length = length; 903d1605cdaSAndrew Turner 904d1605cdaSAndrew Turner mtx_lock_spin(&parent_pic->pic_child_lock); 905d1605cdaSAndrew Turner #ifdef INVARIANTS 906d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent_pic->pic_children, pc_next) { 907d1605cdaSAndrew Turner KASSERT(child->pc_pic != pic, ("%s: Adding a child PIC twice", 908d1605cdaSAndrew Turner __func__)); 909d1605cdaSAndrew Turner } 910d1605cdaSAndrew Turner #endif 911d1605cdaSAndrew Turner SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next); 912d1605cdaSAndrew Turner mtx_unlock_spin(&parent_pic->pic_child_lock); 913d1605cdaSAndrew Turner 914d1605cdaSAndrew Turner return (pic); 915d1605cdaSAndrew Turner } 916d1605cdaSAndrew Turner 917895c8b1cSMichal Meloun static int 918895c8b1cSMichal Meloun intr_resolve_irq(device_t dev, intptr_t xref, struct intr_map_data *data, 919895c8b1cSMichal Meloun struct intr_irqsrc **isrc) 9202b3ad188SAdrian Chadd { 921bff6be3eSSvatopluk Kraus struct intr_pic *pic; 922895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 923bff6be3eSSvatopluk Kraus 924bff6be3eSSvatopluk Kraus if (data == NULL) 925bff6be3eSSvatopluk Kraus return (EINVAL); 926bff6be3eSSvatopluk Kraus 927c0d52370SAndrew Turner pic = pic_lookup(dev, xref, 928c0d52370SAndrew Turner (data->type == INTR_MAP_DATA_MSI) ? FLAG_MSI : FLAG_PIC); 92915adccc6SSvatopluk Kraus if (pic == NULL) 930bff6be3eSSvatopluk Kraus return (ESRCH); 931bff6be3eSSvatopluk Kraus 932895c8b1cSMichal Meloun switch (data->type) { 933895c8b1cSMichal Meloun case INTR_MAP_DATA_MSI: 934c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 935895c8b1cSMichal Meloun ("%s: Found a non-MSI controller: %s", __func__, 936895c8b1cSMichal Meloun device_get_name(pic->pic_dev))); 937895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)data; 938895c8b1cSMichal Meloun *isrc = msi->isrc; 939895c8b1cSMichal Meloun return (0); 940895c8b1cSMichal Meloun 941895c8b1cSMichal Meloun default: 942c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_PIC, 9433fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 9443fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 945895c8b1cSMichal Meloun return (PIC_MAP_INTR(pic->pic_dev, data, isrc)); 946895c8b1cSMichal Meloun } 947895c8b1cSMichal Meloun } 948895c8b1cSMichal Meloun 949895c8b1cSMichal Meloun int 950895c8b1cSMichal Meloun intr_activate_irq(device_t dev, struct resource *res) 951895c8b1cSMichal Meloun { 952895c8b1cSMichal Meloun device_t map_dev; 953895c8b1cSMichal Meloun intptr_t map_xref; 954895c8b1cSMichal Meloun struct intr_map_data *data; 955895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 956895c8b1cSMichal Meloun u_int res_id; 957895c8b1cSMichal Meloun int error; 958895c8b1cSMichal Meloun 959895c8b1cSMichal Meloun KASSERT(rman_get_start(res) == rman_get_end(res), 960895c8b1cSMichal Meloun ("%s: more interrupts in resource", __func__)); 961895c8b1cSMichal Meloun 962895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 963895c8b1cSMichal Meloun if (intr_map_get_isrc(res_id) != NULL) 964895c8b1cSMichal Meloun panic("Attempt to double activation of resource id: %u\n", 965895c8b1cSMichal Meloun res_id); 966895c8b1cSMichal Meloun intr_map_copy_map_data(res_id, &map_dev, &map_xref, &data); 967895c8b1cSMichal Meloun error = intr_resolve_irq(map_dev, map_xref, data, &isrc); 968895c8b1cSMichal Meloun if (error != 0) { 969895c8b1cSMichal Meloun free(data, M_INTRNG); 970895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 971895c8b1cSMichal Meloun /* if (error == EINVAL) return(0); */ 972bff6be3eSSvatopluk Kraus return (error); 973bff6be3eSSvatopluk Kraus } 974895c8b1cSMichal Meloun intr_map_set_isrc(res_id, isrc); 975895c8b1cSMichal Meloun rman_set_virtual(res, data); 976895c8b1cSMichal Meloun return (PIC_ACTIVATE_INTR(isrc->isrc_dev, isrc, res, data)); 977bff6be3eSSvatopluk Kraus } 978bff6be3eSSvatopluk Kraus 979bff6be3eSSvatopluk Kraus int 980895c8b1cSMichal Meloun intr_deactivate_irq(device_t dev, struct resource *res) 981bff6be3eSSvatopluk Kraus { 982bff6be3eSSvatopluk Kraus struct intr_map_data *data; 983bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 984895c8b1cSMichal Meloun u_int res_id; 985895c8b1cSMichal Meloun int error; 986bff6be3eSSvatopluk Kraus 987bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 988bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 989bff6be3eSSvatopluk Kraus 990895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 991895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 992bff6be3eSSvatopluk Kraus if (isrc == NULL) 993895c8b1cSMichal Meloun panic("Attempt to deactivate non-active resource id: %u\n", 994895c8b1cSMichal Meloun res_id); 995bff6be3eSSvatopluk Kraus 996c4263292SSvatopluk Kraus data = rman_get_virtual(res); 997895c8b1cSMichal Meloun error = PIC_DEACTIVATE_INTR(isrc->isrc_dev, isrc, res, data); 998895c8b1cSMichal Meloun intr_map_set_isrc(res_id, NULL); 999895c8b1cSMichal Meloun rman_set_virtual(res, NULL); 1000895c8b1cSMichal Meloun free(data, M_INTRNG); 1001895c8b1cSMichal Meloun return (error); 1002bff6be3eSSvatopluk Kraus } 1003bff6be3eSSvatopluk Kraus 1004bff6be3eSSvatopluk Kraus int 1005bff6be3eSSvatopluk Kraus intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, 1006bff6be3eSSvatopluk Kraus driver_intr_t hand, void *arg, int flags, void **cookiep) 1007bff6be3eSSvatopluk Kraus { 1008bff6be3eSSvatopluk Kraus int error; 1009bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1010bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1011bff6be3eSSvatopluk Kraus const char *name; 1012895c8b1cSMichal Meloun u_int res_id; 1013bff6be3eSSvatopluk Kraus 1014bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1015bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1016bff6be3eSSvatopluk Kraus 1017895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1018895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 1019895c8b1cSMichal Meloun if (isrc == NULL) { 1020895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 1021bff6be3eSSvatopluk Kraus return (EINVAL); 1022895c8b1cSMichal Meloun } 10232b3ad188SAdrian Chadd 1024c4263292SSvatopluk Kraus data = rman_get_virtual(res); 10252b3ad188SAdrian Chadd name = device_get_nameunit(dev); 10262b3ad188SAdrian Chadd 10272b3ad188SAdrian Chadd #ifdef INTR_SOLO 10282b3ad188SAdrian Chadd /* 1029e3043798SPedro F. Giffuni * Standard handling is done through MI interrupt framework. However, 10302b3ad188SAdrian Chadd * some interrupts could request solely own special handling. This 10312b3ad188SAdrian Chadd * non standard handling can be used for interrupt controllers without 10322b3ad188SAdrian Chadd * handler (filter only), so in case that interrupt controllers are 10332b3ad188SAdrian Chadd * chained, MI interrupt framework is called only in leaf controller. 10342b3ad188SAdrian Chadd * 10352b3ad188SAdrian Chadd * Note that root interrupt controller routine is served as well, 10362b3ad188SAdrian Chadd * however in intr_irq_handler(), i.e. main system dispatch routine. 10372b3ad188SAdrian Chadd */ 10382b3ad188SAdrian Chadd if (flags & INTR_SOLO && hand != NULL) { 10392b3ad188SAdrian Chadd debugf("irq %u cannot solo on %s\n", irq, name); 10402b3ad188SAdrian Chadd return (EINVAL); 10412b3ad188SAdrian Chadd } 10422b3ad188SAdrian Chadd 10432b3ad188SAdrian Chadd if (flags & INTR_SOLO) { 10442b3ad188SAdrian Chadd error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, 10452b3ad188SAdrian Chadd arg, cookiep); 1046ce44a736SIan Lepore debugf("irq %u setup filter error %d on %s\n", isrc->isrc_irq, error, 10472b3ad188SAdrian Chadd name); 10482b3ad188SAdrian Chadd } else 10492b3ad188SAdrian Chadd #endif 10502b3ad188SAdrian Chadd { 10512b3ad188SAdrian Chadd error = isrc_add_handler(isrc, name, filt, hand, arg, flags, 10522b3ad188SAdrian Chadd cookiep); 1053ce44a736SIan Lepore debugf("irq %u add handler error %d on %s\n", isrc->isrc_irq, error, name); 10542b3ad188SAdrian Chadd } 10552b3ad188SAdrian Chadd if (error != 0) 10562b3ad188SAdrian Chadd return (error); 10572b3ad188SAdrian Chadd 10582b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1059bff6be3eSSvatopluk Kraus error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); 1060bff6be3eSSvatopluk Kraus if (error == 0) { 10612b3ad188SAdrian Chadd isrc->isrc_handlers++; 1062bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 1) 10632b3ad188SAdrian Chadd PIC_ENABLE_INTR(isrc->isrc_dev, isrc); 10642b3ad188SAdrian Chadd } 10652b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 1066bff6be3eSSvatopluk Kraus if (error != 0) 1067bff6be3eSSvatopluk Kraus intr_event_remove_handler(*cookiep); 1068bff6be3eSSvatopluk Kraus return (error); 10692b3ad188SAdrian Chadd } 10702b3ad188SAdrian Chadd 10712b3ad188SAdrian Chadd int 1072bff6be3eSSvatopluk Kraus intr_teardown_irq(device_t dev, struct resource *res, void *cookie) 10732b3ad188SAdrian Chadd { 10742b3ad188SAdrian Chadd int error; 1075bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1076bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1077895c8b1cSMichal Meloun u_int res_id; 10782b3ad188SAdrian Chadd 1079bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1080bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1081bff6be3eSSvatopluk Kraus 1082895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1083895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 10842b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 10852b3ad188SAdrian Chadd return (EINVAL); 1086bff6be3eSSvatopluk Kraus 1087c4263292SSvatopluk Kraus data = rman_get_virtual(res); 1088c4263292SSvatopluk Kraus 1089169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 10902b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 10912b3ad188SAdrian Chadd if (isrc != cookie) 10922b3ad188SAdrian Chadd return (EINVAL); 10932b3ad188SAdrian Chadd 10942b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 10952b3ad188SAdrian Chadd isrc->isrc_filter = NULL; 10962b3ad188SAdrian Chadd isrc->isrc_arg = NULL; 10972b3ad188SAdrian Chadd isrc->isrc_handlers = 0; 10982b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1099bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 11002b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 11012b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11022b3ad188SAdrian Chadd return (0); 11032b3ad188SAdrian Chadd } 1104169e6abdSSvatopluk Kraus #endif 11052b3ad188SAdrian Chadd if (isrc != intr_handler_source(cookie)) 11062b3ad188SAdrian Chadd return (EINVAL); 11072b3ad188SAdrian Chadd 11082b3ad188SAdrian Chadd error = intr_event_remove_handler(cookie); 11092b3ad188SAdrian Chadd if (error == 0) { 11102b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11112b3ad188SAdrian Chadd isrc->isrc_handlers--; 1112bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 0) 11132b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1114bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 11152b3ad188SAdrian Chadd intrcnt_updatename(isrc); 11162b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11172b3ad188SAdrian Chadd } 11182b3ad188SAdrian Chadd return (error); 11192b3ad188SAdrian Chadd } 11202b3ad188SAdrian Chadd 11212b3ad188SAdrian Chadd int 1122bff6be3eSSvatopluk Kraus intr_describe_irq(device_t dev, struct resource *res, void *cookie, 1123bff6be3eSSvatopluk Kraus const char *descr) 11242b3ad188SAdrian Chadd { 11252b3ad188SAdrian Chadd int error; 1126bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1127895c8b1cSMichal Meloun u_int res_id; 11282b3ad188SAdrian Chadd 1129bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1130bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1131bff6be3eSSvatopluk Kraus 1132895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1133895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 11342b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 11352b3ad188SAdrian Chadd return (EINVAL); 1136169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 11372b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 11382b3ad188SAdrian Chadd if (isrc != cookie) 11392b3ad188SAdrian Chadd return (EINVAL); 11402b3ad188SAdrian Chadd 11412b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11422b3ad188SAdrian Chadd isrc_update_name(isrc, descr); 11432b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11442b3ad188SAdrian Chadd return (0); 11452b3ad188SAdrian Chadd } 1146169e6abdSSvatopluk Kraus #endif 11472b3ad188SAdrian Chadd error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); 11482b3ad188SAdrian Chadd if (error == 0) { 11492b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11502b3ad188SAdrian Chadd intrcnt_updatename(isrc); 11512b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11522b3ad188SAdrian Chadd } 11532b3ad188SAdrian Chadd return (error); 11542b3ad188SAdrian Chadd } 11552b3ad188SAdrian Chadd 11562b3ad188SAdrian Chadd #ifdef SMP 11572b3ad188SAdrian Chadd int 1158bff6be3eSSvatopluk Kraus intr_bind_irq(device_t dev, struct resource *res, int cpu) 11592b3ad188SAdrian Chadd { 11602b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 1161895c8b1cSMichal Meloun u_int res_id; 11622b3ad188SAdrian Chadd 1163bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1164bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1165bff6be3eSSvatopluk Kraus 1166895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1167895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 11682b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 11692b3ad188SAdrian Chadd return (EINVAL); 1170169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 11712b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) 11722b3ad188SAdrian Chadd return (intr_isrc_assign_cpu(isrc, cpu)); 1173169e6abdSSvatopluk Kraus #endif 11742b3ad188SAdrian Chadd return (intr_event_bind(isrc->isrc_event, cpu)); 11752b3ad188SAdrian Chadd } 11762b3ad188SAdrian Chadd 11772b3ad188SAdrian Chadd /* 11782b3ad188SAdrian Chadd * Return the CPU that the next interrupt source should use. 11792b3ad188SAdrian Chadd * For now just returns the next CPU according to round-robin. 11802b3ad188SAdrian Chadd */ 11812b3ad188SAdrian Chadd u_int 11822b3ad188SAdrian Chadd intr_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask) 11832b3ad188SAdrian Chadd { 1184a92a2f00SAndrew Turner u_int cpu; 11852b3ad188SAdrian Chadd 1186a92a2f00SAndrew Turner KASSERT(!CPU_EMPTY(cpumask), ("%s: Empty CPU mask", __func__)); 1187a92a2f00SAndrew Turner if (!irq_assign_cpu || mp_ncpus == 1) { 1188a92a2f00SAndrew Turner cpu = PCPU_GET(cpuid); 1189a92a2f00SAndrew Turner 1190a92a2f00SAndrew Turner if (CPU_ISSET(cpu, cpumask)) 1191a92a2f00SAndrew Turner return (curcpu); 1192a92a2f00SAndrew Turner 1193a92a2f00SAndrew Turner return (CPU_FFS(cpumask) - 1); 1194a92a2f00SAndrew Turner } 11952b3ad188SAdrian Chadd 11962b3ad188SAdrian Chadd do { 11972b3ad188SAdrian Chadd last_cpu++; 11982b3ad188SAdrian Chadd if (last_cpu > mp_maxid) 11992b3ad188SAdrian Chadd last_cpu = 0; 12002b3ad188SAdrian Chadd } while (!CPU_ISSET(last_cpu, cpumask)); 12012b3ad188SAdrian Chadd return (last_cpu); 12022b3ad188SAdrian Chadd } 12032b3ad188SAdrian Chadd 1204dc425090SMitchell Horne #ifndef EARLY_AP_STARTUP 12052b3ad188SAdrian Chadd /* 12062b3ad188SAdrian Chadd * Distribute all the interrupt sources among the available 12072b3ad188SAdrian Chadd * CPUs once the AP's have been launched. 12082b3ad188SAdrian Chadd */ 12092b3ad188SAdrian Chadd static void 12102b3ad188SAdrian Chadd intr_irq_shuffle(void *arg __unused) 12112b3ad188SAdrian Chadd { 12122b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 12132b3ad188SAdrian Chadd u_int i; 12142b3ad188SAdrian Chadd 12152b3ad188SAdrian Chadd if (mp_ncpus == 1) 12162b3ad188SAdrian Chadd return; 12172b3ad188SAdrian Chadd 12182b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1219dc425090SMitchell Horne irq_assign_cpu = true; 12202b3ad188SAdrian Chadd for (i = 0; i < NIRQ; i++) { 12212b3ad188SAdrian Chadd isrc = irq_sources[i]; 12222b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0 || 1223cf55df9fSSvatopluk Kraus isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) 12242b3ad188SAdrian Chadd continue; 12252b3ad188SAdrian Chadd 12262b3ad188SAdrian Chadd if (isrc->isrc_event != NULL && 12272b3ad188SAdrian Chadd isrc->isrc_flags & INTR_ISRCF_BOUND && 12282b3ad188SAdrian Chadd isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1) 12292b3ad188SAdrian Chadd panic("%s: CPU inconsistency", __func__); 12302b3ad188SAdrian Chadd 12312b3ad188SAdrian Chadd if ((isrc->isrc_flags & INTR_ISRCF_BOUND) == 0) 12322b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); /* start again */ 12332b3ad188SAdrian Chadd 12342b3ad188SAdrian Chadd /* 12352b3ad188SAdrian Chadd * We are in wicked position here if the following call fails 12362b3ad188SAdrian Chadd * for bound ISRC. The best thing we can do is to clear 12372b3ad188SAdrian Chadd * isrc_cpu so inconsistency with ie_cpu will be detectable. 12382b3ad188SAdrian Chadd */ 1239bff6be3eSSvatopluk Kraus if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0) 12402b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 12412b3ad188SAdrian Chadd } 12422b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12432b3ad188SAdrian Chadd } 12442b3ad188SAdrian Chadd SYSINIT(intr_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, intr_irq_shuffle, NULL); 1245dc425090SMitchell Horne #endif /* !EARLY_AP_STARTUP */ 12462b3ad188SAdrian Chadd 12472b3ad188SAdrian Chadd #else 12482b3ad188SAdrian Chadd u_int 12492b3ad188SAdrian Chadd intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) 12502b3ad188SAdrian Chadd { 12512b3ad188SAdrian Chadd 12522b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 12532b3ad188SAdrian Chadd } 1254dc425090SMitchell Horne #endif /* SMP */ 12552b3ad188SAdrian Chadd 12563fc155dcSAndrew Turner /* 1257895c8b1cSMichal Meloun * Allocate memory for new intr_map_data structure. 1258895c8b1cSMichal Meloun * Initialize common fields. 1259895c8b1cSMichal Meloun */ 1260895c8b1cSMichal Meloun struct intr_map_data * 1261895c8b1cSMichal Meloun intr_alloc_map_data(enum intr_map_data_type type, size_t len, int flags) 1262895c8b1cSMichal Meloun { 1263895c8b1cSMichal Meloun struct intr_map_data *data; 1264895c8b1cSMichal Meloun 1265895c8b1cSMichal Meloun data = malloc(len, M_INTRNG, flags); 1266895c8b1cSMichal Meloun data->type = type; 1267895c8b1cSMichal Meloun data->len = len; 1268895c8b1cSMichal Meloun return (data); 1269895c8b1cSMichal Meloun } 1270895c8b1cSMichal Meloun 1271895c8b1cSMichal Meloun void intr_free_intr_map_data(struct intr_map_data *data) 1272895c8b1cSMichal Meloun { 1273895c8b1cSMichal Meloun 1274895c8b1cSMichal Meloun free(data, M_INTRNG); 1275895c8b1cSMichal Meloun } 1276895c8b1cSMichal Meloun 1277895c8b1cSMichal Meloun /* 12783fc155dcSAndrew Turner * Register a MSI/MSI-X interrupt controller 12793fc155dcSAndrew Turner */ 12803fc155dcSAndrew Turner int 12813fc155dcSAndrew Turner intr_msi_register(device_t dev, intptr_t xref) 12823fc155dcSAndrew Turner { 12833fc155dcSAndrew Turner struct intr_pic *pic; 12843fc155dcSAndrew Turner 12853fc155dcSAndrew Turner if (dev == NULL) 12863fc155dcSAndrew Turner return (EINVAL); 1287c0d52370SAndrew Turner pic = pic_create(dev, xref, FLAG_MSI); 12883fc155dcSAndrew Turner if (pic == NULL) 12893fc155dcSAndrew Turner return (ENOMEM); 12903fc155dcSAndrew Turner 12913fc155dcSAndrew Turner debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic, 12923fc155dcSAndrew Turner device_get_nameunit(dev), dev, (uintmax_t)xref); 12933fc155dcSAndrew Turner return (0); 12943fc155dcSAndrew Turner } 12953fc155dcSAndrew Turner 12963fc155dcSAndrew Turner int 12973fc155dcSAndrew Turner intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count, 12983fc155dcSAndrew Turner int maxcount, int *irqs) 12993fc155dcSAndrew Turner { 1300e707c8beSRuslan Bukin struct iommu_domain *domain; 13013fc155dcSAndrew Turner struct intr_irqsrc **isrc; 13023fc155dcSAndrew Turner struct intr_pic *pic; 13033fc155dcSAndrew Turner device_t pdev; 1304895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 13053fc155dcSAndrew Turner int err, i; 13063fc155dcSAndrew Turner 1307c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 13083fc155dcSAndrew Turner if (pic == NULL) 13093fc155dcSAndrew Turner return (ESRCH); 13103fc155dcSAndrew Turner 1311c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 13123fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 13133fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 13143fc155dcSAndrew Turner 1315e707c8beSRuslan Bukin /* 1316e707c8beSRuslan Bukin * If this is the first time we have used this context ask the 1317e707c8beSRuslan Bukin * interrupt controller to map memory the msi source will need. 1318e707c8beSRuslan Bukin */ 1319e707c8beSRuslan Bukin err = MSI_IOMMU_INIT(pic->pic_dev, child, &domain); 1320e707c8beSRuslan Bukin if (err != 0) 1321e707c8beSRuslan Bukin return (err); 1322e707c8beSRuslan Bukin 13233fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 13243fc155dcSAndrew Turner err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc); 1325895c8b1cSMichal Meloun if (err != 0) { 1326895c8b1cSMichal Meloun free(isrc, M_INTRNG); 1327895c8b1cSMichal Meloun return (err); 13283fc155dcSAndrew Turner } 13293fc155dcSAndrew Turner 1330895c8b1cSMichal Meloun for (i = 0; i < count; i++) { 1331e707c8beSRuslan Bukin isrc[i]->isrc_iommu = domain; 1332895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)intr_alloc_map_data( 1333895c8b1cSMichal Meloun INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO); 1334895c8b1cSMichal Meloun msi-> isrc = isrc[i]; 1335e707c8beSRuslan Bukin 1336895c8b1cSMichal Meloun irqs[i] = intr_map_irq(pic->pic_dev, xref, 1337895c8b1cSMichal Meloun (struct intr_map_data *)msi); 1338895c8b1cSMichal Meloun } 13393fc155dcSAndrew Turner free(isrc, M_INTRNG); 13403fc155dcSAndrew Turner 13413fc155dcSAndrew Turner return (err); 13423fc155dcSAndrew Turner } 13433fc155dcSAndrew Turner 13443fc155dcSAndrew Turner int 13453fc155dcSAndrew Turner intr_release_msi(device_t pci, device_t child, intptr_t xref, int count, 13463fc155dcSAndrew Turner int *irqs) 13473fc155dcSAndrew Turner { 13483fc155dcSAndrew Turner struct intr_irqsrc **isrc; 13493fc155dcSAndrew Turner struct intr_pic *pic; 1350609b0fe9SOleksandr Tymoshenko struct intr_map_data_msi *msi; 13513fc155dcSAndrew Turner int i, err; 13523fc155dcSAndrew Turner 1353c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 13543fc155dcSAndrew Turner if (pic == NULL) 13553fc155dcSAndrew Turner return (ESRCH); 13563fc155dcSAndrew Turner 1357c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 13583fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 13593fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 13603fc155dcSAndrew Turner 13613fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 13623fc155dcSAndrew Turner 1363609b0fe9SOleksandr Tymoshenko for (i = 0; i < count; i++) { 1364609b0fe9SOleksandr Tymoshenko msi = (struct intr_map_data_msi *) 1365609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(irqs[i]); 1366609b0fe9SOleksandr Tymoshenko KASSERT(msi->hdr.type == INTR_MAP_DATA_MSI, 1367609b0fe9SOleksandr Tymoshenko ("%s: irq %d map data is not MSI", __func__, 1368609b0fe9SOleksandr Tymoshenko irqs[i])); 1369609b0fe9SOleksandr Tymoshenko isrc[i] = msi->isrc; 1370609b0fe9SOleksandr Tymoshenko } 13713fc155dcSAndrew Turner 1372*f32f0095SRuslan Bukin MSI_IOMMU_DEINIT(pic->pic_dev, child); 1373*f32f0095SRuslan Bukin 13743fc155dcSAndrew Turner err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc); 1375895c8b1cSMichal Meloun 1376895c8b1cSMichal Meloun for (i = 0; i < count; i++) { 1377895c8b1cSMichal Meloun if (isrc[i] != NULL) 1378895c8b1cSMichal Meloun intr_unmap_irq(irqs[i]); 1379895c8b1cSMichal Meloun } 1380895c8b1cSMichal Meloun 13813fc155dcSAndrew Turner free(isrc, M_INTRNG); 13823fc155dcSAndrew Turner return (err); 13833fc155dcSAndrew Turner } 13843fc155dcSAndrew Turner 13853fc155dcSAndrew Turner int 13863fc155dcSAndrew Turner intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq) 13873fc155dcSAndrew Turner { 1388e707c8beSRuslan Bukin struct iommu_domain *domain; 13893fc155dcSAndrew Turner struct intr_irqsrc *isrc; 13903fc155dcSAndrew Turner struct intr_pic *pic; 13913fc155dcSAndrew Turner device_t pdev; 1392895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 13933fc155dcSAndrew Turner int err; 13943fc155dcSAndrew Turner 1395c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 13963fc155dcSAndrew Turner if (pic == NULL) 13973fc155dcSAndrew Turner return (ESRCH); 13983fc155dcSAndrew Turner 1399c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 14003fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14013fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14023fc155dcSAndrew Turner 1403e707c8beSRuslan Bukin /* 1404e707c8beSRuslan Bukin * If this is the first time we have used this context ask the 1405e707c8beSRuslan Bukin * interrupt controller to map memory the msi source will need. 1406e707c8beSRuslan Bukin */ 1407e707c8beSRuslan Bukin err = MSI_IOMMU_INIT(pic->pic_dev, child, &domain); 1408e707c8beSRuslan Bukin if (err != 0) 1409e707c8beSRuslan Bukin return (err); 1410e707c8beSRuslan Bukin 14113fc155dcSAndrew Turner err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc); 14123fc155dcSAndrew Turner if (err != 0) 14133fc155dcSAndrew Turner return (err); 14143fc155dcSAndrew Turner 1415e707c8beSRuslan Bukin isrc->isrc_iommu = domain; 1416895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)intr_alloc_map_data( 1417895c8b1cSMichal Meloun INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO); 1418895c8b1cSMichal Meloun msi->isrc = isrc; 1419895c8b1cSMichal Meloun *irq = intr_map_irq(pic->pic_dev, xref, (struct intr_map_data *)msi); 14203fc155dcSAndrew Turner return (0); 14213fc155dcSAndrew Turner } 14223fc155dcSAndrew Turner 14233fc155dcSAndrew Turner int 14243fc155dcSAndrew Turner intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq) 14253fc155dcSAndrew Turner { 14263fc155dcSAndrew Turner struct intr_irqsrc *isrc; 14273fc155dcSAndrew Turner struct intr_pic *pic; 1428609b0fe9SOleksandr Tymoshenko struct intr_map_data_msi *msi; 14293fc155dcSAndrew Turner int err; 14303fc155dcSAndrew Turner 1431c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 14323fc155dcSAndrew Turner if (pic == NULL) 14333fc155dcSAndrew Turner return (ESRCH); 14343fc155dcSAndrew Turner 1435c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 14363fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14373fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14383fc155dcSAndrew Turner 1439609b0fe9SOleksandr Tymoshenko msi = (struct intr_map_data_msi *) 1440609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(irq); 1441609b0fe9SOleksandr Tymoshenko KASSERT(msi->hdr.type == INTR_MAP_DATA_MSI, 1442609b0fe9SOleksandr Tymoshenko ("%s: irq %d map data is not MSI", __func__, 1443609b0fe9SOleksandr Tymoshenko irq)); 1444609b0fe9SOleksandr Tymoshenko isrc = msi->isrc; 1445895c8b1cSMichal Meloun if (isrc == NULL) { 1446895c8b1cSMichal Meloun intr_unmap_irq(irq); 14473fc155dcSAndrew Turner return (EINVAL); 1448895c8b1cSMichal Meloun } 14493fc155dcSAndrew Turner 1450*f32f0095SRuslan Bukin MSI_IOMMU_DEINIT(pic->pic_dev, child); 1451*f32f0095SRuslan Bukin 14523fc155dcSAndrew Turner err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc); 1453895c8b1cSMichal Meloun intr_unmap_irq(irq); 1454895c8b1cSMichal Meloun 14553fc155dcSAndrew Turner return (err); 14563fc155dcSAndrew Turner } 14573fc155dcSAndrew Turner 14583fc155dcSAndrew Turner int 14593fc155dcSAndrew Turner intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq, 14603fc155dcSAndrew Turner uint64_t *addr, uint32_t *data) 14613fc155dcSAndrew Turner { 14623fc155dcSAndrew Turner struct intr_irqsrc *isrc; 14633fc155dcSAndrew Turner struct intr_pic *pic; 14643fc155dcSAndrew Turner int err; 14653fc155dcSAndrew Turner 1466c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 14673fc155dcSAndrew Turner if (pic == NULL) 14683fc155dcSAndrew Turner return (ESRCH); 14693fc155dcSAndrew Turner 1470c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 14713fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14723fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14733fc155dcSAndrew Turner 1474895c8b1cSMichal Meloun isrc = intr_map_get_isrc(irq); 14753fc155dcSAndrew Turner if (isrc == NULL) 14763fc155dcSAndrew Turner return (EINVAL); 14773fc155dcSAndrew Turner 14783fc155dcSAndrew Turner err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data); 1479e707c8beSRuslan Bukin 1480e707c8beSRuslan Bukin #ifdef IOMMU 1481e707c8beSRuslan Bukin if (isrc->isrc_iommu != NULL) 1482e707c8beSRuslan Bukin iommu_translate_msi(isrc->isrc_iommu, addr); 1483e707c8beSRuslan Bukin #endif 1484e707c8beSRuslan Bukin 14853fc155dcSAndrew Turner return (err); 14863fc155dcSAndrew Turner } 14873fc155dcSAndrew Turner 14882b3ad188SAdrian Chadd void dosoftints(void); 14892b3ad188SAdrian Chadd void 14902b3ad188SAdrian Chadd dosoftints(void) 14912b3ad188SAdrian Chadd { 14922b3ad188SAdrian Chadd } 14932b3ad188SAdrian Chadd 14942b3ad188SAdrian Chadd #ifdef SMP 14952b3ad188SAdrian Chadd /* 14962b3ad188SAdrian Chadd * Init interrupt controller on another CPU. 14972b3ad188SAdrian Chadd */ 14982b3ad188SAdrian Chadd void 14992b3ad188SAdrian Chadd intr_pic_init_secondary(void) 15002b3ad188SAdrian Chadd { 15012b3ad188SAdrian Chadd 15022b3ad188SAdrian Chadd /* 15032b3ad188SAdrian Chadd * QQQ: Only root PIC is aware of other CPUs ??? 15042b3ad188SAdrian Chadd */ 15055b70c08cSSvatopluk Kraus KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); 15062b3ad188SAdrian Chadd 15072b3ad188SAdrian Chadd //mtx_lock(&isrc_table_lock); 15085b70c08cSSvatopluk Kraus PIC_INIT_SECONDARY(intr_irq_root_dev); 15092b3ad188SAdrian Chadd //mtx_unlock(&isrc_table_lock); 15102b3ad188SAdrian Chadd } 15112b3ad188SAdrian Chadd #endif 15122b3ad188SAdrian Chadd 15132b3ad188SAdrian Chadd #ifdef DDB 15142b3ad188SAdrian Chadd DB_SHOW_COMMAND(irqs, db_show_irqs) 15152b3ad188SAdrian Chadd { 15162b3ad188SAdrian Chadd u_int i, irqsum; 1517bff6be3eSSvatopluk Kraus u_long num; 15182b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 15192b3ad188SAdrian Chadd 15202b3ad188SAdrian Chadd for (irqsum = 0, i = 0; i < NIRQ; i++) { 15212b3ad188SAdrian Chadd isrc = irq_sources[i]; 15222b3ad188SAdrian Chadd if (isrc == NULL) 15232b3ad188SAdrian Chadd continue; 15242b3ad188SAdrian Chadd 1525bff6be3eSSvatopluk Kraus num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; 15262b3ad188SAdrian Chadd db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, 15272b3ad188SAdrian Chadd isrc->isrc_name, isrc->isrc_cpu.__bits[0], 1528bff6be3eSSvatopluk Kraus isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); 1529bff6be3eSSvatopluk Kraus irqsum += num; 15302b3ad188SAdrian Chadd } 15312b3ad188SAdrian Chadd db_printf("irq total %u\n", irqsum); 15322b3ad188SAdrian Chadd } 15332b3ad188SAdrian Chadd #endif 1534895c8b1cSMichal Meloun 1535895c8b1cSMichal Meloun /* 1536895c8b1cSMichal Meloun * Interrupt mapping table functions. 1537895c8b1cSMichal Meloun * 1538895c8b1cSMichal Meloun * Please, keep this part separately, it can be transformed to 1539895c8b1cSMichal Meloun * extension of standard resources. 1540895c8b1cSMichal Meloun */ 1541895c8b1cSMichal Meloun struct intr_map_entry 1542895c8b1cSMichal Meloun { 1543895c8b1cSMichal Meloun device_t dev; 1544895c8b1cSMichal Meloun intptr_t xref; 1545895c8b1cSMichal Meloun struct intr_map_data *map_data; 1546895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 1547895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 1548895c8b1cSMichal Meloun /*int flags */ 1549895c8b1cSMichal Meloun }; 1550895c8b1cSMichal Meloun 1551895c8b1cSMichal Meloun /* XXX Convert irq_map[] to dynamicaly expandable one. */ 1552895c8b1cSMichal Meloun static struct intr_map_entry *irq_map[2 * NIRQ]; 1553895c8b1cSMichal Meloun static int irq_map_count = nitems(irq_map); 1554895c8b1cSMichal Meloun static int irq_map_first_free_idx; 1555895c8b1cSMichal Meloun static struct mtx irq_map_lock; 1556895c8b1cSMichal Meloun 1557895c8b1cSMichal Meloun static struct intr_irqsrc * 1558895c8b1cSMichal Meloun intr_map_get_isrc(u_int res_id) 1559895c8b1cSMichal Meloun { 1560895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 1561895c8b1cSMichal Meloun 1562ecc8ccb4SAndrew Turner isrc = NULL; 1563895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1564ecc8ccb4SAndrew Turner if (res_id < irq_map_count && irq_map[res_id] != NULL) 1565895c8b1cSMichal Meloun isrc = irq_map[res_id]->isrc; 1566895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1567ecc8ccb4SAndrew Turner 1568895c8b1cSMichal Meloun return (isrc); 1569895c8b1cSMichal Meloun } 1570895c8b1cSMichal Meloun 1571895c8b1cSMichal Meloun static void 1572895c8b1cSMichal Meloun intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc) 1573895c8b1cSMichal Meloun { 1574895c8b1cSMichal Meloun 1575895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1576ecc8ccb4SAndrew Turner if (res_id < irq_map_count && irq_map[res_id] != NULL) 1577895c8b1cSMichal Meloun irq_map[res_id]->isrc = isrc; 1578895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1579895c8b1cSMichal Meloun } 1580895c8b1cSMichal Meloun 1581895c8b1cSMichal Meloun /* 1582895c8b1cSMichal Meloun * Get a copy of intr_map_entry data 1583895c8b1cSMichal Meloun */ 1584609b0fe9SOleksandr Tymoshenko static struct intr_map_data * 1585609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(u_int res_id) 1586609b0fe9SOleksandr Tymoshenko { 1587609b0fe9SOleksandr Tymoshenko struct intr_map_data *data; 1588609b0fe9SOleksandr Tymoshenko 1589609b0fe9SOleksandr Tymoshenko data = NULL; 1590609b0fe9SOleksandr Tymoshenko mtx_lock(&irq_map_lock); 1591609b0fe9SOleksandr Tymoshenko if (res_id >= irq_map_count || irq_map[res_id] == NULL) 1592609b0fe9SOleksandr Tymoshenko panic("Attempt to copy invalid resource id: %u\n", res_id); 1593609b0fe9SOleksandr Tymoshenko data = irq_map[res_id]->map_data; 1594609b0fe9SOleksandr Tymoshenko mtx_unlock(&irq_map_lock); 1595609b0fe9SOleksandr Tymoshenko 1596609b0fe9SOleksandr Tymoshenko return (data); 1597609b0fe9SOleksandr Tymoshenko } 1598609b0fe9SOleksandr Tymoshenko 1599609b0fe9SOleksandr Tymoshenko /* 1600609b0fe9SOleksandr Tymoshenko * Get a copy of intr_map_entry data 1601609b0fe9SOleksandr Tymoshenko */ 1602895c8b1cSMichal Meloun static void 1603895c8b1cSMichal Meloun intr_map_copy_map_data(u_int res_id, device_t *map_dev, intptr_t *map_xref, 1604895c8b1cSMichal Meloun struct intr_map_data **data) 1605895c8b1cSMichal Meloun { 1606895c8b1cSMichal Meloun size_t len; 1607895c8b1cSMichal Meloun 1608895c8b1cSMichal Meloun len = 0; 1609895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1610895c8b1cSMichal Meloun if (res_id >= irq_map_count || irq_map[res_id] == NULL) 1611895c8b1cSMichal Meloun panic("Attempt to copy invalid resource id: %u\n", res_id); 1612895c8b1cSMichal Meloun if (irq_map[res_id]->map_data != NULL) 1613895c8b1cSMichal Meloun len = irq_map[res_id]->map_data->len; 1614895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1615895c8b1cSMichal Meloun 1616895c8b1cSMichal Meloun if (len == 0) 1617895c8b1cSMichal Meloun *data = NULL; 1618895c8b1cSMichal Meloun else 1619895c8b1cSMichal Meloun *data = malloc(len, M_INTRNG, M_WAITOK | M_ZERO); 1620895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1621895c8b1cSMichal Meloun if (irq_map[res_id] == NULL) 1622895c8b1cSMichal Meloun panic("Attempt to copy invalid resource id: %u\n", res_id); 1623895c8b1cSMichal Meloun if (len != 0) { 1624895c8b1cSMichal Meloun if (len != irq_map[res_id]->map_data->len) 1625895c8b1cSMichal Meloun panic("Resource id: %u has changed.\n", res_id); 1626895c8b1cSMichal Meloun memcpy(*data, irq_map[res_id]->map_data, len); 1627895c8b1cSMichal Meloun } 1628895c8b1cSMichal Meloun *map_dev = irq_map[res_id]->dev; 1629895c8b1cSMichal Meloun *map_xref = irq_map[res_id]->xref; 1630895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1631895c8b1cSMichal Meloun } 1632895c8b1cSMichal Meloun 1633895c8b1cSMichal Meloun /* 1634895c8b1cSMichal Meloun * Allocate and fill new entry in irq_map table. 1635895c8b1cSMichal Meloun */ 1636895c8b1cSMichal Meloun u_int 1637895c8b1cSMichal Meloun intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data) 1638895c8b1cSMichal Meloun { 1639895c8b1cSMichal Meloun u_int i; 1640895c8b1cSMichal Meloun struct intr_map_entry *entry; 1641895c8b1cSMichal Meloun 1642895c8b1cSMichal Meloun /* Prepare new entry first. */ 1643895c8b1cSMichal Meloun entry = malloc(sizeof(*entry), M_INTRNG, M_WAITOK | M_ZERO); 1644895c8b1cSMichal Meloun 1645895c8b1cSMichal Meloun entry->dev = dev; 1646895c8b1cSMichal Meloun entry->xref = xref; 1647895c8b1cSMichal Meloun entry->map_data = data; 1648895c8b1cSMichal Meloun entry->isrc = NULL; 1649895c8b1cSMichal Meloun 1650895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1651895c8b1cSMichal Meloun for (i = irq_map_first_free_idx; i < irq_map_count; i++) { 1652895c8b1cSMichal Meloun if (irq_map[i] == NULL) { 1653895c8b1cSMichal Meloun irq_map[i] = entry; 1654895c8b1cSMichal Meloun irq_map_first_free_idx = i + 1; 1655895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1656895c8b1cSMichal Meloun return (i); 1657895c8b1cSMichal Meloun } 1658895c8b1cSMichal Meloun } 1659895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1660895c8b1cSMichal Meloun 1661895c8b1cSMichal Meloun /* XXX Expand irq_map table */ 1662895c8b1cSMichal Meloun panic("IRQ mapping table is full."); 1663895c8b1cSMichal Meloun } 1664895c8b1cSMichal Meloun 1665895c8b1cSMichal Meloun /* 1666895c8b1cSMichal Meloun * Remove and free mapping entry. 1667895c8b1cSMichal Meloun */ 1668895c8b1cSMichal Meloun void 1669895c8b1cSMichal Meloun intr_unmap_irq(u_int res_id) 1670895c8b1cSMichal Meloun { 1671895c8b1cSMichal Meloun struct intr_map_entry *entry; 1672895c8b1cSMichal Meloun 1673895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1674895c8b1cSMichal Meloun if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL)) 1675895c8b1cSMichal Meloun panic("Attempt to unmap invalid resource id: %u\n", res_id); 1676895c8b1cSMichal Meloun entry = irq_map[res_id]; 1677895c8b1cSMichal Meloun irq_map[res_id] = NULL; 1678895c8b1cSMichal Meloun irq_map_first_free_idx = res_id; 1679895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1680895c8b1cSMichal Meloun intr_free_intr_map_data(entry->map_data); 1681895c8b1cSMichal Meloun free(entry, M_INTRNG); 1682895c8b1cSMichal Meloun } 1683895c8b1cSMichal Meloun 1684895c8b1cSMichal Meloun /* 1685895c8b1cSMichal Meloun * Clone mapping entry. 1686895c8b1cSMichal Meloun */ 1687895c8b1cSMichal Meloun u_int 1688895c8b1cSMichal Meloun intr_map_clone_irq(u_int old_res_id) 1689895c8b1cSMichal Meloun { 1690895c8b1cSMichal Meloun device_t map_dev; 1691895c8b1cSMichal Meloun intptr_t map_xref; 1692895c8b1cSMichal Meloun struct intr_map_data *data; 1693895c8b1cSMichal Meloun 1694895c8b1cSMichal Meloun intr_map_copy_map_data(old_res_id, &map_dev, &map_xref, &data); 1695895c8b1cSMichal Meloun return (intr_map_irq(map_dev, map_xref, data)); 1696895c8b1cSMichal Meloun } 1697895c8b1cSMichal Meloun 1698895c8b1cSMichal Meloun static void 1699895c8b1cSMichal Meloun intr_map_init(void *dummy __unused) 1700895c8b1cSMichal Meloun { 1701895c8b1cSMichal Meloun 1702895c8b1cSMichal Meloun mtx_init(&irq_map_lock, "intr map table", NULL, MTX_DEF); 1703895c8b1cSMichal Meloun } 1704895c8b1cSMichal Meloun SYSINIT(intr_map_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_map_init, NULL); 1705