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