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