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