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