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