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