Home | History | Annotate | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"@(#)ddi_intr.c	1.21	07/08/28 SMI"
     27 
     28 #include <sys/note.h>
     29 #include <sys/sysmacros.h>
     30 #include <sys/types.h>
     31 #include <sys/param.h>
     32 #include <sys/systm.h>
     33 #include <sys/kmem.h>
     34 #include <sys/cmn_err.h>
     35 #include <sys/debug.h>
     36 #include <sys/avintr.h>
     37 #include <sys/autoconf.h>
     38 #include <sys/sunndi.h>
     39 #include <sys/ndi_impldefs.h>	/* include prototypes */
     40 #include <sys/atomic.h>
     41 
     42 /*
     43  * New DDI interrupt framework
     44  */
     45 
     46 /*
     47  * MSI-X allocation limit.
     48  *
     49  * This MSI-X limit or tunable may be obsolete or change with Interrupt
     50  * Resource Management (IRM) support.
     51  */
     52 uint_t		ddi_msix_alloc_limit = DDI_DEFAULT_MSIX_ALLOC;
     53 
     54 /*
     55  * ddi_intr_get_supported_types:
     56  *	Return, as a bit mask, the hardware interrupt types supported by
     57  *	both the device and by the host in the integer pointed
     58  *	to be the 'typesp' argument.
     59  */
     60 int
     61 ddi_intr_get_supported_types(dev_info_t *dip, int *typesp)
     62 {
     63 	int			ret;
     64 	ddi_intr_handle_impl_t	hdl;
     65 
     66 	if (dip == NULL)
     67 		return (DDI_EINVAL);
     68 
     69 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_supported_types: dip %p\n",
     70 	    (void *)dip));
     71 
     72 	if (*typesp = i_ddi_intr_get_supported_types(dip))
     73 		return (DDI_SUCCESS);
     74 
     75 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
     76 	hdl.ih_dip = dip;
     77 
     78 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_SUPPORTED_TYPES, &hdl,
     79 	    (void *)typesp);
     80 
     81 	if (ret != DDI_SUCCESS)
     82 		return (DDI_INTR_NOTFOUND);
     83 
     84 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_supported_types: types %x\n",
     85 	    *typesp));
     86 
     87 	return (ret);
     88 }
     89 
     90 
     91 /*
     92  * ddi_intr_get_nintrs:
     93  * 	Return as an integer in the integer pointed to by the argument
     94  * 	*nintrsp*, the number of interrupts the device supports for the
     95  *	given interrupt type.
     96  */
     97 int
     98 ddi_intr_get_nintrs(dev_info_t *dip, int type, int *nintrsp)
     99 {
    100 	int			ret;
    101 	ddi_intr_handle_impl_t	hdl;
    102 
    103 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_nintrs: dip %p, type: %d\n",
    104 	    (void *)dip, type));
    105 
    106 	if ((dip == NULL) || !DDI_INTR_TYPE_FLAG_VALID(type) ||
    107 	    !(i_ddi_intr_get_supported_types(dip) & type)) {
    108 		*nintrsp = 0;
    109 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_nintrs: Invalid "
    110 		    "input args\n"));
    111 		return (DDI_EINVAL);
    112 	}
    113 
    114 	if (*nintrsp = i_ddi_intr_get_supported_nintrs(dip, type))
    115 		return (DDI_SUCCESS);
    116 
    117 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
    118 	hdl.ih_dip = dip;
    119 	hdl.ih_type = type;
    120 
    121 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl,
    122 	    (void *)nintrsp);
    123 
    124 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_nintrs:: nintrs %x\n",
    125 	    *nintrsp));
    126 
    127 	return (ret);
    128 }
    129 
    130 
    131 /*
    132  * ddi_intr_get_navail:
    133  *	Bus nexus driver will return availble interrupt count value for
    134  *	a given interrupt type.
    135  *
    136  * 	Return as an integer in the integer pointed to by the argument
    137  * 	*navailp*, the number of interrupts currently available for the
    138  *	given interrupt type.
    139  */
    140 int
    141 ddi_intr_get_navail(dev_info_t *dip, int type, int *navailp)
    142 {
    143 	int			ret;
    144 	ddi_intr_handle_impl_t	hdl;
    145 
    146 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_navail: dip %p, type: %d\n",
    147 	    (void *)dip, type));
    148 
    149 	if ((dip == NULL) || !DDI_INTR_TYPE_FLAG_VALID(type) ||
    150 	    !(i_ddi_intr_get_supported_types(dip) & type)) {
    151 		*navailp = 0;
    152 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_navail: Invalid "
    153 		    "input args\n"));
    154 		return (DDI_EINVAL);
    155 	}
    156 
    157 	/*
    158 	 * In future, this interface implementation will change
    159 	 * with Resource Management support.
    160 	 */
    161 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
    162 	hdl.ih_dip = dip;
    163 	hdl.ih_type = type;
    164 
    165 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NAVAIL, &hdl,
    166 	    (void *)navailp);
    167 
    168 	return (ret == DDI_SUCCESS ? DDI_SUCCESS : DDI_INTR_NOTFOUND);
    169 }
    170 
    171 
    172 /*
    173  * Interrupt allocate/free functions
    174  */
    175 int
    176 ddi_intr_alloc(dev_info_t *dip, ddi_intr_handle_t *h_array, int type, int inum,
    177     int count, int *actualp, int behavior)
    178 {
    179 	ddi_intr_handle_impl_t	*hdlp, tmp_hdl;
    180 	int			i, ret, cap = 0, intr_type, nintrs = 0;
    181 	uint_t			pri, curr_nintrs = 0;
    182 
    183 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: name %s dip 0x%p "
    184 	    "type %x inum %x count %x behavior %x\n", ddi_driver_name(dip),
    185 	    (void *)dip, type, inum, count, behavior));
    186 
    187 	/* Validate parameters */
    188 	if (dip == NULL || h_array == NULL || count < 1 || inum < 0 ||
    189 	    !DDI_INTR_BEHAVIOR_FLAG_VALID(behavior)) {
    190 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: Invalid args\n"));
    191 		return (DDI_EINVAL);
    192 	}
    193 
    194 	/* Validate interrupt type */
    195 	if (!DDI_INTR_TYPE_FLAG_VALID(type) ||
    196 	    !(i_ddi_intr_get_supported_types(dip) & type)) {
    197 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: type %x not "
    198 		    "supported\n", type));
    199 		return (DDI_EINVAL);
    200 	}
    201 
    202 	/*
    203 	 * Check if the 'inum' was previously allocated or not? Fail, if so.
    204 	 */
    205 	if ((type == DDI_INTR_TYPE_FIXED) &&
    206 	    (i_ddi_get_intr_handle(dip, inum) != NULL)) {
    207 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: inum %d is already "
    208 		    "in use, can not allocate again!!\n", inum));
    209 		return (DDI_EINVAL);
    210 	}
    211 
    212 	/* First, get how many interrupts the device supports */
    213 	if (!(nintrs = i_ddi_intr_get_supported_nintrs(dip, type))) {
    214 		if (ddi_intr_get_nintrs(dip, type, &nintrs) != DDI_SUCCESS) {
    215 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: no "
    216 			    "interrupts found of type %d\n", type));
    217 			return (DDI_INTR_NOTFOUND);
    218 		}
    219 	}
    220 
    221 	/* Is this function invoked with more interrupt than device supports? */
    222 	if (count > nintrs) {
    223 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: no of interrupts "
    224 		    "requested %d is more than supported %d\n", count, nintrs));
    225 		return (DDI_EINVAL);
    226 	}
    227 
    228 	/*
    229 	 * Check if requested interrupt type is not same as interrupt
    230 	 * type is in use if any.
    231 	 */
    232 	if (((intr_type = i_ddi_intr_get_current_type(dip)) != 0) &&
    233 	    (intr_type != type)) {
    234 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: Requested "
    235 		    "interrupt type %x is different from interrupt type %x"
    236 		    "already in use\n", type, intr_type));
    237 
    238 		return (DDI_EINVAL);
    239 	}
    240 
    241 	/*
    242 	 * Check if requested interrupt type is in use and requested number
    243 	 * of interrupts and number of interrupts already in use exceeds the
    244 	 * number of interrupts supported by this device.
    245 	 */
    246 	if (intr_type) {
    247 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: type %x "
    248 		    "is already being used\n", type));
    249 
    250 		curr_nintrs = i_ddi_intr_get_current_nintrs(dip);
    251 		if ((count + curr_nintrs) > nintrs) {
    252 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: count %d "
    253 			    "+ intrs in use %d exceeds supported %d intrs\n",
    254 			    count, curr_nintrs, nintrs));
    255 			return (DDI_EINVAL);
    256 		}
    257 	}
    258 
    259 	/*
    260 	 * For MSI, ensure that the requested interrupt count is a power of 2
    261 	 */
    262 	if (type == DDI_INTR_TYPE_MSI && !ISP2(count)) {
    263 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
    264 		    "MSI count %d is not a power of two\n", count));
    265 		return (DDI_EINVAL);
    266 	}
    267 
    268 	/*
    269 	 * NOTE:
    270 	 *
    271 	 * An intermediate solution is added here to allocate more MSI-X
    272 	 * interrupts to drivers to address some significant performance
    273 	 * issues discovered on various SPARC platforms. More MSI-X interrupts
    274 	 * will be allocated based on existence of "#msix-request" property.
    275 	 * The DDI framework will not honor this property after the Interrupt
    276 	 * Resource Management (IRM) project's integration.
    277 	 *
    278 	 * Hard limit for maximum MSI allocation is set to DDI_MAX_MSI_ALLOC,
    279 	 * however MSI-X's max allocation is controlled by
    280 	 * ddi_msix_alloc_limit().
    281 	 */
    282 	if (DDI_INTR_IS_MSI_OR_MSIX(type)) {
    283 		uint_t	alloc_limit = (type == DDI_INTR_TYPE_MSIX) ?
    284 		    i_ddi_get_msix_alloc_limit(dip) : DDI_MAX_MSI_ALLOC;
    285 
    286 		if (curr_nintrs == alloc_limit) {
    287 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
    288 			    "max # of intrs %d already allocated\n",
    289 			    curr_nintrs));
    290 			return (DDI_EINVAL);
    291 		}
    292 		if ((count + curr_nintrs) > alloc_limit) {
    293 			DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: Requested "
    294 			    "MSI/Xs %d Max MSI/Xs limit %d\n", count,
    295 			    alloc_limit));
    296 
    297 			if (behavior == DDI_INTR_ALLOC_STRICT) {
    298 				DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
    299 				    "DDI_INTR_ALLOC_STRICT flag is passed, "
    300 				    "return failure\n"));
    301 				return (DDI_EAGAIN);
    302 			}
    303 
    304 			count = alloc_limit - curr_nintrs;
    305 		}
    306 	}
    307 
    308 	/* Now allocate required number of interrupts */
    309 	bzero(&tmp_hdl, sizeof (ddi_intr_handle_impl_t));
    310 	tmp_hdl.ih_type = type;
    311 	tmp_hdl.ih_inum = inum;
    312 	tmp_hdl.ih_scratch1 = count;
    313 	tmp_hdl.ih_scratch2 = (void *)(uintptr_t)behavior;
    314 	tmp_hdl.ih_dip = dip;
    315 
    316 	i_ddi_intr_devi_init(dip);
    317 
    318 	if (i_ddi_intr_ops(dip, dip, DDI_INTROP_ALLOC,
    319 	    &tmp_hdl, (void *)actualp) != DDI_SUCCESS) {
    320 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: allocation "
    321 		    "failed\n"));
    322 		i_ddi_intr_devi_fini(dip);
    323 		return (*actualp ? DDI_EAGAIN : DDI_INTR_NOTFOUND);
    324 	}
    325 
    326 	if ((ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPRI,
    327 	    &tmp_hdl, (void *)&pri)) != DDI_SUCCESS) {
    328 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: get priority "
    329 		    "failed\n"));
    330 		goto fail;
    331 	}
    332 
    333 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: getting capability\n"));
    334 
    335 	if ((ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_GETCAP,
    336 	    &tmp_hdl, (void *)&cap)) != DDI_SUCCESS) {
    337 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: get capability "
    338 		    "failed\n"));
    339 		goto fail;
    340 	}
    341 
    342 	/*
    343 	 * Save current interrupt type, supported and current intr count.
    344 	 */
    345 	i_ddi_intr_set_current_type(dip, type);
    346 	i_ddi_intr_set_supported_nintrs(dip, nintrs);
    347 	i_ddi_intr_set_current_nintrs(dip,
    348 	    i_ddi_intr_get_current_nintrs(dip) + *actualp);
    349 
    350 	/* Now, go and handle each "handle" */
    351 	for (i = 0; i < *actualp; i++) {
    352 		hdlp = (ddi_intr_handle_impl_t *)kmem_zalloc(
    353 		    (sizeof (ddi_intr_handle_impl_t)), KM_SLEEP);
    354 		rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
    355 		h_array[i] = (struct __ddi_intr_handle *)hdlp;
    356 		hdlp->ih_type = type;
    357 		hdlp->ih_pri = pri;
    358 		hdlp->ih_cap = cap;
    359 		hdlp->ih_ver = DDI_INTR_VERSION;
    360 		hdlp->ih_state = DDI_IHDL_STATE_ALLOC;
    361 		hdlp->ih_dip = dip;
    362 		hdlp->ih_inum = inum + i;
    363 		i_ddi_alloc_intr_phdl(hdlp);
    364 		if (type & DDI_INTR_TYPE_FIXED)
    365 			i_ddi_set_intr_handle(dip, hdlp->ih_inum, &h_array[i]);
    366 
    367 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: hdlp = 0x%p\n",
    368 		    (void *)h_array[i]));
    369 	}
    370 
    371 	return (DDI_SUCCESS);
    372 
    373 fail:
    374 	(void) i_ddi_intr_ops(tmp_hdl.ih_dip, tmp_hdl.ih_dip,
    375 	    DDI_INTROP_FREE, &tmp_hdl, NULL);
    376 	i_ddi_intr_devi_fini(dip);
    377 
    378 	return (ret);
    379 }
    380 
    381 
    382 int
    383 ddi_intr_free(ddi_intr_handle_t h)
    384 {
    385 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    386 	int			ret;
    387 
    388 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_free: hdlp = %p\n", (void *)hdlp));
    389 
    390 	if (hdlp == NULL)
    391 		return (DDI_EINVAL);
    392 
    393 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    394 	if (((hdlp->ih_flags & DDI_INTR_MSIX_DUP) &&
    395 	    (hdlp->ih_state != DDI_IHDL_STATE_ADDED)) ||
    396 	    ((hdlp->ih_state != DDI_IHDL_STATE_ALLOC) &&
    397 	    (!(hdlp->ih_flags & DDI_INTR_MSIX_DUP)))) {
    398 		rw_exit(&hdlp->ih_rwlock);
    399 		return (DDI_EINVAL);
    400 	}
    401 
    402 	/* Set the number of interrupts to free */
    403 	hdlp->ih_scratch1 = 1;
    404 
    405 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    406 	    DDI_INTROP_FREE, hdlp, NULL);
    407 
    408 	rw_exit(&hdlp->ih_rwlock);
    409 	if (ret == DDI_SUCCESS) {
    410 		/* This would be the dup vector */
    411 		if (hdlp->ih_flags & DDI_INTR_MSIX_DUP)
    412 			atomic_dec_32(&hdlp->ih_main->ih_dup_cnt);
    413 		else {
    414 			i_ddi_intr_set_current_nintrs(hdlp->ih_dip,
    415 			    i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1);
    416 
    417 			if (hdlp->ih_type & DDI_INTR_TYPE_FIXED)
    418 				i_ddi_set_intr_handle(hdlp->ih_dip,
    419 				    hdlp->ih_inum, NULL);
    420 
    421 			i_ddi_intr_devi_fini(hdlp->ih_dip);
    422 			i_ddi_free_intr_phdl(hdlp);
    423 		}
    424 		rw_destroy(&hdlp->ih_rwlock);
    425 		kmem_free(hdlp, sizeof (ddi_intr_handle_impl_t));
    426 	}
    427 
    428 	return (ret);
    429 }
    430 
    431 /*
    432  * Interrupt get/set capacity functions
    433  *
    434  * The logic used to figure this out is shown here:
    435  *
    436  *			Device level		Platform level	    Intr source
    437  * 1. Fixed interrupts
    438  * (non-PCI)
    439  * o Flags supported	N/A			Maskable/Pending/    rootnex
    440  *						No Block Enable
    441  * o navail					1
    442  *
    443  * 2. PCI Fixed interrupts
    444  * o Flags supported	pending/Maskable	Maskable/pending/    pci
    445  *						No Block enable
    446  * o navail		N/A			1
    447  *
    448  * 3. PCI MSI
    449  * o Flags supported	Maskable/Pending	Maskable/Pending    pci
    450  *			Block Enable		(if drvr doesn't)   Block Enable
    451  * o navail		N/A			#vectors - #used    N/A
    452  *
    453  * 4. PCI MSI-X
    454  * o Flags supported	Maskable/Pending	Maskable/Pending    pci
    455  *			Block Enable				    Block Enable
    456  * o navail		N/A			#vectors - #used    N/A
    457  *
    458  * where:
    459  *	#vectors	- Total numbers of vectors available
    460  *	#used		- Total numbers of vectors currently being used
    461  *
    462  * For devices complying to PCI2.3 or greater, see bit10 of Command Register
    463  * 0 - enables assertion of INTx
    464  * 1 - disables assertion of INTx
    465  *
    466  * For non MSI/X interrupts; if the IRQ is shared then all ddi_intr_set_*()
    467  * operations return failure.
    468  */
    469 int
    470 ddi_intr_get_cap(ddi_intr_handle_t h, int *flagsp)
    471 {
    472 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    473 	int			ret;
    474 
    475 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_cap: hdlp = %p\n",
    476 	    (void *)hdlp));
    477 
    478 	*flagsp = 0;
    479 	if (hdlp == NULL)
    480 		return (DDI_EINVAL);
    481 
    482 	rw_enter(&hdlp->ih_rwlock, RW_READER);
    483 
    484 	if (hdlp->ih_cap) {
    485 		*flagsp = hdlp->ih_cap & ~DDI_INTR_FLAG_MSI64;
    486 		rw_exit(&hdlp->ih_rwlock);
    487 		return (DDI_SUCCESS);
    488 	}
    489 
    490 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    491 	    DDI_INTROP_GETCAP, hdlp, (void *)flagsp);
    492 
    493 	if (ret == DDI_SUCCESS) {
    494 		hdlp->ih_cap = *flagsp;
    495 
    496 		/* Mask out MSI/X 64-bit support to the consumer */
    497 		*flagsp &= ~DDI_INTR_FLAG_MSI64;
    498 	}
    499 
    500 	rw_exit(&hdlp->ih_rwlock);
    501 	return (ret);
    502 }
    503 
    504 int
    505 ddi_intr_set_cap(ddi_intr_handle_t h, int flags)
    506 {
    507 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    508 	int			ret;
    509 
    510 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_cap: hdlp = %p", (void *)hdlp));
    511 
    512 	if (hdlp == NULL)
    513 		return (DDI_EINVAL);
    514 
    515 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    516 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
    517 		rw_exit(&hdlp->ih_rwlock);
    518 		return (DDI_EINVAL);
    519 	}
    520 
    521 	/* Only DDI_INTR_FLAG_LEVEL or DDI_INTR_FLAG_EDGE are allowed */
    522 	if (!(flags & (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_LEVEL))) {
    523 		DDI_INTR_APIDBG((CE_CONT, "%s%d: only LEVEL or EDGE capability "
    524 		    "can be set\n", ddi_driver_name(hdlp->ih_dip),
    525 		    ddi_get_instance(hdlp->ih_dip)));
    526 		rw_exit(&hdlp->ih_rwlock);
    527 		return (DDI_EINVAL);
    528 	}
    529 
    530 	/* Both level/edge flags must be currently supported */
    531 	if (!(hdlp->ih_cap & (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_LEVEL))) {
    532 		DDI_INTR_APIDBG((CE_CONT, "%s%d: Both LEVEL and EDGE capability"
    533 		    " must be supported\n", ddi_driver_name(hdlp->ih_dip),
    534 		    ddi_get_instance(hdlp->ih_dip)));
    535 		rw_exit(&hdlp->ih_rwlock);
    536 		return (DDI_ENOTSUP);
    537 	}
    538 
    539 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    540 	    DDI_INTROP_SETCAP, hdlp, &flags);
    541 
    542 	rw_exit(&hdlp->ih_rwlock);
    543 	return (ret);
    544 }
    545 
    546 /*
    547  * Priority related functions
    548  */
    549 
    550 /*
    551  * ddi_intr_get_hilevel_pri:
    552  *	Returns the minimum priority level for a
    553  *	high-level interrupt on a platform.
    554  */
    555 uint_t
    556 ddi_intr_get_hilevel_pri(void)
    557 {
    558 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_hilevel_pri:\n"));
    559 	return (LOCK_LEVEL + 1);
    560 }
    561 
    562 int
    563 ddi_intr_get_pri(ddi_intr_handle_t h, uint_t *prip)
    564 {
    565 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    566 	int			ret;
    567 
    568 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pri: hdlp = %p\n",
    569 	    (void *)hdlp));
    570 
    571 	*prip = 0;
    572 	if (hdlp == NULL)
    573 		return (DDI_EINVAL);
    574 
    575 	rw_enter(&hdlp->ih_rwlock, RW_READER);
    576 	/* Already initialized, just return that */
    577 	if (hdlp->ih_pri) {
    578 		*prip = hdlp->ih_pri;
    579 		rw_exit(&hdlp->ih_rwlock);
    580 		return (DDI_SUCCESS);
    581 	}
    582 
    583 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    584 	    DDI_INTROP_GETPRI, hdlp, (void *)prip);
    585 
    586 	if (ret == DDI_SUCCESS)
    587 		hdlp->ih_pri = *prip;
    588 
    589 	rw_exit(&hdlp->ih_rwlock);
    590 	return (ret);
    591 }
    592 
    593 int
    594 ddi_intr_set_pri(ddi_intr_handle_t h, uint_t pri)
    595 {
    596 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    597 	int			ret;
    598 
    599 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_pri: hdlp = %p", (void *)hdlp));
    600 
    601 	if (hdlp == NULL)
    602 		return (DDI_EINVAL);
    603 
    604 	/* Validate priority argument */
    605 	if (pri < DDI_INTR_PRI_MIN || pri > DDI_INTR_PRI_MAX) {
    606 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_pri: invalid priority "
    607 		    "specified  = %x\n", pri));
    608 		return (DDI_EINVAL);
    609 	}
    610 
    611 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    612 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
    613 		rw_exit(&hdlp->ih_rwlock);
    614 		return (DDI_EINVAL);
    615 	}
    616 
    617 	/* If the passed priority is same as existing priority; do nothing */
    618 	if (pri == hdlp->ih_pri) {
    619 		rw_exit(&hdlp->ih_rwlock);
    620 		return (DDI_SUCCESS);
    621 	}
    622 
    623 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    624 	    DDI_INTROP_SETPRI, hdlp, &pri);
    625 
    626 	if (ret == DDI_SUCCESS)
    627 		hdlp->ih_pri = pri;
    628 
    629 	rw_exit(&hdlp->ih_rwlock);
    630 	return (ret);
    631 }
    632 
    633 /*
    634  * Interrupt add/duplicate/remove handlers
    635  */
    636 int
    637 ddi_intr_add_handler(ddi_intr_handle_t h, ddi_intr_handler_t inthandler,
    638     void *arg1, void *arg2)
    639 {
    640 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    641 	int			ret;
    642 
    643 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_handler: hdlp = 0x%p\n",
    644 	    (void *)hdlp));
    645 
    646 	if ((hdlp == NULL) || (inthandler == NULL))
    647 		return (DDI_EINVAL);
    648 
    649 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    650 	if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
    651 		rw_exit(&hdlp->ih_rwlock);
    652 		return (DDI_EINVAL);
    653 	}
    654 
    655 	hdlp->ih_cb_func = inthandler;
    656 	hdlp->ih_cb_arg1 = arg1;
    657 	hdlp->ih_cb_arg2 = arg2;
    658 
    659 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    660 	    DDI_INTROP_ADDISR, hdlp, NULL);
    661 
    662 	if (ret != DDI_SUCCESS) {
    663 		hdlp->ih_cb_func = NULL;
    664 		hdlp->ih_cb_arg1 = NULL;
    665 		hdlp->ih_cb_arg2 = NULL;
    666 	} else
    667 		hdlp->ih_state = DDI_IHDL_STATE_ADDED;
    668 
    669 	rw_exit(&hdlp->ih_rwlock);
    670 	return (ret);
    671 }
    672 
    673 int
    674 ddi_intr_dup_handler(ddi_intr_handle_t org, int dup_inum,
    675     ddi_intr_handle_t *dup)
    676 {
    677 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)org;
    678 	ddi_intr_handle_impl_t	*dup_hdlp;
    679 	int			ret;
    680 
    681 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_dup_handler: hdlp = 0x%p\n",
    682 	    (void *)hdlp));
    683 
    684 	/* Do some input argument checking ("dup" handle is not allocated) */
    685 	if ((hdlp == NULL) || (*dup != NULL) || (dup_inum < 0)) {
    686 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_dup_handler: Invalid "
    687 		    "input args\n"));
    688 		return (DDI_EINVAL);
    689 	}
    690 
    691 	rw_enter(&hdlp->ih_rwlock, RW_READER);
    692 
    693 	/* Do some input argument checking */
    694 	if ((hdlp->ih_state == DDI_IHDL_STATE_ALLOC) ||	/* intr handle alloc? */
    695 	    (hdlp->ih_type != DDI_INTR_TYPE_MSIX) ||	/* only MSI-X allowed */
    696 	    (hdlp->ih_flags & DDI_INTR_MSIX_DUP)) {	/* only dup original */
    697 		rw_exit(&hdlp->ih_rwlock);
    698 		return (DDI_EINVAL);
    699 	}
    700 
    701 	hdlp->ih_scratch1 = dup_inum;
    702 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    703 	    DDI_INTROP_DUPVEC, hdlp, NULL);
    704 
    705 	if (ret == DDI_SUCCESS) {
    706 		dup_hdlp = (ddi_intr_handle_impl_t *)
    707 		    kmem_alloc(sizeof (ddi_intr_handle_impl_t), KM_SLEEP);
    708 
    709 		atomic_add_32(&hdlp->ih_dup_cnt, 1);
    710 
    711 		*dup = (ddi_intr_handle_t)dup_hdlp;
    712 		bcopy(hdlp, dup_hdlp, sizeof (ddi_intr_handle_impl_t));
    713 
    714 		/* These fields are unique to each dupped msi-x vector */
    715 		rw_init(&dup_hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
    716 		dup_hdlp->ih_state = DDI_IHDL_STATE_ADDED;
    717 		dup_hdlp->ih_inum = dup_inum;
    718 		dup_hdlp->ih_flags |= DDI_INTR_MSIX_DUP;
    719 		dup_hdlp->ih_dup_cnt = 0;
    720 
    721 		/* Point back to original vector */
    722 		dup_hdlp->ih_main = hdlp;
    723 	}
    724 
    725 	rw_exit(&hdlp->ih_rwlock);
    726 	return (ret);
    727 }
    728 
    729 int
    730 ddi_intr_remove_handler(ddi_intr_handle_t h)
    731 {
    732 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    733 	int			ret = DDI_SUCCESS;
    734 
    735 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_handler: hdlp = %p\n",
    736 	    (void *)hdlp));
    737 
    738 	if (hdlp == NULL)
    739 		return (DDI_EINVAL);
    740 
    741 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    742 
    743 	if (hdlp->ih_state != DDI_IHDL_STATE_ADDED) {
    744 		ret = DDI_EINVAL;
    745 		goto done;
    746 	} else if (hdlp->ih_flags & DDI_INTR_MSIX_DUP)
    747 		goto done;
    748 
    749 	ASSERT(hdlp->ih_dup_cnt == 0);
    750 	if (hdlp->ih_dup_cnt > 0) {
    751 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_handler: MSI-X "
    752 		    "dup_cnt %d is not 0\n", hdlp->ih_dup_cnt));
    753 		ret = DDI_FAILURE;
    754 		goto done;
    755 	}
    756 
    757 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    758 	    DDI_INTROP_REMISR, hdlp, NULL);
    759 
    760 	if (ret == DDI_SUCCESS) {
    761 		hdlp->ih_state = DDI_IHDL_STATE_ALLOC;
    762 		hdlp->ih_cb_func = NULL;
    763 		hdlp->ih_cb_arg1 = NULL;
    764 		hdlp->ih_cb_arg2 = NULL;
    765 	}
    766 
    767 done:
    768 	rw_exit(&hdlp->ih_rwlock);
    769 	return (ret);
    770 }
    771 
    772 /*
    773  * Interrupt enable/disable/block_enable/block_disable handlers
    774  */
    775 int
    776 ddi_intr_enable(ddi_intr_handle_t h)
    777 {
    778 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    779 	int			ret;
    780 
    781 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_enable: hdlp = %p\n",
    782 	    (void *)hdlp));
    783 
    784 	if (hdlp == NULL)
    785 		return (DDI_EINVAL);
    786 
    787 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    788 	if ((hdlp->ih_state != DDI_IHDL_STATE_ADDED) ||
    789 	    ((hdlp->ih_type == DDI_INTR_TYPE_MSI) &&
    790 	    (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) {
    791 		rw_exit(&hdlp->ih_rwlock);
    792 		return (DDI_EINVAL);
    793 	}
    794 
    795 	I_DDI_VERIFY_MSIX_HANDLE(hdlp);
    796 
    797 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    798 	    DDI_INTROP_ENABLE, hdlp, NULL);
    799 
    800 	if (ret == DDI_SUCCESS)
    801 		hdlp->ih_state = DDI_IHDL_STATE_ENABLE;
    802 
    803 	rw_exit(&hdlp->ih_rwlock);
    804 	return (ret);
    805 }
    806 
    807 int
    808 ddi_intr_disable(ddi_intr_handle_t h)
    809 {
    810 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    811 	int			ret;
    812 
    813 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_disable: hdlp = %p\n",
    814 	    (void *)hdlp));
    815 
    816 	if (hdlp == NULL)
    817 		return (DDI_EINVAL);
    818 
    819 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    820 	if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
    821 	    ((hdlp->ih_type == DDI_INTR_TYPE_MSI) &&
    822 	    (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) {
    823 		rw_exit(&hdlp->ih_rwlock);
    824 		return (DDI_EINVAL);
    825 	}
    826 
    827 	I_DDI_VERIFY_MSIX_HANDLE(hdlp);
    828 
    829 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    830 	    DDI_INTROP_DISABLE, hdlp, NULL);
    831 
    832 	if (ret == DDI_SUCCESS)
    833 		hdlp->ih_state = DDI_IHDL_STATE_ADDED;
    834 
    835 	rw_exit(&hdlp->ih_rwlock);
    836 	return (ret);
    837 }
    838 
    839 int
    840 ddi_intr_block_enable(ddi_intr_handle_t *h_array, int count)
    841 {
    842 	ddi_intr_handle_impl_t	*hdlp;
    843 	int			i, ret;
    844 
    845 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_enable: h_array = %p\n",
    846 	    (void *)h_array));
    847 
    848 	if (h_array == NULL)
    849 		return (DDI_EINVAL);
    850 
    851 	for (i = 0; i < count; i++) {
    852 		hdlp = (ddi_intr_handle_impl_t *)h_array[i];
    853 		rw_enter(&hdlp->ih_rwlock, RW_READER);
    854 
    855 		if (hdlp->ih_state != DDI_IHDL_STATE_ADDED ||
    856 		    hdlp->ih_type != DDI_INTR_TYPE_MSI ||
    857 		    !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) {
    858 			rw_exit(&hdlp->ih_rwlock);
    859 			return (DDI_EINVAL);
    860 		}
    861 		rw_exit(&hdlp->ih_rwlock);
    862 	}
    863 
    864 	hdlp = (ddi_intr_handle_impl_t *)h_array[0];
    865 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    866 	hdlp->ih_scratch1 = count;
    867 	hdlp->ih_scratch2 = (void *)h_array;
    868 
    869 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    870 	    DDI_INTROP_BLOCKENABLE, hdlp, NULL);
    871 
    872 	rw_exit(&hdlp->ih_rwlock);
    873 
    874 	if (ret == DDI_SUCCESS) {
    875 		for (i = 0; i < count; i++) {
    876 			hdlp = (ddi_intr_handle_impl_t *)h_array[i];
    877 			rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    878 			hdlp->ih_state = DDI_IHDL_STATE_ENABLE;
    879 			rw_exit(&hdlp->ih_rwlock);
    880 		}
    881 	}
    882 
    883 	return (ret);
    884 }
    885 
    886 int
    887 ddi_intr_block_disable(ddi_intr_handle_t *h_array, int count)
    888 {
    889 	ddi_intr_handle_impl_t	*hdlp;
    890 	int			i, ret;
    891 
    892 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_disable: h_array = %p\n",
    893 	    (void *)h_array));
    894 
    895 	if (h_array == NULL)
    896 		return (DDI_EINVAL);
    897 
    898 	for (i = 0; i < count; i++) {
    899 		hdlp = (ddi_intr_handle_impl_t *)h_array[i];
    900 		rw_enter(&hdlp->ih_rwlock, RW_READER);
    901 		if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE ||
    902 		    hdlp->ih_type != DDI_INTR_TYPE_MSI ||
    903 		    !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) {
    904 			rw_exit(&hdlp->ih_rwlock);
    905 			return (DDI_EINVAL);
    906 		}
    907 		rw_exit(&hdlp->ih_rwlock);
    908 	}
    909 
    910 	hdlp = (ddi_intr_handle_impl_t *)h_array[0];
    911 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    912 	hdlp->ih_scratch1 = count;
    913 	hdlp->ih_scratch2 = (void *)h_array;
    914 
    915 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    916 	    DDI_INTROP_BLOCKDISABLE, hdlp, NULL);
    917 
    918 	rw_exit(&hdlp->ih_rwlock);
    919 
    920 	if (ret == DDI_SUCCESS) {
    921 		for (i = 0; i < count; i++) {
    922 			hdlp = (ddi_intr_handle_impl_t *)h_array[i];
    923 			rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    924 			hdlp->ih_state = DDI_IHDL_STATE_ADDED;
    925 			rw_exit(&hdlp->ih_rwlock);
    926 		}
    927 	}
    928 
    929 	return (ret);
    930 }
    931 
    932 /*
    933  * Interrupt set/clr mask handlers
    934  */
    935 int
    936 ddi_intr_set_mask(ddi_intr_handle_t h)
    937 {
    938 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    939 	int			ret;
    940 
    941 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_mask: hdlp = %p\n",
    942 	    (void *)hdlp));
    943 
    944 	if (hdlp == NULL)
    945 		return (DDI_EINVAL);
    946 
    947 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    948 	if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
    949 	    (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE))) {
    950 		rw_exit(&hdlp->ih_rwlock);
    951 		return (DDI_EINVAL);
    952 	}
    953 
    954 	ret =  i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    955 	    DDI_INTROP_SETMASK, hdlp, NULL);
    956 
    957 	rw_exit(&hdlp->ih_rwlock);
    958 	return (ret);
    959 }
    960 
    961 int
    962 ddi_intr_clr_mask(ddi_intr_handle_t h)
    963 {
    964 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    965 	int			ret;
    966 
    967 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_clr_mask: hdlp = %p\n",
    968 	    (void *)hdlp));
    969 
    970 	if (hdlp == NULL)
    971 		return (DDI_EINVAL);
    972 
    973 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
    974 	if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
    975 	    (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE))) {
    976 		rw_exit(&hdlp->ih_rwlock);
    977 		return (DDI_EINVAL);
    978 	}
    979 
    980 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
    981 	    DDI_INTROP_CLRMASK, hdlp, NULL);
    982 
    983 	rw_exit(&hdlp->ih_rwlock);
    984 	return (ret);
    985 }
    986 
    987 /*
    988  * Interrupt get_pending handler
    989  */
    990 int
    991 ddi_intr_get_pending(ddi_intr_handle_t h, int *pendingp)
    992 {
    993 	ddi_intr_handle_impl_t	*hdlp = (ddi_intr_handle_impl_t *)h;
    994 	int			ret;
    995 
    996 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pending: hdlp = %p\n",
    997 	    (void *)hdlp));
    998 
    999 	if (hdlp == NULL)
   1000 		return (DDI_EINVAL);
   1001 
   1002 	rw_enter(&hdlp->ih_rwlock, RW_READER);
   1003 	if (!(hdlp->ih_cap & DDI_INTR_FLAG_PENDING)) {
   1004 		rw_exit(&hdlp->ih_rwlock);
   1005 		return (DDI_EINVAL);
   1006 	}
   1007 
   1008 	ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
   1009 	    DDI_INTROP_GETPENDING, hdlp, (void *)pendingp);
   1010 
   1011 	rw_exit(&hdlp->ih_rwlock);
   1012 	return (ret);
   1013 }
   1014 
   1015 /*
   1016  * Soft interrupt handlers
   1017  */
   1018 /*
   1019  * Add a soft interrupt and register its handler
   1020  */
   1021 /* ARGSUSED */
   1022 int
   1023 ddi_intr_add_softint(dev_info_t *dip, ddi_softint_handle_t *h_p, int soft_pri,
   1024     ddi_intr_handler_t handler, void *arg1)
   1025 {
   1026 	ddi_softint_hdl_impl_t	*hdlp;
   1027 	int			ret;
   1028 
   1029 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: dip = %p, "
   1030 	    "softpri = 0x%x\n", (void *)dip, soft_pri));
   1031 
   1032 	if ((dip == NULL) || (h_p == NULL) || (handler == NULL)) {
   1033 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: "
   1034 		    "invalid arguments"));
   1035 
   1036 		return (DDI_EINVAL);
   1037 	}
   1038 
   1039 	/* Validate input arguments */
   1040 	if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
   1041 	    soft_pri > DDI_INTR_SOFTPRI_MAX) {
   1042 		DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: invalid "
   1043 		    "soft_pri input given  = %x\n", soft_pri));
   1044 		return (DDI_EINVAL);
   1045 	}
   1046 
   1047 	hdlp = (ddi_softint_hdl_impl_t *)kmem_zalloc(
   1048 	    sizeof (ddi_softint_hdl_impl_t), KM_SLEEP);
   1049 
   1050 	/* fill up internally */
   1051 	rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
   1052 	rw_enter(&hdlp->ih_rwlock, RW_WRITER);
   1053 	hdlp->ih_pri = soft_pri;
   1054 	hdlp->ih_dip = dip;
   1055 	hdlp->ih_cb_func = handler;
   1056 	hdlp->ih_cb_arg1 = arg1;
   1057 	DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: hdlp = %p\n",
   1058 	    (void *)hdlp));
   1059 
   1060