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_cap & DDI_INTR_FLAG_MASKABLE)) { 878 rw_exit(&hdlp->ih_rwlock); 879 return (DDI_EINVAL); 880 } 881 882 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 883 DDI_INTROP_SETMASK, hdlp, NULL); 884 885 rw_exit(&hdlp->ih_rwlock); 886 return (ret); 887 } 888 889 int 890 ddi_intr_clr_mask(ddi_intr_handle_t h) 891 { 892 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 893 int ret; 894 895 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_clr_mask: hdlp = %p\n", 896 (void *)hdlp)); 897 898 if (hdlp == NULL) 899 return (DDI_EINVAL); 900 901 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 902 if (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE)) { 903 rw_exit(&hdlp->ih_rwlock); 904 return (DDI_EINVAL); 905 } 906 907 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 908 DDI_INTROP_CLRMASK, hdlp, NULL); 909 910 rw_exit(&hdlp->ih_rwlock); 911 return (ret); 912 } 913 914 /* 915 * Interrupt get_pending handler 916 */ 917 int 918 ddi_intr_get_pending(ddi_intr_handle_t h, int *pendingp) 919 { 920 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 921 int ret; 922 923 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pending: hdlp = %p\n", 924 (void *)hdlp)); 925 926 if (hdlp == NULL) 927 return (DDI_EINVAL); 928 929 rw_enter(&hdlp->ih_rwlock, RW_READER); 930 if (!(hdlp->ih_cap & DDI_INTR_FLAG_PENDING)) { 931 rw_exit(&hdlp->ih_rwlock); 932 return (DDI_EINVAL); 933 } 934 935 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 936 DDI_INTROP_GETPENDING, hdlp, (void *)pendingp); 937 938 rw_exit(&hdlp->ih_rwlock); 939 return (ret); 940 } 941 942 /* 943 * Soft interrupt handlers 944 */ 945 /* 946 * Add a soft interrupt and register its handler 947 */ 948 /* ARGSUSED */ 949 int 950 ddi_intr_add_softint(dev_info_t *dip, ddi_softint_handle_t *h_p, int soft_pri, 951 ddi_intr_handler_t handler, void *arg1) 952 { 953 ddi_softint_hdl_impl_t *hdlp; 954 int ret; 955 956 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: dip = %p, " 957 "softpri = 0x%x\n", (void *)dip, soft_pri)); 958 959 if ((dip == NULL) || (h_p == NULL) || (handler == NULL)) { 960 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: " 961 "invalid arguments")); 962 963 return (DDI_EINVAL); 964 } 965 966 /* Validate input arguments */ 967 if (soft_pri < DDI_INTR_SOFTPRI_MIN || 968 soft_pri > DDI_INTR_SOFTPRI_MAX) { 969 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: invalid " 970 "soft_pri input given = %x\n", soft_pri)); 971 return (DDI_EINVAL); 972 } 973 974 hdlp = (ddi_softint_hdl_impl_t *)kmem_zalloc( 975 sizeof (ddi_softint_hdl_impl_t), KM_SLEEP); 976 977 /* fill up internally */ 978 rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL); 979 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 980 hdlp->ih_pri = soft_pri; 981 hdlp->ih_dip = dip; 982 hdlp->ih_cb_func = handler; 983 hdlp->ih_cb_arg1 = arg1; 984 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: hdlp = %p\n", 985 (void *)hdlp)); 986 987 /* do the platform specific calls */ 988 if ((ret = i_ddi_add_softint(hdlp)) != DDI_SUCCESS) { 989 rw_exit(&hdlp->ih_rwlock); 990 rw_destroy(&hdlp->ih_rwlock); 991 kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t)); 992 return (ret); 993 } 994 995 *h_p = (ddi_softint_handle_t)hdlp; 996 rw_exit(&hdlp->ih_rwlock); 997 return (ret); 998 } 999 1000 /* 1001 * Remove the soft interrupt 1002 */ 1003 int 1004 ddi_intr_remove_softint(ddi_softint_handle_t h) 1005 { 1006 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h; 1007 1008 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_softint: hdlp = %p\n", 1009 (void *)hdlp)); 1010 1011 if (hdlp == NULL) 1012 return (DDI_EINVAL); 1013 1014 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 1015 i_ddi_remove_softint(hdlp); 1016 rw_exit(&hdlp->ih_rwlock); 1017 rw_destroy(&hdlp->ih_rwlock); 1018 1019 /* kmem_free the hdl impl_t structure allocated earlier */ 1020 kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t)); 1021 return (DDI_SUCCESS); 1022 } 1023 1024 /* 1025 * Trigger a soft interrupt 1026 */ 1027 int 1028 ddi_intr_trigger_softint(ddi_softint_handle_t h, void *arg2) 1029 { 1030 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h; 1031 int ret; 1032 1033 if (hdlp == NULL) 1034 return (DDI_EINVAL); 1035 1036 if ((ret = i_ddi_trigger_softint(hdlp, arg2)) != DDI_SUCCESS) { 1037 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_trigger_softint: failed, " 1038 " ret 0%x\n", ret)); 1039 1040 return (ret); 1041 } 1042 1043 hdlp->ih_cb_arg2 = arg2; 1044 return (DDI_SUCCESS); 1045 } 1046 1047 /* 1048 * Get the soft interrupt priority 1049 */ 1050 int 1051 ddi_intr_get_softint_pri(ddi_softint_handle_t h, uint_t *soft_prip) 1052 { 1053 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h; 1054 1055 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_softint_pri: h = %p\n", 1056 (void *)h)); 1057 1058 if (hdlp == NULL) 1059 return (DDI_EINVAL); 1060 1061 rw_enter(&hdlp->ih_rwlock, RW_READER); 1062 *soft_prip = hdlp->ih_pri; 1063 rw_exit(&hdlp->ih_rwlock); 1064 return (DDI_SUCCESS); 1065 } 1066 1067 /* 1068 * Set the soft interrupt priority 1069 */ 1070 int 1071 ddi_intr_set_softint_pri(ddi_softint_handle_t h, uint_t soft_pri) 1072 { 1073 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h; 1074 int ret; 1075 uint_t orig_soft_pri; 1076 1077 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: h = %p\n", 1078 (void *)h)); 1079 1080 if (hdlp == NULL) 1081 return (DDI_EINVAL); 1082 1083 /* Validate priority argument */ 1084 if (soft_pri < DDI_INTR_SOFTPRI_MIN || 1085 soft_pri > DDI_INTR_SOFTPRI_MAX) { 1086 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: invalid " 1087 "soft_pri input given = %x\n", soft_pri)); 1088 return (DDI_EINVAL); 1089 } 1090 1091 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 1092 orig_soft_pri = hdlp->ih_pri; 1093 hdlp->ih_pri = soft_pri; 1094 1095 if ((ret = i_ddi_set_softint_pri(hdlp, orig_soft_pri)) != DDI_SUCCESS) { 1096 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: failed, " 1097 " ret 0%x\n", ret)); 1098 hdlp->ih_pri = orig_soft_pri; 1099 } 1100 1101 rw_exit(&hdlp->ih_rwlock); 1102 return (ret); 1103 } 1104 1105 /* 1106 * Old DDI interrupt framework 1107 * 1108 * The following DDI interrupt interfaces are obsolete. 1109 * Use the above new DDI interrupt interfaces instead. 1110 */ 1111 1112 int 1113 ddi_intr_hilevel(dev_info_t *dip, uint_t inumber) 1114 { 1115 ddi_intr_handle_t hdl, *existing_hdlp; 1116 int actual, ret; 1117 uint_t high_pri, pri; 1118 1119 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: name=%s%d dip=0x%p " 1120 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1121 (void *)dip, inumber)); 1122 1123 /* 1124 * The device driver may have already registed with the 1125 * framework. If so, first try to get the existing interrupt handle 1126 * for that given inumber and use that handle. 1127 */ 1128 existing_hdlp = i_ddi_get_intr_handle(dip, inumber); 1129 if (existing_hdlp) { 1130 hdl = existing_hdlp[0]; /* Use existing handle */ 1131 } else { 1132 if ((ret = ddi_intr_alloc(dip, &hdl, DDI_INTR_TYPE_FIXED, 1133 inumber, 1, &actual, 1134 DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) { 1135 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: " 1136 "ddi_intr_alloc failed, ret 0x%x\n", ret)); 1137 return (0); 1138 } 1139 } 1140 1141 if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) { 1142 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: " 1143 "ddi_intr_get_pri failed, ret 0x%x\n", ret)); 1144 (void) ddi_intr_free(hdl); 1145 return (0); 1146 } 1147 1148 high_pri = ddi_intr_get_hilevel_pri(); 1149 1150 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: pri = %x, " 1151 "high_pri = %x\n", pri, high_pri)); 1152 1153 /* Free the handle allocated here only if no existing handle exists */ 1154 if (existing_hdlp == NULL) 1155 (void) ddi_intr_free(hdl); 1156 1157 return (pri >= high_pri); 1158 } 1159 1160 int 1161 ddi_dev_nintrs(dev_info_t *dip, int *result) 1162 { 1163 DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: name=%s%d dip=0x%p\n", 1164 ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip)); 1165 1166 if (ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, 1167 result) != DDI_SUCCESS) { 1168 DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: " 1169 "ddi_intr_get_nintrs failed\n")); 1170 *result = 0; 1171 } 1172 1173 return (DDI_SUCCESS); 1174 } 1175 1176 int 1177 ddi_get_iblock_cookie(dev_info_t *dip, uint_t inumber, 1178 ddi_iblock_cookie_t *iblock_cookiep) 1179 { 1180 ddi_intr_handle_t hdl, *existing_hdlp; 1181 int actual, ret; 1182 uint_t pri; 1183 1184 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: name=%s%d dip=0x%p " 1185 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1186 (void *)dip, inumber)); 1187 1188 ASSERT(iblock_cookiep != NULL); 1189 1190 /* 1191 * The device driver may have already registed with the 1192 * framework. If so, first try to get the existing interrupt handle 1193 * for that given inumber and use that handle. 1194 */ 1195 existing_hdlp = i_ddi_get_intr_handle(dip, inumber); 1196 if (existing_hdlp) { 1197 hdl = existing_hdlp[0]; /* Use existing handle */ 1198 } else { 1199 if ((ret = ddi_intr_alloc(dip, &hdl, DDI_INTR_TYPE_FIXED, 1200 inumber, 1, &actual, 1201 DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) { 1202 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: " 1203 "ddi_intr_alloc failed, ret 0x%x\n", ret)); 1204 return (DDI_INTR_NOTFOUND); 1205 } 1206 } 1207 1208 if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) { 1209 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: " 1210 "ddi_intr_get_pri failed, ret 0x%x\n", ret)); 1211 1212 (void) ddi_intr_free(hdl); 1213 return (DDI_FAILURE); 1214 } 1215 1216 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri; 1217 /* Free the handle allocated here only if no existing handle exists */ 1218 if (existing_hdlp == NULL) 1219 (void) ddi_intr_free(hdl); 1220 1221 return (DDI_SUCCESS); 1222 } 1223 1224 int 1225 ddi_add_intr(dev_info_t *dip, uint_t inumber, 1226 ddi_iblock_cookie_t *iblock_cookiep, 1227 ddi_idevice_cookie_t *idevice_cookiep, 1228 uint_t (*int_handler)(caddr_t int_handler_arg), 1229 caddr_t int_handler_arg) 1230 { 1231 ddi_intr_handle_t *hdl_p; 1232 int actual, ret; 1233 uint_t pri; 1234 1235 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: name=%s%d dip=0x%p " 1236 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1237 (void *)dip, inumber)); 1238 1239 hdl_p = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP); 1240 1241 if ((ret = ddi_intr_alloc(dip, hdl_p, DDI_INTR_TYPE_FIXED, 1242 inumber, 1, &actual, DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) { 1243 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: " 1244 "ddi_intr_alloc failed, ret 0x%x\n", ret)); 1245 kmem_free(hdl_p, sizeof (ddi_intr_handle_t)); 1246 return (DDI_INTR_NOTFOUND); 1247 } 1248 1249 if ((ret = ddi_intr_get_pri(hdl_p[0], &pri)) != DDI_SUCCESS) { 1250 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: " 1251 "ddi_intr_get_pri failed, ret 0x%x\n", ret)); 1252 (void) ddi_intr_free(hdl_p[0]); 1253 kmem_free(hdl_p, sizeof (ddi_intr_handle_t)); 1254 return (DDI_FAILURE); 1255 } 1256 1257 if ((ret = ddi_intr_add_handler(hdl_p[0], (ddi_intr_handler_t *) 1258 int_handler, int_handler_arg, NULL)) != DDI_SUCCESS) { 1259 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: " 1260 "ddi_intr_add_handler failed, ret 0x%x\n", ret)); 1261 (void) ddi_intr_free(hdl_p[0]); 1262 kmem_free(hdl_p, sizeof (ddi_intr_handle_t)); 1263 return (DDI_FAILURE); 1264 } 1265 1266 if ((ret = ddi_intr_enable(hdl_p[0])) != DDI_SUCCESS) { 1267 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: " 1268 "ddi_intr_enable failed, ret 0x%x\n", ret)); 1269 (void) ddi_intr_remove_handler(hdl_p[0]); 1270 (void) ddi_intr_free(hdl_p[0]); 1271 kmem_free(hdl_p, sizeof (ddi_intr_handle_t)); 1272 return (DDI_FAILURE); 1273 } 1274 1275 if (iblock_cookiep) 1276 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri; 1277 1278 if (idevice_cookiep) { 1279 idevice_cookiep->idev_vector = 0; 1280 idevice_cookiep->idev_priority = pri; 1281 } 1282 1283 return (DDI_SUCCESS); 1284 } 1285 1286 /* ARGSUSED */ 1287 int 1288 ddi_add_fastintr(dev_info_t *dip, uint_t inumber, 1289 ddi_iblock_cookie_t *iblock_cookiep, 1290 ddi_idevice_cookie_t *idevice_cookiep, 1291 uint_t (*hi_int_handler)(void)) 1292 { 1293 DDI_INTR_APIDBG((CE_CONT, "ddi_add_fastintr: name=%s%d dip=0x%p " 1294 "inum=0x%x: Not supported, return failure\n", ddi_driver_name(dip), 1295 ddi_get_instance(dip), (void *)dip, inumber)); 1296 1297 return (DDI_FAILURE); 1298 } 1299 1300 /* ARGSUSED */ 1301 void 1302 ddi_remove_intr(dev_info_t *dip, uint_t inum, ddi_iblock_cookie_t iblock_cookie) 1303 { 1304 ddi_intr_handle_t *hdl_p; 1305 int ret; 1306 1307 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: name=%s%d dip=0x%p " 1308 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1309 (void *)dip, inum)); 1310 1311 if ((hdl_p = i_ddi_get_intr_handle(dip, inum)) == NULL) { 1312 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: no handle " 1313 "found\n")); 1314 return; 1315 } 1316 1317 if ((ret = ddi_intr_disable(hdl_p[0])) != DDI_SUCCESS) { 1318 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: " 1319 "ddi_intr_disable failed, ret 0x%x\n", ret)); 1320 return; 1321 } 1322 1323 if ((ret = ddi_intr_remove_handler(hdl_p[0])) != DDI_SUCCESS) { 1324 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: " 1325 "ddi_intr_remove_handler failed, ret 0x%x\n", ret)); 1326 return; 1327 } 1328 1329 if ((ret = ddi_intr_free(hdl_p[0])) != DDI_SUCCESS) { 1330 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: " 1331 "ddi_intr_free failed, ret 0x%x\n", ret)); 1332 return; 1333 } 1334 1335 kmem_free(hdl_p, sizeof (ddi_intr_handle_t)); 1336 } 1337 1338 /* ARGSUSED */ 1339 int 1340 ddi_get_soft_iblock_cookie(dev_info_t *dip, int preference, 1341 ddi_iblock_cookie_t *iblock_cookiep) 1342 { 1343 DDI_INTR_APIDBG((CE_CONT, "ddi_get_soft_iblock_cookie: name=%s%d " 1344 "dip=0x%p pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1345 (void *)dip, preference)); 1346 1347 ASSERT(iblock_cookiep != NULL); 1348 1349 if (preference == DDI_SOFTINT_FIXED) 1350 return (DDI_FAILURE); 1351 1352 *iblock_cookiep = (ddi_iblock_cookie_t)((uintptr_t) 1353 ((preference > DDI_SOFTINT_MED) ? DDI_SOFT_INTR_PRI_H : 1354 DDI_SOFT_INTR_PRI_M)); 1355 1356 return (DDI_SUCCESS); 1357 } 1358 1359 int 1360 ddi_add_softintr(dev_info_t *dip, int preference, ddi_softintr_t *idp, 1361 ddi_iblock_cookie_t *iblock_cookiep, 1362 ddi_idevice_cookie_t *idevice_cookiep, 1363 uint_t (*int_handler)(caddr_t int_handler_arg), 1364 caddr_t int_handler_arg) 1365 { 1366 ddi_softint_handle_t *hdl_p; 1367 uint64_t softpri; 1368 int ret; 1369 1370 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: name=%s%d dip=0x%p " 1371 "pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1372 (void *)dip, preference)); 1373 1374 if ((idp == NULL) || ((preference == DDI_SOFTINT_FIXED) && 1375 (iblock_cookiep == NULL))) 1376 return (DDI_FAILURE); 1377 1378 /* Translate the priority preference */ 1379 if (preference == DDI_SOFTINT_FIXED) { 1380 softpri = (uint64_t)(uintptr_t)*iblock_cookiep; 1381 softpri = MIN(softpri, DDI_SOFT_INTR_PRI_H); 1382 } else { 1383 softpri = (uint64_t)((preference > DDI_SOFTINT_MED) ? 1384 DDI_SOFT_INTR_PRI_H : DDI_SOFT_INTR_PRI_M); 1385 } 1386 1387 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: preference 0x%x " 1388 "softpri 0x%lx\n", preference, (long)softpri)); 1389 1390 hdl_p = kmem_zalloc(sizeof (ddi_softint_handle_t), KM_SLEEP); 1391 if ((ret = ddi_intr_add_softint(dip, hdl_p, softpri, 1392 (ddi_intr_handler_t *)int_handler, int_handler_arg)) != 1393 DDI_SUCCESS) { 1394 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: " 1395 "ddi_intr_add_softint failed, ret 0x%x\n", ret)); 1396 1397 kmem_free(hdl_p, sizeof (ddi_softint_handle_t)); 1398 return (DDI_FAILURE); 1399 } 1400 1401 if (iblock_cookiep) 1402 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)softpri; 1403 1404 if (idevice_cookiep) { 1405 idevice_cookiep->idev_vector = 0; 1406 idevice_cookiep->idev_priority = softpri; 1407 } 1408 1409 *idp = (ddi_softintr_t)hdl_p; 1410 1411 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: dip = 0x%p, " 1412 "idp = 0x%p, ret = %x\n", (void *)dip, (void *)*idp, ret)); 1413 1414 return (DDI_SUCCESS); 1415 } 1416 1417 void 1418 ddi_remove_softintr(ddi_softintr_t id) 1419 { 1420 ddi_softint_handle_t *h_p = (ddi_softint_handle_t *)id; 1421 1422 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: id=0x%p\n", 1423 (void *)id)); 1424 1425 if (h_p == NULL) 1426 return; 1427 1428 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: handle 0x%p\n", 1429 (void *)h_p)); 1430 1431 (void) ddi_intr_remove_softint(*h_p); 1432 kmem_free(h_p, sizeof (ddi_softint_handle_t)); 1433 } 1434 1435 void 1436 ddi_trigger_softintr(ddi_softintr_t id) 1437 { 1438 ddi_softint_handle_t *h_p = (ddi_softint_handle_t *)id; 1439 int ret; 1440 1441 if (h_p == NULL) 1442 return; 1443 1444 if ((ret = ddi_intr_trigger_softint(*h_p, NULL)) != DDI_SUCCESS) { 1445 DDI_INTR_APIDBG((CE_CONT, "ddi_trigger_softintr: " 1446 "ddi_intr_trigger_softint failed, hdlp 0x%p " 1447 "ret 0x%x\n", (void *)h_p, ret)); 1448 } 1449 } 1450