12b3ad188SAdrian Chadd /*- 2bff6be3eSSvatopluk Kraus * Copyright (c) 2015-2016 Svatopluk Kraus 3bff6be3eSSvatopluk Kraus * Copyright (c) 2015-2016 Michal Meloun 42b3ad188SAdrian Chadd * All rights reserved. 5fae8755fSJessica Clarke * Copyright (c) 2015-2016 The FreeBSD Foundation 6fae8755fSJessica Clarke * Copyright (c) 2021 Jessica Clarke <jrtc27@FreeBSD.org> 7fae8755fSJessica Clarke * 8fae8755fSJessica Clarke * Portions of this software were developed by Andrew Turner under 9fae8755fSJessica Clarke * sponsorship from the FreeBSD Foundation. 102b3ad188SAdrian Chadd * 112b3ad188SAdrian Chadd * Redistribution and use in source and binary forms, with or without 122b3ad188SAdrian Chadd * modification, are permitted provided that the following conditions 132b3ad188SAdrian Chadd * are met: 142b3ad188SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 152b3ad188SAdrian Chadd * notice, this list of conditions and the following disclaimer. 162b3ad188SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 172b3ad188SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 182b3ad188SAdrian Chadd * documentation and/or other materials provided with the distribution. 192b3ad188SAdrian Chadd * 202b3ad188SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 212b3ad188SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 222b3ad188SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 232b3ad188SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 242b3ad188SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 252b3ad188SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 262b3ad188SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 272b3ad188SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 282b3ad188SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 292b3ad188SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 302b3ad188SAdrian Chadd * SUCH DAMAGE. 312b3ad188SAdrian Chadd */ 322b3ad188SAdrian Chadd 332b3ad188SAdrian Chadd #include <sys/cdefs.h> 342b3ad188SAdrian Chadd /* 352b3ad188SAdrian Chadd * New-style Interrupt Framework 362b3ad188SAdrian Chadd * 37895c8b1cSMichal Meloun * TODO: - add support for disconnected PICs. 38895c8b1cSMichal Meloun * - to support IPI (PPI) enabling on other CPUs if already started. 39895c8b1cSMichal Meloun * - to complete things for removable PICs. 402b3ad188SAdrian Chadd */ 412b3ad188SAdrian Chadd 422b3ad188SAdrian Chadd #include "opt_ddb.h" 43df7a2251SAndrew Turner #include "opt_hwpmc_hooks.h" 44e707c8beSRuslan Bukin #include "opt_iommu.h" 452b3ad188SAdrian Chadd 462b3ad188SAdrian Chadd #include <sys/param.h> 472b3ad188SAdrian Chadd #include <sys/systm.h> 4889c52f9dSKyle Evans #include <sys/asan.h> 4928137bdbSMitchell Horne #include <sys/bitstring.h> 502b3ad188SAdrian Chadd #include <sys/bus.h> 512b3ad188SAdrian Chadd #include <sys/conf.h> 522b3ad188SAdrian Chadd #include <sys/cpuset.h> 5382e846dfSMitchell Horne #include <sys/interrupt.h> 544f12b529SKyle Evans #include <sys/intr.h> 5582e846dfSMitchell Horne #include <sys/kernel.h> 5682e846dfSMitchell Horne #include <sys/lock.h> 5782e846dfSMitchell Horne #include <sys/malloc.h> 58c05d7bdaSMark Johnston #include <sys/msan.h> 5982e846dfSMitchell Horne #include <sys/mutex.h> 6082e846dfSMitchell Horne #include <sys/proc.h> 6182e846dfSMitchell Horne #include <sys/queue.h> 626b42a1f4SAndrew Turner #include <sys/rman.h> 632b3ad188SAdrian Chadd #include <sys/sched.h> 642b3ad188SAdrian Chadd #include <sys/smp.h> 65248f0cabSOleksandr Tymoshenko #include <sys/sysctl.h> 6682e846dfSMitchell Horne #include <sys/syslog.h> 6782e846dfSMitchell Horne #include <sys/taskqueue.h> 6882e846dfSMitchell Horne #include <sys/tree.h> 699ed01c32SGleb Smirnoff #include <sys/vmmeter.h> 70df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 71df7a2251SAndrew Turner #include <sys/pmckern.h> 72df7a2251SAndrew Turner #endif 73df7a2251SAndrew Turner 742b3ad188SAdrian Chadd #include <machine/atomic.h> 752b3ad188SAdrian Chadd #include <machine/cpu.h> 762b3ad188SAdrian Chadd #include <machine/smp.h> 772b3ad188SAdrian Chadd #include <machine/stdarg.h> 782b3ad188SAdrian Chadd 792b3ad188SAdrian Chadd #ifdef DDB 802b3ad188SAdrian Chadd #include <ddb/ddb.h> 812b3ad188SAdrian Chadd #endif 822b3ad188SAdrian Chadd 83e707c8beSRuslan Bukin #ifdef IOMMU 84e707c8beSRuslan Bukin #include <dev/iommu/iommu_msi.h> 85e707c8beSRuslan Bukin #endif 86e707c8beSRuslan Bukin 872b3ad188SAdrian Chadd #include "pic_if.h" 883fc155dcSAndrew Turner #include "msi_if.h" 892b3ad188SAdrian Chadd 902b3ad188SAdrian Chadd #define INTRNAME_LEN (2*MAXCOMLEN + 1) 912b3ad188SAdrian Chadd 92*4b01a7faSKyle Evans /* 93*4b01a7faSKyle Evans * Archs may define multiple roots with INTR_ROOT_NUM to support different kinds 94*4b01a7faSKyle Evans * of interrupts (e.g. arm64 FIQs which use a different exception vector than 95*4b01a7faSKyle Evans * IRQs). 96*4b01a7faSKyle Evans */ 97*4b01a7faSKyle Evans #if !defined(INTR_ROOT_NUM) 98*4b01a7faSKyle Evans #define INTR_ROOT_NUM 1 99*4b01a7faSKyle Evans #endif 100*4b01a7faSKyle Evans 1012b3ad188SAdrian Chadd #ifdef DEBUG 1022b3ad188SAdrian Chadd #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 1032b3ad188SAdrian Chadd printf(fmt,##args); } while (0) 1042b3ad188SAdrian Chadd #else 1052b3ad188SAdrian Chadd #define debugf(fmt, args...) 1062b3ad188SAdrian Chadd #endif 1072b3ad188SAdrian Chadd 1082b3ad188SAdrian Chadd MALLOC_DECLARE(M_INTRNG); 1092b3ad188SAdrian Chadd MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); 1102b3ad188SAdrian Chadd 1112b3ad188SAdrian Chadd /* Root interrupt controller stuff. */ 11285918bebSAyrton Munoz struct intr_irq_root { 11385918bebSAyrton Munoz device_t dev; 11485918bebSAyrton Munoz intr_irq_filter_t *filter; 11585918bebSAyrton Munoz void *arg; 11685918bebSAyrton Munoz }; 11785918bebSAyrton Munoz 118*4b01a7faSKyle Evans static struct intr_irq_root intr_irq_roots[INTR_ROOT_NUM]; 1192b3ad188SAdrian Chadd 120d1605cdaSAndrew Turner struct intr_pic_child { 121d1605cdaSAndrew Turner SLIST_ENTRY(intr_pic_child) pc_next; 122d1605cdaSAndrew Turner struct intr_pic *pc_pic; 123d1605cdaSAndrew Turner intr_child_irq_filter_t *pc_filter; 124d1605cdaSAndrew Turner void *pc_filter_arg; 125d1605cdaSAndrew Turner uintptr_t pc_start; 126d1605cdaSAndrew Turner uintptr_t pc_length; 127d1605cdaSAndrew Turner }; 128d1605cdaSAndrew Turner 1292b3ad188SAdrian Chadd /* Interrupt controller definition. */ 1302b3ad188SAdrian Chadd struct intr_pic { 1312b3ad188SAdrian Chadd SLIST_ENTRY(intr_pic) pic_next; 1322b3ad188SAdrian Chadd intptr_t pic_xref; /* hardware identification */ 1332b3ad188SAdrian Chadd device_t pic_dev; 134c0d52370SAndrew Turner /* Only one of FLAG_PIC or FLAG_MSI may be set */ 1353fc155dcSAndrew Turner #define FLAG_PIC (1 << 0) 1363fc155dcSAndrew Turner #define FLAG_MSI (1 << 1) 137c0d52370SAndrew Turner #define FLAG_TYPE_MASK (FLAG_PIC | FLAG_MSI) 1383fc155dcSAndrew Turner u_int pic_flags; 139d1605cdaSAndrew Turner struct mtx pic_child_lock; 140d1605cdaSAndrew Turner SLIST_HEAD(, intr_pic_child) pic_children; 1412b3ad188SAdrian Chadd }; 1422b3ad188SAdrian Chadd 143fae8755fSJessica Clarke #ifdef SMP 144fae8755fSJessica Clarke #define INTR_IPI_NAMELEN (MAXCOMLEN + 1) 145fae8755fSJessica Clarke 146fae8755fSJessica Clarke struct intr_ipi { 147fae8755fSJessica Clarke intr_ipi_handler_t *ii_handler; 148fae8755fSJessica Clarke void *ii_handler_arg; 149fae8755fSJessica Clarke struct intr_irqsrc *ii_isrc; 150fae8755fSJessica Clarke char ii_name[INTR_IPI_NAMELEN]; 151fae8755fSJessica Clarke u_long *ii_count; 152fae8755fSJessica Clarke }; 153103d39efSJessica Clarke 154103d39efSJessica Clarke static device_t intr_ipi_dev; 155103d39efSJessica Clarke static u_int intr_ipi_dev_priority; 156103d39efSJessica Clarke static bool intr_ipi_dev_frozen; 157fae8755fSJessica Clarke #endif 158fae8755fSJessica Clarke 1592b3ad188SAdrian Chadd static struct mtx pic_list_lock; 1602b3ad188SAdrian Chadd static SLIST_HEAD(, intr_pic) pic_list; 1612b3ad188SAdrian Chadd 1629f3a552fSElliott Mitchell static struct intr_pic *pic_lookup(device_t dev, intptr_t xref, u_int flags); 1632b3ad188SAdrian Chadd 1642b3ad188SAdrian Chadd /* Interrupt source definition. */ 1652b3ad188SAdrian Chadd static struct mtx isrc_table_lock; 166248f0cabSOleksandr Tymoshenko static struct intr_irqsrc **irq_sources; 1671e0ba9d4SAndrew Turner static u_int irq_next_free; 1682b3ad188SAdrian Chadd 1692b3ad188SAdrian Chadd #ifdef SMP 170dc425090SMitchell Horne #ifdef EARLY_AP_STARTUP 171dc425090SMitchell Horne static bool irq_assign_cpu = true; 172dc425090SMitchell Horne #else 173dc425090SMitchell Horne static bool irq_assign_cpu = false; 174dc425090SMitchell Horne #endif 175fae8755fSJessica Clarke 176fae8755fSJessica Clarke static struct intr_ipi ipi_sources[INTR_IPI_COUNT]; 1772b3ad188SAdrian Chadd #endif 1782b3ad188SAdrian Chadd 179a3c7da3dSElliott Mitchell u_int intr_nirq = NIRQ; 180248f0cabSOleksandr Tymoshenko SYSCTL_UINT(_machdep, OID_AUTO, nirq, CTLFLAG_RDTUN, &intr_nirq, 0, 181248f0cabSOleksandr Tymoshenko "Number of IRQs"); 1822b3ad188SAdrian Chadd 1832b3ad188SAdrian Chadd /* Data for MI statistics reporting. */ 184248f0cabSOleksandr Tymoshenko u_long *intrcnt; 185248f0cabSOleksandr Tymoshenko char *intrnames; 186248f0cabSOleksandr Tymoshenko size_t sintrcnt; 187248f0cabSOleksandr Tymoshenko size_t sintrnames; 18828137bdbSMitchell Horne int nintrcnt; 18928137bdbSMitchell Horne static bitstr_t *intrcnt_bitmap; 1902b3ad188SAdrian Chadd 191895c8b1cSMichal Meloun static struct intr_irqsrc *intr_map_get_isrc(u_int res_id); 192895c8b1cSMichal Meloun static void intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc); 193609b0fe9SOleksandr Tymoshenko static struct intr_map_data * intr_map_get_map_data(u_int res_id); 194895c8b1cSMichal Meloun static void intr_map_copy_map_data(u_int res_id, device_t *dev, intptr_t *xref, 195895c8b1cSMichal Meloun struct intr_map_data **data); 196895c8b1cSMichal Meloun 1972b3ad188SAdrian Chadd /* 1982b3ad188SAdrian Chadd * Interrupt framework initialization routine. 1992b3ad188SAdrian Chadd */ 2002b3ad188SAdrian Chadd static void 2012b3ad188SAdrian Chadd intr_irq_init(void *dummy __unused) 2022b3ad188SAdrian Chadd { 2032b3ad188SAdrian Chadd 2042b3ad188SAdrian Chadd SLIST_INIT(&pic_list); 2052b3ad188SAdrian Chadd mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); 2063fc155dcSAndrew Turner 2072b3ad188SAdrian Chadd mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); 208248f0cabSOleksandr Tymoshenko 209248f0cabSOleksandr Tymoshenko /* 210248f0cabSOleksandr Tymoshenko * - 2 counters for each I/O interrupt. 2112f0b059eSElliott Mitchell * - mp_maxid + 1 counters for each IPI counters for SMP. 212248f0cabSOleksandr Tymoshenko */ 21328137bdbSMitchell Horne nintrcnt = intr_nirq * 2; 214248f0cabSOleksandr Tymoshenko #ifdef SMP 2152f0b059eSElliott Mitchell nintrcnt += INTR_IPI_COUNT * (mp_maxid + 1); 216248f0cabSOleksandr Tymoshenko #endif 217248f0cabSOleksandr Tymoshenko 21828137bdbSMitchell Horne intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTRNG, 219248f0cabSOleksandr Tymoshenko M_WAITOK | M_ZERO); 22028137bdbSMitchell Horne intrnames = mallocarray(nintrcnt, INTRNAME_LEN, M_INTRNG, 221248f0cabSOleksandr Tymoshenko M_WAITOK | M_ZERO); 22228137bdbSMitchell Horne sintrcnt = nintrcnt * sizeof(u_long); 22328137bdbSMitchell Horne sintrnames = nintrcnt * INTRNAME_LEN; 22428137bdbSMitchell Horne 22528137bdbSMitchell Horne /* Allocate the bitmap tracking counter allocations. */ 22628137bdbSMitchell Horne intrcnt_bitmap = bit_alloc(nintrcnt, M_INTRNG, M_WAITOK | M_ZERO); 22728137bdbSMitchell Horne 228248f0cabSOleksandr Tymoshenko irq_sources = mallocarray(intr_nirq, sizeof(struct intr_irqsrc*), 229248f0cabSOleksandr Tymoshenko M_INTRNG, M_WAITOK | M_ZERO); 2302b3ad188SAdrian Chadd } 2312b3ad188SAdrian Chadd SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); 2322b3ad188SAdrian Chadd 2332b3ad188SAdrian Chadd static void 2342b3ad188SAdrian Chadd intrcnt_setname(const char *name, int index) 2352b3ad188SAdrian Chadd { 2362b3ad188SAdrian Chadd 2372b3ad188SAdrian Chadd snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s", 2382b3ad188SAdrian Chadd INTRNAME_LEN - 1, name); 2392b3ad188SAdrian Chadd } 2402b3ad188SAdrian Chadd 2412b3ad188SAdrian Chadd /* 2422b3ad188SAdrian Chadd * Update name for interrupt source with interrupt event. 2432b3ad188SAdrian Chadd */ 2442b3ad188SAdrian Chadd static void 2452b3ad188SAdrian Chadd intrcnt_updatename(struct intr_irqsrc *isrc) 2462b3ad188SAdrian Chadd { 2472b3ad188SAdrian Chadd 2482b3ad188SAdrian Chadd /* QQQ: What about stray counter name? */ 2492b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2502b3ad188SAdrian Chadd intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index); 2512b3ad188SAdrian Chadd } 2522b3ad188SAdrian Chadd 2532b3ad188SAdrian Chadd /* 2542b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counter increment. 2552b3ad188SAdrian Chadd */ 2562b3ad188SAdrian Chadd static inline void 2572b3ad188SAdrian Chadd isrc_increment_count(struct intr_irqsrc *isrc) 2582b3ad188SAdrian Chadd { 2592b3ad188SAdrian Chadd 260bff6be3eSSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_PPI) 261bff6be3eSSvatopluk Kraus atomic_add_long(&isrc->isrc_count[0], 1); 262bff6be3eSSvatopluk Kraus else 2632b3ad188SAdrian Chadd isrc->isrc_count[0]++; 2642b3ad188SAdrian Chadd } 2652b3ad188SAdrian Chadd 2662b3ad188SAdrian Chadd /* 2672b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt stray counter increment. 2682b3ad188SAdrian Chadd */ 2692b3ad188SAdrian Chadd static inline void 2702b3ad188SAdrian Chadd isrc_increment_straycount(struct intr_irqsrc *isrc) 2712b3ad188SAdrian Chadd { 2722b3ad188SAdrian Chadd 2732b3ad188SAdrian Chadd isrc->isrc_count[1]++; 2742b3ad188SAdrian Chadd } 2752b3ad188SAdrian Chadd 2762b3ad188SAdrian Chadd /* 2772b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt name update. 2782b3ad188SAdrian Chadd */ 2792b3ad188SAdrian Chadd static void 2802b3ad188SAdrian Chadd isrc_update_name(struct intr_irqsrc *isrc, const char *name) 2812b3ad188SAdrian Chadd { 2822b3ad188SAdrian Chadd char str[INTRNAME_LEN]; 2832b3ad188SAdrian Chadd 2842b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 2852b3ad188SAdrian Chadd 2862b3ad188SAdrian Chadd if (name != NULL) { 2872b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); 2882b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2892b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, 2902b3ad188SAdrian Chadd name); 2912b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2922b3ad188SAdrian Chadd } else { 2932b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); 2942b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index); 2952b3ad188SAdrian Chadd snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); 2962b3ad188SAdrian Chadd intrcnt_setname(str, isrc->isrc_index + 1); 2972b3ad188SAdrian Chadd } 2982b3ad188SAdrian Chadd } 2992b3ad188SAdrian Chadd 3002b3ad188SAdrian Chadd /* 3012b3ad188SAdrian Chadd * Virtualization for interrupt source interrupt counters setup. 3022b3ad188SAdrian Chadd */ 3032b3ad188SAdrian Chadd static void 3042b3ad188SAdrian Chadd isrc_setup_counters(struct intr_irqsrc *isrc) 3052b3ad188SAdrian Chadd { 30628137bdbSMitchell Horne int index; 30728137bdbSMitchell Horne 30828137bdbSMitchell Horne mtx_assert(&isrc_table_lock, MA_OWNED); 3092b3ad188SAdrian Chadd 3102b3ad188SAdrian Chadd /* 31128137bdbSMitchell Horne * Allocate two counter values, the second tracking "stray" interrupts. 3122b3ad188SAdrian Chadd */ 31328137bdbSMitchell Horne bit_ffc_area(intrcnt_bitmap, nintrcnt, 2, &index); 31428137bdbSMitchell Horne if (index == -1) 31528137bdbSMitchell Horne panic("Failed to allocate 2 counters. Array exhausted?"); 31628137bdbSMitchell Horne bit_nset(intrcnt_bitmap, index, index + 1); 3172b3ad188SAdrian Chadd isrc->isrc_index = index; 3182b3ad188SAdrian Chadd isrc->isrc_count = &intrcnt[index]; 3192b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 3202b3ad188SAdrian Chadd } 3212b3ad188SAdrian Chadd 322bff6be3eSSvatopluk Kraus /* 323bff6be3eSSvatopluk Kraus * Virtualization for interrupt source interrupt counters release. 324bff6be3eSSvatopluk Kraus */ 325bff6be3eSSvatopluk Kraus static void 326bff6be3eSSvatopluk Kraus isrc_release_counters(struct intr_irqsrc *isrc) 327bff6be3eSSvatopluk Kraus { 32828137bdbSMitchell Horne int idx = isrc->isrc_index; 329bff6be3eSSvatopluk Kraus 33028137bdbSMitchell Horne mtx_assert(&isrc_table_lock, MA_OWNED); 33128137bdbSMitchell Horne 33228137bdbSMitchell Horne bit_nclear(intrcnt_bitmap, idx, idx + 1); 333bff6be3eSSvatopluk Kraus } 334bff6be3eSSvatopluk Kraus 3352b3ad188SAdrian Chadd /* 3362b3ad188SAdrian Chadd * Main interrupt dispatch handler. It's called straight 3372b3ad188SAdrian Chadd * from the assembler, where CPU interrupt is served. 3382b3ad188SAdrian Chadd */ 3392b3ad188SAdrian Chadd void 340*4b01a7faSKyle Evans intr_irq_handler(struct trapframe *tf, uint32_t rootnum) 3412b3ad188SAdrian Chadd { 3422b3ad188SAdrian Chadd struct trapframe * oldframe; 3432b3ad188SAdrian Chadd struct thread * td; 34485918bebSAyrton Munoz struct intr_irq_root *root; 3452b3ad188SAdrian Chadd 346*4b01a7faSKyle Evans KASSERT(rootnum < INTR_ROOT_NUM, 347*4b01a7faSKyle Evans ("%s: invalid interrupt root %d", __func__, rootnum)); 34885918bebSAyrton Munoz 349*4b01a7faSKyle Evans root = &intr_irq_roots[rootnum]; 35085918bebSAyrton Munoz KASSERT(root->filter != NULL, ("%s: no filter", __func__)); 3512b3ad188SAdrian Chadd 35289c52f9dSKyle Evans kasan_mark(tf, sizeof(*tf), sizeof(*tf), 0); 353c05d7bdaSMark Johnston kmsan_mark(tf, sizeof(*tf), KMSAN_STATE_INITED); 35489c52f9dSKyle Evans 35583c9dea1SGleb Smirnoff VM_CNT_INC(v_intr); 3562b3ad188SAdrian Chadd critical_enter(); 3572b3ad188SAdrian Chadd td = curthread; 3582b3ad188SAdrian Chadd oldframe = td->td_intr_frame; 3592b3ad188SAdrian Chadd td->td_intr_frame = tf; 36085918bebSAyrton Munoz (root->filter)(root->arg); 3612b3ad188SAdrian Chadd td->td_intr_frame = oldframe; 3622b3ad188SAdrian Chadd critical_exit(); 363df7a2251SAndrew Turner #ifdef HWPMC_HOOKS 364974692e3SAndrew Turner if (pmc_hook && TRAPF_USERMODE(tf) && 365974692e3SAndrew Turner (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) 366df7a2251SAndrew Turner pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf); 367df7a2251SAndrew Turner #endif 3682b3ad188SAdrian Chadd } 3692b3ad188SAdrian Chadd 370d1605cdaSAndrew Turner int 371d1605cdaSAndrew Turner intr_child_irq_handler(struct intr_pic *parent, uintptr_t irq) 372d1605cdaSAndrew Turner { 373d1605cdaSAndrew Turner struct intr_pic_child *child; 374d1605cdaSAndrew Turner bool found; 375d1605cdaSAndrew Turner 376d1605cdaSAndrew Turner found = false; 377d1605cdaSAndrew Turner mtx_lock_spin(&parent->pic_child_lock); 378d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent->pic_children, pc_next) { 379d1605cdaSAndrew Turner if (child->pc_start <= irq && 380d1605cdaSAndrew Turner irq < (child->pc_start + child->pc_length)) { 381d1605cdaSAndrew Turner found = true; 382d1605cdaSAndrew Turner break; 383d1605cdaSAndrew Turner } 384d1605cdaSAndrew Turner } 385d1605cdaSAndrew Turner mtx_unlock_spin(&parent->pic_child_lock); 386d1605cdaSAndrew Turner 387d1605cdaSAndrew Turner if (found) 388d1605cdaSAndrew Turner return (child->pc_filter(child->pc_filter_arg, irq)); 389d1605cdaSAndrew Turner 390d1605cdaSAndrew Turner return (FILTER_STRAY); 391d1605cdaSAndrew Turner } 392d1605cdaSAndrew Turner 3932b3ad188SAdrian Chadd /* 3942b3ad188SAdrian Chadd * interrupt controller dispatch function for interrupts. It should 3952b3ad188SAdrian Chadd * be called straight from the interrupt controller, when associated interrupt 3962b3ad188SAdrian Chadd * source is learned. 3972b3ad188SAdrian Chadd */ 398bff6be3eSSvatopluk Kraus int 399bff6be3eSSvatopluk Kraus intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) 4002b3ad188SAdrian Chadd { 4012b3ad188SAdrian Chadd 4022b3ad188SAdrian Chadd KASSERT(isrc != NULL, ("%s: no source", __func__)); 4032b3ad188SAdrian Chadd 404103d39efSJessica Clarke if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 4052b3ad188SAdrian Chadd isrc_increment_count(isrc); 4062b3ad188SAdrian Chadd 4072b3ad188SAdrian Chadd #ifdef INTR_SOLO 4082b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 4092b3ad188SAdrian Chadd int error; 4102b3ad188SAdrian Chadd error = isrc->isrc_filter(isrc->isrc_arg, tf); 4112b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 4122b3ad188SAdrian Chadd if (error == FILTER_HANDLED) 413bff6be3eSSvatopluk Kraus return (0); 4142b3ad188SAdrian Chadd } else 4152b3ad188SAdrian Chadd #endif 4162b3ad188SAdrian Chadd if (isrc->isrc_event != NULL) { 4172b3ad188SAdrian Chadd if (intr_event_handle(isrc->isrc_event, tf) == 0) 418bff6be3eSSvatopluk Kraus return (0); 4192b3ad188SAdrian Chadd } 4202b3ad188SAdrian Chadd 421103d39efSJessica Clarke if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 4222b3ad188SAdrian Chadd isrc_increment_straycount(isrc); 423bff6be3eSSvatopluk Kraus return (EINVAL); 4242b3ad188SAdrian Chadd } 4252b3ad188SAdrian Chadd 4262b3ad188SAdrian Chadd /* 4272b3ad188SAdrian Chadd * Alloc unique interrupt number (resource handle) for interrupt source. 4282b3ad188SAdrian Chadd * 4292b3ad188SAdrian Chadd * There could be various strategies how to allocate free interrupt number 4302b3ad188SAdrian Chadd * (resource handle) for new interrupt source. 4312b3ad188SAdrian Chadd * 4322b3ad188SAdrian Chadd * 1. Handles are always allocated forward, so handles are not recycled 4332b3ad188SAdrian Chadd * immediately. However, if only one free handle left which is reused 4342b3ad188SAdrian Chadd * constantly... 4352b3ad188SAdrian Chadd */ 436bff6be3eSSvatopluk Kraus static inline int 437bff6be3eSSvatopluk Kraus isrc_alloc_irq(struct intr_irqsrc *isrc) 4382b3ad188SAdrian Chadd { 439e88c3b1bSMichal Meloun u_int irq; 4402b3ad188SAdrian Chadd 4412b3ad188SAdrian Chadd mtx_assert(&isrc_table_lock, MA_OWNED); 4422b3ad188SAdrian Chadd 443e88c3b1bSMichal Meloun if (irq_next_free >= intr_nirq) 4442b3ad188SAdrian Chadd return (ENOSPC); 4452b3ad188SAdrian Chadd 446e88c3b1bSMichal Meloun for (irq = irq_next_free; irq < intr_nirq; irq++) { 4472b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 4482b3ad188SAdrian Chadd goto found; 4492b3ad188SAdrian Chadd } 4502b3ad188SAdrian Chadd for (irq = 0; irq < irq_next_free; irq++) { 4512b3ad188SAdrian Chadd if (irq_sources[irq] == NULL) 4522b3ad188SAdrian Chadd goto found; 4532b3ad188SAdrian Chadd } 4542b3ad188SAdrian Chadd 455e88c3b1bSMichal Meloun irq_next_free = intr_nirq; 4562b3ad188SAdrian Chadd return (ENOSPC); 4572b3ad188SAdrian Chadd 4582b3ad188SAdrian Chadd found: 4592b3ad188SAdrian Chadd isrc->isrc_irq = irq; 4602b3ad188SAdrian Chadd irq_sources[irq] = isrc; 4612b3ad188SAdrian Chadd 4622b3ad188SAdrian Chadd irq_next_free = irq + 1; 463e88c3b1bSMichal Meloun if (irq_next_free >= intr_nirq) 4642b3ad188SAdrian Chadd irq_next_free = 0; 4652b3ad188SAdrian Chadd return (0); 4662b3ad188SAdrian Chadd } 467bff6be3eSSvatopluk Kraus 4682b3ad188SAdrian Chadd /* 4692b3ad188SAdrian Chadd * Free unique interrupt number (resource handle) from interrupt source. 4702b3ad188SAdrian Chadd */ 471bff6be3eSSvatopluk Kraus static inline int 4722b3ad188SAdrian Chadd isrc_free_irq(struct intr_irqsrc *isrc) 4732b3ad188SAdrian Chadd { 4742b3ad188SAdrian Chadd 475bff6be3eSSvatopluk Kraus mtx_assert(&isrc_table_lock, MA_OWNED); 4762b3ad188SAdrian Chadd 477248f0cabSOleksandr Tymoshenko if (isrc->isrc_irq >= intr_nirq) 4782b3ad188SAdrian Chadd return (EINVAL); 479bff6be3eSSvatopluk Kraus if (irq_sources[isrc->isrc_irq] != isrc) 4802b3ad188SAdrian Chadd return (EINVAL); 4812b3ad188SAdrian Chadd 4822b3ad188SAdrian Chadd irq_sources[isrc->isrc_irq] = NULL; 4838442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 484a49f208dSMichal Meloun 485a49f208dSMichal Meloun /* 486a49f208dSMichal Meloun * If we are recovering from the state irq_sources table is full, 487a49f208dSMichal Meloun * then the following allocation should check the entire table. This 488a49f208dSMichal Meloun * will ensure maximum separation of allocation order from release 489a49f208dSMichal Meloun * order. 490a49f208dSMichal Meloun */ 491a49f208dSMichal Meloun if (irq_next_free >= intr_nirq) 492a49f208dSMichal Meloun irq_next_free = 0; 493a49f208dSMichal Meloun 4942b3ad188SAdrian Chadd return (0); 4952b3ad188SAdrian Chadd } 496bff6be3eSSvatopluk Kraus 49785918bebSAyrton Munoz device_t 498*4b01a7faSKyle Evans intr_irq_root_device(uint32_t rootnum) 49985918bebSAyrton Munoz { 500*4b01a7faSKyle Evans KASSERT(rootnum < INTR_ROOT_NUM, 501*4b01a7faSKyle Evans ("%s: invalid interrupt root %d", __func__, rootnum)); 502*4b01a7faSKyle Evans return (intr_irq_roots[rootnum].dev); 50385918bebSAyrton Munoz } 50485918bebSAyrton Munoz 5052b3ad188SAdrian Chadd /* 506bff6be3eSSvatopluk Kraus * Initialize interrupt source and register it into global interrupt table. 5072b3ad188SAdrian Chadd */ 508bff6be3eSSvatopluk Kraus int 509bff6be3eSSvatopluk Kraus intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, 510bff6be3eSSvatopluk Kraus const char *fmt, ...) 5112b3ad188SAdrian Chadd { 512bff6be3eSSvatopluk Kraus int error; 513bff6be3eSSvatopluk Kraus va_list ap; 5142b3ad188SAdrian Chadd 515bff6be3eSSvatopluk Kraus bzero(isrc, sizeof(struct intr_irqsrc)); 516bff6be3eSSvatopluk Kraus isrc->isrc_dev = dev; 5178442087fSMichal Meloun isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ 518bff6be3eSSvatopluk Kraus isrc->isrc_flags = flags; 5192b3ad188SAdrian Chadd 520bff6be3eSSvatopluk Kraus va_start(ap, fmt); 521bff6be3eSSvatopluk Kraus vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); 522bff6be3eSSvatopluk Kraus va_end(ap); 523bff6be3eSSvatopluk Kraus 524bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 525bff6be3eSSvatopluk Kraus error = isrc_alloc_irq(isrc); 526bff6be3eSSvatopluk Kraus if (error != 0) { 527bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 528bff6be3eSSvatopluk Kraus return (error); 5292b3ad188SAdrian Chadd } 530bff6be3eSSvatopluk Kraus /* 531bff6be3eSSvatopluk Kraus * Setup interrupt counters, but not for IPI sources. Those are setup 532bff6be3eSSvatopluk Kraus * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust 533bff6be3eSSvatopluk Kraus * our counter pool. 534bff6be3eSSvatopluk Kraus */ 535bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 536bff6be3eSSvatopluk Kraus isrc_setup_counters(isrc); 537bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 538bff6be3eSSvatopluk Kraus return (0); 5392b3ad188SAdrian Chadd } 5402b3ad188SAdrian Chadd 5412b3ad188SAdrian Chadd /* 542bff6be3eSSvatopluk Kraus * Deregister interrupt source from global interrupt table. 543bff6be3eSSvatopluk Kraus */ 544bff6be3eSSvatopluk Kraus int 545bff6be3eSSvatopluk Kraus intr_isrc_deregister(struct intr_irqsrc *isrc) 546bff6be3eSSvatopluk Kraus { 547bff6be3eSSvatopluk Kraus int error; 548bff6be3eSSvatopluk Kraus 549bff6be3eSSvatopluk Kraus mtx_lock(&isrc_table_lock); 550bff6be3eSSvatopluk Kraus if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) 551bff6be3eSSvatopluk Kraus isrc_release_counters(isrc); 552bff6be3eSSvatopluk Kraus error = isrc_free_irq(isrc); 553bff6be3eSSvatopluk Kraus mtx_unlock(&isrc_table_lock); 554bff6be3eSSvatopluk Kraus return (error); 555bff6be3eSSvatopluk Kraus } 556bff6be3eSSvatopluk Kraus 5575b613c19SSvatopluk Kraus #ifdef SMP 5585b613c19SSvatopluk Kraus /* 5595b613c19SSvatopluk Kraus * A support function for a PIC to decide if provided ISRC should be inited 5605b613c19SSvatopluk Kraus * on given cpu. The logic of INTR_ISRCF_BOUND flag and isrc_cpu member of 5615b613c19SSvatopluk Kraus * struct intr_irqsrc is the following: 5625b613c19SSvatopluk Kraus * 5635b613c19SSvatopluk Kraus * If INTR_ISRCF_BOUND is set, the ISRC should be inited only on cpus 5645b613c19SSvatopluk Kraus * set in isrc_cpu. If not, the ISRC should be inited on every cpu and 5655b613c19SSvatopluk Kraus * isrc_cpu is kept consistent with it. Thus isrc_cpu is always correct. 5665b613c19SSvatopluk Kraus */ 5675b613c19SSvatopluk Kraus bool 5685b613c19SSvatopluk Kraus intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu) 5695b613c19SSvatopluk Kraus { 5705b613c19SSvatopluk Kraus 5715b613c19SSvatopluk Kraus if (isrc->isrc_handlers == 0) 5725b613c19SSvatopluk Kraus return (false); 5735b613c19SSvatopluk Kraus if ((isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) == 0) 5745b613c19SSvatopluk Kraus return (false); 5755b613c19SSvatopluk Kraus if (isrc->isrc_flags & INTR_ISRCF_BOUND) 5765b613c19SSvatopluk Kraus return (CPU_ISSET(cpu, &isrc->isrc_cpu)); 5775b613c19SSvatopluk Kraus 5785b613c19SSvatopluk Kraus CPU_SET(cpu, &isrc->isrc_cpu); 5795b613c19SSvatopluk Kraus return (true); 5805b613c19SSvatopluk Kraus } 5815b613c19SSvatopluk Kraus #endif 5825b613c19SSvatopluk Kraus 5832b3ad188SAdrian Chadd #ifdef INTR_SOLO 5842b3ad188SAdrian Chadd /* 5852b3ad188SAdrian Chadd * Setup filter into interrupt source. 5862b3ad188SAdrian Chadd */ 5872b3ad188SAdrian Chadd static int 5882b3ad188SAdrian Chadd iscr_setup_filter(struct intr_irqsrc *isrc, const char *name, 5892b3ad188SAdrian Chadd intr_irq_filter_t *filter, void *arg, void **cookiep) 5902b3ad188SAdrian Chadd { 5912b3ad188SAdrian Chadd 5922b3ad188SAdrian Chadd if (filter == NULL) 5932b3ad188SAdrian Chadd return (EINVAL); 5942b3ad188SAdrian Chadd 5952b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 5962b3ad188SAdrian Chadd /* 5972b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 5982b3ad188SAdrian Chadd * how we handle interrupt sources. 5992b3ad188SAdrian Chadd */ 6002b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 6012b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6022b3ad188SAdrian Chadd return (EBUSY); 6032b3ad188SAdrian Chadd } 6042b3ad188SAdrian Chadd isrc->isrc_filter = filter; 6052b3ad188SAdrian Chadd isrc->isrc_arg = arg; 6062b3ad188SAdrian Chadd isrc_update_name(isrc, name); 6072b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6082b3ad188SAdrian Chadd 6092b3ad188SAdrian Chadd *cookiep = isrc; 6102b3ad188SAdrian Chadd return (0); 6112b3ad188SAdrian Chadd } 6122b3ad188SAdrian Chadd #endif 6132b3ad188SAdrian Chadd 6142b3ad188SAdrian Chadd /* 6152b3ad188SAdrian Chadd * Interrupt source pre_ithread method for MI interrupt framework. 6162b3ad188SAdrian Chadd */ 6172b3ad188SAdrian Chadd static void 6182b3ad188SAdrian Chadd intr_isrc_pre_ithread(void *arg) 6192b3ad188SAdrian Chadd { 6202b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6212b3ad188SAdrian Chadd 6222b3ad188SAdrian Chadd PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); 6232b3ad188SAdrian Chadd } 6242b3ad188SAdrian Chadd 6252b3ad188SAdrian Chadd /* 6262b3ad188SAdrian Chadd * Interrupt source post_ithread method for MI interrupt framework. 6272b3ad188SAdrian Chadd */ 6282b3ad188SAdrian Chadd static void 6292b3ad188SAdrian Chadd intr_isrc_post_ithread(void *arg) 6302b3ad188SAdrian Chadd { 6312b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6322b3ad188SAdrian Chadd 6332b3ad188SAdrian Chadd PIC_POST_ITHREAD(isrc->isrc_dev, isrc); 6342b3ad188SAdrian Chadd } 6352b3ad188SAdrian Chadd 6362b3ad188SAdrian Chadd /* 6372b3ad188SAdrian Chadd * Interrupt source post_filter method for MI interrupt framework. 6382b3ad188SAdrian Chadd */ 6392b3ad188SAdrian Chadd static void 6402b3ad188SAdrian Chadd intr_isrc_post_filter(void *arg) 6412b3ad188SAdrian Chadd { 6422b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6432b3ad188SAdrian Chadd 6442b3ad188SAdrian Chadd PIC_POST_FILTER(isrc->isrc_dev, isrc); 6452b3ad188SAdrian Chadd } 6462b3ad188SAdrian Chadd 6472b3ad188SAdrian Chadd /* 6482b3ad188SAdrian Chadd * Interrupt source assign_cpu method for MI interrupt framework. 6492b3ad188SAdrian Chadd */ 6502b3ad188SAdrian Chadd static int 6512b3ad188SAdrian Chadd intr_isrc_assign_cpu(void *arg, int cpu) 6522b3ad188SAdrian Chadd { 6532b3ad188SAdrian Chadd #ifdef SMP 6542b3ad188SAdrian Chadd struct intr_irqsrc *isrc = arg; 6552b3ad188SAdrian Chadd int error; 6562b3ad188SAdrian Chadd 6572b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 6582b3ad188SAdrian Chadd if (cpu == NOCPU) { 6592b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 6602b3ad188SAdrian Chadd isrc->isrc_flags &= ~INTR_ISRCF_BOUND; 6612b3ad188SAdrian Chadd } else { 6622b3ad188SAdrian Chadd CPU_SETOF(cpu, &isrc->isrc_cpu); 6632b3ad188SAdrian Chadd isrc->isrc_flags |= INTR_ISRCF_BOUND; 6642b3ad188SAdrian Chadd } 6652b3ad188SAdrian Chadd 6662b3ad188SAdrian Chadd /* 6672b3ad188SAdrian Chadd * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or 6682b3ad188SAdrian Chadd * re-balance it to another CPU or enable it on more CPUs. However, 6692b3ad188SAdrian Chadd * PIC is expected to change isrc_cpu appropriately to keep us well 670e3043798SPedro F. Giffuni * informed if the call is successful. 6712b3ad188SAdrian Chadd */ 6722b3ad188SAdrian Chadd if (irq_assign_cpu) { 673bff6be3eSSvatopluk Kraus error = PIC_BIND_INTR(isrc->isrc_dev, isrc); 6742b3ad188SAdrian Chadd if (error) { 6752b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 6762b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6772b3ad188SAdrian Chadd return (error); 6782b3ad188SAdrian Chadd } 6792b3ad188SAdrian Chadd } 6802b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 6812b3ad188SAdrian Chadd return (0); 6822b3ad188SAdrian Chadd #else 6832b3ad188SAdrian Chadd return (EOPNOTSUPP); 6842b3ad188SAdrian Chadd #endif 6852b3ad188SAdrian Chadd } 6862b3ad188SAdrian Chadd 6872b3ad188SAdrian Chadd /* 6882b3ad188SAdrian Chadd * Create interrupt event for interrupt source. 6892b3ad188SAdrian Chadd */ 6902b3ad188SAdrian Chadd static int 6912b3ad188SAdrian Chadd isrc_event_create(struct intr_irqsrc *isrc) 6922b3ad188SAdrian Chadd { 6932b3ad188SAdrian Chadd struct intr_event *ie; 6942b3ad188SAdrian Chadd int error; 6952b3ad188SAdrian Chadd 6962b3ad188SAdrian Chadd error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq, 6972b3ad188SAdrian Chadd intr_isrc_pre_ithread, intr_isrc_post_ithread, intr_isrc_post_filter, 6982b3ad188SAdrian Chadd intr_isrc_assign_cpu, "%s:", isrc->isrc_name); 6992b3ad188SAdrian Chadd if (error) 7002b3ad188SAdrian Chadd return (error); 7012b3ad188SAdrian Chadd 7022b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7032b3ad188SAdrian Chadd /* 7042b3ad188SAdrian Chadd * Make sure that we do not mix the two ways 7052b3ad188SAdrian Chadd * how we handle interrupt sources. Let contested event wins. 7062b3ad188SAdrian Chadd */ 707169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 7082b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { 709169e6abdSSvatopluk Kraus #else 710169e6abdSSvatopluk Kraus if (isrc->isrc_event != NULL) { 711169e6abdSSvatopluk Kraus #endif 7122b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7132b3ad188SAdrian Chadd intr_event_destroy(ie); 7142b3ad188SAdrian Chadd return (isrc->isrc_event != NULL ? EBUSY : 0); 7152b3ad188SAdrian Chadd } 7162b3ad188SAdrian Chadd isrc->isrc_event = ie; 7172b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7182b3ad188SAdrian Chadd 7192b3ad188SAdrian Chadd return (0); 7202b3ad188SAdrian Chadd } 7212b3ad188SAdrian Chadd #ifdef notyet 7222b3ad188SAdrian Chadd /* 7232b3ad188SAdrian Chadd * Destroy interrupt event for interrupt source. 7242b3ad188SAdrian Chadd */ 7252b3ad188SAdrian Chadd static void 7262b3ad188SAdrian Chadd isrc_event_destroy(struct intr_irqsrc *isrc) 7272b3ad188SAdrian Chadd { 7282b3ad188SAdrian Chadd struct intr_event *ie; 7292b3ad188SAdrian Chadd 7302b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7312b3ad188SAdrian Chadd ie = isrc->isrc_event; 7322b3ad188SAdrian Chadd isrc->isrc_event = NULL; 7332b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7342b3ad188SAdrian Chadd 7352b3ad188SAdrian Chadd if (ie != NULL) 7362b3ad188SAdrian Chadd intr_event_destroy(ie); 7372b3ad188SAdrian Chadd } 7382b3ad188SAdrian Chadd #endif 7392b3ad188SAdrian Chadd /* 7402b3ad188SAdrian Chadd * Add handler to interrupt source. 7412b3ad188SAdrian Chadd */ 7422b3ad188SAdrian Chadd static int 7432b3ad188SAdrian Chadd isrc_add_handler(struct intr_irqsrc *isrc, const char *name, 7442b3ad188SAdrian Chadd driver_filter_t filter, driver_intr_t handler, void *arg, 7452b3ad188SAdrian Chadd enum intr_type flags, void **cookiep) 7462b3ad188SAdrian Chadd { 7472b3ad188SAdrian Chadd int error; 7482b3ad188SAdrian Chadd 7492b3ad188SAdrian Chadd if (isrc->isrc_event == NULL) { 7502b3ad188SAdrian Chadd error = isrc_event_create(isrc); 7512b3ad188SAdrian Chadd if (error) 7522b3ad188SAdrian Chadd return (error); 7532b3ad188SAdrian Chadd } 7542b3ad188SAdrian Chadd 7552b3ad188SAdrian Chadd error = intr_event_add_handler(isrc->isrc_event, name, filter, handler, 7562b3ad188SAdrian Chadd arg, intr_priority(flags), flags, cookiep); 7572b3ad188SAdrian Chadd if (error == 0) { 7582b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 7592b3ad188SAdrian Chadd intrcnt_updatename(isrc); 7602b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 7612b3ad188SAdrian Chadd } 7622b3ad188SAdrian Chadd 7632b3ad188SAdrian Chadd return (error); 7642b3ad188SAdrian Chadd } 7652b3ad188SAdrian Chadd 7662b3ad188SAdrian Chadd /* 7672b3ad188SAdrian Chadd * Lookup interrupt controller locked. 7682b3ad188SAdrian Chadd */ 769bff6be3eSSvatopluk Kraus static inline struct intr_pic * 7709f3a552fSElliott Mitchell pic_lookup_locked(device_t dev, intptr_t xref, u_int flags) 7712b3ad188SAdrian Chadd { 7722b3ad188SAdrian Chadd struct intr_pic *pic; 7732b3ad188SAdrian Chadd 7742b3ad188SAdrian Chadd mtx_assert(&pic_list_lock, MA_OWNED); 7752b3ad188SAdrian Chadd 7764be58cbaSSvatopluk Kraus if (dev == NULL && xref == 0) 7774be58cbaSSvatopluk Kraus return (NULL); 7784be58cbaSSvatopluk Kraus 7794be58cbaSSvatopluk Kraus /* Note that pic->pic_dev is never NULL on registered PIC. */ 7802b3ad188SAdrian Chadd SLIST_FOREACH(pic, &pic_list, pic_next) { 781c0d52370SAndrew Turner if ((pic->pic_flags & FLAG_TYPE_MASK) != 782c0d52370SAndrew Turner (flags & FLAG_TYPE_MASK)) 783c0d52370SAndrew Turner continue; 784c0d52370SAndrew Turner 7854be58cbaSSvatopluk Kraus if (dev == NULL) { 7864be58cbaSSvatopluk Kraus if (xref == pic->pic_xref) 7874be58cbaSSvatopluk Kraus return (pic); 7884be58cbaSSvatopluk Kraus } else if (xref == 0 || pic->pic_xref == 0) { 7894be58cbaSSvatopluk Kraus if (dev == pic->pic_dev) 7904be58cbaSSvatopluk Kraus return (pic); 7914be58cbaSSvatopluk Kraus } else if (xref == pic->pic_xref && dev == pic->pic_dev) 7922b3ad188SAdrian Chadd return (pic); 7932b3ad188SAdrian Chadd } 7942b3ad188SAdrian Chadd return (NULL); 7952b3ad188SAdrian Chadd } 7962b3ad188SAdrian Chadd 7972b3ad188SAdrian Chadd /* 7982b3ad188SAdrian Chadd * Lookup interrupt controller. 7992b3ad188SAdrian Chadd */ 8002b3ad188SAdrian Chadd static struct intr_pic * 8019f3a552fSElliott Mitchell pic_lookup(device_t dev, intptr_t xref, u_int flags) 8022b3ad188SAdrian Chadd { 8032b3ad188SAdrian Chadd struct intr_pic *pic; 8042b3ad188SAdrian Chadd 8052b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 806c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 8072b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8082b3ad188SAdrian Chadd return (pic); 8092b3ad188SAdrian Chadd } 8102b3ad188SAdrian Chadd 8112b3ad188SAdrian Chadd /* 8122b3ad188SAdrian Chadd * Create interrupt controller. 8132b3ad188SAdrian Chadd */ 8142b3ad188SAdrian Chadd static struct intr_pic * 8159f3a552fSElliott Mitchell pic_create(device_t dev, intptr_t xref, u_int flags) 8162b3ad188SAdrian Chadd { 8172b3ad188SAdrian Chadd struct intr_pic *pic; 8182b3ad188SAdrian Chadd 8192b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 820c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 8212b3ad188SAdrian Chadd if (pic != NULL) { 8222b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8232b3ad188SAdrian Chadd return (pic); 8242b3ad188SAdrian Chadd } 8252b3ad188SAdrian Chadd pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO); 826b48c6083SAndrew Turner if (pic == NULL) { 827b48c6083SAndrew Turner mtx_unlock(&pic_list_lock); 828b48c6083SAndrew Turner return (NULL); 829b48c6083SAndrew Turner } 8302b3ad188SAdrian Chadd pic->pic_xref = xref; 8312b3ad188SAdrian Chadd pic->pic_dev = dev; 832c0d52370SAndrew Turner pic->pic_flags = flags; 833d1605cdaSAndrew Turner mtx_init(&pic->pic_child_lock, "pic child lock", NULL, MTX_SPIN); 8342b3ad188SAdrian Chadd SLIST_INSERT_HEAD(&pic_list, pic, pic_next); 8352b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8362b3ad188SAdrian Chadd 8372b3ad188SAdrian Chadd return (pic); 8382b3ad188SAdrian Chadd } 8392b3ad188SAdrian Chadd #ifdef notyet 8402b3ad188SAdrian Chadd /* 8412b3ad188SAdrian Chadd * Destroy interrupt controller. 8422b3ad188SAdrian Chadd */ 8432b3ad188SAdrian Chadd static void 8449f3a552fSElliott Mitchell pic_destroy(device_t dev, intptr_t xref, u_int flags) 8452b3ad188SAdrian Chadd { 8462b3ad188SAdrian Chadd struct intr_pic *pic; 8472b3ad188SAdrian Chadd 8482b3ad188SAdrian Chadd mtx_lock(&pic_list_lock); 849c0d52370SAndrew Turner pic = pic_lookup_locked(dev, xref, flags); 8502b3ad188SAdrian Chadd if (pic == NULL) { 8512b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8522b3ad188SAdrian Chadd return; 8532b3ad188SAdrian Chadd } 8542b3ad188SAdrian Chadd SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next); 8552b3ad188SAdrian Chadd mtx_unlock(&pic_list_lock); 8562b3ad188SAdrian Chadd 8572b3ad188SAdrian Chadd free(pic, M_INTRNG); 8582b3ad188SAdrian Chadd } 8592b3ad188SAdrian Chadd #endif 8602b3ad188SAdrian Chadd /* 8612b3ad188SAdrian Chadd * Register interrupt controller. 8622b3ad188SAdrian Chadd */ 8639346e913SAndrew Turner struct intr_pic * 8642b3ad188SAdrian Chadd intr_pic_register(device_t dev, intptr_t xref) 8652b3ad188SAdrian Chadd { 8662b3ad188SAdrian Chadd struct intr_pic *pic; 8672b3ad188SAdrian Chadd 8684be58cbaSSvatopluk Kraus if (dev == NULL) 8699346e913SAndrew Turner return (NULL); 870c0d52370SAndrew Turner pic = pic_create(dev, xref, FLAG_PIC); 8712b3ad188SAdrian Chadd if (pic == NULL) 8729346e913SAndrew Turner return (NULL); 8732b3ad188SAdrian Chadd 874cff33fa8SEd Maste debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic, 875cff33fa8SEd Maste device_get_nameunit(dev), dev, (uintmax_t)xref); 8769346e913SAndrew Turner return (pic); 8772b3ad188SAdrian Chadd } 8782b3ad188SAdrian Chadd 8792b3ad188SAdrian Chadd /* 8802b3ad188SAdrian Chadd * Unregister interrupt controller. 8812b3ad188SAdrian Chadd */ 8822b3ad188SAdrian Chadd int 883bff6be3eSSvatopluk Kraus intr_pic_deregister(device_t dev, intptr_t xref) 8842b3ad188SAdrian Chadd { 8852b3ad188SAdrian Chadd 8862b3ad188SAdrian Chadd panic("%s: not implemented", __func__); 8872b3ad188SAdrian Chadd } 8882b3ad188SAdrian Chadd 8892b3ad188SAdrian Chadd /* 8902b3ad188SAdrian Chadd * Mark interrupt controller (itself) as a root one. 8912b3ad188SAdrian Chadd * 8922b3ad188SAdrian Chadd * Note that only an interrupt controller can really know its position 8932b3ad188SAdrian Chadd * in interrupt controller's tree. So root PIC must claim itself as a root. 8942b3ad188SAdrian Chadd * 8952b3ad188SAdrian Chadd * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, 8962b3ad188SAdrian Chadd * page 30: 8972b3ad188SAdrian Chadd * "The root of the interrupt tree is determined when traversal 8982b3ad188SAdrian Chadd * of the interrupt tree reaches an interrupt controller node without 8992b3ad188SAdrian Chadd * an interrupts property and thus no explicit interrupt parent." 9002b3ad188SAdrian Chadd */ 9012b3ad188SAdrian Chadd int 9022b3ad188SAdrian Chadd intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, 903*4b01a7faSKyle Evans void *arg, uint32_t rootnum) 9042b3ad188SAdrian Chadd { 9053fc155dcSAndrew Turner struct intr_pic *pic; 90685918bebSAyrton Munoz struct intr_irq_root *root; 9072b3ad188SAdrian Chadd 908c0d52370SAndrew Turner pic = pic_lookup(dev, xref, FLAG_PIC); 9093fc155dcSAndrew Turner if (pic == NULL) { 9102b3ad188SAdrian Chadd device_printf(dev, "not registered\n"); 9112b3ad188SAdrian Chadd return (EINVAL); 9122b3ad188SAdrian Chadd } 9133fc155dcSAndrew Turner 914c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_PIC, 9153fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 9163fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 9173fc155dcSAndrew Turner 9182b3ad188SAdrian Chadd if (filter == NULL) { 9192b3ad188SAdrian Chadd device_printf(dev, "filter missing\n"); 9202b3ad188SAdrian Chadd return (EINVAL); 9212b3ad188SAdrian Chadd } 9222b3ad188SAdrian Chadd 9232b3ad188SAdrian Chadd /* 9242b3ad188SAdrian Chadd * Only one interrupt controllers could be on the root for now. 9252b3ad188SAdrian Chadd * Note that we further suppose that there is not threaded interrupt 9262b3ad188SAdrian Chadd * routine (handler) on the root. See intr_irq_handler(). 9272b3ad188SAdrian Chadd */ 928*4b01a7faSKyle Evans KASSERT(rootnum < INTR_ROOT_NUM, 929*4b01a7faSKyle Evans ("%s: invalid interrupt root %d", __func__, rootnum)); 930*4b01a7faSKyle Evans root = &intr_irq_roots[rootnum]; 93185918bebSAyrton Munoz if (root->dev != NULL) { 9322b3ad188SAdrian Chadd device_printf(dev, "another root already set\n"); 9332b3ad188SAdrian Chadd return (EBUSY); 9342b3ad188SAdrian Chadd } 9352b3ad188SAdrian Chadd 93685918bebSAyrton Munoz root->dev = dev; 93785918bebSAyrton Munoz root->filter = filter; 93885918bebSAyrton Munoz root->arg = arg; 9392b3ad188SAdrian Chadd 9402b3ad188SAdrian Chadd debugf("irq root set to %s\n", device_get_nameunit(dev)); 9412b3ad188SAdrian Chadd return (0); 9422b3ad188SAdrian Chadd } 9432b3ad188SAdrian Chadd 944d1605cdaSAndrew Turner /* 945d1605cdaSAndrew Turner * Add a handler to manage a sub range of a parents interrupts. 946d1605cdaSAndrew Turner */ 947a3e828c9SJessica Clarke int 948d1605cdaSAndrew Turner intr_pic_add_handler(device_t parent, struct intr_pic *pic, 949d1605cdaSAndrew Turner intr_child_irq_filter_t *filter, void *arg, uintptr_t start, 950d1605cdaSAndrew Turner uintptr_t length) 951d1605cdaSAndrew Turner { 952d1605cdaSAndrew Turner struct intr_pic *parent_pic; 953d1605cdaSAndrew Turner struct intr_pic_child *newchild; 954d1605cdaSAndrew Turner #ifdef INVARIANTS 955d1605cdaSAndrew Turner struct intr_pic_child *child; 956d1605cdaSAndrew Turner #endif 957d1605cdaSAndrew Turner 958c0d52370SAndrew Turner /* Find the parent PIC */ 959c0d52370SAndrew Turner parent_pic = pic_lookup(parent, 0, FLAG_PIC); 960d1605cdaSAndrew Turner if (parent_pic == NULL) 961a3e828c9SJessica Clarke return (ENXIO); 962d1605cdaSAndrew Turner 963d1605cdaSAndrew Turner newchild = malloc(sizeof(*newchild), M_INTRNG, M_WAITOK | M_ZERO); 964d1605cdaSAndrew Turner newchild->pc_pic = pic; 965d1605cdaSAndrew Turner newchild->pc_filter = filter; 966d1605cdaSAndrew Turner newchild->pc_filter_arg = arg; 967d1605cdaSAndrew Turner newchild->pc_start = start; 968d1605cdaSAndrew Turner newchild->pc_length = length; 969d1605cdaSAndrew Turner 970d1605cdaSAndrew Turner mtx_lock_spin(&parent_pic->pic_child_lock); 971d1605cdaSAndrew Turner #ifdef INVARIANTS 972d1605cdaSAndrew Turner SLIST_FOREACH(child, &parent_pic->pic_children, pc_next) { 973d1605cdaSAndrew Turner KASSERT(child->pc_pic != pic, ("%s: Adding a child PIC twice", 974d1605cdaSAndrew Turner __func__)); 975d1605cdaSAndrew Turner } 976d1605cdaSAndrew Turner #endif 977d1605cdaSAndrew Turner SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next); 978d1605cdaSAndrew Turner mtx_unlock_spin(&parent_pic->pic_child_lock); 979d1605cdaSAndrew Turner 980a3e828c9SJessica Clarke return (0); 981d1605cdaSAndrew Turner } 982d1605cdaSAndrew Turner 983895c8b1cSMichal Meloun static int 984895c8b1cSMichal Meloun intr_resolve_irq(device_t dev, intptr_t xref, struct intr_map_data *data, 985895c8b1cSMichal Meloun struct intr_irqsrc **isrc) 9862b3ad188SAdrian Chadd { 987bff6be3eSSvatopluk Kraus struct intr_pic *pic; 988895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 989bff6be3eSSvatopluk Kraus 990bff6be3eSSvatopluk Kraus if (data == NULL) 991bff6be3eSSvatopluk Kraus return (EINVAL); 992bff6be3eSSvatopluk Kraus 993c0d52370SAndrew Turner pic = pic_lookup(dev, xref, 994c0d52370SAndrew Turner (data->type == INTR_MAP_DATA_MSI) ? FLAG_MSI : FLAG_PIC); 99515adccc6SSvatopluk Kraus if (pic == NULL) 996bff6be3eSSvatopluk Kraus return (ESRCH); 997bff6be3eSSvatopluk Kraus 998895c8b1cSMichal Meloun switch (data->type) { 999895c8b1cSMichal Meloun case INTR_MAP_DATA_MSI: 1000c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 1001895c8b1cSMichal Meloun ("%s: Found a non-MSI controller: %s", __func__, 1002895c8b1cSMichal Meloun device_get_name(pic->pic_dev))); 1003895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)data; 1004895c8b1cSMichal Meloun *isrc = msi->isrc; 1005895c8b1cSMichal Meloun return (0); 1006895c8b1cSMichal Meloun 1007895c8b1cSMichal Meloun default: 1008c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_PIC, 10093fc155dcSAndrew Turner ("%s: Found a non-PIC controller: %s", __func__, 10103fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 1011895c8b1cSMichal Meloun return (PIC_MAP_INTR(pic->pic_dev, data, isrc)); 1012895c8b1cSMichal Meloun } 1013895c8b1cSMichal Meloun } 1014895c8b1cSMichal Meloun 1015eb20867fSMichal Meloun bool 1016eb20867fSMichal Meloun intr_is_per_cpu(struct resource *res) 1017eb20867fSMichal Meloun { 1018eb20867fSMichal Meloun u_int res_id; 1019eb20867fSMichal Meloun struct intr_irqsrc *isrc; 1020eb20867fSMichal Meloun 1021eb20867fSMichal Meloun res_id = (u_int)rman_get_start(res); 1022eb20867fSMichal Meloun isrc = intr_map_get_isrc(res_id); 1023eb20867fSMichal Meloun 1024eb20867fSMichal Meloun if (isrc == NULL) 1025eb20867fSMichal Meloun panic("Attempt to get isrc for non-active resource id: %u\n", 1026eb20867fSMichal Meloun res_id); 1027eb20867fSMichal Meloun return ((isrc->isrc_flags & INTR_ISRCF_PPI) != 0); 1028eb20867fSMichal Meloun } 1029eb20867fSMichal Meloun 1030895c8b1cSMichal Meloun int 1031895c8b1cSMichal Meloun intr_activate_irq(device_t dev, struct resource *res) 1032895c8b1cSMichal Meloun { 1033895c8b1cSMichal Meloun device_t map_dev; 1034895c8b1cSMichal Meloun intptr_t map_xref; 1035895c8b1cSMichal Meloun struct intr_map_data *data; 1036895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 1037895c8b1cSMichal Meloun u_int res_id; 1038895c8b1cSMichal Meloun int error; 1039895c8b1cSMichal Meloun 1040895c8b1cSMichal Meloun KASSERT(rman_get_start(res) == rman_get_end(res), 1041895c8b1cSMichal Meloun ("%s: more interrupts in resource", __func__)); 1042895c8b1cSMichal Meloun 1043895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1044895c8b1cSMichal Meloun if (intr_map_get_isrc(res_id) != NULL) 1045895c8b1cSMichal Meloun panic("Attempt to double activation of resource id: %u\n", 1046895c8b1cSMichal Meloun res_id); 1047895c8b1cSMichal Meloun intr_map_copy_map_data(res_id, &map_dev, &map_xref, &data); 1048895c8b1cSMichal Meloun error = intr_resolve_irq(map_dev, map_xref, data, &isrc); 1049895c8b1cSMichal Meloun if (error != 0) { 1050895c8b1cSMichal Meloun free(data, M_INTRNG); 1051895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 1052895c8b1cSMichal Meloun /* if (error == EINVAL) return(0); */ 1053bff6be3eSSvatopluk Kraus return (error); 1054bff6be3eSSvatopluk Kraus } 1055895c8b1cSMichal Meloun intr_map_set_isrc(res_id, isrc); 1056895c8b1cSMichal Meloun rman_set_virtual(res, data); 1057895c8b1cSMichal Meloun return (PIC_ACTIVATE_INTR(isrc->isrc_dev, isrc, res, data)); 1058bff6be3eSSvatopluk Kraus } 1059bff6be3eSSvatopluk Kraus 1060bff6be3eSSvatopluk Kraus int 1061895c8b1cSMichal Meloun intr_deactivate_irq(device_t dev, struct resource *res) 1062bff6be3eSSvatopluk Kraus { 1063bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1064bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1065895c8b1cSMichal Meloun u_int res_id; 1066895c8b1cSMichal Meloun int error; 1067bff6be3eSSvatopluk Kraus 1068bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1069bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1070bff6be3eSSvatopluk Kraus 1071895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1072895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 1073bff6be3eSSvatopluk Kraus if (isrc == NULL) 1074895c8b1cSMichal Meloun panic("Attempt to deactivate non-active resource id: %u\n", 1075895c8b1cSMichal Meloun res_id); 1076bff6be3eSSvatopluk Kraus 1077c4263292SSvatopluk Kraus data = rman_get_virtual(res); 1078895c8b1cSMichal Meloun error = PIC_DEACTIVATE_INTR(isrc->isrc_dev, isrc, res, data); 1079895c8b1cSMichal Meloun intr_map_set_isrc(res_id, NULL); 1080895c8b1cSMichal Meloun rman_set_virtual(res, NULL); 1081895c8b1cSMichal Meloun free(data, M_INTRNG); 1082895c8b1cSMichal Meloun return (error); 1083bff6be3eSSvatopluk Kraus } 1084bff6be3eSSvatopluk Kraus 1085bff6be3eSSvatopluk Kraus int 1086bff6be3eSSvatopluk Kraus intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, 1087bff6be3eSSvatopluk Kraus driver_intr_t hand, void *arg, int flags, void **cookiep) 1088bff6be3eSSvatopluk Kraus { 1089bff6be3eSSvatopluk Kraus int error; 1090bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1091bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1092bff6be3eSSvatopluk Kraus const char *name; 1093895c8b1cSMichal Meloun u_int res_id; 1094bff6be3eSSvatopluk Kraus 1095bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1096bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1097bff6be3eSSvatopluk Kraus 1098895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1099895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 1100895c8b1cSMichal Meloun if (isrc == NULL) { 1101895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 1102bff6be3eSSvatopluk Kraus return (EINVAL); 1103895c8b1cSMichal Meloun } 11042b3ad188SAdrian Chadd 1105c4263292SSvatopluk Kraus data = rman_get_virtual(res); 11062b3ad188SAdrian Chadd name = device_get_nameunit(dev); 11072b3ad188SAdrian Chadd 11082b3ad188SAdrian Chadd #ifdef INTR_SOLO 11092b3ad188SAdrian Chadd /* 1110e3043798SPedro F. Giffuni * Standard handling is done through MI interrupt framework. However, 11112b3ad188SAdrian Chadd * some interrupts could request solely own special handling. This 11122b3ad188SAdrian Chadd * non standard handling can be used for interrupt controllers without 11132b3ad188SAdrian Chadd * handler (filter only), so in case that interrupt controllers are 11142b3ad188SAdrian Chadd * chained, MI interrupt framework is called only in leaf controller. 11152b3ad188SAdrian Chadd * 11162b3ad188SAdrian Chadd * Note that root interrupt controller routine is served as well, 11172b3ad188SAdrian Chadd * however in intr_irq_handler(), i.e. main system dispatch routine. 11182b3ad188SAdrian Chadd */ 11192b3ad188SAdrian Chadd if (flags & INTR_SOLO && hand != NULL) { 11202b3ad188SAdrian Chadd debugf("irq %u cannot solo on %s\n", irq, name); 11212b3ad188SAdrian Chadd return (EINVAL); 11222b3ad188SAdrian Chadd } 11232b3ad188SAdrian Chadd 11242b3ad188SAdrian Chadd if (flags & INTR_SOLO) { 11252b3ad188SAdrian Chadd error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, 11262b3ad188SAdrian Chadd arg, cookiep); 1127ce44a736SIan Lepore debugf("irq %u setup filter error %d on %s\n", isrc->isrc_irq, error, 11282b3ad188SAdrian Chadd name); 11292b3ad188SAdrian Chadd } else 11302b3ad188SAdrian Chadd #endif 11312b3ad188SAdrian Chadd { 11322b3ad188SAdrian Chadd error = isrc_add_handler(isrc, name, filt, hand, arg, flags, 11332b3ad188SAdrian Chadd cookiep); 1134ce44a736SIan Lepore debugf("irq %u add handler error %d on %s\n", isrc->isrc_irq, error, name); 11352b3ad188SAdrian Chadd } 11362b3ad188SAdrian Chadd if (error != 0) 11372b3ad188SAdrian Chadd return (error); 11382b3ad188SAdrian Chadd 11392b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1140bff6be3eSSvatopluk Kraus error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); 1141bff6be3eSSvatopluk Kraus if (error == 0) { 11422b3ad188SAdrian Chadd isrc->isrc_handlers++; 1143bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 1) 11442b3ad188SAdrian Chadd PIC_ENABLE_INTR(isrc->isrc_dev, isrc); 11452b3ad188SAdrian Chadd } 11462b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 1147bff6be3eSSvatopluk Kraus if (error != 0) 1148bff6be3eSSvatopluk Kraus intr_event_remove_handler(*cookiep); 1149bff6be3eSSvatopluk Kraus return (error); 11502b3ad188SAdrian Chadd } 11512b3ad188SAdrian Chadd 11522b3ad188SAdrian Chadd int 1153bff6be3eSSvatopluk Kraus intr_teardown_irq(device_t dev, struct resource *res, void *cookie) 11542b3ad188SAdrian Chadd { 11552b3ad188SAdrian Chadd int error; 1156bff6be3eSSvatopluk Kraus struct intr_map_data *data; 1157bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1158895c8b1cSMichal Meloun u_int res_id; 11592b3ad188SAdrian Chadd 1160bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1161bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1162bff6be3eSSvatopluk Kraus 1163895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1164895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 11652b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 11662b3ad188SAdrian Chadd return (EINVAL); 1167bff6be3eSSvatopluk Kraus 1168c4263292SSvatopluk Kraus data = rman_get_virtual(res); 1169c4263292SSvatopluk Kraus 1170169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 11712b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 11722b3ad188SAdrian Chadd if (isrc != cookie) 11732b3ad188SAdrian Chadd return (EINVAL); 11742b3ad188SAdrian Chadd 11752b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11762b3ad188SAdrian Chadd isrc->isrc_filter = NULL; 11772b3ad188SAdrian Chadd isrc->isrc_arg = NULL; 11782b3ad188SAdrian Chadd isrc->isrc_handlers = 0; 11792b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1180bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 11812b3ad188SAdrian Chadd isrc_update_name(isrc, NULL); 11822b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11832b3ad188SAdrian Chadd return (0); 11842b3ad188SAdrian Chadd } 1185169e6abdSSvatopluk Kraus #endif 11862b3ad188SAdrian Chadd if (isrc != intr_handler_source(cookie)) 11872b3ad188SAdrian Chadd return (EINVAL); 11882b3ad188SAdrian Chadd 11892b3ad188SAdrian Chadd error = intr_event_remove_handler(cookie); 11902b3ad188SAdrian Chadd if (error == 0) { 11912b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 11922b3ad188SAdrian Chadd isrc->isrc_handlers--; 1193bff6be3eSSvatopluk Kraus if (isrc->isrc_handlers == 0) 11942b3ad188SAdrian Chadd PIC_DISABLE_INTR(isrc->isrc_dev, isrc); 1195bff6be3eSSvatopluk Kraus PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); 11962b3ad188SAdrian Chadd intrcnt_updatename(isrc); 11972b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 11982b3ad188SAdrian Chadd } 11992b3ad188SAdrian Chadd return (error); 12002b3ad188SAdrian Chadd } 12012b3ad188SAdrian Chadd 12022b3ad188SAdrian Chadd int 1203bff6be3eSSvatopluk Kraus intr_describe_irq(device_t dev, struct resource *res, void *cookie, 1204bff6be3eSSvatopluk Kraus const char *descr) 12052b3ad188SAdrian Chadd { 12062b3ad188SAdrian Chadd int error; 1207bff6be3eSSvatopluk Kraus struct intr_irqsrc *isrc; 1208895c8b1cSMichal Meloun u_int res_id; 12092b3ad188SAdrian Chadd 1210bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1211bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1212bff6be3eSSvatopluk Kraus 1213895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1214895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 12152b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 12162b3ad188SAdrian Chadd return (EINVAL); 1217169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 12182b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) { 12192b3ad188SAdrian Chadd if (isrc != cookie) 12202b3ad188SAdrian Chadd return (EINVAL); 12212b3ad188SAdrian Chadd 12222b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12232b3ad188SAdrian Chadd isrc_update_name(isrc, descr); 12242b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12252b3ad188SAdrian Chadd return (0); 12262b3ad188SAdrian Chadd } 1227169e6abdSSvatopluk Kraus #endif 12282b3ad188SAdrian Chadd error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); 12292b3ad188SAdrian Chadd if (error == 0) { 12302b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 12312b3ad188SAdrian Chadd intrcnt_updatename(isrc); 12322b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 12332b3ad188SAdrian Chadd } 12342b3ad188SAdrian Chadd return (error); 12352b3ad188SAdrian Chadd } 12362b3ad188SAdrian Chadd 12372b3ad188SAdrian Chadd #ifdef SMP 12382b3ad188SAdrian Chadd int 1239bff6be3eSSvatopluk Kraus intr_bind_irq(device_t dev, struct resource *res, int cpu) 12402b3ad188SAdrian Chadd { 12412b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 1242895c8b1cSMichal Meloun u_int res_id; 12432b3ad188SAdrian Chadd 1244bff6be3eSSvatopluk Kraus KASSERT(rman_get_start(res) == rman_get_end(res), 1245bff6be3eSSvatopluk Kraus ("%s: more interrupts in resource", __func__)); 1246bff6be3eSSvatopluk Kraus 1247895c8b1cSMichal Meloun res_id = (u_int)rman_get_start(res); 1248895c8b1cSMichal Meloun isrc = intr_map_get_isrc(res_id); 12492b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0) 12502b3ad188SAdrian Chadd return (EINVAL); 1251169e6abdSSvatopluk Kraus #ifdef INTR_SOLO 12522b3ad188SAdrian Chadd if (isrc->isrc_filter != NULL) 12532b3ad188SAdrian Chadd return (intr_isrc_assign_cpu(isrc, cpu)); 1254169e6abdSSvatopluk Kraus #endif 12552b3ad188SAdrian Chadd return (intr_event_bind(isrc->isrc_event, cpu)); 12562b3ad188SAdrian Chadd } 12572b3ad188SAdrian Chadd 12582b3ad188SAdrian Chadd /* 12592b3ad188SAdrian Chadd * Return the CPU that the next interrupt source should use. 12602b3ad188SAdrian Chadd * For now just returns the next CPU according to round-robin. 12612b3ad188SAdrian Chadd */ 12622b3ad188SAdrian Chadd u_int 12632b3ad188SAdrian Chadd intr_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask) 12642b3ad188SAdrian Chadd { 1265a92a2f00SAndrew Turner u_int cpu; 12662b3ad188SAdrian Chadd 1267a92a2f00SAndrew Turner KASSERT(!CPU_EMPTY(cpumask), ("%s: Empty CPU mask", __func__)); 1268a92a2f00SAndrew Turner if (!irq_assign_cpu || mp_ncpus == 1) { 1269a92a2f00SAndrew Turner cpu = PCPU_GET(cpuid); 1270a92a2f00SAndrew Turner 1271a92a2f00SAndrew Turner if (CPU_ISSET(cpu, cpumask)) 1272a92a2f00SAndrew Turner return (curcpu); 1273a92a2f00SAndrew Turner 1274a92a2f00SAndrew Turner return (CPU_FFS(cpumask) - 1); 1275a92a2f00SAndrew Turner } 12762b3ad188SAdrian Chadd 12772b3ad188SAdrian Chadd do { 12782b3ad188SAdrian Chadd last_cpu++; 12792b3ad188SAdrian Chadd if (last_cpu > mp_maxid) 12802b3ad188SAdrian Chadd last_cpu = 0; 12812b3ad188SAdrian Chadd } while (!CPU_ISSET(last_cpu, cpumask)); 12822b3ad188SAdrian Chadd return (last_cpu); 12832b3ad188SAdrian Chadd } 12842b3ad188SAdrian Chadd 1285dc425090SMitchell Horne #ifndef EARLY_AP_STARTUP 12862b3ad188SAdrian Chadd /* 12872b3ad188SAdrian Chadd * Distribute all the interrupt sources among the available 12882b3ad188SAdrian Chadd * CPUs once the AP's have been launched. 12892b3ad188SAdrian Chadd */ 12902b3ad188SAdrian Chadd static void 12912b3ad188SAdrian Chadd intr_irq_shuffle(void *arg __unused) 12922b3ad188SAdrian Chadd { 12932b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 12942b3ad188SAdrian Chadd u_int i; 12952b3ad188SAdrian Chadd 12962b3ad188SAdrian Chadd if (mp_ncpus == 1) 12972b3ad188SAdrian Chadd return; 12982b3ad188SAdrian Chadd 12992b3ad188SAdrian Chadd mtx_lock(&isrc_table_lock); 1300dc425090SMitchell Horne irq_assign_cpu = true; 1301248f0cabSOleksandr Tymoshenko for (i = 0; i < intr_nirq; i++) { 13022b3ad188SAdrian Chadd isrc = irq_sources[i]; 13032b3ad188SAdrian Chadd if (isrc == NULL || isrc->isrc_handlers == 0 || 1304cf55df9fSSvatopluk Kraus isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) 13052b3ad188SAdrian Chadd continue; 13062b3ad188SAdrian Chadd 13072b3ad188SAdrian Chadd if (isrc->isrc_event != NULL && 13082b3ad188SAdrian Chadd isrc->isrc_flags & INTR_ISRCF_BOUND && 13092b3ad188SAdrian Chadd isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1) 13102b3ad188SAdrian Chadd panic("%s: CPU inconsistency", __func__); 13112b3ad188SAdrian Chadd 13122b3ad188SAdrian Chadd if ((isrc->isrc_flags & INTR_ISRCF_BOUND) == 0) 13132b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); /* start again */ 13142b3ad188SAdrian Chadd 13152b3ad188SAdrian Chadd /* 13162b3ad188SAdrian Chadd * We are in wicked position here if the following call fails 13172b3ad188SAdrian Chadd * for bound ISRC. The best thing we can do is to clear 13182b3ad188SAdrian Chadd * isrc_cpu so inconsistency with ie_cpu will be detectable. 13192b3ad188SAdrian Chadd */ 1320bff6be3eSSvatopluk Kraus if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0) 13212b3ad188SAdrian Chadd CPU_ZERO(&isrc->isrc_cpu); 13222b3ad188SAdrian Chadd } 13232b3ad188SAdrian Chadd mtx_unlock(&isrc_table_lock); 13242b3ad188SAdrian Chadd } 13252b3ad188SAdrian Chadd SYSINIT(intr_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, intr_irq_shuffle, NULL); 1326dc425090SMitchell Horne #endif /* !EARLY_AP_STARTUP */ 13272b3ad188SAdrian Chadd 13282b3ad188SAdrian Chadd #else 13292b3ad188SAdrian Chadd u_int 13302b3ad188SAdrian Chadd intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) 13312b3ad188SAdrian Chadd { 13322b3ad188SAdrian Chadd 13332b3ad188SAdrian Chadd return (PCPU_GET(cpuid)); 13342b3ad188SAdrian Chadd } 1335dc425090SMitchell Horne #endif /* SMP */ 13362b3ad188SAdrian Chadd 13373fc155dcSAndrew Turner /* 1338895c8b1cSMichal Meloun * Allocate memory for new intr_map_data structure. 1339895c8b1cSMichal Meloun * Initialize common fields. 1340895c8b1cSMichal Meloun */ 1341895c8b1cSMichal Meloun struct intr_map_data * 1342895c8b1cSMichal Meloun intr_alloc_map_data(enum intr_map_data_type type, size_t len, int flags) 1343895c8b1cSMichal Meloun { 1344895c8b1cSMichal Meloun struct intr_map_data *data; 1345895c8b1cSMichal Meloun 1346895c8b1cSMichal Meloun data = malloc(len, M_INTRNG, flags); 1347895c8b1cSMichal Meloun data->type = type; 1348895c8b1cSMichal Meloun data->len = len; 1349895c8b1cSMichal Meloun return (data); 1350895c8b1cSMichal Meloun } 1351895c8b1cSMichal Meloun 1352895c8b1cSMichal Meloun void intr_free_intr_map_data(struct intr_map_data *data) 1353895c8b1cSMichal Meloun { 1354895c8b1cSMichal Meloun 1355895c8b1cSMichal Meloun free(data, M_INTRNG); 1356895c8b1cSMichal Meloun } 1357895c8b1cSMichal Meloun 1358895c8b1cSMichal Meloun /* 13593fc155dcSAndrew Turner * Register a MSI/MSI-X interrupt controller 13603fc155dcSAndrew Turner */ 13613fc155dcSAndrew Turner int 13623fc155dcSAndrew Turner intr_msi_register(device_t dev, intptr_t xref) 13633fc155dcSAndrew Turner { 13643fc155dcSAndrew Turner struct intr_pic *pic; 13653fc155dcSAndrew Turner 13663fc155dcSAndrew Turner if (dev == NULL) 13673fc155dcSAndrew Turner return (EINVAL); 1368c0d52370SAndrew Turner pic = pic_create(dev, xref, FLAG_MSI); 13693fc155dcSAndrew Turner if (pic == NULL) 13703fc155dcSAndrew Turner return (ENOMEM); 13713fc155dcSAndrew Turner 13723fc155dcSAndrew Turner debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic, 13733fc155dcSAndrew Turner device_get_nameunit(dev), dev, (uintmax_t)xref); 13743fc155dcSAndrew Turner return (0); 13753fc155dcSAndrew Turner } 13763fc155dcSAndrew Turner 13773fc155dcSAndrew Turner int 13783fc155dcSAndrew Turner intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count, 13793fc155dcSAndrew Turner int maxcount, int *irqs) 13803fc155dcSAndrew Turner { 1381e707c8beSRuslan Bukin struct iommu_domain *domain; 13823fc155dcSAndrew Turner struct intr_irqsrc **isrc; 13833fc155dcSAndrew Turner struct intr_pic *pic; 13843fc155dcSAndrew Turner device_t pdev; 1385895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 13863fc155dcSAndrew Turner int err, i; 13873fc155dcSAndrew Turner 1388c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 13893fc155dcSAndrew Turner if (pic == NULL) 13903fc155dcSAndrew Turner return (ESRCH); 13913fc155dcSAndrew Turner 1392c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 13933fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 13943fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 13953fc155dcSAndrew Turner 1396e707c8beSRuslan Bukin /* 1397e707c8beSRuslan Bukin * If this is the first time we have used this context ask the 1398e707c8beSRuslan Bukin * interrupt controller to map memory the msi source will need. 1399e707c8beSRuslan Bukin */ 1400e707c8beSRuslan Bukin err = MSI_IOMMU_INIT(pic->pic_dev, child, &domain); 1401e707c8beSRuslan Bukin if (err != 0) 1402e707c8beSRuslan Bukin return (err); 1403e707c8beSRuslan Bukin 14043fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 14053fc155dcSAndrew Turner err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc); 1406895c8b1cSMichal Meloun if (err != 0) { 1407895c8b1cSMichal Meloun free(isrc, M_INTRNG); 1408895c8b1cSMichal Meloun return (err); 14093fc155dcSAndrew Turner } 14103fc155dcSAndrew Turner 1411895c8b1cSMichal Meloun for (i = 0; i < count; i++) { 1412e707c8beSRuslan Bukin isrc[i]->isrc_iommu = domain; 1413895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)intr_alloc_map_data( 1414895c8b1cSMichal Meloun INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO); 1415895c8b1cSMichal Meloun msi-> isrc = isrc[i]; 1416e707c8beSRuslan Bukin 1417895c8b1cSMichal Meloun irqs[i] = intr_map_irq(pic->pic_dev, xref, 1418895c8b1cSMichal Meloun (struct intr_map_data *)msi); 1419895c8b1cSMichal Meloun } 14203fc155dcSAndrew Turner free(isrc, M_INTRNG); 14213fc155dcSAndrew Turner 14223fc155dcSAndrew Turner return (err); 14233fc155dcSAndrew Turner } 14243fc155dcSAndrew Turner 14253fc155dcSAndrew Turner int 14263fc155dcSAndrew Turner intr_release_msi(device_t pci, device_t child, intptr_t xref, int count, 14273fc155dcSAndrew Turner int *irqs) 14283fc155dcSAndrew Turner { 14293fc155dcSAndrew Turner struct intr_irqsrc **isrc; 14303fc155dcSAndrew Turner struct intr_pic *pic; 1431609b0fe9SOleksandr Tymoshenko struct intr_map_data_msi *msi; 14323fc155dcSAndrew Turner int i, err; 14333fc155dcSAndrew Turner 1434c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 14353fc155dcSAndrew Turner if (pic == NULL) 14363fc155dcSAndrew Turner return (ESRCH); 14373fc155dcSAndrew Turner 1438c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 14393fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14403fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14413fc155dcSAndrew Turner 14423fc155dcSAndrew Turner isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); 14433fc155dcSAndrew Turner 1444609b0fe9SOleksandr Tymoshenko for (i = 0; i < count; i++) { 1445609b0fe9SOleksandr Tymoshenko msi = (struct intr_map_data_msi *) 1446609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(irqs[i]); 1447609b0fe9SOleksandr Tymoshenko KASSERT(msi->hdr.type == INTR_MAP_DATA_MSI, 1448609b0fe9SOleksandr Tymoshenko ("%s: irq %d map data is not MSI", __func__, 1449609b0fe9SOleksandr Tymoshenko irqs[i])); 1450609b0fe9SOleksandr Tymoshenko isrc[i] = msi->isrc; 1451609b0fe9SOleksandr Tymoshenko } 14523fc155dcSAndrew Turner 1453f32f0095SRuslan Bukin MSI_IOMMU_DEINIT(pic->pic_dev, child); 1454f32f0095SRuslan Bukin 14553fc155dcSAndrew Turner err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc); 1456895c8b1cSMichal Meloun 1457895c8b1cSMichal Meloun for (i = 0; i < count; i++) { 1458895c8b1cSMichal Meloun if (isrc[i] != NULL) 1459895c8b1cSMichal Meloun intr_unmap_irq(irqs[i]); 1460895c8b1cSMichal Meloun } 1461895c8b1cSMichal Meloun 14623fc155dcSAndrew Turner free(isrc, M_INTRNG); 14633fc155dcSAndrew Turner return (err); 14643fc155dcSAndrew Turner } 14653fc155dcSAndrew Turner 14663fc155dcSAndrew Turner int 14673fc155dcSAndrew Turner intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq) 14683fc155dcSAndrew Turner { 1469e707c8beSRuslan Bukin struct iommu_domain *domain; 14703fc155dcSAndrew Turner struct intr_irqsrc *isrc; 14713fc155dcSAndrew Turner struct intr_pic *pic; 14723fc155dcSAndrew Turner device_t pdev; 1473895c8b1cSMichal Meloun struct intr_map_data_msi *msi; 14743fc155dcSAndrew Turner int err; 14753fc155dcSAndrew Turner 1476c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 14773fc155dcSAndrew Turner if (pic == NULL) 14783fc155dcSAndrew Turner return (ESRCH); 14793fc155dcSAndrew Turner 1480c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 14813fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 14823fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 14833fc155dcSAndrew Turner 1484e707c8beSRuslan Bukin /* 1485e707c8beSRuslan Bukin * If this is the first time we have used this context ask the 1486e707c8beSRuslan Bukin * interrupt controller to map memory the msi source will need. 1487e707c8beSRuslan Bukin */ 1488e707c8beSRuslan Bukin err = MSI_IOMMU_INIT(pic->pic_dev, child, &domain); 1489e707c8beSRuslan Bukin if (err != 0) 1490e707c8beSRuslan Bukin return (err); 1491e707c8beSRuslan Bukin 14923fc155dcSAndrew Turner err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc); 14933fc155dcSAndrew Turner if (err != 0) 14943fc155dcSAndrew Turner return (err); 14953fc155dcSAndrew Turner 1496e707c8beSRuslan Bukin isrc->isrc_iommu = domain; 1497895c8b1cSMichal Meloun msi = (struct intr_map_data_msi *)intr_alloc_map_data( 1498895c8b1cSMichal Meloun INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO); 1499895c8b1cSMichal Meloun msi->isrc = isrc; 1500895c8b1cSMichal Meloun *irq = intr_map_irq(pic->pic_dev, xref, (struct intr_map_data *)msi); 15013fc155dcSAndrew Turner return (0); 15023fc155dcSAndrew Turner } 15033fc155dcSAndrew Turner 15043fc155dcSAndrew Turner int 15053fc155dcSAndrew Turner intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq) 15063fc155dcSAndrew Turner { 15073fc155dcSAndrew Turner struct intr_irqsrc *isrc; 15083fc155dcSAndrew Turner struct intr_pic *pic; 1509609b0fe9SOleksandr Tymoshenko struct intr_map_data_msi *msi; 15103fc155dcSAndrew Turner int err; 15113fc155dcSAndrew Turner 1512c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 15133fc155dcSAndrew Turner if (pic == NULL) 15143fc155dcSAndrew Turner return (ESRCH); 15153fc155dcSAndrew Turner 1516c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 15173fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 15183fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 15193fc155dcSAndrew Turner 1520609b0fe9SOleksandr Tymoshenko msi = (struct intr_map_data_msi *) 1521609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(irq); 1522609b0fe9SOleksandr Tymoshenko KASSERT(msi->hdr.type == INTR_MAP_DATA_MSI, 1523609b0fe9SOleksandr Tymoshenko ("%s: irq %d map data is not MSI", __func__, 1524609b0fe9SOleksandr Tymoshenko irq)); 1525609b0fe9SOleksandr Tymoshenko isrc = msi->isrc; 1526895c8b1cSMichal Meloun if (isrc == NULL) { 1527895c8b1cSMichal Meloun intr_unmap_irq(irq); 15283fc155dcSAndrew Turner return (EINVAL); 1529895c8b1cSMichal Meloun } 15303fc155dcSAndrew Turner 1531f32f0095SRuslan Bukin MSI_IOMMU_DEINIT(pic->pic_dev, child); 1532f32f0095SRuslan Bukin 15333fc155dcSAndrew Turner err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc); 1534895c8b1cSMichal Meloun intr_unmap_irq(irq); 1535895c8b1cSMichal Meloun 15363fc155dcSAndrew Turner return (err); 15373fc155dcSAndrew Turner } 15383fc155dcSAndrew Turner 15393fc155dcSAndrew Turner int 15403fc155dcSAndrew Turner intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq, 15413fc155dcSAndrew Turner uint64_t *addr, uint32_t *data) 15423fc155dcSAndrew Turner { 15433fc155dcSAndrew Turner struct intr_irqsrc *isrc; 15443fc155dcSAndrew Turner struct intr_pic *pic; 15453fc155dcSAndrew Turner int err; 15463fc155dcSAndrew Turner 1547c0d52370SAndrew Turner pic = pic_lookup(NULL, xref, FLAG_MSI); 15483fc155dcSAndrew Turner if (pic == NULL) 15493fc155dcSAndrew Turner return (ESRCH); 15503fc155dcSAndrew Turner 1551c0d52370SAndrew Turner KASSERT((pic->pic_flags & FLAG_TYPE_MASK) == FLAG_MSI, 15523fc155dcSAndrew Turner ("%s: Found a non-MSI controller: %s", __func__, 15533fc155dcSAndrew Turner device_get_name(pic->pic_dev))); 15543fc155dcSAndrew Turner 1555895c8b1cSMichal Meloun isrc = intr_map_get_isrc(irq); 15563fc155dcSAndrew Turner if (isrc == NULL) 15573fc155dcSAndrew Turner return (EINVAL); 15583fc155dcSAndrew Turner 15593fc155dcSAndrew Turner err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data); 1560e707c8beSRuslan Bukin 1561e707c8beSRuslan Bukin #ifdef IOMMU 1562e707c8beSRuslan Bukin if (isrc->isrc_iommu != NULL) 1563e707c8beSRuslan Bukin iommu_translate_msi(isrc->isrc_iommu, addr); 1564e707c8beSRuslan Bukin #endif 1565e707c8beSRuslan Bukin 15663fc155dcSAndrew Turner return (err); 15673fc155dcSAndrew Turner } 15683fc155dcSAndrew Turner 15692b3ad188SAdrian Chadd void dosoftints(void); 15702b3ad188SAdrian Chadd void 15712b3ad188SAdrian Chadd dosoftints(void) 15722b3ad188SAdrian Chadd { 15732b3ad188SAdrian Chadd } 15742b3ad188SAdrian Chadd 15752b3ad188SAdrian Chadd #ifdef SMP 15762b3ad188SAdrian Chadd /* 15772b3ad188SAdrian Chadd * Init interrupt controller on another CPU. 15782b3ad188SAdrian Chadd */ 15792b3ad188SAdrian Chadd void 15802b3ad188SAdrian Chadd intr_pic_init_secondary(void) 15812b3ad188SAdrian Chadd { 158285918bebSAyrton Munoz device_t dev; 1583*4b01a7faSKyle Evans uint32_t rootnum; 15842b3ad188SAdrian Chadd 15852b3ad188SAdrian Chadd /* 158685918bebSAyrton Munoz * QQQ: Only root PICs are aware of other CPUs ??? 15872b3ad188SAdrian Chadd */ 15882b3ad188SAdrian Chadd //mtx_lock(&isrc_table_lock); 1589*4b01a7faSKyle Evans for (rootnum = 0; rootnum < INTR_ROOT_NUM; rootnum++) { 1590*4b01a7faSKyle Evans dev = intr_irq_roots[rootnum].dev; 159185918bebSAyrton Munoz if (dev != NULL) { 1592*4b01a7faSKyle Evans PIC_INIT_SECONDARY(dev, rootnum); 159385918bebSAyrton Munoz } 159485918bebSAyrton Munoz } 15952b3ad188SAdrian Chadd //mtx_unlock(&isrc_table_lock); 15962b3ad188SAdrian Chadd } 15972b3ad188SAdrian Chadd #endif 15982b3ad188SAdrian Chadd 15992b3ad188SAdrian Chadd #ifdef DDB 1600c84c5e00SMitchell Horne DB_SHOW_COMMAND_FLAGS(irqs, db_show_irqs, DB_CMD_MEMSAFE) 16012b3ad188SAdrian Chadd { 16022b3ad188SAdrian Chadd u_int i, irqsum; 1603bff6be3eSSvatopluk Kraus u_long num; 16042b3ad188SAdrian Chadd struct intr_irqsrc *isrc; 16052b3ad188SAdrian Chadd 1606248f0cabSOleksandr Tymoshenko for (irqsum = 0, i = 0; i < intr_nirq; i++) { 16072b3ad188SAdrian Chadd isrc = irq_sources[i]; 16082b3ad188SAdrian Chadd if (isrc == NULL) 16092b3ad188SAdrian Chadd continue; 16102b3ad188SAdrian Chadd 1611bff6be3eSSvatopluk Kraus num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; 16122b3ad188SAdrian Chadd db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, 16132b3ad188SAdrian Chadd isrc->isrc_name, isrc->isrc_cpu.__bits[0], 1614bff6be3eSSvatopluk Kraus isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); 1615bff6be3eSSvatopluk Kraus irqsum += num; 16162b3ad188SAdrian Chadd } 16172b3ad188SAdrian Chadd db_printf("irq total %u\n", irqsum); 16182b3ad188SAdrian Chadd } 16192b3ad188SAdrian Chadd #endif 1620895c8b1cSMichal Meloun 1621895c8b1cSMichal Meloun /* 1622895c8b1cSMichal Meloun * Interrupt mapping table functions. 1623895c8b1cSMichal Meloun * 1624895c8b1cSMichal Meloun * Please, keep this part separately, it can be transformed to 1625895c8b1cSMichal Meloun * extension of standard resources. 1626895c8b1cSMichal Meloun */ 1627895c8b1cSMichal Meloun struct intr_map_entry 1628895c8b1cSMichal Meloun { 1629895c8b1cSMichal Meloun device_t dev; 1630895c8b1cSMichal Meloun intptr_t xref; 1631895c8b1cSMichal Meloun struct intr_map_data *map_data; 1632895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 1633895c8b1cSMichal Meloun /* XXX TODO DISCONECTED PICs */ 1634895c8b1cSMichal Meloun /*int flags */ 1635895c8b1cSMichal Meloun }; 1636895c8b1cSMichal Meloun 1637895c8b1cSMichal Meloun /* XXX Convert irq_map[] to dynamicaly expandable one. */ 1638248f0cabSOleksandr Tymoshenko static struct intr_map_entry **irq_map; 1639a3c7da3dSElliott Mitchell static u_int irq_map_count; 1640a3c7da3dSElliott Mitchell static u_int irq_map_first_free_idx; 1641895c8b1cSMichal Meloun static struct mtx irq_map_lock; 1642895c8b1cSMichal Meloun 1643895c8b1cSMichal Meloun static struct intr_irqsrc * 1644895c8b1cSMichal Meloun intr_map_get_isrc(u_int res_id) 1645895c8b1cSMichal Meloun { 1646895c8b1cSMichal Meloun struct intr_irqsrc *isrc; 1647895c8b1cSMichal Meloun 1648ecc8ccb4SAndrew Turner isrc = NULL; 1649895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1650ecc8ccb4SAndrew Turner if (res_id < irq_map_count && irq_map[res_id] != NULL) 1651895c8b1cSMichal Meloun isrc = irq_map[res_id]->isrc; 1652895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1653ecc8ccb4SAndrew Turner 1654895c8b1cSMichal Meloun return (isrc); 1655895c8b1cSMichal Meloun } 1656895c8b1cSMichal Meloun 1657895c8b1cSMichal Meloun static void 1658895c8b1cSMichal Meloun intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc) 1659895c8b1cSMichal Meloun { 1660895c8b1cSMichal Meloun 1661895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1662ecc8ccb4SAndrew Turner if (res_id < irq_map_count && irq_map[res_id] != NULL) 1663895c8b1cSMichal Meloun irq_map[res_id]->isrc = isrc; 1664895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1665895c8b1cSMichal Meloun } 1666895c8b1cSMichal Meloun 1667895c8b1cSMichal Meloun /* 1668895c8b1cSMichal Meloun * Get a copy of intr_map_entry data 1669895c8b1cSMichal Meloun */ 1670609b0fe9SOleksandr Tymoshenko static struct intr_map_data * 1671609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(u_int res_id) 1672609b0fe9SOleksandr Tymoshenko { 1673609b0fe9SOleksandr Tymoshenko struct intr_map_data *data; 1674609b0fe9SOleksandr Tymoshenko 1675609b0fe9SOleksandr Tymoshenko data = NULL; 1676609b0fe9SOleksandr Tymoshenko mtx_lock(&irq_map_lock); 1677609b0fe9SOleksandr Tymoshenko if (res_id >= irq_map_count || irq_map[res_id] == NULL) 1678609b0fe9SOleksandr Tymoshenko panic("Attempt to copy invalid resource id: %u\n", res_id); 1679609b0fe9SOleksandr Tymoshenko data = irq_map[res_id]->map_data; 1680609b0fe9SOleksandr Tymoshenko mtx_unlock(&irq_map_lock); 1681609b0fe9SOleksandr Tymoshenko 1682609b0fe9SOleksandr Tymoshenko return (data); 1683609b0fe9SOleksandr Tymoshenko } 1684609b0fe9SOleksandr Tymoshenko 1685609b0fe9SOleksandr Tymoshenko /* 1686609b0fe9SOleksandr Tymoshenko * Get a copy of intr_map_entry data 1687609b0fe9SOleksandr Tymoshenko */ 1688895c8b1cSMichal Meloun static void 1689895c8b1cSMichal Meloun intr_map_copy_map_data(u_int res_id, device_t *map_dev, intptr_t *map_xref, 1690895c8b1cSMichal Meloun struct intr_map_data **data) 1691895c8b1cSMichal Meloun { 1692895c8b1cSMichal Meloun size_t len; 1693895c8b1cSMichal Meloun 1694895c8b1cSMichal Meloun len = 0; 1695895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1696895c8b1cSMichal Meloun if (res_id >= irq_map_count || irq_map[res_id] == NULL) 1697895c8b1cSMichal Meloun panic("Attempt to copy invalid resource id: %u\n", res_id); 1698895c8b1cSMichal Meloun if (irq_map[res_id]->map_data != NULL) 1699895c8b1cSMichal Meloun len = irq_map[res_id]->map_data->len; 1700895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1701895c8b1cSMichal Meloun 1702895c8b1cSMichal Meloun if (len == 0) 1703895c8b1cSMichal Meloun *data = NULL; 1704895c8b1cSMichal Meloun else 1705895c8b1cSMichal Meloun *data = malloc(len, M_INTRNG, M_WAITOK | M_ZERO); 1706895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1707895c8b1cSMichal Meloun if (irq_map[res_id] == NULL) 1708895c8b1cSMichal Meloun panic("Attempt to copy invalid resource id: %u\n", res_id); 1709895c8b1cSMichal Meloun if (len != 0) { 1710895c8b1cSMichal Meloun if (len != irq_map[res_id]->map_data->len) 1711895c8b1cSMichal Meloun panic("Resource id: %u has changed.\n", res_id); 1712895c8b1cSMichal Meloun memcpy(*data, irq_map[res_id]->map_data, len); 1713895c8b1cSMichal Meloun } 1714895c8b1cSMichal Meloun *map_dev = irq_map[res_id]->dev; 1715895c8b1cSMichal Meloun *map_xref = irq_map[res_id]->xref; 1716895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1717895c8b1cSMichal Meloun } 1718895c8b1cSMichal Meloun 1719895c8b1cSMichal Meloun /* 1720895c8b1cSMichal Meloun * Allocate and fill new entry in irq_map table. 1721895c8b1cSMichal Meloun */ 1722895c8b1cSMichal Meloun u_int 1723895c8b1cSMichal Meloun intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data) 1724895c8b1cSMichal Meloun { 1725895c8b1cSMichal Meloun u_int i; 1726895c8b1cSMichal Meloun struct intr_map_entry *entry; 1727895c8b1cSMichal Meloun 1728895c8b1cSMichal Meloun /* Prepare new entry first. */ 1729895c8b1cSMichal Meloun entry = malloc(sizeof(*entry), M_INTRNG, M_WAITOK | M_ZERO); 1730895c8b1cSMichal Meloun 1731895c8b1cSMichal Meloun entry->dev = dev; 1732895c8b1cSMichal Meloun entry->xref = xref; 1733895c8b1cSMichal Meloun entry->map_data = data; 1734895c8b1cSMichal Meloun entry->isrc = NULL; 1735895c8b1cSMichal Meloun 1736895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1737895c8b1cSMichal Meloun for (i = irq_map_first_free_idx; i < irq_map_count; i++) { 1738895c8b1cSMichal Meloun if (irq_map[i] == NULL) { 1739895c8b1cSMichal Meloun irq_map[i] = entry; 1740895c8b1cSMichal Meloun irq_map_first_free_idx = i + 1; 1741895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1742895c8b1cSMichal Meloun return (i); 1743895c8b1cSMichal Meloun } 1744895c8b1cSMichal Meloun } 17459beb195fSAndrew Turner for (i = 0; i < irq_map_first_free_idx; i++) { 17469beb195fSAndrew Turner if (irq_map[i] == NULL) { 17479beb195fSAndrew Turner irq_map[i] = entry; 17489beb195fSAndrew Turner irq_map_first_free_idx = i + 1; 17499beb195fSAndrew Turner mtx_unlock(&irq_map_lock); 17509beb195fSAndrew Turner return (i); 17519beb195fSAndrew Turner } 17529beb195fSAndrew Turner } 1753895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1754895c8b1cSMichal Meloun 1755895c8b1cSMichal Meloun /* XXX Expand irq_map table */ 1756895c8b1cSMichal Meloun panic("IRQ mapping table is full."); 1757895c8b1cSMichal Meloun } 1758895c8b1cSMichal Meloun 1759895c8b1cSMichal Meloun /* 1760895c8b1cSMichal Meloun * Remove and free mapping entry. 1761895c8b1cSMichal Meloun */ 1762895c8b1cSMichal Meloun void 1763895c8b1cSMichal Meloun intr_unmap_irq(u_int res_id) 1764895c8b1cSMichal Meloun { 1765895c8b1cSMichal Meloun struct intr_map_entry *entry; 1766895c8b1cSMichal Meloun 1767895c8b1cSMichal Meloun mtx_lock(&irq_map_lock); 1768895c8b1cSMichal Meloun if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL)) 1769895c8b1cSMichal Meloun panic("Attempt to unmap invalid resource id: %u\n", res_id); 1770895c8b1cSMichal Meloun entry = irq_map[res_id]; 1771895c8b1cSMichal Meloun irq_map[res_id] = NULL; 1772895c8b1cSMichal Meloun irq_map_first_free_idx = res_id; 1773895c8b1cSMichal Meloun mtx_unlock(&irq_map_lock); 1774895c8b1cSMichal Meloun intr_free_intr_map_data(entry->map_data); 1775895c8b1cSMichal Meloun free(entry, M_INTRNG); 1776895c8b1cSMichal Meloun } 1777895c8b1cSMichal Meloun 1778895c8b1cSMichal Meloun /* 1779895c8b1cSMichal Meloun * Clone mapping entry. 1780895c8b1cSMichal Meloun */ 1781895c8b1cSMichal Meloun u_int 1782895c8b1cSMichal Meloun intr_map_clone_irq(u_int old_res_id) 1783895c8b1cSMichal Meloun { 1784895c8b1cSMichal Meloun device_t map_dev; 1785895c8b1cSMichal Meloun intptr_t map_xref; 1786895c8b1cSMichal Meloun struct intr_map_data *data; 1787895c8b1cSMichal Meloun 1788895c8b1cSMichal Meloun intr_map_copy_map_data(old_res_id, &map_dev, &map_xref, &data); 1789895c8b1cSMichal Meloun return (intr_map_irq(map_dev, map_xref, data)); 1790895c8b1cSMichal Meloun } 1791895c8b1cSMichal Meloun 1792895c8b1cSMichal Meloun static void 1793895c8b1cSMichal Meloun intr_map_init(void *dummy __unused) 1794895c8b1cSMichal Meloun { 1795895c8b1cSMichal Meloun 1796895c8b1cSMichal Meloun mtx_init(&irq_map_lock, "intr map table", NULL, MTX_DEF); 1797248f0cabSOleksandr Tymoshenko 1798248f0cabSOleksandr Tymoshenko irq_map_count = 2 * intr_nirq; 1799248f0cabSOleksandr Tymoshenko irq_map = mallocarray(irq_map_count, sizeof(struct intr_map_entry*), 1800248f0cabSOleksandr Tymoshenko M_INTRNG, M_WAITOK | M_ZERO); 1801895c8b1cSMichal Meloun } 1802895c8b1cSMichal Meloun SYSINIT(intr_map_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_map_init, NULL); 1803fae8755fSJessica Clarke 1804fae8755fSJessica Clarke #ifdef SMP 1805fae8755fSJessica Clarke /* Virtualization for interrupt source IPI counter increment. */ 1806fae8755fSJessica Clarke static inline void 1807fae8755fSJessica Clarke intr_ipi_increment_count(u_long *counter, u_int cpu) 1808fae8755fSJessica Clarke { 1809fae8755fSJessica Clarke 1810fae8755fSJessica Clarke KASSERT(cpu < mp_maxid + 1, ("%s: too big cpu %u", __func__, cpu)); 1811fae8755fSJessica Clarke counter[cpu]++; 1812fae8755fSJessica Clarke } 1813fae8755fSJessica Clarke 1814fae8755fSJessica Clarke /* 1815fae8755fSJessica Clarke * Virtualization for interrupt source IPI counters setup. 1816fae8755fSJessica Clarke */ 1817fae8755fSJessica Clarke static u_long * 1818fae8755fSJessica Clarke intr_ipi_setup_counters(const char *name) 1819fae8755fSJessica Clarke { 1820fae8755fSJessica Clarke u_int index, i; 1821fae8755fSJessica Clarke char str[INTRNAME_LEN]; 1822fae8755fSJessica Clarke 1823fae8755fSJessica Clarke mtx_lock(&isrc_table_lock); 1824fae8755fSJessica Clarke 1825fae8755fSJessica Clarke /* 1826fae8755fSJessica Clarke * We should never have a problem finding mp_maxid + 1 contiguous 1827fae8755fSJessica Clarke * counters, in practice. Interrupts will be allocated sequentially 1828fae8755fSJessica Clarke * during boot, so the array should fill from low to high index. Once 1829fae8755fSJessica Clarke * reserved, the IPI counters will never be released. Similarly, we 1830fae8755fSJessica Clarke * will not need to allocate more IPIs once the system is running. 1831fae8755fSJessica Clarke */ 1832fae8755fSJessica Clarke bit_ffc_area(intrcnt_bitmap, nintrcnt, mp_maxid + 1, &index); 1833fae8755fSJessica Clarke if (index == -1) 1834fae8755fSJessica Clarke panic("Failed to allocate %d counters. Array exhausted?", 1835fae8755fSJessica Clarke mp_maxid + 1); 1836fae8755fSJessica Clarke bit_nset(intrcnt_bitmap, index, index + mp_maxid); 1837fae8755fSJessica Clarke for (i = 0; i < mp_maxid + 1; i++) { 1838fae8755fSJessica Clarke snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); 1839fae8755fSJessica Clarke intrcnt_setname(str, index + i); 1840fae8755fSJessica Clarke } 1841fae8755fSJessica Clarke mtx_unlock(&isrc_table_lock); 1842fae8755fSJessica Clarke return (&intrcnt[index]); 1843fae8755fSJessica Clarke } 1844fae8755fSJessica Clarke 1845fae8755fSJessica Clarke /* 1846fae8755fSJessica Clarke * Lookup IPI source. 1847fae8755fSJessica Clarke */ 1848fae8755fSJessica Clarke static struct intr_ipi * 1849fae8755fSJessica Clarke intr_ipi_lookup(u_int ipi) 1850fae8755fSJessica Clarke { 1851fae8755fSJessica Clarke 1852fae8755fSJessica Clarke if (ipi >= INTR_IPI_COUNT) 1853fae8755fSJessica Clarke panic("%s: no such IPI %u", __func__, ipi); 1854fae8755fSJessica Clarke 1855fae8755fSJessica Clarke return (&ipi_sources[ipi]); 1856fae8755fSJessica Clarke } 1857fae8755fSJessica Clarke 1858103d39efSJessica Clarke int 1859103d39efSJessica Clarke intr_ipi_pic_register(device_t dev, u_int priority) 1860103d39efSJessica Clarke { 1861103d39efSJessica Clarke if (intr_ipi_dev_frozen) { 1862103d39efSJessica Clarke device_printf(dev, "IPI device already frozen"); 1863103d39efSJessica Clarke return (EBUSY); 1864103d39efSJessica Clarke } 1865103d39efSJessica Clarke 18663b03e1bbSAndrew Turner if (intr_ipi_dev == NULL || priority > intr_ipi_dev_priority) { 18673b03e1bbSAndrew Turner intr_ipi_dev_priority = priority; 1868103d39efSJessica Clarke intr_ipi_dev = dev; 18693b03e1bbSAndrew Turner } 1870103d39efSJessica Clarke 1871103d39efSJessica Clarke return (0); 1872103d39efSJessica Clarke } 1873103d39efSJessica Clarke 1874fae8755fSJessica Clarke /* 1875fae8755fSJessica Clarke * Setup IPI handler on interrupt controller. 1876fae8755fSJessica Clarke * 1877fae8755fSJessica Clarke * Not SMP coherent. 1878fae8755fSJessica Clarke */ 1879fae8755fSJessica Clarke void 1880fae8755fSJessica Clarke intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand, 1881fae8755fSJessica Clarke void *arg) 1882fae8755fSJessica Clarke { 1883fae8755fSJessica Clarke struct intr_irqsrc *isrc; 1884fae8755fSJessica Clarke struct intr_ipi *ii; 1885fae8755fSJessica Clarke int error; 1886fae8755fSJessica Clarke 1887103d39efSJessica Clarke if (!intr_ipi_dev_frozen) { 1888103d39efSJessica Clarke if (intr_ipi_dev == NULL) 1889103d39efSJessica Clarke panic("%s: no IPI PIC attached", __func__); 1890103d39efSJessica Clarke 1891103d39efSJessica Clarke intr_ipi_dev_frozen = true; 1892103d39efSJessica Clarke device_printf(intr_ipi_dev, "using for IPIs\n"); 1893103d39efSJessica Clarke } 1894103d39efSJessica Clarke 1895fae8755fSJessica Clarke KASSERT(hand != NULL, ("%s: ipi %u no handler", __func__, ipi)); 1896fae8755fSJessica Clarke 1897103d39efSJessica Clarke error = PIC_IPI_SETUP(intr_ipi_dev, ipi, &isrc); 1898fae8755fSJessica Clarke if (error != 0) 1899fae8755fSJessica Clarke return; 1900fae8755fSJessica Clarke 1901fae8755fSJessica Clarke isrc->isrc_handlers++; 1902fae8755fSJessica Clarke 1903fae8755fSJessica Clarke ii = intr_ipi_lookup(ipi); 1904fae8755fSJessica Clarke KASSERT(ii->ii_count == NULL, ("%s: ipi %u reused", __func__, ipi)); 1905fae8755fSJessica Clarke 1906fae8755fSJessica Clarke ii->ii_handler = hand; 1907fae8755fSJessica Clarke ii->ii_handler_arg = arg; 1908fae8755fSJessica Clarke ii->ii_isrc = isrc; 1909fae8755fSJessica Clarke strlcpy(ii->ii_name, name, INTR_IPI_NAMELEN); 1910fae8755fSJessica Clarke ii->ii_count = intr_ipi_setup_counters(name); 1911fae8755fSJessica Clarke 1912103d39efSJessica Clarke PIC_ENABLE_INTR(intr_ipi_dev, isrc); 1913fae8755fSJessica Clarke } 1914fae8755fSJessica Clarke 1915fae8755fSJessica Clarke void 1916fae8755fSJessica Clarke intr_ipi_send(cpuset_t cpus, u_int ipi) 1917fae8755fSJessica Clarke { 1918fae8755fSJessica Clarke struct intr_ipi *ii; 1919fae8755fSJessica Clarke 1920103d39efSJessica Clarke KASSERT(intr_ipi_dev_frozen, 1921103d39efSJessica Clarke ("%s: IPI device not yet frozen", __func__)); 1922fae8755fSJessica Clarke 1923fae8755fSJessica Clarke ii = intr_ipi_lookup(ipi); 1924fae8755fSJessica Clarke if (ii->ii_count == NULL) 1925fae8755fSJessica Clarke panic("%s: not setup IPI %u", __func__, ipi); 1926fae8755fSJessica Clarke 1927fae8755fSJessica Clarke /* 1928fae8755fSJessica Clarke * XXX: Surely needed on other architectures too? Either way should be 1929fae8755fSJessica Clarke * some kind of MI hook defined in an MD header, or the responsibility 1930fae8755fSJessica Clarke * of the MD caller if not widespread. 1931fae8755fSJessica Clarke */ 1932fae8755fSJessica Clarke #ifdef __aarch64__ 1933fae8755fSJessica Clarke /* 1934fae8755fSJessica Clarke * Ensure that this CPU's stores will be visible to IPI 1935fae8755fSJessica Clarke * recipients before starting to send the interrupts. 1936fae8755fSJessica Clarke */ 1937fae8755fSJessica Clarke dsb(ishst); 1938fae8755fSJessica Clarke #endif 1939fae8755fSJessica Clarke 1940103d39efSJessica Clarke PIC_IPI_SEND(intr_ipi_dev, ii->ii_isrc, cpus, ipi); 1941fae8755fSJessica Clarke } 1942fae8755fSJessica Clarke 1943fae8755fSJessica Clarke /* 1944fae8755fSJessica Clarke * interrupt controller dispatch function for IPIs. It should 1945fae8755fSJessica Clarke * be called straight from the interrupt controller, when associated 1946fae8755fSJessica Clarke * interrupt source is learned. Or from anybody who has an interrupt 1947fae8755fSJessica Clarke * source mapped. 1948fae8755fSJessica Clarke */ 1949fae8755fSJessica Clarke void 1950fae8755fSJessica Clarke intr_ipi_dispatch(u_int ipi) 1951fae8755fSJessica Clarke { 1952fae8755fSJessica Clarke struct intr_ipi *ii; 1953fae8755fSJessica Clarke 1954fae8755fSJessica Clarke ii = intr_ipi_lookup(ipi); 1955fae8755fSJessica Clarke if (ii->ii_count == NULL) 1956fae8755fSJessica Clarke panic("%s: not setup IPI %u", __func__, ipi); 1957fae8755fSJessica Clarke 1958fae8755fSJessica Clarke intr_ipi_increment_count(ii->ii_count, PCPU_GET(cpuid)); 1959fae8755fSJessica Clarke 1960fae8755fSJessica Clarke ii->ii_handler(ii->ii_handler_arg); 1961fae8755fSJessica Clarke } 1962fae8755fSJessica Clarke #endif 1963