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