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