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