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