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