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 769 rw_exit(&hdlp->ih_rwlock); 770 return (ret); 771 } 772 773 int 774 ddi_intr_disable(ddi_intr_handle_t h) 775 { 776 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h; 777 int ret; 778 779 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_disable: hdlp = %p\n", 780 (void *)hdlp)); 781 782 if (hdlp == NULL) 783 return (DDI_EINVAL); 784 785 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 786 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) || 787 ((hdlp->ih_type == DDI_INTR_TYPE_MSI) && 788 (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) { 789 rw_exit(&hdlp->ih_rwlock); 790 return (DDI_EINVAL); 791 } 792 793 I_DDI_VERIFY_MSIX_HANDLE(hdlp); 794 795 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 796 DDI_INTROP_DISABLE, hdlp, NULL); 797 798 if (ret == DDI_SUCCESS) 799 hdlp->ih_state = DDI_IHDL_STATE_ADDED; 800 801 rw_exit(&hdlp->ih_rwlock); 802 return (ret); 803 } 804 805 int 806 ddi_intr_block_enable(ddi_intr_handle_t *h_array, int count) 807 { 808 ddi_intr_handle_impl_t *hdlp; 809 int i, ret; 810 811 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_enable: h_array = %p\n", 812 (void *)h_array)); 813 814 if (h_array == NULL) 815 return (DDI_EINVAL); 816 817 for (i = 0; i < count; i++) { 818 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 819 rw_enter(&hdlp->ih_rwlock, RW_READER); 820 821 if (hdlp->ih_state != DDI_IHDL_STATE_ADDED || 822 hdlp->ih_type != DDI_INTR_TYPE_MSI || 823 !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) { 824 rw_exit(&hdlp->ih_rwlock); 825 return (DDI_EINVAL); 826 } 827 rw_exit(&hdlp->ih_rwlock); 828 } 829 830 hdlp = (ddi_intr_handle_impl_t *)h_array[0]; 831 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 832 hdlp->ih_scratch1 = count; 833 hdlp->ih_scratch2 = (void *)h_array; 834 835 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 836 DDI_INTROP_BLOCKENABLE, hdlp, NULL); 837 838 rw_exit(&hdlp->ih_rwlock); 839 840 if (ret == DDI_SUCCESS) { 841 for (i = 0; i < count; i++) { 842 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 843 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 844 hdlp->ih_state = DDI_IHDL_STATE_ENABLE; 845 rw_exit(&hdlp->ih_rwlock); 846 } 847 } 848 849 return (ret); 850 } 851 852 int 853 ddi_intr_block_disable(ddi_intr_handle_t *h_array, int count) 854 { 855 ddi_intr_handle_impl_t *hdlp; 856 int i, ret; 857 858 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_disable: h_array = %p\n", 859 (void *)h_array)); 860 861 if (h_array == NULL) 862 return (DDI_EINVAL); 863 864 for (i = 0; i < count; i++) { 865 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 866 rw_enter(&hdlp->ih_rwlock, RW_READER); 867 if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE || 868 hdlp->ih_type != DDI_INTR_TYPE_MSI || 869 !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) { 870 rw_exit(&hdlp->ih_rwlock); 871 return (DDI_EINVAL); 872 } 873 rw_exit(&hdlp->ih_rwlock); 874 } 875 876 hdlp = (ddi_intr_handle_impl_t *)h_array[0]; 877 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 878 hdlp->ih_scratch1 = count; 879 hdlp->ih_scratch2 = (void *)h_array; 880 881 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip, 882 DDI_INTROP_BLOCKDISABLE, hdlp, NULL); 883 884 rw_exit(&hdlp->ih_rwlock); 885 886 if (ret == DDI_SUCCESS) { 887 for (i = 0; i < count; i++) { 888 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 889 rw_enter(&hdlp->ih_rwlock, RW_WRITER); 890 hdlp->ih_state = DDI_IHDL_STATE_ADDED; 891 rw_exit(&hdlp->ih_rwlock); 892 } 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