xref: /illumos-gate/usr/src/uts/sun4/io/ivintr.c (revision bb25c06c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Interrupt Vector Table Configuration
30  */
31 
32 #include <sys/types.h>
33 #include <sys/cpuvar.h>
34 #include <sys/ivintr.h>
35 #include <sys/intreg.h>
36 #include <sys/cmn_err.h>
37 #include <sys/privregs.h>
38 #include <sys/sunddi.h>
39 
40 /*
41  * Allocate an Interrupt Vector Table and some interrupt vector data structures
42  * for the reserved pool as part of the startup code. First try to allocate an
43  * interrupt vector data structure from the reserved pool, otherwise allocate it
44  * using kmem cache method.
45  */
46 static	kmutex_t intr_vec_mutex;	/* Protect interrupt vector table */
47 
48 /*
49  * Global softint linked list - used by softint mdb dcmd.
50  */
51 static	kmutex_t softint_mutex;		/* Protect global softint linked list */
52 intr_vec_t	*softint_list = NULL;
53 
54 /* Reserved pool for interrupt allocation */
55 intr_vec_t	*intr_vec_pool = NULL;	/* For HW and single target SW intrs */
56 intr_vecx_t	*intr_vecx_pool = NULL;	/* For multi target SW intrs */
57 
58 /* Kmem cache handle for interrupt allocation */
59 kmem_cache_t	*intr_vec_cache = NULL;	/* For HW and single target SW intrs */
60 
61 /*
62  * init_ivintr() - Initialize an Interrupt Vector Table.
63  */
64 void
65 init_ivintr()
66 {
67 	mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL);
68 	mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL);
69 
70 	/*
71 	 * Initialize the reserved interrupt vector data structure pools
72 	 * used for hardware and software interrupts.
73 	 */
74 	intr_vec_pool = (intr_vec_t *)((caddr_t)intr_vec_table +
75 	    (MAXIVNUM * sizeof (intr_vec_t *)));
76 	intr_vecx_pool = (intr_vecx_t *)((caddr_t)intr_vec_pool +
77 	    (MAX_RSVD_IV * sizeof (intr_vec_t)));
78 
79 	bzero(intr_vec_table, MAXIVNUM * sizeof (intr_vec_t *));
80 	bzero(intr_vec_pool, MAX_RSVD_IV * sizeof (intr_vec_t));
81 	bzero(intr_vecx_pool, MAX_RSVD_IVX * sizeof (intr_vecx_t));
82 }
83 
84 /*
85  * fini_ivintr() - Uninitialize an Interrupt Vector Table.
86  */
87 void
88 fini_ivintr()
89 {
90 	if (intr_vec_cache)
91 		kmem_cache_destroy(intr_vec_cache);
92 
93 	mutex_destroy(&intr_vec_mutex);
94 	mutex_destroy(&softint_mutex);
95 }
96 
97 /*
98  * iv_alloc() - Allocate an interrupt vector data structure.
99  *
100  * This function allocates an interrupt vector data structure for hardware
101  * and single or multi target software interrupts either from the reserved
102  * pool or using kmem cache method.
103  */
104 static intr_vec_t *
105 iv_alloc(softint_type_t type)
106 {
107 	intr_vec_t	*iv_p;
108 	int		i, count;
109 
110 	count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV;
111 
112 	/*
113 	 * First try to allocate an interrupt vector data structure from the
114 	 * reserved pool, otherwise allocate it using kmem_cache_alloc().
115 	 */
116 	for (i = 0; i < count; i++) {
117 		iv_p = (type == SOFTINT_MT) ?
118 		    (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i];
119 
120 		if (iv_p->iv_pil == 0)
121 			break;
122 	}
123 
124 	if (i < count)
125 		return (iv_p);
126 
127 	if (type == SOFTINT_MT)
128 		cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi "
129 		    "target software interrupts, %d", MAX_RSVD_IVX);
130 
131 	/*
132 	 * If the interrupt vector data structure reserved pool is already
133 	 * exhausted, then allocate an interrupt vector data structure using
134 	 * kmem_cache_alloc(), but only for the hardware and single software
135 	 * interrupts. Create a kmem cache for the interrupt allocation,
136 	 * if it is not already available.
137 	 */
138 	if (intr_vec_cache == NULL)
139 		intr_vec_cache = kmem_cache_create("intr_vec_cache",
140 		    sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
141 
142 	iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP);
143 	bzero(iv_p, sizeof (intr_vec_t));
144 
145 	iv_p->iv_flags =  IV_CACHE_ALLOC;
146 	return (iv_p);
147 }
148 
149 /*
150  * iv_free() - Free an interrupt vector data structure.
151  */
152 static void
153 iv_free(intr_vec_t *iv_p)
154 {
155 	if (iv_p->iv_flags & IV_CACHE_ALLOC) {
156 		ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT));
157 		kmem_cache_free(intr_vec_cache, iv_p);
158 	} else {
159 		(iv_p->iv_flags & IV_SOFTINT_MT) ?
160 		    bzero(iv_p, sizeof (intr_vecx_t)) :
161 		    bzero(iv_p, sizeof (intr_vec_t));
162 	}
163 }
164 
165 /*
166  * add_ivintr() - Add an interrupt handler to the system
167  */
168 int
169 add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler,
170     caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload)
171 {
172 	intr_vec_t	*iv_p, *new_iv_p;
173 
174 	if (inum >= MAXIVNUM || pil > PIL_MAX)
175 		return (EINVAL);
176 
177 	ASSERT((uintptr_t)intr_handler > KERNELBASE);
178 
179 	/* Make sure the payload buffer address is 64 bit aligned */
180 	VERIFY(((uint64_t)intr_payload & 0x7) == 0);
181 
182 	new_iv_p = iv_alloc(SOFTINT_ST);
183 	mutex_enter(&intr_vec_mutex);
184 
185 	for (iv_p = (intr_vec_t *)intr_vec_table[inum];
186 	    iv_p; iv_p = iv_p->iv_vec_next) {
187 		if (iv_p->iv_pil == pil) {
188 			mutex_exit(&intr_vec_mutex);
189 			iv_free(new_iv_p);
190 			return (EINVAL);
191 		}
192 	}
193 
194 	ASSERT(iv_p == NULL);
195 
196 	new_iv_p->iv_handler = intr_handler;
197 	new_iv_p->iv_arg1 = intr_arg1;
198 	new_iv_p->iv_arg2 = intr_arg2;
199 	new_iv_p->iv_payload_buf = intr_payload;
200 	new_iv_p->iv_pil = (ushort_t)pil;
201 	new_iv_p->iv_inum = inum;
202 
203 	new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum];
204 	intr_vec_table[inum] = (uint64_t)new_iv_p;
205 
206 	mutex_exit(&intr_vec_mutex);
207 	return (0);
208 }
209 
210 /*
211  * rem_ivintr() - Remove an interrupt handler from the system
212  */
213 int
214 rem_ivintr(uint_t inum, uint_t pil)
215 {
216 	intr_vec_t	*iv_p, *prev_iv_p;
217 
218 	if (inum >= MAXIVNUM || pil > PIL_MAX)
219 		return (EINVAL);
220 
221 	mutex_enter(&intr_vec_mutex);
222 
223 	for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum];
224 	    iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next)
225 		if (iv_p->iv_pil == pil)
226 			break;
227 
228 	if (iv_p == NULL) {
229 		mutex_exit(&intr_vec_mutex);
230 		return (EIO);
231 	}
232 
233 	ASSERT(iv_p->iv_pil_next == NULL);
234 
235 	if (prev_iv_p == iv_p)
236 		intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next;
237 	else
238 		prev_iv_p->iv_vec_next = iv_p->iv_vec_next;
239 
240 	mutex_exit(&intr_vec_mutex);
241 
242 	iv_free(iv_p);
243 	return (0);
244 }
245 
246 /*
247  * add_softintr() - add a software interrupt handler to the system
248  */
249 uint64_t
250 add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1,
251     softint_type_t type)
252 {
253 	intr_vec_t	*iv_p;
254 
255 	if (pil > PIL_MAX)
256 		return (NULL);
257 
258 	iv_p = iv_alloc(type);
259 
260 	iv_p->iv_handler = (intrfunc)intr_handler;
261 	iv_p->iv_arg1 = intr_arg1;
262 	iv_p->iv_pil = (ushort_t)pil;
263 	if (type == SOFTINT_MT)
264 		iv_p->iv_flags |=  IV_SOFTINT_MT;
265 
266 	mutex_enter(&softint_mutex);
267 	if (softint_list)
268 		iv_p->iv_vec_next = softint_list;
269 	softint_list = iv_p;
270 	mutex_exit(&softint_mutex);
271 
272 	return ((uint64_t)iv_p);
273 }
274 
275 /*
276  * rem_softintr() - remove a software interrupt handler from the system
277  */
278 int
279 rem_softintr(uint64_t softint_id)
280 {
281 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
282 
283 	ASSERT(iv_p != NULL);
284 
285 	if (iv_p->iv_flags & IV_SOFTINT_PEND)
286 		return (EIO);
287 
288 	ASSERT(iv_p->iv_pil_next == NULL);
289 
290 	mutex_enter(&softint_mutex);
291 	if (softint_list == iv_p) {
292 		softint_list = iv_p->iv_vec_next;
293 	} else {
294 		intr_vec_t	*list = softint_list;
295 
296 		while (list && (list->iv_vec_next != iv_p))
297 			list = list->iv_vec_next;
298 
299 		list->iv_vec_next = iv_p->iv_vec_next;
300 	}
301 	mutex_exit(&softint_mutex);
302 
303 	iv_free(iv_p);
304 	return (0);
305 }
306 
307 /*
308  * update_softint_arg2() - Update softint arg2.
309  *
310  * NOTE: Do not grab any mutex in this function since it may get called
311  *	 from the high-level interrupt context.
312  */
313 int
314 update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2)
315 {
316 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
317 
318 	ASSERT(iv_p != NULL);
319 
320 	if (iv_p->iv_flags & IV_SOFTINT_PEND)
321 		return (EIO);
322 
323 	iv_p->iv_arg2 = intr_arg2;
324 	return (0);
325 }
326 
327 /*
328  * update_softint_pri() - Update softint priority.
329  */
330 int
331 update_softint_pri(uint64_t softint_id, uint_t pil)
332 {
333 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
334 
335 	ASSERT(iv_p != NULL);
336 
337 	if (pil > PIL_MAX)
338 		return (EINVAL);
339 
340 	iv_p->iv_pil = pil;
341 	return (0);
342 }
343