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