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