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