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 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 
     39 #if defined(__i386) || defined(__amd64)
     40 /*
     41  * MSI-X allocation limit.
     42  */
     43 uint_t		ddi_msix_alloc_limit = DDI_DEFAULT_MSIX_ALLOC;
     44 #endif
     45 
     46 /*
     47  * New DDI interrupt framework
     48  */
     49 void
     50 i_ddi_intr_devi_init(dev_info_t *dip)
     51 {
     52 	int	supported_types;
     53 
     54 	DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_init: dip %p\n",
     55 	    (void *)dip));
     56 
     57 	if (DEVI(dip)->devi_intr_p)
     58 		return;
     59 
     60 	DEVI(dip)->devi_intr_p = kmem_zalloc(sizeof (devinfo_intr_t), KM_SLEEP);
     61 
     62 	supported_types = i_ddi_intr_get_supported_types(dip);
     63 
     64 	/* Save supported interrupt types information */
     65 	i_ddi_intr_set_supported_types(dip, supported_types);
     66 }
     67 
     68 void
     69 i_ddi_intr_devi_fini(dev_info_t *dip)
     70 {
     71 	devinfo_intr_t	*intr_p = DEVI(dip)->devi_intr_p;
     72 
     73 	DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_fini: dip %p\n",
     74 	    (void *)dip));
     75 
     76 	if ((intr_p == NULL) || i_ddi_intr_get_current_nintrs(dip))
     77 		return;
     78 
     79 	/*
     80 	 * devi_intr_handle_p will only be used for devices
     81 	 * which are using the legacy DDI Interrupt interfaces.
     82 	 */
     83 	if (intr_p->devi_intr_handle_p) {
     84 		/* nintrs could be zero; so check for it first */
     85 		if (intr_p->devi_intr_sup_nintrs) {
     86 			kmem_free(intr_p->devi_intr_handle_p,
     87 			    intr_p->devi_intr_sup_nintrs *
     88 			    sizeof (ddi_intr_handle_t));
     89 		}
     90 	}
     91 
     92 	/*
     93 	 * devi_irm_req_p will only be used for devices which
     94 	 * are mapped to an Interrupt Resource Management pool.
     95 	 */
     96 	if (intr_p->devi_irm_req_p)
     97 		(void) i_ddi_irm_remove(dip);
     98 
     99 	kmem_free(DEVI(dip)->devi_intr_p, sizeof (devinfo_intr_t));
    100 	DEVI(dip)->devi_intr_p = NULL;
    101 }
    102 
    103 uint_t
    104 i_ddi_intr_get_supported_types(dev_info_t *dip)
    105 {
    106 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
    107 	ddi_intr_handle_impl_t	hdl;
    108 	int			ret, intr_types;
    109 
    110 	if ((intr_p) && (intr_p->devi_intr_sup_types))
    111 		return (intr_p->devi_intr_sup_types);
    112 
    113 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
    114 	hdl.ih_dip = dip;
    115 
    116 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_SUPPORTED_TYPES, &hdl,
    117 	    (void *)&intr_types);
    118 
    119 	return ((ret == DDI_SUCCESS) ? intr_types : 0);
    120 }
    121 
    122 /*
    123  * NOTE: This function is only called by i_ddi_dev_init().
    124  */
    125 void
    126 i_ddi_intr_set_supported_types(dev_info_t *dip, int intr_types)
    127 {
    128 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
    129 
    130 	if (intr_p)
    131 		intr_p->devi_intr_sup_types = intr_types;
    132 }
    133 
    134 uint_t
    135 i_ddi_intr_get_supported_nintrs(dev_info_t *dip, int intr_type)
    136 {
    137 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
    138 	ddi_intr_handle_impl_t	hdl;
    139 	int			ret, nintrs;
    140 
    141 	if ((intr_p) && (intr_p->devi_intr_curr_type == intr_type) &&
    142 	    (intr_p->devi_intr_sup_nintrs))
    143 		return (intr_p->devi_intr_sup_nintrs);
    144 
    145 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
    146 	hdl.ih_dip = dip;
    147 	hdl.ih_type = intr_type;
    148 
    149 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl,
    150 	    (void *)&nintrs);
    151 
    152 	return ((ret == DDI_SUCCESS) ? nintrs : 0);
    153 }
    154 
    155 /*
    156  * NOTE: This function is only called by ddi_intr_alloc().
    157  */
    158 void
    159 i_ddi_intr_set_supported_nintrs(dev_info_t *dip, int nintrs)
    160 {
    161 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    162 
    163 	if (intr_p)
    164 		intr_p->devi_intr_sup_nintrs = nintrs;
    165 }
    166 
    167 uint_t
    168 i_ddi_intr_get_current_type(dev_info_t *dip)
    169 {
    170 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    171 
    172 	return (intr_p ? intr_p->devi_intr_curr_type : 0);
    173 }
    174 
    175 /*
    176  * NOTE: This function is only called by
    177  *       ddi_intr_alloc() and ddi_intr_free().
    178  */
    179 void
    180 i_ddi_intr_set_current_type(dev_info_t *dip, int intr_type)
    181 {
    182 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    183 
    184 	if (intr_p)
    185 		intr_p->devi_intr_curr_type = intr_type;
    186 }
    187 
    188 uint_t
    189 i_ddi_intr_get_current_nintrs(dev_info_t *dip)
    190 {
    191 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    192 
    193 	return (intr_p ? intr_p->devi_intr_curr_nintrs : 0);
    194 }
    195 
    196 /*
    197  * NOTE: This function is only called by
    198  *       ddi_intr_alloc() and ddi_intr_free().
    199  */
    200 void
    201 i_ddi_intr_set_current_nintrs(dev_info_t *dip, int nintrs)
    202 {
    203 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    204 
    205 	if (intr_p)
    206 		intr_p->devi_intr_curr_nintrs = nintrs;
    207 }
    208 
    209 uint_t
    210 i_ddi_intr_get_current_nenables(dev_info_t *dip)
    211 {
    212 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    213 
    214 	return (intr_p ? intr_p->devi_intr_curr_nenables : 0);
    215 }
    216 
    217 void
    218 i_ddi_intr_set_current_nenables(dev_info_t *dip, int nintrs)
    219 {
    220 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    221 
    222 	if (intr_p)
    223 		intr_p->devi_intr_curr_nenables = nintrs;
    224 }
    225 
    226 uint_t
    227 i_ddi_intr_get_current_navail(dev_info_t *dip, int type)
    228 {
    229 	ddi_intr_handle_impl_t	hdl;
    230 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
    231 	ddi_cb_t		*cb_p;
    232 	ddi_irm_pool_t		*pool_p;
    233 	ddi_irm_req_t		*req_p;
    234 	uint_t			navail = 0, nintrs;
    235 
    236 	/* Get maximum number of supported interrupts */
    237 	nintrs = i_ddi_intr_get_supported_nintrs(dip, type);
    238 
    239 	/* Check for an interrupt pool */
    240 	pool_p = i_ddi_intr_get_pool(dip, type);
    241 
    242 	/*
    243 	 * If a pool exists, then IRM determines the availability.
    244 	 * Otherwise, use the older INTROP method.
    245 	 */
    246 	if (pool_p) {
    247 		if (intr_p && (req_p = intr_p->devi_irm_req_p) &&
    248 		    (type == req_p->ireq_type)) {
    249 			mutex_enter(&pool_p->ipool_navail_lock);
    250 			navail = req_p->ireq_navail;
    251 			mutex_exit(&pool_p->ipool_navail_lock);
    252 			return (navail);
    253 		}
    254 		if ((type == DDI_INTR_TYPE_MSIX) &&
    255 		    (cb_p = DEVI(dip)->devi_cb_p) &&
    256 		    (cb_p->cb_flags & DDI_CB_FLAG_INTR)) {
    257 			return (nintrs);
    258 		}
    259 		navail = pool_p->ipool_defsz;
    260 	} else {
    261 		bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
    262 		hdl.ih_dip = dip;
    263 		hdl.ih_type = type;
    264 
    265 		if (i_ddi_intr_ops(dip, dip, DDI_INTROP_NAVAIL, &hdl,
    266 		    (void *)&navail) != DDI_SUCCESS) {
    267 			return (0);
    268 		}
    269 	}
    270 
    271 #if defined(__i386) || defined(__amd64)
    272 	/* Global tunable workaround */
    273 	if (type == DDI_INTR_TYPE_MSIX) {
    274 		navail = MIN(nintrs, ddi_msix_alloc_limit);
    275 	}
    276 #endif
    277 
    278 	/* Always restrict MSI to a precise limit */
    279 	if (type == DDI_INTR_TYPE_MSI)
    280 		navail = MIN(navail, DDI_MAX_MSI_ALLOC);
    281 
    282 	/* Ensure availability doesn't exceed what's supported */
    283 	navail = MIN(navail, nintrs);
    284 
    285 	return (navail);
    286 }
    287 
    288 ddi_intr_msix_t *
    289 i_ddi_get_msix(dev_info_t *dip)
    290 {
    291 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    292 
    293 	return (intr_p ? intr_p->devi_msix_p : NULL);
    294 }
    295 
    296 void
    297 i_ddi_set_msix(dev_info_t *dip, ddi_intr_msix_t *msix_p)
    298 {
    299 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    300 
    301 	if (intr_p)
    302 		intr_p->devi_msix_p = msix_p;
    303 }
    304 
    305 ddi_intr_handle_t
    306 i_ddi_get_intr_handle(dev_info_t *dip, int inum)
    307 {
    308 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    309 
    310 	if (intr_p == NULL)
    311 		return (NULL);
    312 
    313 	/*
    314 	 * Changed this to a check and return NULL if an invalid inum
    315 	 * is passed to retrieve a handle
    316 	 */
    317 	if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs))
    318 		return (NULL);
    319 
    320 	return ((intr_p->devi_intr_handle_p) ?
    321 	    intr_p->devi_intr_handle_p[inum] : NULL);
    322 }
    323 
    324 void
    325 i_ddi_set_intr_handle(dev_info_t *dip, int inum, ddi_intr_handle_t intr_hdl)
    326 {
    327 	devinfo_intr_t	*intr_p = DEVI(dip)->devi_intr_p;
    328 
    329 	if (intr_p == NULL)
    330 		return;
    331 
    332 	/*
    333 	 * Changed this to a check and return if an invalid inum
    334 	 * is passed to set a handle
    335 	 */
    336 	if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs))
    337 		return;
    338 
    339 	if (intr_hdl && (intr_p->devi_intr_handle_p == NULL)) {
    340 		/* nintrs could be zero; so check for it first */
    341 		if (intr_p->devi_intr_sup_nintrs)
    342 			intr_p->devi_intr_handle_p = kmem_zalloc(
    343 			    sizeof (ddi_intr_handle_t) *
    344 			    intr_p->devi_intr_sup_nintrs, KM_SLEEP);
    345 	}
    346 
    347 	if (intr_p->devi_intr_handle_p)
    348 		intr_p->devi_intr_handle_p[inum] = intr_hdl;
    349 }
    350 
    351 /*
    352  * The "ddi-intr-weight" property contains the weight of each interrupt
    353  * associated with a dev_info node. For devices with multiple interrupts per
    354  * dev_info node, the total load of the device is "devi_intr_weight * nintr",
    355  * possibly spread out over multiple CPUs.
    356  *
    357  * Maintaining this as a property permits possible tweaking in the product
    358  * in response to customer problems via driver.conf property definitions at
    359  * the driver or the instance level.  This does not mean that "ddi-intr_weight"
    360  * is a formal or committed interface.
    361  */
    362 int32_t
    363 i_ddi_get_intr_weight(dev_info_t *dip)
    364 {
    365 	int32_t	weight;
    366 
    367 	weight = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    368 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "ddi-intr-weight", -1);
    369 	if (weight < -1)
    370 		weight = -1;			/* undefined */
    371 	return (weight);
    372 }
    373 
    374 int32_t
    375 i_ddi_set_intr_weight(dev_info_t *dip, int32_t weight)
    376 {
    377 	int32_t oweight;
    378 
    379 	oweight = i_ddi_get_intr_weight(dip);
    380 	if ((weight > 0) && (oweight != weight))
    381 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
    382 		    "ddi-intr-weight", weight);
    383 	return (oweight);
    384 }
    385 
    386 /*
    387  * Old DDI interrupt framework
    388  *
    389  * NOTE:
    390  *	The following 4 busops entry points are obsoleted with version
    391  *	9 or greater. Use i_ddi_intr_op interface in place of these
    392  *	obsolete interfaces.
    393  *
    394  *	Remove these busops entry points and all related data structures
    395  *	in future major/minor solaris release.
    396  */
    397 
    398 /* ARGSUSED */
    399 ddi_intrspec_t
    400 i_ddi_get_intrspec(dev_info_t *dip, dev_info_t *rdip, uint_t inumber)
    401 {
    402 	dev_info_t	*pdip = ddi_get_parent(dip);
    403 
    404 	cmn_err(CE_WARN, "Failed to process interrupt "
    405 	    "for %s%d due to down-rev nexus driver %s%d",
    406 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
    407 	    ddi_driver_name(pdip), ddi_get_instance(pdip));
    408 
    409 	return (NULL);
    410 }
    411 
    412 /* ARGSUSED */
    413 int
    414 i_ddi_add_intrspec(dev_info_t *dip, dev_info_t *rdip, ddi_intrspec_t intrspec,
    415     ddi_iblock_cookie_t *iblock_cookiep,
    416     ddi_idevice_cookie_t *idevice_cookiep,
    417     uint_t (*int_handler)(caddr_t int_handler_arg),
    418     caddr_t int_handler_arg, int kind)
    419 {
    420 	dev_info_t	*pdip = ddi_get_parent(dip);
    421 
    422 	cmn_err(CE_WARN, "Failed to process interrupt "
    423 	    "for %s%d due to down-rev nexus driver %s%d",
    424 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
    425 	    ddi_driver_name(pdip), ddi_get_instance(pdip));
    426 
    427 	return (DDI_ENOTSUP);
    428 }
    429 
    430 /* ARGSUSED */
    431 void
    432 i_ddi_remove_intrspec(dev_info_t *dip, dev_info_t *rdip,
    433     ddi_intrspec_t intrspec, ddi_iblock_cookie_t iblock_cookie)
    434 {
    435 	dev_info_t	*pdip = ddi_get_parent(dip);
    436 
    437 	cmn_err(CE_WARN, "Failed to process interrupt "
    438 	    "for %s%d due to down-rev nexus driver %s%d",
    439 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
    440 	    ddi_driver_name(pdip), ddi_get_instance(pdip));
    441 }
    442 
    443 /* ARGSUSED */
    444 int
    445 i_ddi_intr_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_ctlop_t op,
    446     void *arg, void *val)
    447 {
    448 	dev_info_t	*pdip = ddi_get_parent(dip);
    449 
    450 	cmn_err(CE_WARN, "Failed to process interrupt "
    451 	    "for %s%d due to down-rev nexus driver %s%d",
    452 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
    453 	    ddi_driver_name(pdip), ddi_get_instance(pdip));
    454 
    455 	return (DDI_ENOTSUP);
    456 }
    457 
    458 #if defined(__i386) || defined(__amd64)
    459 ddi_acc_handle_t
    460 i_ddi_get_pci_config_handle(dev_info_t *dip)
    461 {
    462 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    463 
    464 	return (intr_p ? intr_p->devi_cfg_handle : NULL);
    465 }
    466 
    467 void
    468 i_ddi_set_pci_config_handle(dev_info_t *dip, ddi_acc_handle_t handle)
    469 {
    470 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    471 
    472 	if (intr_p)
    473 		intr_p->devi_cfg_handle = handle;
    474 }
    475 
    476 
    477 int
    478 i_ddi_get_msi_msix_cap_ptr(dev_info_t *dip)
    479 {
    480 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    481 
    482 	return (intr_p ? intr_p->devi_cap_ptr : 0);
    483 }
    484 
    485 void
    486 i_ddi_set_msi_msix_cap_ptr(dev_info_t *dip, int cap_ptr)
    487 {
    488 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
    489 
    490 	if (intr_p)
    491 		intr_p->devi_cap_ptr = cap_ptr;
    492 }
    493 #endif
    494