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