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