xref: /titanic_51/usr/src/uts/common/os/ddi_intr.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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 		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 		rw_destroy(&hdlp->ih_rwlock);
372 		kmem_free(hdlp, sizeof (ddi_intr_handle_impl_t));
373 	}
374 
375 	return (ret);
376 }
377 
378 /*
379  * Interrupt get/set capacity functions
380  *
381  * The logic used to figure this out is shown here:
382  *
383  *			Device level		Platform level	    Intr source
384  * 1. Fixed interrupts
385  * (non-PCI)
386  * o Flags supported	N/A			Maskable/Pending/    rootnex
387  *						No Block Enable
388  * o navail					1
389  *
390  * 2. PCI Fixed interrupts
391  * o Flags supported	pending/Maskable	Maskable/pending/    pci
392  *						No Block enable
393  * o navail		N/A			1
394  *
395  * 3. PCI MSI
396  * o Flags supported	Maskable/Pending	Maskable/Pending    pci
397  *			Block Enable		(if drvr doesn't)   Block Enable
398  * o navail		N/A			#vectors - #used    N/A
399  *
400  * 4. PCI MSI-X
401  * o Flags supported	Maskable/Pending	Maskable/Pending    pci
402  *			Block Enable				    Block Enable
403  * o navail		N/A			#vectors - #used    N/A
404  *
405  * where:
406  *	#vectors	- Total numbers of vectors available
407  *	#used		- Total numbers of vectors currently being used
408  *
409  * For devices complying to PCI2.3 or greater, see bit10 of Command Register
410  * 0 - enables assertion of INTx
411  * 1 - disables assertion of INTx
412  *
413  * For non MSI/X interrupts; if the IRQ is shared then all ddi_intr_set_*()
414  * operations return failure.
415  */
416 int
417 ddi_intr_get_cap(ddi_intr_handle_t h, int *flagsp)
418 {
419 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
420 	int			ret;
421 
422 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_cap: hdlp = %p\n",
423 	    (void *)hdlp));
424 
425 	*flagsp = 0;
426 	if (hdlp == NULL)
427 		return (DDI_EINVAL);
428 
429 	rw_enter(&hdlp->ih_rwlock, RW_READER);
430 
431 	if (hdlp->ih_cap) {
432 		*flagsp = hdlp->ih_cap & ~DDI_INTR_FLAG_MSI64;
433 		rw_exit(&hdlp->ih_rwlock);
434 		return (DDI_SUCCESS);
435 	}
436 
437 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
438 	    DDI_INTROP_GETCAP, hdlp, (void *)flagsp);
439 
440 	if (ret == DDI_SUCCESS) {
441 		hdlp->ih_cap = *flagsp;
442 
443 		/* Mask out MSI/X 64-bit support to the consumer */
444 		*flagsp &= ~DDI_INTR_FLAG_MSI64;
445 	}
446 
447 	rw_exit(&hdlp->ih_rwlock);
448 	return (ret);
449 }
450 
451 int
452 ddi_intr_set_cap(ddi_intr_handle_t h, int flags)
453 {
454 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
455 	int			ret;
456 
457 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_cap: hdlp = %p", (void *)hdlp));
458 
459 	if (hdlp == NULL)
460 		return (DDI_EINVAL);
461 
462 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
463 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
464 		rw_exit(&hdlp->ih_rwlock);
465 		return (DDI_EINVAL);
466 	}
467 
468 	/* Only DDI_INTR_FLAG_LEVEL or DDI_INTR_FLAG_EDGE are allowed */
469 	if (!(flags & (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_LEVEL))) {
470 		DDI_INTR_APIDBG((CE_CONT, "%s%d: only LEVEL or EDGE capability "
471 		    "can be set\n", ddi_driver_name(hdlp->ih_dip),
472 		    ddi_get_instance(hdlp->ih_dip)));
473 		rw_exit(&hdlp->ih_rwlock);
474 		return (DDI_EINVAL);
475 	}
476 
477 	/* Both level/edge flags must be currently supported */
478 	if (!(hdlp->ih_cap & (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_LEVEL))) {
479 		DDI_INTR_APIDBG((CE_CONT, "%s%d: Both LEVEL and EDGE capability"
480 		    " must be supported\n", ddi_driver_name(hdlp->ih_dip),
481 		    ddi_get_instance(hdlp->ih_dip)));
482 		rw_exit(&hdlp->ih_rwlock);
483 		return (DDI_ENOTSUP);
484 	}
485 
486 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
487 	    DDI_INTROP_SETCAP, hdlp, &flags);
488 
489 	rw_exit(&hdlp->ih_rwlock);
490 	return (ret);
491 }
492 
493 /*
494  * Priority related functions
495  */
496 
497 /*
498  * ddi_intr_get_hilevel_pri:
499  *	Returns the minimum priority level for a
500  *	high-level interrupt on a platform.
501  */
502 uint_t
503 ddi_intr_get_hilevel_pri(void)
504 {
505 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_hilevel_pri:\n"));
506 	return (LOCK_LEVEL + 1);
507 }
508 
509 int
510 ddi_intr_get_pri(ddi_intr_handle_t h, uint_t *prip)
511 {
512 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
513 	int			ret;
514 
515 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pri: hdlp = %p\n",
516 	    (void *)hdlp));
517 
518 	*prip = 0;
519 	if (hdlp == NULL)
520 		return (DDI_EINVAL);
521 
522 	rw_enter(&hdlp->ih_rwlock, RW_READER);
523 	/* Already initialized, just return that */
524 	if (hdlp->ih_pri) {
525 		*prip = hdlp->ih_pri;
526 		rw_exit(&hdlp->ih_rwlock);
527 		return (DDI_SUCCESS);
528 	}
529 
530 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
531 	    DDI_INTROP_GETPRI, hdlp, (void *)prip);
532 
533 	if (ret == DDI_SUCCESS)
534 		hdlp->ih_pri = *prip;
535 
536 	rw_exit(&hdlp->ih_rwlock);
537 	return (ret);
538 }
539 
540 int
541 ddi_intr_set_pri(ddi_intr_handle_t h, uint_t pri)
542 {
543 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
544 	int			ret;
545 
546 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_pri: hdlp = %p", (void *)hdlp));
547 
548 	if (hdlp == NULL)
549 		return (DDI_EINVAL);
550 
551 	/* Validate priority argument */
552 	if (pri < DDI_INTR_PRI_MIN || pri > DDI_INTR_PRI_MAX) {
553 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_pri: invalid priority "
554 		    "specified  = %x\n", pri));
555 		return (DDI_EINVAL);
556 	}
557 
558 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
559 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
560 		rw_exit(&hdlp->ih_rwlock);
561 		return (DDI_EINVAL);
562 	}
563 
564 	/* If the passed priority is same as existing priority; do nothing */
565 	if (pri == hdlp->ih_pri) {
566 		rw_exit(&hdlp->ih_rwlock);
567 		return (DDI_SUCCESS);
568 	}
569 
570 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
571 	    DDI_INTROP_SETPRI, hdlp, &pri);
572 
573 	if (ret == DDI_SUCCESS)
574 		hdlp->ih_pri = pri;
575 
576 	rw_exit(&hdlp->ih_rwlock);
577 	return (ret);
578 }
579 
580 /*
581  * Interrupt add/duplicate/remove handlers
582  */
583 int
584 ddi_intr_add_handler(ddi_intr_handle_t h, ddi_intr_handler_t inthandler,
585     void *arg1, void *arg2)
586 {
587 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
588 	int			ret;
589 
590 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_handler: hdlp = 0x%p\n",
591 	    (void *)hdlp));
592 
593 	if ((hdlp == NULL) || (inthandler == NULL))
594 		return (DDI_EINVAL);
595 
596 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
597 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
598 		rw_exit(&hdlp->ih_rwlock);
599 		return (DDI_EINVAL);
600 	}
601 
602 	hdlp->ih_cb_func = inthandler;
603 	hdlp->ih_cb_arg1 = arg1;
604 	hdlp->ih_cb_arg2 = arg2;
605 
606 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
607 	    DDI_INTROP_ADDISR, hdlp, NULL);
608 
609 	if (ret != DDI_SUCCESS) {
610 		hdlp->ih_cb_func = NULL;
611 		hdlp->ih_cb_arg1 = NULL;
612 		hdlp->ih_cb_arg2 = NULL;
613 	} else
614 		hdlp->ih_state = DDI_IHDL_STATE_ADDED;
615 
616 	rw_exit(&hdlp->ih_rwlock);
617 	return (ret);
618 }
619 
620 int
621 ddi_intr_dup_handler(ddi_intr_handle_t org, int vector, ddi_intr_handle_t *dup)
622 {
623 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)org;
624 	ddi_intr_handle_impl_t	*dup_hdlp;
625 	int			ret;
626 
627 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_dup_handler: hdlp = 0x%p\n",
628 	    (void *)hdlp));
629 
630 	/* Do some input argument checking ("dup" is not allocated) */
631 	if ((hdlp == NULL) || (dup != NULL))
632 		return (DDI_EINVAL);
633 
634 	rw_enter(&hdlp->ih_rwlock, RW_READER);
635 
636 	/* Do some input argument checking */
637 	if ((hdlp->ih_state == DDI_IHDL_STATE_ALLOC) ||	/* intr handle alloc? */
638 	    (hdlp->ih_type != DDI_INTR_TYPE_MSIX)) {	/* only MSI-X allowed */
639 		rw_exit(&hdlp->ih_rwlock);
640 		return (DDI_EINVAL);
641 	}
642 
643 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
644 	    DDI_INTROP_DUPVEC, hdlp, (void *)&vector);
645 
646 	if (ret == DDI_SUCCESS) {
647 		dup_hdlp = (ddi_intr_handle_impl_t *)
648 		    kmem_zalloc(sizeof (ddi_intr_handle_impl_t), KM_SLEEP);
649 
650 		dup = (ddi_intr_handle_t *)dup_hdlp;
651 		rw_init(&dup_hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
652 		rw_enter(&dup_hdlp->ih_rwlock, RW_WRITER);
653 		dup_hdlp->ih_ver = DDI_INTR_VERSION;
654 		dup_hdlp->ih_state = DDI_IHDL_STATE_ADDED;
655 		dup_hdlp->ih_dip = hdlp->ih_dip;
656 		dup_hdlp->ih_type = hdlp->ih_type;
657 		dup_hdlp->ih_pri = hdlp->ih_pri;
658 		dup_hdlp->ih_cap = hdlp->ih_cap;
659 		dup_hdlp->ih_inum = hdlp->ih_inum;
660 		/* What about MSI-X vector */
661 
662 		dup_hdlp->ih_cb_func = hdlp->ih_cb_func;
663 		dup_hdlp->ih_cb_arg1 = hdlp->ih_cb_arg1;
664 		dup_hdlp->ih_cb_arg2 = hdlp->ih_cb_arg2;
665 		rw_exit(&dup_hdlp->ih_rwlock);
666 	}
667 
668 	rw_exit(&hdlp->ih_rwlock);
669 	return (ret);
670 }
671 
672 int
673 ddi_intr_remove_handler(ddi_intr_handle_t h)
674 {
675 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
676 	int			ret;
677 
678 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_handler: hdlp = %p\n",
679 	    (void *)hdlp));
680 
681 	if (hdlp == NULL)
682 		return (DDI_EINVAL);
683 
684 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
685 	if (hdlp->ih_state != DDI_IHDL_STATE_ADDED) {
686 		rw_exit(&hdlp->ih_rwlock);
687 		return (DDI_EINVAL);
688 	}
689 
690 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
691 	    DDI_INTROP_REMISR, hdlp, NULL);
692 
693 	if (ret == DDI_SUCCESS) {
694 		hdlp->ih_state = DDI_IHDL_STATE_ALLOC;
695 		hdlp->ih_cb_func = NULL;
696 		hdlp->ih_cb_arg1 = NULL;
697 		hdlp->ih_cb_arg2 = NULL;
698 	}
699 
700 	rw_exit(&hdlp->ih_rwlock);
701 	return (ret);
702 }
703 
704 /*
705  * Interrupt enable/disable/block_enable/block_disable handlers
706  */
707 int
708 ddi_intr_enable(ddi_intr_handle_t h)
709 {
710 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
711 	int			ret;
712 
713 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_enable: hdlp = %p\n",
714 	    (void *)hdlp));
715 
716 	if (hdlp == NULL)
717 		return (DDI_EINVAL);
718 
719 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
720 	if ((hdlp->ih_state != DDI_IHDL_STATE_ADDED) ||
721 	    ((hdlp->ih_type == DDI_INTR_TYPE_MSI) &&
722 	    (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) {
723 		rw_exit(&hdlp->ih_rwlock);
724 		return (DDI_EINVAL);
725 	}
726 
727 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
728 	    DDI_INTROP_ENABLE, hdlp, NULL);
729 
730 	if (ret == DDI_SUCCESS)
731 		hdlp->ih_state = DDI_IHDL_STATE_ENABLE;
732 
733 	rw_exit(&hdlp->ih_rwlock);
734 	return (ret);
735 }
736 
737 int
738 ddi_intr_disable(ddi_intr_handle_t h)
739 {
740 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
741 	int			ret;
742 
743 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_disable: hdlp = %p\n",
744 	    (void *)hdlp));
745 
746 	if (hdlp == NULL)
747 		return (DDI_EINVAL);
748 
749 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
750 	if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
751 	    ((hdlp->ih_type == DDI_INTR_TYPE_MSI) &&
752 	    (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) {
753 		rw_exit(&hdlp->ih_rwlock);
754 		return (DDI_EINVAL);
755 	}
756 
757 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
758 	    DDI_INTROP_DISABLE, hdlp, NULL);
759 
760 	if (ret == DDI_SUCCESS)
761 		hdlp->ih_state = DDI_IHDL_STATE_ADDED;
762 
763 	rw_exit(&hdlp->ih_rwlock);
764 	return (ret);
765 }
766 
767 int
768 ddi_intr_block_enable(ddi_intr_handle_t *h_array, int count)
769 {
770 	ddi_intr_handle_impl_t	*hdlp;
771 	int			i, ret;
772 
773 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_enable: h_array = %p\n",
774 	    (void *)h_array));
775 
776 	if (h_array == NULL)
777 		return (DDI_EINVAL);
778 
779 	for (i = 0; i < count; i++) {
780 		hdlp = (ddi_intr_handle_impl_t *)h_array[i];
781 		rw_enter(&hdlp->ih_rwlock, RW_READER);
782 
783 		if (hdlp->ih_state != DDI_IHDL_STATE_ADDED ||
784 		    hdlp->ih_type != DDI_INTR_TYPE_MSI ||
785 		    !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) {
786 			rw_exit(&hdlp->ih_rwlock);
787 			return (DDI_EINVAL);
788 		}
789 		rw_exit(&hdlp->ih_rwlock);
790 	}
791 
792 	hdlp = (ddi_intr_handle_impl_t *)h_array[0];
793 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
794 	hdlp->ih_scratch1 = count;
795 
796 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
797 	    DDI_INTROP_BLOCKENABLE, hdlp, NULL);
798 
799 	rw_exit(&hdlp->ih_rwlock);
800 
801 	if (ret == DDI_SUCCESS) {
802 		for (i = 0; i < count; i++) {
803 			hdlp = (ddi_intr_handle_impl_t *)h_array[i];
804 			rw_enter(&hdlp->ih_rwlock, RW_WRITER);
805 			hdlp->ih_state = DDI_IHDL_STATE_ENABLE;
806 			rw_exit(&hdlp->ih_rwlock);
807 		}
808 	}
809 
810 	return (ret);
811 }
812 
813 int
814 ddi_intr_block_disable(ddi_intr_handle_t *h_array, int count)
815 {
816 	ddi_intr_handle_impl_t	*hdlp;
817 	int			i, ret;
818 
819 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_disable: h_array = %p\n",
820 	    (void *)h_array));
821 
822 	if (h_array == NULL)
823 		return (DDI_EINVAL);
824 
825 	for (i = 0; i < count; i++) {
826 		hdlp = (ddi_intr_handle_impl_t *)h_array[i];
827 		rw_enter(&hdlp->ih_rwlock, RW_READER);
828 		if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE ||
829 		    hdlp->ih_type != DDI_INTR_TYPE_MSI ||
830 		    !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) {
831 			rw_exit(&hdlp->ih_rwlock);
832 			return (DDI_EINVAL);
833 		}
834 		rw_exit(&hdlp->ih_rwlock);
835 	}
836 
837 	hdlp = (ddi_intr_handle_impl_t *)h_array[0];
838 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
839 	hdlp->ih_scratch1 = count;
840 
841 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
842 	    DDI_INTROP_BLOCKDISABLE, hdlp, NULL);
843 
844 	rw_exit(&hdlp->ih_rwlock);
845 
846 	if (ret == DDI_SUCCESS) {
847 		for (i = 0; i < count; i++) {
848 			hdlp = (ddi_intr_handle_impl_t *)h_array[i];
849 			rw_enter(&hdlp->ih_rwlock, RW_WRITER);
850 			hdlp->ih_state = DDI_IHDL_STATE_ADDED;
851 			rw_exit(&hdlp->ih_rwlock);
852 		}
853 	}
854 
855 	return (ret);
856 }
857 
858 /*
859  * Interrupt set/clr mask handlers
860  */
861 int
862 ddi_intr_set_mask(ddi_intr_handle_t h)
863 {
864 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
865 	int			ret;
866 
867 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_mask: hdlp = %p\n",
868 	    (void *)hdlp));
869 
870 	if (hdlp == NULL)
871 		return (DDI_EINVAL);
872 
873 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
874 	if (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE)) {
875 		rw_exit(&hdlp->ih_rwlock);
876 		return (DDI_EINVAL);
877 	}
878 
879 	ret =  i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
880 	    DDI_INTROP_SETMASK, hdlp, NULL);
881 
882 	rw_exit(&hdlp->ih_rwlock);
883 	return (ret);
884 }
885 
886 int
887 ddi_intr_clr_mask(ddi_intr_handle_t h)
888 {
889 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
890 	int			ret;
891 
892 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_clr_mask: hdlp = %p\n",
893 	    (void *)hdlp));
894 
895 	if (hdlp == NULL)
896 		return (DDI_EINVAL);
897 
898 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
899 	if (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE)) {
900 		rw_exit(&hdlp->ih_rwlock);
901 		return (DDI_EINVAL);
902 	}
903 
904 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
905 	    DDI_INTROP_CLRMASK, hdlp, NULL);
906 
907 	rw_exit(&hdlp->ih_rwlock);
908 	return (ret);
909 }
910 
911 /*
912  * Interrupt get_pending handler
913  */
914 int
915 ddi_intr_get_pending(ddi_intr_handle_t h, int *pendingp)
916 {
917 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
918 	int			ret;
919 
920 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pending: hdlp = %p\n",
921 	    (void *)hdlp));
922 
923 	if (hdlp == NULL)
924 		return (DDI_EINVAL);
925 
926 	rw_enter(&hdlp->ih_rwlock, RW_READER);
927 	if (!(hdlp->ih_cap & DDI_INTR_FLAG_PENDING)) {
928 		rw_exit(&hdlp->ih_rwlock);
929 		return (DDI_EINVAL);
930 	}
931 
932 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
933 	    DDI_INTROP_GETPENDING, hdlp, (void *)pendingp);
934 
935 	rw_exit(&hdlp->ih_rwlock);
936 	return (ret);
937 }
938 
939 /*
940  * Soft interrupt handlers
941  */
942 /*
943  * Add a soft interrupt and register its handler
944  */
945 /* ARGSUSED */
946 int
947 ddi_intr_add_softint(dev_info_t *dip, ddi_softint_handle_t *h_p, int soft_pri,
948     ddi_intr_handler_t handler, void *arg1)
949 {
950 	ddi_softint_hdl_impl_t	*hdlp;
951 	int			ret;
952 
953 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: dip = %p, "
954 	    "softpri = 0x%x\n", (void *)dip, soft_pri));
955 
956 	if ((dip == NULL) || (h_p == NULL) || (handler == NULL)) {
957 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: "
958 		    "invalid arguments"));
959 
960 		return (DDI_EINVAL);
961 	}
962 
963 	/* Validate input arguments */
964 	if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
965 	    soft_pri > DDI_INTR_SOFTPRI_MAX) {
966 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: invalid "
967 		    "soft_pri input given  = %x\n", soft_pri));
968 		return (DDI_EINVAL);
969 	}
970 
971 	hdlp = (ddi_softint_hdl_impl_t *)kmem_zalloc(
972 	    sizeof (ddi_softint_hdl_impl_t), KM_SLEEP);
973 
974 	/* fill up internally */
975 	rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
976 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
977 	hdlp->ih_pri = soft_pri;
978 	hdlp->ih_dip = dip;
979 	hdlp->ih_cb_func = handler;
980 	hdlp->ih_cb_arg1 = arg1;
981 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: hdlp = %p\n",
982 	    (void *)hdlp));
983 
984 	/* do the platform specific calls */
985 	if ((ret = i_ddi_add_softint(hdlp)) != DDI_SUCCESS) {
986 		rw_exit(&hdlp->ih_rwlock);
987 		rw_destroy(&hdlp->ih_rwlock);
988 		kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t));
989 		return (ret);
990 	}
991 
992 	*h_p = (ddi_softint_handle_t)hdlp;
993 	rw_exit(&hdlp->ih_rwlock);
994 	return (ret);
995 }
996 
997 /*
998  * Remove the soft interrupt
999  */
1000 int
1001 ddi_intr_remove_softint(ddi_softint_handle_t h)
1002 {
1003 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)h;
1004 
1005 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_softint: hdlp = %p\n",
1006 	    (void *)hdlp));
1007 
1008 	if (hdlp == NULL)
1009 		return (DDI_EINVAL);
1010 
1011 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
1012 	i_ddi_remove_softint(hdlp);
1013 	rw_exit(&hdlp->ih_rwlock);
1014 	rw_destroy(&hdlp->ih_rwlock);
1015 
1016 	/* kmem_free the hdl impl_t structure allocated earlier */
1017 	kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t));
1018 	return (DDI_SUCCESS);
1019 }
1020 
1021 /*
1022  * Trigger a soft interrupt
1023  */
1024 int
1025 ddi_intr_trigger_softint(ddi_softint_handle_t h, void *arg2)
1026 {
1027 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)h;
1028 	int			ret;
1029 
1030 	if (hdlp == NULL)
1031 		return (DDI_EINVAL);
1032 
1033 	if ((ret = i_ddi_trigger_softint(hdlp, arg2)) != DDI_SUCCESS) {
1034 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_trigger_softint: failed, "
1035 		    " ret 0%x\n", ret));
1036 
1037 		return (ret);
1038 	}
1039 
1040 	hdlp->ih_cb_arg2 = arg2;
1041 	return (DDI_SUCCESS);
1042 }
1043 
1044 /*
1045  * Get the soft interrupt priority
1046  */
1047 int
1048 ddi_intr_get_softint_pri(ddi_softint_handle_t h, uint_t *soft_prip)
1049 {
1050 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)h;
1051 
1052 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_softint_pri: h = %p\n",
1053 	    (void *)h));
1054 
1055 	if (hdlp == NULL)
1056 		return (DDI_EINVAL);
1057 
1058 	rw_enter(&hdlp->ih_rwlock, RW_READER);
1059 	*soft_prip = hdlp->ih_pri;
1060 	rw_exit(&hdlp->ih_rwlock);
1061 	return (DDI_SUCCESS);
1062 }
1063 
1064 /*
1065  * Set the soft interrupt priority
1066  */
1067 int
1068 ddi_intr_set_softint_pri(ddi_softint_handle_t h, uint_t soft_pri)
1069 {
1070 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)h;
1071 	int			ret;
1072 	uint_t			orig_soft_pri;
1073 
1074 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: h = %p\n",
1075 	    (void *)h));
1076 
1077 	if (hdlp == NULL)
1078 		return (DDI_EINVAL);
1079 
1080 	/* Validate priority argument */
1081 	if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
1082 	    soft_pri > DDI_INTR_SOFTPRI_MAX) {
1083 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: invalid "
1084 		    "soft_pri input given  = %x\n", soft_pri));
1085 		return (DDI_EINVAL);
1086 	}
1087 
1088 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
1089 	orig_soft_pri = hdlp->ih_pri;
1090 	hdlp->ih_pri = soft_pri;
1091 
1092 	if ((ret = i_ddi_set_softint_pri(hdlp, orig_soft_pri)) != DDI_SUCCESS) {
1093 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: failed, "
1094 		    " ret 0%x\n", ret));
1095 		hdlp->ih_pri = orig_soft_pri;
1096 	}
1097 
1098 	rw_exit(&hdlp->ih_rwlock);
1099 	return (ret);
1100 }
1101 
1102 /*
1103  * Old DDI interrupt framework
1104  *
1105  * The following DDI interrupt interfaces are obsolete.
1106  * Use the above new DDI interrupt interfaces instead.
1107  */
1108 
1109 int
1110 ddi_intr_hilevel(dev_info_t *dip, uint_t inumber)
1111 {
1112 	ddi_intr_handle_t	hdl, *existing_hdlp;
1113 	int			actual, ret;
1114 	uint_t			high_pri, pri;
1115 
1116 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: name=%s%d dip=0x%p "
1117 	    "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1118 	    (void *)dip, inumber));
1119 
1120 	/*
1121 	 * The device driver may have already registed with the
1122 	 * framework. If so, first try to get the existing interrupt handle
1123 	 * for that given inumber and use that handle.
1124 	 */
1125 	existing_hdlp = i_ddi_get_intr_handle(dip, inumber);
1126 	if (existing_hdlp) {
1127 		hdl = existing_hdlp[0];	/* Use existing handle */
1128 	} else {
1129 		if ((ret = ddi_intr_alloc(dip, &hdl, DDI_INTR_TYPE_FIXED,
1130 		    inumber, 1, &actual, 0)) != DDI_SUCCESS) {
1131 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: "
1132 			    "ddi_intr_alloc failed, ret 0x%x\n", ret));
1133 			return (0);
1134 		}
1135 	}
1136 
1137 	if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) {
1138 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: "
1139 		    "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1140 		(void) ddi_intr_free(hdl);
1141 		return (0);
1142 	}
1143 
1144 	high_pri = ddi_intr_get_hilevel_pri();
1145 
1146 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: pri = %x, "
1147 	    "high_pri = %x\n", pri, high_pri));
1148 
1149 	/* Free the handle allocated here only if no existing handle exists */
1150 	if (existing_hdlp == NULL)
1151 		(void) ddi_intr_free(hdl);
1152 
1153 	return (pri >= high_pri);
1154 }
1155 
1156 int
1157 ddi_dev_nintrs(dev_info_t *dip, int *result)
1158 {
1159 	DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: name=%s%d dip=0x%p\n",
1160 	    ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip));
1161 
1162 	if (ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED,
1163 	    result) != DDI_SUCCESS) {
1164 		DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: "
1165 		    "ddi_intr_get_nintrs failed\n"));
1166 		*result = 0;
1167 	}
1168 
1169 	return (DDI_SUCCESS);
1170 }
1171 
1172 int
1173 ddi_get_iblock_cookie(dev_info_t *dip, uint_t inumber,
1174     ddi_iblock_cookie_t *iblock_cookiep)
1175 {
1176 	ddi_intr_handle_t	hdl, *existing_hdlp;
1177 	int			actual, ret;
1178 	uint_t			pri;
1179 
1180 	DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: name=%s%d dip=0x%p "
1181 	    "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1182 	    (void *)dip, inumber));
1183 
1184 	ASSERT(iblock_cookiep != NULL);
1185 
1186 	/*
1187 	 * The device driver may have already registed with the
1188 	 * framework. If so, first try to get the existing interrupt handle
1189 	 * for that given inumber and use that handle.
1190 	 */
1191 	existing_hdlp = i_ddi_get_intr_handle(dip, inumber);
1192 	if (existing_hdlp) {
1193 		hdl = existing_hdlp[0];	/* Use existing handle */
1194 	} else {
1195 		if ((ret = ddi_intr_alloc(dip, &hdl, DDI_INTR_TYPE_FIXED,
1196 		    inumber, 1, &actual, 0)) != DDI_SUCCESS) {
1197 			DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: "
1198 			    "ddi_intr_alloc failed, ret 0x%x\n", ret));
1199 			return (DDI_INTR_NOTFOUND);
1200 		}
1201 	}
1202 
1203 	if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) {
1204 		DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: "
1205 		    "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1206 
1207 		(void) ddi_intr_free(hdl);
1208 		return (DDI_FAILURE);
1209 	}
1210 
1211 	*iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri;
1212 	/* Free the handle allocated here only if no existing handle exists */
1213 	if (existing_hdlp == NULL)
1214 		(void) ddi_intr_free(hdl);
1215 
1216 	return (DDI_SUCCESS);
1217 }
1218 
1219 int
1220 ddi_add_intr(dev_info_t *dip, uint_t inumber,
1221     ddi_iblock_cookie_t *iblock_cookiep,
1222     ddi_idevice_cookie_t *idevice_cookiep,
1223     uint_t (*int_handler)(caddr_t int_handler_arg),
1224     caddr_t int_handler_arg)
1225 {
1226 	ddi_intr_handle_t	*hdl_p;
1227 	int			actual, ret;
1228 	uint_t			pri;
1229 
1230 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: name=%s%d dip=0x%p "
1231 	    "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1232 	    (void *)dip, inumber));
1233 
1234 	hdl_p = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP);
1235 
1236 	if ((ret = ddi_intr_alloc(dip, hdl_p, DDI_INTR_TYPE_FIXED,
1237 	    inumber, 1, &actual, 0)) != DDI_SUCCESS) {
1238 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1239 		    "ddi_intr_alloc failed, ret 0x%x\n", ret));
1240 		kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1241 		return (DDI_INTR_NOTFOUND);
1242 	}
1243 
1244 	if ((ret = ddi_intr_get_pri(hdl_p[0], &pri)) != DDI_SUCCESS)  {
1245 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1246 		    "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1247 		(void) ddi_intr_free(hdl_p[0]);
1248 		kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1249 		return (DDI_FAILURE);
1250 	}
1251 
1252 	if ((ret = ddi_intr_add_handler(hdl_p[0], (ddi_intr_handler_t *)
1253 	    int_handler, int_handler_arg, NULL)) != DDI_SUCCESS) {
1254 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1255 		    "ddi_intr_add_handler failed, ret 0x%x\n", ret));
1256 		(void) ddi_intr_free(hdl_p[0]);
1257 		kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1258 		return (DDI_FAILURE);
1259 	}
1260 
1261 	if ((ret = ddi_intr_enable(hdl_p[0])) != DDI_SUCCESS) {
1262 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1263 		    "ddi_intr_enable failed, ret 0x%x\n", ret));
1264 		(void) ddi_intr_remove_handler(hdl_p[0]);
1265 		(void) ddi_intr_free(hdl_p[0]);
1266 		kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1267 		return (DDI_FAILURE);
1268 	}
1269 
1270 	if (iblock_cookiep)
1271 		*iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri;
1272 
1273 	if (idevice_cookiep) {
1274 		idevice_cookiep->idev_vector = 0;
1275 		idevice_cookiep->idev_priority = pri;
1276 	}
1277 
1278 	return (DDI_SUCCESS);
1279 }
1280 
1281 /* ARGSUSED */
1282 int
1283 ddi_add_fastintr(dev_info_t *dip, uint_t inumber,
1284     ddi_iblock_cookie_t *iblock_cookiep,
1285     ddi_idevice_cookie_t *idevice_cookiep,
1286     uint_t (*hi_int_handler)(void))
1287 {
1288 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_fastintr: name=%s%d dip=0x%p "
1289 	    "inum=0x%x: Not supported, return failure\n", ddi_driver_name(dip),
1290 	    ddi_get_instance(dip), (void *)dip, inumber));
1291 
1292 	return (DDI_FAILURE);
1293 }
1294 
1295 /* ARGSUSED */
1296 void
1297 ddi_remove_intr(dev_info_t *dip, uint_t inum, ddi_iblock_cookie_t iblock_cookie)
1298 {
1299 	ddi_intr_handle_t	*hdl_p;
1300 	int			ret;
1301 
1302 	DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: name=%s%d dip=0x%p "
1303 	    "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1304 	    (void *)dip, inum));
1305 
1306 	if ((hdl_p = i_ddi_get_intr_handle(dip, inum)) == NULL) {
1307 		DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: no handle "
1308 		    "found\n"));
1309 		return;
1310 	}
1311 
1312 	if ((ret = ddi_intr_disable(hdl_p[0])) != DDI_SUCCESS) {
1313 		DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1314 		    "ddi_intr_disable failed, ret 0x%x\n", ret));
1315 		return;
1316 	}
1317 
1318 	if ((ret = ddi_intr_remove_handler(hdl_p[0])) != DDI_SUCCESS) {
1319 		DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1320 		    "ddi_intr_remove_handler failed, ret 0x%x\n", ret));
1321 		return;
1322 	}
1323 
1324 	if ((ret = ddi_intr_free(hdl_p[0])) != DDI_SUCCESS) {
1325 		DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1326 		    "ddi_intr_free failed, ret 0x%x\n", ret));
1327 		return;
1328 	}
1329 
1330 	kmem_free(hdl_p, sizeof (ddi_intr_handle_t));
1331 }
1332 
1333 /* ARGSUSED */
1334 int
1335 ddi_get_soft_iblock_cookie(dev_info_t *dip, int preference,
1336     ddi_iblock_cookie_t *iblock_cookiep)
1337 {
1338 	DDI_INTR_APIDBG((CE_CONT, "ddi_get_soft_iblock_cookie: name=%s%d "
1339 	    "dip=0x%p pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1340 	    (void *)dip, preference));
1341 
1342 	ASSERT(iblock_cookiep != NULL);
1343 
1344 	if (preference == DDI_SOFTINT_FIXED)
1345 		return (DDI_FAILURE);
1346 
1347 	*iblock_cookiep = (ddi_iblock_cookie_t)((uintptr_t)
1348 	    ((preference > DDI_SOFTINT_MED) ? DDI_SOFT_INTR_PRI_H :
1349 	    DDI_SOFT_INTR_PRI_M));
1350 
1351 	return (DDI_SUCCESS);
1352 }
1353 
1354 int
1355 ddi_add_softintr(dev_info_t *dip, int preference, ddi_softintr_t *idp,
1356     ddi_iblock_cookie_t *iblock_cookiep,
1357     ddi_idevice_cookie_t *idevice_cookiep,
1358     uint_t (*int_handler)(caddr_t int_handler_arg),
1359     caddr_t int_handler_arg)
1360 {
1361 	ddi_softint_handle_t	*hdl_p;
1362 	uint64_t		softpri;
1363 	int			ret;
1364 
1365 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: name=%s%d dip=0x%p "
1366 	    "pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1367 	    (void *)dip, preference));
1368 
1369 	if ((idp == NULL) || ((preference == DDI_SOFTINT_FIXED) &&
1370 	    (iblock_cookiep == NULL)))
1371 		return (DDI_FAILURE);
1372 
1373 	/* Translate the priority preference */
1374 	if (preference == DDI_SOFTINT_FIXED) {
1375 		softpri = (uint64_t)(uintptr_t)*iblock_cookiep;
1376 		softpri = MIN(softpri, DDI_SOFT_INTR_PRI_H);
1377 	} else {
1378 		softpri = (uint64_t)((preference > DDI_SOFTINT_MED) ?
1379 		    DDI_SOFT_INTR_PRI_H : DDI_SOFT_INTR_PRI_M);
1380 	}
1381 
1382 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: preference 0x%x "
1383 	    "softpri 0x%lx\n", preference, (long)softpri));
1384 
1385 	hdl_p = kmem_zalloc(sizeof (ddi_softint_handle_t), KM_SLEEP);
1386 	if ((ret = ddi_intr_add_softint(dip, hdl_p, softpri,
1387 	    (ddi_intr_handler_t *)int_handler, int_handler_arg)) !=
1388 	    DDI_SUCCESS) {
1389 		DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: "
1390 		    "ddi_intr_add_softint failed, ret 0x%x\n", ret));
1391 
1392 		kmem_free(hdl_p, sizeof (ddi_softint_handle_t));
1393 		return (DDI_FAILURE);
1394 	}
1395 
1396 	if (iblock_cookiep)
1397 		*iblock_cookiep =  (ddi_iblock_cookie_t)(uintptr_t)softpri;
1398 
1399 	if (idevice_cookiep) {
1400 		idevice_cookiep->idev_vector = 0;
1401 		idevice_cookiep->idev_priority = softpri;
1402 	}
1403 
1404 	*idp = (ddi_softintr_t)hdl_p;
1405 
1406 	DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: dip = 0x%p, "
1407 	    "idp = 0x%p, ret = %x\n", (void *)dip, (void *)*idp, ret));
1408 
1409 	return (DDI_SUCCESS);
1410 }
1411 
1412 void
1413 ddi_remove_softintr(ddi_softintr_t id)
1414 {
1415 	ddi_softint_handle_t	*h_p = (ddi_softint_handle_t *)id;
1416 
1417 	DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: id=0x%p\n",
1418 	    (void *)id));
1419 
1420 	if (h_p == NULL)
1421 		return;
1422 
1423 	DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: handle 0x%p\n",
1424 	    (void *)h_p));
1425 
1426 	(void) ddi_intr_remove_softint(*h_p);
1427 	kmem_free(h_p, sizeof (ddi_softint_handle_t));
1428 }
1429 
1430 void
1431 ddi_trigger_softintr(ddi_softintr_t id)
1432 {
1433 	ddi_softint_handle_t	*h_p = (ddi_softint_handle_t *)id;
1434 	int			ret;
1435 
1436 	if (h_p == NULL)
1437 		return;
1438 
1439 	if ((ret = ddi_intr_trigger_softint(*h_p, NULL)) != DDI_SUCCESS) {
1440 		DDI_INTR_APIDBG((CE_CONT, "ddi_trigger_softintr: "
1441 		    "ddi_intr_trigger_softint failed, hdlp 0x%p "
1442 		    "ret 0x%x\n", (void *)h_p, ret));
1443 	}
1444 }
1445