xref: /illumos-gate/usr/src/uts/sun4/io/ivintr.c (revision d2ec54f7875f7e05edd56195adbeb593c947763f)
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 static	kmutex_t intr_vec_pool_mutex;	/* Protect interrupt vector pool */
58 
59 /* Kmem cache handle for interrupt allocation */
60 kmem_cache_t	*intr_vec_cache = NULL;	/* For HW and single target SW intrs */
61 
62 /*
63  * init_ivintr() - Initialize an Interrupt Vector Table.
64  */
65 void
66 init_ivintr()
67 {
68 	mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL);
69 	mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL);
70 	mutex_init(&intr_vec_pool_mutex, NULL, MUTEX_DRIVER, NULL);
71 
72 	/*
73 	 * Initialize the reserved interrupt vector data structure pools
74 	 * used for hardware and software interrupts.
75 	 */
76 	intr_vec_pool = (intr_vec_t *)((caddr_t)intr_vec_table +
77 	    (MAXIVNUM * sizeof (intr_vec_t *)));
78 	intr_vecx_pool = (intr_vecx_t *)((caddr_t)intr_vec_pool +
79 	    (MAX_RSVD_IV * sizeof (intr_vec_t)));
80 
81 	bzero(intr_vec_table, MAXIVNUM * sizeof (intr_vec_t *));
82 	bzero(intr_vec_pool, MAX_RSVD_IV * sizeof (intr_vec_t));
83 	bzero(intr_vecx_pool, MAX_RSVD_IVX * sizeof (intr_vecx_t));
84 }
85 
86 /*
87  * fini_ivintr() - Uninitialize an Interrupt Vector Table.
88  */
89 void
90 fini_ivintr()
91 {
92 	if (intr_vec_cache)
93 		kmem_cache_destroy(intr_vec_cache);
94 
95 	mutex_destroy(&intr_vec_pool_mutex);
96 	mutex_destroy(&softint_mutex);
97 	mutex_destroy(&intr_vec_mutex);
98 }
99 
100 /*
101  * iv_alloc() - Allocate an interrupt vector data structure.
102  *
103  * This function allocates an interrupt vector data structure for hardware
104  * and single or multi target software interrupts either from the reserved
105  * pool or using kmem cache method.
106  */
107 static intr_vec_t *
108 iv_alloc(softint_type_t type)
109 {
110 	intr_vec_t	*iv_p;
111 	int		i, count;
112 
113 	count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV;
114 
115 	/*
116 	 * First try to allocate an interrupt vector data structure from the
117 	 * reserved pool, otherwise allocate it using kmem_cache_alloc().
118 	 */
119 	mutex_enter(&intr_vec_pool_mutex);
120 	for (i = 0; i < count; i++) {
121 		iv_p = (type == SOFTINT_MT) ?
122 		    (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i];
123 
124 		if (iv_p->iv_pil == 0) {
125 			iv_p->iv_pil = 1;	/* Default PIL */
126 			break;
127 		}
128 	}
129 	mutex_exit(&intr_vec_pool_mutex);
130 
131 	if (i < count)
132 		return (iv_p);
133 
134 	if (type == SOFTINT_MT)
135 		cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi "
136 		    "target software interrupts, %d", MAX_RSVD_IVX);
137 
138 	/*
139 	 * If the interrupt vector data structure reserved pool is already
140 	 * exhausted, then allocate an interrupt vector data structure using
141 	 * kmem_cache_alloc(), but only for the hardware and single software
142 	 * interrupts. Create a kmem cache for the interrupt allocation,
143 	 * if it is not already available.
144 	 */
145 	if (intr_vec_cache == NULL)
146 		intr_vec_cache = kmem_cache_create("intr_vec_cache",
147 		    sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
148 
149 	iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP);
150 	bzero(iv_p, sizeof (intr_vec_t));
151 	iv_p->iv_flags =  IV_CACHE_ALLOC;
152 
153 	return (iv_p);
154 }
155 
156 /*
157  * iv_free() - Free an interrupt vector data structure.
158  */
159 static void
160 iv_free(intr_vec_t *iv_p)
161 {
162 	if (iv_p->iv_flags & IV_CACHE_ALLOC) {
163 		ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT));
164 		kmem_cache_free(intr_vec_cache, iv_p);
165 	} else {
166 		mutex_enter(&intr_vec_pool_mutex);
167 		bzero(iv_p, (iv_p->iv_flags & IV_SOFTINT_MT) ?
168 		    sizeof (intr_vecx_t) : sizeof (intr_vec_t));
169 		mutex_exit(&intr_vec_pool_mutex);
170 	}
171 }
172 
173 /*
174  * add_ivintr() - Add an interrupt handler to the system
175  */
176 int
177 add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler,
178     caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload)
179 {
180 	intr_vec_t	*iv_p, *new_iv_p;
181 
182 	if (inum >= MAXIVNUM || pil > PIL_MAX)
183 		return (EINVAL);
184 
185 	ASSERT((uintptr_t)intr_handler > KERNELBASE);
186 
187 	/* Make sure the payload buffer address is 64 bit aligned */
188 	VERIFY(((uint64_t)intr_payload & 0x7) == 0);
189 
190 	new_iv_p = iv_alloc(SOFTINT_ST);
191 	mutex_enter(&intr_vec_mutex);
192 
193 	for (iv_p = (intr_vec_t *)intr_vec_table[inum];
194 	    iv_p; iv_p = iv_p->iv_vec_next) {
195 		if (iv_p->iv_pil == pil) {
196 			mutex_exit(&intr_vec_mutex);
197 			iv_free(new_iv_p);
198 			return (EINVAL);
199 		}
200 	}
201 
202 	ASSERT(iv_p == NULL);
203 
204 	new_iv_p->iv_handler = intr_handler;
205 	new_iv_p->iv_arg1 = intr_arg1;
206 	new_iv_p->iv_arg2 = intr_arg2;
207 	new_iv_p->iv_payload_buf = intr_payload;
208 	new_iv_p->iv_pil = (ushort_t)pil;
209 	new_iv_p->iv_inum = inum;
210 
211 	new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum];
212 	intr_vec_table[inum] = (uint64_t)new_iv_p;
213 
214 	mutex_exit(&intr_vec_mutex);
215 	return (0);
216 }
217 
218 /*
219  * rem_ivintr() - Remove an interrupt handler from the system
220  */
221 int
222 rem_ivintr(uint_t inum, uint_t pil)
223 {
224 	intr_vec_t	*iv_p, *prev_iv_p;
225 
226 	if (inum >= MAXIVNUM || pil > PIL_MAX)
227 		return (EINVAL);
228 
229 	mutex_enter(&intr_vec_mutex);
230 
231 	for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum];
232 	    iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next)
233 		if (iv_p->iv_pil == pil)
234 			break;
235 
236 	if (iv_p == NULL) {
237 		mutex_exit(&intr_vec_mutex);
238 		return (EIO);
239 	}
240 
241 	ASSERT(iv_p->iv_pil_next == NULL);
242 
243 	if (prev_iv_p == iv_p)
244 		intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next;
245 	else
246 		prev_iv_p->iv_vec_next = iv_p->iv_vec_next;
247 
248 	mutex_exit(&intr_vec_mutex);
249 
250 	iv_free(iv_p);
251 	return (0);
252 }
253 
254 /*
255  * add_softintr() - add a software interrupt handler to the system
256  */
257 uint64_t
258 add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1,
259     softint_type_t type)
260 {
261 	intr_vec_t	*iv_p;
262 
263 	if (pil > PIL_MAX)
264 		return (NULL);
265 
266 	iv_p = iv_alloc(type);
267 
268 	iv_p->iv_handler = (intrfunc)intr_handler;
269 	iv_p->iv_arg1 = intr_arg1;
270 	iv_p->iv_pil = (ushort_t)pil;
271 	if (type == SOFTINT_MT)
272 		iv_p->iv_flags |=  IV_SOFTINT_MT;
273 
274 	mutex_enter(&softint_mutex);
275 	if (softint_list)
276 		iv_p->iv_vec_next = softint_list;
277 	softint_list = iv_p;
278 	mutex_exit(&softint_mutex);
279 
280 	return ((uint64_t)iv_p);
281 }
282 
283 /*
284  * rem_softintr() - remove a software interrupt handler from the system
285  */
286 int
287 rem_softintr(uint64_t softint_id)
288 {
289 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
290 
291 	ASSERT(iv_p != NULL);
292 
293 	if (iv_p->iv_flags & IV_SOFTINT_PEND)
294 		return (EIO);
295 
296 	ASSERT(iv_p->iv_pil_next == NULL);
297 
298 	mutex_enter(&softint_mutex);
299 	if (softint_list == iv_p) {
300 		softint_list = iv_p->iv_vec_next;
301 	} else {
302 		intr_vec_t	*list = softint_list;
303 
304 		while (list && (list->iv_vec_next != iv_p))
305 			list = list->iv_vec_next;
306 
307 		list->iv_vec_next = iv_p->iv_vec_next;
308 	}
309 	mutex_exit(&softint_mutex);
310 
311 	iv_free(iv_p);
312 	return (0);
313 }
314 
315 /*
316  * update_softint_arg2() - Update softint arg2.
317  *
318  * NOTE: Do not grab any mutex in this function since it may get called
319  *	 from the high-level interrupt context.
320  */
321 int
322 update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2)
323 {
324 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
325 
326 	ASSERT(iv_p != NULL);
327 
328 	if (iv_p->iv_flags & IV_SOFTINT_PEND)
329 		return (EIO);
330 
331 	iv_p->iv_arg2 = intr_arg2;
332 	return (0);
333 }
334 
335 /*
336  * update_softint_pri() - Update softint priority.
337  */
338 int
339 update_softint_pri(uint64_t softint_id, uint_t pil)
340 {
341 	intr_vec_t	*iv_p = (intr_vec_t *)softint_id;
342 
343 	ASSERT(iv_p != NULL);
344 
345 	if (pil > PIL_MAX)
346 		return (EINVAL);
347 
348 	iv_p->iv_pil = pil;
349 	return (0);
350 }
351