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 target get/set functions 732 */ 733 int 734 ddi_intr_get_affinity(ddi_intr_handle_t h, ddi_intr_target_t *tgt_p) 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_get_affinity: hdlp = %p\n", 740 (void *)hdlp)); 741 742 if ((hdlp == NULL) || (tgt_p == NULL)) 743 return (DDI_EINVAL); 744 745 rw_enter(&hdlp->ih_rwlock, RW_READER); 746 if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE) { 747 rw_exit(&hdlp->ih_rwlock); 748 return (DDI_EINVAL); 749 } 750 751 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 752 DDI_INTROP_GETTARGET, hdlp, (void *)tgt_p); 753 754 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_affinity: target %x\n", 755 *tgt_p)); 756 757 if (ret == DDI_SUCCESS) 758 hdlp->ih_target = *tgt_p; 759 760 rw_exit(&hdlp->ih_rwlock); 761 return (ret); 762 } 763 764 int 765 ddi_intr_set_affinity(ddi_intr_handle_t h, ddi_intr_target_t tgt) 766 { 767 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 768 int ret; 769 770 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_affinity: hdlp = %p " 771 "target %x\n", (void *)hdlp, tgt)); 772 773 if (hdlp == NULL) 774 return (DDI_EINVAL); 775 776 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 777 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) || 778 !(hdlp->ih_cap & DDI_INTR_FLAG_RETARGETABLE)) { 779 rw_exit(&hdlp->ih_rwlock); 780 return (DDI_EINVAL); 781 } 782 783 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 784 DDI_INTROP_SETTARGET, hdlp, &tgt); 785 786 if (ret == DDI_SUCCESS) 787 hdlp->ih_target = tgt; 788 789 rw_exit(&hdlp->ih_rwlock); 790 return (ret); 791 } 792 793 /* 794 * Interrupt enable/disable/block_enable/block_disable handlers 795 */ 796 int 797 ddi_intr_enable(ddi_intr_handle_t h) 798 { 799 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 800 int ret; 801 802 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_enable: hdlp = %p\n", 803 (void *)hdlp)); 804 805 if (hdlp == NULL) 806 return (DDI_EINVAL); 807 808 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 809 if ((hdlp->ih_state != DDI_IHDL_STATE_ADDED) || 810 ((hdlp->ih_type == DDI_INTR_TYPE_MSI) && 811 (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) { 812 rw_exit(&hdlp->ih_rwlock); 813 return (DDI_EINVAL); 814 } 815 816 I_DDI_VERIFY_MSIX_HANDLE(hdlp); 817 818 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 819 DDI_INTROP_ENABLE, hdlp, NULL); 820 821 if (ret == DDI_SUCCESS) { 822 hdlp->ih_state = DDI_IHDL_STATE_ENABLE; 823 i_ddi_intr_set_current_nenables(hdlp->ih_dip, 824 i_ddi_intr_get_current_nenables(hdlp->ih_dip) + 1); 825 } 826 827 rw_exit(&hdlp->ih_rwlock); 828 return (ret); 829 } 830 831 int 832 ddi_intr_disable(ddi_intr_handle_t h) 833 { 834 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 835 int ret; 836 837 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_disable: hdlp = %p\n", 838 (void *)hdlp)); 839 840 if (hdlp == NULL) 841 return (DDI_EINVAL); 842 843 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 844 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) || 845 ((hdlp->ih_type == DDI_INTR_TYPE_MSI) && 846 (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) { 847 rw_exit(&hdlp->ih_rwlock); 848 return (DDI_EINVAL); 849 } 850 851 I_DDI_VERIFY_MSIX_HANDLE(hdlp); 852 853 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 854 DDI_INTROP_DISABLE, hdlp, NULL); 855 856 if (ret == DDI_SUCCESS) { 857 hdlp->ih_state = DDI_IHDL_STATE_ADDED; 858 i_ddi_intr_set_current_nenables(hdlp->ih_dip, 859 i_ddi_intr_get_current_nenables(hdlp->ih_dip) - 1); 860 } 861 862 rw_exit(&hdlp->ih_rwlock); 863 return (ret); 864 } 865 866 int 867 ddi_intr_block_enable(ddi_intr_handle_t *h_array, int count) 868 { 869 ddi_intr_handle_impl_t *hdlp; 870 int i, ret; 871 872 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_enable: h_array = %p\n", 873 (void *)h_array)); 874 875 if (h_array == NULL) 876 return (DDI_EINVAL); 877 878 for (i = 0; i < count; i++) { 879 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 880 rw_enter(&hdlp->ih_rwlock, RW_READER); 881 882 if (hdlp->ih_state != DDI_IHDL_STATE_ADDED || 883 hdlp->ih_type != DDI_INTR_TYPE_MSI || 884 !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) { 885 rw_exit(&hdlp->ih_rwlock); 886 return (DDI_EINVAL); 887 } 888 rw_exit(&hdlp->ih_rwlock); 889 } 890 891 hdlp = (ddi_intr_handle_impl_t *)h_array[0]; 892 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 893 hdlp->ih_scratch1 = count; 894 hdlp->ih_scratch2 = (void *)h_array; 895 896 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 897 DDI_INTROP_BLOCKENABLE, hdlp, NULL); 898 899 rw_exit(&hdlp->ih_rwlock); 900 901 if (ret == DDI_SUCCESS) { 902 for (i = 0; i < count; i++) { 903 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 904 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 905 hdlp->ih_state = DDI_IHDL_STATE_ENABLE; 906 rw_exit(&hdlp->ih_rwlock); 907 } 908 i_ddi_intr_set_current_nenables(hdlp->ih_dip, 1); 909 } 910 911 return (ret); 912 } 913 914 int 915 ddi_intr_block_disable(ddi_intr_handle_t *h_array, int count) 916 { 917 ddi_intr_handle_impl_t *hdlp; 918 int i, ret; 919 920 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_disable: h_array = %p\n", 921 (void *)h_array)); 922 923 if (h_array == NULL) 924 return (DDI_EINVAL); 925 926 for (i = 0; i < count; i++) { 927 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 928 rw_enter(&hdlp->ih_rwlock, RW_READER); 929 if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE || 930 hdlp->ih_type != DDI_INTR_TYPE_MSI || 931 !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) { 932 rw_exit(&hdlp->ih_rwlock); 933 return (DDI_EINVAL); 934 } 935 rw_exit(&hdlp->ih_rwlock); 936 } 937 938 hdlp = (ddi_intr_handle_impl_t *)h_array[0]; 939 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 940 hdlp->ih_scratch1 = count; 941 hdlp->ih_scratch2 = (void *)h_array; 942 943 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 944 DDI_INTROP_BLOCKDISABLE, hdlp, NULL); 945 946 rw_exit(&hdlp->ih_rwlock); 947 948 if (ret == DDI_SUCCESS) { 949 for (i = 0; i < count; i++) { 950 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 951 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 952 hdlp->ih_state = DDI_IHDL_STATE_ADDED; 953 rw_exit(&hdlp->ih_rwlock); 954 } 955 i_ddi_intr_set_current_nenables(hdlp->ih_dip, 0); 956 } 957 958 return (ret); 959 } 960 961 /* 962 * Interrupt set/clr mask handlers 963 */ 964 int 965 ddi_intr_set_mask(ddi_intr_handle_t h) 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_set_mask: hdlp = %p\n", 971 (void *)hdlp)); 972 973 if (hdlp == NULL) 974 return (DDI_EINVAL); 975 976 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 977 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) || 978 (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE))) { 979 rw_exit(&hdlp->ih_rwlock); 980 return (DDI_EINVAL); 981 } 982 983 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 984 DDI_INTROP_SETMASK, hdlp, NULL); 985 986 rw_exit(&hdlp->ih_rwlock); 987 return (ret); 988 } 989 990 int 991 ddi_intr_clr_mask(ddi_intr_handle_t h) 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_clr_mask: hdlp = %p\n", 997 (void *)hdlp)); 998 999 if (hdlp == NULL) 1000 return (DDI_EINVAL); 1001 1002 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 1003 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) || 1004 (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE))) { 1005 rw_exit(&hdlp->ih_rwlock); 1006 return (DDI_EINVAL); 1007 } 1008 1009 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 1010 DDI_INTROP_CLRMASK, hdlp, NULL); 1011 1012 rw_exit(&hdlp->ih_rwlock); 1013 return (ret); 1014 } 1015 1016 /* 1017 * Interrupt get_pending handler 1018 */ 1019 int 1020 ddi_intr_get_pending(ddi_intr_handle_t h, int *pendingp) 1021 { 1022 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 1023 int ret; 1024 1025 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pending: hdlp = %p\n", 1026 (void *)hdlp)); 1027 1028 if (hdlp == NULL) 1029 return (DDI_EINVAL); 1030 1031 rw_enter(&hdlp->ih_rwlock, RW_READER); 1032 if (!(hdlp->ih_cap & DDI_INTR_FLAG_PENDING)) { 1033 rw_exit(&hdlp->ih_rwlock); 1034 return (DDI_EINVAL); 1035 } 1036 1037 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 1038 DDI_INTROP_GETPENDING, hdlp, (void *)pendingp); 1039 1040 rw_exit(&hdlp->ih_rwlock); 1041 return (ret); 1042 } 1043 1044 /* 1045 * Set the number of interrupts requested from IRM 1046 */ 1047 int 1048 ddi_intr_set_nreq(dev_info_t *dip, int nreq) 1049 { 1050 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_nreq: dip %p, nreq %d\n", 1051 (void *)dip, nreq)); 1052 1053 if (dip == NULL) 1054 return (DDI_EINVAL); 1055 1056 return (i_ddi_irm_modify(dip, nreq)); 1057 } 1058 1059 /* 1060 * Soft interrupt handlers 1061 */ 1062 /* 1063 * Add a soft interrupt and register its handler 1064 */ 1065 /* ARGSUSED */ 1066 int 1067 ddi_intr_add_softint(dev_info_t *dip, ddi_softint_handle_t *h_p, int soft_pri, 1068 ddi_intr_handler_t handler, void *arg1) 1069 { 1070 ddi_softint_hdl_impl_t *hdlp; 1071 int ret; 1072 1073 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: dip = %p, " 1074 "softpri = 0x%x\n", (void *)dip, soft_pri)); 1075 1076 if ((dip == NULL) || (h_p == NULL) || (handler == NULL)) { 1077 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: " 1078 "invalid arguments")); 1079 1080 return (DDI_EINVAL); 1081 } 1082 1083 /* Validate input arguments */ 1084 if (soft_pri < DDI_INTR_SOFTPRI_MIN || 1085 soft_pri > DDI_INTR_SOFTPRI_MAX) { 1086 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: invalid " 1087 "soft_pri input given = %x\n", soft_pri)); 1088 return (DDI_EINVAL); 1089 } 1090 1091 hdlp = (ddi_softint_hdl_impl_t *)kmem_zalloc( 1092 sizeof (ddi_softint_hdl_impl_t), KM_SLEEP); 1093 1094 /* fill up internally */ 1095 rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL); 1096 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 1097 hdlp->ih_pri = soft_pri; 1098 hdlp->ih_dip = dip; 1099 hdlp->ih_cb_func = handler; 1100 hdlp->ih_cb_arg1 = arg1; 1101 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: hdlp = %p\n", 1102 (void *)hdlp)); 1103 1104 /* do the platform specific calls */ 1105 if ((ret = i_ddi_add_softint(hdlp)) != DDI_SUCCESS) { 1106 rw_exit(&hdlp->ih_rwlock); 1107 rw_destroy(&hdlp->ih_rwlock); 1108 kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t)); 1109 return (ret); 1110 } 1111 1112 *h_p = (ddi_softint_handle_t)hdlp; 1113 rw_exit(&hdlp->ih_rwlock); 1114 return (ret); 1115 } 1116 1117 /* 1118 * Remove the soft interrupt 1119 */ 1120 int 1121 ddi_intr_remove_softint(ddi_softint_handle_t h) 1122 { 1123 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h; 1124 1125 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_softint: hdlp = %p\n", 1126 (void *)hdlp)); 1127 1128 if (hdlp == NULL) 1129 return (DDI_EINVAL); 1130 1131 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 1132 i_ddi_remove_softint(hdlp); 1133 rw_exit(&hdlp->ih_rwlock); 1134 rw_destroy(&hdlp->ih_rwlock); 1135 1136 /* kmem_free the hdl impl_t structure allocated earlier */ 1137 kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t)); 1138 return (DDI_SUCCESS); 1139 } 1140 1141 /* 1142 * Trigger a soft interrupt 1143 */ 1144 int 1145 ddi_intr_trigger_softint(ddi_softint_handle_t h, void *arg2) 1146 { 1147 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h; 1148 int ret; 1149 1150 if (hdlp == NULL) 1151 return (DDI_EINVAL); 1152 1153 if ((ret = i_ddi_trigger_softint(hdlp, arg2)) != DDI_SUCCESS) { 1154 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_trigger_softint: failed, " 1155 " ret 0%x\n", ret)); 1156 1157 return (ret); 1158 } 1159 1160 hdlp->ih_cb_arg2 = arg2; 1161 return (DDI_SUCCESS); 1162 } 1163 1164 /* 1165 * Get the soft interrupt priority 1166 */ 1167 int 1168 ddi_intr_get_softint_pri(ddi_softint_handle_t h, uint_t *soft_prip) 1169 { 1170 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h; 1171 1172 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_softint_pri: h = %p\n", 1173 (void *)h)); 1174 1175 if (hdlp == NULL) 1176 return (DDI_EINVAL); 1177 1178 rw_enter(&hdlp->ih_rwlock, RW_READER); 1179 *soft_prip = hdlp->ih_pri; 1180 rw_exit(&hdlp->ih_rwlock); 1181 return (DDI_SUCCESS); 1182 } 1183 1184 /* 1185 * Set the soft interrupt priority 1186 */ 1187 int 1188 ddi_intr_set_softint_pri(ddi_softint_handle_t h, uint_t soft_pri) 1189 { 1190 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h; 1191 int ret; 1192 uint_t orig_soft_pri; 1193 1194 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: h = %p\n", 1195 (void *)h)); 1196 1197 if (hdlp == NULL) 1198 return (DDI_EINVAL); 1199 1200 /* Validate priority argument */ 1201 if (soft_pri < DDI_INTR_SOFTPRI_MIN || 1202 soft_pri > DDI_INTR_SOFTPRI_MAX) { 1203 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: invalid " 1204 "soft_pri input given = %x\n", soft_pri)); 1205 return (DDI_EINVAL); 1206 } 1207 1208 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 1209 orig_soft_pri = hdlp->ih_pri; 1210 hdlp->ih_pri = soft_pri; 1211 1212 if ((ret = i_ddi_set_softint_pri(hdlp, orig_soft_pri)) != DDI_SUCCESS) { 1213 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: failed, " 1214 " ret 0%x\n", ret)); 1215 hdlp->ih_pri = orig_soft_pri; 1216 } 1217 1218 rw_exit(&hdlp->ih_rwlock); 1219 return (ret); 1220 } 1221 1222 /* 1223 * Old DDI interrupt framework 1224 * 1225 * The following DDI interrupt interfaces are obsolete. 1226 * Use the above new DDI interrupt interfaces instead. 1227 */ 1228 1229 int 1230 ddi_intr_hilevel(dev_info_t *dip, uint_t inumber) 1231 { 1232 ddi_intr_handle_t hdl; 1233 ddi_intr_handle_t *hdl_p; 1234 size_t hdl_sz = 0; 1235 int actual, ret; 1236 uint_t high_pri, pri; 1237 1238 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: name=%s%d dip=0x%p " 1239 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1240 (void *)dip, inumber)); 1241 1242 /* 1243 * The device driver may have already registed with the 1244 * framework. If so, first try to get the existing interrupt handle 1245 * for that given inumber and use that handle. 1246 */ 1247 if ((hdl = i_ddi_get_intr_handle(dip, inumber)) == NULL) { 1248 hdl_sz = sizeof (ddi_intr_handle_t) * (inumber + 1); 1249 hdl_p = kmem_zalloc(hdl_sz, KM_SLEEP); 1250 if ((ret = ddi_intr_alloc(dip, hdl_p, DDI_INTR_TYPE_FIXED, 1251 inumber, 1, &actual, 1252 DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) { 1253 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: " 1254 "ddi_intr_alloc failed, ret 0x%x\n", ret)); 1255 kmem_free(hdl_p, hdl_sz); 1256 return (0); 1257 } 1258 hdl = hdl_p[inumber]; 1259 } 1260 1261 if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) { 1262 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: " 1263 "ddi_intr_get_pri failed, ret 0x%x\n", ret)); 1264 (void) ddi_intr_free(hdl); 1265 if (hdl_sz) 1266 kmem_free(hdl_p, hdl_sz); 1267 return (0); 1268 } 1269 1270 high_pri = ddi_intr_get_hilevel_pri(); 1271 1272 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: pri = %x, " 1273 "high_pri = %x\n", pri, high_pri)); 1274 1275 /* Free the handle allocated here only if no existing handle exists */ 1276 if (hdl_sz) { 1277 (void) ddi_intr_free(hdl); 1278 kmem_free(hdl_p, hdl_sz); 1279 } 1280 1281 return (pri >= high_pri); 1282 } 1283 1284 int 1285 ddi_dev_nintrs(dev_info_t *dip, int *result) 1286 { 1287 DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: name=%s%d dip=0x%p\n", 1288 ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip)); 1289 1290 if (ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, 1291 result) != DDI_SUCCESS) { 1292 DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: " 1293 "ddi_intr_get_nintrs failed\n")); 1294 *result = 0; 1295 } 1296 1297 return (DDI_SUCCESS); 1298 } 1299 1300 int 1301 ddi_get_iblock_cookie(dev_info_t *dip, uint_t inumber, 1302 ddi_iblock_cookie_t *iblock_cookiep) 1303 { 1304 ddi_intr_handle_t hdl; 1305 ddi_intr_handle_t *hdl_p; 1306 size_t hdl_sz = 0; 1307 int actual, ret; 1308 uint_t pri; 1309 1310 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: name=%s%d dip=0x%p " 1311 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1312 (void *)dip, inumber)); 1313 1314 ASSERT(iblock_cookiep != NULL); 1315 1316 /* 1317 * The device driver may have already registed with the 1318 * framework. If so, first try to get the existing interrupt handle 1319 * for that given inumber and use that handle. 1320 */ 1321 if ((hdl = i_ddi_get_intr_handle(dip, inumber)) == NULL) { 1322 hdl_sz = sizeof (ddi_intr_handle_t) * (inumber + 1); 1323 hdl_p = kmem_zalloc(hdl_sz, KM_SLEEP); 1324 if ((ret = ddi_intr_alloc(dip, hdl_p, 1325 DDI_INTR_TYPE_FIXED, inumber, 1, &actual, 1326 DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) { 1327 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: " 1328 "ddi_intr_alloc failed, ret 0x%x\n", ret)); 1329 kmem_free(hdl_p, hdl_sz); 1330 return (DDI_INTR_NOTFOUND); 1331 } 1332 hdl = hdl_p[inumber]; 1333 } 1334 1335 if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) { 1336 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: " 1337 "ddi_intr_get_pri failed, ret 0x%x\n", ret)); 1338 (void) ddi_intr_free(hdl); 1339 if (hdl_sz) 1340 kmem_free(hdl_p, hdl_sz); 1341 return (DDI_FAILURE); 1342 } 1343 1344 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri; 1345 /* Free the handle allocated here only if no existing handle exists */ 1346 if (hdl_sz) { 1347 (void) ddi_intr_free(hdl); 1348 kmem_free(hdl_p, hdl_sz); 1349 } 1350 1351 return (DDI_SUCCESS); 1352 } 1353 1354 int 1355 ddi_add_intr(dev_info_t *dip, uint_t inumber, 1356 ddi_iblock_cookie_t *iblock_cookiep, 1357 ddi_idevice_cookie_t *idevice_cookiep, 1358 uint_t (*int_handler)(caddr_t int_handler_arg), 1359 caddr_t int_handler_arg) 1360 { 1361 ddi_intr_handle_t *hdl_p; 1362 size_t hdl_sz; 1363 int actual, ret; 1364 uint_t pri; 1365 1366 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: name=%s%d dip=0x%p " 1367 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1368 (void *)dip, inumber)); 1369 1370 hdl_sz = sizeof (ddi_intr_handle_t) * (inumber + 1); 1371 hdl_p = kmem_zalloc(hdl_sz, KM_SLEEP); 1372 1373 if ((ret = ddi_intr_alloc(dip, hdl_p, DDI_INTR_TYPE_FIXED, 1374 inumber, 1, &actual, DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) { 1375 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: " 1376 "ddi_intr_alloc failed, ret 0x%x\n", ret)); 1377 kmem_free(hdl_p, hdl_sz); 1378 return (DDI_INTR_NOTFOUND); 1379 } 1380 1381 if ((ret = ddi_intr_get_pri(hdl_p[inumber], &pri)) != DDI_SUCCESS) { 1382 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: " 1383 "ddi_intr_get_pri failed, ret 0x%x\n", ret)); 1384 (void) ddi_intr_free(hdl_p[inumber]); 1385 kmem_free(hdl_p, hdl_sz); 1386 return (DDI_FAILURE); 1387 } 1388 1389 if ((ret = ddi_intr_add_handler(hdl_p[inumber], (ddi_intr_handler_t *) 1390 int_handler, int_handler_arg, NULL)) != DDI_SUCCESS) { 1391 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: " 1392 "ddi_intr_add_handler failed, ret 0x%x\n", ret)); 1393 (void) ddi_intr_free(hdl_p[inumber]); 1394 kmem_free(hdl_p, hdl_sz); 1395 return (DDI_FAILURE); 1396 } 1397 1398 if ((ret = ddi_intr_enable(hdl_p[inumber])) != DDI_SUCCESS) { 1399 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: " 1400 "ddi_intr_enable failed, ret 0x%x\n", ret)); 1401 (void) ddi_intr_remove_handler(hdl_p[inumber]); 1402 (void) ddi_intr_free(hdl_p[inumber]); 1403 kmem_free(hdl_p, hdl_sz); 1404 return (DDI_FAILURE); 1405 } 1406 1407 if (iblock_cookiep) 1408 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri; 1409 1410 if (idevice_cookiep) { 1411 idevice_cookiep->idev_vector = 0; 1412 idevice_cookiep->idev_priority = pri; 1413 } 1414 1415 kmem_free(hdl_p, hdl_sz); 1416 1417 return (DDI_SUCCESS); 1418 } 1419 1420 /* ARGSUSED */ 1421 int 1422 ddi_add_fastintr(dev_info_t *dip, uint_t inumber, 1423 ddi_iblock_cookie_t *iblock_cookiep, 1424 ddi_idevice_cookie_t *idevice_cookiep, 1425 uint_t (*hi_int_handler)(void)) 1426 { 1427 DDI_INTR_APIDBG((CE_CONT, "ddi_add_fastintr: name=%s%d dip=0x%p " 1428 "inum=0x%x: Not supported, return failure\n", ddi_driver_name(dip), 1429 ddi_get_instance(dip), (void *)dip, inumber)); 1430 1431 return (DDI_FAILURE); 1432 } 1433 1434 /* ARGSUSED */ 1435 void 1436 ddi_remove_intr(dev_info_t *dip, uint_t inum, ddi_iblock_cookie_t iblock_cookie) 1437 { 1438 ddi_intr_handle_t hdl; 1439 int ret; 1440 1441 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: name=%s%d dip=0x%p " 1442 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1443 (void *)dip, inum)); 1444 1445 if ((hdl = i_ddi_get_intr_handle(dip, inum)) == NULL) { 1446 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: no handle " 1447 "found\n")); 1448 return; 1449 } 1450 1451 if ((ret = ddi_intr_disable(hdl)) != DDI_SUCCESS) { 1452 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: " 1453 "ddi_intr_disable failed, ret 0x%x\n", ret)); 1454 return; 1455 } 1456 1457 if ((ret = ddi_intr_remove_handler(hdl)) != DDI_SUCCESS) { 1458 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: " 1459 "ddi_intr_remove_handler failed, ret 0x%x\n", ret)); 1460 return; 1461 } 1462 1463 if ((ret = ddi_intr_free(hdl)) != DDI_SUCCESS) { 1464 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: " 1465 "ddi_intr_free failed, ret 0x%x\n", ret)); 1466 return; 1467 } 1468 } 1469 1470 /* ARGSUSED */ 1471 int 1472 ddi_get_soft_iblock_cookie(dev_info_t *dip, int preference, 1473 ddi_iblock_cookie_t *iblock_cookiep) 1474 { 1475 DDI_INTR_APIDBG((CE_CONT, "ddi_get_soft_iblock_cookie: name=%s%d " 1476 "dip=0x%p pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1477 (void *)dip, preference)); 1478 1479 ASSERT(iblock_cookiep != NULL); 1480 1481 if (preference == DDI_SOFTINT_FIXED) 1482 return (DDI_FAILURE); 1483 1484 *iblock_cookiep = (ddi_iblock_cookie_t)((uintptr_t) 1485 ((preference > DDI_SOFTINT_MED) ? DDI_SOFT_INTR_PRI_H : 1486 DDI_SOFT_INTR_PRI_M)); 1487 1488 return (DDI_SUCCESS); 1489 } 1490 1491 int 1492 ddi_add_softintr(dev_info_t *dip, int preference, ddi_softintr_t *idp, 1493 ddi_iblock_cookie_t *iblock_cookiep, 1494 ddi_idevice_cookie_t *idevice_cookiep, 1495 uint_t (*int_handler)(caddr_t int_handler_arg), 1496 caddr_t int_handler_arg) 1497 { 1498 ddi_softint_handle_t *hdl_p; 1499 uint64_t softpri; 1500 int ret; 1501 1502 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: name=%s%d dip=0x%p " 1503 "pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip), 1504 (void *)dip, preference)); 1505 1506 if ((idp == NULL) || ((preference == DDI_SOFTINT_FIXED) && 1507 (iblock_cookiep == NULL))) 1508 return (DDI_FAILURE); 1509 1510 /* Translate the priority preference */ 1511 if (preference == DDI_SOFTINT_FIXED) { 1512 softpri = (uint64_t)(uintptr_t)*iblock_cookiep; 1513 softpri = MIN(softpri, DDI_SOFT_INTR_PRI_H); 1514 } else { 1515 softpri = (uint64_t)((preference > DDI_SOFTINT_MED) ? 1516 DDI_SOFT_INTR_PRI_H : DDI_SOFT_INTR_PRI_M); 1517 } 1518 1519 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: preference 0x%x " 1520 "softpri 0x%lx\n", preference, (long)softpri)); 1521 1522 hdl_p = kmem_zalloc(sizeof (ddi_softint_handle_t), KM_SLEEP); 1523 if ((ret = ddi_intr_add_softint(dip, hdl_p, softpri, 1524 (ddi_intr_handler_t *)int_handler, int_handler_arg)) != 1525 DDI_SUCCESS) { 1526 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: " 1527 "ddi_intr_add_softint failed, ret 0x%x\n", ret)); 1528 1529 kmem_free(hdl_p, sizeof (ddi_softint_handle_t)); 1530 return (DDI_FAILURE); 1531 } 1532 1533 if (iblock_cookiep) 1534 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)softpri; 1535 1536 if (idevice_cookiep) { 1537 idevice_cookiep->idev_vector = 0; 1538 idevice_cookiep->idev_priority = softpri; 1539 } 1540 1541 *idp = (ddi_softintr_t)hdl_p; 1542 1543 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: dip = 0x%p, " 1544 "idp = 0x%p, ret = %x\n", (void *)dip, (void *)*idp, ret)); 1545 1546 return (DDI_SUCCESS); 1547 } 1548 1549 void 1550 ddi_remove_softintr(ddi_softintr_t id) 1551 { 1552 ddi_softint_handle_t *h_p = (ddi_softint_handle_t *)id; 1553 1554 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: id=0x%p\n", 1555 (void *)id)); 1556 1557 if (h_p == NULL) 1558 return; 1559 1560 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: handle 0x%p\n", 1561 (void *)h_p)); 1562 1563 (void) ddi_intr_remove_softint(*h_p); 1564 kmem_free(h_p, sizeof (ddi_softint_handle_t)); 1565 } 1566 1567 void 1568 ddi_trigger_softintr(ddi_softintr_t id) 1569 { 1570 ddi_softint_handle_t *h_p = (ddi_softint_handle_t *)id; 1571 int ret; 1572 1573 if (h_p == NULL) 1574 return; 1575 1576 if ((ret = ddi_intr_trigger_softint(*h_p, NULL)) != DDI_SUCCESS) { 1577 DDI_INTR_APIDBG((CE_CONT, "ddi_trigger_softintr: " 1578 "ddi_intr_trigger_softint failed, hdlp 0x%p " 1579 "ret 0x%x\n", (void *)h_p, ret)); 1580 } 1581 } 1582