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