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