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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <sys/note.h> 26 #include <sys/sysmacros.h> 27 #include <sys/types.h> 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/kmem.h> 31 #include <sys/cmn_err.h> 32 #include <sys/debug.h> 33 #include <sys/avintr.h> 34 #include <sys/autoconf.h> 35 #include <sys/sunndi.h> 36 #include <sys/ndi_impldefs.h> /* include prototypes */ 37 38 #if defined(__x86) 39 /* 40 * MSI-X allocation limit. 41 */ 42 uint_t ddi_msix_alloc_limit = DDI_DEFAULT_MSIX_ALLOC; 43 #endif 44 45 /* 46 * New DDI interrupt framework 47 */ 48 void 49 i_ddi_intr_devi_init(dev_info_t *dip) 50 { 51 int supported_types; 52 53 DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_init: dip %p\n", 54 (void *)dip)); 55 56 if (DEVI(dip)->devi_intr_p) 57 return; 58 59 DEVI(dip)->devi_intr_p = kmem_zalloc(sizeof (devinfo_intr_t), KM_SLEEP); 60 61 supported_types = i_ddi_intr_get_supported_types(dip); 62 63 /* Save supported interrupt types information */ 64 i_ddi_intr_set_supported_types(dip, supported_types); 65 } 66 67 void 68 i_ddi_intr_devi_fini(dev_info_t *dip) 69 { 70 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 71 72 DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_fini: dip %p\n", 73 (void *)dip)); 74 75 if ((intr_p == NULL) || i_ddi_intr_get_current_nintrs(dip)) 76 return; 77 78 /* 79 * devi_intr_handle_p will only be used for devices 80 * which are using the legacy DDI Interrupt interfaces. 81 */ 82 if (intr_p->devi_intr_handle_p) { 83 /* nintrs could be zero; so check for it first */ 84 if (intr_p->devi_intr_sup_nintrs) { 85 kmem_free(intr_p->devi_intr_handle_p, 86 intr_p->devi_intr_sup_nintrs * 87 sizeof (ddi_intr_handle_t)); 88 } 89 } 90 91 /* 92 * devi_irm_req_p will only be used for devices which 93 * are mapped to an Interrupt Resource Management pool. 94 */ 95 if (intr_p->devi_irm_req_p) 96 (void) i_ddi_irm_remove(dip); 97 98 kmem_free(DEVI(dip)->devi_intr_p, sizeof (devinfo_intr_t)); 99 DEVI(dip)->devi_intr_p = NULL; 100 } 101 102 uint_t 103 i_ddi_intr_get_supported_types(dev_info_t *dip) 104 { 105 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 106 ddi_intr_handle_impl_t hdl; 107 int ret, intr_types; 108 109 if ((intr_p) && (intr_p->devi_intr_sup_types)) 110 return (intr_p->devi_intr_sup_types); 111 112 bzero(&hdl, sizeof (ddi_intr_handle_impl_t)); 113 hdl.ih_dip = dip; 114 115 ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_SUPPORTED_TYPES, &hdl, 116 (void *)&intr_types); 117 118 return ((ret == DDI_SUCCESS) ? intr_types : 0); 119 } 120 121 /* 122 * NOTE: This function is only called by i_ddi_dev_init(). 123 */ 124 void 125 i_ddi_intr_set_supported_types(dev_info_t *dip, int intr_types) 126 { 127 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 128 129 if (intr_p) 130 intr_p->devi_intr_sup_types = intr_types; 131 } 132 133 uint_t 134 i_ddi_intr_get_supported_nintrs(dev_info_t *dip, int intr_type) 135 { 136 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 137 ddi_intr_handle_impl_t hdl; 138 int ret, nintrs; 139 140 if ((intr_p) && (intr_p->devi_intr_curr_type == intr_type) && 141 (intr_p->devi_intr_sup_nintrs)) 142 return (intr_p->devi_intr_sup_nintrs); 143 144 bzero(&hdl, sizeof (ddi_intr_handle_impl_t)); 145 hdl.ih_dip = dip; 146 hdl.ih_type = intr_type; 147 148 ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl, 149 (void *)&nintrs); 150 151 return ((ret == DDI_SUCCESS) ? nintrs : 0); 152 } 153 154 /* 155 * NOTE: This function is only called by ddi_intr_alloc(). 156 */ 157 void 158 i_ddi_intr_set_supported_nintrs(dev_info_t *dip, int nintrs) 159 { 160 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 161 162 if (intr_p) 163 intr_p->devi_intr_sup_nintrs = nintrs; 164 } 165 166 uint_t 167 i_ddi_intr_get_current_type(dev_info_t *dip) 168 { 169 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 170 171 return (intr_p ? intr_p->devi_intr_curr_type : 0); 172 } 173 174 /* 175 * NOTE: This function is only called by 176 * ddi_intr_alloc() and ddi_intr_free(). 177 */ 178 void 179 i_ddi_intr_set_current_type(dev_info_t *dip, int intr_type) 180 { 181 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 182 183 if (intr_p) 184 intr_p->devi_intr_curr_type = intr_type; 185 } 186 187 uint_t 188 i_ddi_intr_get_current_nintrs(dev_info_t *dip) 189 { 190 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 191 192 return (intr_p ? intr_p->devi_intr_curr_nintrs : 0); 193 } 194 195 /* 196 * NOTE: This function is only called by 197 * ddi_intr_alloc() and ddi_intr_free(). 198 */ 199 void 200 i_ddi_intr_set_current_nintrs(dev_info_t *dip, int nintrs) 201 { 202 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 203 204 if (intr_p) 205 intr_p->devi_intr_curr_nintrs = nintrs; 206 } 207 208 uint_t 209 i_ddi_intr_get_current_nenables(dev_info_t *dip) 210 { 211 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 212 213 return (intr_p ? intr_p->devi_intr_curr_nenables : 0); 214 } 215 216 void 217 i_ddi_intr_set_current_nenables(dev_info_t *dip, int nintrs) 218 { 219 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 220 221 if (intr_p) 222 intr_p->devi_intr_curr_nenables = nintrs; 223 } 224 225 /* 226 * i_ddi_intr_get_current_navail: 227 * 228 * Return the number of interrupts currently available. 229 * If a precise number set by IRM is not available, then 230 * return the limit determined by i_ddi_intr_get_limit(). 231 */ 232 uint_t 233 i_ddi_intr_get_current_navail(dev_info_t *dip, int type) 234 { 235 devinfo_intr_t *intr_p; 236 ddi_irm_pool_t *pool_p; 237 ddi_irm_req_t *req_p; 238 uint_t navail; 239 240 /* Check for a precise number from IRM */ 241 if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) && 242 ((req_p = intr_p->devi_irm_req_p) != NULL) && 243 (type == req_p->ireq_type) && 244 ((pool_p = req_p->ireq_pool_p) != NULL)) { 245 /* 246 * Lock to be sure a rebalance is not in progress. 247 * (Should be changed to a rwlock.) 248 */ 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 255 /* Otherwise, return the limit */ 256 return (i_ddi_intr_get_limit(dip, type, NULL)); 257 } 258 259 /* 260 * i_ddi_intr_get_limit: 261 * 262 * Return the limit of how many interrupts a driver can allocate. 263 */ 264 uint_t 265 i_ddi_intr_get_limit(dev_info_t *dip, int type, ddi_irm_pool_t *pool_p) 266 { 267 ddi_intr_handle_impl_t hdl; 268 uint_t limit, nintrs; 269 270 /* Check for interrupt pool */ 271 if (pool_p == NULL) 272 pool_p = i_ddi_intr_get_pool(dip, type); 273 274 /* Get default limit, from interrupt pool or by INTROP method */ 275 if (pool_p != NULL) { 276 limit = pool_p->ipool_defsz; 277 } else { 278 bzero(&hdl, sizeof (ddi_intr_handle_impl_t)); 279 hdl.ih_dip = dip; 280 hdl.ih_type = type; 281 if (i_ddi_intr_ops(dip, dip, DDI_INTROP_NAVAIL, &hdl, 282 (void *)&limit) != DDI_SUCCESS) 283 return (0); 284 } 285 286 /* Get maximum supported by the device */ 287 nintrs = i_ddi_intr_get_supported_nintrs(dip, type); 288 289 /* No limit if device and system both support IRM */ 290 if ((pool_p != NULL) && (i_ddi_irm_supported(dip, type) == DDI_SUCCESS)) 291 return (nintrs); 292 293 /* Limit cannot exceed what device supports */ 294 limit = MIN(limit, nintrs); 295 296 /* Impose a global MSI-X limit on x86 */ 297 #if defined(__x86) 298 if (type == DDI_INTR_TYPE_MSIX) 299 limit = MIN(limit, ddi_msix_alloc_limit); 300 #endif 301 302 /* Impose a global MSI limit on all platforms */ 303 if (type == DDI_INTR_TYPE_MSI) 304 limit = MIN(limit, DDI_MAX_MSI_ALLOC); 305 306 return (limit); 307 } 308 309 ddi_intr_msix_t * 310 i_ddi_get_msix(dev_info_t *dip) 311 { 312 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 313 314 return (intr_p ? intr_p->devi_msix_p : NULL); 315 } 316 317 void 318 i_ddi_set_msix(dev_info_t *dip, ddi_intr_msix_t *msix_p) 319 { 320 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 321 322 if (intr_p) 323 intr_p->devi_msix_p = msix_p; 324 } 325 326 ddi_intr_handle_t 327 i_ddi_get_intr_handle(dev_info_t *dip, int inum) 328 { 329 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 330 331 if (intr_p == NULL) 332 return (NULL); 333 334 /* 335 * Changed this to a check and return NULL if an invalid inum 336 * is passed to retrieve a handle 337 */ 338 if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs)) 339 return (NULL); 340 341 return ((intr_p->devi_intr_handle_p) ? 342 intr_p->devi_intr_handle_p[inum] : NULL); 343 } 344 345 void 346 i_ddi_set_intr_handle(dev_info_t *dip, int inum, ddi_intr_handle_t intr_hdl) 347 { 348 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 349 350 if (intr_p == NULL) 351 return; 352 353 /* 354 * Changed this to a check and return if an invalid inum 355 * is passed to set a handle 356 */ 357 if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs)) 358 return; 359 360 if (intr_hdl && (intr_p->devi_intr_handle_p == NULL)) { 361 /* nintrs could be zero; so check for it first */ 362 if (intr_p->devi_intr_sup_nintrs) 363 intr_p->devi_intr_handle_p = kmem_zalloc( 364 sizeof (ddi_intr_handle_t) * 365 intr_p->devi_intr_sup_nintrs, KM_SLEEP); 366 } 367 368 if (intr_p->devi_intr_handle_p) 369 intr_p->devi_intr_handle_p[inum] = intr_hdl; 370 } 371 372 /* 373 * The "ddi-intr-weight" property contains the weight of each interrupt 374 * associated with a dev_info node. For devices with multiple interrupts per 375 * dev_info node, the total load of the device is "devi_intr_weight * nintr", 376 * possibly spread out over multiple CPUs. 377 * 378 * Maintaining this as a property permits possible tweaking in the product 379 * in response to customer problems via driver.conf property definitions at 380 * the driver or the instance level. This does not mean that "ddi-intr_weight" 381 * is a formal or committed interface. 382 */ 383 int32_t 384 i_ddi_get_intr_weight(dev_info_t *dip) 385 { 386 int32_t weight; 387 388 weight = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 389 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "ddi-intr-weight", -1); 390 if (weight < -1) 391 weight = -1; /* undefined */ 392 return (weight); 393 } 394 395 int32_t 396 i_ddi_set_intr_weight(dev_info_t *dip, int32_t weight) 397 { 398 int32_t oweight; 399 400 oweight = i_ddi_get_intr_weight(dip); 401 if ((weight > 0) && (oweight != weight)) 402 (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, 403 "ddi-intr-weight", weight); 404 return (oweight); 405 } 406 407 /* 408 * Old DDI interrupt framework 409 * 410 * NOTE: 411 * The following 4 busops entry points are obsoleted with version 412 * 9 or greater. Use i_ddi_intr_op interface in place of these 413 * obsolete interfaces. 414 * 415 * Remove these busops entry points and all related data structures 416 * in future major/minor solaris release. 417 */ 418 419 /* ARGSUSED */ 420 ddi_intrspec_t 421 i_ddi_get_intrspec(dev_info_t *dip, dev_info_t *rdip, uint_t inumber) 422 { 423 dev_info_t *pdip = ddi_get_parent(dip); 424 425 cmn_err(CE_WARN, "Failed to process interrupt " 426 "for %s%d due to down-rev nexus driver %s%d", 427 ddi_driver_name(rdip), ddi_get_instance(rdip), 428 ddi_driver_name(pdip), ddi_get_instance(pdip)); 429 430 return (NULL); 431 } 432 433 /* ARGSUSED */ 434 int 435 i_ddi_add_intrspec(dev_info_t *dip, dev_info_t *rdip, ddi_intrspec_t intrspec, 436 ddi_iblock_cookie_t *iblock_cookiep, 437 ddi_idevice_cookie_t *idevice_cookiep, 438 uint_t (*int_handler)(caddr_t int_handler_arg), 439 caddr_t int_handler_arg, int kind) 440 { 441 dev_info_t *pdip = ddi_get_parent(dip); 442 443 cmn_err(CE_WARN, "Failed to process interrupt " 444 "for %s%d due to down-rev nexus driver %s%d", 445 ddi_driver_name(rdip), ddi_get_instance(rdip), 446 ddi_driver_name(pdip), ddi_get_instance(pdip)); 447 448 return (DDI_ENOTSUP); 449 } 450 451 /* ARGSUSED */ 452 void 453 i_ddi_remove_intrspec(dev_info_t *dip, dev_info_t *rdip, 454 ddi_intrspec_t intrspec, ddi_iblock_cookie_t iblock_cookie) 455 { 456 dev_info_t *pdip = ddi_get_parent(dip); 457 458 cmn_err(CE_WARN, "Failed to process interrupt " 459 "for %s%d due to down-rev nexus driver %s%d", 460 ddi_driver_name(rdip), ddi_get_instance(rdip), 461 ddi_driver_name(pdip), ddi_get_instance(pdip)); 462 } 463 464 /* ARGSUSED */ 465 int 466 i_ddi_intr_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_ctlop_t op, 467 void *arg, void *val) 468 { 469 dev_info_t *pdip = ddi_get_parent(dip); 470 471 cmn_err(CE_WARN, "Failed to process interrupt " 472 "for %s%d due to down-rev nexus driver %s%d", 473 ddi_driver_name(rdip), ddi_get_instance(rdip), 474 ddi_driver_name(pdip), ddi_get_instance(pdip)); 475 476 return (DDI_ENOTSUP); 477 } 478 479 /* 480 * Interrupt target get/set functions 481 */ 482 int 483 get_intr_affinity(ddi_intr_handle_t h, processorid_t *tgt_p) 484 { 485 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 486 int ret; 487 488 DDI_INTR_APIDBG((CE_CONT, "get_intr_affinity: hdlp = %p\n", 489 (void *)hdlp)); 490 491 if ((hdlp == NULL) || (tgt_p == NULL)) 492 return (DDI_EINVAL); 493 494 rw_enter(&hdlp->ih_rwlock, RW_READER); 495 if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE) { 496 rw_exit(&hdlp->ih_rwlock); 497 return (DDI_EINVAL); 498 } 499 500 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 501 DDI_INTROP_GETTARGET, hdlp, (void *)tgt_p); 502 503 DDI_INTR_APIDBG((CE_CONT, "get_intr_affinity: target %x\n", 504 *tgt_p)); 505 506 if (ret == DDI_SUCCESS) 507 hdlp->ih_target = *tgt_p; 508 509 rw_exit(&hdlp->ih_rwlock); 510 return (ret); 511 } 512 513 int 514 set_intr_affinity(ddi_intr_handle_t h, processorid_t tgt) 515 { 516 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 517 int ret; 518 519 DDI_INTR_APIDBG((CE_CONT, "set_intr_affinity: hdlp = %p " 520 "target %x\n", (void *)hdlp, tgt)); 521 522 if (hdlp == NULL) 523 return (DDI_EINVAL); 524 525 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 526 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) || 527 (hdlp->ih_type != DDI_INTR_TYPE_MSIX)) { 528 rw_exit(&hdlp->ih_rwlock); 529 return (DDI_EINVAL); 530 } 531 532 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 533 DDI_INTROP_SETTARGET, hdlp, &tgt); 534 535 if (ret == DDI_SUCCESS) 536 hdlp->ih_target = tgt; 537 538 rw_exit(&hdlp->ih_rwlock); 539 return (ret); 540 } 541 542 #if defined(__x86) 543 ddi_acc_handle_t 544 i_ddi_get_pci_config_handle(dev_info_t *dip) 545 { 546 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 547 548 return (intr_p ? intr_p->devi_cfg_handle : NULL); 549 } 550 551 void 552 i_ddi_set_pci_config_handle(dev_info_t *dip, ddi_acc_handle_t handle) 553 { 554 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 555 556 if (intr_p) 557 intr_p->devi_cfg_handle = handle; 558 } 559 560 561 int 562 i_ddi_get_msi_msix_cap_ptr(dev_info_t *dip) 563 { 564 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 565 566 return (intr_p ? intr_p->devi_cap_ptr : 0); 567 } 568 569 void 570 i_ddi_set_msi_msix_cap_ptr(dev_info_t *dip, int cap_ptr) 571 { 572 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p; 573 574 if (intr_p) 575 intr_p->devi_cap_ptr = cap_ptr; 576 } 577 #endif 578