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