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