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 }