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