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