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 /*
  23  * Copyright 2008-2013 Solarflare Communications Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/ddi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/atomic.h>
  31 #include <sys/modctl.h>
  32 #include <sys/conf.h>
  33 #include <sys/ethernet.h>
  34 #include <sys/pci.h>
  35 #include <sys/pcie.h>
  36 
  37 #include "sfxge.h"
  38 
  39 #include "efx.h"
  40 
  41 
  42 /* Interrupt table DMA attributes */
  43 static ddi_device_acc_attr_t sfxge_intr_devacc = {
  44 
  45         DDI_DEVICE_ATTR_V0,     /* devacc_attr_version */
  46         DDI_NEVERSWAP_ACC,      /* devacc_attr_endian_flags */
  47         DDI_STRICTORDER_ACC     /* devacc_attr_dataorder */
  48 };
  49 
  50 static ddi_dma_attr_t sfxge_intr_dma_attr = {
  51         DMA_ATTR_V0,            /* dma_attr_version     */
  52         0,                      /* dma_attr_addr_lo     */
  53         0xffffffffffffffffull,  /* dma_attr_addr_hi     */
  54         0xffffffffffffffffull,  /* dma_attr_count_max   */
  55         EFX_INTR_SIZE,          /* dma_attr_align       */
  56         0xffffffff,             /* dma_attr_burstsizes  */
  57         1,                      /* dma_attr_minxfer     */
  58         0xffffffffffffffffull,  /* dma_attr_maxxfer     */
  59         0xffffffffffffffffull,  /* dma_attr_seg         */
  60         1,                      /* dma_attr_sgllen      */
  61         1,                      /* dma_attr_granular    */
  62         0                       /* dma_attr_flags       */
  63 };
  64 
  65 static unsigned int
  66 sfxge_intr_line(caddr_t arg1, caddr_t arg2)
  67 {
  68         sfxge_t *sp = (void *)arg1;
  69         efx_nic_t *enp = sp->s_enp;
  70         sfxge_intr_t *sip = &(sp->s_intr);
  71         unsigned int index;
  72         boolean_t fatal;
  73         uint32_t qmask;
  74         int rc;
  75 
  76         _NOTE(ARGUNUSED(arg2))
  77 
  78         ASSERT3U(sip->si_type, ==, EFX_INTR_LINE);
  79 
  80         if (sip->si_state != SFXGE_INTR_STARTED &&
  81             sip->si_state != SFXGE_INTR_TESTING) {
  82                 rc = DDI_INTR_UNCLAIMED;
  83                 goto done;
  84         }
  85 
  86         if (sip->si_state == SFXGE_INTR_TESTING) {
  87                 sip->si_mask |= 1;   /* only one interrupt */
  88                 rc = DDI_INTR_CLAIMED;
  89                 goto done;
  90         }
  91 
  92         efx_intr_status_line(enp, &fatal, &qmask);
  93 
  94         if (fatal) {
  95                 sfxge_intr_fatal(sp);
  96 
  97                 rc = DDI_INTR_CLAIMED;
  98                 goto done;
  99         }
 100 
 101         if (qmask != 0) {
 102                 for (index = 0; index < EFX_INTR_NEVQS; index++) {
 103                         if (qmask & (1 << index))
 104                                 (void) sfxge_ev_qpoll(sp, index);
 105                 }
 106 
 107                 sip->si_zero_count = 0;
 108                 sfxge_gld_rx_push(sp);
 109                 rc = DDI_INTR_CLAIMED;
 110                 goto done;
 111         }
 112 
 113         /*
 114          * bug15671/bug17203 workaround. Return CLAIMED for the first ISR=0
 115          * interrupt, and poll all evqs for work. For subsequent ISR=0
 116          * interrupts (the line must be shared in this case), just rearm the
 117          * event queues to ensure we don't miss an interrupt.
 118          */
 119         if (sip->si_zero_count++ == 0) {
 120                 for (index = 0; index < EFX_INTR_NEVQS; index++) {
 121                         if (sp->s_sep[index] != NULL)
 122                                 (void) sfxge_ev_qpoll(sp, index);
 123                 }
 124 
 125                 rc = DDI_INTR_CLAIMED;
 126         } else {
 127                 for (index = 0; index < EFX_INTR_NEVQS; index++) {
 128                         if (sp->s_sep[index] != NULL)
 129                                 (void) sfxge_ev_qprime(sp, index);
 130                 }
 131 
 132                 rc = DDI_INTR_UNCLAIMED;
 133         }
 134 
 135 done:
 136         return (rc);
 137 }
 138 
 139 static unsigned int
 140 sfxge_intr_message(caddr_t arg1, caddr_t arg2)
 141 {
 142         sfxge_t *sp = (void *)arg1;
 143         efx_nic_t *enp = sp->s_enp;
 144         sfxge_intr_t *sip = &(sp->s_intr);
 145         unsigned int index = (unsigned int)(uintptr_t)arg2;
 146         boolean_t fatal;
 147         int rc;
 148 
 149         ASSERT3U(sip->si_type, ==, EFX_INTR_MESSAGE);
 150 
 151         if (sip->si_state != SFXGE_INTR_STARTED &&
 152             sip->si_state != SFXGE_INTR_TESTING) {
 153                 rc = DDI_INTR_UNCLAIMED;
 154                 goto done;
 155         }
 156 
 157         if (sip->si_state == SFXGE_INTR_TESTING) {
 158                 uint64_t mask;
 159 
 160                 do {
 161                         mask = sip->si_mask;
 162                 } while (atomic_cas_64(&(sip->si_mask), mask,
 163                     mask | (1 << index)) != mask);
 164 
 165                 rc = DDI_INTR_CLAIMED;
 166                 goto done;
 167         }
 168 
 169         efx_intr_status_message(enp, index, &fatal);
 170 
 171         if (fatal) {
 172                 sfxge_intr_fatal(sp);
 173 
 174                 rc = DDI_INTR_CLAIMED;
 175                 goto done;
 176         }
 177 
 178         (void) sfxge_ev_qpoll(sp, index);
 179 
 180         sfxge_gld_rx_push(sp);
 181         rc = DDI_INTR_CLAIMED;
 182 
 183 done:
 184         return (rc);
 185 }
 186 
 187 static int
 188 sfxge_intr_bus_enable(sfxge_t *sp)
 189 {
 190         sfxge_intr_t *sip = &(sp->s_intr);
 191         ddi_intr_handler_t *handler;
 192         int add_index;
 193         int en_index;
 194         int err;
 195         int rc;
 196 
 197         /* Serialise all instances to avoid problems seen in bug31184. */
 198         mutex_enter(&sfxge_global_lock);
 199 
 200         switch (sip->si_type) {
 201         case EFX_INTR_MESSAGE:
 202                 handler = sfxge_intr_message;
 203                 break;
 204 
 205         case EFX_INTR_LINE:
 206                 handler = sfxge_intr_line;
 207                 break;
 208 
 209         default:
 210                 cmn_err(CE_WARN, SFXGE_CMN_ERR
 211                     "[%s%d] bus_enable: unknown intr type"
 212                     " (si_type=%d nalloc=%d)\n",
 213                     ddi_driver_name(sp->s_dip),
 214                     ddi_get_instance(sp->s_dip),
 215                     sip->si_type, sip->si_nalloc);
 216                 ASSERT(B_FALSE);
 217                 rc = EINVAL;
 218                 goto fail1;
 219         }
 220 
 221         /* Try to add the handlers */
 222         for (add_index = 0; add_index < sip->si_nalloc; add_index++) {
 223                 unsigned int pri;
 224 
 225                 (void) ddi_intr_get_pri(sip->si_table[add_index], &pri);
 226                 DTRACE_PROBE2(pri, unsigned int, add_index, unsigned int, pri);
 227 
 228                 err = ddi_intr_add_handler(sip->si_table[add_index], handler,
 229                     (caddr_t)sp, (caddr_t)(uintptr_t)add_index);
 230                 if (err != DDI_SUCCESS) {
 231                         cmn_err(CE_WARN, SFXGE_CMN_ERR
 232                             "[%s%d] bus_enable: ddi_intr_add_handler failed"
 233                             " err=%d (h=%p idx=%d nalloc=%d)\n",
 234                             ddi_driver_name(sp->s_dip),
 235                             ddi_get_instance(sp->s_dip),
 236                             err, sip->si_table[add_index], add_index,
 237                             sip->si_nalloc);
 238 
 239                         rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
 240                         goto fail2;
 241                 }
 242         }
 243 
 244         /* Get interrupt capabilities */
 245         err = ddi_intr_get_cap(sip->si_table[0], &(sip->si_cap));
 246         if (err != DDI_SUCCESS) {
 247                 cmn_err(CE_WARN, SFXGE_CMN_ERR
 248                     "[%s%d] bus_enable: ddi_intr_get_cap failed"
 249                     " err=%d (h=%p idx=%d nalloc=%d)\n",
 250                     ddi_driver_name(sp->s_dip),
 251                     ddi_get_instance(sp->s_dip),
 252                     err, sip->si_table[0], 0, sip->si_nalloc);
 253 
 254                 if (err == DDI_EINVAL)
 255                         rc = EINVAL;
 256                 else if (err == DDI_ENOTSUP)
 257                         rc = ENOTSUP;
 258                 else
 259                         rc = EFAULT;
 260 
 261                 goto fail3;
 262         }
 263 
 264         /* Enable interrupts at the bus  */
 265         if (sip->si_cap & DDI_INTR_FLAG_BLOCK) {
 266                 en_index = 0; /* Silence gcc */
 267                 err = ddi_intr_block_enable(sip->si_table, sip->si_nalloc);
 268                 if (err != DDI_SUCCESS) {
 269                         cmn_err(CE_WARN, SFXGE_CMN_ERR
 270                             "[%s%d] bus_enable: ddi_intr_block_enable failed"
 271                             " err=%d (table=%p nalloc=%d)\n",
 272                             ddi_driver_name(sp->s_dip),
 273                             ddi_get_instance(sp->s_dip),
 274                             err, sip->si_table, sip->si_nalloc);
 275 
 276                         rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
 277                         goto fail4;
 278                 }
 279         } else {
 280                 for (en_index = 0; en_index < sip->si_nalloc; en_index++) {
 281                         err = ddi_intr_enable(sip->si_table[en_index]);
 282                         if (err != DDI_SUCCESS) {
 283                                 cmn_err(CE_WARN, SFXGE_CMN_ERR
 284                                     "[%s%d] bus_enable: ddi_intr_enable failed"
 285                                     " err=%d (h=%p idx=%d nalloc=%d)\n",
 286                                     ddi_driver_name(sp->s_dip),
 287                                     ddi_get_instance(sp->s_dip),
 288                                     err, sip->si_table[en_index], en_index,
 289                                     sip->si_nalloc);
 290 
 291                                 rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
 292                                 goto fail4;
 293                         }
 294                 }
 295         }
 296 
 297         mutex_exit(&sfxge_global_lock);
 298         return (0);
 299 
 300 fail4:
 301         DTRACE_PROBE(fail4);
 302 
 303         /* Disable the enabled handlers */
 304         if (!(sip->si_cap & DDI_INTR_FLAG_BLOCK)) {
 305                 while (--en_index >= 0) {
 306                         err = ddi_intr_disable(sip->si_table[en_index]);
 307                         if (err != DDI_SUCCESS) {
 308                                 cmn_err(CE_WARN, SFXGE_CMN_ERR
 309                                     "[%s%d] bus_enable: ddi_intr_disable"
 310                                     " failed err=%d (h=%p idx=%d nalloc=%d)\n",
 311                                     ddi_driver_name(sp->s_dip),
 312                                     ddi_get_instance(sp->s_dip),
 313                                     err, sip->si_table[en_index], en_index,
 314                                     sip->si_nalloc);
 315                         }
 316                 }
 317         }
 318 
 319 fail3:
 320         DTRACE_PROBE(fail3);
 321 
 322         /* Remove all handlers */
 323         add_index = sip->si_nalloc;
 324 
 325 fail2:
 326         DTRACE_PROBE(fail2);
 327 
 328         /* Remove remaining handlers */
 329         while (--add_index >= 0) {
 330                 err = ddi_intr_remove_handler(sip->si_table[add_index]);
 331                 if (err != DDI_SUCCESS) {
 332                         cmn_err(CE_WARN, SFXGE_CMN_ERR
 333                             "[%s%d] bus_enable: ddi_intr_remove_handler"
 334                             " failed err=%d (h=%p idx=%d nalloc=%d)\n",
 335                             ddi_driver_name(sp->s_dip),
 336                             ddi_get_instance(sp->s_dip),
 337                             err, sip->si_table[add_index], add_index,
 338                             sip->si_nalloc);
 339                 }
 340         }
 341 
 342 fail1:
 343         DTRACE_PROBE1(fail1, int, rc);
 344 
 345         mutex_exit(&sfxge_global_lock);
 346         return (rc);
 347 }
 348 
 349 static void
 350 sfxge_intr_bus_disable(sfxge_t *sp)
 351 {
 352         sfxge_intr_t *sip = &(sp->s_intr);
 353         int index;
 354         int err;
 355 
 356         /* Serialise all instances to avoid problems seen in bug31184. */
 357         mutex_enter(&sfxge_global_lock);
 358 
 359         /* Disable interrupts at the bus */
 360         if (sip->si_cap & DDI_INTR_FLAG_BLOCK) {
 361                 err = ddi_intr_block_disable(sip->si_table, sip->si_nalloc);
 362                 if (err != DDI_SUCCESS) {
 363                         cmn_err(CE_WARN, SFXGE_CMN_ERR
 364                             "[%s%d] bus_disable: ddi_intr_block_disable"
 365                             " failed err=%d (table=%p nalloc=%d)\n",
 366                             ddi_driver_name(sp->s_dip),
 367                             ddi_get_instance(sp->s_dip),
 368                             err, sip->si_table, sip->si_nalloc);
 369                 }
 370         } else {
 371                 index = sip->si_nalloc;
 372                 while (--index >= 0) {
 373                         err = ddi_intr_disable(sip->si_table[index]);
 374                         if (err != DDI_SUCCESS) {
 375                                 cmn_err(CE_WARN, SFXGE_CMN_ERR
 376                                     "[%s%d] bus_disable: ddi_intr_disable"
 377                                     " failed err=%d (h=%p idx=%d nalloc=%d)\n",
 378                                     ddi_driver_name(sp->s_dip),
 379                                     ddi_get_instance(sp->s_dip),
 380                                     err, sip->si_table[index], index,
 381                                     sip->si_nalloc);
 382                         }
 383                 }
 384         }
 385 
 386         sip->si_cap = 0;
 387 
 388         /* Remove all handlers */
 389         index = sip->si_nalloc;
 390         while (--index >= 0) {
 391                 err = ddi_intr_remove_handler(sip->si_table[index]);
 392                 if (err != DDI_SUCCESS) {
 393                         cmn_err(CE_WARN, SFXGE_CMN_ERR
 394                             "[%s%d] bus_disable: ddi_intr_remove_handler"
 395                             " failed err=%d (h=%p idx=%d nalloc=%d)\n",
 396                             ddi_driver_name(sp->s_dip),
 397                             ddi_get_instance(sp->s_dip),
 398                             err, sip->si_table[index], index,
 399                             sip->si_nalloc);
 400                 }
 401         }
 402 
 403         mutex_exit(&sfxge_global_lock);
 404 }
 405 
 406 static int
 407 sfxge_intr_nic_enable(sfxge_t *sp)
 408 {
 409         sfxge_intr_t *sip = &(sp->s_intr);
 410         efsys_mem_t *esmp = &(sip->si_mem);
 411         efx_nic_t *enp = sp->s_enp;
 412         unsigned int index;
 413         uint64_t mask;
 414         unsigned int count;
 415         int rc;
 416 
 417         /* Zero the memory */
 418         (void) memset(esmp->esm_base, 0, EFX_INTR_SIZE);
 419 
 420         /* Enable interrupts at the NIC */
 421         if ((rc = efx_intr_init(enp, sip->si_type, esmp)) != 0)
 422                 goto fail1;
 423 
 424         efx_intr_enable(enp);
 425 
 426         /* Test the interrupts */
 427         mask = 0;
 428         for (index = 0; index < sip->si_nalloc; index++) {
 429                 mask |= (1 << index);
 430 
 431                 rc = efx_intr_trigger(enp, index);
 432                 ASSERT3U(rc, ==, 0);
 433         }
 434 
 435         /* Wait for the tests to complete */
 436         count = 0;
 437         do {
 438                 DTRACE_PROBE1(wait, unsigned int, count);
 439 
 440                 /* Spin for 1 ms */
 441                 drv_usecwait(1000);
 442 
 443                 /*
 444                  * Check to see that all the test interrupts have been
 445                  * processed.
 446                  */
 447                 if ((mask & sip->si_mask) == mask)
 448                         goto done;
 449 
 450         } while (++count < 20);
 451 
 452         rc = ETIMEDOUT;
 453         goto fail2;
 454 
 455 done:
 456         return (0);
 457 
 458 fail2:
 459         DTRACE_PROBE(fail2);
 460 
 461         cmn_err(CE_WARN, SFXGE_CMN_ERR
 462             "[%s%d] Interrupt test failed (mask=%"PRIx64" got=%"
 463             PRIx64"). NIC is disabled\n",
 464             ddi_driver_name(sp->s_dip), ddi_get_instance(sp->s_dip),
 465             mask, sip->si_mask);
 466 
 467         DTRACE_PROBE2(int_test_fail, uint64_t, mask, uint64_t, sip->si_mask);
 468 
 469         sip->si_mask = 0;
 470 
 471         /* Disable interrupts at the NIC */
 472         efx_intr_disable(enp);
 473         efx_intr_fini(enp);
 474 
 475 fail1:
 476         DTRACE_PROBE1(fail1, int, rc);
 477 
 478         return (rc);
 479 }
 480 
 481 static void
 482 sfxge_intr_nic_disable(sfxge_t *sp)
 483 {
 484         sfxge_intr_t *sip = &(sp->s_intr);
 485         efx_nic_t *enp = sp->s_enp;
 486 
 487         sip->si_mask = 0;
 488 
 489         /* Disable interrupts at the NIC */
 490         efx_intr_disable(enp);
 491         efx_intr_fini(enp);
 492 }
 493 
 494 inline unsigned pow2_le(unsigned long n) {
 495         unsigned int order = 1;
 496         ASSERT3U(n, >, 0);
 497         while ((1ul << order) <= n) ++order;
 498         return (1ul << (order - 1));
 499 }
 500 
 501 int
 502 sfxge_intr_init(sfxge_t *sp)
 503 {
 504         dev_info_t *dip = sp->s_dip;
 505         sfxge_intr_t *sip = &(sp->s_intr);
 506         efsys_mem_t *esmp = &(sip->si_mem);
 507         sfxge_dma_buffer_attr_t dma_attr;
 508         int err;
 509         int rc;
 510         int types;
 511         int type;
 512         int index;
 513         unsigned int nalloc;
 514         int navail;
 515 
 516         SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
 517 
 518         ASSERT3U(sip->si_state, ==, SFXGE_INTR_UNINITIALIZED);
 519 
 520 #ifdef __sparc
 521         /* PSARC 2007/453 */
 522         (void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
 523             "#msix-request", NULL, 0);
 524 #endif
 525 
 526         /* Get the map of supported interrupt types */
 527         err = ddi_intr_get_supported_types(dip, &types);
 528         if (err != DDI_SUCCESS) {
 529                 cmn_err(CE_WARN, SFXGE_CMN_ERR
 530                     "[%s%d] intr_init: ddi_intr_get_supported_types"
 531                     " failed err=%d\n",
 532                     ddi_driver_name(sp->s_dip),
 533                     ddi_get_instance(sp->s_dip),
 534                     err);
 535 
 536                 if (err == DDI_EINVAL)
 537                         rc = EINVAL;
 538                 else if (err == DDI_INTR_NOTFOUND)
 539                         rc = ENOENT;
 540                 else
 541                         rc = EFAULT;
 542 
 543                 goto fail1;
 544         }
 545 
 546         /* Choose most favourable type */
 547         if (types & DDI_INTR_TYPE_MSIX) {
 548                 DTRACE_PROBE(msix);
 549 
 550                 type = DDI_INTR_TYPE_MSIX;
 551                 sip->si_type = EFX_INTR_MESSAGE;
 552         } else {
 553                 DTRACE_PROBE(fixed);
 554 
 555                 ASSERT(types & DDI_INTR_TYPE_FIXED);
 556 
 557                 type = DDI_INTR_TYPE_FIXED;
 558                 sip->si_type = EFX_INTR_LINE;
 559         }
 560 
 561         /* Get the number of available interrupts */
 562         navail = 0;
 563         err = ddi_intr_get_navail(dip, type, &navail);
 564         if (err != DDI_SUCCESS) {
 565                 cmn_err(CE_WARN, SFXGE_CMN_ERR
 566                     "[%s%d] intr_init: ddi_intr_get_navail failed err=%d\n",
 567                     ddi_driver_name(sp->s_dip), ddi_get_instance(sp->s_dip),
 568                     err);
 569 
 570                 if (err == DDI_EINVAL)
 571                         rc = EINVAL;
 572                 else if (err == DDI_INTR_NOTFOUND)
 573                         rc = ENOENT;
 574                 else
 575                         rc = EFAULT;
 576 
 577                 goto fail2;
 578         }
 579 
 580         /* Double-check */
 581         if (navail == 0) {
 582                 rc = ENOENT;
 583                 goto fail2;
 584         }
 585 
 586         /*
 587          * Allow greater number of MSI-X interrupts than CPUs.
 588          * This can be useful to prevent RX no desc drops; See task 32179.
 589          * Limit non MSI-X interrupts to a single instance.
 590          */
 591         if (type != DDI_INTR_TYPE_MSIX)
 592                 navail = 1;
 593         else
 594                 navail = min(navail, sfxge_rx_scale_prop_get(sp));
 595 
 596         DTRACE_PROBE1(navail, unsigned int, navail);
 597 
 598         /* Allocate a handle table */
 599         sip->si_table_size = navail * sizeof (ddi_intr_handle_t);
 600         sip->si_table = kmem_zalloc(sip->si_table_size, KM_SLEEP);
 601 
 602         /*
 603          * Allocate interrupt handles.
 604          * Serialise all device instances to avoid problems seen in bug31184.
 605          */
 606         mutex_enter(&sfxge_global_lock);
 607 
 608         err = ddi_intr_alloc(dip, sip->si_table, type, 0,
 609             navail, &(sip->si_nalloc), DDI_INTR_ALLOC_NORMAL);
 610 
 611         mutex_exit(&sfxge_global_lock);
 612 
 613         if (err != DDI_SUCCESS) {
 614                 cmn_err(CE_WARN, SFXGE_CMN_ERR
 615                     "[%s%d] intr_init: ddi_intr_alloc failed err=%d"
 616                     " (navail=%d nalloc=%d)\n",
 617                     ddi_driver_name(sp->s_dip), ddi_get_instance(sp->s_dip),
 618                     err, navail, sip->si_nalloc);
 619 
 620                 if (err == DDI_EINVAL)
 621                         rc = EINVAL;
 622                 else if (err == DDI_EAGAIN)
 623                         rc = EAGAIN;
 624                 else if (err == DDI_INTR_NOTFOUND)
 625                         rc = ENOENT;
 626                 else
 627                         rc = EFAULT;
 628 
 629                 goto fail3;
 630         }
 631 
 632         /* Double-check */
 633         if (sip->si_nalloc == 0) {
 634                 rc = ENOENT;
 635                 goto fail3;
 636         }
 637 
 638         /* Round down to a power of 2 */
 639         nalloc = pow2_le(sip->si_nalloc);
 640 
 641         /* Free off any excess handles */
 642         mutex_enter(&sfxge_global_lock);
 643 
 644         index = sip->si_nalloc;
 645         while (--index >= nalloc) {
 646                 (void) ddi_intr_free(sip->si_table[index]);
 647                 sip->si_table[index] = NULL;
 648         }
 649 
 650         mutex_exit(&sfxge_global_lock);
 651 
 652         sip->si_nalloc = nalloc;
 653         DTRACE_PROBE1(nalloc, unsigned int, sip->si_nalloc);
 654 
 655         dma_attr.sdba_dip        = sp->s_dip;
 656         dma_attr.sdba_dattrp     = &sfxge_intr_dma_attr;
 657         dma_attr.sdba_callback   = DDI_DMA_SLEEP;
 658         dma_attr.sdba_length     = EFX_INTR_SIZE;
 659         dma_attr.sdba_memflags   = DDI_DMA_CONSISTENT;
 660         dma_attr.sdba_devaccp    = &sfxge_intr_devacc;
 661         dma_attr.sdba_bindflags  = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
 662         dma_attr.sdba_maxcookies = 1;
 663         dma_attr.sdba_zeroinit   = B_TRUE;
 664 
 665         if ((rc = sfxge_dma_buffer_create(esmp, &dma_attr)) != 0)
 666                 goto fail4;
 667 
 668         /* Store the highest priority for convenience */
 669         sip->si_intr_pri = 0;
 670         for (index = 0; index < sip->si_nalloc; index++) {
 671                 int pri;
 672                 if ((rc = ddi_intr_get_pri(sip->si_table[index], &pri)) !=  0)
 673                         goto fail5;
 674                 if (pri > sip->si_intr_pri)
 675                         sip->si_intr_pri = pri;
 676         }
 677 
 678         sip->si_state = SFXGE_INTR_INITIALIZED;
 679         return (0);
 680 
 681 fail5:
 682         DTRACE_PROBE(fail5);
 683 
 684 fail4:
 685         DTRACE_PROBE(fail4);
 686 
 687         /* Free interrupt handles */
 688         mutex_exit(&sfxge_global_lock);
 689 
 690         index = sip->si_nalloc;
 691         while (--index >= 0) {
 692                 err = ddi_intr_free(sip->si_table[index]);
 693                 if (err != DDI_SUCCESS) {
 694                         cmn_err(CE_WARN, SFXGE_CMN_ERR
 695                             "[%s%d] intr_init: ddi_intr_free failed err=%d"
 696                             " (h=%p idx=%d nalloc=%d)\n",
 697                             ddi_driver_name(sp->s_dip),
 698                             ddi_get_instance(sp->s_dip),
 699                             err, sip->si_table[index], index, sip->si_nalloc);
 700                 }
 701                 sip->si_table[index] = NULL;
 702         }
 703         sip->si_nalloc = 0;
 704 
 705         mutex_exit(&sfxge_global_lock);
 706 
 707 fail3:
 708         DTRACE_PROBE(fail3);
 709 
 710         /* Free the handle table */
 711         kmem_free(sip->si_table, sip->si_table_size);
 712         sip->si_table = NULL;
 713         sip->si_table_size = 0;
 714 
 715 fail2:
 716         DTRACE_PROBE(fail2);
 717 
 718         /* Clear the interrupt type */
 719         sip->si_type = EFX_INTR_INVALID;
 720 
 721 fail1:
 722         DTRACE_PROBE1(fail1, int, rc);
 723 
 724         SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
 725 
 726         return (rc);
 727 }
 728 
 729 int
 730 sfxge_intr_start(sfxge_t *sp)
 731 {
 732         sfxge_intr_t *sip = &(sp->s_intr);
 733         int rc;
 734 
 735         ASSERT3U(sip->si_state, ==, SFXGE_INTR_INITIALIZED);
 736 
 737         /* Enable interrupts at the bus */
 738         if ((rc = sfxge_intr_bus_enable(sp)) != 0)
 739                 goto fail1;
 740 
 741         sip->si_state = SFXGE_INTR_TESTING;
 742 
 743         /* Enable interrupts at the NIC */
 744         if ((rc = sfxge_intr_nic_enable(sp)) != 0)
 745                 goto fail2;
 746 
 747         sip->si_state = SFXGE_INTR_STARTED;
 748 
 749         return (0);
 750 
 751 fail2:
 752         DTRACE_PROBE(fail2);
 753 
 754         /* Disable interrupts at the bus */
 755         sfxge_intr_bus_disable(sp);
 756 
 757 fail1:
 758         DTRACE_PROBE1(fail1, int, rc);
 759 
 760         sip->si_state = SFXGE_INTR_INITIALIZED;
 761 
 762         return (rc);
 763 }
 764 
 765 void
 766 sfxge_intr_stop(sfxge_t *sp)
 767 {
 768         sfxge_intr_t *sip = &(sp->s_intr);
 769 
 770         ASSERT3U(sip->si_state, ==, SFXGE_INTR_STARTED);
 771 
 772         sip->si_state = SFXGE_INTR_INITIALIZED;
 773 
 774         /* Disable interrupts at the NIC */
 775         sfxge_intr_nic_disable(sp);
 776 
 777         /* Disable interrupts at the bus */
 778         sfxge_intr_bus_disable(sp);
 779 }
 780 
 781 void
 782 sfxge_intr_fini(sfxge_t *sp)
 783 {
 784         sfxge_intr_t *sip = &(sp->s_intr);
 785         efsys_mem_t *esmp = &(sip->si_mem);
 786         int index;
 787         int err;
 788 
 789         ASSERT3U(sip->si_state, ==, SFXGE_INTR_INITIALIZED);
 790 
 791         sip->si_state = SFXGE_INTR_UNINITIALIZED;
 792 
 793         /* Tear down dma setup */
 794         sfxge_dma_buffer_destroy(esmp);
 795 
 796 
 797         /* Free interrupt handles */
 798         mutex_enter(&sfxge_global_lock);
 799 
 800         index = sip->si_nalloc;
 801         while (--index >= 0) {
 802                 err = ddi_intr_free(sip->si_table[index]);
 803                 if (err != DDI_SUCCESS) {
 804                         cmn_err(CE_WARN, SFXGE_CMN_ERR
 805                             "[%s%d] intr_fini: ddi_intr_free failed err=%d"
 806                             " (h=%p idx=%d nalloc=%d)\n",
 807                             ddi_driver_name(sp->s_dip),
 808                             ddi_get_instance(sp->s_dip),
 809                             err, sip->si_table[index], index, sip->si_nalloc);
 810                 }
 811                 sip->si_table[index] = NULL;
 812         }
 813         sip->si_nalloc = 0;
 814 
 815         mutex_exit(&sfxge_global_lock);
 816 
 817         /* Free the handle table */
 818         kmem_free(sip->si_table, sip->si_table_size);
 819         sip->si_table = NULL;
 820         sip->si_table_size = 0;
 821 
 822         /* Clear the interrupt type */
 823         sip->si_type = EFX_INTR_INVALID;
 824 
 825         SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
 826 }