xref: /onnv-gate/usr/src/uts/sun4/io/ivintr.c (revision 3273)
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 /*
222973Sgovinda  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Interrupt Vector Table Configuration
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
322973Sgovinda #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/cpuvar.h>
340Sstevel@tonic-gate #include <sys/ivintr.h>
350Sstevel@tonic-gate #include <sys/intreg.h>
360Sstevel@tonic-gate #include <sys/cmn_err.h>
370Sstevel@tonic-gate #include <sys/privregs.h>
380Sstevel@tonic-gate #include <sys/sunddi.h>
390Sstevel@tonic-gate 
402973Sgovinda /*
412973Sgovinda  * Allocate an Interrupt Vector Table and some interrupt vector data structures
422973Sgovinda  * for the reserved pool as part of the startup code. First try to allocate an
432973Sgovinda  * interrupt vector data structure from the reserved pool, otherwise allocate it
442973Sgovinda  * using kmem cache method.
452973Sgovinda  */
462973Sgovinda static	kmutex_t intr_vec_mutex;	/* Protect interrupt vector table */
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /*
492973Sgovinda  * Global softint linked list - used by softint mdb dcmd.
500Sstevel@tonic-gate  */
512973Sgovinda static	kmutex_t softint_mutex;		/* Protect global softint linked list */
522973Sgovinda intr_vec_t	*softint_list = NULL;
532973Sgovinda 
542973Sgovinda /* Reserved pool for interrupt allocation */
552973Sgovinda intr_vec_t	*intr_vec_pool = NULL;	/* For HW and single target SW intrs */
562973Sgovinda intr_vecx_t	*intr_vecx_pool = NULL;	/* For multi target SW intrs */
57*3273Sgovinda static	kmutex_t intr_vec_pool_mutex;	/* Protect interrupt vector pool */
582973Sgovinda 
592973Sgovinda /* Kmem cache handle for interrupt allocation */
602973Sgovinda kmem_cache_t	*intr_vec_cache = NULL;	/* For HW and single target SW intrs */
610Sstevel@tonic-gate 
620Sstevel@tonic-gate /*
632973Sgovinda  * init_ivintr() - Initialize an Interrupt Vector Table.
640Sstevel@tonic-gate  */
652973Sgovinda void
662973Sgovinda init_ivintr()
672973Sgovinda {
682973Sgovinda 	mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL);
692973Sgovinda 	mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL);
70*3273Sgovinda 	mutex_init(&intr_vec_pool_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
902973Sgovinda fini_ivintr()
912973Sgovinda {
922973Sgovinda 	if (intr_vec_cache)
932973Sgovinda 		kmem_cache_destroy(intr_vec_cache);
942973Sgovinda 
95*3273Sgovinda 	mutex_destroy(&intr_vec_pool_mutex);
96*3273Sgovinda 	mutex_destroy(&softint_mutex);
972973Sgovinda 	mutex_destroy(&intr_vec_mutex);
982973Sgovinda }
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate /*
1012973Sgovinda  * iv_alloc() - Allocate an interrupt vector data structure.
1022973Sgovinda  *
1032973Sgovinda  * This function allocates an interrupt vector data structure for hardware
1042973Sgovinda  * and single or multi target software interrupts either from the reserved
1052973Sgovinda  * pool or using kmem cache method.
1060Sstevel@tonic-gate  */
1072973Sgovinda static intr_vec_t *
1082973Sgovinda iv_alloc(softint_type_t type)
1092973Sgovinda {
1102973Sgovinda 	intr_vec_t	*iv_p;
1112973Sgovinda 	int		i, count;
1122973Sgovinda 
1132973Sgovinda 	count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV;
1142973Sgovinda 
1152973Sgovinda 	/*
1162973Sgovinda 	 * First try to allocate an interrupt vector data structure from the
1172973Sgovinda 	 * reserved pool, otherwise allocate it using kmem_cache_alloc().
1182973Sgovinda 	 */
119*3273Sgovinda 	mutex_enter(&intr_vec_pool_mutex);
1202973Sgovinda 	for (i = 0; i < count; i++) {
1212973Sgovinda 		iv_p = (type == SOFTINT_MT) ?
1222973Sgovinda 		    (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i];
1230Sstevel@tonic-gate 
124*3273Sgovinda 		if (iv_p->iv_pil == 0) {
125*3273Sgovinda 			iv_p->iv_pil = 1;	/* Default PIL */
1262973Sgovinda 			break;
127*3273Sgovinda 		}
1282973Sgovinda 	}
129*3273Sgovinda 	mutex_exit(&intr_vec_pool_mutex);
1302973Sgovinda 
1312973Sgovinda 	if (i < count)
1322973Sgovinda 		return (iv_p);
1332973Sgovinda 
1342973Sgovinda 	if (type == SOFTINT_MT)
1352973Sgovinda 		cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi "
1362973Sgovinda 		    "target software interrupts, %d", MAX_RSVD_IVX);
1372973Sgovinda 
1382973Sgovinda 	/*
1392973Sgovinda 	 * If the interrupt vector data structure reserved pool is already
1402973Sgovinda 	 * exhausted, then allocate an interrupt vector data structure using
1412973Sgovinda 	 * kmem_cache_alloc(), but only for the hardware and single software
1422973Sgovinda 	 * interrupts. Create a kmem cache for the interrupt allocation,
1432973Sgovinda 	 * if it is not already available.
1442973Sgovinda 	 */
1452973Sgovinda 	if (intr_vec_cache == NULL)
1462973Sgovinda 		intr_vec_cache = kmem_cache_create("intr_vec_cache",
1472973Sgovinda 		    sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
1482973Sgovinda 
1492973Sgovinda 	iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP);
1502973Sgovinda 	bzero(iv_p, sizeof (intr_vec_t));
151*3273Sgovinda 	iv_p->iv_flags =  IV_CACHE_ALLOC;
1522973Sgovinda 
1532973Sgovinda 	return (iv_p);
1542973Sgovinda }
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate /*
1572973Sgovinda  * iv_free() - Free an interrupt vector data structure.
1580Sstevel@tonic-gate  */
1592973Sgovinda static void
1602973Sgovinda iv_free(intr_vec_t *iv_p)
1610Sstevel@tonic-gate {
1622973Sgovinda 	if (iv_p->iv_flags & IV_CACHE_ALLOC) {
1632973Sgovinda 		ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT));
1642973Sgovinda 		kmem_cache_free(intr_vec_cache, iv_p);
1650Sstevel@tonic-gate 	} else {
166*3273Sgovinda 		mutex_enter(&intr_vec_pool_mutex);
167*3273Sgovinda 		bzero(iv_p, (iv_p->iv_flags & IV_SOFTINT_MT) ?
168*3273Sgovinda 		    sizeof (intr_vecx_t) : sizeof (intr_vec_t));
169*3273Sgovinda 		mutex_exit(&intr_vec_pool_mutex);
1700Sstevel@tonic-gate 	}
1710Sstevel@tonic-gate }
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate /*
1742973Sgovinda  * add_ivintr() - Add an interrupt handler to the system
1750Sstevel@tonic-gate  */
1760Sstevel@tonic-gate int
1770Sstevel@tonic-gate add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler,
1782973Sgovinda     caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload)
1790Sstevel@tonic-gate {
1802973Sgovinda 	intr_vec_t	*iv_p, *new_iv_p;
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	if (inum >= MAXIVNUM || pil > PIL_MAX)
1830Sstevel@tonic-gate 		return (EINVAL);
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	ASSERT((uintptr_t)intr_handler > KERNELBASE);
1862973Sgovinda 
1870Sstevel@tonic-gate 	/* Make sure the payload buffer address is 64 bit aligned */
1880Sstevel@tonic-gate 	VERIFY(((uint64_t)intr_payload & 0x7) == 0);
1890Sstevel@tonic-gate 
1902973Sgovinda 	new_iv_p = iv_alloc(SOFTINT_ST);
1912973Sgovinda 	mutex_enter(&intr_vec_mutex);
1922973Sgovinda 
1932973Sgovinda 	for (iv_p = (intr_vec_t *)intr_vec_table[inum];
1942973Sgovinda 	    iv_p; iv_p = iv_p->iv_vec_next) {
1952973Sgovinda 		if (iv_p->iv_pil == pil) {
1962973Sgovinda 			mutex_exit(&intr_vec_mutex);
1972973Sgovinda 			iv_free(new_iv_p);
1982973Sgovinda 			return (EINVAL);
1992973Sgovinda 		}
2002973Sgovinda 	}
2010Sstevel@tonic-gate 
2022973Sgovinda 	ASSERT(iv_p == NULL);
2030Sstevel@tonic-gate 
2042973Sgovinda 	new_iv_p->iv_handler = intr_handler;
2052973Sgovinda 	new_iv_p->iv_arg1 = intr_arg1;
2062973Sgovinda 	new_iv_p->iv_arg2 = intr_arg2;
2072973Sgovinda 	new_iv_p->iv_payload_buf = intr_payload;
2082973Sgovinda 	new_iv_p->iv_pil = (ushort_t)pil;
2092973Sgovinda 	new_iv_p->iv_inum = inum;
2102973Sgovinda 
2112973Sgovinda 	new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum];
2122973Sgovinda 	intr_vec_table[inum] = (uint64_t)new_iv_p;
2132973Sgovinda 
2142973Sgovinda 	mutex_exit(&intr_vec_mutex);
2150Sstevel@tonic-gate 	return (0);
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate /*
2192973Sgovinda  * rem_ivintr() - Remove an interrupt handler from the system
2200Sstevel@tonic-gate  */
2212973Sgovinda int
2222973Sgovinda rem_ivintr(uint_t inum, uint_t pil)
2230Sstevel@tonic-gate {
2242973Sgovinda 	intr_vec_t	*iv_p, *prev_iv_p;
2250Sstevel@tonic-gate 
2262973Sgovinda 	if (inum >= MAXIVNUM || pil > PIL_MAX)
2272973Sgovinda 		return (EINVAL);
2280Sstevel@tonic-gate 
2292973Sgovinda 	mutex_enter(&intr_vec_mutex);
2300Sstevel@tonic-gate 
2312973Sgovinda 	for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum];
2322973Sgovinda 	    iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next)
2332973Sgovinda 		if (iv_p->iv_pil == pil)
2342973Sgovinda 			break;
2352973Sgovinda 
2362973Sgovinda 	if (iv_p == NULL) {
2372973Sgovinda 		mutex_exit(&intr_vec_mutex);
2382973Sgovinda 		return (EIO);
2390Sstevel@tonic-gate 	}
2400Sstevel@tonic-gate 
2412973Sgovinda 	ASSERT(iv_p->iv_pil_next == NULL);
2422973Sgovinda 
2432973Sgovinda 	if (prev_iv_p == iv_p)
2442973Sgovinda 		intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next;
2452973Sgovinda 	else
2462973Sgovinda 		prev_iv_p->iv_vec_next = iv_p->iv_vec_next;
2472973Sgovinda 
2482973Sgovinda 	mutex_exit(&intr_vec_mutex);
2492973Sgovinda 
2502973Sgovinda 	iv_free(iv_p);
2512973Sgovinda 	return (0);
2520Sstevel@tonic-gate }
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate  * add_softintr() - add a software interrupt handler to the system
2560Sstevel@tonic-gate  */
2572973Sgovinda uint64_t
2582973Sgovinda add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1,
2592973Sgovinda     softint_type_t type)
2600Sstevel@tonic-gate {
2612973Sgovinda 	intr_vec_t	*iv_p;
2620Sstevel@tonic-gate 
2632973Sgovinda 	if (pil > PIL_MAX)
2642973Sgovinda 		return (NULL);
2652973Sgovinda 
2662973Sgovinda 	iv_p = iv_alloc(type);
2670Sstevel@tonic-gate 
2682973Sgovinda 	iv_p->iv_handler = (intrfunc)intr_handler;
2692973Sgovinda 	iv_p->iv_arg1 = intr_arg1;
2702973Sgovinda 	iv_p->iv_pil = (ushort_t)pil;
2712973Sgovinda 	if (type == SOFTINT_MT)
2722973Sgovinda 		iv_p->iv_flags |=  IV_SOFTINT_MT;
2730Sstevel@tonic-gate 
2742973Sgovinda 	mutex_enter(&softint_mutex);
2752973Sgovinda 	if (softint_list)
2762973Sgovinda 		iv_p->iv_vec_next = softint_list;
2772973Sgovinda 	softint_list = iv_p;
2782973Sgovinda 	mutex_exit(&softint_mutex);
2790Sstevel@tonic-gate 
2802973Sgovinda 	return ((uint64_t)iv_p);
2810Sstevel@tonic-gate }
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate /*
2840Sstevel@tonic-gate  * rem_softintr() - remove a software interrupt handler from the system
2850Sstevel@tonic-gate  */
2862973Sgovinda int
2872973Sgovinda rem_softintr(uint64_t softint_id)
2880Sstevel@tonic-gate {
2892973Sgovinda 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
2902973Sgovinda 
2912973Sgovinda 	ASSERT(iv_p != NULL);
2922973Sgovinda 
2932973Sgovinda 	if (iv_p->iv_flags & IV_SOFTINT_PEND)
2942973Sgovinda 		return (EIO);
2952973Sgovinda 
2962973Sgovinda 	ASSERT(iv_p->iv_pil_next == NULL);
2970Sstevel@tonic-gate 
2982973Sgovinda 	mutex_enter(&softint_mutex);
2992973Sgovinda 	if (softint_list == iv_p) {
3002973Sgovinda 		softint_list = iv_p->iv_vec_next;
3012973Sgovinda 	} else {
3022973Sgovinda 		intr_vec_t	*list = softint_list;
3032973Sgovinda 
3042973Sgovinda 		while (list && (list->iv_vec_next != iv_p))
3052973Sgovinda 			list = list->iv_vec_next;
3062973Sgovinda 
3072973Sgovinda 		list->iv_vec_next = iv_p->iv_vec_next;
3082973Sgovinda 	}
3092973Sgovinda 	mutex_exit(&softint_mutex);
3102973Sgovinda 
3112973Sgovinda 	iv_free(iv_p);
3122973Sgovinda 	return (0);
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate 
3152973Sgovinda /*
3162973Sgovinda  * update_softint_arg2() - Update softint arg2.
3172973Sgovinda  *
3182973Sgovinda  * NOTE: Do not grab any mutex in this function since it may get called
3192973Sgovinda  *	 from the high-level interrupt context.
3202973Sgovinda  */
3210Sstevel@tonic-gate int
3222973Sgovinda update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2)
3230Sstevel@tonic-gate {
3242973Sgovinda 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
3250Sstevel@tonic-gate 
3262973Sgovinda 	ASSERT(iv_p != NULL);
3270Sstevel@tonic-gate 
3282973Sgovinda 	if (iv_p->iv_flags & IV_SOFTINT_PEND)
3292973Sgovinda 		return (EIO);
3300Sstevel@tonic-gate 
3312973Sgovinda 	iv_p->iv_arg2 = intr_arg2;
3322973Sgovinda 	return (0);
3330Sstevel@tonic-gate }
3340Sstevel@tonic-gate 
3352973Sgovinda /*
3362973Sgovinda  * update_softint_pri() - Update softint priority.
3372973Sgovinda  */
3380Sstevel@tonic-gate int
3392973Sgovinda update_softint_pri(uint64_t softint_id, uint_t pil)
3400Sstevel@tonic-gate {
3412973Sgovinda 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
3420Sstevel@tonic-gate 
3432973Sgovinda 	ASSERT(iv_p != NULL);
3440Sstevel@tonic-gate 
3452973Sgovinda 	if (pil > PIL_MAX)
3462973Sgovinda 		return (EINVAL);
3472973Sgovinda 
3482973Sgovinda 	iv_p->iv_pil = pil;
3492973Sgovinda 	return (0);
3500Sstevel@tonic-gate }
351