12b3ad188SAdrian Chadd /*- 2bff6be3eSSvatopluk Kraus * Copyright (c) 2015-2016 Svatopluk Kraus 3bff6be3eSSvatopluk Kraus * Copyright (c) 2015-2016 Michal Meloun 42b3ad188SAdrian Chadd * All rights reserved. 52b3ad188SAdrian Chadd * 62b3ad188SAdrian Chadd * Redistribution and use in source and binary forms, with or without 72b3ad188SAdrian Chadd * modification, are permitted provided that the following conditions 82b3ad188SAdrian Chadd * are met: 92b3ad188SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 102b3ad188SAdrian Chadd * notice, this list of conditions and the following disclaimer. 112b3ad188SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 122b3ad188SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 132b3ad188SAdrian Chadd * documentation and/or other materials provided with the distribution. 142b3ad188SAdrian Chadd * 152b3ad188SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 162b3ad188SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 172b3ad188SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 182b3ad188SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 192b3ad188SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 202b3ad188SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 212b3ad188SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 222b3ad188SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 232b3ad188SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 242b3ad188SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 252b3ad188SAdrian Chadd * SUCH DAMAGE. 262b3ad188SAdrian Chadd */ 272b3ad188SAdrian Chadd 282b3ad188SAdrian Chadd #include <sys/cdefs.h> 292b3ad188SAdrian Chadd __FBSDID("$FreeBSD$"); 302b3ad188SAdrian Chadd 312b3ad188SAdrian Chadd /* 322b3ad188SAdrian Chadd * New-style Interrupt Framework 332b3ad188SAdrian Chadd * 342b3ad188SAdrian Chadd * TODO: - to support IPI (PPI) enabling on other CPUs if already started 352b3ad188SAdrian Chadd * - to complete things for removable PICs 362b3ad188SAdrian Chadd */ 372b3ad188SAdrian Chadd 38bff6be3eSSvatopluk Kraus #include "opt_acpi.h" 392b3ad188SAdrian Chadd #include "opt_ddb.h" 402b3ad188SAdrian Chadd #include "opt_platform.h" 412b3ad188SAdrian Chadd 422b3ad188SAdrian Chadd #include <sys/param.h> 432b3ad188SAdrian Chadd #include <sys/systm.h> 442b3ad188SAdrian Chadd #include <sys/kernel.h> 452b3ad188SAdrian Chadd #include <sys/syslog.h> 462b3ad188SAdrian Chadd #include <sys/malloc.h> 472b3ad188SAdrian Chadd #include <sys/proc.h> 482b3ad188SAdrian Chadd #include <sys/queue.h> 492b3ad188SAdrian Chadd #include <sys/bus.h> 502b3ad188SAdrian Chadd #include <sys/interrupt.h> 512b3ad188SAdrian Chadd #include <sys/conf.h> 522b3ad188SAdrian Chadd #include <sys/cpuset.h> 536b42a1f4SAndrew Turner #include <sys/rman.h> 542b3ad188SAdrian Chadd #include <sys/sched.h> 552b3ad188SAdrian Chadd #include <sys/smp.h> 562b3ad188SAdrian Chadd #include <machine/atomic.h> 572b3ad188SAdrian Chadd #include <machine/intr.h> 582b3ad188SAdrian Chadd #include <machine/cpu.h> 592b3ad188SAdrian Chadd #include <machine/smp.h> 602b3ad188SAdrian Chadd #include <machine/stdarg.h> 612b3ad188SAdrian Chadd 620cc5515aSAdrian Chadd #ifdef FDT 632b3ad188SAdrian Chadd #include <dev/ofw/openfirm.h> 642b3ad188SAdrian Chadd #include <dev/ofw/ofw_bus.h> 652b3ad188SAdrian Chadd #include <dev/ofw/ofw_bus_subr.h> 660cc5515aSAdrian Chadd #endif 672b3ad188SAdrian Chadd 682b3ad188SAdrian Chadd #ifdef DDB 692b3ad188SAdrian Chadd #include <ddb/ddb.h> 702b3ad188SAdrian Chadd #endif 712b3ad188SAdrian Chadd 722b3ad188SAdrian Chadd #include "pic_if.h" 732b3ad188SAdrian Chadd 742b3ad188SAdrian Chadd #define INTRNAME_LEN (2*MAXCOMLEN + 1) 752b3ad188SAdrian Chadd 762b3ad188SAdrian Chadd #ifdef DEBUG 772b3ad188SAdrian Chadd #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 782b3ad188SAdrian Chadd printf(fmt,##args); } while (0) 792b3ad188SAdrian Chadd #else 802b3ad188SAdrian Chadd #define debugf(fmt, args...) 812b3ad188SAdrian Chadd #endif 822b3ad188SAdrian Chadd 832b3ad188SAdrian Chadd MALLOC_DECLARE(M_INTRNG); 842b3ad188SAdrian Chadd MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); 852b3ad188SAdrian Chadd 862b3ad188SAdrian Chadd /* Main interrupt handler called from assembler -> 'hidden' for C code. */ 872b3ad188SAdrian Chadd void intr_irq_handler(struct trapframe *tf); 882b3ad188SAdrian Chadd 892b3ad188SAdrian Chadd /* Root interrupt controller stuff. */ 905b70c08cSSvatopluk Kraus device_t intr_irq_root_dev; 912b3ad188SAdrian Chadd static intr_irq_filter_t *irq_root_filter; 922b3ad188SAdrian Chadd static void *irq_root_arg; 932b3ad188SAdrian Chadd static u_int irq_root_ipicount; 942b3ad188SAdrian Chadd 952b3ad188SAdrian Chadd /* Interrupt controller definition. */ 962b3ad188SAdrian Chadd struct intr_pic { 972b3ad188SAdrian Chadd SLIST_ENTRY(intr_pic) pic_next; 982b3ad188SAdrian Chadd intptr_t pic_xref; /* hardware identification */ 992b3ad188SAdrian Chadd device_t pic_dev; 1002b3ad188SAdrian Chadd }; 1012b3ad188SAdrian Chadd 1022b3ad188SAdrian Chadd static struct mtx pic_list_lock; 1032b3ad188SAdrian Chadd static SLIST_HEAD(, intr_pic) pic_list; 1042b3ad188SAdrian Chadd 1052b3ad188SAdrian Chadd static struct intr_pic *pic_lookup(device_t dev, intptr_t xref); 1062b3ad188SAdrian Chadd 1072b3ad188SAdrian Chadd /* Interrupt source definition. */ 1082b3ad188SAdrian Chadd static struct mtx isrc_table_lock; 1092b3ad188SAdrian Chadd static struct intr_irqsrc *irq_sources[NIRQ]; 1102b3ad188SAdrian Chadd u_int irq_next_free; 1112b3ad188SAdrian Chadd 112bff6be3eSSvatopluk Kraus /* 113bff6be3eSSvatopluk Kraus * XXX - All stuff around struct intr_dev_data is considered as temporary 114bff6be3eSSvatopluk Kraus * until better place for storing struct intr_map_data will be find. 115bff6be3eSSvatopluk Kraus * 116bff6be3eSSvatopluk Kraus * For now, there are two global interrupt numbers spaces: 117bff6be3eSSvatopluk Kraus * <0, NIRQ) ... interrupts without config data 118bff6be3eSSvatopluk Kraus * managed in irq_sources[] 119bff6be3eSSvatopluk Kraus * IRQ_DDATA_BASE + <0, 2 * NIRQ) ... interrupts with config data 120bff6be3eSSvatopluk Kraus * managed in intr_ddata_tab[] 121bff6be3eSSvatopluk Kraus * 122bff6be3eSSvatopluk Kraus * Read intr_ddata_lookup() to see how these spaces are worked with. 123bff6be3eSSvatopluk Kraus * Note that each interrupt number from second space duplicates some number 124bff6be3eSSvatopluk Kraus * from first space at this moment. An interrupt number from first space can 125bff6be3eSSvatopluk Kraus * be duplicated even multiple times in second space. 126bff6be3eSSvatopluk Kraus */ 127bff6be3eSSvatopluk Kraus struct intr_dev_data { 128bff6be3eSSvatopluk Kraus device_t idd_dev; 129bff6be3eSSvatopluk Kraus intptr_t idd_xref; 130bff6be3eSSvatopluk Kraus u_int idd_irq; 131bff6be3eSSvatopluk Kraus struct intr_map_data idd_data; 132bff6be3eSSvatopluk Kraus struct intr_irqsrc * idd_isrc; 133bff6be3eSSvatopluk Kraus }; 134bff6be3eSSvatopluk Kraus 135bff6be3eSSvatopluk Kraus static struct intr_dev_data *intr_ddata_tab[2 * NIRQ]; 136bff6be3eSSvatopluk Kraus static u_int intr_ddata_first_unused; 137bff6be3eSSvatopluk Kraus 138bff6be3eSSvatopluk Kraus #define IRQ_DDATA_BASE 10000 1398442087fSMichal Meloun CTASSERT(IRQ_DDATA_BASE > nitems(irq_sources)); 140bff6be3eSSvatopluk Kraus 1412b3ad188SAdrian Chadd #ifdef SMP 1422b3ad188SAdrian Chadd static boolean_t irq_assign_cpu = FALSE; 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 1622b3ad188SAdrian Chadd /* 1632b3ad188SAdrian Chadd * Interrupt framework initialization routine. 1642b3ad188SAdrian Chadd */ 1652b3ad188SAdrian Chadd static void 1662b3ad188SAdrian Chadd intr_irq_init(void *dummy __unused) 1672b3ad188SAdrian Chadd { 1682b3ad188SAdrian Chadd 1692b3ad188SAdrian Chadd SLIST_INIT(&pic_list); 1702b3ad188SAdrian Chadd mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); 1712b3ad188SAdrian Chadd mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); 1722b3ad188SAdrian Chadd } 1732b3ad188SAdrian Chadd SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); 1742b3ad188SAdrian Chadd 1752b3ad188SAdrian Chadd static void 1762b3ad188SAdrian Chadd intrcnt_setname(const char *name, int index) 1772b3ad188SAdrian Chadd { 1782b3ad188SAdrian Chadd 1792b3ad188SAdrian Chadd snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s", 1802b3ad188SAdrian Chadd INTRNAME_LEN - 1, name); 1812b3ad188SAdrian Chadd } 1822b3ad188SAdrian Chadd 1832b3ad188SAdrian Chadd /* 1842b3ad188SAdrian Chadd * Update name for interrupt source with interrupt event. 1852b3ad188SAdrian Chadd */ 1862b3ad188SAdrian Chadd static void 1872b3ad188SAdrian Chadd intrcnt_updatename(struct intr_irqsrc *isrc) 1882b3ad188SAdrian Chadd { 1892b3ad188SAdrian Chadd 1902b3ad188SAdrian Chadd /* QQQ: What about stray counter name? */ 1912b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 1922b3ad188SAdrian Chadd intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index); 1932b3ad188SAdrian Chadd } 1942b3ad188SAdrian Chadd 1952b3ad188SAdrian Chadd /* 1962b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counter increment. 1972b3ad188SAdrian Chadd */ 1982b3ad188SAdrian Chadd static inline void 1992b3ad188SAdrian Chadd isrc_increment_count(struct intr_irqsrc *isrc) 2002b3ad188SAdrian Chadd { 2012b3ad188SAdrian Chadd 202bff6be3eSSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_PPI) 203bff6be3eSSvatopluk Kraus atomic_add_long(&isrc->isrc_count[0], 1); 204bff6be3eSSvatopluk Kraus else 2052b3ad188SAdrian Chadd isrc->isrc_count[0]++; 2062b3ad188SAdrian Chadd } 2072b3ad188SAdrian Chadd 2082b3ad188SAdrian Chadd /* 2092b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt stray counter increment. 2102b3ad188SAdrian Chadd */ 2112b3ad188SAdrian Chadd static inline void 2122b3ad188SAdrian Chadd isrc_increment_straycount(struct intr_irqsrc *isrc) 2132b3ad188SAdrian Chadd { 2142b3ad188SAdrian Chadd 2152b3ad188SAdrian Chadd isrc->isrc_count[1]++; 2162b3ad188SAdrian Chadd } 2172b3ad188SAdrian Chadd 2182b3ad188SAdrian Chadd /* 2192b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt name update. 2202b3ad188SAdrian Chadd */ 2212b3ad188SAdrian Chadd static void 2222b3ad188SAdrian Chadd isrc_update_name(struct intr_irqsrc *isrc, const char *name) 2232b3ad188SAdrian Chadd { 2242b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2252b3ad188SAdrian Chadd 2262b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2272b3ad188SAdrian Chadd 2282b3ad188SAdrian Chadd if (name != NULL) { 2292b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); 2302b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2312b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, 2322b3ad188SAdrian Chadd name); 2332b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2342b3ad188SAdrian Chadd } else { 2352b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); 2362b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2372b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); 2382b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2392b3ad188SAdrian Chadd } 2402b3ad188SAdrian Chadd } 2412b3ad188SAdrian Chadd 2422b3ad188SAdrian Chadd /* 2432b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counters setup. 2442b3ad188SAdrian Chadd */ 2452b3ad188SAdrian Chadd static void 2462b3ad188SAdrian Chadd isrc_setup_counters(struct intr_irqsrc *isrc) 2472b3ad188SAdrian Chadd { 2482b3ad188SAdrian Chadd u_int index; 2492b3ad188SAdrian Chadd 2502b3ad188SAdrian Chadd /* 2512b3ad188SAdrian Chadd * XXX - it does not work well with removable controllers and 2522b3ad188SAdrian Chadd * interrupt sources !!! 2532b3ad188SAdrian Chadd */ 2542b3ad188SAdrian Chadd index = atomic_fetchadd_int(&intrcnt_index, 2); 2552b3ad188SAdrian Chadd isrc->isrc_index = index; 2562b3ad188SAdrian Chadd isrc->isrc_count = &intrcnt[index]; 2572b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 2582b3ad188SAdrian Chadd } 2592b3ad188SAdrian Chadd 260bff6be3eSSvatopluk Kraus /* 261bff6be3eSSvatopluk Kraus * Virtualization for interrupt source interrupt counters release. 262bff6be3eSSvatopluk Kraus */ 263bff6be3eSSvatopluk Kraus static void 264bff6be3eSSvatopluk Kraus isrc_release_counters(struct intr_irqsrc *isrc) 265bff6be3eSSvatopluk Kraus { 266bff6be3eSSvatopluk Kraus 267bff6be3eSSvatopluk Kraus panic("%s: not implemented", __func__); 268bff6be3eSSvatopluk Kraus } 269bff6be3eSSvatopluk Kraus 2702b3ad188SAdrian Chadd #ifdef SMP 2712b3ad188SAdrian Chadd /* 2722b3ad188SAdrian Chadd * Virtualization for interrupt source IPI counters setup. 2732b3ad188SAdrian Chadd */ 2745b70c08cSSvatopluk Kraus u_long * 2755b70c08cSSvatopluk Kraus intr_ipi_setup_counters(const char *name) 2762b3ad188SAdrian Chadd { 2772b3ad188SAdrian Chadd u_int index, i; 2782b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2792b3ad188SAdrian Chadd 2802b3ad188SAdrian Chadd index = atomic_fetchadd_int(&intrcnt_index, MAXCPU); 2812b3ad188SAdrian Chadd for (i = 0; i < MAXCPU; i++) { 2822b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); 2832b3ad188SAdrian Chadd intrcnt_setname(str, index + i); 2842b3ad188SAdrian Chadd } 2855b70c08cSSvatopluk Kraus return (&intrcnt[index]); 2862b3ad188SAdrian Chadd } 2872b3ad188SAdrian Chadd #endif 2882b3ad188SAdrian Chadd 2892b3ad188SAdrian Chadd /* 2902b3ad188SAdrian Chadd * Main interrupt dispatch handler. It's called straight 2912b3ad188SAdrian Chadd * from the assembler, where CPU interrupt is served. 2922b3ad188SAdrian Chadd */ 2932b3ad188SAdrian Chadd void 2942b3ad188SAdrian Chadd intr_irq_handler(struct trapframe *tf) 2952b3ad188SAdrian Chadd { 2962b3ad188SAdrian Chadd struct trapframe * oldframe; 2972b3ad188SAdrian Chadd struct thread * td; 2982b3ad188SAdrian Chadd 2992b3ad188SAdrian Chadd KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__)); 3002b3ad188SAdrian Chadd 3012b3ad188SAdrian Chadd PCPU_INC(cnt.v_intr); 3022b3ad188SAdrian Chadd critical_enter(); 3032b3ad188SAdrian Chadd td = curthread; 3042b3ad188SAdrian Chadd oldframe = td->td_intr_frame; 3052b3ad188SAdrian Chadd td->td_intr_frame = tf; 3062b3ad188SAdrian Chadd irq_root_filter(irq_root_arg); 3072b3ad188SAdrian Chadd td->td_intr_frame = oldframe; 3082b3ad188SAdrian Chadd critical_exit(); 3092b3ad188SAdrian Chadd } 3102b3ad188SAdrian Chadd 3112b3ad188SAdrian Chadd /* 3122b3ad188SAdrian Chadd * interrupt controller dispatch function for interrupts. It should 3132b3ad188SAdrian Chadd * be called straight from the interrupt controller, when associated interrupt 3142b3ad188SAdrian Chadd * source is learned. 3152b3ad188SAdrian Chadd */ 316bff6be3eSSvatopluk Kraus int 317bff6be3eSSvatopluk Kraus intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) 3182b3ad188SAdrian Chadd { 3192b3ad188SAdrian Chadd 3202b3ad188SAdrian Chadd KASSERT(isrc != NULL, ("%s: no source", __func__)); 3212b3ad188SAdrian Chadd 3222b3ad188SAdrian Chadd isrc_increment_count(isrc); 3232b3ad188SAdrian Chadd 3242b3ad188SAdrian Chadd #ifdef INTR_SOLO 3252b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 3262b3ad188SAdrian Chadd int error; 3272b3ad188SAdrian Chadd error = isrc->isrc_filter(isrc->isrc_arg, tf); 3282b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 3292b3ad188SAdrian Chadd if (error == FILTER_HANDLED) 330bff6be3eSSvatopluk Kraus return (0); 3312b3ad188SAdrian Chadd } else 3322b3ad188SAdrian Chadd #endif 3332b3ad188SAdrian Chadd if (isrc->isrc_event != NULL) { 3342b3ad188SAdrian Chadd if (intr_event_handle(isrc->isrc_event, tf) == 0) 335bff6be3eSSvatopluk Kraus return (0); 3362b3ad188SAdrian Chadd } 3372b3ad188SAdrian Chadd 3382b3ad188SAdrian Chadd isrc_increment_straycount(isrc); 339bff6be3eSSvatopluk Kraus return (EINVAL); 3402b3ad188SAdrian Chadd } 3412b3ad188SAdrian Chadd 3422b3ad188SAdrian Chadd /* 3432b3ad188SAdrian Chadd * Alloc unique interrupt number (resource handle) for interrupt source. 3442b3ad188SAdrian Chadd * 3452b3ad188SAdrian Chadd * There could be various strategies how to allocate free interrupt number 3462b3ad188SAdrian Chadd * (resource handle) for new interrupt source. 3472b3ad188SAdrian Chadd * 3482b3ad188SAdrian Chadd * 1. Handles are always allocated forward, so handles are not recycled 3492b3ad188SAdrian Chadd * immediately. However, if only one free handle left which is reused 3502b3ad188SAdrian Chadd * constantly... 3512b3ad188SAdrian Chadd */ 352bff6be3eSSvatopluk Kraus static inline int 353bff6be3eSSvatopluk Kraus isrc_alloc_irq(struct intr_irqsrc *isrc) 3542b3ad188SAdrian Chadd { 3552b3ad188SAdrian Chadd u_int maxirqs, irq; 3562b3ad188SAdrian Chadd 3572b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 3582b3ad188SAdrian Chadd 3592b3ad188SAdrian Chadd maxirqs = nitems(irq_sources); 3602b3ad188SAdrian Chadd if (irq_next_free >= maxirqs) 3612b3ad188SAdrian Chadd return (ENOSPC); 3622b3ad188SAdrian Chadd 3632b3ad188SAdrian Chadd for (irq = irq_next_free; irq < maxirqs; irq++) { 3642b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 3652b3ad188SAdrian Chadd goto found; 3662b3ad188SAdrian Chadd } 3672b3ad188SAdrian Chadd for (irq = 0; irq < irq_next_free; irq++) { 3682b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 3692b3ad188SAdrian Chadd goto found; 3702b3ad188SAdrian Chadd } 3712b3ad188SAdrian Chadd 3722b3ad188SAdrian Chadd irq_next_free = maxirqs; 3732b3ad188SAdrian Chadd return (ENOSPC); 3742b3ad188SAdrian Chadd 3752b3ad188SAdrian Chadd found: 3762b3ad188SAdrian Chadd isrc->isrc_irq = irq; 3772b3ad188SAdrian Chadd irq_sources[irq] = isrc; 3782b3ad188SAdrian Chadd 3792b3ad188SAdrian Chadd irq_next_free = irq + 1; 3802b3ad188SAdrian Chadd if (irq_next_free >= maxirqs) 3812b3ad188SAdrian Chadd irq_next_free = 0; 3822b3ad188SAdrian Chadd return (0); 3832b3ad188SAdrian Chadd } 384bff6be3eSSvatopluk Kraus 3852b3ad188SAdrian Chadd /* 3862b3ad188SAdrian Chadd * Free unique interrupt number (resource handle) from interrupt source. 3872b3ad188SAdrian Chadd */ 388bff6be3eSSvatopluk Kraus static inline int 3892b3ad188SAdrian Chadd isrc_free_irq(struct intr_irqsrc *isrc) 3902b3ad188SAdrian Chadd { 3912b3ad188SAdrian Chadd 392bff6be3eSSvatopluk Kraus mtx_assert(&isrc_table_lock, MA_OWNED); 3932b3ad188SAdrian Chadd 394bff6be3eSSvatopluk Kraus if (isrc->isrc_irq >= nitems(irq_sources)) 3952b3ad188SAdrian Chadd return (EINVAL); 396bff6be3eSSvatopluk Kraus if (irq_sources[isrc->isrc_irq] != isrc) 3972b3ad188SAdrian Chadd return (EINVAL); 3982b3ad188SAdrian Chadd 3992b3ad188SAdrian Chadd irq_sources[isrc->isrc_irq] = NULL; 4008442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 4012b3ad188SAdrian Chadd return (0); 4022b3ad188SAdrian Chadd } 403bff6be3eSSvatopluk Kraus 4042b3ad188SAdrian Chadd /* 4052b3ad188SAdrian Chadd * Lookup interrupt source by interrupt number (resource handle). 4062b3ad188SAdrian Chadd */ 407bff6be3eSSvatopluk Kraus static inline struct intr_irqsrc * 4082b3ad188SAdrian Chadd isrc_lookup(u_int irq) 4092b3ad188SAdrian Chadd { 4102b3ad188SAdrian Chadd 4112b3ad188SAdrian Chadd if (irq < nitems(irq_sources)) 4122b3ad188SAdrian Chadd return (irq_sources[irq]); 4132b3ad188SAdrian Chadd return (NULL); 4142b3ad188SAdrian Chadd } 4152b3ad188SAdrian Chadd 4162b3ad188SAdrian Chadd /* 417bff6be3eSSvatopluk Kraus * Initialize interrupt source and register it into global interrupt table. 4182b3ad188SAdrian Chadd */ 419bff6be3eSSvatopluk Kraus int 420bff6be3eSSvatopluk Kraus intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, 421bff6be3eSSvatopluk Kraus const char *fmt, ...) 4222b3ad188SAdrian Chadd { 423bff6be3eSSvatopluk Kraus int error; 424bff6be3eSSvatopluk Kraus va_list ap; 4252b3ad188SAdrian Chadd 426bff6be3eSSvatopluk Kraus bzero(isrc, sizeof(struct intr_irqsrc)); 427bff6be3eSSvatopluk Kraus isrc->isrc_dev = dev; 4288442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 429bff6be3eSSvatopluk Kraus isrc->isrc_flags = flags; 4302b3ad188SAdrian Chadd 431bff6be3eSSvatopluk Kraus va_start(ap, fmt); 432bff6be3eSSvatopluk Kraus vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); 433bff6be3eSSvatopluk Kraus va_end(ap); 434bff6be3eSSvatopluk Kraus 435bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 436bff6be3eSSvatopluk Kraus error = isrc_alloc_irq(isrc); 437bff6be3eSSvatopluk Kraus if (error != 0) { 438bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 439bff6be3eSSvatopluk Kraus return (error); 4402b3ad188SAdrian Chadd } 441bff6be3eSSvatopluk Kraus /* 442bff6be3eSSvatopluk Kraus * Setup interrupt counters, but not for IPI sources. Those are setup 443bff6be3eSSvatopluk Kraus * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust 444bff6be3eSSvatopluk Kraus * our counter pool. 445bff6be3eSSvatopluk Kraus */ 446bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 447bff6be3eSSvatopluk Kraus isrc_setup_counters(isrc); 448bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 449bff6be3eSSvatopluk Kraus return (0); 4502b3ad188SAdrian Chadd } 4512b3ad188SAdrian Chadd 4522b3ad188SAdrian Chadd /* 453bff6be3eSSvatopluk Kraus * Deregister interrupt source from global interrupt table. 454bff6be3eSSvatopluk Kraus */ 455bff6be3eSSvatopluk Kraus int 456bff6be3eSSvatopluk Kraus intr_isrc_deregister(struct intr_irqsrc *isrc) 457bff6be3eSSvatopluk Kraus { 458bff6be3eSSvatopluk Kraus int error; 459bff6be3eSSvatopluk Kraus 460bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 461bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 462bff6be3eSSvatopluk Kraus isrc_release_counters(isrc); 463bff6be3eSSvatopluk Kraus error = isrc_free_irq(isrc); 464bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 465bff6be3eSSvatopluk Kraus return (error); 466bff6be3eSSvatopluk Kraus } 467bff6be3eSSvatopluk Kraus 4685b613c19SSvatopluk Kraus #ifdef SMP 4695b613c19SSvatopluk Kraus /* 4705b613c19SSvatopluk Kraus * A support function for a PIC to decide if provided ISRC should be inited 4715b613c19SSvatopluk Kraus * on given cpu. The logic of INTR_ISRCF_BOUND flag and isrc_cpu member of 4725b613c19SSvatopluk Kraus * struct intr_irqsrc is the following: 4735b613c19SSvatopluk Kraus * 4745b613c19SSvatopluk Kraus * If INTR_ISRCF_BOUND is set, the ISRC should be inited only on cpus 4755b613c19SSvatopluk Kraus * set in isrc_cpu. If not, the ISRC should be inited on every cpu and 4765b613c19SSvatopluk Kraus * isrc_cpu is kept consistent with it. Thus isrc_cpu is always correct. 4775b613c19SSvatopluk Kraus */ 4785b613c19SSvatopluk Kraus bool 4795b613c19SSvatopluk Kraus intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu) 4805b613c19SSvatopluk Kraus { 4815b613c19SSvatopluk Kraus 4825b613c19SSvatopluk Kraus if (isrc->isrc_handlers == 0) 4835b613c19SSvatopluk Kraus return (false); 4845b613c19SSvatopluk Kraus if ((isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) == 0) 4855b613c19SSvatopluk Kraus return (false); 4865b613c19SSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_BOUND) 4875b613c19SSvatopluk Kraus return (CPU_ISSET(cpu, &isrc->isrc_cpu)); 4885b613c19SSvatopluk Kraus 4895b613c19SSvatopluk Kraus CPU_SET(cpu, &isrc->isrc_cpu); 4905b613c19SSvatopluk Kraus return (true); 4915b613c19SSvatopluk Kraus } 4925b613c19SSvatopluk Kraus #endif 4935b613c19SSvatopluk Kraus 494bff6be3eSSvatopluk Kraus static struct intr_dev_data * 495bff6be3eSSvatopluk Kraus intr_ddata_alloc(u_int extsize) 496bff6be3eSSvatopluk Kraus { 497bff6be3eSSvatopluk Kraus struct intr_dev_data *ddata; 498bff6be3eSSvatopluk Kraus 499bff6be3eSSvatopluk Kraus ddata = malloc(sizeof(*ddata) + extsize, M_INTRNG, M_WAITOK | M_ZERO); 500bff6be3eSSvatopluk Kraus 501bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 502bff6be3eSSvatopluk Kraus if (intr_ddata_first_unused >= nitems(intr_ddata_tab)) { 503bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 504bff6be3eSSvatopluk Kraus free(ddata, M_INTRNG); 505bff6be3eSSvatopluk Kraus return (NULL); 506bff6be3eSSvatopluk Kraus } 507bff6be3eSSvatopluk Kraus intr_ddata_tab[intr_ddata_first_unused] = ddata; 508bff6be3eSSvatopluk Kraus ddata->idd_irq = IRQ_DDATA_BASE + intr_ddata_first_unused++; 509bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 510bff6be3eSSvatopluk Kraus return (ddata); 511bff6be3eSSvatopluk Kraus } 512bff6be3eSSvatopluk Kraus 513bff6be3eSSvatopluk Kraus static struct intr_irqsrc * 514bff6be3eSSvatopluk Kraus intr_ddata_lookup(u_int irq, struct intr_map_data **datap) 515bff6be3eSSvatopluk Kraus { 516bff6be3eSSvatopluk Kraus int error; 517bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 518bff6be3eSSvatopluk Kraus struct intr_dev_data *ddata; 519bff6be3eSSvatopluk Kraus 520bff6be3eSSvatopluk Kraus isrc = isrc_lookup(irq); 521bff6be3eSSvatopluk Kraus if (isrc != NULL) { 522bff6be3eSSvatopluk Kraus if (datap != NULL) 523bff6be3eSSvatopluk Kraus *datap = NULL; 524bff6be3eSSvatopluk Kraus return (isrc); 525bff6be3eSSvatopluk Kraus } 526bff6be3eSSvatopluk Kraus 527bff6be3eSSvatopluk Kraus if (irq < IRQ_DDATA_BASE) 528bff6be3eSSvatopluk Kraus return (NULL); 529bff6be3eSSvatopluk Kraus 530bff6be3eSSvatopluk Kraus irq -= IRQ_DDATA_BASE; 531bff6be3eSSvatopluk Kraus if (irq >= nitems(intr_ddata_tab)) 532bff6be3eSSvatopluk Kraus return (NULL); 533bff6be3eSSvatopluk Kraus 534bff6be3eSSvatopluk Kraus ddata = intr_ddata_tab[irq]; 535bff6be3eSSvatopluk Kraus if (ddata->idd_isrc == NULL) { 536bff6be3eSSvatopluk Kraus error = intr_map_irq(ddata->idd_dev, ddata->idd_xref, 537bff6be3eSSvatopluk Kraus &ddata->idd_data, &irq); 538bff6be3eSSvatopluk Kraus if (error != 0) 539bff6be3eSSvatopluk Kraus return (NULL); 540bff6be3eSSvatopluk Kraus ddata->idd_isrc = isrc_lookup(irq); 541bff6be3eSSvatopluk Kraus } 542bff6be3eSSvatopluk Kraus if (datap != NULL) 543bff6be3eSSvatopluk Kraus *datap = &ddata->idd_data; 544bff6be3eSSvatopluk Kraus return (ddata->idd_isrc); 545bff6be3eSSvatopluk Kraus } 546bff6be3eSSvatopluk Kraus 547bff6be3eSSvatopluk Kraus #ifdef DEV_ACPI 548bff6be3eSSvatopluk Kraus /* 549bff6be3eSSvatopluk Kraus * Map interrupt source according to ACPI info into framework. If such mapping 5502b3ad188SAdrian Chadd * does not exist, create it. Return unique interrupt number (resource handle) 5512b3ad188SAdrian Chadd * associated with mapped interrupt source. 5522b3ad188SAdrian Chadd */ 5532b3ad188SAdrian Chadd u_int 554bff6be3eSSvatopluk Kraus intr_acpi_map_irq(device_t dev, u_int irq, enum intr_polarity pol, 555bff6be3eSSvatopluk Kraus enum intr_trigger trig) 5562b3ad188SAdrian Chadd { 557bff6be3eSSvatopluk Kraus struct intr_dev_data *ddata; 5582b3ad188SAdrian Chadd 559bff6be3eSSvatopluk Kraus ddata = intr_ddata_alloc(0); 560bff6be3eSSvatopluk Kraus if (ddata == NULL) 5618442087fSMichal Meloun return (INTR_IRQ_INVALID); /* no space left */ 5622b3ad188SAdrian Chadd 563bff6be3eSSvatopluk Kraus ddata->idd_dev = dev; 564bff6be3eSSvatopluk Kraus ddata->idd_data.type = INTR_MAP_DATA_ACPI; 565bff6be3eSSvatopluk Kraus ddata->idd_data.acpi.irq = irq; 566bff6be3eSSvatopluk Kraus ddata->idd_data.acpi.pol = pol; 567bff6be3eSSvatopluk Kraus ddata->idd_data.acpi.trig = trig; 568bff6be3eSSvatopluk Kraus return (ddata->idd_irq); 5692b3ad188SAdrian Chadd } 570bff6be3eSSvatopluk Kraus #endif 5712b3ad188SAdrian Chadd #ifdef FDT 5722b3ad188SAdrian Chadd /* 5732b3ad188SAdrian Chadd * Map interrupt source according to FDT data into framework. If such mapping 5742b3ad188SAdrian Chadd * does not exist, create it. Return unique interrupt number (resource handle) 5752b3ad188SAdrian Chadd * associated with mapped interrupt source. 5762b3ad188SAdrian Chadd */ 5772b3ad188SAdrian Chadd u_int 5782b3ad188SAdrian Chadd intr_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells) 5792b3ad188SAdrian Chadd { 580bff6be3eSSvatopluk Kraus struct intr_dev_data *ddata; 5812b3ad188SAdrian Chadd u_int cellsize; 5822b3ad188SAdrian Chadd 5832b3ad188SAdrian Chadd cellsize = ncells * sizeof(*cells); 584bff6be3eSSvatopluk Kraus ddata = intr_ddata_alloc(cellsize); 585bff6be3eSSvatopluk Kraus if (ddata == NULL) 5868442087fSMichal Meloun return (INTR_IRQ_INVALID); /* no space left */ 5872b3ad188SAdrian Chadd 588bff6be3eSSvatopluk Kraus ddata->idd_xref = (intptr_t)node; 589bff6be3eSSvatopluk Kraus ddata->idd_data.type = INTR_MAP_DATA_FDT; 590bff6be3eSSvatopluk Kraus ddata->idd_data.fdt.ncells = ncells; 591bff6be3eSSvatopluk Kraus ddata->idd_data.fdt.cells = (pcell_t *)(ddata + 1); 592bff6be3eSSvatopluk Kraus memcpy(ddata->idd_data.fdt.cells, cells, cellsize); 593bff6be3eSSvatopluk Kraus return (ddata->idd_irq); 5942b3ad188SAdrian Chadd } 5952b3ad188SAdrian Chadd #endif 5962b3ad188SAdrian Chadd 59739f6c1bdSMichal Meloun /* 59839f6c1bdSMichal Meloun * Store GPIO interrupt decription in framework and return unique interrupt 59939f6c1bdSMichal Meloun * number (resource handle) associated with it. 60039f6c1bdSMichal Meloun */ 60139f6c1bdSMichal Meloun u_int 60239f6c1bdSMichal Meloun intr_gpio_map_irq(device_t dev, u_int pin_num, u_int pin_flags, u_int intr_mode) 60339f6c1bdSMichal Meloun { 60439f6c1bdSMichal Meloun struct intr_dev_data *ddata; 60539f6c1bdSMichal Meloun 60639f6c1bdSMichal Meloun ddata = intr_ddata_alloc(0); 60739f6c1bdSMichal Meloun if (ddata == NULL) 6088442087fSMichal Meloun return (INTR_IRQ_INVALID); /* no space left */ 60939f6c1bdSMichal Meloun 61039f6c1bdSMichal Meloun ddata->idd_dev = dev; 61139f6c1bdSMichal Meloun ddata->idd_data.type = INTR_MAP_DATA_GPIO; 61239f6c1bdSMichal Meloun ddata->idd_data.gpio.gpio_pin_num = pin_num; 61339f6c1bdSMichal Meloun ddata->idd_data.gpio.gpio_pin_flags = pin_flags; 61439f6c1bdSMichal Meloun ddata->idd_data.gpio.gpio_intr_mode = intr_mode; 61539f6c1bdSMichal Meloun return (ddata->idd_irq); 61639f6c1bdSMichal Meloun } 61739f6c1bdSMichal Meloun 6182b3ad188SAdrian Chadd #ifdef INTR_SOLO 6192b3ad188SAdrian Chadd /* 6202b3ad188SAdrian Chadd * Setup filter into interrupt source. 6212b3ad188SAdrian Chadd */ 6222b3ad188SAdrian Chadd static int 6232b3ad188SAdrian Chadd iscr_setup_filter(struct intr_irqsrc *isrc, const char *name, 6242b3ad188SAdrian Chadd intr_irq_filter_t *filter, void *arg, void **cookiep) 6252b3ad188SAdrian Chadd { 6262b3ad188SAdrian Chadd 6272b3ad188SAdrian Chadd if (filter == NULL) 6282b3ad188SAdrian Chadd return (EINVAL); 6292b3ad188SAdrian Chadd 6302b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6312b3ad188SAdrian Chadd /* 6322b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 6332b3ad188SAdrian Chadd * how we handle interrupt sources. 6342b3ad188SAdrian Chadd */ 6352b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 6362b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6372b3ad188SAdrian Chadd return (EBUSY); 6382b3ad188SAdrian Chadd } 6392b3ad188SAdrian Chadd isrc->isrc_filter = filter; 6402b3ad188SAdrian Chadd isrc->isrc_arg = arg; 6412b3ad188SAdrian Chadd isrc_update_name(isrc, name); 6422b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6432b3ad188SAdrian Chadd 6442b3ad188SAdrian Chadd *cookiep = isrc; 6452b3ad188SAdrian Chadd return (0); 6462b3ad188SAdrian Chadd } 6472b3ad188SAdrian Chadd #endif 6482b3ad188SAdrian Chadd 6492b3ad188SAdrian Chadd /* 6502b3ad188SAdrian Chadd * Interrupt source pre_ithread method for MI interrupt framework. 6512b3ad188SAdrian Chadd */ 6522b3ad188SAdrian Chadd static void 6532b3ad188SAdrian Chadd intr_isrc_pre_ithread(void *arg) 6542b3ad188SAdrian Chadd { 6552b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6562b3ad188SAdrian Chadd 6572b3ad188SAdrian Chadd PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); 6582b3ad188SAdrian Chadd } 6592b3ad188SAdrian Chadd 6602b3ad188SAdrian Chadd /* 6612b3ad188SAdrian Chadd * Interrupt source post_ithread method for MI interrupt framework. 6622b3ad188SAdrian Chadd */ 6632b3ad188SAdrian Chadd static void 6642b3ad188SAdrian Chadd intr_isrc_post_ithread(void *arg) 6652b3ad188SAdrian Chadd { 6662b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6672b3ad188SAdrian Chadd 6682b3ad188SAdrian Chadd PIC_POST_ITHREAD(isrc->isrc_dev, isrc); 6692b3ad188SAdrian Chadd } 6702b3ad188SAdrian Chadd 6712b3ad188SAdrian Chadd /* 6722b3ad188SAdrian Chadd * Interrupt source post_filter method for MI interrupt framework. 6732b3ad188SAdrian Chadd */ 6742b3ad188SAdrian Chadd static void 6752b3ad188SAdrian Chadd intr_isrc_post_filter(void *arg) 6762b3ad188SAdrian Chadd { 6772b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6782b3ad188SAdrian Chadd 6792b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 6802b3ad188SAdrian Chadd } 6812b3ad188SAdrian Chadd 6822b3ad188SAdrian Chadd /* 6832b3ad188SAdrian Chadd * Interrupt source assign_cpu method for MI interrupt framework. 6842b3ad188SAdrian Chadd */ 6852b3ad188SAdrian Chadd static int 6862b3ad188SAdrian Chadd intr_isrc_assign_cpu(void *arg, int cpu) 6872b3ad188SAdrian Chadd { 6882b3ad188SAdrian Chadd #ifdef SMP 6892b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6902b3ad188SAdrian Chadd int error; 6912b3ad188SAdrian Chadd 6925b70c08cSSvatopluk Kraus if (isrc->isrc_dev != intr_irq_root_dev) 6932b3ad188SAdrian Chadd return (EINVAL); 6942b3ad188SAdrian Chadd 6952b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6962b3ad188SAdrian Chadd if (cpu == NOCPU) { 6972b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 6982b3ad188SAdrian Chadd isrc->isrc_flags &= ~INTR_ISRCF_BOUND; 6992b3ad188SAdrian Chadd } else { 7002b3ad188SAdrian Chadd CPU_SETOF(cpu, &isrc->isrc_cpu); 7012b3ad188SAdrian Chadd isrc->isrc_flags |= INTR_ISRCF_BOUND; 7022b3ad188SAdrian Chadd } 7032b3ad188SAdrian Chadd 7042b3ad188SAdrian Chadd /* 7052b3ad188SAdrian Chadd * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or 7062b3ad188SAdrian Chadd * re-balance it to another CPU or enable it on more CPUs. However, 7072b3ad188SAdrian Chadd * PIC is expected to change isrc_cpu appropriately to keep us well 708e3043798SPedro F. Giffuni * informed if the call is successful. 7092b3ad188SAdrian Chadd */ 7102b3ad188SAdrian Chadd if (irq_assign_cpu) { 711bff6be3eSSvatopluk Kraus error = PIC_BIND_INTR(isrc->isrc_dev, isrc); 7122b3ad188SAdrian Chadd if (error) { 7132b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 7142b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7152b3ad188SAdrian Chadd return (error); 7162b3ad188SAdrian Chadd } 7172b3ad188SAdrian Chadd } 7182b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7192b3ad188SAdrian Chadd return (0); 7202b3ad188SAdrian Chadd #else 7212b3ad188SAdrian Chadd return (EOPNOTSUPP); 7222b3ad188SAdrian Chadd #endif 7232b3ad188SAdrian Chadd } 7242b3ad188SAdrian Chadd 7252b3ad188SAdrian Chadd /* 7262b3ad188SAdrian Chadd * Create interrupt event for interrupt source. 7272b3ad188SAdrian Chadd */ 7282b3ad188SAdrian Chadd static int 7292b3ad188SAdrian Chadd isrc_event_create(struct intr_irqsrc *isrc) 7302b3ad188SAdrian Chadd { 7312b3ad188SAdrian Chadd struct intr_event *ie; 7322b3ad188SAdrian Chadd int error; 7332b3ad188SAdrian Chadd 7342b3ad188SAdrian Chadd error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq, 7352b3ad188SAdrian Chadd intr_isrc_pre_ithread, intr_isrc_post_ithread, intr_isrc_post_filter, 7362b3ad188SAdrian Chadd intr_isrc_assign_cpu, "%s:", isrc->isrc_name); 7372b3ad188SAdrian Chadd if (error) 7382b3ad188SAdrian Chadd return (error); 7392b3ad188SAdrian Chadd 7402b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7412b3ad188SAdrian Chadd /* 7422b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 7432b3ad188SAdrian Chadd * how we handle interrupt sources. Let contested event wins. 7442b3ad188SAdrian Chadd */ 745169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 7462b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 747169e6abdSSvatopluk Kraus #else 748169e6abdSSvatopluk Kraus if (isrc->isrc_event != NULL) { 749169e6abdSSvatopluk Kraus #endif 7502b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7512b3ad188SAdrian Chadd intr_event_destroy(ie); 7522b3ad188SAdrian Chadd return (isrc->isrc_event != NULL ? EBUSY : 0); 7532b3ad188SAdrian Chadd } 7542b3ad188SAdrian Chadd isrc->isrc_event = ie; 7552b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7562b3ad188SAdrian Chadd 7572b3ad188SAdrian Chadd return (0); 7582b3ad188SAdrian Chadd } 7592b3ad188SAdrian Chadd #ifdef notyet 7602b3ad188SAdrian Chadd /* 7612b3ad188SAdrian Chadd * Destroy interrupt event for interrupt source. 7622b3ad188SAdrian Chadd */ 7632b3ad188SAdrian Chadd static void 7642b3ad188SAdrian Chadd isrc_event_destroy(struct intr_irqsrc *isrc) 7652b3ad188SAdrian Chadd { 7662b3ad188SAdrian Chadd struct intr_event *ie; 7672b3ad188SAdrian Chadd 7682b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7692b3ad188SAdrian Chadd ie = isrc->isrc_event; 7702b3ad188SAdrian Chadd isrc->isrc_event = NULL; 7712b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7722b3ad188SAdrian Chadd 7732b3ad188SAdrian Chadd if (ie != NULL) 7742b3ad188SAdrian Chadd intr_event_destroy(ie); 7752b3ad188SAdrian Chadd } 7762b3ad188SAdrian Chadd #endif 7772b3ad188SAdrian Chadd /* 7782b3ad188SAdrian Chadd * Add handler to interrupt source. 7792b3ad188SAdrian Chadd */ 7802b3ad188SAdrian Chadd static int 7812b3ad188SAdrian Chadd isrc_add_handler(struct intr_irqsrc *isrc, const char *name, 7822b3ad188SAdrian Chadd driver_filter_t filter, driver_intr_t handler, void *arg, 7832b3ad188SAdrian Chadd enum intr_type flags, void **cookiep) 7842b3ad188SAdrian Chadd { 7852b3ad188SAdrian Chadd int error; 7862b3ad188SAdrian Chadd 7872b3ad188SAdrian Chadd if (isrc->isrc_event == NULL) { 7882b3ad188SAdrian Chadd error = isrc_event_create(isrc); 7892b3ad188SAdrian Chadd if (error) 7902b3ad188SAdrian Chadd return (error); 7912b3ad188SAdrian Chadd } 7922b3ad188SAdrian Chadd 7932b3ad188SAdrian Chadd error = intr_event_add_handler(isrc->isrc_event, name, filter, handler, 7942b3ad188SAdrian Chadd arg, intr_priority(flags), flags, cookiep); 7952b3ad188SAdrian Chadd if (error == 0) { 7962b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7972b3ad188SAdrian Chadd intrcnt_updatename(isrc); 7982b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7992b3ad188SAdrian Chadd } 8002b3ad188SAdrian Chadd 8012b3ad188SAdrian Chadd return (error); 8022b3ad188SAdrian Chadd } 8032b3ad188SAdrian Chadd 8042b3ad188SAdrian Chadd /* 8052b3ad188SAdrian Chadd * Lookup interrupt controller locked. 8062b3ad188SAdrian Chadd */ 807bff6be3eSSvatopluk Kraus static inline struct intr_pic * 8082b3ad188SAdrian Chadd pic_lookup_locked(device_t dev, intptr_t xref) 8092b3ad188SAdrian Chadd { 8102b3ad188SAdrian Chadd struct intr_pic *pic; 8112b3ad188SAdrian Chadd 8122b3ad188SAdrian Chadd mtx_assert(&pic_list_lock, MA_OWNED); 8132b3ad188SAdrian Chadd 8144be58cbaSSvatopluk Kraus if (dev == NULL && xref == 0) 8154be58cbaSSvatopluk Kraus return (NULL); 8164be58cbaSSvatopluk Kraus 8174be58cbaSSvatopluk Kraus /* Note that pic->pic_dev is never NULL on registered PIC. */ 8182b3ad188SAdrian Chadd SLIST_FOREACH(pic, &pic_list, pic_next) { 8194be58cbaSSvatopluk Kraus if (dev == NULL) { 8204be58cbaSSvatopluk Kraus if (xref == pic->pic_xref) 8214be58cbaSSvatopluk Kraus return (pic); 8224be58cbaSSvatopluk Kraus } else if (xref == 0 || pic->pic_xref == 0) { 8234be58cbaSSvatopluk Kraus if (dev == pic->pic_dev) 8244be58cbaSSvatopluk Kraus return (pic); 8254be58cbaSSvatopluk Kraus } else if (xref == pic->pic_xref && dev == pic->pic_dev) 8262b3ad188SAdrian Chadd return (pic); 8272b3ad188SAdrian Chadd } 8282b3ad188SAdrian Chadd return (NULL); 8292b3ad188SAdrian Chadd } 8302b3ad188SAdrian Chadd 8312b3ad188SAdrian Chadd /* 8322b3ad188SAdrian Chadd * Lookup interrupt controller. 8332b3ad188SAdrian Chadd */ 8342b3ad188SAdrian Chadd static struct intr_pic * 8352b3ad188SAdrian Chadd pic_lookup(device_t dev, intptr_t xref) 8362b3ad188SAdrian Chadd { 8372b3ad188SAdrian Chadd struct intr_pic *pic; 8382b3ad188SAdrian Chadd 8392b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 8402b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 8412b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8422b3ad188SAdrian Chadd return (pic); 8432b3ad188SAdrian Chadd } 8442b3ad188SAdrian Chadd 8452b3ad188SAdrian Chadd /* 8462b3ad188SAdrian Chadd * Create interrupt controller. 8472b3ad188SAdrian Chadd */ 8482b3ad188SAdrian Chadd static struct intr_pic * 8492b3ad188SAdrian Chadd pic_create(device_t dev, intptr_t xref) 8502b3ad188SAdrian Chadd { 8512b3ad188SAdrian Chadd struct intr_pic *pic; 8522b3ad188SAdrian Chadd 8532b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 8542b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 8552b3ad188SAdrian Chadd if (pic != NULL) { 8562b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8572b3ad188SAdrian Chadd return (pic); 8582b3ad188SAdrian Chadd } 8592b3ad188SAdrian Chadd pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO); 8602b3ad188SAdrian Chadd pic->pic_xref = xref; 8612b3ad188SAdrian Chadd pic->pic_dev = dev; 8622b3ad188SAdrian Chadd SLIST_INSERT_HEAD(&pic_list, pic, pic_next); 8632b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8642b3ad188SAdrian Chadd 8652b3ad188SAdrian Chadd return (pic); 8662b3ad188SAdrian Chadd } 8672b3ad188SAdrian Chadd #ifdef notyet 8682b3ad188SAdrian Chadd /* 8692b3ad188SAdrian Chadd * Destroy interrupt controller. 8702b3ad188SAdrian Chadd */ 8712b3ad188SAdrian Chadd static void 8722b3ad188SAdrian Chadd pic_destroy(device_t dev, intptr_t xref) 8732b3ad188SAdrian Chadd { 8742b3ad188SAdrian Chadd struct intr_pic *pic; 8752b3ad188SAdrian Chadd 8762b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 8772b3ad188SAdrian Chadd pic = pic_lookup_locked(dev, xref); 8782b3ad188SAdrian Chadd if (pic == NULL) { 8792b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8802b3ad188SAdrian Chadd return; 8812b3ad188SAdrian Chadd } 8822b3ad188SAdrian Chadd SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next); 8832b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8842b3ad188SAdrian Chadd 8852b3ad188SAdrian Chadd free(pic, M_INTRNG); 8862b3ad188SAdrian Chadd } 8872b3ad188SAdrian Chadd #endif 8882b3ad188SAdrian Chadd /* 8892b3ad188SAdrian Chadd * Register interrupt controller. 8902b3ad188SAdrian Chadd */ 8912b3ad188SAdrian Chadd int 8922b3ad188SAdrian Chadd intr_pic_register(device_t dev, intptr_t xref) 8932b3ad188SAdrian Chadd { 8942b3ad188SAdrian Chadd struct intr_pic *pic; 8952b3ad188SAdrian Chadd 8964be58cbaSSvatopluk Kraus if (dev == NULL) 8974be58cbaSSvatopluk Kraus return (EINVAL); 8982b3ad188SAdrian Chadd pic = pic_create(dev, xref); 8992b3ad188SAdrian Chadd if (pic == NULL) 9002b3ad188SAdrian Chadd return (ENOMEM); 9012b3ad188SAdrian Chadd 9024be58cbaSSvatopluk Kraus debugf("PIC %p registered for %s <dev %p, xref %x>\n", pic, 9034be58cbaSSvatopluk Kraus device_get_nameunit(dev), dev, xref); 9042b3ad188SAdrian Chadd return (0); 9052b3ad188SAdrian Chadd } 9062b3ad188SAdrian Chadd 9072b3ad188SAdrian Chadd /* 9082b3ad188SAdrian Chadd * Unregister interrupt controller. 9092b3ad188SAdrian Chadd */ 9102b3ad188SAdrian Chadd int 911bff6be3eSSvatopluk Kraus intr_pic_deregister(device_t dev, intptr_t xref) 9122b3ad188SAdrian Chadd { 9132b3ad188SAdrian Chadd 9142b3ad188SAdrian Chadd panic("%s: not implemented", __func__); 9152b3ad188SAdrian Chadd } 9162b3ad188SAdrian Chadd 9172b3ad188SAdrian Chadd /* 9182b3ad188SAdrian Chadd * Mark interrupt controller (itself) as a root one. 9192b3ad188SAdrian Chadd * 9202b3ad188SAdrian Chadd * Note that only an interrupt controller can really know its position 9212b3ad188SAdrian Chadd * in interrupt controller's tree. So root PIC must claim itself as a root. 9222b3ad188SAdrian Chadd * 9232b3ad188SAdrian Chadd * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, 9242b3ad188SAdrian Chadd * page 30: 9252b3ad188SAdrian Chadd * "The root of the interrupt tree is determined when traversal 9262b3ad188SAdrian Chadd * of the interrupt tree reaches an interrupt controller node without 9272b3ad188SAdrian Chadd * an interrupts property and thus no explicit interrupt parent." 9282b3ad188SAdrian Chadd */ 9292b3ad188SAdrian Chadd int 9302b3ad188SAdrian Chadd intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, 9312b3ad188SAdrian Chadd void *arg, u_int ipicount) 9322b3ad188SAdrian Chadd { 9332b3ad188SAdrian Chadd 9342b3ad188SAdrian Chadd if (pic_lookup(dev, xref) == NULL) { 9352b3ad188SAdrian Chadd device_printf(dev, "not registered\n"); 9362b3ad188SAdrian Chadd return (EINVAL); 9372b3ad188SAdrian Chadd } 9382b3ad188SAdrian Chadd if (filter == NULL) { 9392b3ad188SAdrian Chadd device_printf(dev, "filter missing\n"); 9402b3ad188SAdrian Chadd return (EINVAL); 9412b3ad188SAdrian Chadd } 9422b3ad188SAdrian Chadd 9432b3ad188SAdrian Chadd /* 9442b3ad188SAdrian Chadd * Only one interrupt controllers could be on the root for now. 9452b3ad188SAdrian Chadd * Note that we further suppose that there is not threaded interrupt 9462b3ad188SAdrian Chadd * routine (handler) on the root. See intr_irq_handler(). 9472b3ad188SAdrian Chadd */ 9485b70c08cSSvatopluk Kraus if (intr_irq_root_dev != NULL) { 9492b3ad188SAdrian Chadd device_printf(dev, "another root already set\n"); 9502b3ad188SAdrian Chadd return (EBUSY); 9512b3ad188SAdrian Chadd } 9522b3ad188SAdrian Chadd 9535b70c08cSSvatopluk Kraus intr_irq_root_dev = dev; 9542b3ad188SAdrian Chadd irq_root_filter = filter; 9552b3ad188SAdrian Chadd irq_root_arg = arg; 9562b3ad188SAdrian Chadd irq_root_ipicount = ipicount; 9572b3ad188SAdrian Chadd 9582b3ad188SAdrian Chadd debugf("irq root set to %s\n", device_get_nameunit(dev)); 9592b3ad188SAdrian Chadd return (0); 9602b3ad188SAdrian Chadd } 9612b3ad188SAdrian Chadd 9622b3ad188SAdrian Chadd int 963bff6be3eSSvatopluk Kraus intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data, 964bff6be3eSSvatopluk Kraus u_int *irqp) 9652b3ad188SAdrian Chadd { 9662b3ad188SAdrian Chadd int error; 967bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 968bff6be3eSSvatopluk Kraus struct intr_pic *pic; 969bff6be3eSSvatopluk Kraus 970bff6be3eSSvatopluk Kraus if (data == NULL) 971bff6be3eSSvatopluk Kraus return (EINVAL); 972bff6be3eSSvatopluk Kraus 973bff6be3eSSvatopluk Kraus pic = pic_lookup(dev, xref); 974*15adccc6SSvatopluk Kraus if (pic == NULL) 975bff6be3eSSvatopluk Kraus return (ESRCH); 976bff6be3eSSvatopluk Kraus 977bff6be3eSSvatopluk Kraus error = PIC_MAP_INTR(pic->pic_dev, data, &isrc); 978bff6be3eSSvatopluk Kraus if (error == 0) 979bff6be3eSSvatopluk Kraus *irqp = isrc->isrc_irq; 980bff6be3eSSvatopluk Kraus return (error); 981bff6be3eSSvatopluk Kraus } 982bff6be3eSSvatopluk Kraus 983bff6be3eSSvatopluk Kraus int 984bff6be3eSSvatopluk Kraus intr_alloc_irq(device_t dev, struct resource *res) 985bff6be3eSSvatopluk Kraus { 986bff6be3eSSvatopluk Kraus struct intr_map_data *data; 987bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 988bff6be3eSSvatopluk Kraus 989bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 990bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 991bff6be3eSSvatopluk Kraus 992bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), &data); 993bff6be3eSSvatopluk Kraus if (isrc == NULL) 994bff6be3eSSvatopluk Kraus return (EINVAL); 995bff6be3eSSvatopluk Kraus 996bff6be3eSSvatopluk Kraus return (PIC_ALLOC_INTR(isrc->isrc_dev, isrc, res, data)); 997bff6be3eSSvatopluk Kraus } 998bff6be3eSSvatopluk Kraus 999bff6be3eSSvatopluk Kraus int 1000bff6be3eSSvatopluk Kraus intr_release_irq(device_t dev, struct resource *res) 1001bff6be3eSSvatopluk Kraus { 1002bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1003bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1004bff6be3eSSvatopluk Kraus 1005bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1006bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1007bff6be3eSSvatopluk Kraus 1008bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), &data); 1009bff6be3eSSvatopluk Kraus if (isrc == NULL) 1010bff6be3eSSvatopluk Kraus return (EINVAL); 1011bff6be3eSSvatopluk Kraus 1012bff6be3eSSvatopluk Kraus return (PIC_RELEASE_INTR(isrc->isrc_dev, isrc, res, data)); 1013bff6be3eSSvatopluk Kraus } 1014bff6be3eSSvatopluk Kraus 1015bff6be3eSSvatopluk Kraus int 1016bff6be3eSSvatopluk Kraus intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, 1017bff6be3eSSvatopluk Kraus driver_intr_t hand, void *arg, int flags, void **cookiep) 1018bff6be3eSSvatopluk Kraus { 1019bff6be3eSSvatopluk Kraus int error; 1020bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1021bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1022bff6be3eSSvatopluk Kraus const char *name; 1023bff6be3eSSvatopluk Kraus 1024bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1025bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1026bff6be3eSSvatopluk Kraus 1027bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), &data); 1028bff6be3eSSvatopluk Kraus if (isrc == NULL) 1029bff6be3eSSvatopluk Kraus return (EINVAL); 10302b3ad188SAdrian Chadd 10312b3ad188SAdrian Chadd name = device_get_nameunit(dev); 10322b3ad188SAdrian Chadd 10332b3ad188SAdrian Chadd #ifdef INTR_SOLO 10342b3ad188SAdrian Chadd /* 1035e3043798SPedro F. Giffuni * Standard handling is done through MI interrupt framework. However, 10362b3ad188SAdrian Chadd * some interrupts could request solely own special handling. This 10372b3ad188SAdrian Chadd * non standard handling can be used for interrupt controllers without 10382b3ad188SAdrian Chadd * handler (filter only), so in case that interrupt controllers are 10392b3ad188SAdrian Chadd * chained, MI interrupt framework is called only in leaf controller. 10402b3ad188SAdrian Chadd * 10412b3ad188SAdrian Chadd * Note that root interrupt controller routine is served as well, 10422b3ad188SAdrian Chadd * however in intr_irq_handler(), i.e. main system dispatch routine. 10432b3ad188SAdrian Chadd */ 10442b3ad188SAdrian Chadd if (flags & INTR_SOLO && hand != NULL) { 10452b3ad188SAdrian Chadd debugf("irq %u cannot solo on %s\n", irq, name); 10462b3ad188SAdrian Chadd return (EINVAL); 10472b3ad188SAdrian Chadd } 10482b3ad188SAdrian Chadd 10492b3ad188SAdrian Chadd if (flags & INTR_SOLO) { 10502b3ad188SAdrian Chadd error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, 10512b3ad188SAdrian Chadd arg, cookiep); 10522b3ad188SAdrian Chadd debugf("irq %u setup filter error %d on %s\n", irq, error, 10532b3ad188SAdrian Chadd name); 10542b3ad188SAdrian Chadd } else 10552b3ad188SAdrian Chadd #endif 10562b3ad188SAdrian Chadd { 10572b3ad188SAdrian Chadd error = isrc_add_handler(isrc, name, filt, hand, arg, flags, 10582b3ad188SAdrian Chadd cookiep); 10592b3ad188SAdrian Chadd debugf("irq %u add handler error %d on %s\n", irq, error, name); 10602b3ad188SAdrian Chadd } 10612b3ad188SAdrian Chadd if (error != 0) 10622b3ad188SAdrian Chadd return (error); 10632b3ad188SAdrian Chadd 10642b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1065bff6be3eSSvatopluk Kraus error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); 1066bff6be3eSSvatopluk Kraus if (error == 0) { 10672b3ad188SAdrian Chadd isrc->isrc_handlers++; 1068bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 1) 10692b3ad188SAdrian Chadd PIC_ENABLE_INTR(isrc->isrc_dev, isrc); 10702b3ad188SAdrian Chadd } 10712b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 1072bff6be3eSSvatopluk Kraus if (error != 0) 1073bff6be3eSSvatopluk Kraus intr_event_remove_handler(*cookiep); 1074bff6be3eSSvatopluk Kraus return (error); 10752b3ad188SAdrian Chadd } 10762b3ad188SAdrian Chadd 10772b3ad188SAdrian Chadd int 1078bff6be3eSSvatopluk Kraus intr_teardown_irq(device_t dev, struct resource *res, void *cookie) 10792b3ad188SAdrian Chadd { 10802b3ad188SAdrian Chadd int error; 1081bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1082bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 10832b3ad188SAdrian Chadd 1084bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1085bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1086bff6be3eSSvatopluk Kraus 1087bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), &data); 10882b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 10892b3ad188SAdrian Chadd return (EINVAL); 1090bff6be3eSSvatopluk Kraus 1091169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 10922b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 10932b3ad188SAdrian Chadd if (isrc != cookie) 10942b3ad188SAdrian Chadd return (EINVAL); 10952b3ad188SAdrian Chadd 10962b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 10972b3ad188SAdrian Chadd isrc->isrc_filter = NULL; 10982b3ad188SAdrian Chadd isrc->isrc_arg = NULL; 10992b3ad188SAdrian Chadd isrc->isrc_handlers = 0; 11002b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1101bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 11022b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 11032b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11042b3ad188SAdrian Chadd return (0); 11052b3ad188SAdrian Chadd } 1106169e6abdSSvatopluk Kraus #endif 11072b3ad188SAdrian Chadd if (isrc != intr_handler_source(cookie)) 11082b3ad188SAdrian Chadd return (EINVAL); 11092b3ad188SAdrian Chadd 11102b3ad188SAdrian Chadd error = intr_event_remove_handler(cookie); 11112b3ad188SAdrian Chadd if (error == 0) { 11122b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11132b3ad188SAdrian Chadd isrc->isrc_handlers--; 1114bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 0) 11152b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1116bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 11172b3ad188SAdrian Chadd intrcnt_updatename(isrc); 11182b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11192b3ad188SAdrian Chadd } 11202b3ad188SAdrian Chadd return (error); 11212b3ad188SAdrian Chadd } 11222b3ad188SAdrian Chadd 11232b3ad188SAdrian Chadd int 1124bff6be3eSSvatopluk Kraus intr_describe_irq(device_t dev, struct resource *res, void *cookie, 1125bff6be3eSSvatopluk Kraus const char *descr) 11262b3ad188SAdrian Chadd { 11272b3ad188SAdrian Chadd int error; 1128bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 11292b3ad188SAdrian Chadd 1130bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1131bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1132bff6be3eSSvatopluk Kraus 1133bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), NULL); 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; 11612b3ad188SAdrian Chadd 1162bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1163bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1164bff6be3eSSvatopluk Kraus 1165bff6be3eSSvatopluk Kraus isrc = intr_ddata_lookup(rman_get_start(res), NULL); 11662b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 11672b3ad188SAdrian Chadd return (EINVAL); 1168169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 11692b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) 11702b3ad188SAdrian Chadd return (intr_isrc_assign_cpu(isrc, cpu)); 1171169e6abdSSvatopluk Kraus #endif 11722b3ad188SAdrian Chadd return (intr_event_bind(isrc->isrc_event, cpu)); 11732b3ad188SAdrian Chadd } 11742b3ad188SAdrian Chadd 11752b3ad188SAdrian Chadd /* 11762b3ad188SAdrian Chadd * Return the CPU that the next interrupt source should use. 11772b3ad188SAdrian Chadd * For now just returns the next CPU according to round-robin. 11782b3ad188SAdrian Chadd */ 11792b3ad188SAdrian Chadd u_int 11802b3ad188SAdrian Chadd intr_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask) 11812b3ad188SAdrian Chadd { 11822b3ad188SAdrian Chadd 11832b3ad188SAdrian Chadd if (!irq_assign_cpu || mp_ncpus == 1) 11842b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 11852b3ad188SAdrian Chadd 11862b3ad188SAdrian Chadd do { 11872b3ad188SAdrian Chadd last_cpu++; 11882b3ad188SAdrian Chadd if (last_cpu > mp_maxid) 11892b3ad188SAdrian Chadd last_cpu = 0; 11902b3ad188SAdrian Chadd } while (!CPU_ISSET(last_cpu, cpumask)); 11912b3ad188SAdrian Chadd return (last_cpu); 11922b3ad188SAdrian Chadd } 11932b3ad188SAdrian Chadd 11942b3ad188SAdrian Chadd /* 11952b3ad188SAdrian Chadd * Distribute all the interrupt sources among the available 11962b3ad188SAdrian Chadd * CPUs once the AP's have been launched. 11972b3ad188SAdrian Chadd */ 11982b3ad188SAdrian Chadd static void 11992b3ad188SAdrian Chadd intr_irq_shuffle(void *arg __unused) 12002b3ad188SAdrian Chadd { 12012b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 12022b3ad188SAdrian Chadd u_int i; 12032b3ad188SAdrian Chadd 12042b3ad188SAdrian Chadd if (mp_ncpus == 1) 12052b3ad188SAdrian Chadd return; 12062b3ad188SAdrian Chadd 12072b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12082b3ad188SAdrian Chadd irq_assign_cpu = TRUE; 12092b3ad188SAdrian Chadd for (i = 0; i < NIRQ; i++) { 12102b3ad188SAdrian Chadd isrc = irq_sources[i]; 12112b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0 || 1212cf55df9fSSvatopluk Kraus isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) 12132b3ad188SAdrian Chadd continue; 12142b3ad188SAdrian Chadd 12152b3ad188SAdrian Chadd if (isrc->isrc_event != NULL && 12162b3ad188SAdrian Chadd isrc->isrc_flags & INTR_ISRCF_BOUND && 12172b3ad188SAdrian Chadd isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1) 12182b3ad188SAdrian Chadd panic("%s: CPU inconsistency", __func__); 12192b3ad188SAdrian Chadd 12202b3ad188SAdrian Chadd if ((isrc->isrc_flags & INTR_ISRCF_BOUND) == 0) 12212b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); /* start again */ 12222b3ad188SAdrian Chadd 12232b3ad188SAdrian Chadd /* 12242b3ad188SAdrian Chadd * We are in wicked position here if the following call fails 12252b3ad188SAdrian Chadd * for bound ISRC. The best thing we can do is to clear 12262b3ad188SAdrian Chadd * isrc_cpu so inconsistency with ie_cpu will be detectable. 12272b3ad188SAdrian Chadd */ 1228bff6be3eSSvatopluk Kraus if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0) 12292b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 12302b3ad188SAdrian Chadd } 12312b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12322b3ad188SAdrian Chadd } 12332b3ad188SAdrian Chadd SYSINIT(intr_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, intr_irq_shuffle, NULL); 12342b3ad188SAdrian Chadd 12352b3ad188SAdrian Chadd #else 12362b3ad188SAdrian Chadd u_int 12372b3ad188SAdrian Chadd intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) 12382b3ad188SAdrian Chadd { 12392b3ad188SAdrian Chadd 12402b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 12412b3ad188SAdrian Chadd } 12422b3ad188SAdrian Chadd #endif 12432b3ad188SAdrian Chadd 12442b3ad188SAdrian Chadd void dosoftints(void); 12452b3ad188SAdrian Chadd void 12462b3ad188SAdrian Chadd dosoftints(void) 12472b3ad188SAdrian Chadd { 12482b3ad188SAdrian Chadd } 12492b3ad188SAdrian Chadd 12502b3ad188SAdrian Chadd #ifdef SMP 12512b3ad188SAdrian Chadd /* 12522b3ad188SAdrian Chadd * Init interrupt controller on another CPU. 12532b3ad188SAdrian Chadd */ 12542b3ad188SAdrian Chadd void 12552b3ad188SAdrian Chadd intr_pic_init_secondary(void) 12562b3ad188SAdrian Chadd { 12572b3ad188SAdrian Chadd 12582b3ad188SAdrian Chadd /* 12592b3ad188SAdrian Chadd * QQQ: Only root PIC is aware of other CPUs ??? 12602b3ad188SAdrian Chadd */ 12615b70c08cSSvatopluk Kraus KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); 12622b3ad188SAdrian Chadd 12632b3ad188SAdrian Chadd //mtx_lock(&isrc_table_lock); 12645b70c08cSSvatopluk Kraus PIC_INIT_SECONDARY(intr_irq_root_dev); 12652b3ad188SAdrian Chadd //mtx_unlock(&isrc_table_lock); 12662b3ad188SAdrian Chadd } 12672b3ad188SAdrian Chadd #endif 12682b3ad188SAdrian Chadd 12692b3ad188SAdrian Chadd #ifdef DDB 12702b3ad188SAdrian Chadd DB_SHOW_COMMAND(irqs, db_show_irqs) 12712b3ad188SAdrian Chadd { 12722b3ad188SAdrian Chadd u_int i, irqsum; 1273bff6be3eSSvatopluk Kraus u_long num; 12742b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 12752b3ad188SAdrian Chadd 12762b3ad188SAdrian Chadd for (irqsum = 0, i = 0; i < NIRQ; i++) { 12772b3ad188SAdrian Chadd isrc = irq_sources[i]; 12782b3ad188SAdrian Chadd if (isrc == NULL) 12792b3ad188SAdrian Chadd continue; 12802b3ad188SAdrian Chadd 1281bff6be3eSSvatopluk Kraus num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; 12822b3ad188SAdrian Chadd db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, 12832b3ad188SAdrian Chadd isrc->isrc_name, isrc->isrc_cpu.__bits[0], 1284bff6be3eSSvatopluk Kraus isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); 1285bff6be3eSSvatopluk Kraus irqsum += num; 12862b3ad188SAdrian Chadd } 12872b3ad188SAdrian Chadd db_printf("irq total %u\n", irqsum); 12882b3ad188SAdrian Chadd } 12892b3ad188SAdrian Chadd #endif 1290