xref: /freebsd/sys/kern/subr_intr.c (revision 609b0fe96628b3af8e57a13ae431fe284a5a1974)
12b3ad188SAdrian Chadd /*-
2bff6be3eSSvatopluk Kraus  * Copyright (c) 2015-2016 Svatopluk Kraus
3bff6be3eSSvatopluk Kraus  * Copyright (c) 2015-2016 Michal Meloun
42b3ad188SAdrian Chadd  * All rights reserved.
52b3ad188SAdrian Chadd  *
62b3ad188SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
72b3ad188SAdrian Chadd  * modification, are permitted provided that the following conditions
82b3ad188SAdrian Chadd  * are met:
92b3ad188SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
102b3ad188SAdrian Chadd  *    notice, this list of conditions and the following disclaimer.
112b3ad188SAdrian Chadd  * 2. Redistributions in binary form must reproduce the above copyright
122b3ad188SAdrian Chadd  *    notice, this list of conditions and the following disclaimer in the
132b3ad188SAdrian Chadd  *    documentation and/or other materials provided with the distribution.
142b3ad188SAdrian Chadd  *
152b3ad188SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162b3ad188SAdrian Chadd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172b3ad188SAdrian Chadd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182b3ad188SAdrian Chadd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192b3ad188SAdrian Chadd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202b3ad188SAdrian Chadd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212b3ad188SAdrian Chadd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222b3ad188SAdrian Chadd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232b3ad188SAdrian Chadd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
242b3ad188SAdrian Chadd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252b3ad188SAdrian Chadd  * SUCH DAMAGE.
262b3ad188SAdrian Chadd  */
272b3ad188SAdrian Chadd 
282b3ad188SAdrian Chadd #include <sys/cdefs.h>
292b3ad188SAdrian Chadd __FBSDID("$FreeBSD$");
302b3ad188SAdrian Chadd 
312b3ad188SAdrian Chadd /*
322b3ad188SAdrian Chadd  *	New-style Interrupt Framework
332b3ad188SAdrian Chadd  *
34895c8b1cSMichal Meloun  *  TODO: - add support for disconnected PICs.
35895c8b1cSMichal Meloun  *        - to support IPI (PPI) enabling on other CPUs if already started.
36895c8b1cSMichal Meloun  *        - to complete things for removable PICs.
372b3ad188SAdrian Chadd  */
382b3ad188SAdrian Chadd 
392b3ad188SAdrian Chadd #include "opt_ddb.h"
40df7a2251SAndrew Turner #include "opt_hwpmc_hooks.h"
412b3ad188SAdrian Chadd 
422b3ad188SAdrian Chadd #include <sys/param.h>
432b3ad188SAdrian Chadd #include <sys/systm.h>
442b3ad188SAdrian Chadd #include <sys/kernel.h>
452b3ad188SAdrian Chadd #include <sys/syslog.h>
462b3ad188SAdrian Chadd #include <sys/malloc.h>
472b3ad188SAdrian Chadd #include <sys/proc.h>
482b3ad188SAdrian Chadd #include <sys/queue.h>
492b3ad188SAdrian Chadd #include <sys/bus.h>
502b3ad188SAdrian Chadd #include <sys/interrupt.h>
512b3ad188SAdrian Chadd #include <sys/conf.h>
522b3ad188SAdrian Chadd #include <sys/cpuset.h>
536b42a1f4SAndrew Turner #include <sys/rman.h>
542b3ad188SAdrian Chadd #include <sys/sched.h>
552b3ad188SAdrian Chadd #include <sys/smp.h>
56df7a2251SAndrew Turner #ifdef HWPMC_HOOKS
57df7a2251SAndrew Turner #include <sys/pmckern.h>
58df7a2251SAndrew Turner #endif
59df7a2251SAndrew Turner 
602b3ad188SAdrian Chadd #include <machine/atomic.h>
612b3ad188SAdrian Chadd #include <machine/intr.h>
622b3ad188SAdrian Chadd #include <machine/cpu.h>
632b3ad188SAdrian Chadd #include <machine/smp.h>
642b3ad188SAdrian Chadd #include <machine/stdarg.h>
652b3ad188SAdrian Chadd 
662b3ad188SAdrian Chadd #ifdef DDB
672b3ad188SAdrian Chadd #include <ddb/ddb.h>
682b3ad188SAdrian Chadd #endif
692b3ad188SAdrian Chadd 
702b3ad188SAdrian Chadd #include "pic_if.h"
713fc155dcSAndrew Turner #include "msi_if.h"
722b3ad188SAdrian Chadd 
732b3ad188SAdrian Chadd #define	INTRNAME_LEN	(2*MAXCOMLEN + 1)
742b3ad188SAdrian Chadd 
752b3ad188SAdrian Chadd #ifdef DEBUG
762b3ad188SAdrian Chadd #define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
772b3ad188SAdrian Chadd     printf(fmt,##args); } while (0)
782b3ad188SAdrian Chadd #else
792b3ad188SAdrian Chadd #define debugf(fmt, args...)
802b3ad188SAdrian Chadd #endif
812b3ad188SAdrian Chadd 
822b3ad188SAdrian Chadd MALLOC_DECLARE(M_INTRNG);
832b3ad188SAdrian Chadd MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling");
842b3ad188SAdrian Chadd 
852b3ad188SAdrian Chadd /* Main interrupt handler called from assembler -> 'hidden' for C code. */
862b3ad188SAdrian Chadd void intr_irq_handler(struct trapframe *tf);
872b3ad188SAdrian Chadd 
882b3ad188SAdrian Chadd /* Root interrupt controller stuff. */
895b70c08cSSvatopluk Kraus device_t intr_irq_root_dev;
902b3ad188SAdrian Chadd static intr_irq_filter_t *irq_root_filter;
912b3ad188SAdrian Chadd static void *irq_root_arg;
922b3ad188SAdrian Chadd static u_int irq_root_ipicount;
932b3ad188SAdrian Chadd 
94d1605cdaSAndrew Turner struct intr_pic_child {
95d1605cdaSAndrew Turner 	SLIST_ENTRY(intr_pic_child)	 pc_next;
96d1605cdaSAndrew Turner 	struct intr_pic			*pc_pic;
97d1605cdaSAndrew Turner 	intr_child_irq_filter_t		*pc_filter;
98d1605cdaSAndrew Turner 	void				*pc_filter_arg;
99d1605cdaSAndrew Turner 	uintptr_t			 pc_start;
100d1605cdaSAndrew Turner 	uintptr_t			 pc_length;
101d1605cdaSAndrew Turner };
102d1605cdaSAndrew Turner 
1032b3ad188SAdrian Chadd /* Interrupt controller definition. */
1042b3ad188SAdrian Chadd struct intr_pic {
1052b3ad188SAdrian Chadd 	SLIST_ENTRY(intr_pic)	pic_next;
1062b3ad188SAdrian Chadd 	intptr_t		pic_xref;	/* hardware identification */
1072b3ad188SAdrian Chadd 	device_t		pic_dev;
1083fc155dcSAndrew Turner #define	FLAG_PIC	(1 << 0)
1093fc155dcSAndrew Turner #define	FLAG_MSI	(1 << 1)
1103fc155dcSAndrew Turner 	u_int			pic_flags;
111d1605cdaSAndrew Turner 	struct mtx		pic_child_lock;
112d1605cdaSAndrew Turner 	SLIST_HEAD(, intr_pic_child) pic_children;
1132b3ad188SAdrian Chadd };
1142b3ad188SAdrian Chadd 
1152b3ad188SAdrian Chadd static struct mtx pic_list_lock;
1162b3ad188SAdrian Chadd static SLIST_HEAD(, intr_pic) pic_list;
1172b3ad188SAdrian Chadd 
1182b3ad188SAdrian Chadd static struct intr_pic *pic_lookup(device_t dev, intptr_t xref);
1192b3ad188SAdrian Chadd 
1202b3ad188SAdrian Chadd /* Interrupt source definition. */
1212b3ad188SAdrian Chadd static struct mtx isrc_table_lock;
1222b3ad188SAdrian Chadd static struct intr_irqsrc *irq_sources[NIRQ];
1232b3ad188SAdrian Chadd u_int irq_next_free;
1242b3ad188SAdrian Chadd 
1252b3ad188SAdrian Chadd #ifdef SMP
1262b3ad188SAdrian Chadd static boolean_t irq_assign_cpu = FALSE;
1272b3ad188SAdrian Chadd #endif
1282b3ad188SAdrian Chadd 
1292b3ad188SAdrian Chadd /*
1302b3ad188SAdrian Chadd  * - 2 counters for each I/O interrupt.
1312b3ad188SAdrian Chadd  * - MAXCPU counters for each IPI counters for SMP.
1322b3ad188SAdrian Chadd  */
1332b3ad188SAdrian Chadd #ifdef SMP
1342b3ad188SAdrian Chadd #define INTRCNT_COUNT   (NIRQ * 2 + INTR_IPI_COUNT * MAXCPU)
1352b3ad188SAdrian Chadd #else
1362b3ad188SAdrian Chadd #define INTRCNT_COUNT   (NIRQ * 2)
1372b3ad188SAdrian Chadd #endif
1382b3ad188SAdrian Chadd 
1392b3ad188SAdrian Chadd /* Data for MI statistics reporting. */
1402b3ad188SAdrian Chadd u_long intrcnt[INTRCNT_COUNT];
1412b3ad188SAdrian Chadd char intrnames[INTRCNT_COUNT * INTRNAME_LEN];
1422b3ad188SAdrian Chadd size_t sintrcnt = sizeof(intrcnt);
1432b3ad188SAdrian Chadd size_t sintrnames = sizeof(intrnames);
1442b3ad188SAdrian Chadd static u_int intrcnt_index;
1452b3ad188SAdrian Chadd 
146895c8b1cSMichal Meloun static struct intr_irqsrc *intr_map_get_isrc(u_int res_id);
147895c8b1cSMichal Meloun static void intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc);
148*609b0fe9SOleksandr Tymoshenko static struct intr_map_data * intr_map_get_map_data(u_int res_id);
149895c8b1cSMichal Meloun static void intr_map_copy_map_data(u_int res_id, device_t *dev, intptr_t *xref,
150895c8b1cSMichal Meloun     struct intr_map_data **data);
151895c8b1cSMichal Meloun 
1522b3ad188SAdrian Chadd /*
1532b3ad188SAdrian Chadd  *  Interrupt framework initialization routine.
1542b3ad188SAdrian Chadd  */
1552b3ad188SAdrian Chadd static void
1562b3ad188SAdrian Chadd intr_irq_init(void *dummy __unused)
1572b3ad188SAdrian Chadd {
1582b3ad188SAdrian Chadd 
1592b3ad188SAdrian Chadd 	SLIST_INIT(&pic_list);
1602b3ad188SAdrian Chadd 	mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF);
1613fc155dcSAndrew Turner 
1622b3ad188SAdrian Chadd 	mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF);
1632b3ad188SAdrian Chadd }
1642b3ad188SAdrian Chadd SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL);
1652b3ad188SAdrian Chadd 
1662b3ad188SAdrian Chadd static void
1672b3ad188SAdrian Chadd intrcnt_setname(const char *name, int index)
1682b3ad188SAdrian Chadd {
1692b3ad188SAdrian Chadd 
1702b3ad188SAdrian Chadd 	snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s",
1712b3ad188SAdrian Chadd 	    INTRNAME_LEN - 1, name);
1722b3ad188SAdrian Chadd }
1732b3ad188SAdrian Chadd 
1742b3ad188SAdrian Chadd /*
1752b3ad188SAdrian Chadd  *  Update name for interrupt source with interrupt event.
1762b3ad188SAdrian Chadd  */
1772b3ad188SAdrian Chadd static void
1782b3ad188SAdrian Chadd intrcnt_updatename(struct intr_irqsrc *isrc)
1792b3ad188SAdrian Chadd {
1802b3ad188SAdrian Chadd 
1812b3ad188SAdrian Chadd 	/* QQQ: What about stray counter name? */
1822b3ad188SAdrian Chadd 	mtx_assert(&isrc_table_lock, MA_OWNED);
1832b3ad188SAdrian Chadd 	intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index);
1842b3ad188SAdrian Chadd }
1852b3ad188SAdrian Chadd 
1862b3ad188SAdrian Chadd /*
1872b3ad188SAdrian Chadd  *  Virtualization for interrupt source interrupt counter increment.
1882b3ad188SAdrian Chadd  */
1892b3ad188SAdrian Chadd static inline void
1902b3ad188SAdrian Chadd isrc_increment_count(struct intr_irqsrc *isrc)
1912b3ad188SAdrian Chadd {
1922b3ad188SAdrian Chadd 
193bff6be3eSSvatopluk Kraus 	if (isrc->isrc_flags & INTR_ISRCF_PPI)
194bff6be3eSSvatopluk Kraus 		atomic_add_long(&isrc->isrc_count[0], 1);
195bff6be3eSSvatopluk Kraus 	else
1962b3ad188SAdrian Chadd 		isrc->isrc_count[0]++;
1972b3ad188SAdrian Chadd }
1982b3ad188SAdrian Chadd 
1992b3ad188SAdrian Chadd /*
2002b3ad188SAdrian Chadd  *  Virtualization for interrupt source interrupt stray counter increment.
2012b3ad188SAdrian Chadd  */
2022b3ad188SAdrian Chadd static inline void
2032b3ad188SAdrian Chadd isrc_increment_straycount(struct intr_irqsrc *isrc)
2042b3ad188SAdrian Chadd {
2052b3ad188SAdrian Chadd 
2062b3ad188SAdrian Chadd 	isrc->isrc_count[1]++;
2072b3ad188SAdrian Chadd }
2082b3ad188SAdrian Chadd 
2092b3ad188SAdrian Chadd /*
2102b3ad188SAdrian Chadd  *  Virtualization for interrupt source interrupt name update.
2112b3ad188SAdrian Chadd  */
2122b3ad188SAdrian Chadd static void
2132b3ad188SAdrian Chadd isrc_update_name(struct intr_irqsrc *isrc, const char *name)
2142b3ad188SAdrian Chadd {
2152b3ad188SAdrian Chadd 	char str[INTRNAME_LEN];
2162b3ad188SAdrian Chadd 
2172b3ad188SAdrian Chadd 	mtx_assert(&isrc_table_lock, MA_OWNED);
2182b3ad188SAdrian Chadd 
2192b3ad188SAdrian Chadd 	if (name != NULL) {
2202b3ad188SAdrian Chadd 		snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name);
2212b3ad188SAdrian Chadd 		intrcnt_setname(str, isrc->isrc_index);
2222b3ad188SAdrian Chadd 		snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name,
2232b3ad188SAdrian Chadd 		    name);
2242b3ad188SAdrian Chadd 		intrcnt_setname(str, isrc->isrc_index + 1);
2252b3ad188SAdrian Chadd 	} else {
2262b3ad188SAdrian Chadd 		snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name);
2272b3ad188SAdrian Chadd 		intrcnt_setname(str, isrc->isrc_index);
2282b3ad188SAdrian Chadd 		snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name);
2292b3ad188SAdrian Chadd 		intrcnt_setname(str, isrc->isrc_index + 1);
2302b3ad188SAdrian Chadd 	}
2312b3ad188SAdrian Chadd }
2322b3ad188SAdrian Chadd 
2332b3ad188SAdrian Chadd /*
2342b3ad188SAdrian Chadd  *  Virtualization for interrupt source interrupt counters setup.
2352b3ad188SAdrian Chadd  */
2362b3ad188SAdrian Chadd static void
2372b3ad188SAdrian Chadd isrc_setup_counters(struct intr_irqsrc *isrc)
2382b3ad188SAdrian Chadd {
2392b3ad188SAdrian Chadd 	u_int index;
2402b3ad188SAdrian Chadd 
2412b3ad188SAdrian Chadd 	/*
2422b3ad188SAdrian Chadd 	 *  XXX - it does not work well with removable controllers and
2432b3ad188SAdrian Chadd 	 *        interrupt sources !!!
2442b3ad188SAdrian Chadd 	 */
2452b3ad188SAdrian Chadd 	index = atomic_fetchadd_int(&intrcnt_index, 2);
2462b3ad188SAdrian Chadd 	isrc->isrc_index = index;
2472b3ad188SAdrian Chadd 	isrc->isrc_count = &intrcnt[index];
2482b3ad188SAdrian Chadd 	isrc_update_name(isrc, NULL);
2492b3ad188SAdrian Chadd }
2502b3ad188SAdrian Chadd 
251bff6be3eSSvatopluk Kraus /*
252bff6be3eSSvatopluk Kraus  *  Virtualization for interrupt source interrupt counters release.
253bff6be3eSSvatopluk Kraus  */
254bff6be3eSSvatopluk Kraus static void
255bff6be3eSSvatopluk Kraus isrc_release_counters(struct intr_irqsrc *isrc)
256bff6be3eSSvatopluk Kraus {
257bff6be3eSSvatopluk Kraus 
258bff6be3eSSvatopluk Kraus 	panic("%s: not implemented", __func__);
259bff6be3eSSvatopluk Kraus }
260bff6be3eSSvatopluk Kraus 
2612b3ad188SAdrian Chadd #ifdef SMP
2622b3ad188SAdrian Chadd /*
2632b3ad188SAdrian Chadd  *  Virtualization for interrupt source IPI counters setup.
2642b3ad188SAdrian Chadd  */
2655b70c08cSSvatopluk Kraus u_long *
2665b70c08cSSvatopluk Kraus intr_ipi_setup_counters(const char *name)
2672b3ad188SAdrian Chadd {
2682b3ad188SAdrian Chadd 	u_int index, i;
2692b3ad188SAdrian Chadd 	char str[INTRNAME_LEN];
2702b3ad188SAdrian Chadd 
2712b3ad188SAdrian Chadd 	index = atomic_fetchadd_int(&intrcnt_index, MAXCPU);
2722b3ad188SAdrian Chadd 	for (i = 0; i < MAXCPU; i++) {
2732b3ad188SAdrian Chadd 		snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name);
2742b3ad188SAdrian Chadd 		intrcnt_setname(str, index + i);
2752b3ad188SAdrian Chadd 	}
2765b70c08cSSvatopluk Kraus 	return (&intrcnt[index]);
2772b3ad188SAdrian Chadd }
2782b3ad188SAdrian Chadd #endif
2792b3ad188SAdrian Chadd 
2802b3ad188SAdrian Chadd /*
2812b3ad188SAdrian Chadd  *  Main interrupt dispatch handler. It's called straight
2822b3ad188SAdrian Chadd  *  from the assembler, where CPU interrupt is served.
2832b3ad188SAdrian Chadd  */
2842b3ad188SAdrian Chadd void
2852b3ad188SAdrian Chadd intr_irq_handler(struct trapframe *tf)
2862b3ad188SAdrian Chadd {
2872b3ad188SAdrian Chadd 	struct trapframe * oldframe;
2882b3ad188SAdrian Chadd 	struct thread * td;
2892b3ad188SAdrian Chadd 
2902b3ad188SAdrian Chadd 	KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__));
2912b3ad188SAdrian Chadd 
2922b3ad188SAdrian Chadd 	PCPU_INC(cnt.v_intr);
2932b3ad188SAdrian Chadd 	critical_enter();
2942b3ad188SAdrian Chadd 	td = curthread;
2952b3ad188SAdrian Chadd 	oldframe = td->td_intr_frame;
2962b3ad188SAdrian Chadd 	td->td_intr_frame = tf;
2972b3ad188SAdrian Chadd 	irq_root_filter(irq_root_arg);
2982b3ad188SAdrian Chadd 	td->td_intr_frame = oldframe;
2992b3ad188SAdrian Chadd 	critical_exit();
300df7a2251SAndrew Turner #ifdef HWPMC_HOOKS
301974692e3SAndrew Turner 	if (pmc_hook && TRAPF_USERMODE(tf) &&
302974692e3SAndrew Turner 	    (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN))
303df7a2251SAndrew Turner 		pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf);
304df7a2251SAndrew Turner #endif
3052b3ad188SAdrian Chadd }
3062b3ad188SAdrian Chadd 
307d1605cdaSAndrew Turner int
308d1605cdaSAndrew Turner intr_child_irq_handler(struct intr_pic *parent, uintptr_t irq)
309d1605cdaSAndrew Turner {
310d1605cdaSAndrew Turner 	struct intr_pic_child *child;
311d1605cdaSAndrew Turner 	bool found;
312d1605cdaSAndrew Turner 
313d1605cdaSAndrew Turner 	found = false;
314d1605cdaSAndrew Turner 	mtx_lock_spin(&parent->pic_child_lock);
315d1605cdaSAndrew Turner 	SLIST_FOREACH(child, &parent->pic_children, pc_next) {
316d1605cdaSAndrew Turner 		if (child->pc_start <= irq &&
317d1605cdaSAndrew Turner 		    irq < (child->pc_start + child->pc_length)) {
318d1605cdaSAndrew Turner 			found = true;
319d1605cdaSAndrew Turner 			break;
320d1605cdaSAndrew Turner 		}
321d1605cdaSAndrew Turner 	}
322d1605cdaSAndrew Turner 	mtx_unlock_spin(&parent->pic_child_lock);
323d1605cdaSAndrew Turner 
324d1605cdaSAndrew Turner 	if (found)
325d1605cdaSAndrew Turner 		return (child->pc_filter(child->pc_filter_arg, irq));
326d1605cdaSAndrew Turner 
327d1605cdaSAndrew Turner 	return (FILTER_STRAY);
328d1605cdaSAndrew Turner }
329d1605cdaSAndrew Turner 
3302b3ad188SAdrian Chadd /*
3312b3ad188SAdrian Chadd  *  interrupt controller dispatch function for interrupts. It should
3322b3ad188SAdrian Chadd  *  be called straight from the interrupt controller, when associated interrupt
3332b3ad188SAdrian Chadd  *  source is learned.
3342b3ad188SAdrian Chadd  */
335bff6be3eSSvatopluk Kraus int
336bff6be3eSSvatopluk Kraus intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf)
3372b3ad188SAdrian Chadd {
3382b3ad188SAdrian Chadd 
3392b3ad188SAdrian Chadd 	KASSERT(isrc != NULL, ("%s: no source", __func__));
3402b3ad188SAdrian Chadd 
3412b3ad188SAdrian Chadd 	isrc_increment_count(isrc);
3422b3ad188SAdrian Chadd 
3432b3ad188SAdrian Chadd #ifdef INTR_SOLO
3442b3ad188SAdrian Chadd 	if (isrc->isrc_filter != NULL) {
3452b3ad188SAdrian Chadd 		int error;
3462b3ad188SAdrian Chadd 		error = isrc->isrc_filter(isrc->isrc_arg, tf);
3472b3ad188SAdrian Chadd 		PIC_POST_FILTER(isrc->isrc_dev, isrc);
3482b3ad188SAdrian Chadd 		if (error == FILTER_HANDLED)
349bff6be3eSSvatopluk Kraus 			return (0);
3502b3ad188SAdrian Chadd 	} else
3512b3ad188SAdrian Chadd #endif
3522b3ad188SAdrian Chadd 	if (isrc->isrc_event != NULL) {
3532b3ad188SAdrian Chadd 		if (intr_event_handle(isrc->isrc_event, tf) == 0)
354bff6be3eSSvatopluk Kraus 			return (0);
3552b3ad188SAdrian Chadd 	}
3562b3ad188SAdrian Chadd 
3572b3ad188SAdrian Chadd 	isrc_increment_straycount(isrc);
358bff6be3eSSvatopluk Kraus 	return (EINVAL);
3592b3ad188SAdrian Chadd }
3602b3ad188SAdrian Chadd 
3612b3ad188SAdrian Chadd /*
3622b3ad188SAdrian Chadd  *  Alloc unique interrupt number (resource handle) for interrupt source.
3632b3ad188SAdrian Chadd  *
3642b3ad188SAdrian Chadd  *  There could be various strategies how to allocate free interrupt number
3652b3ad188SAdrian Chadd  *  (resource handle) for new interrupt source.
3662b3ad188SAdrian Chadd  *
3672b3ad188SAdrian Chadd  *  1. Handles are always allocated forward, so handles are not recycled
3682b3ad188SAdrian Chadd  *     immediately. However, if only one free handle left which is reused
3692b3ad188SAdrian Chadd  *     constantly...
3702b3ad188SAdrian Chadd  */
371bff6be3eSSvatopluk Kraus static inline int
372bff6be3eSSvatopluk Kraus isrc_alloc_irq(struct intr_irqsrc *isrc)
3732b3ad188SAdrian Chadd {
3742b3ad188SAdrian Chadd 	u_int maxirqs, irq;
3752b3ad188SAdrian Chadd 
3762b3ad188SAdrian Chadd 	mtx_assert(&isrc_table_lock, MA_OWNED);
3772b3ad188SAdrian Chadd 
3782b3ad188SAdrian Chadd 	maxirqs = nitems(irq_sources);
3792b3ad188SAdrian Chadd 	if (irq_next_free >= maxirqs)
3802b3ad188SAdrian Chadd 		return (ENOSPC);
3812b3ad188SAdrian Chadd 
3822b3ad188SAdrian Chadd 	for (irq = irq_next_free; irq < maxirqs; irq++) {
3832b3ad188SAdrian Chadd 		if (irq_sources[irq] == NULL)
3842b3ad188SAdrian Chadd 			goto found;
3852b3ad188SAdrian Chadd 	}
3862b3ad188SAdrian Chadd 	for (irq = 0; irq < irq_next_free; irq++) {
3872b3ad188SAdrian Chadd 		if (irq_sources[irq] == NULL)
3882b3ad188SAdrian Chadd 			goto found;
3892b3ad188SAdrian Chadd 	}
3902b3ad188SAdrian Chadd 
3912b3ad188SAdrian Chadd 	irq_next_free = maxirqs;
3922b3ad188SAdrian Chadd 	return (ENOSPC);
3932b3ad188SAdrian Chadd 
3942b3ad188SAdrian Chadd found:
3952b3ad188SAdrian Chadd 	isrc->isrc_irq = irq;
3962b3ad188SAdrian Chadd 	irq_sources[irq] = isrc;
3972b3ad188SAdrian Chadd 
3982b3ad188SAdrian Chadd 	irq_next_free = irq + 1;
3992b3ad188SAdrian Chadd 	if (irq_next_free >= maxirqs)
4002b3ad188SAdrian Chadd 		irq_next_free = 0;
4012b3ad188SAdrian Chadd 	return (0);
4022b3ad188SAdrian Chadd }
403bff6be3eSSvatopluk Kraus 
4042b3ad188SAdrian Chadd /*
4052b3ad188SAdrian Chadd  *  Free unique interrupt number (resource handle) from interrupt source.
4062b3ad188SAdrian Chadd  */
407bff6be3eSSvatopluk Kraus static inline int
4082b3ad188SAdrian Chadd isrc_free_irq(struct intr_irqsrc *isrc)
4092b3ad188SAdrian Chadd {
4102b3ad188SAdrian Chadd 
411bff6be3eSSvatopluk Kraus 	mtx_assert(&isrc_table_lock, MA_OWNED);
4122b3ad188SAdrian Chadd 
413bff6be3eSSvatopluk Kraus 	if (isrc->isrc_irq >= nitems(irq_sources))
4142b3ad188SAdrian Chadd 		return (EINVAL);
415bff6be3eSSvatopluk Kraus 	if (irq_sources[isrc->isrc_irq] != isrc)
4162b3ad188SAdrian Chadd 		return (EINVAL);
4172b3ad188SAdrian Chadd 
4182b3ad188SAdrian Chadd 	irq_sources[isrc->isrc_irq] = NULL;
4198442087fSMichal Meloun 	isrc->isrc_irq = INTR_IRQ_INVALID;	/* just to be safe */
4202b3ad188SAdrian Chadd 	return (0);
4212b3ad188SAdrian Chadd }
422bff6be3eSSvatopluk Kraus 
4232b3ad188SAdrian Chadd /*
424bff6be3eSSvatopluk Kraus  *  Initialize interrupt source and register it into global interrupt table.
4252b3ad188SAdrian Chadd  */
426bff6be3eSSvatopluk Kraus int
427bff6be3eSSvatopluk Kraus intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags,
428bff6be3eSSvatopluk Kraus     const char *fmt, ...)
4292b3ad188SAdrian Chadd {
430bff6be3eSSvatopluk Kraus 	int error;
431bff6be3eSSvatopluk Kraus 	va_list ap;
4322b3ad188SAdrian Chadd 
433bff6be3eSSvatopluk Kraus 	bzero(isrc, sizeof(struct intr_irqsrc));
434bff6be3eSSvatopluk Kraus 	isrc->isrc_dev = dev;
4358442087fSMichal Meloun 	isrc->isrc_irq = INTR_IRQ_INVALID;	/* just to be safe */
436bff6be3eSSvatopluk Kraus 	isrc->isrc_flags = flags;
4372b3ad188SAdrian Chadd 
438bff6be3eSSvatopluk Kraus 	va_start(ap, fmt);
439bff6be3eSSvatopluk Kraus 	vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap);
440bff6be3eSSvatopluk Kraus 	va_end(ap);
441bff6be3eSSvatopluk Kraus 
442bff6be3eSSvatopluk Kraus 	mtx_lock(&isrc_table_lock);
443bff6be3eSSvatopluk Kraus 	error = isrc_alloc_irq(isrc);
444bff6be3eSSvatopluk Kraus 	if (error != 0) {
445bff6be3eSSvatopluk Kraus 		mtx_unlock(&isrc_table_lock);
446bff6be3eSSvatopluk Kraus 		return (error);
4472b3ad188SAdrian Chadd 	}
448bff6be3eSSvatopluk Kraus 	/*
449bff6be3eSSvatopluk Kraus 	 * Setup interrupt counters, but not for IPI sources. Those are setup
450bff6be3eSSvatopluk Kraus 	 * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust
451bff6be3eSSvatopluk Kraus 	 * our counter pool.
452bff6be3eSSvatopluk Kraus 	 */
453bff6be3eSSvatopluk Kraus 	if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0)
454bff6be3eSSvatopluk Kraus 		isrc_setup_counters(isrc);
455bff6be3eSSvatopluk Kraus 	mtx_unlock(&isrc_table_lock);
456bff6be3eSSvatopluk Kraus 	return (0);
4572b3ad188SAdrian Chadd }
4582b3ad188SAdrian Chadd 
4592b3ad188SAdrian Chadd /*
460bff6be3eSSvatopluk Kraus  *  Deregister interrupt source from global interrupt table.
461bff6be3eSSvatopluk Kraus  */
462bff6be3eSSvatopluk Kraus int
463bff6be3eSSvatopluk Kraus intr_isrc_deregister(struct intr_irqsrc *isrc)
464bff6be3eSSvatopluk Kraus {
465bff6be3eSSvatopluk Kraus 	int error;
466bff6be3eSSvatopluk Kraus 
467bff6be3eSSvatopluk Kraus 	mtx_lock(&isrc_table_lock);
468bff6be3eSSvatopluk Kraus 	if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0)
469bff6be3eSSvatopluk Kraus 		isrc_release_counters(isrc);
470bff6be3eSSvatopluk Kraus 	error = isrc_free_irq(isrc);
471bff6be3eSSvatopluk Kraus 	mtx_unlock(&isrc_table_lock);
472bff6be3eSSvatopluk Kraus 	return (error);
473bff6be3eSSvatopluk Kraus }
474bff6be3eSSvatopluk Kraus 
4755b613c19SSvatopluk Kraus #ifdef SMP
4765b613c19SSvatopluk Kraus /*
4775b613c19SSvatopluk Kraus  *  A support function for a PIC to decide if provided ISRC should be inited
4785b613c19SSvatopluk Kraus  *  on given cpu. The logic of INTR_ISRCF_BOUND flag and isrc_cpu member of
4795b613c19SSvatopluk Kraus  *  struct intr_irqsrc is the following:
4805b613c19SSvatopluk Kraus  *
4815b613c19SSvatopluk Kraus  *     If INTR_ISRCF_BOUND is set, the ISRC should be inited only on cpus
4825b613c19SSvatopluk Kraus  *     set in isrc_cpu. If not, the ISRC should be inited on every cpu and
4835b613c19SSvatopluk Kraus  *     isrc_cpu is kept consistent with it. Thus isrc_cpu is always correct.
4845b613c19SSvatopluk Kraus  */
4855b613c19SSvatopluk Kraus bool
4865b613c19SSvatopluk Kraus intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu)
4875b613c19SSvatopluk Kraus {
4885b613c19SSvatopluk Kraus 
4895b613c19SSvatopluk Kraus 	if (isrc->isrc_handlers == 0)
4905b613c19SSvatopluk Kraus 		return (false);
4915b613c19SSvatopluk Kraus 	if ((isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) == 0)
4925b613c19SSvatopluk Kraus 		return (false);
4935b613c19SSvatopluk Kraus 	if (isrc->isrc_flags & INTR_ISRCF_BOUND)
4945b613c19SSvatopluk Kraus 		return (CPU_ISSET(cpu, &isrc->isrc_cpu));
4955b613c19SSvatopluk Kraus 
4965b613c19SSvatopluk Kraus 	CPU_SET(cpu, &isrc->isrc_cpu);
4975b613c19SSvatopluk Kraus 	return (true);
4985b613c19SSvatopluk Kraus }
4995b613c19SSvatopluk Kraus #endif
5005b613c19SSvatopluk Kraus 
5012b3ad188SAdrian Chadd #ifdef INTR_SOLO
5022b3ad188SAdrian Chadd /*
5032b3ad188SAdrian Chadd  *  Setup filter into interrupt source.
5042b3ad188SAdrian Chadd  */
5052b3ad188SAdrian Chadd static int
5062b3ad188SAdrian Chadd iscr_setup_filter(struct intr_irqsrc *isrc, const char *name,
5072b3ad188SAdrian Chadd     intr_irq_filter_t *filter, void *arg, void **cookiep)
5082b3ad188SAdrian Chadd {
5092b3ad188SAdrian Chadd 
5102b3ad188SAdrian Chadd 	if (filter == NULL)
5112b3ad188SAdrian Chadd 		return (EINVAL);
5122b3ad188SAdrian Chadd 
5132b3ad188SAdrian Chadd 	mtx_lock(&isrc_table_lock);
5142b3ad188SAdrian Chadd 	/*
5152b3ad188SAdrian Chadd 	 * Make sure that we do not mix the two ways
5162b3ad188SAdrian Chadd 	 * how we handle interrupt sources.
5172b3ad188SAdrian Chadd 	 */
5182b3ad188SAdrian Chadd 	if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) {
5192b3ad188SAdrian Chadd 		mtx_unlock(&isrc_table_lock);
5202b3ad188SAdrian Chadd 		return (EBUSY);
5212b3ad188SAdrian Chadd 	}
5222b3ad188SAdrian Chadd 	isrc->isrc_filter = filter;
5232b3ad188SAdrian Chadd 	isrc->isrc_arg = arg;
5242b3ad188SAdrian Chadd 	isrc_update_name(isrc, name);
5252b3ad188SAdrian Chadd 	mtx_unlock(&isrc_table_lock);
5262b3ad188SAdrian Chadd 
5272b3ad188SAdrian Chadd 	*cookiep = isrc;
5282b3ad188SAdrian Chadd 	return (0);
5292b3ad188SAdrian Chadd }
5302b3ad188SAdrian Chadd #endif
5312b3ad188SAdrian Chadd 
5322b3ad188SAdrian Chadd /*
5332b3ad188SAdrian Chadd  *  Interrupt source pre_ithread method for MI interrupt framework.
5342b3ad188SAdrian Chadd  */
5352b3ad188SAdrian Chadd static void
5362b3ad188SAdrian Chadd intr_isrc_pre_ithread(void *arg)
5372b3ad188SAdrian Chadd {
5382b3ad188SAdrian Chadd 	struct intr_irqsrc *isrc = arg;
5392b3ad188SAdrian Chadd 
5402b3ad188SAdrian Chadd 	PIC_PRE_ITHREAD(isrc->isrc_dev, isrc);
5412b3ad188SAdrian Chadd }
5422b3ad188SAdrian Chadd 
5432b3ad188SAdrian Chadd /*
5442b3ad188SAdrian Chadd  *  Interrupt source post_ithread method for MI interrupt framework.
5452b3ad188SAdrian Chadd  */
5462b3ad188SAdrian Chadd static void
5472b3ad188SAdrian Chadd intr_isrc_post_ithread(void *arg)
5482b3ad188SAdrian Chadd {
5492b3ad188SAdrian Chadd 	struct intr_irqsrc *isrc = arg;
5502b3ad188SAdrian Chadd 
5512b3ad188SAdrian Chadd 	PIC_POST_ITHREAD(isrc->isrc_dev, isrc);
5522b3ad188SAdrian Chadd }
5532b3ad188SAdrian Chadd 
5542b3ad188SAdrian Chadd /*
5552b3ad188SAdrian Chadd  *  Interrupt source post_filter method for MI interrupt framework.
5562b3ad188SAdrian Chadd  */
5572b3ad188SAdrian Chadd static void
5582b3ad188SAdrian Chadd intr_isrc_post_filter(void *arg)
5592b3ad188SAdrian Chadd {
5602b3ad188SAdrian Chadd 	struct intr_irqsrc *isrc = arg;
5612b3ad188SAdrian Chadd 
5622b3ad188SAdrian Chadd 	PIC_POST_FILTER(isrc->isrc_dev, isrc);
5632b3ad188SAdrian Chadd }
5642b3ad188SAdrian Chadd 
5652b3ad188SAdrian Chadd /*
5662b3ad188SAdrian Chadd  *  Interrupt source assign_cpu method for MI interrupt framework.
5672b3ad188SAdrian Chadd  */
5682b3ad188SAdrian Chadd static int
5692b3ad188SAdrian Chadd intr_isrc_assign_cpu(void *arg, int cpu)
5702b3ad188SAdrian Chadd {
5712b3ad188SAdrian Chadd #ifdef SMP
5722b3ad188SAdrian Chadd 	struct intr_irqsrc *isrc = arg;
5732b3ad188SAdrian Chadd 	int error;
5742b3ad188SAdrian Chadd 
5755b70c08cSSvatopluk Kraus 	if (isrc->isrc_dev != intr_irq_root_dev)
5762b3ad188SAdrian Chadd 		return (EINVAL);
5772b3ad188SAdrian Chadd 
5782b3ad188SAdrian Chadd 	mtx_lock(&isrc_table_lock);
5792b3ad188SAdrian Chadd 	if (cpu == NOCPU) {
5802b3ad188SAdrian Chadd 		CPU_ZERO(&isrc->isrc_cpu);
5812b3ad188SAdrian Chadd 		isrc->isrc_flags &= ~INTR_ISRCF_BOUND;
5822b3ad188SAdrian Chadd 	} else {
5832b3ad188SAdrian Chadd 		CPU_SETOF(cpu, &isrc->isrc_cpu);
5842b3ad188SAdrian Chadd 		isrc->isrc_flags |= INTR_ISRCF_BOUND;
5852b3ad188SAdrian Chadd 	}
5862b3ad188SAdrian Chadd 
5872b3ad188SAdrian Chadd 	/*
5882b3ad188SAdrian Chadd 	 * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or
5892b3ad188SAdrian Chadd 	 * re-balance it to another CPU or enable it on more CPUs. However,
5902b3ad188SAdrian Chadd 	 * PIC is expected to change isrc_cpu appropriately to keep us well
591e3043798SPedro F. Giffuni 	 * informed if the call is successful.
5922b3ad188SAdrian Chadd 	 */
5932b3ad188SAdrian Chadd 	if (irq_assign_cpu) {
594bff6be3eSSvatopluk Kraus 		error = PIC_BIND_INTR(isrc->isrc_dev, isrc);
5952b3ad188SAdrian Chadd 		if (error) {
5962b3ad188SAdrian Chadd 			CPU_ZERO(&isrc->isrc_cpu);
5972b3ad188SAdrian Chadd 			mtx_unlock(&isrc_table_lock);
5982b3ad188SAdrian Chadd 			return (error);
5992b3ad188SAdrian Chadd 		}
6002b3ad188SAdrian Chadd 	}
6012b3ad188SAdrian Chadd 	mtx_unlock(&isrc_table_lock);
6022b3ad188SAdrian Chadd 	return (0);
6032b3ad188SAdrian Chadd #else
6042b3ad188SAdrian Chadd 	return (EOPNOTSUPP);
6052b3ad188SAdrian Chadd #endif
6062b3ad188SAdrian Chadd }
6072b3ad188SAdrian Chadd 
6082b3ad188SAdrian Chadd /*
6092b3ad188SAdrian Chadd  *  Create interrupt event for interrupt source.
6102b3ad188SAdrian Chadd  */
6112b3ad188SAdrian Chadd static int
6122b3ad188SAdrian Chadd isrc_event_create(struct intr_irqsrc *isrc)
6132b3ad188SAdrian Chadd {
6142b3ad188SAdrian Chadd 	struct intr_event *ie;
6152b3ad188SAdrian Chadd 	int error;
6162b3ad188SAdrian Chadd 
6172b3ad188SAdrian Chadd 	error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq,
6182b3ad188SAdrian Chadd 	    intr_isrc_pre_ithread, intr_isrc_post_ithread, intr_isrc_post_filter,
6192b3ad188SAdrian Chadd 	    intr_isrc_assign_cpu, "%s:", isrc->isrc_name);
6202b3ad188SAdrian Chadd 	if (error)
6212b3ad188SAdrian Chadd 		return (error);
6222b3ad188SAdrian Chadd 
6232b3ad188SAdrian Chadd 	mtx_lock(&isrc_table_lock);
6242b3ad188SAdrian Chadd 	/*
6252b3ad188SAdrian Chadd 	 * Make sure that we do not mix the two ways
6262b3ad188SAdrian Chadd 	 * how we handle interrupt sources. Let contested event wins.
6272b3ad188SAdrian Chadd 	 */
628169e6abdSSvatopluk Kraus #ifdef INTR_SOLO
6292b3ad188SAdrian Chadd 	if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) {
630169e6abdSSvatopluk Kraus #else
631169e6abdSSvatopluk Kraus 	if (isrc->isrc_event != NULL) {
632169e6abdSSvatopluk Kraus #endif
6332b3ad188SAdrian Chadd 		mtx_unlock(&isrc_table_lock);
6342b3ad188SAdrian Chadd 		intr_event_destroy(ie);
6352b3ad188SAdrian Chadd 		return (isrc->isrc_event != NULL ? EBUSY : 0);
6362b3ad188SAdrian Chadd 	}
6372b3ad188SAdrian Chadd 	isrc->isrc_event = ie;
6382b3ad188SAdrian Chadd 	mtx_unlock(&isrc_table_lock);
6392b3ad188SAdrian Chadd 
6402b3ad188SAdrian Chadd 	return (0);
6412b3ad188SAdrian Chadd }
6422b3ad188SAdrian Chadd #ifdef notyet
6432b3ad188SAdrian Chadd /*
6442b3ad188SAdrian Chadd  *  Destroy interrupt event for interrupt source.
6452b3ad188SAdrian Chadd  */
6462b3ad188SAdrian Chadd static void
6472b3ad188SAdrian Chadd isrc_event_destroy(struct intr_irqsrc *isrc)
6482b3ad188SAdrian Chadd {
6492b3ad188SAdrian Chadd 	struct intr_event *ie;
6502b3ad188SAdrian Chadd 
6512b3ad188SAdrian Chadd 	mtx_lock(&isrc_table_lock);
6522b3ad188SAdrian Chadd 	ie = isrc->isrc_event;
6532b3ad188SAdrian Chadd 	isrc->isrc_event = NULL;
6542b3ad188SAdrian Chadd 	mtx_unlock(&isrc_table_lock);
6552b3ad188SAdrian Chadd 
6562b3ad188SAdrian Chadd 	if (ie != NULL)
6572b3ad188SAdrian Chadd 		intr_event_destroy(ie);
6582b3ad188SAdrian Chadd }
6592b3ad188SAdrian Chadd #endif
6602b3ad188SAdrian Chadd /*
6612b3ad188SAdrian Chadd  *  Add handler to interrupt source.
6622b3ad188SAdrian Chadd  */
6632b3ad188SAdrian Chadd static int
6642b3ad188SAdrian Chadd isrc_add_handler(struct intr_irqsrc *isrc, const char *name,
6652b3ad188SAdrian Chadd     driver_filter_t filter, driver_intr_t handler, void *arg,
6662b3ad188SAdrian Chadd     enum intr_type flags, void **cookiep)
6672b3ad188SAdrian Chadd {
6682b3ad188SAdrian Chadd 	int error;
6692b3ad188SAdrian Chadd 
6702b3ad188SAdrian Chadd 	if (isrc->isrc_event == NULL) {
6712b3ad188SAdrian Chadd 		error = isrc_event_create(isrc);
6722b3ad188SAdrian Chadd 		if (error)
6732b3ad188SAdrian Chadd 			return (error);
6742b3ad188SAdrian Chadd 	}
6752b3ad188SAdrian Chadd 
6762b3ad188SAdrian Chadd 	error = intr_event_add_handler(isrc->isrc_event, name, filter, handler,
6772b3ad188SAdrian Chadd 	    arg, intr_priority(flags), flags, cookiep);
6782b3ad188SAdrian Chadd 	if (error == 0) {
6792b3ad188SAdrian Chadd 		mtx_lock(&isrc_table_lock);
6802b3ad188SAdrian Chadd 		intrcnt_updatename(isrc);
6812b3ad188SAdrian Chadd 		mtx_unlock(&isrc_table_lock);
6822b3ad188SAdrian Chadd 	}
6832b3ad188SAdrian Chadd 
6842b3ad188SAdrian Chadd 	return (error);
6852b3ad188SAdrian Chadd }
6862b3ad188SAdrian Chadd 
6872b3ad188SAdrian Chadd /*
6882b3ad188SAdrian Chadd  *  Lookup interrupt controller locked.
6892b3ad188SAdrian Chadd  */
690bff6be3eSSvatopluk Kraus static inline struct intr_pic *
6912b3ad188SAdrian Chadd pic_lookup_locked(device_t dev, intptr_t xref)
6922b3ad188SAdrian Chadd {
6932b3ad188SAdrian Chadd 	struct intr_pic *pic;
6942b3ad188SAdrian Chadd 
6952b3ad188SAdrian Chadd 	mtx_assert(&pic_list_lock, MA_OWNED);
6962b3ad188SAdrian Chadd 
6974be58cbaSSvatopluk Kraus 	if (dev == NULL && xref == 0)
6984be58cbaSSvatopluk Kraus 		return (NULL);
6994be58cbaSSvatopluk Kraus 
7004be58cbaSSvatopluk Kraus 	/* Note that pic->pic_dev is never NULL on registered PIC. */
7012b3ad188SAdrian Chadd 	SLIST_FOREACH(pic, &pic_list, pic_next) {
7024be58cbaSSvatopluk Kraus 		if (dev == NULL) {
7034be58cbaSSvatopluk Kraus 			if (xref == pic->pic_xref)
7044be58cbaSSvatopluk Kraus 				return (pic);
7054be58cbaSSvatopluk Kraus 		} else if (xref == 0 || pic->pic_xref == 0) {
7064be58cbaSSvatopluk Kraus 			if (dev == pic->pic_dev)
7074be58cbaSSvatopluk Kraus 				return (pic);
7084be58cbaSSvatopluk Kraus 		} else if (xref == pic->pic_xref && dev == pic->pic_dev)
7092b3ad188SAdrian Chadd 				return (pic);
7102b3ad188SAdrian Chadd 	}
7112b3ad188SAdrian Chadd 	return (NULL);
7122b3ad188SAdrian Chadd }
7132b3ad188SAdrian Chadd 
7142b3ad188SAdrian Chadd /*
7152b3ad188SAdrian Chadd  *  Lookup interrupt controller.
7162b3ad188SAdrian Chadd  */
7172b3ad188SAdrian Chadd static struct intr_pic *
7182b3ad188SAdrian Chadd pic_lookup(device_t dev, intptr_t xref)
7192b3ad188SAdrian Chadd {
7202b3ad188SAdrian Chadd 	struct intr_pic *pic;
7212b3ad188SAdrian Chadd 
7222b3ad188SAdrian Chadd 	mtx_lock(&pic_list_lock);
7232b3ad188SAdrian Chadd 	pic = pic_lookup_locked(dev, xref);
7242b3ad188SAdrian Chadd 	mtx_unlock(&pic_list_lock);
7252b3ad188SAdrian Chadd 	return (pic);
7262b3ad188SAdrian Chadd }
7272b3ad188SAdrian Chadd 
7282b3ad188SAdrian Chadd /*
7292b3ad188SAdrian Chadd  *  Create interrupt controller.
7302b3ad188SAdrian Chadd  */
7312b3ad188SAdrian Chadd static struct intr_pic *
7322b3ad188SAdrian Chadd pic_create(device_t dev, intptr_t xref)
7332b3ad188SAdrian Chadd {
7342b3ad188SAdrian Chadd 	struct intr_pic *pic;
7352b3ad188SAdrian Chadd 
7362b3ad188SAdrian Chadd 	mtx_lock(&pic_list_lock);
7372b3ad188SAdrian Chadd 	pic = pic_lookup_locked(dev, xref);
7382b3ad188SAdrian Chadd 	if (pic != NULL) {
7392b3ad188SAdrian Chadd 		mtx_unlock(&pic_list_lock);
7402b3ad188SAdrian Chadd 		return (pic);
7412b3ad188SAdrian Chadd 	}
7422b3ad188SAdrian Chadd 	pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO);
743b48c6083SAndrew Turner 	if (pic == NULL) {
744b48c6083SAndrew Turner 		mtx_unlock(&pic_list_lock);
745b48c6083SAndrew Turner 		return (NULL);
746b48c6083SAndrew Turner 	}
7472b3ad188SAdrian Chadd 	pic->pic_xref = xref;
7482b3ad188SAdrian Chadd 	pic->pic_dev = dev;
749d1605cdaSAndrew Turner 	mtx_init(&pic->pic_child_lock, "pic child lock", NULL, MTX_SPIN);
7502b3ad188SAdrian Chadd 	SLIST_INSERT_HEAD(&pic_list, pic, pic_next);
7512b3ad188SAdrian Chadd 	mtx_unlock(&pic_list_lock);
7522b3ad188SAdrian Chadd 
7532b3ad188SAdrian Chadd 	return (pic);
7542b3ad188SAdrian Chadd }
7552b3ad188SAdrian Chadd #ifdef notyet
7562b3ad188SAdrian Chadd /*
7572b3ad188SAdrian Chadd  *  Destroy interrupt controller.
7582b3ad188SAdrian Chadd  */
7592b3ad188SAdrian Chadd static void
7602b3ad188SAdrian Chadd pic_destroy(device_t dev, intptr_t xref)
7612b3ad188SAdrian Chadd {
7622b3ad188SAdrian Chadd 	struct intr_pic *pic;
7632b3ad188SAdrian Chadd 
7642b3ad188SAdrian Chadd 	mtx_lock(&pic_list_lock);
7652b3ad188SAdrian Chadd 	pic = pic_lookup_locked(dev, xref);
7662b3ad188SAdrian Chadd 	if (pic == NULL) {
7672b3ad188SAdrian Chadd 		mtx_unlock(&pic_list_lock);
7682b3ad188SAdrian Chadd 		return;
7692b3ad188SAdrian Chadd 	}
7702b3ad188SAdrian Chadd 	SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next);
7712b3ad188SAdrian Chadd 	mtx_unlock(&pic_list_lock);
7722b3ad188SAdrian Chadd 
7732b3ad188SAdrian Chadd 	free(pic, M_INTRNG);
7742b3ad188SAdrian Chadd }
7752b3ad188SAdrian Chadd #endif
7762b3ad188SAdrian Chadd /*
7772b3ad188SAdrian Chadd  *  Register interrupt controller.
7782b3ad188SAdrian Chadd  */
7799346e913SAndrew Turner struct intr_pic *
7802b3ad188SAdrian Chadd intr_pic_register(device_t dev, intptr_t xref)
7812b3ad188SAdrian Chadd {
7822b3ad188SAdrian Chadd 	struct intr_pic *pic;
7832b3ad188SAdrian Chadd 
7844be58cbaSSvatopluk Kraus 	if (dev == NULL)
7859346e913SAndrew Turner 		return (NULL);
7862b3ad188SAdrian Chadd 	pic = pic_create(dev, xref);
7872b3ad188SAdrian Chadd 	if (pic == NULL)
7889346e913SAndrew Turner 		return (NULL);
7892b3ad188SAdrian Chadd 
7903fc155dcSAndrew Turner 	pic->pic_flags |= FLAG_PIC;
7913fc155dcSAndrew Turner 
7924be58cbaSSvatopluk Kraus 	debugf("PIC %p registered for %s <dev %p, xref %x>\n", pic,
7934be58cbaSSvatopluk Kraus 	    device_get_nameunit(dev), dev, xref);
7949346e913SAndrew Turner 	return (pic);
7952b3ad188SAdrian Chadd }
7962b3ad188SAdrian Chadd 
7972b3ad188SAdrian Chadd /*
7982b3ad188SAdrian Chadd  *  Unregister interrupt controller.
7992b3ad188SAdrian Chadd  */
8002b3ad188SAdrian Chadd int
801bff6be3eSSvatopluk Kraus intr_pic_deregister(device_t dev, intptr_t xref)
8022b3ad188SAdrian Chadd {
8032b3ad188SAdrian Chadd 
8042b3ad188SAdrian Chadd 	panic("%s: not implemented", __func__);
8052b3ad188SAdrian Chadd }
8062b3ad188SAdrian Chadd 
8072b3ad188SAdrian Chadd /*
8082b3ad188SAdrian Chadd  *  Mark interrupt controller (itself) as a root one.
8092b3ad188SAdrian Chadd  *
8102b3ad188SAdrian Chadd  *  Note that only an interrupt controller can really know its position
8112b3ad188SAdrian Chadd  *  in interrupt controller's tree. So root PIC must claim itself as a root.
8122b3ad188SAdrian Chadd  *
8132b3ad188SAdrian Chadd  *  In FDT case, according to ePAPR approved version 1.1 from 08 April 2011,
8142b3ad188SAdrian Chadd  *  page 30:
8152b3ad188SAdrian Chadd  *    "The root of the interrupt tree is determined when traversal
8162b3ad188SAdrian Chadd  *     of the interrupt tree reaches an interrupt controller node without
8172b3ad188SAdrian Chadd  *     an interrupts property and thus no explicit interrupt parent."
8182b3ad188SAdrian Chadd  */
8192b3ad188SAdrian Chadd int
8202b3ad188SAdrian Chadd intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter,
8212b3ad188SAdrian Chadd     void *arg, u_int ipicount)
8222b3ad188SAdrian Chadd {
8233fc155dcSAndrew Turner 	struct intr_pic *pic;
8242b3ad188SAdrian Chadd 
8253fc155dcSAndrew Turner 	pic = pic_lookup(dev, xref);
8263fc155dcSAndrew Turner 	if (pic == NULL) {
8272b3ad188SAdrian Chadd 		device_printf(dev, "not registered\n");
8282b3ad188SAdrian Chadd 		return (EINVAL);
8292b3ad188SAdrian Chadd 	}
8303fc155dcSAndrew Turner 
8313fc155dcSAndrew Turner 	KASSERT((pic->pic_flags & FLAG_PIC) != 0,
8323fc155dcSAndrew Turner 	    ("%s: Found a non-PIC controller: %s", __func__,
8333fc155dcSAndrew Turner 	     device_get_name(pic->pic_dev)));
8343fc155dcSAndrew Turner 
8352b3ad188SAdrian Chadd 	if (filter == NULL) {
8362b3ad188SAdrian Chadd 		device_printf(dev, "filter missing\n");
8372b3ad188SAdrian Chadd 		return (EINVAL);
8382b3ad188SAdrian Chadd 	}
8392b3ad188SAdrian Chadd 
8402b3ad188SAdrian Chadd 	/*
8412b3ad188SAdrian Chadd 	 * Only one interrupt controllers could be on the root for now.
8422b3ad188SAdrian Chadd 	 * Note that we further suppose that there is not threaded interrupt
8432b3ad188SAdrian Chadd 	 * routine (handler) on the root. See intr_irq_handler().
8442b3ad188SAdrian Chadd 	 */
8455b70c08cSSvatopluk Kraus 	if (intr_irq_root_dev != NULL) {
8462b3ad188SAdrian Chadd 		device_printf(dev, "another root already set\n");
8472b3ad188SAdrian Chadd 		return (EBUSY);
8482b3ad188SAdrian Chadd 	}
8492b3ad188SAdrian Chadd 
8505b70c08cSSvatopluk Kraus 	intr_irq_root_dev = dev;
8512b3ad188SAdrian Chadd 	irq_root_filter = filter;
8522b3ad188SAdrian Chadd 	irq_root_arg = arg;
8532b3ad188SAdrian Chadd 	irq_root_ipicount = ipicount;
8542b3ad188SAdrian Chadd 
8552b3ad188SAdrian Chadd 	debugf("irq root set to %s\n", device_get_nameunit(dev));
8562b3ad188SAdrian Chadd 	return (0);
8572b3ad188SAdrian Chadd }
8582b3ad188SAdrian Chadd 
859d1605cdaSAndrew Turner /*
860d1605cdaSAndrew Turner  * Add a handler to manage a sub range of a parents interrupts.
861d1605cdaSAndrew Turner  */
862d1605cdaSAndrew Turner struct intr_pic *
863d1605cdaSAndrew Turner intr_pic_add_handler(device_t parent, struct intr_pic *pic,
864d1605cdaSAndrew Turner     intr_child_irq_filter_t *filter, void *arg, uintptr_t start,
865d1605cdaSAndrew Turner     uintptr_t length)
866d1605cdaSAndrew Turner {
867d1605cdaSAndrew Turner 	struct intr_pic *parent_pic;
868d1605cdaSAndrew Turner 	struct intr_pic_child *newchild;
869d1605cdaSAndrew Turner #ifdef INVARIANTS
870d1605cdaSAndrew Turner 	struct intr_pic_child *child;
871d1605cdaSAndrew Turner #endif
872d1605cdaSAndrew Turner 
873d1605cdaSAndrew Turner 	parent_pic = pic_lookup(parent, 0);
874d1605cdaSAndrew Turner 	if (parent_pic == NULL)
875d1605cdaSAndrew Turner 		return (NULL);
876d1605cdaSAndrew Turner 
877d1605cdaSAndrew Turner 	newchild = malloc(sizeof(*newchild), M_INTRNG, M_WAITOK | M_ZERO);
878d1605cdaSAndrew Turner 	newchild->pc_pic = pic;
879d1605cdaSAndrew Turner 	newchild->pc_filter = filter;
880d1605cdaSAndrew Turner 	newchild->pc_filter_arg = arg;
881d1605cdaSAndrew Turner 	newchild->pc_start = start;
882d1605cdaSAndrew Turner 	newchild->pc_length = length;
883d1605cdaSAndrew Turner 
884d1605cdaSAndrew Turner 	mtx_lock_spin(&parent_pic->pic_child_lock);
885d1605cdaSAndrew Turner #ifdef INVARIANTS
886d1605cdaSAndrew Turner 	SLIST_FOREACH(child, &parent_pic->pic_children, pc_next) {
887d1605cdaSAndrew Turner 		KASSERT(child->pc_pic != pic, ("%s: Adding a child PIC twice",
888d1605cdaSAndrew Turner 		    __func__));
889d1605cdaSAndrew Turner 	}
890d1605cdaSAndrew Turner #endif
891d1605cdaSAndrew Turner 	SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next);
892d1605cdaSAndrew Turner 	mtx_unlock_spin(&parent_pic->pic_child_lock);
893d1605cdaSAndrew Turner 
894d1605cdaSAndrew Turner 	return (pic);
895d1605cdaSAndrew Turner }
896d1605cdaSAndrew Turner 
897895c8b1cSMichal Meloun static int
898895c8b1cSMichal Meloun intr_resolve_irq(device_t dev, intptr_t xref, struct intr_map_data *data,
899895c8b1cSMichal Meloun     struct intr_irqsrc **isrc)
9002b3ad188SAdrian Chadd {
901bff6be3eSSvatopluk Kraus 	struct intr_pic *pic;
902895c8b1cSMichal Meloun 	struct intr_map_data_msi *msi;
903bff6be3eSSvatopluk Kraus 
904bff6be3eSSvatopluk Kraus 	if (data == NULL)
905bff6be3eSSvatopluk Kraus 		return (EINVAL);
906bff6be3eSSvatopluk Kraus 
907bff6be3eSSvatopluk Kraus 	pic = pic_lookup(dev, xref);
90815adccc6SSvatopluk Kraus 	if (pic == NULL)
909bff6be3eSSvatopluk Kraus 		return (ESRCH);
910bff6be3eSSvatopluk Kraus 
911895c8b1cSMichal Meloun 	switch (data->type) {
912895c8b1cSMichal Meloun 	case INTR_MAP_DATA_MSI:
913895c8b1cSMichal Meloun 		KASSERT((pic->pic_flags & FLAG_MSI) != 0,
914895c8b1cSMichal Meloun 		    ("%s: Found a non-MSI controller: %s", __func__,
915895c8b1cSMichal Meloun 		     device_get_name(pic->pic_dev)));
916895c8b1cSMichal Meloun 		msi = (struct intr_map_data_msi *)data;
917895c8b1cSMichal Meloun 		*isrc = msi->isrc;
918895c8b1cSMichal Meloun 		return (0);
919895c8b1cSMichal Meloun 
920895c8b1cSMichal Meloun 	default:
9213fc155dcSAndrew Turner 		KASSERT((pic->pic_flags & FLAG_PIC) != 0,
9223fc155dcSAndrew Turner 		    ("%s: Found a non-PIC controller: %s", __func__,
9233fc155dcSAndrew Turner 		     device_get_name(pic->pic_dev)));
924895c8b1cSMichal Meloun 		return (PIC_MAP_INTR(pic->pic_dev, data, isrc));
9253fc155dcSAndrew Turner 
926895c8b1cSMichal Meloun 	}
927895c8b1cSMichal Meloun }
928895c8b1cSMichal Meloun 
929895c8b1cSMichal Meloun int
930895c8b1cSMichal Meloun intr_activate_irq(device_t dev, struct resource *res)
931895c8b1cSMichal Meloun {
932895c8b1cSMichal Meloun 	device_t map_dev;
933895c8b1cSMichal Meloun 	intptr_t map_xref;
934895c8b1cSMichal Meloun 	struct intr_map_data *data;
935895c8b1cSMichal Meloun 	struct intr_irqsrc *isrc;
936895c8b1cSMichal Meloun 	u_int res_id;
937895c8b1cSMichal Meloun 	int error;
938895c8b1cSMichal Meloun 
939895c8b1cSMichal Meloun 	KASSERT(rman_get_start(res) == rman_get_end(res),
940895c8b1cSMichal Meloun 	    ("%s: more interrupts in resource", __func__));
941895c8b1cSMichal Meloun 
942895c8b1cSMichal Meloun 	res_id = (u_int)rman_get_start(res);
943895c8b1cSMichal Meloun 	if (intr_map_get_isrc(res_id) != NULL)
944895c8b1cSMichal Meloun 		panic("Attempt to double activation of resource id: %u\n",
945895c8b1cSMichal Meloun 		    res_id);
946895c8b1cSMichal Meloun 	intr_map_copy_map_data(res_id, &map_dev, &map_xref, &data);
947895c8b1cSMichal Meloun 	error = intr_resolve_irq(map_dev, map_xref, data, &isrc);
948895c8b1cSMichal Meloun 	if (error != 0) {
949895c8b1cSMichal Meloun 		free(data, M_INTRNG);
950895c8b1cSMichal Meloun 		/* XXX TODO DISCONECTED PICs */
951895c8b1cSMichal Meloun 		/* if (error == EINVAL) return(0); */
952bff6be3eSSvatopluk Kraus 		return (error);
953bff6be3eSSvatopluk Kraus 	}
954895c8b1cSMichal Meloun 	intr_map_set_isrc(res_id, isrc);
955895c8b1cSMichal Meloun 	rman_set_virtual(res, data);
956895c8b1cSMichal Meloun 	return (PIC_ACTIVATE_INTR(isrc->isrc_dev, isrc, res, data));
957bff6be3eSSvatopluk Kraus }
958bff6be3eSSvatopluk Kraus 
959bff6be3eSSvatopluk Kraus int
960895c8b1cSMichal Meloun intr_deactivate_irq(device_t dev, struct resource *res)
961bff6be3eSSvatopluk Kraus {
962bff6be3eSSvatopluk Kraus 	struct intr_map_data *data;
963bff6be3eSSvatopluk Kraus 	struct intr_irqsrc *isrc;
964895c8b1cSMichal Meloun 	u_int res_id;
965895c8b1cSMichal Meloun 	int error;
966bff6be3eSSvatopluk Kraus 
967bff6be3eSSvatopluk Kraus 	KASSERT(rman_get_start(res) == rman_get_end(res),
968bff6be3eSSvatopluk Kraus 	    ("%s: more interrupts in resource", __func__));
969bff6be3eSSvatopluk Kraus 
970895c8b1cSMichal Meloun 	res_id = (u_int)rman_get_start(res);
971895c8b1cSMichal Meloun 	isrc = intr_map_get_isrc(res_id);
972bff6be3eSSvatopluk Kraus 	if (isrc == NULL)
973895c8b1cSMichal Meloun 		panic("Attempt to deactivate non-active resource id: %u\n",
974895c8b1cSMichal Meloun 		    res_id);
975bff6be3eSSvatopluk Kraus 
976c4263292SSvatopluk Kraus 	data = rman_get_virtual(res);
977895c8b1cSMichal Meloun 	error = PIC_DEACTIVATE_INTR(isrc->isrc_dev, isrc, res, data);
978895c8b1cSMichal Meloun 	intr_map_set_isrc(res_id, NULL);
979895c8b1cSMichal Meloun 	rman_set_virtual(res, NULL);
980895c8b1cSMichal Meloun 	free(data, M_INTRNG);
981895c8b1cSMichal Meloun 	return (error);
982bff6be3eSSvatopluk Kraus }
983bff6be3eSSvatopluk Kraus 
984bff6be3eSSvatopluk Kraus int
985bff6be3eSSvatopluk Kraus intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt,
986bff6be3eSSvatopluk Kraus     driver_intr_t hand, void *arg, int flags, void **cookiep)
987bff6be3eSSvatopluk Kraus {
988bff6be3eSSvatopluk Kraus 	int error;
989bff6be3eSSvatopluk Kraus 	struct intr_map_data *data;
990bff6be3eSSvatopluk Kraus 	struct intr_irqsrc *isrc;
991bff6be3eSSvatopluk Kraus 	const char *name;
992895c8b1cSMichal Meloun 	u_int res_id;
993bff6be3eSSvatopluk Kraus 
994bff6be3eSSvatopluk Kraus 	KASSERT(rman_get_start(res) == rman_get_end(res),
995bff6be3eSSvatopluk Kraus 	    ("%s: more interrupts in resource", __func__));
996bff6be3eSSvatopluk Kraus 
997895c8b1cSMichal Meloun 	res_id = (u_int)rman_get_start(res);
998895c8b1cSMichal Meloun 	isrc = intr_map_get_isrc(res_id);
999895c8b1cSMichal Meloun 	if (isrc == NULL) {
1000895c8b1cSMichal Meloun 		/* XXX TODO DISCONECTED PICs */
1001bff6be3eSSvatopluk Kraus 		return (EINVAL);
1002895c8b1cSMichal Meloun 	}
10032b3ad188SAdrian Chadd 
1004c4263292SSvatopluk Kraus 	data = rman_get_virtual(res);
10052b3ad188SAdrian Chadd 	name = device_get_nameunit(dev);
10062b3ad188SAdrian Chadd 
10072b3ad188SAdrian Chadd #ifdef INTR_SOLO
10082b3ad188SAdrian Chadd 	/*
1009e3043798SPedro F. Giffuni 	 * Standard handling is done through MI interrupt framework. However,
10102b3ad188SAdrian Chadd 	 * some interrupts could request solely own special handling. This
10112b3ad188SAdrian Chadd 	 * non standard handling can be used for interrupt controllers without
10122b3ad188SAdrian Chadd 	 * handler (filter only), so in case that interrupt controllers are
10132b3ad188SAdrian Chadd 	 * chained, MI interrupt framework is called only in leaf controller.
10142b3ad188SAdrian Chadd 	 *
10152b3ad188SAdrian Chadd 	 * Note that root interrupt controller routine is served as well,
10162b3ad188SAdrian Chadd 	 * however in intr_irq_handler(), i.e. main system dispatch routine.
10172b3ad188SAdrian Chadd 	 */
10182b3ad188SAdrian Chadd 	if (flags & INTR_SOLO && hand != NULL) {
10192b3ad188SAdrian Chadd 		debugf("irq %u cannot solo on %s\n", irq, name);
10202b3ad188SAdrian Chadd 		return (EINVAL);
10212b3ad188SAdrian Chadd 	}
10222b3ad188SAdrian Chadd 
10232b3ad188SAdrian Chadd 	if (flags & INTR_SOLO) {
10242b3ad188SAdrian Chadd 		error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt,
10252b3ad188SAdrian Chadd 		    arg, cookiep);
10262b3ad188SAdrian Chadd 		debugf("irq %u setup filter error %d on %s\n", irq, error,
10272b3ad188SAdrian Chadd 		    name);
10282b3ad188SAdrian Chadd 	} else
10292b3ad188SAdrian Chadd #endif
10302b3ad188SAdrian Chadd 		{
10312b3ad188SAdrian Chadd 		error = isrc_add_handler(isrc, name, filt, hand, arg, flags,
10322b3ad188SAdrian Chadd 		    cookiep);
10332b3ad188SAdrian Chadd 		debugf("irq %u add handler error %d on %s\n", irq, error, name);
10342b3ad188SAdrian Chadd 	}
10352b3ad188SAdrian Chadd 	if (error != 0)
10362b3ad188SAdrian Chadd 		return (error);
10372b3ad188SAdrian Chadd 
10382b3ad188SAdrian Chadd 	mtx_lock(&isrc_table_lock);
1039bff6be3eSSvatopluk Kraus 	error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data);
1040bff6be3eSSvatopluk Kraus 	if (error == 0) {
10412b3ad188SAdrian Chadd 		isrc->isrc_handlers++;
1042bff6be3eSSvatopluk Kraus 		if (isrc->isrc_handlers == 1)
10432b3ad188SAdrian Chadd 			PIC_ENABLE_INTR(isrc->isrc_dev, isrc);
10442b3ad188SAdrian Chadd 	}
10452b3ad188SAdrian Chadd 	mtx_unlock(&isrc_table_lock);
1046bff6be3eSSvatopluk Kraus 	if (error != 0)
1047bff6be3eSSvatopluk Kraus 		intr_event_remove_handler(*cookiep);
1048bff6be3eSSvatopluk Kraus 	return (error);
10492b3ad188SAdrian Chadd }
10502b3ad188SAdrian Chadd 
10512b3ad188SAdrian Chadd int
1052bff6be3eSSvatopluk Kraus intr_teardown_irq(device_t dev, struct resource *res, void *cookie)
10532b3ad188SAdrian Chadd {
10542b3ad188SAdrian Chadd 	int error;
1055bff6be3eSSvatopluk Kraus 	struct intr_map_data *data;
1056bff6be3eSSvatopluk Kraus 	struct intr_irqsrc *isrc;
1057895c8b1cSMichal Meloun 	u_int res_id;
10582b3ad188SAdrian Chadd 
1059bff6be3eSSvatopluk Kraus 	KASSERT(rman_get_start(res) == rman_get_end(res),
1060bff6be3eSSvatopluk Kraus 	    ("%s: more interrupts in resource", __func__));
1061bff6be3eSSvatopluk Kraus 
1062895c8b1cSMichal Meloun 	res_id = (u_int)rman_get_start(res);
1063895c8b1cSMichal Meloun 	isrc = intr_map_get_isrc(res_id);
10642b3ad188SAdrian Chadd 	if (isrc == NULL || isrc->isrc_handlers == 0)
10652b3ad188SAdrian Chadd 		return (EINVAL);
1066bff6be3eSSvatopluk Kraus 
1067c4263292SSvatopluk Kraus 	data = rman_get_virtual(res);
1068c4263292SSvatopluk Kraus 
1069169e6abdSSvatopluk Kraus #ifdef INTR_SOLO
10702b3ad188SAdrian Chadd 	if (isrc->isrc_filter != NULL) {
10712b3ad188SAdrian Chadd 		if (isrc != cookie)
10722b3ad188SAdrian Chadd 			return (EINVAL);
10732b3ad188SAdrian Chadd 
10742b3ad188SAdrian Chadd 		mtx_lock(&isrc_table_lock);
10752b3ad188SAdrian Chadd 		isrc->isrc_filter = NULL;
10762b3ad188SAdrian Chadd 		isrc->isrc_arg = NULL;
10772b3ad188SAdrian Chadd 		isrc->isrc_handlers = 0;
10782b3ad188SAdrian Chadd 		PIC_DISABLE_INTR(isrc->isrc_dev, isrc);
1079bff6be3eSSvatopluk Kraus 		PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data);
10802b3ad188SAdrian Chadd 		isrc_update_name(isrc, NULL);
10812b3ad188SAdrian Chadd 		mtx_unlock(&isrc_table_lock);
10822b3ad188SAdrian Chadd 		return (0);
10832b3ad188SAdrian Chadd 	}
1084169e6abdSSvatopluk Kraus #endif
10852b3ad188SAdrian Chadd 	if (isrc != intr_handler_source(cookie))
10862b3ad188SAdrian Chadd 		return (EINVAL);
10872b3ad188SAdrian Chadd 
10882b3ad188SAdrian Chadd 	error = intr_event_remove_handler(cookie);
10892b3ad188SAdrian Chadd 	if (error == 0) {
10902b3ad188SAdrian Chadd 		mtx_lock(&isrc_table_lock);
10912b3ad188SAdrian Chadd 		isrc->isrc_handlers--;
1092bff6be3eSSvatopluk Kraus 		if (isrc->isrc_handlers == 0)
10932b3ad188SAdrian Chadd 			PIC_DISABLE_INTR(isrc->isrc_dev, isrc);
1094bff6be3eSSvatopluk Kraus 		PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data);
10952b3ad188SAdrian Chadd 		intrcnt_updatename(isrc);
10962b3ad188SAdrian Chadd 		mtx_unlock(&isrc_table_lock);
10972b3ad188SAdrian Chadd 	}
10982b3ad188SAdrian Chadd 	return (error);
10992b3ad188SAdrian Chadd }
11002b3ad188SAdrian Chadd 
11012b3ad188SAdrian Chadd int
1102bff6be3eSSvatopluk Kraus intr_describe_irq(device_t dev, struct resource *res, void *cookie,
1103bff6be3eSSvatopluk Kraus     const char *descr)
11042b3ad188SAdrian Chadd {
11052b3ad188SAdrian Chadd 	int error;
1106bff6be3eSSvatopluk Kraus 	struct intr_irqsrc *isrc;
1107895c8b1cSMichal Meloun 	u_int res_id;
11082b3ad188SAdrian Chadd 
1109bff6be3eSSvatopluk Kraus 	KASSERT(rman_get_start(res) == rman_get_end(res),
1110bff6be3eSSvatopluk Kraus 	    ("%s: more interrupts in resource", __func__));
1111bff6be3eSSvatopluk Kraus 
1112895c8b1cSMichal Meloun 	res_id = (u_int)rman_get_start(res);
1113895c8b1cSMichal Meloun 	isrc = intr_map_get_isrc(res_id);
11142b3ad188SAdrian Chadd 	if (isrc == NULL || isrc->isrc_handlers == 0)
11152b3ad188SAdrian Chadd 		return (EINVAL);
1116169e6abdSSvatopluk Kraus #ifdef INTR_SOLO
11172b3ad188SAdrian Chadd 	if (isrc->isrc_filter != NULL) {
11182b3ad188SAdrian Chadd 		if (isrc != cookie)
11192b3ad188SAdrian Chadd 			return (EINVAL);
11202b3ad188SAdrian Chadd 
11212b3ad188SAdrian Chadd 		mtx_lock(&isrc_table_lock);
11222b3ad188SAdrian Chadd 		isrc_update_name(isrc, descr);
11232b3ad188SAdrian Chadd 		mtx_unlock(&isrc_table_lock);
11242b3ad188SAdrian Chadd 		return (0);
11252b3ad188SAdrian Chadd 	}
1126169e6abdSSvatopluk Kraus #endif
11272b3ad188SAdrian Chadd 	error = intr_event_describe_handler(isrc->isrc_event, cookie, descr);
11282b3ad188SAdrian Chadd 	if (error == 0) {
11292b3ad188SAdrian Chadd 		mtx_lock(&isrc_table_lock);
11302b3ad188SAdrian Chadd 		intrcnt_updatename(isrc);
11312b3ad188SAdrian Chadd 		mtx_unlock(&isrc_table_lock);
11322b3ad188SAdrian Chadd 	}
11332b3ad188SAdrian Chadd 	return (error);
11342b3ad188SAdrian Chadd }
11352b3ad188SAdrian Chadd 
11362b3ad188SAdrian Chadd #ifdef SMP
11372b3ad188SAdrian Chadd int
1138bff6be3eSSvatopluk Kraus intr_bind_irq(device_t dev, struct resource *res, int cpu)
11392b3ad188SAdrian Chadd {
11402b3ad188SAdrian Chadd 	struct intr_irqsrc *isrc;
1141895c8b1cSMichal Meloun 	u_int res_id;
11422b3ad188SAdrian Chadd 
1143bff6be3eSSvatopluk Kraus 	KASSERT(rman_get_start(res) == rman_get_end(res),
1144bff6be3eSSvatopluk Kraus 	    ("%s: more interrupts in resource", __func__));
1145bff6be3eSSvatopluk Kraus 
1146895c8b1cSMichal Meloun 	res_id = (u_int)rman_get_start(res);
1147895c8b1cSMichal Meloun 	isrc = intr_map_get_isrc(res_id);
11482b3ad188SAdrian Chadd 	if (isrc == NULL || isrc->isrc_handlers == 0)
11492b3ad188SAdrian Chadd 		return (EINVAL);
1150169e6abdSSvatopluk Kraus #ifdef INTR_SOLO
11512b3ad188SAdrian Chadd 	if (isrc->isrc_filter != NULL)
11522b3ad188SAdrian Chadd 		return (intr_isrc_assign_cpu(isrc, cpu));
1153169e6abdSSvatopluk Kraus #endif
11542b3ad188SAdrian Chadd 	return (intr_event_bind(isrc->isrc_event, cpu));
11552b3ad188SAdrian Chadd }
11562b3ad188SAdrian Chadd 
11572b3ad188SAdrian Chadd /*
11582b3ad188SAdrian Chadd  * Return the CPU that the next interrupt source should use.
11592b3ad188SAdrian Chadd  * For now just returns the next CPU according to round-robin.
11602b3ad188SAdrian Chadd  */
11612b3ad188SAdrian Chadd u_int
11622b3ad188SAdrian Chadd intr_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask)
11632b3ad188SAdrian Chadd {
11642b3ad188SAdrian Chadd 
11652b3ad188SAdrian Chadd 	if (!irq_assign_cpu || mp_ncpus == 1)
11662b3ad188SAdrian Chadd 		return (PCPU_GET(cpuid));
11672b3ad188SAdrian Chadd 
11682b3ad188SAdrian Chadd 	do {
11692b3ad188SAdrian Chadd 		last_cpu++;
11702b3ad188SAdrian Chadd 		if (last_cpu > mp_maxid)
11712b3ad188SAdrian Chadd 			last_cpu = 0;
11722b3ad188SAdrian Chadd 	} while (!CPU_ISSET(last_cpu, cpumask));
11732b3ad188SAdrian Chadd 	return (last_cpu);
11742b3ad188SAdrian Chadd }
11752b3ad188SAdrian Chadd 
11762b3ad188SAdrian Chadd /*
11772b3ad188SAdrian Chadd  *  Distribute all the interrupt sources among the available
11782b3ad188SAdrian Chadd  *  CPUs once the AP's have been launched.
11792b3ad188SAdrian Chadd  */
11802b3ad188SAdrian Chadd static void
11812b3ad188SAdrian Chadd intr_irq_shuffle(void *arg __unused)
11822b3ad188SAdrian Chadd {
11832b3ad188SAdrian Chadd 	struct intr_irqsrc *isrc;
11842b3ad188SAdrian Chadd 	u_int i;
11852b3ad188SAdrian Chadd 
11862b3ad188SAdrian Chadd 	if (mp_ncpus == 1)
11872b3ad188SAdrian Chadd 		return;
11882b3ad188SAdrian Chadd 
11892b3ad188SAdrian Chadd 	mtx_lock(&isrc_table_lock);
11902b3ad188SAdrian Chadd 	irq_assign_cpu = TRUE;
11912b3ad188SAdrian Chadd 	for (i = 0; i < NIRQ; i++) {
11922b3ad188SAdrian Chadd 		isrc = irq_sources[i];
11932b3ad188SAdrian Chadd 		if (isrc == NULL || isrc->isrc_handlers == 0 ||
1194cf55df9fSSvatopluk Kraus 		    isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI))
11952b3ad188SAdrian Chadd 			continue;
11962b3ad188SAdrian Chadd 
11972b3ad188SAdrian Chadd 		if (isrc->isrc_event != NULL &&
11982b3ad188SAdrian Chadd 		    isrc->isrc_flags & INTR_ISRCF_BOUND &&
11992b3ad188SAdrian Chadd 		    isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1)
12002b3ad188SAdrian Chadd 			panic("%s: CPU inconsistency", __func__);
12012b3ad188SAdrian Chadd 
12022b3ad188SAdrian Chadd 		if ((isrc->isrc_flags & INTR_ISRCF_BOUND) == 0)
12032b3ad188SAdrian Chadd 			CPU_ZERO(&isrc->isrc_cpu); /* start again */
12042b3ad188SAdrian Chadd 
12052b3ad188SAdrian Chadd 		/*
12062b3ad188SAdrian Chadd 		 * We are in wicked position here if the following call fails
12072b3ad188SAdrian Chadd 		 * for bound ISRC. The best thing we can do is to clear
12082b3ad188SAdrian Chadd 		 * isrc_cpu so inconsistency with ie_cpu will be detectable.
12092b3ad188SAdrian Chadd 		 */
1210bff6be3eSSvatopluk Kraus 		if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0)
12112b3ad188SAdrian Chadd 			CPU_ZERO(&isrc->isrc_cpu);
12122b3ad188SAdrian Chadd 	}
12132b3ad188SAdrian Chadd 	mtx_unlock(&isrc_table_lock);
12142b3ad188SAdrian Chadd }
12152b3ad188SAdrian Chadd SYSINIT(intr_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, intr_irq_shuffle, NULL);
12162b3ad188SAdrian Chadd 
12172b3ad188SAdrian Chadd #else
12182b3ad188SAdrian Chadd u_int
12192b3ad188SAdrian Chadd intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask)
12202b3ad188SAdrian Chadd {
12212b3ad188SAdrian Chadd 
12222b3ad188SAdrian Chadd 	return (PCPU_GET(cpuid));
12232b3ad188SAdrian Chadd }
12242b3ad188SAdrian Chadd #endif
12252b3ad188SAdrian Chadd 
12263fc155dcSAndrew Turner /*
1227895c8b1cSMichal Meloun  * Allocate memory for new intr_map_data structure.
1228895c8b1cSMichal Meloun  * Initialize common fields.
1229895c8b1cSMichal Meloun  */
1230895c8b1cSMichal Meloun struct intr_map_data *
1231895c8b1cSMichal Meloun intr_alloc_map_data(enum intr_map_data_type type, size_t len, int flags)
1232895c8b1cSMichal Meloun {
1233895c8b1cSMichal Meloun 	struct intr_map_data *data;
1234895c8b1cSMichal Meloun 
1235895c8b1cSMichal Meloun 	data = malloc(len, M_INTRNG, flags);
1236895c8b1cSMichal Meloun 	data->type = type;
1237895c8b1cSMichal Meloun 	data->len = len;
1238895c8b1cSMichal Meloun 	return (data);
1239895c8b1cSMichal Meloun }
1240895c8b1cSMichal Meloun 
1241895c8b1cSMichal Meloun void intr_free_intr_map_data(struct intr_map_data *data)
1242895c8b1cSMichal Meloun {
1243895c8b1cSMichal Meloun 
1244895c8b1cSMichal Meloun 	free(data, M_INTRNG);
1245895c8b1cSMichal Meloun }
1246895c8b1cSMichal Meloun 
1247895c8b1cSMichal Meloun 
1248895c8b1cSMichal Meloun /*
12493fc155dcSAndrew Turner  *  Register a MSI/MSI-X interrupt controller
12503fc155dcSAndrew Turner  */
12513fc155dcSAndrew Turner int
12523fc155dcSAndrew Turner intr_msi_register(device_t dev, intptr_t xref)
12533fc155dcSAndrew Turner {
12543fc155dcSAndrew Turner 	struct intr_pic *pic;
12553fc155dcSAndrew Turner 
12563fc155dcSAndrew Turner 	if (dev == NULL)
12573fc155dcSAndrew Turner 		return (EINVAL);
12583fc155dcSAndrew Turner 	pic = pic_create(dev, xref);
12593fc155dcSAndrew Turner 	if (pic == NULL)
12603fc155dcSAndrew Turner 		return (ENOMEM);
12613fc155dcSAndrew Turner 
12623fc155dcSAndrew Turner 	pic->pic_flags |= FLAG_MSI;
12633fc155dcSAndrew Turner 
12643fc155dcSAndrew Turner 	debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic,
12653fc155dcSAndrew Turner 	    device_get_nameunit(dev), dev, (uintmax_t)xref);
12663fc155dcSAndrew Turner 	return (0);
12673fc155dcSAndrew Turner }
12683fc155dcSAndrew Turner 
12693fc155dcSAndrew Turner int
12703fc155dcSAndrew Turner intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count,
12713fc155dcSAndrew Turner     int maxcount, int *irqs)
12723fc155dcSAndrew Turner {
12733fc155dcSAndrew Turner 	struct intr_irqsrc **isrc;
12743fc155dcSAndrew Turner 	struct intr_pic *pic;
12753fc155dcSAndrew Turner 	device_t pdev;
1276895c8b1cSMichal Meloun 	struct intr_map_data_msi *msi;
12773fc155dcSAndrew Turner 	int err, i;
12783fc155dcSAndrew Turner 
12793fc155dcSAndrew Turner 	pic = pic_lookup(NULL, xref);
12803fc155dcSAndrew Turner 	if (pic == NULL)
12813fc155dcSAndrew Turner 		return (ESRCH);
12823fc155dcSAndrew Turner 
12833fc155dcSAndrew Turner 	KASSERT((pic->pic_flags & FLAG_MSI) != 0,
12843fc155dcSAndrew Turner 	    ("%s: Found a non-MSI controller: %s", __func__,
12853fc155dcSAndrew Turner 	     device_get_name(pic->pic_dev)));
12863fc155dcSAndrew Turner 
12873fc155dcSAndrew Turner 	isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK);
12883fc155dcSAndrew Turner 	err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc);
1289895c8b1cSMichal Meloun 	if (err != 0) {
1290895c8b1cSMichal Meloun 		free(isrc, M_INTRNG);
1291895c8b1cSMichal Meloun 		return (err);
12923fc155dcSAndrew Turner 	}
12933fc155dcSAndrew Turner 
1294895c8b1cSMichal Meloun 	for (i = 0; i < count; i++) {
1295895c8b1cSMichal Meloun 		msi = (struct intr_map_data_msi *)intr_alloc_map_data(
1296895c8b1cSMichal Meloun 		    INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO);
1297895c8b1cSMichal Meloun 		msi-> isrc = isrc[i];
1298895c8b1cSMichal Meloun 		irqs[i] = intr_map_irq(pic->pic_dev, xref,
1299895c8b1cSMichal Meloun 		    (struct intr_map_data *)msi);
1300895c8b1cSMichal Meloun 
1301895c8b1cSMichal Meloun 	}
13023fc155dcSAndrew Turner 	free(isrc, M_INTRNG);
13033fc155dcSAndrew Turner 
13043fc155dcSAndrew Turner 	return (err);
13053fc155dcSAndrew Turner }
13063fc155dcSAndrew Turner 
13073fc155dcSAndrew Turner int
13083fc155dcSAndrew Turner intr_release_msi(device_t pci, device_t child, intptr_t xref, int count,
13093fc155dcSAndrew Turner     int *irqs)
13103fc155dcSAndrew Turner {
13113fc155dcSAndrew Turner 	struct intr_irqsrc **isrc;
13123fc155dcSAndrew Turner 	struct intr_pic *pic;
1313*609b0fe9SOleksandr Tymoshenko 	struct intr_map_data_msi *msi;
13143fc155dcSAndrew Turner 	int i, err;
13153fc155dcSAndrew Turner 
13163fc155dcSAndrew Turner 	pic = pic_lookup(NULL, xref);
13173fc155dcSAndrew Turner 	if (pic == NULL)
13183fc155dcSAndrew Turner 		return (ESRCH);
13193fc155dcSAndrew Turner 
13203fc155dcSAndrew Turner 	KASSERT((pic->pic_flags & FLAG_MSI) != 0,
13213fc155dcSAndrew Turner 	    ("%s: Found a non-MSI controller: %s", __func__,
13223fc155dcSAndrew Turner 	     device_get_name(pic->pic_dev)));
13233fc155dcSAndrew Turner 
13243fc155dcSAndrew Turner 	isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK);
13253fc155dcSAndrew Turner 
1326*609b0fe9SOleksandr Tymoshenko 	for (i = 0; i < count; i++) {
1327*609b0fe9SOleksandr Tymoshenko 		msi = (struct intr_map_data_msi *)
1328*609b0fe9SOleksandr Tymoshenko 		    intr_map_get_map_data(irqs[i]);
1329*609b0fe9SOleksandr Tymoshenko 		KASSERT(msi->hdr.type == INTR_MAP_DATA_MSI,
1330*609b0fe9SOleksandr Tymoshenko 		    ("%s: irq %d map data is not MSI", __func__,
1331*609b0fe9SOleksandr Tymoshenko 		    irqs[i]));
1332*609b0fe9SOleksandr Tymoshenko 		isrc[i] = msi->isrc;
1333*609b0fe9SOleksandr Tymoshenko 	}
13343fc155dcSAndrew Turner 
13353fc155dcSAndrew Turner 	err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc);
1336895c8b1cSMichal Meloun 
1337895c8b1cSMichal Meloun 	for (i = 0; i < count; i++) {
1338895c8b1cSMichal Meloun 		if (isrc[i] != NULL)
1339895c8b1cSMichal Meloun 			intr_unmap_irq(irqs[i]);
1340895c8b1cSMichal Meloun 	}
1341895c8b1cSMichal Meloun 
13423fc155dcSAndrew Turner 	free(isrc, M_INTRNG);
13433fc155dcSAndrew Turner 	return (err);
13443fc155dcSAndrew Turner }
13453fc155dcSAndrew Turner 
13463fc155dcSAndrew Turner int
13473fc155dcSAndrew Turner intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq)
13483fc155dcSAndrew Turner {
13493fc155dcSAndrew Turner 	struct intr_irqsrc *isrc;
13503fc155dcSAndrew Turner 	struct intr_pic *pic;
13513fc155dcSAndrew Turner 	device_t pdev;
1352895c8b1cSMichal Meloun 	struct intr_map_data_msi *msi;
13533fc155dcSAndrew Turner 	int err;
13543fc155dcSAndrew Turner 
13553fc155dcSAndrew Turner 	pic = pic_lookup(NULL, xref);
13563fc155dcSAndrew Turner 	if (pic == NULL)
13573fc155dcSAndrew Turner 		return (ESRCH);
13583fc155dcSAndrew Turner 
13593fc155dcSAndrew Turner 	KASSERT((pic->pic_flags & FLAG_MSI) != 0,
13603fc155dcSAndrew Turner 	    ("%s: Found a non-MSI controller: %s", __func__,
13613fc155dcSAndrew Turner 	     device_get_name(pic->pic_dev)));
13623fc155dcSAndrew Turner 
1363895c8b1cSMichal Meloun 
13643fc155dcSAndrew Turner 	err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc);
13653fc155dcSAndrew Turner 	if (err != 0)
13663fc155dcSAndrew Turner 		return (err);
13673fc155dcSAndrew Turner 
1368895c8b1cSMichal Meloun 	msi = (struct intr_map_data_msi *)intr_alloc_map_data(
1369895c8b1cSMichal Meloun 		    INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO);
1370895c8b1cSMichal Meloun 	msi->isrc = isrc;
1371895c8b1cSMichal Meloun 	*irq = intr_map_irq(pic->pic_dev, xref, (struct intr_map_data *)msi);
13723fc155dcSAndrew Turner 	return (0);
13733fc155dcSAndrew Turner }
13743fc155dcSAndrew Turner 
13753fc155dcSAndrew Turner int
13763fc155dcSAndrew Turner intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq)
13773fc155dcSAndrew Turner {
13783fc155dcSAndrew Turner 	struct intr_irqsrc *isrc;
13793fc155dcSAndrew Turner 	struct intr_pic *pic;
1380*609b0fe9SOleksandr Tymoshenko 	struct intr_map_data_msi *msi;
13813fc155dcSAndrew Turner 	int err;
13823fc155dcSAndrew Turner 
13833fc155dcSAndrew Turner 	pic = pic_lookup(NULL, xref);
13843fc155dcSAndrew Turner 	if (pic == NULL)
13853fc155dcSAndrew Turner 		return (ESRCH);
13863fc155dcSAndrew Turner 
13873fc155dcSAndrew Turner 	KASSERT((pic->pic_flags & FLAG_MSI) != 0,
13883fc155dcSAndrew Turner 	    ("%s: Found a non-MSI controller: %s", __func__,
13893fc155dcSAndrew Turner 	     device_get_name(pic->pic_dev)));
13903fc155dcSAndrew Turner 
1391*609b0fe9SOleksandr Tymoshenko 	msi = (struct intr_map_data_msi *)
1392*609b0fe9SOleksandr Tymoshenko 	    intr_map_get_map_data(irq);
1393*609b0fe9SOleksandr Tymoshenko 	KASSERT(msi->hdr.type == INTR_MAP_DATA_MSI,
1394*609b0fe9SOleksandr Tymoshenko 	    ("%s: irq %d map data is not MSI", __func__,
1395*609b0fe9SOleksandr Tymoshenko 	    irq));
1396*609b0fe9SOleksandr Tymoshenko 	isrc = msi->isrc;
1397895c8b1cSMichal Meloun 	if (isrc == NULL) {
1398895c8b1cSMichal Meloun 		intr_unmap_irq(irq);
13993fc155dcSAndrew Turner 		return (EINVAL);
1400895c8b1cSMichal Meloun 	}
14013fc155dcSAndrew Turner 
14023fc155dcSAndrew Turner 	err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc);
1403895c8b1cSMichal Meloun 	intr_unmap_irq(irq);
1404895c8b1cSMichal Meloun 
14053fc155dcSAndrew Turner 	return (err);
14063fc155dcSAndrew Turner }
14073fc155dcSAndrew Turner 
14083fc155dcSAndrew Turner int
14093fc155dcSAndrew Turner intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq,
14103fc155dcSAndrew Turner     uint64_t *addr, uint32_t *data)
14113fc155dcSAndrew Turner {
14123fc155dcSAndrew Turner 	struct intr_irqsrc *isrc;
14133fc155dcSAndrew Turner 	struct intr_pic *pic;
14143fc155dcSAndrew Turner 	int err;
14153fc155dcSAndrew Turner 
14163fc155dcSAndrew Turner 	pic = pic_lookup(NULL, xref);
14173fc155dcSAndrew Turner 	if (pic == NULL)
14183fc155dcSAndrew Turner 		return (ESRCH);
14193fc155dcSAndrew Turner 
14203fc155dcSAndrew Turner 	KASSERT((pic->pic_flags & FLAG_MSI) != 0,
14213fc155dcSAndrew Turner 	    ("%s: Found a non-MSI controller: %s", __func__,
14223fc155dcSAndrew Turner 	     device_get_name(pic->pic_dev)));
14233fc155dcSAndrew Turner 
1424895c8b1cSMichal Meloun 	isrc = intr_map_get_isrc(irq);
14253fc155dcSAndrew Turner 	if (isrc == NULL)
14263fc155dcSAndrew Turner 		return (EINVAL);
14273fc155dcSAndrew Turner 
14283fc155dcSAndrew Turner 	err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data);
14293fc155dcSAndrew Turner 	return (err);
14303fc155dcSAndrew Turner }
14313fc155dcSAndrew Turner 
14323fc155dcSAndrew Turner 
14332b3ad188SAdrian Chadd void dosoftints(void);
14342b3ad188SAdrian Chadd void
14352b3ad188SAdrian Chadd dosoftints(void)
14362b3ad188SAdrian Chadd {
14372b3ad188SAdrian Chadd }
14382b3ad188SAdrian Chadd 
14392b3ad188SAdrian Chadd #ifdef SMP
14402b3ad188SAdrian Chadd /*
14412b3ad188SAdrian Chadd  *  Init interrupt controller on another CPU.
14422b3ad188SAdrian Chadd  */
14432b3ad188SAdrian Chadd void
14442b3ad188SAdrian Chadd intr_pic_init_secondary(void)
14452b3ad188SAdrian Chadd {
14462b3ad188SAdrian Chadd 
14472b3ad188SAdrian Chadd 	/*
14482b3ad188SAdrian Chadd 	 * QQQ: Only root PIC is aware of other CPUs ???
14492b3ad188SAdrian Chadd 	 */
14505b70c08cSSvatopluk Kraus 	KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
14512b3ad188SAdrian Chadd 
14522b3ad188SAdrian Chadd 	//mtx_lock(&isrc_table_lock);
14535b70c08cSSvatopluk Kraus 	PIC_INIT_SECONDARY(intr_irq_root_dev);
14542b3ad188SAdrian Chadd 	//mtx_unlock(&isrc_table_lock);
14552b3ad188SAdrian Chadd }
14562b3ad188SAdrian Chadd #endif
14572b3ad188SAdrian Chadd 
14582b3ad188SAdrian Chadd #ifdef DDB
14592b3ad188SAdrian Chadd DB_SHOW_COMMAND(irqs, db_show_irqs)
14602b3ad188SAdrian Chadd {
14612b3ad188SAdrian Chadd 	u_int i, irqsum;
1462bff6be3eSSvatopluk Kraus 	u_long num;
14632b3ad188SAdrian Chadd 	struct intr_irqsrc *isrc;
14642b3ad188SAdrian Chadd 
14652b3ad188SAdrian Chadd 	for (irqsum = 0, i = 0; i < NIRQ; i++) {
14662b3ad188SAdrian Chadd 		isrc = irq_sources[i];
14672b3ad188SAdrian Chadd 		if (isrc == NULL)
14682b3ad188SAdrian Chadd 			continue;
14692b3ad188SAdrian Chadd 
1470bff6be3eSSvatopluk Kraus 		num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0;
14712b3ad188SAdrian Chadd 		db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i,
14722b3ad188SAdrian Chadd 		    isrc->isrc_name, isrc->isrc_cpu.__bits[0],
1473bff6be3eSSvatopluk Kraus 		    isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num);
1474bff6be3eSSvatopluk Kraus 		irqsum += num;
14752b3ad188SAdrian Chadd 	}
14762b3ad188SAdrian Chadd 	db_printf("irq total %u\n", irqsum);
14772b3ad188SAdrian Chadd }
14782b3ad188SAdrian Chadd #endif
1479895c8b1cSMichal Meloun 
1480895c8b1cSMichal Meloun /*
1481895c8b1cSMichal Meloun  * Interrupt mapping table functions.
1482895c8b1cSMichal Meloun  *
1483895c8b1cSMichal Meloun  * Please, keep this part separately, it can be transformed to
1484895c8b1cSMichal Meloun  * extension of standard resources.
1485895c8b1cSMichal Meloun  */
1486895c8b1cSMichal Meloun struct intr_map_entry
1487895c8b1cSMichal Meloun {
1488895c8b1cSMichal Meloun 	device_t 		dev;
1489895c8b1cSMichal Meloun 	intptr_t 		xref;
1490895c8b1cSMichal Meloun 	struct intr_map_data 	*map_data;
1491895c8b1cSMichal Meloun 	struct intr_irqsrc 	*isrc;
1492895c8b1cSMichal Meloun 	/* XXX TODO DISCONECTED PICs */
1493895c8b1cSMichal Meloun 	/*int			flags */
1494895c8b1cSMichal Meloun };
1495895c8b1cSMichal Meloun 
1496895c8b1cSMichal Meloun /* XXX Convert irq_map[] to dynamicaly expandable one. */
1497895c8b1cSMichal Meloun static struct intr_map_entry *irq_map[2 * NIRQ];
1498895c8b1cSMichal Meloun static int irq_map_count = nitems(irq_map);
1499895c8b1cSMichal Meloun static int irq_map_first_free_idx;
1500895c8b1cSMichal Meloun static struct mtx irq_map_lock;
1501895c8b1cSMichal Meloun 
1502895c8b1cSMichal Meloun static struct intr_irqsrc *
1503895c8b1cSMichal Meloun intr_map_get_isrc(u_int res_id)
1504895c8b1cSMichal Meloun {
1505895c8b1cSMichal Meloun 	struct intr_irqsrc *isrc;
1506895c8b1cSMichal Meloun 
1507895c8b1cSMichal Meloun 	mtx_lock(&irq_map_lock);
1508895c8b1cSMichal Meloun 	if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL)) {
1509895c8b1cSMichal Meloun 		mtx_unlock(&irq_map_lock);
1510895c8b1cSMichal Meloun 		return (NULL);
1511895c8b1cSMichal Meloun 	}
1512895c8b1cSMichal Meloun 	isrc = irq_map[res_id]->isrc;
1513895c8b1cSMichal Meloun 	mtx_unlock(&irq_map_lock);
1514895c8b1cSMichal Meloun 	return (isrc);
1515895c8b1cSMichal Meloun }
1516895c8b1cSMichal Meloun 
1517895c8b1cSMichal Meloun static void
1518895c8b1cSMichal Meloun intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc)
1519895c8b1cSMichal Meloun {
1520895c8b1cSMichal Meloun 
1521895c8b1cSMichal Meloun 	mtx_lock(&irq_map_lock);
1522895c8b1cSMichal Meloun 	if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL)) {
1523895c8b1cSMichal Meloun 		mtx_unlock(&irq_map_lock);
1524895c8b1cSMichal Meloun 		return;
1525895c8b1cSMichal Meloun 	}
1526895c8b1cSMichal Meloun 	irq_map[res_id]->isrc = isrc;
1527895c8b1cSMichal Meloun 	mtx_unlock(&irq_map_lock);
1528895c8b1cSMichal Meloun }
1529895c8b1cSMichal Meloun 
1530895c8b1cSMichal Meloun /*
1531895c8b1cSMichal Meloun  * Get a copy of intr_map_entry data
1532895c8b1cSMichal Meloun  */
1533*609b0fe9SOleksandr Tymoshenko static struct intr_map_data *
1534*609b0fe9SOleksandr Tymoshenko intr_map_get_map_data(u_int res_id)
1535*609b0fe9SOleksandr Tymoshenko {
1536*609b0fe9SOleksandr Tymoshenko 	struct intr_map_data *data;
1537*609b0fe9SOleksandr Tymoshenko 
1538*609b0fe9SOleksandr Tymoshenko 	data = NULL;
1539*609b0fe9SOleksandr Tymoshenko 	mtx_lock(&irq_map_lock);
1540*609b0fe9SOleksandr Tymoshenko 	if (res_id >= irq_map_count || irq_map[res_id] == NULL)
1541*609b0fe9SOleksandr Tymoshenko 		panic("Attempt to copy invalid resource id: %u\n", res_id);
1542*609b0fe9SOleksandr Tymoshenko 	data = irq_map[res_id]->map_data;
1543*609b0fe9SOleksandr Tymoshenko 	mtx_unlock(&irq_map_lock);
1544*609b0fe9SOleksandr Tymoshenko 
1545*609b0fe9SOleksandr Tymoshenko 	return (data);
1546*609b0fe9SOleksandr Tymoshenko }
1547*609b0fe9SOleksandr Tymoshenko 
1548*609b0fe9SOleksandr Tymoshenko /*
1549*609b0fe9SOleksandr Tymoshenko  * Get a copy of intr_map_entry data
1550*609b0fe9SOleksandr Tymoshenko  */
1551895c8b1cSMichal Meloun static void
1552895c8b1cSMichal Meloun intr_map_copy_map_data(u_int res_id, device_t *map_dev, intptr_t *map_xref,
1553895c8b1cSMichal Meloun     struct intr_map_data **data)
1554895c8b1cSMichal Meloun {
1555895c8b1cSMichal Meloun 	size_t len;
1556895c8b1cSMichal Meloun 
1557895c8b1cSMichal Meloun 	len = 0;
1558895c8b1cSMichal Meloun 	mtx_lock(&irq_map_lock);
1559895c8b1cSMichal Meloun 	if (res_id >= irq_map_count || irq_map[res_id] == NULL)
1560895c8b1cSMichal Meloun 		panic("Attempt to copy invalid resource id: %u\n", res_id);
1561895c8b1cSMichal Meloun 	if (irq_map[res_id]->map_data != NULL)
1562895c8b1cSMichal Meloun 		len = irq_map[res_id]->map_data->len;
1563895c8b1cSMichal Meloun 	mtx_unlock(&irq_map_lock);
1564895c8b1cSMichal Meloun 
1565895c8b1cSMichal Meloun 	if (len == 0)
1566895c8b1cSMichal Meloun 		*data = NULL;
1567895c8b1cSMichal Meloun 	else
1568895c8b1cSMichal Meloun 		*data = malloc(len, M_INTRNG, M_WAITOK | M_ZERO);
1569895c8b1cSMichal Meloun 	mtx_lock(&irq_map_lock);
1570895c8b1cSMichal Meloun 	if (irq_map[res_id] == NULL)
1571895c8b1cSMichal Meloun 		panic("Attempt to copy invalid resource id: %u\n", res_id);
1572895c8b1cSMichal Meloun 	if (len != 0) {
1573895c8b1cSMichal Meloun 		if (len != irq_map[res_id]->map_data->len)
1574895c8b1cSMichal Meloun 			panic("Resource id: %u has changed.\n", res_id);
1575895c8b1cSMichal Meloun 		memcpy(*data, irq_map[res_id]->map_data, len);
1576895c8b1cSMichal Meloun 	}
1577895c8b1cSMichal Meloun 	*map_dev = irq_map[res_id]->dev;
1578895c8b1cSMichal Meloun 	*map_xref = irq_map[res_id]->xref;
1579895c8b1cSMichal Meloun 	mtx_unlock(&irq_map_lock);
1580895c8b1cSMichal Meloun }
1581895c8b1cSMichal Meloun 
1582895c8b1cSMichal Meloun 
1583895c8b1cSMichal Meloun /*
1584895c8b1cSMichal Meloun  * Allocate and fill new entry in irq_map table.
1585895c8b1cSMichal Meloun  */
1586895c8b1cSMichal Meloun u_int
1587895c8b1cSMichal Meloun intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data)
1588895c8b1cSMichal Meloun {
1589895c8b1cSMichal Meloun 	u_int i;
1590895c8b1cSMichal Meloun 	struct intr_map_entry *entry;
1591895c8b1cSMichal Meloun 
1592895c8b1cSMichal Meloun 	/* Prepare new entry first. */
1593895c8b1cSMichal Meloun 	entry = malloc(sizeof(*entry), M_INTRNG, M_WAITOK | M_ZERO);
1594895c8b1cSMichal Meloun 
1595895c8b1cSMichal Meloun 	entry->dev = dev;
1596895c8b1cSMichal Meloun 	entry->xref = xref;
1597895c8b1cSMichal Meloun 	entry->map_data = data;
1598895c8b1cSMichal Meloun 	entry->isrc = NULL;
1599895c8b1cSMichal Meloun 
1600895c8b1cSMichal Meloun 	mtx_lock(&irq_map_lock);
1601895c8b1cSMichal Meloun 	for (i = irq_map_first_free_idx; i < irq_map_count; i++) {
1602895c8b1cSMichal Meloun 		if (irq_map[i] == NULL) {
1603895c8b1cSMichal Meloun 			irq_map[i] = entry;
1604895c8b1cSMichal Meloun 			irq_map_first_free_idx = i + 1;
1605895c8b1cSMichal Meloun 			mtx_unlock(&irq_map_lock);
1606895c8b1cSMichal Meloun 			return (i);
1607895c8b1cSMichal Meloun 		}
1608895c8b1cSMichal Meloun 	}
1609895c8b1cSMichal Meloun 	mtx_unlock(&irq_map_lock);
1610895c8b1cSMichal Meloun 
1611895c8b1cSMichal Meloun 	/* XXX Expand irq_map table */
1612895c8b1cSMichal Meloun 	panic("IRQ mapping table is full.");
1613895c8b1cSMichal Meloun }
1614895c8b1cSMichal Meloun 
1615895c8b1cSMichal Meloun /*
1616895c8b1cSMichal Meloun  * Remove and free mapping entry.
1617895c8b1cSMichal Meloun  */
1618895c8b1cSMichal Meloun void
1619895c8b1cSMichal Meloun intr_unmap_irq(u_int res_id)
1620895c8b1cSMichal Meloun {
1621895c8b1cSMichal Meloun 	struct intr_map_entry *entry;
1622895c8b1cSMichal Meloun 
1623895c8b1cSMichal Meloun 	mtx_lock(&irq_map_lock);
1624895c8b1cSMichal Meloun 	if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL))
1625895c8b1cSMichal Meloun 		panic("Attempt to unmap invalid resource id: %u\n", res_id);
1626895c8b1cSMichal Meloun 	entry = irq_map[res_id];
1627895c8b1cSMichal Meloun 	irq_map[res_id] = NULL;
1628895c8b1cSMichal Meloun 	irq_map_first_free_idx = res_id;
1629895c8b1cSMichal Meloun 	mtx_unlock(&irq_map_lock);
1630895c8b1cSMichal Meloun 	intr_free_intr_map_data(entry->map_data);
1631895c8b1cSMichal Meloun 	free(entry, M_INTRNG);
1632895c8b1cSMichal Meloun }
1633895c8b1cSMichal Meloun 
1634895c8b1cSMichal Meloun /*
1635895c8b1cSMichal Meloun  * Clone mapping entry.
1636895c8b1cSMichal Meloun  */
1637895c8b1cSMichal Meloun u_int
1638895c8b1cSMichal Meloun intr_map_clone_irq(u_int old_res_id)
1639895c8b1cSMichal Meloun {
1640895c8b1cSMichal Meloun 	device_t map_dev;
1641895c8b1cSMichal Meloun 	intptr_t map_xref;
1642895c8b1cSMichal Meloun 	struct intr_map_data *data;
1643895c8b1cSMichal Meloun 
1644895c8b1cSMichal Meloun 	intr_map_copy_map_data(old_res_id, &map_dev, &map_xref, &data);
1645895c8b1cSMichal Meloun 	return (intr_map_irq(map_dev, map_xref, data));
1646895c8b1cSMichal Meloun }
1647895c8b1cSMichal Meloun 
1648895c8b1cSMichal Meloun static void
1649895c8b1cSMichal Meloun intr_map_init(void *dummy __unused)
1650895c8b1cSMichal Meloun {
1651895c8b1cSMichal Meloun 
1652895c8b1cSMichal Meloun 	mtx_init(&irq_map_lock, "intr map table", NULL, MTX_DEF);
1653895c8b1cSMichal Meloun }
1654895c8b1cSMichal Meloun SYSINIT(intr_map_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_map_init, NULL);
1655