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