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