12b3ad188SAdrian Chadd /*- 2bff6be3eSSvatopluk Kraus * Copyright (c) 2015-2016 Svatopluk Kraus 3bff6be3eSSvatopluk Kraus * Copyright (c) 2015-2016 Michal Meloun 42b3ad188SAdrian Chadd * All rights reserved. 52b3ad188SAdrian Chadd * 62b3ad188SAdrian Chadd * Redistribution and use in source and binary forms, with or without 72b3ad188SAdrian Chadd * modification, are permitted provided that the following conditions 82b3ad188SAdrian Chadd * are met: 92b3ad188SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 102b3ad188SAdrian Chadd * notice, this list of conditions and the following disclaimer. 112b3ad188SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 122b3ad188SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 132b3ad188SAdrian Chadd * documentation and/or other materials provided with the distribution. 142b3ad188SAdrian Chadd * 152b3ad188SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 162b3ad188SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 172b3ad188SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 182b3ad188SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 192b3ad188SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 202b3ad188SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 212b3ad188SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 222b3ad188SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 232b3ad188SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 242b3ad188SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 252b3ad188SAdrian Chadd * SUCH DAMAGE. 262b3ad188SAdrian Chadd */ 272b3ad188SAdrian Chadd 282b3ad188SAdrian Chadd #include <sys/cdefs.h> 292b3ad188SAdrian Chadd __FBSDID("$FreeBSD$"); 302b3ad188SAdrian Chadd 312b3ad188SAdrian Chadd /* 322b3ad188SAdrian Chadd * New-style Interrupt Framework 332b3ad188SAdrian Chadd * 342b3ad188SAdrian Chadd * TODO: - to support IPI (PPI) enabling on other CPUs if already started 352b3ad188SAdrian Chadd * - to complete things for removable PICs 362b3ad188SAdrian Chadd */ 372b3ad188SAdrian Chadd 382b3ad188SAdrian Chadd #include "opt_ddb.h" 39df7a2251SAndrew Turner #include "opt_hwpmc_hooks.h" 402b3ad188SAdrian Chadd 412b3ad188SAdrian Chadd #include <sys/param.h> 422b3ad188SAdrian Chadd #include <sys/systm.h> 432b3ad188SAdrian Chadd #include <sys/kernel.h> 442b3ad188SAdrian Chadd #include <sys/syslog.h> 452b3ad188SAdrian Chadd #include <sys/malloc.h> 462b3ad188SAdrian Chadd #include <sys/proc.h> 472b3ad188SAdrian Chadd #include <sys/queue.h> 482b3ad188SAdrian Chadd #include <sys/bus.h> 492b3ad188SAdrian Chadd #include <sys/interrupt.h> 502b3ad188SAdrian Chadd #include <sys/conf.h> 512b3ad188SAdrian Chadd #include <sys/cpuset.h> 526b42a1f4SAndrew Turner #include <sys/rman.h> 532b3ad188SAdrian Chadd #include <sys/sched.h> 542b3ad188SAdrian Chadd #include <sys/smp.h> 55df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 56df7a2251SAndrew Turner #include <sys/pmckern.h> 57df7a2251SAndrew Turner #endif 58df7a2251SAndrew Turner 592b3ad188SAdrian Chadd #include <machine/atomic.h> 602b3ad188SAdrian Chadd #include <machine/intr.h> 612b3ad188SAdrian Chadd #include <machine/cpu.h> 622b3ad188SAdrian Chadd #include <machine/smp.h> 632b3ad188SAdrian Chadd #include <machine/stdarg.h> 642b3ad188SAdrian Chadd 652b3ad188SAdrian Chadd #ifdef DDB 662b3ad188SAdrian Chadd #include <ddb/ddb.h> 672b3ad188SAdrian Chadd #endif 682b3ad188SAdrian Chadd 692b3ad188SAdrian Chadd #include "pic_if.h" 703fc155dcSAndrew Turner #include "msi_if.h" 712b3ad188SAdrian Chadd 722b3ad188SAdrian Chadd #define INTRNAME_LEN (2*MAXCOMLEN + 1) 732b3ad188SAdrian Chadd 742b3ad188SAdrian Chadd #ifdef DEBUG 752b3ad188SAdrian Chadd #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 762b3ad188SAdrian Chadd printf(fmt,##args); } while (0) 772b3ad188SAdrian Chadd #else 782b3ad188SAdrian Chadd #define debugf(fmt, args...) 792b3ad188SAdrian Chadd #endif 802b3ad188SAdrian Chadd 812b3ad188SAdrian Chadd MALLOC_DECLARE(M_INTRNG); 822b3ad188SAdrian Chadd MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); 832b3ad188SAdrian Chadd 842b3ad188SAdrian Chadd /* Main interrupt handler called from assembler -> 'hidden' for C code. */ 852b3ad188SAdrian Chadd void intr_irq_handler(struct trapframe *tf); 862b3ad188SAdrian Chadd 872b3ad188SAdrian Chadd /* Root interrupt controller stuff. */ 885b70c08cSSvatopluk Kraus device_t intr_irq_root_dev; 892b3ad188SAdrian Chadd static intr_irq_filter_t *irq_root_filter; 902b3ad188SAdrian Chadd static void *irq_root_arg; 912b3ad188SAdrian Chadd static u_int irq_root_ipicount; 922b3ad188SAdrian Chadd 93d1605cdaSAndrew Turner struct intr_pic_child { 94d1605cdaSAndrew Turner SLIST_ENTRY(intr_pic_child) pc_next; 95d1605cdaSAndrew Turner struct intr_pic *pc_pic; 96d1605cdaSAndrew Turner intr_child_irq_filter_t *pc_filter; 97d1605cdaSAndrew Turner void *pc_filter_arg; 98d1605cdaSAndrew Turner uintptr_t pc_start; 99d1605cdaSAndrew Turner uintptr_t pc_length; 100d1605cdaSAndrew Turner }; 101d1605cdaSAndrew Turner 1022b3ad188SAdrian Chadd /* Interrupt controller definition. */ 1032b3ad188SAdrian Chadd struct intr_pic { 1042b3ad188SAdrian Chadd SLIST_ENTRY(intr_pic) pic_next; 1052b3ad188SAdrian Chadd intptr_t pic_xref; /* hardware identification */ 1062b3ad188SAdrian Chadd device_t pic_dev; 1073fc155dcSAndrew Turner #define FLAG_PIC (1 << 0) 1083fc155dcSAndrew Turner #define FLAG_MSI (1 << 1) 1093fc155dcSAndrew Turner u_int pic_flags; 110d1605cdaSAndrew Turner struct mtx pic_child_lock; 111d1605cdaSAndrew Turner SLIST_HEAD(, intr_pic_child) pic_children; 1122b3ad188SAdrian Chadd }; 1132b3ad188SAdrian Chadd 1142b3ad188SAdrian Chadd static struct mtx pic_list_lock; 1152b3ad188SAdrian Chadd static SLIST_HEAD(, intr_pic) pic_list; 1162b3ad188SAdrian Chadd 1172b3ad188SAdrian Chadd static struct intr_pic *pic_lookup(device_t dev, intptr_t xref); 1182b3ad188SAdrian Chadd 1192b3ad188SAdrian Chadd /* Interrupt source definition. */ 1202b3ad188SAdrian Chadd static struct mtx isrc_table_lock; 1212b3ad188SAdrian Chadd static struct intr_irqsrc *irq_sources[NIRQ]; 1222b3ad188SAdrian Chadd u_int irq_next_free; 1232b3ad188SAdrian Chadd 1242b3ad188SAdrian Chadd #ifdef SMP 1252b3ad188SAdrian Chadd static boolean_t irq_assign_cpu = FALSE; 1262b3ad188SAdrian Chadd #endif 1272b3ad188SAdrian Chadd 1282b3ad188SAdrian Chadd /* 1292b3ad188SAdrian Chadd * - 2 counters for each I/O interrupt. 1302b3ad188SAdrian Chadd * - MAXCPU counters for each IPI counters for SMP. 1312b3ad188SAdrian Chadd */ 1322b3ad188SAdrian Chadd #ifdef SMP 1332b3ad188SAdrian Chadd #define INTRCNT_COUNT (NIRQ * 2 + INTR_IPI_COUNT * MAXCPU) 1342b3ad188SAdrian Chadd #else 1352b3ad188SAdrian Chadd #define INTRCNT_COUNT (NIRQ * 2) 1362b3ad188SAdrian Chadd #endif 1372b3ad188SAdrian Chadd 1382b3ad188SAdrian Chadd /* Data for MI statistics reporting. */ 1392b3ad188SAdrian Chadd u_long intrcnt[INTRCNT_COUNT]; 1402b3ad188SAdrian Chadd char intrnames[INTRCNT_COUNT * INTRNAME_LEN]; 1412b3ad188SAdrian Chadd size_t sintrcnt = sizeof(intrcnt); 1422b3ad188SAdrian Chadd size_t sintrnames = sizeof(intrnames); 1432b3ad188SAdrian Chadd static u_int intrcnt_index; 1442b3ad188SAdrian Chadd 1452b3ad188SAdrian Chadd /* 1462b3ad188SAdrian Chadd * Interrupt framework initialization routine. 1472b3ad188SAdrian Chadd */ 1482b3ad188SAdrian Chadd static void 1492b3ad188SAdrian Chadd intr_irq_init(void *dummy __unused) 1502b3ad188SAdrian Chadd { 1512b3ad188SAdrian Chadd 1522b3ad188SAdrian Chadd SLIST_INIT(&pic_list); 1532b3ad188SAdrian Chadd mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); 1543fc155dcSAndrew Turner 1552b3ad188SAdrian Chadd mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); 1562b3ad188SAdrian Chadd } 1572b3ad188SAdrian Chadd SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); 1582b3ad188SAdrian Chadd 1592b3ad188SAdrian Chadd static void 1602b3ad188SAdrian Chadd intrcnt_setname(const char *name, int index) 1612b3ad188SAdrian Chadd { 1622b3ad188SAdrian Chadd 1632b3ad188SAdrian Chadd snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s", 1642b3ad188SAdrian Chadd INTRNAME_LEN - 1, name); 1652b3ad188SAdrian Chadd } 1662b3ad188SAdrian Chadd 1672b3ad188SAdrian Chadd /* 1682b3ad188SAdrian Chadd * Update name for interrupt source with interrupt event. 1692b3ad188SAdrian Chadd */ 1702b3ad188SAdrian Chadd static void 1712b3ad188SAdrian Chadd intrcnt_updatename(struct intr_irqsrc *isrc) 1722b3ad188SAdrian Chadd { 1732b3ad188SAdrian Chadd 1742b3ad188SAdrian Chadd /* QQQ: What about stray counter name? */ 1752b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 1762b3ad188SAdrian Chadd intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index); 1772b3ad188SAdrian Chadd } 1782b3ad188SAdrian Chadd 1792b3ad188SAdrian Chadd /* 1802b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counter increment. 1812b3ad188SAdrian Chadd */ 1822b3ad188SAdrian Chadd static inline void 1832b3ad188SAdrian Chadd isrc_increment_count(struct intr_irqsrc *isrc) 1842b3ad188SAdrian Chadd { 1852b3ad188SAdrian Chadd 186bff6be3eSSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_PPI) 187bff6be3eSSvatopluk Kraus atomic_add_long(&isrc->isrc_count[0], 1); 188bff6be3eSSvatopluk Kraus else 1892b3ad188SAdrian Chadd isrc->isrc_count[0]++; 1902b3ad188SAdrian Chadd } 1912b3ad188SAdrian Chadd 1922b3ad188SAdrian Chadd /* 1932b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt stray counter increment. 1942b3ad188SAdrian Chadd */ 1952b3ad188SAdrian Chadd static inline void 1962b3ad188SAdrian Chadd isrc_increment_straycount(struct intr_irqsrc *isrc) 1972b3ad188SAdrian Chadd { 1982b3ad188SAdrian Chadd 1992b3ad188SAdrian Chadd isrc->isrc_count[1]++; 2002b3ad188SAdrian Chadd } 2012b3ad188SAdrian Chadd 2022b3ad188SAdrian Chadd /* 2032b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt name update. 2042b3ad188SAdrian Chadd */ 2052b3ad188SAdrian Chadd static void 2062b3ad188SAdrian Chadd isrc_update_name(struct intr_irqsrc *isrc, const char *name) 2072b3ad188SAdrian Chadd { 2082b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2092b3ad188SAdrian Chadd 2102b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2112b3ad188SAdrian Chadd 2122b3ad188SAdrian Chadd if (name != NULL) { 2132b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); 2142b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2152b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, 2162b3ad188SAdrian Chadd name); 2172b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2182b3ad188SAdrian Chadd } else { 2192b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); 2202b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2212b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); 2222b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2232b3ad188SAdrian Chadd } 2242b3ad188SAdrian Chadd } 2252b3ad188SAdrian Chadd 2262b3ad188SAdrian Chadd /* 2272b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counters setup. 2282b3ad188SAdrian Chadd */ 2292b3ad188SAdrian Chadd static void 2302b3ad188SAdrian Chadd isrc_setup_counters(struct intr_irqsrc *isrc) 2312b3ad188SAdrian Chadd { 2322b3ad188SAdrian Chadd u_int index; 2332b3ad188SAdrian Chadd 2342b3ad188SAdrian Chadd /* 2352b3ad188SAdrian Chadd * XXX - it does not work well with removable controllers and 2362b3ad188SAdrian Chadd * interrupt sources !!! 2372b3ad188SAdrian Chadd */ 2382b3ad188SAdrian Chadd index = atomic_fetchadd_int(&intrcnt_index, 2); 2392b3ad188SAdrian Chadd isrc->isrc_index = index; 2402b3ad188SAdrian Chadd isrc->isrc_count = &intrcnt[index]; 2412b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 2422b3ad188SAdrian Chadd } 2432b3ad188SAdrian Chadd 244bff6be3eSSvatopluk Kraus /* 245bff6be3eSSvatopluk Kraus * Virtualization for interrupt source interrupt counters release. 246bff6be3eSSvatopluk Kraus */ 247bff6be3eSSvatopluk Kraus static void 248bff6be3eSSvatopluk Kraus isrc_release_counters(struct intr_irqsrc *isrc) 249bff6be3eSSvatopluk Kraus { 250bff6be3eSSvatopluk Kraus 251bff6be3eSSvatopluk Kraus panic("%s: not implemented", __func__); 252bff6be3eSSvatopluk Kraus } 253bff6be3eSSvatopluk Kraus 2542b3ad188SAdrian Chadd #ifdef SMP 2552b3ad188SAdrian Chadd /* 2562b3ad188SAdrian Chadd * Virtualization for interrupt source IPI counters setup. 2572b3ad188SAdrian Chadd */ 2585b70c08cSSvatopluk Kraus u_long * 2595b70c08cSSvatopluk Kraus intr_ipi_setup_counters(const char *name) 2602b3ad188SAdrian Chadd { 2612b3ad188SAdrian Chadd u_int index, i; 2622b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2632b3ad188SAdrian Chadd 2642b3ad188SAdrian Chadd index = atomic_fetchadd_int(&intrcnt_index, MAXCPU); 2652b3ad188SAdrian Chadd for (i = 0; i < MAXCPU; i++) { 2662b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); 2672b3ad188SAdrian Chadd intrcnt_setname(str, index + i); 2682b3ad188SAdrian Chadd } 2695b70c08cSSvatopluk Kraus return (&intrcnt[index]); 2702b3ad188SAdrian Chadd } 2712b3ad188SAdrian Chadd #endif 2722b3ad188SAdrian Chadd 2732b3ad188SAdrian Chadd /* 2742b3ad188SAdrian Chadd * Main interrupt dispatch handler. It's called straight 2752b3ad188SAdrian Chadd * from the assembler, where CPU interrupt is served. 2762b3ad188SAdrian Chadd */ 2772b3ad188SAdrian Chadd void 2782b3ad188SAdrian Chadd intr_irq_handler(struct trapframe *tf) 2792b3ad188SAdrian Chadd { 2802b3ad188SAdrian Chadd struct trapframe * oldframe; 2812b3ad188SAdrian Chadd struct thread * td; 2822b3ad188SAdrian Chadd 2832b3ad188SAdrian Chadd KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__)); 2842b3ad188SAdrian Chadd 2852b3ad188SAdrian Chadd PCPU_INC(cnt.v_intr); 2862b3ad188SAdrian Chadd critical_enter(); 2872b3ad188SAdrian Chadd td = curthread; 2882b3ad188SAdrian Chadd oldframe = td->td_intr_frame; 2892b3ad188SAdrian Chadd td->td_intr_frame = tf; 2902b3ad188SAdrian Chadd irq_root_filter(irq_root_arg); 2912b3ad188SAdrian Chadd td->td_intr_frame = oldframe; 2922b3ad188SAdrian Chadd critical_exit(); 293df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 294974692e3SAndrew Turner if (pmc_hook && TRAPF_USERMODE(tf) && 295974692e3SAndrew Turner (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) 296df7a2251SAndrew Turner pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf); 297df7a2251SAndrew Turner #endif 2982b3ad188SAdrian Chadd } 2992b3ad188SAdrian Chadd 300d1605cdaSAndrew Turner int 301d1605cdaSAndrew Turner intr_child_irq_handler(struct intr_pic *parent, uintptr_t irq) 302d1605cdaSAndrew Turner { 303d1605cdaSAndrew Turner struct intr_pic_child *child; 304d1605cdaSAndrew Turner bool found; 305d1605cdaSAndrew Turner 306d1605cdaSAndrew Turner found = false; 307d1605cdaSAndrew Turner mtx_lock_spin(&parent->pic_child_lock); 308d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent->pic_children, pc_next) { 309d1605cdaSAndrew Turner if (child->pc_start <= irq && 310d1605cdaSAndrew Turner irq < (child->pc_start + child->pc_length)) { 311d1605cdaSAndrew Turner found = true; 312d1605cdaSAndrew Turner break; 313d1605cdaSAndrew Turner } 314d1605cdaSAndrew Turner } 315d1605cdaSAndrew Turner mtx_unlock_spin(&parent->pic_child_lock); 316d1605cdaSAndrew Turner 317d1605cdaSAndrew Turner if (found) 318d1605cdaSAndrew Turner return (child->pc_filter(child->pc_filter_arg, irq)); 319d1605cdaSAndrew Turner 320d1605cdaSAndrew Turner return (FILTER_STRAY); 321d1605cdaSAndrew Turner } 322d1605cdaSAndrew Turner 3232b3ad188SAdrian Chadd /* 3242b3ad188SAdrian Chadd * interrupt controller dispatch function for interrupts. It should 3252b3ad188SAdrian Chadd * be called straight from the interrupt controller, when associated interrupt 3262b3ad188SAdrian Chadd * source is learned. 3272b3ad188SAdrian Chadd */ 328bff6be3eSSvatopluk Kraus int 329bff6be3eSSvatopluk Kraus intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) 3302b3ad188SAdrian Chadd { 3312b3ad188SAdrian Chadd 3322b3ad188SAdrian Chadd KASSERT(isrc != NULL, ("%s: no source", __func__)); 3332b3ad188SAdrian Chadd 3342b3ad188SAdrian Chadd isrc_increment_count(isrc); 3352b3ad188SAdrian Chadd 3362b3ad188SAdrian Chadd #ifdef INTR_SOLO 3372b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 3382b3ad188SAdrian Chadd int error; 3392b3ad188SAdrian Chadd error = isrc->isrc_filter(isrc->isrc_arg, tf); 3402b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 3412b3ad188SAdrian Chadd if (error == FILTER_HANDLED) 342bff6be3eSSvatopluk Kraus return (0); 3432b3ad188SAdrian Chadd } else 3442b3ad188SAdrian Chadd #endif 3452b3ad188SAdrian Chadd if (isrc->isrc_event != NULL) { 3462b3ad188SAdrian Chadd if (intr_event_handle(isrc->isrc_event, tf) == 0) 347bff6be3eSSvatopluk Kraus return (0); 3482b3ad188SAdrian Chadd } 3492b3ad188SAdrian Chadd 3502b3ad188SAdrian Chadd isrc_increment_straycount(isrc); 351bff6be3eSSvatopluk Kraus return (EINVAL); 3522b3ad188SAdrian Chadd } 3532b3ad188SAdrian Chadd 3542b3ad188SAdrian Chadd /* 3552b3ad188SAdrian Chadd * Alloc unique interrupt number (resource handle) for interrupt source. 3562b3ad188SAdrian Chadd * 3572b3ad188SAdrian Chadd * There could be various strategies how to allocate free interrupt number 3582b3ad188SAdrian Chadd * (resource handle) for new interrupt source. 3592b3ad188SAdrian Chadd * 3602b3ad188SAdrian Chadd * 1. Handles are always allocated forward, so handles are not recycled 3612b3ad188SAdrian Chadd * immediately. However, if only one free handle left which is reused 3622b3ad188SAdrian Chadd * constantly... 3632b3ad188SAdrian Chadd */ 364bff6be3eSSvatopluk Kraus static inline int 365bff6be3eSSvatopluk Kraus isrc_alloc_irq(struct intr_irqsrc *isrc) 3662b3ad188SAdrian Chadd { 3672b3ad188SAdrian Chadd u_int maxirqs, irq; 3682b3ad188SAdrian Chadd 3692b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 3702b3ad188SAdrian Chadd 3712b3ad188SAdrian Chadd maxirqs = nitems(irq_sources); 3722b3ad188SAdrian Chadd if (irq_next_free >= maxirqs) 3732b3ad188SAdrian Chadd return (ENOSPC); 3742b3ad188SAdrian Chadd 3752b3ad188SAdrian Chadd for (irq = irq_next_free; irq < maxirqs; irq++) { 3762b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 3772b3ad188SAdrian Chadd goto found; 3782b3ad188SAdrian Chadd } 3792b3ad188SAdrian Chadd for (irq = 0; irq < irq_next_free; irq++) { 3802b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 3812b3ad188SAdrian Chadd goto found; 3822b3ad188SAdrian Chadd } 3832b3ad188SAdrian Chadd 3842b3ad188SAdrian Chadd irq_next_free = maxirqs; 3852b3ad188SAdrian Chadd return (ENOSPC); 3862b3ad188SAdrian Chadd 3872b3ad188SAdrian Chadd found: 3882b3ad188SAdrian Chadd isrc->isrc_irq = irq; 3892b3ad188SAdrian Chadd irq_sources[irq] = isrc; 3902b3ad188SAdrian Chadd 3912b3ad188SAdrian Chadd irq_next_free = irq + 1; 3922b3ad188SAdrian Chadd if (irq_next_free >= maxirqs) 3932b3ad188SAdrian Chadd irq_next_free = 0; 3942b3ad188SAdrian Chadd return (0); 3952b3ad188SAdrian Chadd } 396bff6be3eSSvatopluk Kraus 3972b3ad188SAdrian Chadd /* 3982b3ad188SAdrian Chadd * Free unique interrupt number (resource handle) from interrupt source. 3992b3ad188SAdrian Chadd */ 400bff6be3eSSvatopluk Kraus static inline int 4012b3ad188SAdrian Chadd isrc_free_irq(struct intr_irqsrc *isrc) 4022b3ad188SAdrian Chadd { 4032b3ad188SAdrian Chadd 404bff6be3eSSvatopluk Kraus mtx_assert(&isrc_table_lock, MA_OWNED); 4052b3ad188SAdrian Chadd 406bff6be3eSSvatopluk Kraus if (isrc->isrc_irq >= nitems(irq_sources)) 4072b3ad188SAdrian Chadd return (EINVAL); 408bff6be3eSSvatopluk Kraus if (irq_sources[isrc->isrc_irq] != isrc) 4092b3ad188SAdrian Chadd return (EINVAL); 4102b3ad188SAdrian Chadd 4112b3ad188SAdrian Chadd irq_sources[isrc->isrc_irq] = NULL; 4128442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 4132b3ad188SAdrian Chadd return (0); 4142b3ad188SAdrian Chadd } 415bff6be3eSSvatopluk Kraus 4162b3ad188SAdrian Chadd /* 4172b3ad188SAdrian Chadd * Lookup interrupt source by interrupt number (resource handle). 4182b3ad188SAdrian Chadd */ 419bff6be3eSSvatopluk Kraus static inline struct intr_irqsrc * 4202b3ad188SAdrian Chadd isrc_lookup(u_int irq) 4212b3ad188SAdrian Chadd { 4222b3ad188SAdrian Chadd 4232b3ad188SAdrian Chadd if (irq < nitems(irq_sources)) 4242b3ad188SAdrian Chadd return (irq_sources[irq]); 4252b3ad188SAdrian Chadd return (NULL); 4262b3ad188SAdrian Chadd } 4272b3ad188SAdrian Chadd 4282b3ad188SAdrian Chadd /* 429bff6be3eSSvatopluk Kraus * Initialize interrupt source and register it into global interrupt table. 4302b3ad188SAdrian Chadd */ 431bff6be3eSSvatopluk Kraus int 432bff6be3eSSvatopluk Kraus intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, 433bff6be3eSSvatopluk Kraus const char *fmt, ...) 4342b3ad188SAdrian Chadd { 435bff6be3eSSvatopluk Kraus int error; 436bff6be3eSSvatopluk Kraus va_list ap; 4372b3ad188SAdrian Chadd 438bff6be3eSSvatopluk Kraus bzero(isrc, sizeof(struct intr_irqsrc)); 439bff6be3eSSvatopluk Kraus isrc->isrc_dev = dev; 4408442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 441bff6be3eSSvatopluk Kraus isrc->isrc_flags = flags; 4422b3ad188SAdrian Chadd 443bff6be3eSSvatopluk Kraus va_start(ap, fmt); 444bff6be3eSSvatopluk Kraus vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); 445bff6be3eSSvatopluk Kraus va_end(ap); 446bff6be3eSSvatopluk Kraus 447bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 448bff6be3eSSvatopluk Kraus error = isrc_alloc_irq(isrc); 449bff6be3eSSvatopluk Kraus if (error != 0) { 450bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 451bff6be3eSSvatopluk Kraus return (error); 4522b3ad188SAdrian Chadd } 453bff6be3eSSvatopluk Kraus /* 454bff6be3eSSvatopluk Kraus * Setup interrupt counters, but not for IPI sources. Those are setup 455bff6be3eSSvatopluk Kraus * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust 456bff6be3eSSvatopluk Kraus * our counter pool. 457bff6be3eSSvatopluk Kraus */ 458bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 459bff6be3eSSvatopluk Kraus isrc_setup_counters(isrc); 460bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 461bff6be3eSSvatopluk Kraus return (0); 4622b3ad188SAdrian Chadd } 4632b3ad188SAdrian Chadd 4642b3ad188SAdrian Chadd /* 465bff6be3eSSvatopluk Kraus * Deregister interrupt source from global interrupt table. 466bff6be3eSSvatopluk Kraus */ 467bff6be3eSSvatopluk Kraus int 468bff6be3eSSvatopluk Kraus intr_isrc_deregister(struct intr_irqsrc *isrc) 469bff6be3eSSvatopluk Kraus { 470bff6be3eSSvatopluk Kraus int error; 471bff6be3eSSvatopluk Kraus 472bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 473bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 474bff6be3eSSvatopluk Kraus isrc_release_counters(isrc); 475bff6be3eSSvatopluk Kraus error = isrc_free_irq(isrc); 476bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 477bff6be3eSSvatopluk Kraus return (error); 478bff6be3eSSvatopluk Kraus } 479bff6be3eSSvatopluk Kraus 4805b613c19SSvatopluk Kraus #ifdef SMP 4815b613c19SSvatopluk Kraus /* 4825b613c19SSvatopluk Kraus * A support function for a PIC to decide if provided ISRC should be inited 4835b613c19SSvatopluk Kraus * on given cpu. The logic of INTR_ISRCF_BOUND flag and isrc_cpu member of 4845b613c19SSvatopluk Kraus * struct intr_irqsrc is the following: 4855b613c19SSvatopluk Kraus * 4865b613c19SSvatopluk Kraus * If INTR_ISRCF_BOUND is set, the ISRC should be inited only on cpus 4875b613c19SSvatopluk Kraus * set in isrc_cpu. If not, the ISRC should be inited on every cpu and 4885b613c19SSvatopluk Kraus * isrc_cpu is kept consistent with it. Thus isrc_cpu is always correct. 4895b613c19SSvatopluk Kraus */ 4905b613c19SSvatopluk Kraus bool 4915b613c19SSvatopluk Kraus intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu) 4925b613c19SSvatopluk Kraus { 4935b613c19SSvatopluk Kraus 4945b613c19SSvatopluk Kraus if (isrc->isrc_handlers == 0) 4955b613c19SSvatopluk Kraus return (false); 4965b613c19SSvatopluk Kraus if ((isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) == 0) 4975b613c19SSvatopluk Kraus return (false); 4985b613c19SSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_BOUND) 4995b613c19SSvatopluk Kraus return (CPU_ISSET(cpu, &isrc->isrc_cpu)); 5005b613c19SSvatopluk Kraus 5015b613c19SSvatopluk Kraus CPU_SET(cpu, &isrc->isrc_cpu); 5025b613c19SSvatopluk Kraus return (true); 5035b613c19SSvatopluk Kraus } 5045b613c19SSvatopluk Kraus #endif 5055b613c19SSvatopluk Kraus 5062b3ad188SAdrian Chadd #ifdef INTR_SOLO 5072b3ad188SAdrian Chadd /* 5082b3ad188SAdrian Chadd * Setup filter into interrupt source. 5092b3ad188SAdrian Chadd */ 5102b3ad188SAdrian Chadd static int 5112b3ad188SAdrian Chadd iscr_setup_filter(struct intr_irqsrc *isrc, const char *name, 5122b3ad188SAdrian Chadd intr_irq_filter_t *filter, void *arg, void **cookiep) 5132b3ad188SAdrian Chadd { 5142b3ad188SAdrian Chadd 5152b3ad188SAdrian Chadd if (filter == NULL) 5162b3ad188SAdrian Chadd return (EINVAL); 5172b3ad188SAdrian Chadd 5182b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 5192b3ad188SAdrian Chadd /* 5202b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 5212b3ad188SAdrian Chadd * how we handle interrupt sources. 5222b3ad188SAdrian Chadd */ 5232b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 5242b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 5252b3ad188SAdrian Chadd return (EBUSY); 5262b3ad188SAdrian Chadd } 5272b3ad188SAdrian Chadd isrc->isrc_filter = filter; 5282b3ad188SAdrian Chadd isrc->isrc_arg = arg; 5292b3ad188SAdrian Chadd isrc_update_name(isrc, name); 5302b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 5312b3ad188SAdrian Chadd 5322b3ad188SAdrian Chadd *cookiep = isrc; 5332b3ad188SAdrian Chadd return (0); 5342b3ad188SAdrian Chadd } 5352b3ad188SAdrian Chadd #endif 5362b3ad188SAdrian Chadd 5372b3ad188SAdrian Chadd /* 5382b3ad188SAdrian Chadd * Interrupt source pre_ithread method for MI interrupt framework. 5392b3ad188SAdrian Chadd */ 5402b3ad188SAdrian Chadd static void 5412b3ad188SAdrian Chadd intr_isrc_pre_ithread(void *arg) 5422b3ad188SAdrian Chadd { 5432b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 5442b3ad188SAdrian Chadd 5452b3ad188SAdrian Chadd PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); 5462b3ad188SAdrian Chadd } 5472b3ad188SAdrian Chadd 5482b3ad188SAdrian Chadd /* 5492b3ad188SAdrian Chadd * Interrupt source post_ithread method for MI interrupt framework. 5502b3ad188SAdrian Chadd */ 5512b3ad188SAdrian Chadd static void 5522b3ad188SAdrian Chadd intr_isrc_post_ithread(void *arg) 5532b3ad188SAdrian Chadd { 5542b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 5552b3ad188SAdrian Chadd 5562b3ad188SAdrian Chadd PIC_POST_ITHREAD(isrc->isrc_dev, isrc); 5572b3ad188SAdrian Chadd } 5582b3ad188SAdrian Chadd 5592b3ad188SAdrian Chadd /* 5602b3ad188SAdrian Chadd * Interrupt source post_filter method for MI interrupt framework. 5612b3ad188SAdrian Chadd */ 5622b3ad188SAdrian Chadd static void 5632b3ad188SAdrian Chadd intr_isrc_post_filter(void *arg) 5642b3ad188SAdrian Chadd { 5652b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 5662b3ad188SAdrian Chadd 5672b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 5682b3ad188SAdrian Chadd } 5692b3ad188SAdrian Chadd 5702b3ad188SAdrian Chadd /* 5712b3ad188SAdrian Chadd * Interrupt source assign_cpu method for MI interrupt framework. 5722b3ad188SAdrian Chadd */ 5732b3ad188SAdrian Chadd static int 5742b3ad188SAdrian Chadd intr_isrc_assign_cpu(void *arg, int cpu) 5752b3ad188SAdrian Chadd { 5762b3ad188SAdrian Chadd #ifdef SMP 5772b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 5782b3ad188SAdrian Chadd int error; 5792b3ad188SAdrian Chadd 5805b70c08cSSvatopluk Kraus if (isrc->isrc_dev != intr_irq_root_dev) 5812b3ad188SAdrian Chadd return (EINVAL); 5822b3ad188SAdrian Chadd 5832b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 5842b3ad188SAdrian Chadd if (cpu == NOCPU) { 5852b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 5862b3ad188SAdrian Chadd isrc->isrc_flags &= ~INTR_ISRCF_BOUND; 5872b3ad188SAdrian Chadd } else { 5882b3ad188SAdrian Chadd CPU_SETOF(cpu, &isrc->isrc_cpu); 5892b3ad188SAdrian Chadd isrc->isrc_flags |= INTR_ISRCF_BOUND; 5902b3ad188SAdrian Chadd } 5912b3ad188SAdrian Chadd 5922b3ad188SAdrian Chadd /* 5932b3ad188SAdrian Chadd * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or 5942b3ad188SAdrian Chadd * re-balance it to another CPU or enable it on more CPUs. However, 5952b3ad188SAdrian Chadd * PIC is expected to change isrc_cpu appropriately to keep us well 596e3043798SPedro F. Giffuni * informed if the call is successful. 5972b3ad188SAdrian Chadd */ 5982b3ad188SAdrian Chadd if (irq_assign_cpu) { 599bff6be3eSSvatopluk Kraus error = PIC_BIND_INTR(isrc->isrc_dev, isrc); 6002b3ad188SAdrian Chadd if (error) { 6012b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 6022b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6032b3ad188SAdrian Chadd return (error); 6042b3ad188SAdrian Chadd } 6052b3ad188SAdrian Chadd } 6062b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6072b3ad188SAdrian Chadd return (0); 6082b3ad188SAdrian Chadd #else 6092b3ad188SAdrian Chadd return (EOPNOTSUPP); 6102b3ad188SAdrian Chadd #endif 6112b3ad188SAdrian Chadd } 6122b3ad188SAdrian Chadd 6132b3ad188SAdrian Chadd /* 6142b3ad188SAdrian Chadd * Create interrupt event for interrupt source. 6152b3ad188SAdrian Chadd */ 6162b3ad188SAdrian Chadd static int 6172b3ad188SAdrian Chadd isrc_event_create(struct intr_irqsrc *isrc) 6182b3ad188SAdrian Chadd { 6192b3ad188SAdrian Chadd struct intr_event *ie; 6202b3ad188SAdrian Chadd int error; 6212b3ad188SAdrian Chadd 6222b3ad188SAdrian Chadd error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq, 6232b3ad188SAdrian Chadd intr_isrc_pre_ithread, intr_isrc_post_ithread, intr_isrc_post_filter, 6242b3ad188SAdrian Chadd intr_isrc_assign_cpu, "%s:", isrc->isrc_name); 6252b3ad188SAdrian Chadd if (error) 6262b3ad188SAdrian Chadd return (error); 6272b3ad188SAdrian Chadd 6282b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6292b3ad188SAdrian Chadd /* 6302b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 6312b3ad188SAdrian Chadd * how we handle interrupt sources. Let contested event wins. 6322b3ad188SAdrian Chadd */ 633169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 6342b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 635169e6abdSSvatopluk Kraus #else 636169e6abdSSvatopluk Kraus if (isrc->isrc_event != NULL) { 637169e6abdSSvatopluk Kraus #endif 6382b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6392b3ad188SAdrian Chadd intr_event_destroy(ie); 6402b3ad188SAdrian Chadd return (isrc->isrc_event != NULL ? EBUSY : 0); 6412b3ad188SAdrian Chadd } 6422b3ad188SAdrian Chadd isrc->isrc_event = ie; 6432b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6442b3ad188SAdrian Chadd 6452b3ad188SAdrian Chadd return (0); 6462b3ad188SAdrian Chadd } 6472b3ad188SAdrian Chadd #ifdef notyet 6482b3ad188SAdrian Chadd /* 6492b3ad188SAdrian Chadd * Destroy interrupt event for interrupt source. 6502b3ad188SAdrian Chadd */ 6512b3ad188SAdrian Chadd static void 6522b3ad188SAdrian Chadd isrc_event_destroy(struct intr_irqsrc *isrc) 6532b3ad188SAdrian Chadd { 6542b3ad188SAdrian Chadd struct intr_event *ie; 6552b3ad188SAdrian Chadd 6562b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6572b3ad188SAdrian Chadd ie = isrc->isrc_event; 6582b3ad188SAdrian Chadd isrc->isrc_event = NULL; 6592b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6602b3ad188SAdrian Chadd 6612b3ad188SAdrian Chadd if (ie != NULL) 6622b3ad188SAdrian Chadd intr_event_destroy(ie); 6632b3ad188SAdrian Chadd } 6642b3ad188SAdrian Chadd #endif 6652b3ad188SAdrian Chadd /* 6662b3ad188SAdrian Chadd * Add handler to interrupt source. 6672b3ad188SAdrian Chadd */ 6682b3ad188SAdrian Chadd static int 6692b3ad188SAdrian Chadd isrc_add_handler(struct intr_irqsrc *isrc, const char *name, 6702b3ad188SAdrian Chadd driver_filter_t filter, driver_intr_t handler, void *arg, 6712b3ad188SAdrian Chadd enum intr_type flags, void **cookiep) 6722b3ad188SAdrian Chadd { 6732b3ad188SAdrian Chadd int error; 6742b3ad188SAdrian Chadd 6752b3ad188SAdrian Chadd if (isrc->isrc_event == NULL) { 6762b3ad188SAdrian Chadd error = isrc_event_create(isrc); 6772b3ad188SAdrian Chadd if (error) 6782b3ad188SAdrian Chadd return (error); 6792b3ad188SAdrian Chadd } 6802b3ad188SAdrian Chadd 6812b3ad188SAdrian Chadd error = intr_event_add_handler(isrc->isrc_event, name, filter, handler, 6822b3ad188SAdrian Chadd arg, intr_priority(flags), flags, cookiep); 6832b3ad188SAdrian Chadd if (error == 0) { 6842b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6852b3ad188SAdrian Chadd intrcnt_updatename(isrc); 6862b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6872b3ad188SAdrian Chadd } 6882b3ad188SAdrian Chadd 6892b3ad188SAdrian Chadd return (error); 6902b3ad188SAdrian Chadd } 6912b3ad188SAdrian Chadd 6922b3ad188SAdrian Chadd /* 6932b3ad188SAdrian Chadd * Lookup interrupt controller locked. 6942b3ad188SAdrian Chadd */ 695bff6be3eSSvatopluk Kraus static inline struct intr_pic * 6962b3ad188SAdrian Chadd pic_lookup_locked(device_t dev, intptr_t xref) 6972b3ad188SAdrian Chadd { 6982b3ad188SAdrian Chadd struct intr_pic *pic; 6992b3ad188SAdrian Chadd 7002b3ad188SAdrian Chadd mtx_assert(&pic_list_lock, MA_OWNED); 7012b3ad188SAdrian Chadd 7024be58cbaSSvatopluk Kraus if (dev == NULL && xref == 0) 7034be58cbaSSvatopluk Kraus return (NULL); 7044be58cbaSSvatopluk Kraus 7054be58cbaSSvatopluk Kraus /* Note that pic->pic_dev is never NULL on registered PIC. */ 7062b3ad188SAdrian Chadd SLIST_FOREACH(pic, &pic_list, pic_next) { 7074be58cbaSSvatopluk Kraus if (dev == NULL) { 7084be58cbaSSvatopluk Kraus if (xref == pic->pic_xref) 7094be58cbaSSvatopluk Kraus return (pic); 7104be58cbaSSvatopluk Kraus } else if (xref == 0 || pic->pic_xref == 0) { 7114be58cbaSSvatopluk Kraus if (dev == pic->pic_dev) 7124be58cbaSSvatopluk Kraus return (pic); 7134be58cbaSSvatopluk Kraus } else if (xref == pic->pic_xref && dev == pic->pic_dev) 7142b3ad188SAdrian Chadd return (pic); 7152b3ad188SAdrian Chadd } 7162b3ad188SAdrian Chadd return (NULL); 7172b3ad188SAdrian Chadd } 7182b3ad188SAdrian Chadd 7192b3ad188SAdrian Chadd /* 7202b3ad188SAdrian Chadd * Lookup interrupt controller. 7212b3ad188SAdrian Chadd */ 7222b3ad188SAdrian Chadd static struct intr_pic * 7232b3ad188SAdrian Chadd pic_lookup(device_t dev, intptr_t xref) 7242b3ad188SAdrian Chadd { 7252b3ad188SAdrian Chadd struct intr_pic *pic; 7262b3ad188SAdrian Chadd 7272b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 7282b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 7292b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7302b3ad188SAdrian Chadd return (pic); 7312b3ad188SAdrian Chadd } 7322b3ad188SAdrian Chadd 7332b3ad188SAdrian Chadd /* 7342b3ad188SAdrian Chadd * Create interrupt controller. 7352b3ad188SAdrian Chadd */ 7362b3ad188SAdrian Chadd static struct intr_pic * 7372b3ad188SAdrian Chadd pic_create(device_t dev, intptr_t xref) 7382b3ad188SAdrian Chadd { 7392b3ad188SAdrian Chadd struct intr_pic *pic; 7402b3ad188SAdrian Chadd 7412b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 7422b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 7432b3ad188SAdrian Chadd if (pic != NULL) { 7442b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7452b3ad188SAdrian Chadd return (pic); 7462b3ad188SAdrian Chadd } 7472b3ad188SAdrian Chadd pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO); 748b48c6083SAndrew Turner if (pic == NULL) { 749b48c6083SAndrew Turner mtx_unlock(&pic_list_lock); 750b48c6083SAndrew Turner return (NULL); 751b48c6083SAndrew Turner } 7522b3ad188SAdrian Chadd pic->pic_xref = xref; 7532b3ad188SAdrian Chadd pic->pic_dev = dev; 754d1605cdaSAndrew Turner mtx_init(&pic->pic_child_lock, "pic child lock", NULL, MTX_SPIN); 7552b3ad188SAdrian Chadd SLIST_INSERT_HEAD(&pic_list, pic, pic_next); 7562b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7572b3ad188SAdrian Chadd 7582b3ad188SAdrian Chadd return (pic); 7592b3ad188SAdrian Chadd } 7602b3ad188SAdrian Chadd #ifdef notyet 7612b3ad188SAdrian Chadd /* 7622b3ad188SAdrian Chadd * Destroy interrupt controller. 7632b3ad188SAdrian Chadd */ 7642b3ad188SAdrian Chadd static void 7652b3ad188SAdrian Chadd pic_destroy(device_t dev, intptr_t xref) 7662b3ad188SAdrian Chadd { 7672b3ad188SAdrian Chadd struct intr_pic *pic; 7682b3ad188SAdrian Chadd 7692b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 7702b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 7712b3ad188SAdrian Chadd if (pic == NULL) { 7722b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7732b3ad188SAdrian Chadd return; 7742b3ad188SAdrian Chadd } 7752b3ad188SAdrian Chadd SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next); 7762b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 7772b3ad188SAdrian Chadd 7782b3ad188SAdrian Chadd free(pic, M_INTRNG); 7792b3ad188SAdrian Chadd } 7802b3ad188SAdrian Chadd #endif 7812b3ad188SAdrian Chadd /* 7822b3ad188SAdrian Chadd * Register interrupt controller. 7832b3ad188SAdrian Chadd */ 7849346e913SAndrew Turner struct intr_pic * 7852b3ad188SAdrian Chadd intr_pic_register(device_t dev, intptr_t xref) 7862b3ad188SAdrian Chadd { 7872b3ad188SAdrian Chadd struct intr_pic *pic; 7882b3ad188SAdrian Chadd 7894be58cbaSSvatopluk Kraus if (dev == NULL) 7909346e913SAndrew Turner return (NULL); 7912b3ad188SAdrian Chadd pic = pic_create(dev, xref); 7922b3ad188SAdrian Chadd if (pic == NULL) 7939346e913SAndrew Turner return (NULL); 7942b3ad188SAdrian Chadd 7953fc155dcSAndrew Turner pic->pic_flags |= FLAG_PIC; 7963fc155dcSAndrew Turner 7974be58cbaSSvatopluk Kraus debugf("PIC %p registered for %s <dev %p, xref %x>\n", pic, 7984be58cbaSSvatopluk Kraus device_get_nameunit(dev), dev, xref); 7999346e913SAndrew Turner return (pic); 8002b3ad188SAdrian Chadd } 8012b3ad188SAdrian Chadd 8022b3ad188SAdrian Chadd /* 8032b3ad188SAdrian Chadd * Unregister interrupt controller. 8042b3ad188SAdrian Chadd */ 8052b3ad188SAdrian Chadd int 806bff6be3eSSvatopluk Kraus intr_pic_deregister(device_t dev, intptr_t xref) 8072b3ad188SAdrian Chadd { 8082b3ad188SAdrian Chadd 8092b3ad188SAdrian Chadd panic("%s: not implemented", __func__); 8102b3ad188SAdrian Chadd } 8112b3ad188SAdrian Chadd 8122b3ad188SAdrian Chadd /* 8132b3ad188SAdrian Chadd * Mark interrupt controller (itself) as a root one. 8142b3ad188SAdrian Chadd * 8152b3ad188SAdrian Chadd * Note that only an interrupt controller can really know its position 8162b3ad188SAdrian Chadd * in interrupt controller's tree. So root PIC must claim itself as a root. 8172b3ad188SAdrian Chadd * 8182b3ad188SAdrian Chadd * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, 8192b3ad188SAdrian Chadd * page 30: 8202b3ad188SAdrian Chadd * "The root of the interrupt tree is determined when traversal 8212b3ad188SAdrian Chadd * of the interrupt tree reaches an interrupt controller node without 8222b3ad188SAdrian Chadd * an interrupts property and thus no explicit interrupt parent." 8232b3ad188SAdrian Chadd */ 8242b3ad188SAdrian Chadd int 8252b3ad188SAdrian Chadd intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, 8262b3ad188SAdrian Chadd void *arg, u_int ipicount) 8272b3ad188SAdrian Chadd { 8283fc155dcSAndrew Turner struct intr_pic *pic; 8292b3ad188SAdrian Chadd 8303fc155dcSAndrew Turner pic = pic_lookup(dev, xref); 8313fc155dcSAndrew Turner if (pic == NULL) { 8322b3ad188SAdrian Chadd device_printf(dev, "not registered\n"); 8332b3ad188SAdrian Chadd return (EINVAL); 8342b3ad188SAdrian Chadd } 8353fc155dcSAndrew Turner 8363fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_PIC) != 0, 8373fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 8383fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 8393fc155dcSAndrew Turner 8402b3ad188SAdrian Chadd if (filter == NULL) { 8412b3ad188SAdrian Chadd device_printf(dev, "filter missing\n"); 8422b3ad188SAdrian Chadd return (EINVAL); 8432b3ad188SAdrian Chadd } 8442b3ad188SAdrian Chadd 8452b3ad188SAdrian Chadd /* 8462b3ad188SAdrian Chadd * Only one interrupt controllers could be on the root for now. 8472b3ad188SAdrian Chadd * Note that we further suppose that there is not threaded interrupt 8482b3ad188SAdrian Chadd * routine (handler) on the root. See intr_irq_handler(). 8492b3ad188SAdrian Chadd */ 8505b70c08cSSvatopluk Kraus if (intr_irq_root_dev != NULL) { 8512b3ad188SAdrian Chadd device_printf(dev, "another root already set\n"); 8522b3ad188SAdrian Chadd return (EBUSY); 8532b3ad188SAdrian Chadd } 8542b3ad188SAdrian Chadd 8555b70c08cSSvatopluk Kraus intr_irq_root_dev = dev; 8562b3ad188SAdrian Chadd irq_root_filter = filter; 8572b3ad188SAdrian Chadd irq_root_arg = arg; 8582b3ad188SAdrian Chadd irq_root_ipicount = ipicount; 8592b3ad188SAdrian Chadd 8602b3ad188SAdrian Chadd debugf("irq root set to %s\n", device_get_nameunit(dev)); 8612b3ad188SAdrian Chadd return (0); 8622b3ad188SAdrian Chadd } 8632b3ad188SAdrian Chadd 864d1605cdaSAndrew Turner /* 865d1605cdaSAndrew Turner * Add a handler to manage a sub range of a parents interrupts. 866d1605cdaSAndrew Turner */ 867d1605cdaSAndrew Turner struct intr_pic * 868d1605cdaSAndrew Turner intr_pic_add_handler(device_t parent, struct intr_pic *pic, 869d1605cdaSAndrew Turner intr_child_irq_filter_t *filter, void *arg, uintptr_t start, 870d1605cdaSAndrew Turner uintptr_t length) 871d1605cdaSAndrew Turner { 872d1605cdaSAndrew Turner struct intr_pic *parent_pic; 873d1605cdaSAndrew Turner struct intr_pic_child *newchild; 874d1605cdaSAndrew Turner #ifdef INVARIANTS 875d1605cdaSAndrew Turner struct intr_pic_child *child; 876d1605cdaSAndrew Turner #endif 877d1605cdaSAndrew Turner 878d1605cdaSAndrew Turner parent_pic = pic_lookup(parent, 0); 879d1605cdaSAndrew Turner if (parent_pic == NULL) 880d1605cdaSAndrew Turner return (NULL); 881d1605cdaSAndrew Turner 882d1605cdaSAndrew Turner newchild = malloc(sizeof(*newchild), M_INTRNG, M_WAITOK | M_ZERO); 883d1605cdaSAndrew Turner newchild->pc_pic = pic; 884d1605cdaSAndrew Turner newchild->pc_filter = filter; 885d1605cdaSAndrew Turner newchild->pc_filter_arg = arg; 886d1605cdaSAndrew Turner newchild->pc_start = start; 887d1605cdaSAndrew Turner newchild->pc_length = length; 888d1605cdaSAndrew Turner 889d1605cdaSAndrew Turner mtx_lock_spin(&parent_pic->pic_child_lock); 890d1605cdaSAndrew Turner #ifdef INVARIANTS 891d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent_pic->pic_children, pc_next) { 892d1605cdaSAndrew Turner KASSERT(child->pc_pic != pic, ("%s: Adding a child PIC twice", 893d1605cdaSAndrew Turner __func__)); 894d1605cdaSAndrew Turner } 895d1605cdaSAndrew Turner #endif 896d1605cdaSAndrew Turner SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next); 897d1605cdaSAndrew Turner mtx_unlock_spin(&parent_pic->pic_child_lock); 898d1605cdaSAndrew Turner 899d1605cdaSAndrew Turner return (pic); 900d1605cdaSAndrew Turner } 901d1605cdaSAndrew Turner 9022b3ad188SAdrian Chadd int 903bff6be3eSSvatopluk Kraus intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data, 904bff6be3eSSvatopluk Kraus u_int *irqp) 9052b3ad188SAdrian Chadd { 9062b3ad188SAdrian Chadd int error; 907bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 908bff6be3eSSvatopluk Kraus struct intr_pic *pic; 909bff6be3eSSvatopluk Kraus 910bff6be3eSSvatopluk Kraus if (data == NULL) 911bff6be3eSSvatopluk Kraus return (EINVAL); 912bff6be3eSSvatopluk Kraus 913bff6be3eSSvatopluk Kraus pic = pic_lookup(dev, xref); 91415adccc6SSvatopluk Kraus if (pic == NULL) 915bff6be3eSSvatopluk Kraus return (ESRCH); 916bff6be3eSSvatopluk Kraus 9173fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_PIC) != 0, 9183fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 9193fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 9203fc155dcSAndrew Turner 921bff6be3eSSvatopluk Kraus error = PIC_MAP_INTR(pic->pic_dev, data, &isrc); 922bff6be3eSSvatopluk Kraus if (error == 0) 923bff6be3eSSvatopluk Kraus *irqp = isrc->isrc_irq; 924bff6be3eSSvatopluk Kraus return (error); 925bff6be3eSSvatopluk Kraus } 926bff6be3eSSvatopluk Kraus 927bff6be3eSSvatopluk Kraus int 928bff6be3eSSvatopluk Kraus intr_alloc_irq(device_t dev, struct resource *res) 929bff6be3eSSvatopluk Kraus { 930bff6be3eSSvatopluk Kraus struct intr_map_data *data; 931bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 932bff6be3eSSvatopluk Kraus 933bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 934bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 935bff6be3eSSvatopluk Kraus 936ad5244ecSSvatopluk Kraus isrc = isrc_lookup(rman_get_start(res)); 937bff6be3eSSvatopluk Kraus if (isrc == NULL) 938bff6be3eSSvatopluk Kraus return (EINVAL); 939bff6be3eSSvatopluk Kraus 940*c4263292SSvatopluk Kraus data = rman_get_virtual(res); 941bff6be3eSSvatopluk Kraus return (PIC_ALLOC_INTR(isrc->isrc_dev, isrc, res, data)); 942bff6be3eSSvatopluk Kraus } 943bff6be3eSSvatopluk Kraus 944bff6be3eSSvatopluk Kraus int 945bff6be3eSSvatopluk Kraus intr_release_irq(device_t dev, struct resource *res) 946bff6be3eSSvatopluk Kraus { 947bff6be3eSSvatopluk Kraus struct intr_map_data *data; 948bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 949bff6be3eSSvatopluk Kraus 950bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 951bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 952bff6be3eSSvatopluk Kraus 953ad5244ecSSvatopluk Kraus isrc = isrc_lookup(rman_get_start(res)); 954bff6be3eSSvatopluk Kraus if (isrc == NULL) 955bff6be3eSSvatopluk Kraus return (EINVAL); 956bff6be3eSSvatopluk Kraus 957*c4263292SSvatopluk Kraus data = rman_get_virtual(res); 958bff6be3eSSvatopluk Kraus return (PIC_RELEASE_INTR(isrc->isrc_dev, isrc, res, data)); 959bff6be3eSSvatopluk Kraus } 960bff6be3eSSvatopluk Kraus 961bff6be3eSSvatopluk Kraus int 962bff6be3eSSvatopluk Kraus intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, 963bff6be3eSSvatopluk Kraus driver_intr_t hand, void *arg, int flags, void **cookiep) 964bff6be3eSSvatopluk Kraus { 965bff6be3eSSvatopluk Kraus int error; 966bff6be3eSSvatopluk Kraus struct intr_map_data *data; 967bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 968bff6be3eSSvatopluk Kraus const char *name; 969bff6be3eSSvatopluk Kraus 970bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 971bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 972bff6be3eSSvatopluk Kraus 973ad5244ecSSvatopluk Kraus isrc = isrc_lookup(rman_get_start(res)); 974bff6be3eSSvatopluk Kraus if (isrc == NULL) 975bff6be3eSSvatopluk Kraus return (EINVAL); 9762b3ad188SAdrian Chadd 977*c4263292SSvatopluk Kraus data = rman_get_virtual(res); 9782b3ad188SAdrian Chadd name = device_get_nameunit(dev); 9792b3ad188SAdrian Chadd 9802b3ad188SAdrian Chadd #ifdef INTR_SOLO 9812b3ad188SAdrian Chadd /* 982e3043798SPedro F. Giffuni * Standard handling is done through MI interrupt framework. However, 9832b3ad188SAdrian Chadd * some interrupts could request solely own special handling. This 9842b3ad188SAdrian Chadd * non standard handling can be used for interrupt controllers without 9852b3ad188SAdrian Chadd * handler (filter only), so in case that interrupt controllers are 9862b3ad188SAdrian Chadd * chained, MI interrupt framework is called only in leaf controller. 9872b3ad188SAdrian Chadd * 9882b3ad188SAdrian Chadd * Note that root interrupt controller routine is served as well, 9892b3ad188SAdrian Chadd * however in intr_irq_handler(), i.e. main system dispatch routine. 9902b3ad188SAdrian Chadd */ 9912b3ad188SAdrian Chadd if (flags & INTR_SOLO && hand != NULL) { 9922b3ad188SAdrian Chadd debugf("irq %u cannot solo on %s\n", irq, name); 9932b3ad188SAdrian Chadd return (EINVAL); 9942b3ad188SAdrian Chadd } 9952b3ad188SAdrian Chadd 9962b3ad188SAdrian Chadd if (flags & INTR_SOLO) { 9972b3ad188SAdrian Chadd error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, 9982b3ad188SAdrian Chadd arg, cookiep); 9992b3ad188SAdrian Chadd debugf("irq %u setup filter error %d on %s\n", irq, error, 10002b3ad188SAdrian Chadd name); 10012b3ad188SAdrian Chadd } else 10022b3ad188SAdrian Chadd #endif 10032b3ad188SAdrian Chadd { 10042b3ad188SAdrian Chadd error = isrc_add_handler(isrc, name, filt, hand, arg, flags, 10052b3ad188SAdrian Chadd cookiep); 10062b3ad188SAdrian Chadd debugf("irq %u add handler error %d on %s\n", irq, error, name); 10072b3ad188SAdrian Chadd } 10082b3ad188SAdrian Chadd if (error != 0) 10092b3ad188SAdrian Chadd return (error); 10102b3ad188SAdrian Chadd 10112b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1012bff6be3eSSvatopluk Kraus error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); 1013bff6be3eSSvatopluk Kraus if (error == 0) { 10142b3ad188SAdrian Chadd isrc->isrc_handlers++; 1015bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 1) 10162b3ad188SAdrian Chadd PIC_ENABLE_INTR(isrc->isrc_dev, isrc); 10172b3ad188SAdrian Chadd } 10182b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 1019bff6be3eSSvatopluk Kraus if (error != 0) 1020bff6be3eSSvatopluk Kraus intr_event_remove_handler(*cookiep); 1021bff6be3eSSvatopluk Kraus return (error); 10222b3ad188SAdrian Chadd } 10232b3ad188SAdrian Chadd 10242b3ad188SAdrian Chadd int 1025bff6be3eSSvatopluk Kraus intr_teardown_irq(device_t dev, struct resource *res, void *cookie) 10262b3ad188SAdrian Chadd { 10272b3ad188SAdrian Chadd int error; 1028bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1029bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 10302b3ad188SAdrian Chadd 1031bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1032bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1033bff6be3eSSvatopluk Kraus 1034ad5244ecSSvatopluk Kraus isrc = isrc_lookup(rman_get_start(res)); 10352b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 10362b3ad188SAdrian Chadd return (EINVAL); 1037bff6be3eSSvatopluk Kraus 1038*c4263292SSvatopluk Kraus data = rman_get_virtual(res); 1039*c4263292SSvatopluk Kraus 1040169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 10412b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 10422b3ad188SAdrian Chadd if (isrc != cookie) 10432b3ad188SAdrian Chadd return (EINVAL); 10442b3ad188SAdrian Chadd 10452b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 10462b3ad188SAdrian Chadd isrc->isrc_filter = NULL; 10472b3ad188SAdrian Chadd isrc->isrc_arg = NULL; 10482b3ad188SAdrian Chadd isrc->isrc_handlers = 0; 10492b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1050bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 10512b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 10522b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 10532b3ad188SAdrian Chadd return (0); 10542b3ad188SAdrian Chadd } 1055169e6abdSSvatopluk Kraus #endif 10562b3ad188SAdrian Chadd if (isrc != intr_handler_source(cookie)) 10572b3ad188SAdrian Chadd return (EINVAL); 10582b3ad188SAdrian Chadd 10592b3ad188SAdrian Chadd error = intr_event_remove_handler(cookie); 10602b3ad188SAdrian Chadd if (error == 0) { 10612b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 10622b3ad188SAdrian Chadd isrc->isrc_handlers--; 1063bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 0) 10642b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1065bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 10662b3ad188SAdrian Chadd intrcnt_updatename(isrc); 10672b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 10682b3ad188SAdrian Chadd } 10692b3ad188SAdrian Chadd return (error); 10702b3ad188SAdrian Chadd } 10712b3ad188SAdrian Chadd 10722b3ad188SAdrian Chadd int 1073bff6be3eSSvatopluk Kraus intr_describe_irq(device_t dev, struct resource *res, void *cookie, 1074bff6be3eSSvatopluk Kraus const char *descr) 10752b3ad188SAdrian Chadd { 10762b3ad188SAdrian Chadd int error; 1077bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 10782b3ad188SAdrian Chadd 1079bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1080bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1081bff6be3eSSvatopluk Kraus 1082*c4263292SSvatopluk Kraus isrc = isrc_lookup(rman_get_start(res)); 10832b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 10842b3ad188SAdrian Chadd return (EINVAL); 1085169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 10862b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 10872b3ad188SAdrian Chadd if (isrc != cookie) 10882b3ad188SAdrian Chadd return (EINVAL); 10892b3ad188SAdrian Chadd 10902b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 10912b3ad188SAdrian Chadd isrc_update_name(isrc, descr); 10922b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 10932b3ad188SAdrian Chadd return (0); 10942b3ad188SAdrian Chadd } 1095169e6abdSSvatopluk Kraus #endif 10962b3ad188SAdrian Chadd error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); 10972b3ad188SAdrian Chadd if (error == 0) { 10982b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 10992b3ad188SAdrian Chadd intrcnt_updatename(isrc); 11002b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11012b3ad188SAdrian Chadd } 11022b3ad188SAdrian Chadd return (error); 11032b3ad188SAdrian Chadd } 11042b3ad188SAdrian Chadd 11052b3ad188SAdrian Chadd #ifdef SMP 11062b3ad188SAdrian Chadd int 1107bff6be3eSSvatopluk Kraus intr_bind_irq(device_t dev, struct resource *res, int cpu) 11082b3ad188SAdrian Chadd { 11092b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 11102b3ad188SAdrian Chadd 1111bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1112bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1113bff6be3eSSvatopluk Kraus 1114*c4263292SSvatopluk Kraus isrc = isrc_lookup(rman_get_start(res)); 11152b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 11162b3ad188SAdrian Chadd return (EINVAL); 1117169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 11182b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) 11192b3ad188SAdrian Chadd return (intr_isrc_assign_cpu(isrc, cpu)); 1120169e6abdSSvatopluk Kraus #endif 11212b3ad188SAdrian Chadd return (intr_event_bind(isrc->isrc_event, cpu)); 11222b3ad188SAdrian Chadd } 11232b3ad188SAdrian Chadd 11242b3ad188SAdrian Chadd /* 11252b3ad188SAdrian Chadd * Return the CPU that the next interrupt source should use. 11262b3ad188SAdrian Chadd * For now just returns the next CPU according to round-robin. 11272b3ad188SAdrian Chadd */ 11282b3ad188SAdrian Chadd u_int 11292b3ad188SAdrian Chadd intr_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask) 11302b3ad188SAdrian Chadd { 11312b3ad188SAdrian Chadd 11322b3ad188SAdrian Chadd if (!irq_assign_cpu || mp_ncpus == 1) 11332b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 11342b3ad188SAdrian Chadd 11352b3ad188SAdrian Chadd do { 11362b3ad188SAdrian Chadd last_cpu++; 11372b3ad188SAdrian Chadd if (last_cpu > mp_maxid) 11382b3ad188SAdrian Chadd last_cpu = 0; 11392b3ad188SAdrian Chadd } while (!CPU_ISSET(last_cpu, cpumask)); 11402b3ad188SAdrian Chadd return (last_cpu); 11412b3ad188SAdrian Chadd } 11422b3ad188SAdrian Chadd 11432b3ad188SAdrian Chadd /* 11442b3ad188SAdrian Chadd * Distribute all the interrupt sources among the available 11452b3ad188SAdrian Chadd * CPUs once the AP's have been launched. 11462b3ad188SAdrian Chadd */ 11472b3ad188SAdrian Chadd static void 11482b3ad188SAdrian Chadd intr_irq_shuffle(void *arg __unused) 11492b3ad188SAdrian Chadd { 11502b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 11512b3ad188SAdrian Chadd u_int i; 11522b3ad188SAdrian Chadd 11532b3ad188SAdrian Chadd if (mp_ncpus == 1) 11542b3ad188SAdrian Chadd return; 11552b3ad188SAdrian Chadd 11562b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11572b3ad188SAdrian Chadd irq_assign_cpu = TRUE; 11582b3ad188SAdrian Chadd for (i = 0; i < NIRQ; i++) { 11592b3ad188SAdrian Chadd isrc = irq_sources[i]; 11602b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0 || 1161cf55df9fSSvatopluk Kraus isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) 11622b3ad188SAdrian Chadd continue; 11632b3ad188SAdrian Chadd 11642b3ad188SAdrian Chadd if (isrc->isrc_event != NULL && 11652b3ad188SAdrian Chadd isrc->isrc_flags & INTR_ISRCF_BOUND && 11662b3ad188SAdrian Chadd isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1) 11672b3ad188SAdrian Chadd panic("%s: CPU inconsistency", __func__); 11682b3ad188SAdrian Chadd 11692b3ad188SAdrian Chadd if ((isrc->isrc_flags & INTR_ISRCF_BOUND) == 0) 11702b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); /* start again */ 11712b3ad188SAdrian Chadd 11722b3ad188SAdrian Chadd /* 11732b3ad188SAdrian Chadd * We are in wicked position here if the following call fails 11742b3ad188SAdrian Chadd * for bound ISRC. The best thing we can do is to clear 11752b3ad188SAdrian Chadd * isrc_cpu so inconsistency with ie_cpu will be detectable. 11762b3ad188SAdrian Chadd */ 1177bff6be3eSSvatopluk Kraus if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0) 11782b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 11792b3ad188SAdrian Chadd } 11802b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11812b3ad188SAdrian Chadd } 11822b3ad188SAdrian Chadd SYSINIT(intr_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, intr_irq_shuffle, NULL); 11832b3ad188SAdrian Chadd 11842b3ad188SAdrian Chadd #else 11852b3ad188SAdrian Chadd u_int 11862b3ad188SAdrian Chadd intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) 11872b3ad188SAdrian Chadd { 11882b3ad188SAdrian Chadd 11892b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 11902b3ad188SAdrian Chadd } 11912b3ad188SAdrian Chadd #endif 11922b3ad188SAdrian Chadd 11933fc155dcSAndrew Turner /* 11943fc155dcSAndrew Turner * Register a MSI/MSI-X interrupt controller 11953fc155dcSAndrew Turner */ 11963fc155dcSAndrew Turner int 11973fc155dcSAndrew Turner intr_msi_register(device_t dev, intptr_t xref) 11983fc155dcSAndrew Turner { 11993fc155dcSAndrew Turner struct intr_pic *pic; 12003fc155dcSAndrew Turner 12013fc155dcSAndrew Turner if (dev == NULL) 12023fc155dcSAndrew Turner return (EINVAL); 12033fc155dcSAndrew Turner pic = pic_create(dev, xref); 12043fc155dcSAndrew Turner if (pic == NULL) 12053fc155dcSAndrew Turner return (ENOMEM); 12063fc155dcSAndrew Turner 12073fc155dcSAndrew Turner pic->pic_flags |= FLAG_MSI; 12083fc155dcSAndrew Turner 12093fc155dcSAndrew Turner debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic, 12103fc155dcSAndrew Turner device_get_nameunit(dev), dev, (uintmax_t)xref); 12113fc155dcSAndrew Turner return (0); 12123fc155dcSAndrew Turner } 12133fc155dcSAndrew Turner 12143fc155dcSAndrew Turner int 12153fc155dcSAndrew Turner intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count, 12163fc155dcSAndrew Turner int maxcount, int *irqs) 12173fc155dcSAndrew Turner { 12183fc155dcSAndrew Turner struct intr_irqsrc **isrc; 12193fc155dcSAndrew Turner struct intr_pic *pic; 12203fc155dcSAndrew Turner device_t pdev; 12213fc155dcSAndrew Turner int err, i; 12223fc155dcSAndrew Turner 12233fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 12243fc155dcSAndrew Turner if (pic == NULL) 12253fc155dcSAndrew Turner return (ESRCH); 12263fc155dcSAndrew Turner 12273fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 12283fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 12293fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 12303fc155dcSAndrew Turner 12313fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 12323fc155dcSAndrew Turner err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc); 12333fc155dcSAndrew Turner if (err == 0) { 12343fc155dcSAndrew Turner for (i = 0; i < count; i++) { 12353fc155dcSAndrew Turner irqs[i] = isrc[i]->isrc_irq; 12363fc155dcSAndrew Turner } 12373fc155dcSAndrew Turner } 12383fc155dcSAndrew Turner 12393fc155dcSAndrew Turner free(isrc, M_INTRNG); 12403fc155dcSAndrew Turner 12413fc155dcSAndrew Turner return (err); 12423fc155dcSAndrew Turner } 12433fc155dcSAndrew Turner 12443fc155dcSAndrew Turner int 12453fc155dcSAndrew Turner intr_release_msi(device_t pci, device_t child, intptr_t xref, int count, 12463fc155dcSAndrew Turner int *irqs) 12473fc155dcSAndrew Turner { 12483fc155dcSAndrew Turner struct intr_irqsrc **isrc; 12493fc155dcSAndrew Turner struct intr_pic *pic; 12503fc155dcSAndrew Turner int i, err; 12513fc155dcSAndrew Turner 12523fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 12533fc155dcSAndrew Turner if (pic == NULL) 12543fc155dcSAndrew Turner return (ESRCH); 12553fc155dcSAndrew Turner 12563fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 12573fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 12583fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 12593fc155dcSAndrew Turner 12603fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 12613fc155dcSAndrew Turner 12623fc155dcSAndrew Turner for (i = 0; i < count; i++) { 12633fc155dcSAndrew Turner isrc[i] = isrc_lookup(irqs[i]); 12643fc155dcSAndrew Turner if (isrc == NULL) { 12653fc155dcSAndrew Turner free(isrc, M_INTRNG); 12663fc155dcSAndrew Turner return (EINVAL); 12673fc155dcSAndrew Turner } 12683fc155dcSAndrew Turner } 12693fc155dcSAndrew Turner 12703fc155dcSAndrew Turner err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc); 12713fc155dcSAndrew Turner free(isrc, M_INTRNG); 12723fc155dcSAndrew Turner return (err); 12733fc155dcSAndrew Turner } 12743fc155dcSAndrew Turner 12753fc155dcSAndrew Turner int 12763fc155dcSAndrew Turner intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq) 12773fc155dcSAndrew Turner { 12783fc155dcSAndrew Turner struct intr_irqsrc *isrc; 12793fc155dcSAndrew Turner struct intr_pic *pic; 12803fc155dcSAndrew Turner device_t pdev; 12813fc155dcSAndrew Turner int err; 12823fc155dcSAndrew Turner 12833fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 12843fc155dcSAndrew Turner if (pic == NULL) 12853fc155dcSAndrew Turner return (ESRCH); 12863fc155dcSAndrew Turner 12873fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 12883fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 12893fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 12903fc155dcSAndrew Turner 12913fc155dcSAndrew Turner err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc); 12923fc155dcSAndrew Turner if (err != 0) 12933fc155dcSAndrew Turner return (err); 12943fc155dcSAndrew Turner 12953fc155dcSAndrew Turner *irq = isrc->isrc_irq; 12963fc155dcSAndrew Turner return (0); 12973fc155dcSAndrew Turner } 12983fc155dcSAndrew Turner 12993fc155dcSAndrew Turner int 13003fc155dcSAndrew Turner intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq) 13013fc155dcSAndrew Turner { 13023fc155dcSAndrew Turner struct intr_irqsrc *isrc; 13033fc155dcSAndrew Turner struct intr_pic *pic; 13043fc155dcSAndrew Turner int err; 13053fc155dcSAndrew Turner 13063fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 13073fc155dcSAndrew Turner if (pic == NULL) 13083fc155dcSAndrew Turner return (ESRCH); 13093fc155dcSAndrew Turner 13103fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 13113fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 13123fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 13133fc155dcSAndrew Turner 13143fc155dcSAndrew Turner isrc = isrc_lookup(irq); 13153fc155dcSAndrew Turner if (isrc == NULL) 13163fc155dcSAndrew Turner return (EINVAL); 13173fc155dcSAndrew Turner 13183fc155dcSAndrew Turner err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc); 13193fc155dcSAndrew Turner return (err); 13203fc155dcSAndrew Turner } 13213fc155dcSAndrew Turner 13223fc155dcSAndrew Turner int 13233fc155dcSAndrew Turner intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq, 13243fc155dcSAndrew Turner uint64_t *addr, uint32_t *data) 13253fc155dcSAndrew Turner { 13263fc155dcSAndrew Turner struct intr_irqsrc *isrc; 13273fc155dcSAndrew Turner struct intr_pic *pic; 13283fc155dcSAndrew Turner int err; 13293fc155dcSAndrew Turner 13303fc155dcSAndrew Turner pic = pic_lookup(NULL, xref); 13313fc155dcSAndrew Turner if (pic == NULL) 13323fc155dcSAndrew Turner return (ESRCH); 13333fc155dcSAndrew Turner 13343fc155dcSAndrew Turner KASSERT((pic->pic_flags & FLAG_MSI) != 0, 13353fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 13363fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 13373fc155dcSAndrew Turner 13383fc155dcSAndrew Turner isrc = isrc_lookup(irq); 13393fc155dcSAndrew Turner if (isrc == NULL) 13403fc155dcSAndrew Turner return (EINVAL); 13413fc155dcSAndrew Turner 13423fc155dcSAndrew Turner err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data); 13433fc155dcSAndrew Turner return (err); 13443fc155dcSAndrew Turner } 13453fc155dcSAndrew Turner 13463fc155dcSAndrew Turner 13472b3ad188SAdrian Chadd void dosoftints(void); 13482b3ad188SAdrian Chadd void 13492b3ad188SAdrian Chadd dosoftints(void) 13502b3ad188SAdrian Chadd { 13512b3ad188SAdrian Chadd } 13522b3ad188SAdrian Chadd 13532b3ad188SAdrian Chadd #ifdef SMP 13542b3ad188SAdrian Chadd /* 13552b3ad188SAdrian Chadd * Init interrupt controller on another CPU. 13562b3ad188SAdrian Chadd */ 13572b3ad188SAdrian Chadd void 13582b3ad188SAdrian Chadd intr_pic_init_secondary(void) 13592b3ad188SAdrian Chadd { 13602b3ad188SAdrian Chadd 13612b3ad188SAdrian Chadd /* 13622b3ad188SAdrian Chadd * QQQ: Only root PIC is aware of other CPUs ??? 13632b3ad188SAdrian Chadd */ 13645b70c08cSSvatopluk Kraus KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); 13652b3ad188SAdrian Chadd 13662b3ad188SAdrian Chadd //mtx_lock(&isrc_table_lock); 13675b70c08cSSvatopluk Kraus PIC_INIT_SECONDARY(intr_irq_root_dev); 13682b3ad188SAdrian Chadd //mtx_unlock(&isrc_table_lock); 13692b3ad188SAdrian Chadd } 13702b3ad188SAdrian Chadd #endif 13712b3ad188SAdrian Chadd 13722b3ad188SAdrian Chadd #ifdef DDB 13732b3ad188SAdrian Chadd DB_SHOW_COMMAND(irqs, db_show_irqs) 13742b3ad188SAdrian Chadd { 13752b3ad188SAdrian Chadd u_int i, irqsum; 1376bff6be3eSSvatopluk Kraus u_long num; 13772b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 13782b3ad188SAdrian Chadd 13792b3ad188SAdrian Chadd for (irqsum = 0, i = 0; i < NIRQ; i++) { 13802b3ad188SAdrian Chadd isrc = irq_sources[i]; 13812b3ad188SAdrian Chadd if (isrc == NULL) 13822b3ad188SAdrian Chadd continue; 13832b3ad188SAdrian Chadd 1384bff6be3eSSvatopluk Kraus num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; 13852b3ad188SAdrian Chadd db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, 13862b3ad188SAdrian Chadd isrc->isrc_name, isrc->isrc_cpu.__bits[0], 1387bff6be3eSSvatopluk Kraus isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); 1388bff6be3eSSvatopluk Kraus irqsum += num; 13892b3ad188SAdrian Chadd } 13902b3ad188SAdrian Chadd db_printf("irq total %u\n", irqsum); 13912b3ad188SAdrian Chadd } 13922b3ad188SAdrian Chadd #endif 1393