xref: /illumos-gate/usr/src/uts/common/os/ddi_intr.c (revision d6bb6a8465e557cb946ef49d56ed3202f6218652)
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 #include <sys/note.h>
29 #include <sys/sysmacros.h>
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kmem.h>
34 #include <sys/cmn_err.h>
35 #include <sys/debug.h>
36 #include <sys/avintr.h>
37 #include <sys/autoconf.h>
38 #include <sys/sunndi.h>
39 #include <sys/ndi_impldefs.h>	/* include prototypes */
40 
41 /*
42  * New DDI interrupt framework
43  */
44 
45 /*
46  * MSI/X allocation limit.
47  * This limit will change with Resource Management support.
48  */
49 uint_t		ddi_msix_alloc_limit = 2;
50 
51 /*
52  * ddi_intr_get_supported_types:
53  *	Return, as a bit mask, the hardware interrupt types supported by
54  *	both the device and by the host in the integer pointed
55  *	to be the 'typesp' argument.
56  */
57 int
58 ddi_intr_get_supported_types(dev_info_t *dip, int *typesp)
59 {
60 	int			ret;
61 	ddi_intr_handle_impl_t	hdl;
62 
63 	if (dip == NULL)
64 		return (DDI_EINVAL);
65 
66 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_supported_types: dip %p\n",
67 	    (void *)dip));
68 
69 	if (*typesp = i_ddi_intr_get_supported_types(dip))
70 		return (DDI_SUCCESS);
71 
72 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
73 	hdl.ih_dip = dip;
74 
75 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_SUPPORTED_TYPES, &hdl,
76 	    (void *)typesp);
77 
78 	if (ret != DDI_SUCCESS)
79 		return (DDI_INTR_NOTFOUND);
80 
81 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_supported_types: types %x\n",
82 	    *typesp));
83 
84 	return (ret);
85 }
86 
87 
88 /*
89  * ddi_intr_get_nintrs:
90  * 	Return as an integer in the integer pointed to by the argument
91  * 	*nintrsp*, the number of interrupts the device supports for the
92  *	given interrupt type.
93  */
94 int
95 ddi_intr_get_nintrs(dev_info_t *dip, int type, int *nintrsp)
96 {
97 	int			ret;
98 	ddi_intr_handle_impl_t	hdl;
99 
100 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_nintrs: dip %p, type: %d\n",
101 	    (void *)dip, type));
102 
103 	if ((dip == NULL) || (type & ~(DDI_INTR_SUP_TYPES))) {
104 		*nintrsp = 0;
105 		return (DDI_EINVAL);
106 	}
107 
108 	if (!(i_ddi_intr_get_supported_types(dip) & type)) {
109 		*nintrsp = 0;
110 		return (DDI_EINVAL);
111 	}
112 
113 	if (*nintrsp = i_ddi_intr_get_supported_nintrs(dip, type))
114 		return (DDI_SUCCESS);
115 
116 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
117 	hdl.ih_dip = dip;
118 	hdl.ih_type = type;
119 
120 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl,
121 	    (void *)nintrsp);
122 
123 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_nintrs:: nintrs %x\n",
124 	    *nintrsp));
125 
126 	return (ret);
127 }
128 
129 
130 /*
131  * ddi_intr_get_navail:
132  *	Bus nexus driver will return availble interrupt count value for
133  *	a given interrupt type.
134  *
135  * 	Return as an integer in the integer pointed to by the argument
136  * 	*navailp*, the number of interrupts currently available for the
137  *	given interrupt type.
138  */
139 int
140 ddi_intr_get_navail(dev_info_t *dip, int type, int *navailp)
141 {
142 	int			ret;
143 	ddi_intr_handle_impl_t	hdl;
144 
145 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_navail: dip %p, type: %d\n",
146 	    (void *)dip, type));
147 
148 	if ((dip == NULL) || (type & ~(DDI_INTR_SUP_TYPES))) {
149 		*navailp = 0;
150 		return (DDI_EINVAL);
151 	}
152 
153 	if (!(i_ddi_intr_get_supported_types(dip) & type)) {
154 		*navailp = 0;
155 		return (DDI_EINVAL);
156 	}
157 
158 	/*
159 	 * In future, this interface implementation will change
160 	 * with Resource Management support.
161 	 */
162 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
163 	hdl.ih_dip = dip;
164 	hdl.ih_type = type;
165 
166 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NAVAIL, &hdl,
167 	    (void *)navailp);
168 
169 	return (ret == DDI_SUCCESS ? DDI_SUCCESS : DDI_INTR_NOTFOUND);
170 }
171 
172 
173 /*
174  * Interrupt allocate/free functions
175  */
176 int
177 ddi_intr_alloc(dev_info_t *dip, ddi_intr_handle_t *h_array, int type, int inum,
178     int count, int *actualp, int behavior)
179 {
180 	ddi_intr_handle_impl_t	*hdlp, tmp_hdl;
181 	int			i, ret, cap = 0, intr_type, nintrs = 0;
182 	uint_t			pri;
183 
184 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: name %s dip 0x%p "
185 	    "type %x inum %x count %x behavior %x\n", ddi_driver_name(dip),
186 	    (void *)dip, type, inum, count, behavior));
187 
188 	/* Validate parameters */
189 	if (dip == NULL || h_array == NULL || count < 1) {
190 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: Invalid args\n"));
191 		return (DDI_EINVAL);
192 	}
193 
194 	/* Validate interrupt type */
195 	if (!(i_ddi_intr_get_supported_types(dip) & type)) {
196 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: type %x not "
197 		    "supported\n", type));
198 		return (DDI_EINVAL);
199 	}
200 
201 	/* First, get how many interrupts the device supports */
202 	if (!(nintrs = i_ddi_intr_get_supported_nintrs(dip, type))) {
203 		if (ddi_intr_get_nintrs(dip, type, &nintrs) != DDI_SUCCESS) {
204 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: no "
205 			    "interrupts found of type %d\n", type));
206 			return (DDI_INTR_NOTFOUND);
207 		}
208 	}
209 
210 	/* Is this function invoked with more interrupt than device supports? */
211 	if (count > nintrs) {
212 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: no of interrupts "
213 		    "requested %d is more than supported %d\n", count, nintrs));
214 		return (DDI_EINVAL);
215 	}
216 
217 	/*
218 	 * Check if requested interrupt type is not same as interrupt
219 	 * type is in use if any.
220 	 */
221 	if (((intr_type = i_ddi_intr_get_current_type(dip)) != 0) &&
222 	    (intr_type != type)) {
223 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: Requested "
224 		    "interrupt type %x is different from interrupt type %x"
225 		    "already in use\n", type, intr_type));
226 
227 		return (DDI_EINVAL);
228 	}
229 
230 	/*
231 	 * Check if requested interrupt type is in use and requested number
232 	 * of interrupts and number of interrupts already in use exceeds the
233 	 * number of interrupts supported by this device.
234 	 */
235 	if (intr_type) {
236 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: type %x "
237 		    "is already being used\n", type));
238 
239 		if ((count + i_ddi_intr_get_current_nintrs(dip)) > nintrs) {
240 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: count %d "
241 			    "+ intrs in use %d exceeds supported %d intrs\n",
242 			    count, i_ddi_intr_get_current_nintrs(dip), nintrs));
243 			return (DDI_EINVAL);
244 		}
245 	}
246 
247 	/*
248 	 * For MSI, ensure that the requested interrupt count is a power of 2
249 	 */
250 	if (type == DDI_INTR_TYPE_MSI && !ISP2(count)) {
251 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
252 		    "MSI count %d is not a power of two\n", count));
253 		return (DDI_EINVAL);
254 	}
255 
256 	/*
257 	 * Limit max MSI/X allocation to ddi_msix_alloc_limit.
258 	 * This limit will change with Resource Management support.
259 	 */
260 	if (DDI_INTR_IS_MSI_OR_MSIX(type) && (count > ddi_msix_alloc_limit)) {
261 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: Requested MSI/Xs %d"
262 		    "Max MSI/Xs limit %d\n", count, ddi_msix_alloc_limit));
263 
264 		if (behavior == DDI_INTR_ALLOC_STRICT) {
265 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
266 			    "DDI_INTR_ALLOC_STRICT flag is passed, "
267 			    "return failure\n"));
268 
269 			return (DDI_EAGAIN);
270 		}
271 
272 		count = ddi_msix_alloc_limit;
273 	}
274 
275 	/* Now allocate required number of interrupts */
276 	bzero(&tmp_hdl, sizeof (ddi_intr_handle_impl_t));
277 	tmp_hdl.ih_type = type;
278 	tmp_hdl.ih_inum = inum;
279 	tmp_hdl.ih_scratch1 = count;
280 	tmp_hdl.ih_scratch2 = (void *)behavior;
281 	tmp_hdl.ih_dip = dip;
282 
283 	if (i_ddi_intr_ops(dip, dip, DDI_INTROP_ALLOC,
284 	    &tmp_hdl, (void *)actualp) != DDI_SUCCESS) {
285 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: allocation "
286 		    "failed\n"));
287 		return (*actualp ? DDI_EAGAIN : DDI_INTR_NOTFOUND);
288 	}
289 
290 	if ((ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPRI,
291 	    &tmp_hdl, (void *)&pri)) != DDI_SUCCESS) {
292 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: get priority "
293 		    "failed\n"));
294 		return (ret);
295 	}
296 
297 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: getting capability\n"));
298 
299 	if ((ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_GETCAP,
300 	    &tmp_hdl, (void *)&cap)) != DDI_SUCCESS) {
301 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: get capability "
302 		    "failed\n"));
303 		return (ret);
304 	}
305 
306 	/* Save current interrupt type, supported and current intr count */
307 	i_ddi_intr_devi_init(dip);
308 	i_ddi_intr_set_current_type(dip, type);
309 	i_ddi_intr_set_supported_nintrs(dip, nintrs);
310 	i_ddi_intr_set_current_nintrs(dip,
311 	    i_ddi_intr_get_current_nintrs(dip) + *actualp);
312 
313 	/* Now, go and handle each "handle" */
314 	for (i = 0; i < *actualp; i++) {
315 		hdlp = (ddi_intr_handle_impl_t *)kmem_zalloc(
316 		    (sizeof (ddi_intr_handle_impl_t)), KM_SLEEP);
317 		rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
318 		h_array[i] = (struct __ddi_intr_handle *)hdlp;
319 		hdlp->ih_type = type;
320 		hdlp->ih_pri = pri;
321 		hdlp->ih_cap = cap;
322 		hdlp->ih_ver = DDI_INTR_VERSION;
323 		hdlp->ih_state = DDI_IHDL_STATE_ALLOC;
324 		hdlp->ih_dip = dip;
325 		hdlp->ih_inum = inum + i;
326 		i_ddi_alloc_intr_phdl(hdlp);
327 		if (type & DDI_INTR_TYPE_FIXED)
328 			i_ddi_set_intr_handle(dip, hdlp->ih_inum, &h_array[i]);
329 
330 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: hdlp = 0x%p\n",
331 		    (void *)h_array[i]));
332 	}
333 
334 	return (DDI_SUCCESS);
335 }
336 
337 
338 int
339 ddi_intr_free(ddi_intr_handle_t h)
340 {
341 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
342 	int			ret;
343 
344 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_free: hdlp = %p\n", (void *)hdlp));
345 
346 	if (hdlp == NULL)
347 		return (DDI_EINVAL);
348 
349 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
350 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
351 		rw_exit(&hdlp->ih_rwlock);
352 		return (DDI_EINVAL);
353 	}
354 
355 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
356 	    DDI_INTROP_FREE, hdlp, NULL);
357 
358 	rw_exit(&hdlp->ih_rwlock);
359 	if (ret == DDI_SUCCESS) {
360 		i_ddi_intr_set_current_nintrs(hdlp->ih_dip,
361 		    i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1);
362 
363 		if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) == 0) {
364 			i_ddi_intr_devi_fini(hdlp->ih_dip);
365 		} else {
366 			if (hdlp->ih_type & DDI_INTR_TYPE_FIXED)
367 				i_ddi_set_intr_handle(hdlp->ih_dip,
368 				    hdlp->ih_inum, NULL);
369 		}
370 
371 		i_ddi_free_intr_phdl(hdlp);
372 		rw_destroy(&hdlp->ih_rwlock);
373 		kmem_free(hdlp, sizeof (ddi_intr_handle_impl_t));
374 	}
375 
376 	return (ret);
377 }
378 
379 /*
380  * Interrupt get/set capacity functions
381  *
382  * The logic used to figure this out is shown here:
383  *
384  *			Device level		Platform level	    Intr source
385  * 1. Fixed interrupts
386  * (non-PCI)
387  * o Flags supported	N/A			Maskable/Pending/    rootnex
388  *						No Block Enable
389  * o navail					1
390  *
391  * 2. PCI Fixed interrupts
392  * o Flags supported	pending/Maskable	Maskable/pending/    pci
393  *						No Block enable
394  * o navail		N/A			1
395  *
396  * 3. PCI MSI
397  * o Flags supported	Maskable/Pending	Maskable/Pending    pci
398  *			Block Enable		(if drvr doesn't)   Block Enable
399  * o navail		N/A			#vectors - #used    N/A
400  *
401  * 4. PCI MSI-X
402  * o Flags supported	Maskable/Pending	Maskable/Pending    pci
403  *			Block Enable				    Block Enable
404  * o navail		N/A			#vectors - #used    N/A
405  *
406  * where:
407  *	#vectors	- Total numbers of vectors available
408  *	#used		- Total numbers of vectors currently being used
409  *
410  * For devices complying to PCI2.3 or greater, see bit10 of Command Register
411  * 0 - enables assertion of INTx
412  * 1 - disables assertion of INTx
413  *
414  * For non MSI/X interrupts; if the IRQ is shared then all ddi_intr_set_*()
415  * operations return failure.
416  */
417 int
418 ddi_intr_get_cap(ddi_intr_handle_t h, int *flagsp)
419 {
420 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
421 	int			ret;
422 
423 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_cap: hdlp = %p\n",
424 	    (void *)hdlp));
425 
426 	*flagsp = 0;
427 	if (hdlp == NULL)
428 		return (DDI_EINVAL);
429 
430 	rw_enter(&hdlp->ih_rwlock, RW_READER);
431 
432 	if (hdlp->ih_cap) {
433 		*flagsp = hdlp->ih_cap & ~DDI_INTR_FLAG_MSI64;
434 		rw_exit(&hdlp->ih_rwlock);
435 		return (DDI_SUCCESS);
436 	}
437 
438 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
439 	    DDI_INTROP_GETCAP, hdlp, (void *)flagsp);
440 
441 	if (ret == DDI_SUCCESS) {
442 		hdlp->ih_cap = *flagsp;
443 
444 		/* Mask out MSI/X 64-bit support to the consumer */
445 		*flagsp &= ~DDI_INTR_FLAG_MSI64;
446 	}
447 
448 	rw_exit(&hdlp->ih_rwlock);
449 	return (ret);
450 }
451 
452 int
453 ddi_intr_set_cap(ddi_intr_handle_t h, int flags)
454 {
455 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
456 	int			ret;
457 
458 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_cap: hdlp = %p", (void *)hdlp));
459 
460 	if (hdlp == NULL)
461 		return (DDI_EINVAL);
462 
463 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
464 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
465 		rw_exit(&hdlp->ih_rwlock);
466 		return (DDI_EINVAL);
467 	}
468 
469 	/* Only DDI_INTR_FLAG_LEVEL or DDI_INTR_FLAG_EDGE are allowed */
470 	if (!(flags & (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_LEVEL))) {
471 		DDI_INTR_APIDBG((CE_CONT, "%s%d: only LEVEL or EDGE capability "
472 		    "can be set\n", ddi_driver_name(hdlp->ih_dip),
473 		    ddi_get_instance(hdlp->ih_dip)));
474 		rw_exit(&hdlp->ih_rwlock);
475 		return (DDI_EINVAL);
476 	}
477 
478 	/* Both level/edge flags must be currently supported */
479 	if (!(hdlp->ih_cap & (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_LEVEL))) {
480 		DDI_INTR_APIDBG((CE_CONT, "%s%d: Both LEVEL and EDGE capability"
481 		    " must be supported\n", ddi_driver_name(hdlp->ih_dip),
482 		    ddi_get_instance(hdlp->ih_dip)));
483 		rw_exit(&hdlp->ih_rwlock);
484 		return (DDI_ENOTSUP);
485 	}
486 
487 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
488 	    DDI_INTROP_SETCAP, hdlp, &flags);
489 
490 	rw_exit(&hdlp->ih_rwlock);
491 	return (ret);
492 }
493 
494 /*
495  * Priority related functions
496  */
497 
498 /*
499  * ddi_intr_get_hilevel_pri:
500  *	Returns the minimum priority level for a
501  *	high-level interrupt on a platform.
502  */
503 uint_t
504 ddi_intr_get_hilevel_pri(void)
505 {
506 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_hilevel_pri:\n"));
507 	return (LOCK_LEVEL + 1);
508 }
509 
510 int
511 ddi_intr_get_pri(ddi_intr_handle_t h, uint_t *prip)
512 {
513 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
514 	int			ret;
515 
516 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pri: hdlp = %p\n",
517 	    (void *)hdlp));
518 
519 	*prip = 0;
520 	if (hdlp == NULL)
521 		return (DDI_EINVAL);
522 
523 	rw_enter(&hdlp->ih_rwlock, RW_READER);
524 	/* Already initialized, just return that */
525 	if (hdlp->ih_pri) {
526 		*prip = hdlp->ih_pri;
527 		rw_exit(&hdlp->ih_rwlock);
528 		return (DDI_SUCCESS);
529 	}
530 
531 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
532 	    DDI_INTROP_GETPRI, hdlp, (void *)prip);
533 
534 	if (ret == DDI_SUCCESS)
535 		hdlp->ih_pri = *prip;
536 
537 	rw_exit(&hdlp->ih_rwlock);
538 	return (ret);
539 }
540 
541 int
542 ddi_intr_set_pri(ddi_intr_handle_t h, uint_t pri)
543 {
544 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
545 	int			ret;
546 
547 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_pri: hdlp = %p", (void *)hdlp));
548 
549 	if (hdlp == NULL)
550 		return (DDI_EINVAL);
551 
552 	/* Validate priority argument */
553 	if (pri < DDI_INTR_PRI_MIN || pri > DDI_INTR_PRI_MAX) {
554 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_pri: invalid priority "
555 		    "specified  = %x\n", pri));
556 		return (DDI_EINVAL);
557 	}
558 
559 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
560 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
561 		rw_exit(&hdlp->ih_rwlock);
562 		return (DDI_EINVAL);
563 	}
564 
565 	/* If the passed priority is same as existing priority; do nothing */
566 	if (pri == hdlp->ih_pri) {
567 		rw_exit(&hdlp->ih_rwlock);
568 		return (DDI_SUCCESS);
569 	}
570 
571 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
572 	    DDI_INTROP_SETPRI, hdlp, &pri);
573 
574 	if (ret == DDI_SUCCESS)
575 		hdlp->ih_pri = pri;
576 
577 	rw_exit(&hdlp->ih_rwlock);
578 	return (ret);
579 }
580 
581 /*
582  * Interrupt add/duplicate/remove handlers
583  */
584 int
585 ddi_intr_add_handler(ddi_intr_handle_t h, ddi_intr_handler_t inthandler,
586     void *arg1, void *arg2)
587 {
588 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
589 	int			ret;
590 
591 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_handler: hdlp = 0x%p\n",
592 	    (void *)hdlp));
593 
594 	if ((hdlp == NULL) || (inthandler == NULL))
595 		return (DDI_EINVAL);
596 
597 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
598 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
599 		rw_exit(&hdlp->ih_rwlock);
600 		return (DDI_EINVAL);
601 	}
602 
603 	hdlp->ih_cb_func = inthandler;
604 	hdlp->ih_cb_arg1 = arg1;
605 	hdlp->ih_cb_arg2 = arg2;
606 
607 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
608 	    DDI_INTROP_ADDISR, hdlp, NULL);
609 
610 	if (ret != DDI_SUCCESS) {
611 		hdlp->ih_cb_func = NULL;
612 		hdlp->ih_cb_arg1 = NULL;
613 		hdlp->ih_cb_arg2 = NULL;
614 	} else
615 		hdlp->ih_state = DDI_IHDL_STATE_ADDED;
616 
617 	rw_exit(&hdlp->ih_rwlock);
618 	return (ret);
619 }
620 
621 int
622 ddi_intr_dup_handler(ddi_intr_handle_t org, int vector, ddi_intr_handle_t *dup)
623 {
624 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)org;
625 	ddi_intr_handle_impl_t	*dup_hdlp;
626 	int			ret;
627 
628 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_dup_handler: hdlp = 0x%p\n",
629 	    (void *)hdlp));
630 
631 	/* Do some input argument checking ("dup" is not allocated) */
632 	if ((hdlp == NULL) || (dup != NULL))
633 		return (DDI_EINVAL);
634 
635 	rw_enter(&hdlp->ih_rwlock, RW_READER);
636 
637 	/* Do some input argument checking */
638 	if ((hdlp->ih_state == DDI_IHDL_STATE_ALLOC) ||	/* intr handle alloc? */
639 	    (hdlp->ih_type != DDI_INTR_TYPE_MSIX)) {	/* only MSI-X allowed */
640 		rw_exit(&hdlp->ih_rwlock);
641 		return (DDI_EINVAL);
642 	}
643 
644 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
645 	    DDI_INTROP_DUPVEC, hdlp, (void *)&vector);
646 
647 	if (ret == DDI_SUCCESS) {
648 		dup_hdlp = (ddi_intr_handle_impl_t *)
649 		    kmem_zalloc(sizeof (ddi_intr_handle_impl_t), KM_SLEEP);
650 
651 		dup = (ddi_intr_handle_t *)dup_hdlp;
652 		rw_init(&dup_hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
653 		rw_enter(&dup_hdlp->ih_rwlock, RW_WRITER);
654 		dup_hdlp->ih_ver = DDI_INTR_VERSION;
655 		dup_hdlp->ih_state = DDI_IHDL_STATE_ADDED;
656 		dup_hdlp->ih_dip = hdlp->ih_dip;
657 		dup_hdlp->ih_type = hdlp->ih_type;
658 		dup_hdlp->ih_pri = hdlp->ih_pri;
659 		dup_hdlp->ih_cap = hdlp->ih_cap;
660 		dup_hdlp->ih_inum = hdlp->ih_inum;
661 		/* What about MSI-X vector */
662 
663 		dup_hdlp->ih_cb_func = hdlp->ih_cb_func;
664 		dup_hdlp->ih_cb_arg1 = hdlp->ih_cb_arg1;
665 		dup_hdlp->ih_cb_arg2 = hdlp->ih_cb_arg2;
666 		rw_exit(&dup_hdlp->ih_rwlock);
667 	}
668 
669 	rw_exit(&hdlp->ih_rwlock);
670 	return (ret);
671 }
672 
673 int
674 ddi_intr_remove_handler(ddi_intr_handle_t h)
675 {
676 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
677 	int			ret;
678 
679 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_handler: hdlp = %p\n",
680 	    (void *)hdlp));
681 
682 	if (hdlp == NULL)
683 		return (DDI_EINVAL);
684 
685 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
686 	if (hdlp->ih_state != DDI_IHDL_STATE_ADDED) {
687 		rw_exit(&hdlp->ih_rwlock);
688 		return (DDI_EINVAL);
689 	}
690 
691 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
692 	    DDI_INTROP_REMISR, hdlp, NULL);
693 
694 	if (ret == DDI_SUCCESS) {
695 		hdlp->ih_state = DDI_IHDL_STATE_ALLOC;
696 		hdlp->ih_cb_func = NULL;
697 		hdlp->ih_cb_arg1 = NULL;
698 		hdlp->ih_cb_arg2 = NULL;
699 	}
700 
701 	rw_exit(&hdlp->ih_rwlock);
702 	return (ret);
703 }
704 
705 /*
706  * Interrupt enable/disable/block_enable/block_disable handlers
707  */
708 int
709 ddi_intr_enable(ddi_intr_handle_t h)
710 {
711 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
712 	int			ret;
713 
714 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_enable: hdlp = %p\n",
715 	    (void *)hdlp));
716 
717 	if (hdlp == NULL)
718 		return (DDI_EINVAL);
719 
720 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
721 	if ((hdlp->ih_state != DDI_IHDL_STATE_ADDED) ||
722 	    ((hdlp->ih_type == DDI_INTR_TYPE_MSI) &&
723 	    (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) {
724 		rw_exit(&hdlp->ih_rwlock);
725 		return (DDI_EINVAL);
726 	}
727 
728 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
729 	    DDI_INTROP_ENABLE, hdlp, NULL);
730 
731 	if (ret == DDI_SUCCESS)
732 		hdlp->ih_state = DDI_IHDL_STATE_ENABLE;
733 
734 	rw_exit(&hdlp->ih_rwlock);
735 	return (ret);
736 }
737 
738 int
739 ddi_intr_disable(ddi_intr_handle_t h)
740 {
741 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
742 	int			ret;
743 
744 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_disable: hdlp = %p\n",
745 	    (void *)hdlp));
746 
747 	if (hdlp == NULL)
748 		return (DDI_EINVAL);
749 
750 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
751 	if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
752 	    ((hdlp->ih_type == DDI_INTR_TYPE_MSI) &&
753 	    (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) {
754 		rw_exit(&hdlp->ih_rwlock);
755 		return (DDI_EINVAL);
756 	}
757 
758 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
759 	    DDI_INTROP_DISABLE, hdlp, NULL);
760 
761 	if (ret == DDI_SUCCESS)
762 		hdlp->ih_state = DDI_IHDL_STATE_ADDED;
763 
764 	rw_exit(&hdlp->ih_rwlock);
765 	return (ret);
766 }
767 
768 int
769 ddi_intr_block_enable(ddi_intr_handle_t *h_array, int count)
770 {
771 	ddi_intr_handle_impl_t	*hdlp;
772 	int			i, ret;
773 
774 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_enable: h_array = %p\n",
775 	    (void *)h_array));
776 
777 	if (h_array == NULL)
778 		return (DDI_EINVAL);
779 
780 	for (i = 0; i < count; i++) {
781 		hdlp = (ddi_intr_handle_impl_t *)h_array[i];
782 		rw_enter(&hdlp->ih_rwlock, RW_READER);
783 
784 		if (hdlp->ih_state != DDI_IHDL_STATE_ADDED ||
785 		    hdlp->ih_type != DDI_INTR_TYPE_MSI ||
786 		    !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) {
787 			rw_exit(&hdlp->ih_rwlock);
788 			return (DDI_EINVAL);
789 		}
790 		rw_exit(&hdlp->ih_rwlock);
791 	}
792 
793 	hdlp = (ddi_intr_handle_impl_t *)h_array[0];
794 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
795 	hdlp->ih_scratch1 = count;
796 	hdlp->ih_scratch2 = (void *)h_array;
797 
798 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
799 	    DDI_INTROP_BLOCKENABLE, hdlp, NULL);
800 
801 	rw_exit(&hdlp->ih_rwlock);
802 
803 	if (ret == DDI_SUCCESS) {
804 		for (i = 0; i < count; i++) {
805 			hdlp = (ddi_intr_handle_impl_t *)h_array[i];
806 			rw_enter(&hdlp->ih_rwlock, RW_WRITER);
807 			hdlp->ih_state = DDI_IHDL_STATE_ENABLE;
808 			rw_exit(&hdlp->ih_rwlock);
809 		}
810 	}
811 
812 	return (ret);
813 }
814 
815 int
816 ddi_intr_block_disable(ddi_intr_handle_t *h_array, int count)
817 {
818 	ddi_intr_handle_impl_t	*hdlp;
819 	int			i, ret;
820 
821 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_disable: h_array = %p\n",
822 	    (void *)h_array));
823 
824 	if (h_array == NULL)
825 		return (DDI_EINVAL);
826 
827 	for (i = 0; i < count; i++) {
828 		hdlp = (ddi_intr_handle_impl_t *)h_array[i];
829 		rw_enter(&hdlp->ih_rwlock, RW_READER);
830 		if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE ||
831 		    hdlp->ih_type != DDI_INTR_TYPE_MSI ||
832 		    !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) {
833 			rw_exit(&hdlp->ih_rwlock);
834 			return (DDI_EINVAL);
835 		}
836 		rw_exit(&hdlp->ih_rwlock);
837 	}
838 
839 	hdlp = (ddi_intr_handle_impl_t *)h_array[0];
840 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
841 	hdlp->ih_scratch1 = count;
842 	hdlp->ih_scratch2 = (void *)h_array;
843 
844 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
845 	    DDI_INTROP_BLOCKDISABLE, hdlp, NULL);
846 
847 	rw_exit(&hdlp->ih_rwlock);
848 
849 	if (ret == DDI_SUCCESS) {
850 		for (i = 0; i < count; i++) {
851 			hdlp = (ddi_intr_handle_impl_t *)h_array[i];
852 			rw_enter(&hdlp->ih_rwlock, RW_WRITER);
853 			hdlp->ih_state = DDI_IHDL_STATE_ADDED;
854 			rw_exit(&hdlp->ih_rwlock);
855 		}
856 	}
857 
858 	return (ret);
859 }
860 
861 /*
862  * Interrupt set/clr mask handlers
863  */
864 int
865 ddi_intr_set_mask(ddi_intr_handle_t h)
866 {
867 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
868 	int			ret;
869 
870 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_mask: hdlp = %p\n",
871 	    (void *)hdlp));
872 
873 	if (hdlp == NULL)
874 		return (DDI_EINVAL);
875 
876 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
877 	if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
878 	    (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE))) {
879 		rw_exit(&hdlp->ih_rwlock);
880 		return (DDI_EINVAL);
881 	}
882 
883 	ret =  i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
884 	    DDI_INTROP_SETMASK, hdlp, NULL);
885 
886 	rw_exit(&hdlp->ih_rwlock);
887 	return (ret);
888 }
889 
890 int
891 ddi_intr_clr_mask(ddi_intr_handle_t h)
892 {
893 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
894 	int			ret;
895 
896 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_clr_mask: hdlp = %p\n",
897 	    (void *)hdlp));
898 
899 	if (hdlp == NULL)
900 		return (DDI_EINVAL);
901 
902 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
903 	if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
904 	    (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE))) {
905 		rw_exit(&hdlp->ih_rwlock);
906 		return (DDI_EINVAL);
907 	}
908 
909 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
910 	    DDI_INTROP_CLRMASK, hdlp, NULL);
911 
912 	rw_exit(&hdlp->ih_rwlock);
913 	return (ret);
914 }
915 
916 /*
917  * Interrupt get_pending handler
918  */
919 int
920 ddi_intr_get_pending(ddi_intr_handle_t h, int *pendingp)
921 {
922 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
923 	int			ret;
924 
925 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pending: hdlp = %p\n",
926 	    (void *)hdlp));
927 
928 	if (hdlp == NULL)
929 		return (DDI_EINVAL);
930 
931 	rw_enter(&hdlp->ih_rwlock, RW_READER);
932 	if (!(hdlp->ih_cap & DDI_INTR_FLAG_PENDING)) {
933 		rw_exit(&hdlp->ih_rwlock);
934 		return (DDI_EINVAL);
935 	}
936 
937 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
938 	    DDI_INTROP_GETPENDING, hdlp, (void *)pendingp);
939 
940 	rw_exit(&hdlp->ih_rwlock);
941 	return (ret);
942 }
943 
944 /*
945  * Soft interrupt handlers
946  */
947 /*
948  * Add a soft interrupt and register its handler
949  */
950 /* ARGSUSED */
951 int
952 ddi_intr_add_softint(dev_info_t *dip, ddi_softint_handle_t *h_p, int soft_pri,
953     ddi_intr_handler_t handler, void *arg1)
954 {
955 	ddi_softint_hdl_impl_t	*hdlp;
956 	int			ret;
957 
958 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: dip = %p, "
959 	    "softpri = 0x%x\n", (void *)dip, soft_pri));
960 
961 	if ((dip == NULL) || (h_p == NULL) || (handler == NULL)) {
962 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: "
963 		    "invalid arguments"));
964 
965 		return (DDI_EINVAL);
966 	}
967 
968 	/* Validate input arguments */
969 	if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
970 	    soft_pri > DDI_INTR_SOFTPRI_MAX) {
971 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: invalid "
972 		    "soft_pri input given  = %x\n", soft_pri));
973 		return (DDI_EINVAL);
974 	}
975 
976 	hdlp = (ddi_softint_hdl_impl_t *)kmem_zalloc(
977 	    sizeof (ddi_softint_hdl_impl_t), KM_SLEEP);
978 
979 	/* fill up internally */
980 	rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
981 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
982 	hdlp->ih_pri = soft_pri;
983 	hdlp->ih_dip = dip;
984 	hdlp->ih_cb_func = handler;
985 	hdlp->ih_cb_arg1 = arg1;
986 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: hdlp = %p\n",
987 	    (void *)hdlp));
988 
989 	/* do the platform specific calls */
990 	if ((ret = i_ddi_add_softint(hdlp)) != DDI_SUCCESS) {
991 		rw_exit(&hdlp->ih_rwlock);
992 		rw_destroy(&hdlp->ih_rwlock);
993 		kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t));
994 		return (ret);
995 	}
996 
997 	*h_p = (ddi_softint_handle_t)hdlp;
998 	rw_exit(&hdlp->ih_rwlock);
999 	return (ret);
1000 }
1001 
1002 /*
1003  * Remove the soft interrupt
1004  */
1005 int
1006 ddi_intr_remove_softint(ddi_softint_handle_t h)
1007 {
1008 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)h;
1009 
1010 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_softint: hdlp = %p\n",
1011 	    (void *)hdlp));
1012 
1013 	if (hdlp == NULL)
1014 		return (DDI_EINVAL);
1015 
1016 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
1017 	i_ddi_remove_softint(hdlp);
1018 	rw_exit(&hdlp->ih_rwlock);
1019 	rw_destroy(&hdlp->ih_rwlock);
1020 
1021 	/* kmem_free the hdl impl_t structure allocated earlier */
1022 	kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t));
1023 	return (DDI_SUCCESS);
1024 }
1025 
1026 /*
1027  * Trigger a soft interrupt
1028  */
1029 int
1030 ddi_intr_trigger_softint(ddi_softint_handle_t h, void *arg2)
1031 {
1032 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)h;
1033 	int			ret;
1034 
1035 	if (hdlp == NULL)
1036 		return (DDI_EINVAL);
1037 
1038 	if ((ret = i_ddi_trigger_softint(hdlp, arg2)) != DDI_SUCCESS) {
1039 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_trigger_softint: failed, "
1040 		    " ret 0%x\n", ret));
1041 
1042 		return (ret);
1043 	}
1044 
1045 	hdlp->ih_cb_arg2 = arg2;
1046 	return (DDI_SUCCESS);
1047 }
1048 
1049 /*
1050  * Get the soft interrupt priority
1051  */
1052 int
1053 ddi_intr_get_softint_pri(ddi_softint_handle_t h, uint_t *soft_prip)
1054 {
1055 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)h;
1056 
1057 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_softint_pri: h = %p\n",
1058 	    (void *)h));
1059 
1060 	if (hdlp == NULL)
1061 		return (DDI_EINVAL);
1062 
1063 	rw_enter(&hdlp->ih_rwlock, RW_READER);
1064 	*soft_prip = hdlp->ih_pri;
1065 	rw_exit(&hdlp->ih_rwlock);
1066 	return (DDI_SUCCESS);
1067 }
1068 
1069 /*
1070  * Set the soft interrupt priority
1071  */
1072 int
1073 ddi_intr_set_softint_pri(ddi_softint_handle_t h, uint_t soft_pri)
1074 {
1075 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)h;
1076 	int			ret;
1077 	uint_t			orig_soft_pri;
1078 
1079 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: h = %p\n",
1080 	    (void *)h));
1081 
1082 	if (hdlp == NULL)
1083 		return (DDI_EINVAL);
1084 
1085 	/* Validate priority argument */
1086 	if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
1087 	    soft_pri > DDI_INTR_SOFTPRI_MAX) {
1088 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: invalid "
1089 		    "soft_pri input given  = %x\n", soft_pri));
1090 		return (DDI_EINVAL);
1091 	}
1092 
1093 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
1094 	orig_soft_pri = hdlp->ih_pri;
1095 	hdlp->ih_pri = soft_pri;
1096 
1097 	if ((ret = i_ddi_set_softint_pri(hdlp, orig_soft_pri)) != DDI_SUCCESS) {
1098 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: failed, "
1099 		    " ret 0%x\n", ret));
1100 		hdlp->ih_pri = orig_soft_pri;
1101 	}
1102 
1103 	rw_exit(&hdlp->ih_rwlock);
1104 	return (ret);
1105 }
1106 
1107 /*
1108  * Old DDI interrupt framework
1109  *
1110  * The following DDI interrupt interfaces are obsolete.
1111  * Use the above new DDI interrupt interfaces instead.
1112  */
1113 
1114 int
1115 ddi_intr_hilevel(dev_info_t *dip, uint_t inumber)
1116 {
1117 	ddi_intr_handle_t	hdl, *existing_hdlp;
1118 	int			actual, ret;
1119 	uint_t			high_pri, pri;
1120 
1121 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: name=%s%d dip=0x%p "
1122 	    "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1123 	    (void *)dip, inumber));
1124 
1125 	/*
1126 	 * The device driver may have already registed with the
1127 	 * framework. If so, first try to get the existing interrupt handle
1128 	 * for that given inumber and use that handle.
1129 	 */
1130 	existing_hdlp = i_ddi_get_intr_handle(dip, inumber);
1131 	if (existing_hdlp) {
1132 		hdl = existing_hdlp[0];	/* Use existing handle */
1133 	} else {
1134 		if ((ret = ddi_intr_alloc(dip, &hdl, DDI_INTR_TYPE_FIXED,
1135 		    inumber, 1, &actual,
1136 		    DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
1137 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: "
1138 			    "ddi_intr_alloc failed, ret 0x%x\n", ret));
1139 			return (0);
1140 		}
1141 	}
1142 
1143 	if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) {
1144 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: "
1145 		    "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1146 		(void) ddi_intr_free(hdl);
1147 		return (0);
1148 	}
1149 
1150 	high_pri = ddi_intr_get_hilevel_pri();
1151 
1152 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: pri = %x, "
1153 	    "high_pri = %x\n", pri, high_pri));
1154 
1155 	/* Free the handle allocated here only if no existing handle exists */
1156 	if (existing_hdlp == NULL)
1157 		(void) ddi_intr_free(hdl);
1158 
1159 	return (pri >= high_pri);
1160 }
1161 
1162 int
1163 ddi_dev_nintrs(dev_info_t *dip, int *result)
1164 {
1165 	DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: name=%s%d dip=0x%p\n",
1166 	    ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip));
1167 
1168 	if (ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED,
1169 	    result) != DDI_SUCCESS) {
1170 		DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: "
1171 		    "ddi_intr_get_nintrs failed\n"));
1172 		*result = 0;
1173 	}
1174 
1175 	return (DDI_SUCCESS);
1176 }
1177 
1178 int
1179 ddi_get_iblock_cookie(dev_info_t *dip, uint_t inumber,
1180     ddi_iblock_cookie_t *iblock_cookiep)
1181 {
1182 	ddi_intr_handle_t	hdl, *existing_hdlp;
1183 	int			actual, ret;
1184 	uint_t			pri;
1185 
1186 	DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: name=%s%d dip=0x%p "
1187 	    "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1188 	    (void *)dip, inumber));
1189 
1190 	ASSERT(iblock_cookiep != NULL);
1191 
1192 	/*
1193 	 * The device driver may have already registed with the
1194 	 * framework. If so, first try to get the existing interrupt handle
1195 	 * for that given inumber and use that handle.
1196 	 */
1197 	existing_hdlp = i_ddi_get_intr_handle(dip, inumber);
1198 	if (existing_hdlp) {
1199 		hdl = existing_hdlp[0];	/* Use existing handle */
1200 	} else {
1201 		if ((ret = ddi_intr_alloc(dip, &hdl, DDI_INTR_TYPE_FIXED,
1202 		    inumber, 1, &actual,
1203 		    DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
1204 			DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: "
1205 			    "ddi_intr_alloc failed, ret 0x%x\n", ret));
1206 			return (DDI_INTR_NOTFOUND);
1207 		}
1208 	}
1209 
1210 	if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) {
1211 		DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: "
1212 		    "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1213 
1214 		(void) ddi_intr_free(hdl);
1215 		return (DDI_FAILURE);
1216 	}
1217 
1218 	*iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri;
1219 	/* Free the handle allocated here only if no existing handle exists */
1220 	if (existing_hdlp == NULL)
1221 		(void) ddi_intr_free(hdl);
1222 
1223 	return (DDI_SUCCESS);
1224 }
1225 
1226 int
1227 ddi_add_intr(dev_info_t *dip, uint_t inumber,
1228     ddi_iblock_cookie_t *iblock_cookiep,
1229     ddi_idevice_cookie_t *idevice_cookiep,
1230     uint_t (*int_handler)(caddr_t int_handler_arg),
1231     caddr_t int_handler_arg)
1232 {
1233 	ddi_intr_handle_t	*hdl_p;
1234 	int			actual, ret;
1235 	uint_t			pri;
1236 
1237 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: name=%s%d dip=0x%p "
1238 	    "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1239 	    (void *)dip, inumber));
1240 
1241 	hdl_p = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP);
1242 
1243 	if ((ret = ddi_intr_alloc(dip, hdl_p, DDI_INTR_TYPE_FIXED,
1244 	    inumber, 1, &actual, DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
1245 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1246 		    "ddi_intr_alloc failed, ret 0x%x\n", ret));
1247 		kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1248 		return (DDI_INTR_NOTFOUND);
1249 	}
1250 
1251 	if ((ret = ddi_intr_get_pri(hdl_p[0], &pri)) != DDI_SUCCESS)  {
1252 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1253 		    "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1254 		(void) ddi_intr_free(hdl_p[0]);
1255 		kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1256 		return (DDI_FAILURE);
1257 	}
1258 
1259 	if ((ret = ddi_intr_add_handler(hdl_p[0], (ddi_intr_handler_t *)
1260 	    int_handler, int_handler_arg, NULL)) != DDI_SUCCESS) {
1261 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1262 		    "ddi_intr_add_handler failed, ret 0x%x\n", ret));
1263 		(void) ddi_intr_free(hdl_p[0]);
1264 		kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1265 		return (DDI_FAILURE);
1266 	}
1267 
1268 	if ((ret = ddi_intr_enable(hdl_p[0])) != DDI_SUCCESS) {
1269 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1270 		    "ddi_intr_enable failed, ret 0x%x\n", ret));
1271 		(void) ddi_intr_remove_handler(hdl_p[0]);
1272 		(void) ddi_intr_free(hdl_p[0]);
1273 		kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1274 		return (DDI_FAILURE);
1275 	}
1276 
1277 	if (iblock_cookiep)
1278 		*iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri;
1279 
1280 	if (idevice_cookiep) {
1281 		idevice_cookiep->idev_vector = 0;
1282 		idevice_cookiep->idev_priority = pri;
1283 	}
1284 
1285 	return (DDI_SUCCESS);
1286 }
1287 
1288 /* ARGSUSED */
1289 int
1290 ddi_add_fastintr(dev_info_t *dip, uint_t inumber,
1291     ddi_iblock_cookie_t *iblock_cookiep,
1292     ddi_idevice_cookie_t *idevice_cookiep,
1293     uint_t (*hi_int_handler)(void))
1294 {
1295 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_fastintr: name=%s%d dip=0x%p "
1296 	    "inum=0x%x: Not supported, return failure\n", ddi_driver_name(dip),
1297 	    ddi_get_instance(dip), (void *)dip, inumber));
1298 
1299 	return (DDI_FAILURE);
1300 }
1301 
1302 /* ARGSUSED */
1303 void
1304 ddi_remove_intr(dev_info_t *dip, uint_t inum, ddi_iblock_cookie_t iblock_cookie)
1305 {
1306 	ddi_intr_handle_t	*hdl_p;
1307 	int			ret;
1308 
1309 	DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: name=%s%d dip=0x%p "
1310 	    "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1311 	    (void *)dip, inum));
1312 
1313 	if ((hdl_p = i_ddi_get_intr_handle(dip, inum)) == NULL) {
1314 		DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: no handle "
1315 		    "found\n"));
1316 		return;
1317 	}
1318 
1319 	if ((ret = ddi_intr_disable(hdl_p[0])) != DDI_SUCCESS) {
1320 		DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1321 		    "ddi_intr_disable failed, ret 0x%x\n", ret));
1322 		return;
1323 	}
1324 
1325 	if ((ret = ddi_intr_remove_handler(hdl_p[0])) != DDI_SUCCESS) {
1326 		DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1327 		    "ddi_intr_remove_handler failed, ret 0x%x\n", ret));
1328 		return;
1329 	}
1330 
1331 	if ((ret = ddi_intr_free(hdl_p[0])) != DDI_SUCCESS) {
1332 		DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1333 		    "ddi_intr_free failed, ret 0x%x\n", ret));
1334 		return;
1335 	}
1336 
1337 	kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1338 }
1339 
1340 /* ARGSUSED */
1341 int
1342 ddi_get_soft_iblock_cookie(dev_info_t *dip, int preference,
1343     ddi_iblock_cookie_t *iblock_cookiep)
1344 {
1345 	DDI_INTR_APIDBG((CE_CONT, "ddi_get_soft_iblock_cookie: name=%s%d "
1346 	    "dip=0x%p pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1347 	    (void *)dip, preference));
1348 
1349 	ASSERT(iblock_cookiep != NULL);
1350 
1351 	if (preference == DDI_SOFTINT_FIXED)
1352 		return (DDI_FAILURE);
1353 
1354 	*iblock_cookiep = (ddi_iblock_cookie_t)((uintptr_t)
1355 	    ((preference > DDI_SOFTINT_MED) ? DDI_SOFT_INTR_PRI_H :
1356 	    DDI_SOFT_INTR_PRI_M));
1357 
1358 	return (DDI_SUCCESS);
1359 }
1360 
1361 int
1362 ddi_add_softintr(dev_info_t *dip, int preference, ddi_softintr_t *idp,
1363     ddi_iblock_cookie_t *iblock_cookiep,
1364     ddi_idevice_cookie_t *idevice_cookiep,
1365     uint_t (*int_handler)(caddr_t int_handler_arg),
1366     caddr_t int_handler_arg)
1367 {
1368 	ddi_softint_handle_t	*hdl_p;
1369 	uint64_t		softpri;
1370 	int			ret;
1371 
1372 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: name=%s%d dip=0x%p "
1373 	    "pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1374 	    (void *)dip, preference));
1375 
1376 	if ((idp == NULL) || ((preference == DDI_SOFTINT_FIXED) &&
1377 	    (iblock_cookiep == NULL)))
1378 		return (DDI_FAILURE);
1379 
1380 	/* Translate the priority preference */
1381 	if (preference == DDI_SOFTINT_FIXED) {
1382 		softpri = (uint64_t)(uintptr_t)*iblock_cookiep;
1383 		softpri = MIN(softpri, DDI_SOFT_INTR_PRI_H);
1384 	} else {
1385 		softpri = (uint64_t)((preference > DDI_SOFTINT_MED) ?
1386 		    DDI_SOFT_INTR_PRI_H : DDI_SOFT_INTR_PRI_M);
1387 	}
1388 
1389 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: preference 0x%x "
1390 	    "softpri 0x%lx\n", preference, (long)softpri));
1391 
1392 	hdl_p = kmem_zalloc(sizeof (ddi_softint_handle_t), KM_SLEEP);
1393 	if ((ret = ddi_intr_add_softint(dip, hdl_p, softpri,
1394 	    (ddi_intr_handler_t *)int_handler, int_handler_arg)) !=
1395 	    DDI_SUCCESS) {
1396 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: "
1397 		    "ddi_intr_add_softint failed, ret 0x%x\n", ret));
1398 
1399 		kmem_free(hdl_p, sizeof (ddi_softint_handle_t));
1400 		return (DDI_FAILURE);
1401 	}
1402 
1403 	if (iblock_cookiep)
1404 		*iblock_cookiep =  (ddi_iblock_cookie_t)(uintptr_t)softpri;
1405 
1406 	if (idevice_cookiep) {
1407 		idevice_cookiep->idev_vector = 0;
1408 		idevice_cookiep->idev_priority = softpri;
1409 	}
1410 
1411 	*idp = (ddi_softintr_t)hdl_p;
1412 
1413 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: dip = 0x%p, "
1414 	    "idp = 0x%p, ret = %x\n", (void *)dip, (void *)*idp, ret));
1415 
1416 	return (DDI_SUCCESS);
1417 }
1418 
1419 void
1420 ddi_remove_softintr(ddi_softintr_t id)
1421 {
1422 	ddi_softint_handle_t	*h_p = (ddi_softint_handle_t *)id;
1423 
1424 	DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: id=0x%p\n",
1425 	    (void *)id));
1426 
1427 	if (h_p == NULL)
1428 		return;
1429 
1430 	DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: handle 0x%p\n",
1431 	    (void *)h_p));
1432 
1433 	(void) ddi_intr_remove_softint(*h_p);
1434 	kmem_free(h_p, sizeof (ddi_softint_handle_t));
1435 }
1436 
1437 void
1438 ddi_trigger_softintr(ddi_softintr_t id)
1439 {
1440 	ddi_softint_handle_t	*h_p = (ddi_softint_handle_t *)id;
1441 	int			ret;
1442 
1443 	if (h_p == NULL)
1444 		return;
1445 
1446 	if ((ret = ddi_intr_trigger_softint(*h_p, NULL)) != DDI_SUCCESS) {
1447 		DDI_INTR_APIDBG((CE_CONT, "ddi_trigger_softintr: "
1448 		    "ddi_intr_trigger_softint failed, hdlp 0x%p "
1449 		    "ret 0x%x\n", (void *)h_p, ret));
1450 	}
1451 }
1452