/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2014 QLogic Corporation
 * The contents of this file are subject to the terms of the
 * QLogic End User License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the License at
 * http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/
 * QLogic_End_User_Software_License.txt
 * See the License for the specific language governing permissions
 * and limitations under the License.
 */

/*
 * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
 */

#include "bnxe.h"

/*
 * The interrupt status bit vector is as follows:
 *
 *   bit 0: default interrupt
 *
 * Single Mode:
 *
 *   bits 1-16:  Function 1 (RSS 0-15)
 *
 * Multi-Function Mode:
 *
 *   bits 1-4:   Virtual Function 1 (RSS 0-3)
 *   bits 5-8:   Virtual Function 2 (RSS 4-7)
 *   bits 9-12:  Virtual Function 3 (RSS 8-11)
 *   bits 13-16: Virtual Function 4 (RSS 12-15)
 *
 * While processing interrupts the programmatic index used for the default
 * status block is 16 and the RSS status blocks are shifted down one (i.e.
 * 0-15).
 *
 * Solaris defaults to 2 MSIX interrupts per function so only the default
 * interrupt plus one RSS interrupt is used.  This default behavior can be
 * modified via the /etc/system configuration file.
 */


static inline char * BnxeIntrTypeName(int intrType)
{
    return (intrType == DDI_INTR_TYPE_MSIX)  ? "MSIX"  :
           (intrType == DDI_INTR_TYPE_MSI)   ? "MSI"   :
           (intrType == DDI_INTR_TYPE_FIXED) ? "FIXED" :
                                               "UNKNOWN";
}


static void BnxeFindDmaHandles(um_device_t * pUM)
{
    lm_address_t physAddr;
    BnxeMemDma * pTmp;
    u32_t idx;

    BNXE_LOCK_ENTER_MEM(pUM);

    /* find the RSS status blocks */

    LM_FOREACH_SB_ID(&pUM->lm_dev, idx)
    {
        if (CHIP_IS_E1x(&pUM->lm_dev))
        {
            physAddr.as_u32.low =
                pUM->lm_dev.vars.status_blocks_arr[idx].hc_status_block_data.e1x_sb_data.common.host_sb_addr.lo;
            physAddr.as_u32.high =
                pUM->lm_dev.vars.status_blocks_arr[idx].hc_status_block_data.e1x_sb_data.common.host_sb_addr.hi;
        }
        else
        {
            physAddr.as_u32.low =
                pUM->lm_dev.vars.status_blocks_arr[idx].hc_status_block_data.e2_sb_data.common.host_sb_addr.lo;
            physAddr.as_u32.high =
                pUM->lm_dev.vars.status_blocks_arr[idx].hc_status_block_data.e2_sb_data.common.host_sb_addr.hi;
        }

        pTmp = (BnxeMemDma *)d_list_peek_head(&pUM->memDmaList);
        while (pTmp)
        {
            if (pTmp->physAddr.as_ptr == physAddr.as_ptr)
            {
                break;
            }

            pTmp = (BnxeMemDma *)d_list_next_entry(&pTmp->link);
        }

        if (pTmp == NULL)
        {
            BnxeLogWarn(pUM, "Failed to find DMA handle for RSS status block %d", idx);
        }

        pUM->statusBlocks[idx] = pTmp;
    }

    /* find the default status block */

    pTmp = (BnxeMemDma *)d_list_peek_head(&pUM->memDmaList);
    while (pTmp)
    {
        if (pTmp->physAddr.as_ptr ==
            pUM->lm_dev.vars.gen_sp_status_block.blk_phy_address.as_ptr)
        {
            break;
        }

        pTmp = (BnxeMemDma *)d_list_next_entry(&pTmp->link);
    }

    if (pTmp == NULL)
    {
        BnxeLogWarn(pUM, "Failed to find DMA handle for default status block");
    }

    pUM->statusBlocks[DEF_STATUS_BLOCK_IGU_INDEX] = pTmp;

    BNXE_LOCK_EXIT_MEM(pUM);
}


void BnxeIntrIguSbEnable(um_device_t * pUM,
                         u32_t         idx,
                         boolean_t     fromISR)
{
    RxQueue * pRxQ = &pUM->rxq[idx];
    u32_t igu_id = (FCOE_CID(&pUM->lm_dev) == idx) ?
                       LM_NON_RSS_SB(&pUM->lm_dev) : idx;

    BNXE_LOCK_ENTER_INTR_FLIP(pUM, igu_id);

    if (fromISR)
    {
        /*
         * If in an ISR and poll mode is ON then poll mode was flipped in the
         * ISR which can occur during Rx processing.  If this is the case then
         * don't do anything.  Only re-enable the IGU when poll mode is OFF.
         */
        if (!pRxQ->inPollMode)
        {
            lm_int_ack_sb_enable(&pUM->lm_dev, igu_id);
        }
    }
    else
    {
        if (!pRxQ->inPollMode)
        {
            /* Why are intrs getting enabled on the ring twice...? */
            cmn_err(CE_PANIC,
                    "%s: Ring %d, enable intrs and NOT in poll mode!",
                    BnxeDevName(pUM), igu_id);
        }

        atomic_swap_32(&pRxQ->inPollMode, B_FALSE);
        pRxQ->intrEnableCnt++;

        lm_int_ack_sb_enable(&pUM->lm_dev, igu_id);
    }

    BNXE_LOCK_EXIT_INTR_FLIP(pUM, igu_id);
}


void BnxeIntrIguSbDisable(um_device_t * pUM,
                          u32_t         idx,
                          boolean_t     fromISR)
{
    RxQueue * pRxQ = &pUM->rxq[idx];
    u32_t igu_id = (FCOE_CID(&pUM->lm_dev) == idx) ?
                       LM_NON_RSS_SB(&pUM->lm_dev) : idx;

    BNXE_LOCK_ENTER_INTR_FLIP(pUM, igu_id);

    if (fromISR)
    {
        /* we should never get here when in poll mode... */
        ASSERT(pRxQ->inPollMode == B_FALSE);
        lm_int_ack_sb_disable(&pUM->lm_dev, igu_id);
    }
    else
    {
        if (pRxQ->inPollMode)
        {
            /* Why are intrs getting disabled on the ring twice...? */
            cmn_err(CE_PANIC,
                    "%s: Ring %d, disable intrs and ALREADY in poll mode!",
                    BnxeDevName(pUM), igu_id);
        }

        /*
         * Note here that the interrupt can already be disabled if GLDv3
         * is disabling the interrupt under the context of an ISR.  This is
         * OK as the inPollMode flag will tell the ISR not to re-enable the
         * interrupt upon return.
         */

        lm_int_ack_sb_disable(&pUM->lm_dev, igu_id);

        atomic_swap_32(&pRxQ->inPollMode, B_TRUE);
        pRxQ->intrDisableCnt++;
    }

    BNXE_LOCK_EXIT_INTR_FLIP(pUM, igu_id);
}


static void BnxeServiceDefSbIntr(um_device_t * pUM,
                                 boolean_t *   pPktsRxed,
                                 boolean_t *   pPktsTxed)
{
    lm_device_t * pLM = (lm_device_t *)pUM;
    u32_t         activity_flg         = 0;
    u16_t         lcl_attn_bits        = 0;
    u16_t         lcl_attn_ack         = 0;
    u16_t         asserted_proc_grps   = 0;
    u16_t         deasserted_proc_grps = 0;

    *pPktsRxed = B_FALSE;
    *pPktsTxed = B_FALSE;

    BnxeLogDbg(pUM, "Default INTR: Handling default status block %d", DEF_STATUS_BLOCK_INDEX);

    ddi_dma_sync(pUM->statusBlocks[DEF_STATUS_BLOCK_IGU_INDEX]->dmaHandle,
                 0, 0, DDI_DMA_SYNC_FORKERNEL);

    if (pUM->fmCapabilities &&
        BnxeCheckDmaHandle(pUM->statusBlocks[DEF_STATUS_BLOCK_IGU_INDEX]->dmaHandle) != DDI_FM_OK)
    {
        ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
    }

    pUM->intrSbCnt[DEF_STATUS_BLOCK_IGU_INDEX]++;

    if (lm_is_def_sb_updated(pLM) == 0)
    {
        BnxeLogDbg(pUM, "Default INTR: No change in default status index so bail!");
        pUM->intrSbNoChangeCnt[DEF_STATUS_BLOCK_IGU_INDEX]++;

        if (pUM->fmCapabilities &&
            BnxeCheckAccHandle(pLM->vars.reg_handle[BAR_0]) != DDI_FM_OK)
        {
            ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
        }

        return;
    }

    /* get a local copy of the indices from the status block */
    lm_update_def_hc_indices(pLM, DEF_STATUS_BLOCK_INDEX, &activity_flg);

    BnxeDbgBreakIfFastPath(pUM, !(activity_flg & LM_DEF_EVENT_MASK));

    BnxeLogDbg(pUM, "Default INTR: processing events on sb: %x, events: 0x%x",
               DEF_STATUS_BLOCK_INDEX, activity_flg);

    if (activity_flg & LM_DEF_ATTN_ACTIVE)
    {
        /* Attentions! Usually these are bad things we don't want to see */

        lm_get_attn_info(pLM, &lcl_attn_bits, &lcl_attn_ack);

        // NOTE: in case the status index of the attention has changed
        // already (while processing), we could override with it our local
        // copy. However, this is not a must, since it will be caught at the
        // end of the loop with the call to lm_is_sb_updated(). In case the
        // dpc_loop_cnt has exhausted, no worry, since will get an interrupt
        // for that at a later time.

        // find out which lines are asserted/deasserted with account to
        // their states, ASSERT if necessary.
        GET_ATTN_CHNG_GROUPS(pLM, lcl_attn_bits, lcl_attn_ack,
                             &asserted_proc_grps, &deasserted_proc_grps);

        BnxeLogDbg(pUM, "Default INTR: asserted_proc_grps: 0x%x, deasserted_proc_grps:0x%x",
                   asserted_proc_grps, deasserted_proc_grps);

        if (asserted_proc_grps)
        {
            lm_handle_assertion_processing(pLM, asserted_proc_grps);

            if (pUM->fmCapabilities &&
                BnxeCheckAccHandle(pLM->vars.reg_handle[BAR_0]) != DDI_FM_OK)
            {
                ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
            }
        }

        // keep in mind that in the same round, it is possible that this
        // func will have processing to do regarding deassertion on bits
        // that are different than the ones processed earlier for assertion
        // processing.

        if (deasserted_proc_grps)
        {
            lm_handle_deassertion_processing(pLM, deasserted_proc_grps);

            if (pUM->fmCapabilities &&
                BnxeCheckAccHandle(pLM->vars.reg_handle[BAR_0]) != DDI_FM_OK)
            {
                ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
            }
        }
    }

    if (activity_flg & LM_DEF_USTORM_ACTIVE)
    {
        /* Check for L4 TOE/iSCSI/FCoE Rx completions. */

        if (lm_is_rx_completion(pLM, ISCSI_CID(pLM)))  
        {
            BnxeDbgBreakMsg(pUM, "Unknown iSCSI Rx completion!");
        }

        if (lm_is_rx_completion(pLM, FCOE_CID(pLM)))  
        {
            *pPktsRxed = B_TRUE;
        }
    }

    if (activity_flg & LM_DEF_CSTORM_ACTIVE)
    {
        if (lm_is_eq_completion(pLM))
        {
            lm_service_eq_intr(pLM);
        }

        if (lm_is_tx_completion(pLM, FWD_CID(pLM)))
        {
            /* XXX Possible? */
            *pPktsTxed = B_TRUE;
        }

        if (lm_is_tx_completion(pLM, ISCSI_CID(pLM)))
        {
            /* XXX iSCSI Tx. NO! */
            BnxeDbgBreakMsg(pUM, "Unknown iSCSI Tx completion!");
        }

        if (lm_is_tx_completion(pLM, FCOE_CID(pLM)))
        {
            *pPktsTxed = B_TRUE;
        }
    }
}


/*
 * This is the polling path for an individual Rx Ring.  Here we simply pull
 * any pending packets out of the hardware and put them into the wait queue.
 * Note that there might already be packets in the wait queue which is OK as
 * the caller will call BnxeRxRingProcess() next to process the queue.
 */
void BnxePollRxRing(um_device_t * pUM,
                    u32_t         idx,
                    boolean_t *   pPktsRxed,
                    boolean_t *   pPktsTxed)
{
    lm_device_t * pLM = (lm_device_t *)pUM;
    u32_t         activity_flg = 0;
    u8_t          drv_rss_id = (u8_t)idx;

    *pPktsRxed = B_FALSE;
    *pPktsTxed = B_FALSE;

    BnxeLogDbg(pUM, "Ring Poll: Handling status block sb_id:%d drv_rss_id:%d",
               idx, drv_rss_id);

    /* use drv_rss_id for mapping into status block array (from LM) */
    ddi_dma_sync(pUM->statusBlocks[drv_rss_id]->dmaHandle,
                 0, 0, DDI_DMA_SYNC_FORKERNEL);

    if (pUM->fmCapabilities &&
        BnxeCheckDmaHandle(pUM->statusBlocks[drv_rss_id]->dmaHandle) != DDI_FM_OK)
    {
        ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
    }

    pUM->intrSbPollCnt[drv_rss_id]++;

    if (lm_is_sb_updated(pLM, drv_rss_id) == 0)
    {
        BnxeLogDbg(pUM, "Ring Poll: No change in status index so bail!");
        pUM->intrSbPollNoChangeCnt[drv_rss_id]++;
        return;
    }

    /* get a local copy of the indices from the status block */
    lm_update_fp_hc_indices(pLM, drv_rss_id, &activity_flg, &drv_rss_id);

    BnxeDbgBreakIf(pUM, !(activity_flg & LM_NON_DEF_EVENT_MASK));

    BnxeLogDbg(pUM, "Ring Poll: processing events on sb: %x, events: 0x%x",
               drv_rss_id, activity_flg);

    if (activity_flg & LM_NON_DEF_USTORM_ACTIVE)
    {
        /* Rx Completions */
        if (lm_is_rx_completion(pLM, drv_rss_id))
        {
            *pPktsRxed = B_TRUE;
        }

        /* XXX Check for L4 TOE/FCoE Rx completions. NO! */
    }

    if (activity_flg & LM_NON_DEF_CSTORM_ACTIVE)
    {
        /* Tx completions */
        if (lm_is_tx_completion(pLM, drv_rss_id))
        {
            *pPktsTxed = B_TRUE;
        }

        /* XXX Check for L4 Tx and L5 EQ completions. NO! */
    }
}


/*
 * This is the polling path for the FCoE Ring.  Here we don't pull any
 * pending packets out of the hardware.  We only care about FCoE Fast Path
 * completions.  FCoE slow path L2 packets are processed via the default
 * status block not the LM_NON_RSS_SB.  In this path we're assuming that
 * the FCoE driver is performing a crashdump.
 */
void BnxePollRxRingFCOE(um_device_t * pUM)
{
    lm_device_t * pLM = (lm_device_t *)pUM;
    u32_t         activity_flg = 0;

    u8_t sb_id = LM_NON_RSS_SB(pLM);
    u8_t drv_rss_id = FCOE_CID(pLM);

    BnxeLogDbg(pUM, "Ring Poll FCoE: Handling status block sb_id:%d drv_rss_id:%d",
               sb_id, drv_rss_id);

    /* use sb_id for mapping into status block array (from LM) */
    ddi_dma_sync(pUM->statusBlocks[sb_id]->dmaHandle,
                 0, 0, DDI_DMA_SYNC_FORKERNEL);

    if (pUM->fmCapabilities &&
        BnxeCheckDmaHandle(pUM->statusBlocks[sb_id]->dmaHandle) != DDI_FM_OK)
    {
        ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
    }

    pUM->intrSbPollCnt[sb_id]++;

    if (lm_is_sb_updated(pLM, sb_id) == 0)
    {
        BnxeLogDbg(pUM, "Ring Poll FCoE: No change in status index so bail!");
        pUM->intrSbPollNoChangeCnt[sb_id]++;
        return;
    }

    /* get a local copy of the indices from the status block */
    lm_update_fp_hc_indices(pLM, sb_id, &activity_flg, &drv_rss_id);

    BnxeDbgBreakIf(pUM, !(activity_flg & LM_NON_DEF_EVENT_MASK));

    BnxeLogDbg(pUM, "Ring Poll FCoE: processing events on sb: %x, events: 0x%x",
               sb_id, activity_flg);

    if (activity_flg & LM_NON_DEF_USTORM_ACTIVE)
    {
        if (lm_fc_is_eq_completion(pLM, drv_rss_id))
        {
            lm_fc_service_eq_intr(pLM, drv_rss_id);
        }
    }
}


static void BnxeServiceSbIntr(um_device_t * pUM,
                              u8_t          sb_id,
                              boolean_t *   pPktsRxed,
                              boolean_t *   pPktsTxed)
{
    lm_device_t * pLM = (lm_device_t *)pUM;
    u32_t         activity_flg = 0;
    u8_t          drv_rss_id;

    *pPktsRxed = B_FALSE;
    *pPktsTxed = B_FALSE;

    drv_rss_id = lm_map_igu_sb_id_to_drv_rss(pLM, sb_id);

    BnxeLogDbg(pUM, "Ring INTR: Handling status block sb_id:%d drv_rss_id:%d",
               sb_id, drv_rss_id);

    /* use sb_id for mapping into status block array (from LM) */
    ddi_dma_sync(pUM->statusBlocks[sb_id]->dmaHandle,
                 0, 0, DDI_DMA_SYNC_FORKERNEL);

    if (pUM->fmCapabilities &&
        BnxeCheckDmaHandle(pUM->statusBlocks[sb_id]->dmaHandle) != DDI_FM_OK)
    {
        ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
    }

    pUM->intrSbCnt[sb_id]++;

    if (lm_is_sb_updated(pLM, sb_id) == 0)
    {
        BnxeLogDbg(pUM, "Ring INTR: No change in status index so bail!");
        pUM->intrSbNoChangeCnt[sb_id]++;
        return;
    }

    /*
     * get a local copy of the indices from the status block
     * XXX note that here drv_rss_id is assigned to the sb_id
     */
    lm_update_fp_hc_indices(pLM, sb_id, &activity_flg, &drv_rss_id);

    BnxeDbgBreakIf(pUM, !(activity_flg & LM_NON_DEF_EVENT_MASK));

    BnxeLogDbg(pUM, "Ring INTR: processing events on sb: %x, events: 0x%x",
               drv_rss_id, activity_flg);

    if (activity_flg & LM_NON_DEF_USTORM_ACTIVE)
    {
        /* Rx Completions */
        if (lm_is_rx_completion(pLM, drv_rss_id))
        {
            *pPktsRxed = B_TRUE;
        }

        if (lm_fc_is_eq_completion(pLM, drv_rss_id))
        {
            lm_fc_service_eq_intr(pLM, drv_rss_id);
        }

        /* XXX Check for ISCSI-OOO and L4 TOE Rx completions. NO! */
    }

    if (activity_flg & LM_NON_DEF_CSTORM_ACTIVE)
    {
        /* Tx completions */
        if (lm_is_tx_completion(pLM, drv_rss_id))
        {
            *pPktsTxed = B_TRUE;
        }

        /* XXX Check for L4 Tx and L5 EQ completions. NO! */

        /* L4 Tx completions */
        if (lm_toe_is_tx_completion(pLM, drv_rss_id))
        {
            BnxeDbgBreakMsg(pUM, "Unknown TOE Tx completion!");
        }

        /* L5 EQ completions */
        if (lm_sc_is_eq_completion(pLM, drv_rss_id))
        {
            BnxeDbgBreakMsg(pUM, "Unknown iSCSI EQ completion!");
            //lm_sc_service_eq_intr(pLM, drv_rss_id);
        }
    }
}


uint_t BnxeIntrISR(caddr_t arg1, caddr_t arg2)
{
    um_device_t *         pUM = (um_device_t *)arg1;
    lm_device_t *         pLM = &pUM->lm_dev;
    lm_interrupt_status_t intrStatus = 0;
    boolean_t             pktsRxed   = 0;
    boolean_t             pktsTxed   = 0;
    u32_t                 rss_id     = 0;
    int                   idx        = (int)(uintptr_t)arg2;

    BNXE_LOCK_ENTER_INTR(pUM, idx);

    if (!pUM->intrEnabled)
    {
        pLM->vars.dbg_intr_in_wrong_state++;

        BNXE_LOCK_EXIT_INTR(pUM, idx);
        return DDI_INTR_UNCLAIMED;
    }

    BnxeLogDbg(pUM, "-> BNXE INTA Interrupt <-");

    if (pLM->vars.enable_intr)
    {
        intrStatus = lm_get_interrupt_status(pLM);

        if (pUM->fmCapabilities &&
            BnxeCheckAccHandle(pLM->vars.reg_handle[BAR_0]) != DDI_FM_OK)
        {
            ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
        }

        if (intrStatus == 0)
        {
            pLM->vars.dbg_intr_zero_status++;

            BNXE_LOCK_EXIT_INTR(pUM, idx);
            return DDI_INTR_UNCLAIMED;
        }
    }
    else
    {
        pLM->vars.dbg_intr_in_disabled++;
        BnxeLogDbg(pUM, "INTA INTR: we got an interrupt when disabled");

        BNXE_LOCK_EXIT_INTR(pUM, idx);
        return DDI_INTR_CLAIMED;
    }

    atomic_add_64((volatile uint64_t *)&pUM->intrFired, 1);

    while (intrStatus)
    {
        if (intrStatus & 0x1)
        {
            if (rss_id == 0)
            {
                lm_int_ack_def_sb_disable(pLM);

                BnxeServiceDefSbIntr(pUM, &pktsRxed, &pktsTxed);

                /*
                 * Default sb only handles FCoE only right now.  If this changes
                 * BnxeServiceDefSbIntr will have to change to return which CIDs
                 * have packets pending.
                 */

                if (pktsTxed) BnxeTxRingProcess(pUM, FCOE_CID(pLM));
                if (pktsRxed) BnxeRxRingProcess(pUM, FCOE_CID(pLM), B_FALSE, 0);

                lm_sq_post_pending(pLM);

                lm_int_ack_def_sb_enable(pLM);
            }
            else
            {
                /*
                 * (rss_id - 1) is used because the non-default sbs are located
                 * in lm_device at indices 0-15.
                 */

                lm_int_ack_sb_disable(pLM, (rss_id - 1));

                BnxeServiceSbIntr(pUM, (rss_id - 1), &pktsRxed, &pktsTxed);

                if (pktsTxed) BnxeTxRingProcess(pUM, (rss_id - 1));
                if (pktsRxed) BnxeRxRingProcess(pUM, (rss_id - 1), B_FALSE, 0);

                lm_sq_post_pending(pLM);

                lm_int_ack_sb_enable(pLM, (rss_id - 1));
            }
        }

        intrStatus >>= 1;
        rss_id++;
    }

    BNXE_LOCK_EXIT_INTR(pUM, idx);

    return DDI_INTR_CLAIMED;
}


uint_t BnxeIntrMISR(caddr_t arg1, caddr_t arg2)
{
    um_device_t * pUM = (um_device_t *)arg1;
    lm_device_t * pLM = &pUM->lm_dev;
    boolean_t     pktsRxed = 0;
    boolean_t     pktsTxed = 0;
    int           sb_id    = (int)(uintptr_t)arg2;
    u32_t         idx;

    BNXE_LOCK_ENTER_INTR(pUM, sb_id);

    if (!pUM->intrEnabled)
    {
        pLM->vars.dbg_intr_in_wrong_state++;

        BNXE_LOCK_EXIT_INTR(pUM, sb_id);
        return DDI_INTR_UNCLAIMED;
    }

    BnxeLogDbg(pUM, "-> BNXE MSIX Interrupt SB %d <-", sb_id);

    if (!pLM->vars.enable_intr)
    {
        pLM->vars.dbg_intr_in_disabled++;
        BnxeLogDbg(pUM, "MISR INTR: we got an interrupt when disabled");

        BNXE_LOCK_EXIT_INTR(pUM, sb_id);
        return DDI_INTR_CLAIMED;
    }

    atomic_add_64((volatile uint64_t *)&pUM->intrFired, 1);

    if (sb_id == DEF_STATUS_BLOCK_IGU_INDEX)
    {
        lm_int_ack_def_sb_disable(pLM);

        BnxeServiceDefSbIntr(pUM, &pktsRxed, &pktsTxed);

        /*
         * Default sb only handles FCoE only right now.  If this changes
         * BnxeServiceDefSbIntr will have to change to return which CIDs
         * have packets pending.
         */

        if (pktsTxed) BnxeTxRingProcess(pUM, FCOE_CID(pLM));
	if (pktsRxed) BnxeRxRingProcess(pUM, FCOE_CID(pLM), FALSE, 0);

        lm_sq_post_pending(pLM);

        lm_int_ack_def_sb_enable(pLM);
    }
    else
    {
        /*
         * Note that polling is not allowed by GLDv3 on the LM_NON_RSS_SB when
         * overlapped with FCoE. This is enforced by the BnxeRxRingIntrEnable
         * and BnxeRxRingIntrDisable routines. The FCoE driver IS ALLOWED to
         * put the SB into poll mode. FCoE trumps GLDv3/L2 and it's assumed
         * the FCoE driver is performing a crashdump in this case.
         */

        idx = ((sb_id == LM_NON_RSS_SB(pLM)) &&
               CLIENT_BOUND(pUM, LM_CLI_IDX_FCOE) &&
               (pUM->rssIntr.intrCount == LM_MAX_RSS_CHAINS(&pUM->lm_dev))) ?
                   FCOE_CID(pLM) : sb_id;

        if (pUM->rxq[idx].inPollMode)
        {
            /* Shouldn't be here! */
            cmn_err(CE_PANIC,
                    "%s: Interupt on RSS/MSIX ring %d when in poll mode!",
                    BnxeDevName(pUM), idx);
        }

        /* accounts for poll mode */
        BnxeIntrIguSbDisable(pUM, idx, B_TRUE);

        BnxeServiceSbIntr(pUM, sb_id, &pktsRxed, &pktsTxed);
 
        if (pktsTxed) BnxeTxRingProcess(pUM, sb_id);
        if (pktsRxed) BnxeRxRingProcess(pUM, sb_id, B_FALSE, 0);

        lm_sq_post_pending(pLM);

        /* accounts for poll mode */
        BnxeIntrIguSbEnable(pUM, idx, B_TRUE);
    }

    BNXE_LOCK_EXIT_INTR(pUM, sb_id);

    return DDI_INTR_CLAIMED;
}


static int BnxeGetInterruptCount(dev_info_t * pDev, int type, int intrTypes)
{
    int nintrs = 0;

    if (intrTypes & type)
    {
        return (ddi_intr_get_nintrs(pDev, type, &nintrs) != DDI_SUCCESS) ?
               -1 : nintrs;
    }

    return -1;
}


static boolean_t BnxeIntrBlockAlloc(um_device_t *   pUM,
                                    int             intrInum,
                                    int             intrCnt,
                                    BnxeIntrBlock * pBlock)

{
    dev_info_t * pDev = pUM->pDev;
    int intrRequest;
    int intrActual;
    int rc, i;

    if ((pUM->intrType == DDI_INTR_TYPE_FIXED) && (intrCnt != 1))
    {
        return B_FALSE;
    }

    intrRequest = intrCnt;
    intrActual  = 0;

    /*
     * We need to allocate an interrupt block array of maximum size which is
     * MAX_RSS_CHAINS plus one for the default interrupt.  Even though we
     * won't allocate all of those handlers the "inum" value passed to
     * ddi_intr_alloc() determines the starting index where the handlers
     * will be allocated.  See the multi-function block offset documentation
     * at the top of this file.
     */
    pBlock->intrHandleBlockSize =
        ((MAX_RSS_CHAINS + 1) * sizeof(ddi_intr_handle_t));

    if ((pBlock->pIntrHandleBlockAlloc =
         (ddi_intr_handle_t *)kmem_zalloc(pBlock->intrHandleBlockSize,
                                          KM_SLEEP)) == NULL)
    {
        BnxeLogWarn(pUM, "Memory alloc failed for isr handle block (inum=%d)!",
                    intrInum);
        return B_FALSE;
    }

    if ((rc = ddi_intr_alloc(pDev,
                             pBlock->pIntrHandleBlockAlloc,
                             pUM->intrType,
                             intrInum,
                             intrRequest,
                             &intrActual,
                             DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS)
    {
        BnxeLogWarn(pUM, "Failed to allocate isr handle block (%d) (inum=%d cnt=%d)!",
                    rc, intrInum, intrRequest);
        kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize);
        return B_FALSE;
    }

    /*
     * Point 'pIntrHandleBlock' to the starting interrupt index in the
     * allocated interrupt block array.  This is done so we can easily enable,
     * disable, free, etc the interrupts.  For 10u8 and beyond the inum value
     * is also used as an index into the interrupt block so we point
     * pIntrHandleBlock to the inum'th index.  For 10u7 and below all
     * interrupt allocations start at index 0 per block.
     */
#if 0

#ifdef DDI_INTR_IRM
    pBlock->pIntrHandleBlock =
        &pBlock->pIntrHandleBlockAlloc[intrInum];
#else
    pBlock->pIntrHandleBlock =
        &pBlock->pIntrHandleBlockAlloc[0];
#endif

#else

    if (pBlock->pIntrHandleBlockAlloc[0])
    {
        pBlock->pIntrHandleBlock =
            &pBlock->pIntrHandleBlockAlloc[0];
    }
    else
    {
        pBlock->pIntrHandleBlock =
            &pBlock->pIntrHandleBlockAlloc[intrInum];
    }

#endif

    if (intrRequest != intrActual)
    {
        BnxeLogWarn(pUM, "Failed to allocate desired isr count (%d/%d)!",
                    intrActual, intrRequest);

#if 0
        for (i = 0; i < intrActual; i++)
        {
            ddi_intr_free(pBlock->pIntrHandleBlock[i]);
        }

        kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize);
        return B_FALSE;
#else
        if (intrActual == 0)
        {
            kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize);
            return B_FALSE;
        }
#endif
    }

    pBlock->intrCount = intrActual;

    if ((rc = ddi_intr_get_cap(pBlock->pIntrHandleBlock[0],
                               &pBlock->intrCapability)) != DDI_SUCCESS)
    {
        BnxeLogWarn(pUM, "Failed to get isr capability (%d)!", rc);
        goto BnxeIntrBlockAlloc_fail;
    }

    if ((rc = ddi_intr_get_pri(pBlock->pIntrHandleBlock[0],
                               &pBlock->intrPriority)) != DDI_SUCCESS)
    {
        BnxeLogWarn(pUM, "Failed to get isr priority (%d)!", rc);
        goto BnxeIntrBlockAlloc_fail;
    }

    if (pBlock->intrPriority >= ddi_intr_get_hilevel_pri())
    {
        BnxeLogWarn(pUM, "Interrupt priority is too high!");
        goto BnxeIntrBlockAlloc_fail;
    }

    return B_TRUE;

BnxeIntrBlockAlloc_fail:

    for (i = 0; i < intrActual; i++)
    {
        ddi_intr_free(pBlock->pIntrHandleBlock[i]);
    }

    kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize);

    memset(pBlock, 0, sizeof(BnxeIntrBlock));

    return B_FALSE;
}


static void BnxeIntrBlockFree(um_device_t *   pUM,
                              BnxeIntrBlock * pBlock)

{
    int i;

    if (pBlock->intrCount == 0)
    {
        memset(pBlock, 0, sizeof(BnxeIntrBlock));
        return;
    }

    for (i = 0; i < pBlock->intrCount; i++)
    {
        ddi_intr_free(pBlock->pIntrHandleBlock[i]);
    }

    kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize);

    memset(pBlock, 0, sizeof(BnxeIntrBlock));
}


static boolean_t BnxeIntrAddHandlers(um_device_t * pUM)
{
    int rc, i, j;

    switch (pUM->intrType)
    {
    case DDI_INTR_TYPE_MSIX:

        if ((rc = ddi_intr_add_handler(
                      pUM->defIntr.pIntrHandleBlock[0],
                      BnxeIntrMISR,
                      (caddr_t)pUM,
                      (caddr_t)(uintptr_t)DEF_STATUS_BLOCK_IGU_INDEX)) !=
            DDI_SUCCESS)
        {
            BnxeLogWarn(pUM, "Failed to add the MSIX default isr handler (%d)", rc);
            return B_FALSE;
        }

        for (i = 0; i < pUM->rssIntr.intrCount; i++)
        {
            if ((rc = ddi_intr_add_handler(
                          pUM->rssIntr.pIntrHandleBlock[i],
                          BnxeIntrMISR,
                          (caddr_t)pUM,
                          (caddr_t)(uintptr_t)i)) !=
                DDI_SUCCESS)
            {
                BnxeLogWarn(pUM, "Failed to add the MSIX RSS isr handler %d (%d)",
                            (i + NDIS_CID(&pUM->lm_dev)), rc);

                ddi_intr_remove_handler(pUM->defIntr.pIntrHandleBlock[0]);

                for (j = 0; j < i; j++) /* unwind */
                {
                    ddi_intr_remove_handler(pUM->rssIntr.pIntrHandleBlock[j]);
                }

                return B_FALSE;
            }
        }

        /*
         * fcoeIntr.intrCount == 1 implies LM_NON_RSS_SB (last) status block
         * was allocated for FCoE and there was no overlap with the RSS
         * allocation.
         */
        if (pUM->fcoeIntr.intrCount == 1)
        {
            if ((rc = ddi_intr_add_handler(
                          pUM->fcoeIntr.pIntrHandleBlock[0],
                          BnxeIntrMISR,
                          (caddr_t)pUM,
                          (caddr_t)(uintptr_t)LM_NON_RSS_SB(&pUM->lm_dev))) !=
                DDI_SUCCESS)
            {
                BnxeLogWarn(pUM, "Failed to add the MSIX FCoE isr handler (%d)", rc);

                ddi_intr_remove_handler(pUM->defIntr.pIntrHandleBlock[0]);

                for (i = 0; i < pUM->rssIntr.intrCount; i++)
                {
                    ddi_intr_remove_handler(pUM->rssIntr.pIntrHandleBlock[i]);
                }

                return B_FALSE;
            }
        }

        break;

    case DDI_INTR_TYPE_FIXED:

        if ((rc = ddi_intr_add_handler(
                               pUM->defIntr.pIntrHandleBlock[0],
                               BnxeIntrISR,
                               (caddr_t)pUM,
                               (caddr_t)(uintptr_t)DEF_STATUS_BLOCK_IGU_INDEX)) !=
            DDI_SUCCESS)
        {
            BnxeLogWarn(pUM, "Failed to add the fixed default isr handler (%d)", rc);
            return B_FALSE;
        }

        break;

    default:

        BnxeLogWarn(pUM, "Failed to add isr handler (unsupported type %d)!",
                    pUM->intrType);
        return B_FALSE;
    }

    return B_TRUE;
}


static void BnxeIntrBlockRemoveHandler(um_device_t *   pUM,
                                       BnxeIntrBlock * pBlock)
{
    int i;

    (void)pUM;

    if (pBlock->intrCount == 0)
    {
        return;
    }

    for (i = 0; i < pBlock->intrCount; i++)
    {
        ddi_intr_remove_handler(pBlock->pIntrHandleBlock[i]);
    }
}


static boolean_t BnxeIntrBlockEnable(um_device_t *   pUM,
                                     BnxeIntrBlock * pBlock)
{
    int rc, i, j;

    if (pBlock->intrCount == 0)
    {
        return B_TRUE;
    }

    if (pBlock->intrCapability & DDI_INTR_FLAG_BLOCK)
    {
        if ((rc = ddi_intr_block_enable(pBlock->pIntrHandleBlock,
                                        pBlock->intrCount)) != DDI_SUCCESS)
        {
            BnxeLogWarn(pUM, "Failed to enable isr block (%d)", rc);
            return B_FALSE;
        }
    }
    else
    {
        for (i = 0; i < pBlock->intrCount; i++)
        {
            if ((rc = ddi_intr_enable(pBlock->pIntrHandleBlock[i])) !=
                DDI_SUCCESS)
            {
                BnxeLogWarn(pUM, "Failed to enable isr %d (%d)", i, rc);

                for (j = 0; j < i; j++) /* unwind */
                {
                    ddi_intr_disable(pBlock->pIntrHandleBlock[j]);
                }

                return B_FALSE;
            }
        }
    }

    return B_TRUE;
}


static void BnxeIntrBlockDisable(um_device_t *   pUM,
                                 BnxeIntrBlock * pBlock)
{
    int i;

    if (pBlock->intrCount == 0)
    {
        return;
    }

    if (pBlock->intrCapability & DDI_INTR_FLAG_BLOCK)
    {
        ddi_intr_block_disable(pBlock->pIntrHandleBlock, pBlock->intrCount);
    }
    else
    {
        for (i = 0; i < pBlock->intrCount; i++)
        {
            ddi_intr_disable(pBlock->pIntrHandleBlock[i]);
        }
    }
}


int BnxeIntrEnable(um_device_t * pUM)
{
    BnxeMemDma * pDma;
    int rc, i, j;

    atomic_swap_64((volatile uint64_t *)&pUM->intrFired, 0);

    for (i = 0; i < (MAX_RSS_CHAINS + 1); i++)
    {
        pUM->intrSbCnt[i]         = 0;
        pUM->intrSbNoChangeCnt[i] = 0;
    }

    /* get the DMA handles for quick access to the status blocks for sync */
    BnxeFindDmaHandles(pUM);

    /* Enable the default interrupt... */

    if (!BnxeIntrBlockEnable(pUM, &pUM->defIntr))
    {
        BnxeLogWarn(pUM, "Failed to enable the default interrupt");
        return -1;
    }

    /* Enable the FCoE interrupt... */

    if (!BnxeIntrBlockEnable(pUM, &pUM->fcoeIntr))
    {
        BnxeLogWarn(pUM, "Failed to enable the FCoE interrupt");
        BnxeIntrBlockDisable(pUM, &pUM->defIntr);
        return -1;
    }

    /* Enable the RSS interrupts... */

    if (!BnxeIntrBlockEnable(pUM, &pUM->rssIntr))
    {
        BnxeLogWarn(pUM, "Failed to enable the RSS interrupt");
        BnxeIntrBlockDisable(pUM, &pUM->defIntr);
        BnxeIntrBlockDisable(pUM, &pUM->fcoeIntr);
        return -1;
    }

    /* allow the hardware to generate interrupts */
    atomic_swap_32(&pUM->intrEnabled, B_TRUE);
    lm_enable_int(&pUM->lm_dev);

    if (pUM->fmCapabilities &&
        BnxeCheckAccHandle(pUM->lm_dev.vars.reg_handle[BAR_0]) != DDI_FM_OK)
    {
        ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
    }

/* XXX do not remove this... edavis */
drv_usecwait(1000000); /* :-( */

    return 0;
}


void BnxeIntrDisable(um_device_t * pUM)
{
    int rc, i;

    /* stop the device from generating any interrupts */
    lm_disable_int(&pUM->lm_dev);

    if (pUM->fmCapabilities &&
        BnxeCheckAccHandle(pUM->lm_dev.vars.reg_handle[BAR_0]) != DDI_FM_OK)
    {
        ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED);
    }

    atomic_swap_32(&pUM->intrEnabled, B_FALSE);

    /*
     * Ensure the ISR no longer touches the hardware by making sure the ISR
     * is not running or the current run completes.   Since interrupts were
     * disabled before here and intrEnabled is FALSE, we can be sure
     * interrupts will no longer be processed.
     */
    for (i = 0; i < (MAX_RSS_CHAINS + 1); i++)
    {
        BNXE_LOCK_ENTER_INTR(pUM, i);
        BNXE_LOCK_EXIT_INTR(pUM, i);
    }

    /* Disable the default interrupt... */

    BnxeIntrBlockDisable(pUM, &pUM->defIntr);

    /* Disable the FCoE interrupt... */

    BnxeIntrBlockDisable(pUM, &pUM->fcoeIntr);

    /* Disable the RSS interrupts... */

    BnxeIntrBlockDisable(pUM, &pUM->rssIntr);
}


boolean_t BnxeIntrInit(um_device_t * pUM)
{
    dev_info_t * pDev;
    int intrTypes = 0;
    int intrTotalAlloc = 0;
    int numMSIX, numMSI, numFIX;
    int rc, i;

    pDev = pUM->pDev;

    atomic_swap_32(&pUM->intrEnabled, B_FALSE);

    if ((rc = ddi_intr_get_supported_types(pDev, &intrTypes)) != DDI_SUCCESS)
    {
        BnxeLogWarn(pUM, "Failed to get supported interrupt types (%d)", rc);
        return B_FALSE;
    }

    numMSIX = BnxeGetInterruptCount(pDev, DDI_INTR_TYPE_MSIX, intrTypes);
    numMSI  = BnxeGetInterruptCount(pDev, DDI_INTR_TYPE_MSI, intrTypes);
    numFIX  = BnxeGetInterruptCount(pDev, DDI_INTR_TYPE_FIXED, intrTypes);

    if (numFIX <= 0)
    {
        BnxeLogWarn(pUM, "Fixed interrupt not supported!");
        return B_FALSE;
    }

    memset(&pUM->defIntr,  0, sizeof(BnxeIntrBlock));
    memset(&pUM->rssIntr,  0, sizeof(BnxeIntrBlock));
    memset(&pUM->fcoeIntr, 0, sizeof(BnxeIntrBlock));

    if (pUM->devParams.disableMsix)
    {
        BnxeLogInfo(pUM, "Forcing fixed level interrupts.");
        pUM->lm_dev.params.interrupt_mode = LM_INT_MODE_INTA;
        pUM->intrType                     = DDI_INTR_TYPE_FIXED;
    }
    else if (numMSIX > 0)
    {
        pUM->lm_dev.params.interrupt_mode = LM_INT_MODE_MIMD;
        pUM->intrType                     = DDI_INTR_TYPE_MSIX;
    }
    else /* numFIX */
    {
        pUM->lm_dev.params.interrupt_mode = LM_INT_MODE_INTA;
        pUM->intrType                     = DDI_INTR_TYPE_FIXED;
    }

    while (1)
    {
        /* allocate the default interrupt */

        if (!BnxeIntrBlockAlloc(pUM,
                                0,
                                1,
                                &pUM->defIntr))
        {
            BnxeLogWarn(pUM, "Failed to allocate default %s interrupt!",
                        BnxeIntrTypeName(pUM->intrType));
            goto BnxeIntrInit_alloc_fail;
        }

        intrTotalAlloc++;

        if (pUM->intrType == DDI_INTR_TYPE_FIXED)
        {
            /* only one interrupt allocated for fixed (default) */
            break;
        }

        if (BnxeProtoFcoeAfex(pUM))
        {
            pUM->devParams.numRings = 0;
        }
        else
        {
            /* allocate the RSS interrupts */

            while (pUM->devParams.numRings > 0)
            {
                if (!BnxeIntrBlockAlloc(pUM,
                                        (NDIS_CID(&pUM->lm_dev) + 1),
                                        pUM->devParams.numRings,
                                        &pUM->rssIntr))
                {
                    BnxeLogWarn(pUM, "Failed to allocate %d RSS %s interrupts!",
                                pUM->devParams.numRings,
                                BnxeIntrTypeName(pUM->intrType));
                    pUM->devParams.numRings >>= 1;
                    continue;
                }

                break;
            }

            if (pUM->devParams.numRings == 0)
            {
                BnxeIntrBlockFree(pUM, &pUM->defIntr);
                goto BnxeIntrInit_alloc_fail;
            }

            BnxeLogInfo(pUM, "Allocated %d RSS %s interrupts.",
                        pUM->rssIntr.intrCount,
                        BnxeIntrTypeName(pUM->intrType));

            intrTotalAlloc += pUM->rssIntr.intrCount; /* intrCount <= numRings */
        }

        /*
         * Allocate the FCoE interrupt only if all available status blocks
         * were not taken up by the RSS chains.  If they were then the last
         * status block (LM_NON_RSS_SB) is overloaded for both RSS and FCoE.
         */

        if (BNXE_FCOE(pUM))
        {
            if (pUM->rssIntr.intrCount < LM_MAX_RSS_CHAINS(&pUM->lm_dev))
            {
                if (!BnxeIntrBlockAlloc(pUM,
                                        (LM_NON_RSS_SB(&pUM->lm_dev) + 1),
                                        1,
                                        &pUM->fcoeIntr))
                {
                    BnxeLogWarn(pUM, "Failed to allocate FCoE %s interrupt!",
                                BnxeIntrTypeName(pUM->intrType));
                    BnxeIntrBlockFree(pUM, &pUM->defIntr);
                    BnxeIntrBlockFree(pUM, &pUM->rssIntr);
                    goto BnxeIntrInit_alloc_fail;
                }

                intrTotalAlloc++;
            }
            else
            {
                /* to be safe, sets fcoeIntr.intrCount to 0 */
                memset(&pUM->fcoeIntr, 0, sizeof(BnxeIntrBlock));
            }
        }

        break;

BnxeIntrInit_alloc_fail:

        if (pUM->intrType == DDI_INTR_TYPE_FIXED)
        {
            return B_FALSE;
        }

        /* fall back to fixed a retry allocation */
        intrTotalAlloc = 0;
        pUM->lm_dev.params.interrupt_mode = LM_INT_MODE_INTA;
        pUM->intrType                     = DDI_INTR_TYPE_FIXED;
    }

    if (pUM->intrType == DDI_INTR_TYPE_MSIX)
    {
        pUM->devParams.numRings          = pUM->rssIntr.intrCount;
        pUM->lm_dev.params.rss_chain_cnt = pUM->rssIntr.intrCount;
        pUM->lm_dev.params.tss_chain_cnt = pUM->rssIntr.intrCount;
    }
    else
    {
        /* fixed level (no rings)... */
        pUM->devParams.numRings          = 0;
        pUM->lm_dev.params.rss_chain_cnt = 1;
        pUM->lm_dev.params.tss_chain_cnt = 1;

        BnxeLogWarn(pUM, "Using Fixed Level Interrupts! (set ddi_msix_alloc_limit in /etc/system)");
    }

    BnxeLogInfo(pUM, "Interrupts (Supported - %d Fixed / %d MSI / %d MSIX) (Allocated - %d %s)",
                numFIX, numMSI, numMSIX, intrTotalAlloc, BnxeIntrTypeName(pUM->intrType));

    if (!BnxeIntrAddHandlers(pUM))
    {
        BnxeLogWarn(pUM, "Failed to add interrupts!");
        BnxeIntrBlockFree(pUM, &pUM->defIntr);
        BnxeIntrBlockFree(pUM, &pUM->fcoeIntr);
        BnxeIntrBlockFree(pUM, &pUM->rssIntr);
        return B_FALSE;
    }

    /* copy default priority and assume rest are the same (for mutex) */
    pUM->intrPriority = pUM->defIntr.intrPriority;

    return B_TRUE;
}


void BnxeIntrFini(um_device_t * pUM)
{
    int i;

    BnxeIntrBlockDisable(pUM, &pUM->defIntr);
    BnxeIntrBlockRemoveHandler(pUM, &pUM->defIntr);
    BnxeIntrBlockFree(pUM, &pUM->defIntr);

    BnxeIntrBlockDisable(pUM, &pUM->fcoeIntr);
    BnxeIntrBlockRemoveHandler(pUM, &pUM->fcoeIntr);
    BnxeIntrBlockFree(pUM, &pUM->fcoeIntr);

    BnxeIntrBlockDisable(pUM, &pUM->rssIntr);
    BnxeIntrBlockRemoveHandler(pUM, &pUM->rssIntr);
    BnxeIntrBlockFree(pUM, &pUM->rssIntr);
}