xref: /onnv-gate/usr/src/uts/sun4/io/ivintr.c (revision 9038:b9402db13c0f)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52973Sgovinda  * Common Development and Distribution License (the "License").
62973Sgovinda  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*9038SEvan.Yan@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * Interrupt Vector Table Configuration
280Sstevel@tonic-gate  */
290Sstevel@tonic-gate 
302973Sgovinda #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/cpuvar.h>
320Sstevel@tonic-gate #include <sys/ivintr.h>
330Sstevel@tonic-gate #include <sys/intreg.h>
340Sstevel@tonic-gate #include <sys/cmn_err.h>
350Sstevel@tonic-gate #include <sys/privregs.h>
360Sstevel@tonic-gate #include <sys/sunddi.h>
370Sstevel@tonic-gate 
382973Sgovinda /*
392973Sgovinda  * Allocate an Interrupt Vector Table and some interrupt vector data structures
402973Sgovinda  * for the reserved pool as part of the startup code. First try to allocate an
412973Sgovinda  * interrupt vector data structure from the reserved pool, otherwise allocate it
422973Sgovinda  * using kmem cache method.
432973Sgovinda  */
442973Sgovinda static	kmutex_t intr_vec_mutex;	/* Protect interrupt vector table */
450Sstevel@tonic-gate 
460Sstevel@tonic-gate /*
472973Sgovinda  * Global softint linked list - used by softint mdb dcmd.
480Sstevel@tonic-gate  */
492973Sgovinda static	kmutex_t softint_mutex;		/* Protect global softint linked list */
502973Sgovinda intr_vec_t	*softint_list = NULL;
512973Sgovinda 
522973Sgovinda /* Reserved pool for interrupt allocation */
532973Sgovinda intr_vec_t	*intr_vec_pool = NULL;	/* For HW and single target SW intrs */
542973Sgovinda intr_vecx_t	*intr_vecx_pool = NULL;	/* For multi target SW intrs */
553273Sgovinda static	kmutex_t intr_vec_pool_mutex;	/* Protect interrupt vector pool */
562973Sgovinda 
572973Sgovinda /* Kmem cache handle for interrupt allocation */
582973Sgovinda kmem_cache_t	*intr_vec_cache = NULL;	/* For HW and single target SW intrs */
59*9038SEvan.Yan@Sun.COM static	kmutex_t intr_vec_cache_mutex;	/* Protect intr_vec_cache usage */
600Sstevel@tonic-gate 
610Sstevel@tonic-gate /*
622973Sgovinda  * init_ivintr() - Initialize an Interrupt Vector Table.
630Sstevel@tonic-gate  */
642973Sgovinda void
init_ivintr()652973Sgovinda init_ivintr()
662973Sgovinda {
672973Sgovinda 	mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL);
682973Sgovinda 	mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL);
693273Sgovinda 	mutex_init(&intr_vec_pool_mutex, NULL, MUTEX_DRIVER, NULL);
70*9038SEvan.Yan@Sun.COM 	mutex_init(&intr_vec_cache_mutex, NULL, MUTEX_DRIVER, NULL);
712973Sgovinda 
722973Sgovinda 	/*
732973Sgovinda 	 * Initialize the reserved interrupt vector data structure pools
742973Sgovinda 	 * used for hardware and software interrupts.
752973Sgovinda 	 */
762973Sgovinda 	intr_vec_pool = (intr_vec_t *)((caddr_t)intr_vec_table +
772973Sgovinda 	    (MAXIVNUM * sizeof (intr_vec_t *)));
782973Sgovinda 	intr_vecx_pool = (intr_vecx_t *)((caddr_t)intr_vec_pool +
792973Sgovinda 	    (MAX_RSVD_IV * sizeof (intr_vec_t)));
802973Sgovinda 
812973Sgovinda 	bzero(intr_vec_table, MAXIVNUM * sizeof (intr_vec_t *));
822973Sgovinda 	bzero(intr_vec_pool, MAX_RSVD_IV * sizeof (intr_vec_t));
832973Sgovinda 	bzero(intr_vecx_pool, MAX_RSVD_IVX * sizeof (intr_vecx_t));
842973Sgovinda }
850Sstevel@tonic-gate 
860Sstevel@tonic-gate /*
872973Sgovinda  * fini_ivintr() - Uninitialize an Interrupt Vector Table.
880Sstevel@tonic-gate  */
892973Sgovinda void
fini_ivintr()902973Sgovinda fini_ivintr()
912973Sgovinda {
92*9038SEvan.Yan@Sun.COM 	mutex_enter(&intr_vec_cache_mutex);
93*9038SEvan.Yan@Sun.COM 	if (intr_vec_cache) {
942973Sgovinda 		kmem_cache_destroy(intr_vec_cache);
95*9038SEvan.Yan@Sun.COM 		intr_vec_cache = NULL;
96*9038SEvan.Yan@Sun.COM 	}
97*9038SEvan.Yan@Sun.COM 	mutex_exit(&intr_vec_cache_mutex);
982973Sgovinda 
993273Sgovinda 	mutex_destroy(&intr_vec_pool_mutex);
1003273Sgovinda 	mutex_destroy(&softint_mutex);
1012973Sgovinda 	mutex_destroy(&intr_vec_mutex);
102*9038SEvan.Yan@Sun.COM 	mutex_destroy(&intr_vec_cache_mutex);
1032973Sgovinda }
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate /*
1062973Sgovinda  * iv_alloc() - Allocate an interrupt vector data structure.
1072973Sgovinda  *
1082973Sgovinda  * This function allocates an interrupt vector data structure for hardware
1092973Sgovinda  * and single or multi target software interrupts either from the reserved
1102973Sgovinda  * pool or using kmem cache method.
1110Sstevel@tonic-gate  */
1122973Sgovinda static intr_vec_t *
iv_alloc(softint_type_t type)1132973Sgovinda iv_alloc(softint_type_t type)
1142973Sgovinda {
1152973Sgovinda 	intr_vec_t	*iv_p;
1162973Sgovinda 	int		i, count;
1172973Sgovinda 
1182973Sgovinda 	count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV;
1192973Sgovinda 
1202973Sgovinda 	/*
1212973Sgovinda 	 * First try to allocate an interrupt vector data structure from the
1222973Sgovinda 	 * reserved pool, otherwise allocate it using kmem_cache_alloc().
1232973Sgovinda 	 */
1243273Sgovinda 	mutex_enter(&intr_vec_pool_mutex);
1252973Sgovinda 	for (i = 0; i < count; i++) {
1262973Sgovinda 		iv_p = (type == SOFTINT_MT) ?
1272973Sgovinda 		    (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i];
1280Sstevel@tonic-gate 
1293273Sgovinda 		if (iv_p->iv_pil == 0) {
1303273Sgovinda 			iv_p->iv_pil = 1;	/* Default PIL */
1312973Sgovinda 			break;
1323273Sgovinda 		}
1332973Sgovinda 	}
1343273Sgovinda 	mutex_exit(&intr_vec_pool_mutex);
1352973Sgovinda 
1362973Sgovinda 	if (i < count)
1372973Sgovinda 		return (iv_p);
1382973Sgovinda 
1392973Sgovinda 	if (type == SOFTINT_MT)
1402973Sgovinda 		cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi "
1412973Sgovinda 		    "target software interrupts, %d", MAX_RSVD_IVX);
1422973Sgovinda 
1432973Sgovinda 	/*
1442973Sgovinda 	 * If the interrupt vector data structure reserved pool is already
1452973Sgovinda 	 * exhausted, then allocate an interrupt vector data structure using
1462973Sgovinda 	 * kmem_cache_alloc(), but only for the hardware and single software
1472973Sgovinda 	 * interrupts. Create a kmem cache for the interrupt allocation,
1482973Sgovinda 	 * if it is not already available.
1492973Sgovinda 	 */
150*9038SEvan.Yan@Sun.COM 	mutex_enter(&intr_vec_cache_mutex);
1512973Sgovinda 	if (intr_vec_cache == NULL)
1522973Sgovinda 		intr_vec_cache = kmem_cache_create("intr_vec_cache",
1532973Sgovinda 		    sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
154*9038SEvan.Yan@Sun.COM 	mutex_exit(&intr_vec_cache_mutex);
1552973Sgovinda 
1562973Sgovinda 	iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP);
1572973Sgovinda 	bzero(iv_p, sizeof (intr_vec_t));
1583273Sgovinda 	iv_p->iv_flags =  IV_CACHE_ALLOC;
1592973Sgovinda 
1602973Sgovinda 	return (iv_p);
1612973Sgovinda }
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate /*
1642973Sgovinda  * iv_free() - Free an interrupt vector data structure.
1650Sstevel@tonic-gate  */
1662973Sgovinda static void
iv_free(intr_vec_t * iv_p)1672973Sgovinda iv_free(intr_vec_t *iv_p)
1680Sstevel@tonic-gate {
1692973Sgovinda 	if (iv_p->iv_flags & IV_CACHE_ALLOC) {
1702973Sgovinda 		ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT));
1712973Sgovinda 		kmem_cache_free(intr_vec_cache, iv_p);
1720Sstevel@tonic-gate 	} else {
1733273Sgovinda 		mutex_enter(&intr_vec_pool_mutex);
1743273Sgovinda 		bzero(iv_p, (iv_p->iv_flags & IV_SOFTINT_MT) ?
1753273Sgovinda 		    sizeof (intr_vecx_t) : sizeof (intr_vec_t));
1763273Sgovinda 		mutex_exit(&intr_vec_pool_mutex);
1770Sstevel@tonic-gate 	}
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate /*
1812973Sgovinda  * add_ivintr() - Add an interrupt handler to the system
1820Sstevel@tonic-gate  */
1830Sstevel@tonic-gate int
add_ivintr(uint_t inum,uint_t pil,intrfunc intr_handler,caddr_t intr_arg1,caddr_t intr_arg2,caddr_t intr_payload)1840Sstevel@tonic-gate add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler,
1852973Sgovinda     caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload)
1860Sstevel@tonic-gate {
1872973Sgovinda 	intr_vec_t	*iv_p, *new_iv_p;
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	if (inum >= MAXIVNUM || pil > PIL_MAX)
1900Sstevel@tonic-gate 		return (EINVAL);
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	ASSERT((uintptr_t)intr_handler > KERNELBASE);
1932973Sgovinda 
1940Sstevel@tonic-gate 	/* Make sure the payload buffer address is 64 bit aligned */
1950Sstevel@tonic-gate 	VERIFY(((uint64_t)intr_payload & 0x7) == 0);
1960Sstevel@tonic-gate 
1972973Sgovinda 	new_iv_p = iv_alloc(SOFTINT_ST);
1982973Sgovinda 	mutex_enter(&intr_vec_mutex);
1992973Sgovinda 
2002973Sgovinda 	for (iv_p = (intr_vec_t *)intr_vec_table[inum];
2012973Sgovinda 	    iv_p; iv_p = iv_p->iv_vec_next) {
2022973Sgovinda 		if (iv_p->iv_pil == pil) {
2032973Sgovinda 			mutex_exit(&intr_vec_mutex);
2042973Sgovinda 			iv_free(new_iv_p);
2052973Sgovinda 			return (EINVAL);
2062973Sgovinda 		}
2072973Sgovinda 	}
2080Sstevel@tonic-gate 
2092973Sgovinda 	ASSERT(iv_p == NULL);
2100Sstevel@tonic-gate 
2112973Sgovinda 	new_iv_p->iv_handler = intr_handler;
2122973Sgovinda 	new_iv_p->iv_arg1 = intr_arg1;
2132973Sgovinda 	new_iv_p->iv_arg2 = intr_arg2;
2142973Sgovinda 	new_iv_p->iv_payload_buf = intr_payload;
2152973Sgovinda 	new_iv_p->iv_pil = (ushort_t)pil;
2162973Sgovinda 	new_iv_p->iv_inum = inum;
2172973Sgovinda 
2182973Sgovinda 	new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum];
2192973Sgovinda 	intr_vec_table[inum] = (uint64_t)new_iv_p;
2202973Sgovinda 
2212973Sgovinda 	mutex_exit(&intr_vec_mutex);
2220Sstevel@tonic-gate 	return (0);
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate /*
2262973Sgovinda  * rem_ivintr() - Remove an interrupt handler from the system
2270Sstevel@tonic-gate  */
2282973Sgovinda int
rem_ivintr(uint_t inum,uint_t pil)2292973Sgovinda rem_ivintr(uint_t inum, uint_t pil)
2300Sstevel@tonic-gate {
2312973Sgovinda 	intr_vec_t	*iv_p, *prev_iv_p;
2320Sstevel@tonic-gate 
2332973Sgovinda 	if (inum >= MAXIVNUM || pil > PIL_MAX)
2342973Sgovinda 		return (EINVAL);
2350Sstevel@tonic-gate 
2362973Sgovinda 	mutex_enter(&intr_vec_mutex);
2370Sstevel@tonic-gate 
2382973Sgovinda 	for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum];
2392973Sgovinda 	    iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next)
2402973Sgovinda 		if (iv_p->iv_pil == pil)
2412973Sgovinda 			break;
2422973Sgovinda 
2432973Sgovinda 	if (iv_p == NULL) {
2442973Sgovinda 		mutex_exit(&intr_vec_mutex);
2452973Sgovinda 		return (EIO);
2460Sstevel@tonic-gate 	}
2470Sstevel@tonic-gate 
2482973Sgovinda 	ASSERT(iv_p->iv_pil_next == NULL);
2492973Sgovinda 
2502973Sgovinda 	if (prev_iv_p == iv_p)
2512973Sgovinda 		intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next;
2522973Sgovinda 	else
2532973Sgovinda 		prev_iv_p->iv_vec_next = iv_p->iv_vec_next;
2542973Sgovinda 
2552973Sgovinda 	mutex_exit(&intr_vec_mutex);
2562973Sgovinda 
2572973Sgovinda 	iv_free(iv_p);
2582973Sgovinda 	return (0);
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate /*
2620Sstevel@tonic-gate  * add_softintr() - add a software interrupt handler to the system
2630Sstevel@tonic-gate  */
2642973Sgovinda uint64_t
add_softintr(uint_t pil,softintrfunc intr_handler,caddr_t intr_arg1,softint_type_t type)2652973Sgovinda add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1,
2662973Sgovinda     softint_type_t type)
2670Sstevel@tonic-gate {
2682973Sgovinda 	intr_vec_t	*iv_p;
2690Sstevel@tonic-gate 
2702973Sgovinda 	if (pil > PIL_MAX)
2712973Sgovinda 		return (NULL);
2722973Sgovinda 
2732973Sgovinda 	iv_p = iv_alloc(type);
2740Sstevel@tonic-gate 
2752973Sgovinda 	iv_p->iv_handler = (intrfunc)intr_handler;
2762973Sgovinda 	iv_p->iv_arg1 = intr_arg1;
2772973Sgovinda 	iv_p->iv_pil = (ushort_t)pil;
2782973Sgovinda 	if (type == SOFTINT_MT)
2792973Sgovinda 		iv_p->iv_flags |=  IV_SOFTINT_MT;
2800Sstevel@tonic-gate 
2812973Sgovinda 	mutex_enter(&softint_mutex);
2822973Sgovinda 	if (softint_list)
2832973Sgovinda 		iv_p->iv_vec_next = softint_list;
2842973Sgovinda 	softint_list = iv_p;
2852973Sgovinda 	mutex_exit(&softint_mutex);
2860Sstevel@tonic-gate 
2872973Sgovinda 	return ((uint64_t)iv_p);
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate /*
2910Sstevel@tonic-gate  * rem_softintr() - remove a software interrupt handler from the system
2920Sstevel@tonic-gate  */
2932973Sgovinda int
rem_softintr(uint64_t softint_id)2942973Sgovinda rem_softintr(uint64_t softint_id)
2950Sstevel@tonic-gate {
2962973Sgovinda 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
2972973Sgovinda 
2982973Sgovinda 	ASSERT(iv_p != NULL);
2992973Sgovinda 
3002973Sgovinda 	if (iv_p->iv_flags & IV_SOFTINT_PEND)
3012973Sgovinda 		return (EIO);
3022973Sgovinda 
3032973Sgovinda 	ASSERT(iv_p->iv_pil_next == NULL);
3040Sstevel@tonic-gate 
3052973Sgovinda 	mutex_enter(&softint_mutex);
3062973Sgovinda 	if (softint_list == iv_p) {
3072973Sgovinda 		softint_list = iv_p->iv_vec_next;
3082973Sgovinda 	} else {
3092973Sgovinda 		intr_vec_t	*list = softint_list;
3102973Sgovinda 
3112973Sgovinda 		while (list && (list->iv_vec_next != iv_p))
3122973Sgovinda 			list = list->iv_vec_next;
3132973Sgovinda 
3142973Sgovinda 		list->iv_vec_next = iv_p->iv_vec_next;
3152973Sgovinda 	}
3162973Sgovinda 	mutex_exit(&softint_mutex);
3172973Sgovinda 
3182973Sgovinda 	iv_free(iv_p);
3192973Sgovinda 	return (0);
3200Sstevel@tonic-gate }
3210Sstevel@tonic-gate 
3222973Sgovinda /*
3232973Sgovinda  * update_softint_arg2() - Update softint arg2.
3242973Sgovinda  *
3252973Sgovinda  * NOTE: Do not grab any mutex in this function since it may get called
3262973Sgovinda  *	 from the high-level interrupt context.
3272973Sgovinda  */
3280Sstevel@tonic-gate int
update_softint_arg2(uint64_t softint_id,caddr_t intr_arg2)3292973Sgovinda update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2)
3300Sstevel@tonic-gate {
3312973Sgovinda 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
3320Sstevel@tonic-gate 
3332973Sgovinda 	ASSERT(iv_p != NULL);
3340Sstevel@tonic-gate 
3352973Sgovinda 	if (iv_p->iv_flags & IV_SOFTINT_PEND)
3362973Sgovinda 		return (EIO);
3370Sstevel@tonic-gate 
3382973Sgovinda 	iv_p->iv_arg2 = intr_arg2;
3392973Sgovinda 	return (0);
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate 
3422973Sgovinda /*
3432973Sgovinda  * update_softint_pri() - Update softint priority.
3442973Sgovinda  */
3450Sstevel@tonic-gate int
update_softint_pri(uint64_t softint_id,uint_t pil)3462973Sgovinda update_softint_pri(uint64_t softint_id, uint_t pil)
3470Sstevel@tonic-gate {
3482973Sgovinda 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
3490Sstevel@tonic-gate 
3502973Sgovinda 	ASSERT(iv_p != NULL);
3510Sstevel@tonic-gate 
3522973Sgovinda 	if (pil > PIL_MAX)
3532973Sgovinda 		return (EINVAL);
3542973Sgovinda 
3552973Sgovinda 	iv_p->iv_pil = pil;
3562973Sgovinda 	return (0);
3570Sstevel@tonic-gate }
358