root/usr/src/uts/common/io/fibre-channel/fca/qlge/qlge.c
/*
 * 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 2010 QLogic Corporation. All rights reserved.
 */

/*
 * Copyright (c) 2018, Joyent, Inc.
 */

#include <qlge.h>
#include <sys/atomic.h>
#include <sys/strsubr.h>
#include <sys/pattr.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <inet/ip.h>



/*
 * Local variables
 */
static struct ether_addr ql_ether_broadcast_addr =
        {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static char version[] = "GLDv3 QLogic 81XX " VERSIONSTR;

/*
 * Local function prototypes
 */
static void ql_free_resources(qlge_t *);
static void ql_fini_kstats(qlge_t *);
static uint32_t ql_get_link_state(qlge_t *);
static void ql_read_conf(qlge_t *);
static int ql_alloc_phys(dev_info_t *, ddi_dma_handle_t *,
    ddi_device_acc_attr_t *, uint_t, ddi_acc_handle_t *,
    size_t, size_t, caddr_t *, ddi_dma_cookie_t *);
static int ql_alloc_phys_rbuf(dev_info_t *, ddi_dma_handle_t *,
    ddi_device_acc_attr_t *, uint_t, ddi_acc_handle_t *,
    size_t, size_t, caddr_t *, ddi_dma_cookie_t *);
static void ql_free_phys(ddi_dma_handle_t *, ddi_acc_handle_t *);
static int ql_set_routing_reg(qlge_t *, uint32_t, uint32_t, int);
static int ql_attach(dev_info_t *, ddi_attach_cmd_t);
static int ql_detach(dev_info_t *, ddi_detach_cmd_t);
static int ql_bringdown_adapter(qlge_t *);
static int ql_bringup_adapter(qlge_t *);
static int ql_asic_reset(qlge_t *);
static void ql_wake_mpi_reset_soft_intr(qlge_t *);
static void ql_stop_timer(qlge_t *qlge);
static void ql_fm_fini(qlge_t *qlge);
int ql_clean_outbound_rx_ring(struct rx_ring *rx_ring);

/*
 * TX dma maping handlers allow multiple sscatter-gather lists
 */
ddi_dma_attr_t  tx_mapping_dma_attr = {
        DMA_ATTR_V0,                    /* dma_attr_version */
        QL_DMA_LOW_ADDRESS,             /* low DMA address range */
        QL_DMA_HIGH_64BIT_ADDRESS,      /* high DMA address range */
        QL_DMA_XFER_COUNTER,            /* DMA counter register */
        QL_DMA_ADDRESS_ALIGNMENT,       /* DMA address alignment, default - 8 */
        QL_DMA_BURSTSIZES,              /* DMA burstsizes */
        QL_DMA_MIN_XFER_SIZE,           /* min effective DMA size */
        QL_DMA_MAX_XFER_SIZE,           /* max DMA xfer size */
        QL_DMA_SEGMENT_BOUNDARY,        /* segment boundary */
        QL_MAX_TX_DMA_HANDLES,          /* s/g list length */
        QL_DMA_GRANULARITY,             /* granularity of device */
        DDI_DMA_RELAXED_ORDERING        /* DMA transfer flags */
};

/*
 * Receive buffers and Request/Response queues do not allow scatter-gather lists
 */
ddi_dma_attr_t  dma_attr = {
        DMA_ATTR_V0,                    /* dma_attr_version */
        QL_DMA_LOW_ADDRESS,             /* low DMA address range */
        QL_DMA_HIGH_64BIT_ADDRESS,      /* high DMA address range */
        QL_DMA_XFER_COUNTER,            /* DMA counter register */
        QL_DMA_ADDRESS_ALIGNMENT,       /* DMA address alignment, default - 8 */
        QL_DMA_BURSTSIZES,              /* DMA burstsizes */
        QL_DMA_MIN_XFER_SIZE,           /* min effective DMA size */
        QL_DMA_MAX_XFER_SIZE,           /* max DMA xfer size */
        QL_DMA_SEGMENT_BOUNDARY,        /* segment boundary */
        1,                              /* s/g list length, i.e no sg list */
        QL_DMA_GRANULARITY,             /* granularity of device */
        QL_DMA_XFER_FLAGS               /* DMA transfer flags */
};
/*
 * Receive buffers do not allow scatter-gather lists
 */
ddi_dma_attr_t  dma_attr_rbuf = {
        DMA_ATTR_V0,                    /* dma_attr_version */
        QL_DMA_LOW_ADDRESS,             /* low DMA address range */
        QL_DMA_HIGH_64BIT_ADDRESS,      /* high DMA address range */
        QL_DMA_XFER_COUNTER,            /* DMA counter register */
        0x1,                            /* DMA address alignment, default - 8 */
        QL_DMA_BURSTSIZES,              /* DMA burstsizes */
        QL_DMA_MIN_XFER_SIZE,           /* min effective DMA size */
        QL_DMA_MAX_XFER_SIZE,           /* max DMA xfer size */
        QL_DMA_SEGMENT_BOUNDARY,        /* segment boundary */
        1,                              /* s/g list length, i.e no sg list */
        QL_DMA_GRANULARITY,             /* granularity of device */
        DDI_DMA_RELAXED_ORDERING        /* DMA transfer flags */
};
/*
 * DMA access attribute structure.
 */
/* device register access from host */
ddi_device_acc_attr_t ql_dev_acc_attr = {
        DDI_DEVICE_ATTR_V0,
        DDI_STRUCTURE_LE_ACC,
        DDI_STRICTORDER_ACC
};

/* host ring descriptors */
ddi_device_acc_attr_t ql_desc_acc_attr = {
        DDI_DEVICE_ATTR_V0,
        DDI_NEVERSWAP_ACC,
        DDI_STRICTORDER_ACC
};

/* host ring buffer */
ddi_device_acc_attr_t ql_buf_acc_attr = {
        DDI_DEVICE_ATTR_V0,
        DDI_NEVERSWAP_ACC,
        DDI_STRICTORDER_ACC
};

/*
 * Hash key table for Receive Side Scaling (RSS) support
 */
const uint8_t key_data[] = {
        0x23, 0x64, 0xa1, 0xaa, 0x37, 0xc0, 0xed, 0x05, 0x2b, 0x36,
        0x50, 0x5c, 0x45, 0x1e, 0x7e, 0xc8, 0x5d, 0x2a, 0x54, 0x2f,
        0xe4, 0x3d, 0x0f, 0xbb, 0x91, 0xd9, 0x25, 0x60, 0xd4, 0xf8,
        0x12, 0xa0, 0x59, 0x4b, 0x9e, 0x8a, 0x51, 0xda, 0xcd, 0x49};

/*
 * Shadow Registers:
 * Outbound queues have a consumer index that is maintained by the chip.
 * Inbound queues have a producer index that is maintained by the chip.
 * For lower overhead, these registers are "shadowed" to host memory
 * which allows the device driver to track the queue progress without
 * PCI reads. When an entry is placed on an inbound queue, the chip will
 * update the relevant index register and then copy the value to the
 * shadow register in host memory.
 * Currently, ql_read_sh_reg only read Inbound queues'producer index.
 */

static inline unsigned int
ql_read_sh_reg(qlge_t *qlge, struct rx_ring *rx_ring)
{
        uint32_t rtn;

        /* re-synchronize shadow prod index dma buffer before reading */
        (void) ddi_dma_sync(qlge->host_copy_shadow_dma_attr.dma_handle,
            rx_ring->prod_idx_sh_reg_offset,
            sizeof (uint32_t), DDI_DMA_SYNC_FORKERNEL);

        rtn = ddi_get32(qlge->host_copy_shadow_dma_attr.acc_handle,
            (uint32_t *)rx_ring->prod_idx_sh_reg);

        return (rtn);
}

/*
 * Read 32 bit atomically
 */
uint32_t
ql_atomic_read_32(volatile uint32_t *target)
{
        /*
         * atomic_add_32_nv returns the new value after the add,
         * we are adding 0 so we should get the original value
         */
        return (atomic_add_32_nv(target, 0));
}

/*
 * Set 32 bit atomically
 */
void
ql_atomic_set_32(volatile uint32_t *target, uint32_t newval)
{
        (void) atomic_swap_32(target, newval);
}


/*
 * Setup device PCI configuration registers.
 * Kernel context.
 */
static void
ql_pci_config(qlge_t *qlge)
{
        uint16_t w;

        qlge->vendor_id = (uint16_t)pci_config_get16(qlge->pci_handle,
            PCI_CONF_VENID);
        qlge->device_id = (uint16_t)pci_config_get16(qlge->pci_handle,
            PCI_CONF_DEVID);

        /*
         * we want to respect framework's setting of PCI
         * configuration space command register and also
         * want to make sure that all bits of interest to us
         * are properly set in PCI Command register(0x04).
         * PCI_COMM_IO          0x1      I/O access enable
         * PCI_COMM_MAE         0x2      Memory access enable
         * PCI_COMM_ME          0x4      bus master enable
         * PCI_COMM_MEMWR_INVAL 0x10     memory write and invalidate enable.
         */
        w = (uint16_t)pci_config_get16(qlge->pci_handle, PCI_CONF_COMM);
        w = (uint16_t)(w & (~PCI_COMM_IO));
        w = (uint16_t)(w | PCI_COMM_MAE | PCI_COMM_ME |
            /* PCI_COMM_MEMWR_INVAL | */
            PCI_COMM_PARITY_DETECT | PCI_COMM_SERR_ENABLE);

        pci_config_put16(qlge->pci_handle, PCI_CONF_COMM, w);

        w = pci_config_get16(qlge->pci_handle, 0x54);
        w = (uint16_t)(w & (~0x7000));
        w = (uint16_t)(w | 0x5000);
        pci_config_put16(qlge->pci_handle, 0x54, w);

        ql_dump_pci_config(qlge);
}

/*
 * This routine parforms the neccessary steps to set GLD mac information
 * such as Function number, xgmac mask and shift bits
 */
static int
ql_set_mac_info(qlge_t *qlge)
{
        uint32_t value;
        int rval = DDI_FAILURE;
        uint32_t fn0_net, fn1_net;

        /* set default value */
        qlge->fn0_net = FN0_NET;
        qlge->fn1_net = FN1_NET;

        if (ql_read_processor_data(qlge, MPI_REG, &value) != DDI_SUCCESS) {
                cmn_err(CE_WARN, "%s(%d) read MPI register failed",
                    __func__, qlge->instance);
                goto exit;
        } else {
                fn0_net = (value >> 1) & 0x07;
                fn1_net = (value >> 5) & 0x07;
                if ((fn0_net > 4) || (fn1_net > 4) || (fn0_net == fn1_net)) {
                        cmn_err(CE_WARN, "%s(%d) bad mpi register value %x, \n"
                            "nic0 function number %d,"
                            "nic1 function number %d "
                            "use default\n",
                            __func__, qlge->instance, value, fn0_net, fn1_net);
                        goto exit;
                } else {
                        qlge->fn0_net = fn0_net;
                        qlge->fn1_net = fn1_net;
                }
        }

        /* Get the function number that the driver is associated with */
        value = ql_read_reg(qlge, REG_STATUS);
        qlge->func_number = (uint8_t)((value >> 6) & 0x03);
        QL_PRINT(DBG_INIT, ("status register is:%x, func_number: %d\n",
            value, qlge->func_number));

        /* The driver is loaded on a non-NIC function? */
        if ((qlge->func_number != qlge->fn0_net) &&
            (qlge->func_number != qlge->fn1_net)) {
                cmn_err(CE_WARN,
                    "Invalid function number = 0x%x\n", qlge->func_number);
                goto exit;
        }
        /* network port 0? */
        if (qlge->func_number == qlge->fn0_net) {
                qlge->xgmac_sem_mask = QL_PORT0_XGMAC_SEM_MASK;
                qlge->xgmac_sem_bits = QL_PORT0_XGMAC_SEM_BITS;
        } else {
                qlge->xgmac_sem_mask = QL_PORT1_XGMAC_SEM_MASK;
                qlge->xgmac_sem_bits = QL_PORT1_XGMAC_SEM_BITS;
        }
        rval = DDI_SUCCESS;
exit:
        return (rval);

}

/*
 * write to doorbell register
 */
void
ql_write_doorbell_reg(qlge_t *qlge, uint32_t *addr, uint32_t data)
{
        ddi_put32(qlge->dev_doorbell_reg_handle, addr, data);
}

/*
 * read from doorbell register
 */
uint32_t
ql_read_doorbell_reg(qlge_t *qlge, uint32_t *addr)
{
        uint32_t ret;

        ret = ddi_get32(qlge->dev_doorbell_reg_handle, addr);

        return  (ret);
}

/*
 * This function waits for a specific bit to come ready
 * in a given register.  It is used mostly by the initialize
 * process, but is also used in kernel thread API such as
 * netdev->set_multi, netdev->set_mac_address, netdev->vlan_rx_add_vid.
 */
static int
ql_wait_reg_rdy(qlge_t *qlge, uint32_t reg, uint32_t bit, uint32_t err_bit)
{
        uint32_t temp;
        int count = UDELAY_COUNT;

        while (count) {
                temp = ql_read_reg(qlge, reg);

                /* check for errors */
                if ((temp & err_bit) != 0) {
                        break;
                } else if ((temp & bit) != 0)
                        return (DDI_SUCCESS);
                qlge_delay(UDELAY_DELAY);
                count--;
        }
        cmn_err(CE_WARN,
            "Waiting for reg %x to come ready failed.", reg);
        if (qlge->fm_enable) {
                ql_fm_ereport(qlge, DDI_FM_DEVICE_NO_RESPONSE);
                atomic_or_32(&qlge->flags, ADAPTER_ERROR);
        }
        return (DDI_FAILURE);
}

/*
 * The CFG register is used to download TX and RX control blocks
 * to the chip. This function waits for an operation to complete.
 */
static int
ql_wait_cfg(qlge_t *qlge, uint32_t bit)
{
        return (ql_wait_reg_bit(qlge, REG_CONFIGURATION, bit, BIT_RESET, 0));
}


/*
 * Used to issue init control blocks to hw. Maps control block,
 * sets address, triggers download, waits for completion.
 */
static int
ql_write_cfg(qlge_t *qlge, uint32_t bit, uint64_t phy_addr, uint16_t q_id)
{
        int status = DDI_SUCCESS;
        uint32_t mask;
        uint32_t value;

        status = ql_sem_spinlock(qlge, SEM_ICB_MASK);
        if (status != DDI_SUCCESS) {
                goto exit;
        }
        status = ql_wait_cfg(qlge, bit);
        if (status != DDI_SUCCESS) {
                goto exit;
        }

        ql_write_reg(qlge, REG_ICB_ACCESS_ADDRESS_LOWER, LS_64BITS(phy_addr));
        ql_write_reg(qlge, REG_ICB_ACCESS_ADDRESS_UPPER, MS_64BITS(phy_addr));

        mask = CFG_Q_MASK | (bit << 16);
        value = bit | (q_id << CFG_Q_SHIFT);
        ql_write_reg(qlge, REG_CONFIGURATION, (mask | value));

        /*
         * Wait for the bit to clear after signaling hw.
         */
        status = ql_wait_cfg(qlge, bit);
        ql_sem_unlock(qlge, SEM_ICB_MASK); /* does flush too */

exit:
        return (status);
}

/*
 * Initialize adapter instance
 */
static int
ql_init_instance(qlge_t *qlge)
{
        int i;

        /* Default value */
        qlge->mac_flags = QL_MAC_INIT;
        qlge->mtu = ETHERMTU;           /* set normal size as default */
        qlge->page_size = VM_PAGE_SIZE; /* default page size */

        for (i = 0; i < MAX_RX_RINGS; i++) {
                qlge->rx_polls[i] = 0;
                qlge->rx_interrupts[i] = 0;
        }

        /*
         * Set up the operating parameters.
         */
        qlge->multicast_list_count = 0;

        /*
         * Set up the max number of unicast list
         */
        qlge->unicst_total = MAX_UNICAST_LIST_SIZE;
        qlge->unicst_avail = MAX_UNICAST_LIST_SIZE;

        /*
         * read user defined properties in .conf file
         */
        ql_read_conf(qlge); /* mtu, pause, LSO etc */
        qlge->rx_ring_count = qlge->tx_ring_count + qlge->rss_ring_count;

        QL_PRINT(DBG_INIT, ("mtu is %d \n", qlge->mtu));

        /* choose Memory Space mapping and get Vendor Id, Device ID etc */
        ql_pci_config(qlge);
        qlge->ip_hdr_offset = 0;

        if (qlge->device_id == 0x8000) {
                /* Schultz card */
                qlge->cfg_flags |= CFG_CHIP_8100;
                /* enable just ipv4 chksum offload for Schultz */
                qlge->cfg_flags |= CFG_CKSUM_FULL_IPv4;
                /*
                 * Schultz firmware does not do pseduo IP header checksum
                 * calculation, needed to be done by driver
                 */
                qlge->cfg_flags |= CFG_HW_UNABLE_PSEUDO_HDR_CKSUM;
                if (qlge->lso_enable)
                        qlge->cfg_flags |= CFG_LSO;
                qlge->cfg_flags |= CFG_SUPPORT_SCATTER_GATHER;
                /* Schultz must split packet header */
                qlge->cfg_flags |= CFG_ENABLE_SPLIT_HEADER;
                qlge->max_read_mbx = 5;
                qlge->ip_hdr_offset = 2;
        }

        /* Set Function Number and some of the iocb mac information */
        if (ql_set_mac_info(qlge) != DDI_SUCCESS)
                return (DDI_FAILURE);

        /* Read network settings from NVRAM */
        /* After nvram is read successfully, update dev_addr */
        if (ql_get_flash_params(qlge) == DDI_SUCCESS) {
                QL_PRINT(DBG_INIT, ("mac%d address is \n", qlge->func_number));
                for (i = 0; i < ETHERADDRL; i++) {
                        qlge->dev_addr.ether_addr_octet[i] =
                            qlge->nic_config.factory_MAC[i];
                }
        } else {
                cmn_err(CE_WARN, "%s(%d): Failed to read flash memory",
                    __func__, qlge->instance);
                return (DDI_FAILURE);
        }

        bcopy(qlge->dev_addr.ether_addr_octet,
            qlge->unicst_addr[0].addr.ether_addr_octet,
            ETHERADDRL);
        QL_DUMP(DBG_INIT, "\t flash mac address dump:\n",
            &qlge->dev_addr.ether_addr_octet[0], 8, ETHERADDRL);

        qlge->port_link_state = LS_DOWN;

        return (DDI_SUCCESS);
}


/*
 * This hardware semaphore provides the mechanism for exclusive access to
 * resources shared between the NIC driver, MPI firmware,
 * FCOE firmware and the FC driver.
 */
static int
ql_sem_trylock(qlge_t *qlge, uint32_t sem_mask)
{
        uint32_t sem_bits = 0;

        switch (sem_mask) {
        case SEM_XGMAC0_MASK:
                sem_bits = SEM_SET << SEM_XGMAC0_SHIFT;
                break;
        case SEM_XGMAC1_MASK:
                sem_bits = SEM_SET << SEM_XGMAC1_SHIFT;
                break;
        case SEM_ICB_MASK:
                sem_bits = SEM_SET << SEM_ICB_SHIFT;
                break;
        case SEM_MAC_ADDR_MASK:
                sem_bits = SEM_SET << SEM_MAC_ADDR_SHIFT;
                break;
        case SEM_FLASH_MASK:
                sem_bits = SEM_SET << SEM_FLASH_SHIFT;
                break;
        case SEM_PROBE_MASK:
                sem_bits = SEM_SET << SEM_PROBE_SHIFT;
                break;
        case SEM_RT_IDX_MASK:
                sem_bits = SEM_SET << SEM_RT_IDX_SHIFT;
                break;
        case SEM_PROC_REG_MASK:
                sem_bits = SEM_SET << SEM_PROC_REG_SHIFT;
                break;
        default:
                cmn_err(CE_WARN, "Bad Semaphore mask!.");
                return (DDI_FAILURE);
        }

        ql_write_reg(qlge, REG_SEMAPHORE, sem_bits | sem_mask);
        return (!(ql_read_reg(qlge, REG_SEMAPHORE) & sem_bits));
}

/*
 * Lock a specific bit of Semaphore register to gain
 * access to a particular shared register
 */
int
ql_sem_spinlock(qlge_t *qlge, uint32_t sem_mask)
{
        unsigned int wait_count = 30;

        while (wait_count) {
                if (!ql_sem_trylock(qlge, sem_mask))
                        return (DDI_SUCCESS);
                qlge_delay(100);
                wait_count--;
        }
        cmn_err(CE_WARN, "%s(%d) sem_mask 0x%x lock timeout ",
            __func__, qlge->instance, sem_mask);
        return (DDI_FAILURE);
}

/*
 * Unock a specific bit of Semaphore register to release
 * access to a particular shared register
 */
void
ql_sem_unlock(qlge_t *qlge, uint32_t sem_mask)
{
        ql_write_reg(qlge, REG_SEMAPHORE, sem_mask);
        (void) ql_read_reg(qlge, REG_SEMAPHORE);        /* flush */
}

/*
 * Get property value from configuration file.
 *
 * string = property string pointer.
 *
 * Returns:
 * 0xFFFFFFFF = no property else property value.
 */
static uint32_t
ql_get_prop(qlge_t *qlge, char *string)
{
        char buf[256];
        uint32_t data;

        /* Get adapter instance parameter. */
        (void) sprintf(buf, "hba%d-%s", qlge->instance, string);
        data = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, qlge->dip, 0, buf,
            (int)0xffffffff);

        /* Adapter instance parameter found? */
        if (data == 0xffffffff) {
                /* No, get default parameter. */
                data = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, qlge->dip, 0,
                    string, (int)0xffffffff);
        }

        return (data);
}

/*
 * Read user setting from configuration file.
 */
static void
ql_read_conf(qlge_t *qlge)
{
        uint32_t data;

        /* clear configuration flags */
        qlge->cfg_flags = 0;

        /* Set up the default ring sizes. */
        qlge->tx_ring_size = NUM_TX_RING_ENTRIES;
        data = ql_get_prop(qlge, "tx_ring_size");
        /* if data is valid */
        if ((data != 0xffffffff) && data) {
                if (qlge->tx_ring_size != data) {
                        qlge->tx_ring_size = (uint16_t)data;
                }
        }

        qlge->rx_ring_size = NUM_RX_RING_ENTRIES;
        data = ql_get_prop(qlge, "rx_ring_size");
        /* if data is valid */
        if ((data != 0xffffffff) && data) {
                if (qlge->rx_ring_size != data) {
                        qlge->rx_ring_size = (uint16_t)data;
                }
        }

        qlge->tx_ring_count = 8;
        data = ql_get_prop(qlge, "tx_ring_count");
        /* if data is valid */
        if ((data != 0xffffffff) && data) {
                if (qlge->tx_ring_count != data) {
                        qlge->tx_ring_count = (uint16_t)data;
                }
        }

        qlge->rss_ring_count = 8;
        data = ql_get_prop(qlge, "rss_ring_count");
        /* if data is valid */
        if ((data != 0xffffffff) && data) {
                if (qlge->rss_ring_count != data) {
                        qlge->rss_ring_count = (uint16_t)data;
                }
        }

        /* Get default rx_copy enable/disable. */
        if ((data = ql_get_prop(qlge, "force-rx-copy")) == 0xffffffff ||
            data == 0) {
                qlge->rx_copy = B_FALSE;
                QL_PRINT(DBG_INIT, ("rx copy mode disabled\n"));
        } else if (data == 1) {
                qlge->rx_copy = B_TRUE;
                QL_PRINT(DBG_INIT, ("rx copy mode enabled\n"));
        }

        qlge->rx_copy_threshold = qlge->rx_ring_size / 4;
        data = ql_get_prop(qlge, "rx_copy_threshold");
        if ((data != 0xffffffff) && (data != 0)) {
                qlge->rx_copy_threshold = data;
                cmn_err(CE_NOTE, "!new rx_copy_threshold %d \n",
                    qlge->rx_copy_threshold);
        }

        /* Get mtu packet size. */
        data = ql_get_prop(qlge, "mtu");
        if ((data == ETHERMTU) || (data == JUMBO_MTU)) {
                if (qlge->mtu != data) {
                        qlge->mtu = data;
                        cmn_err(CE_NOTE, "new mtu is %d\n", qlge->mtu);
                }
        }

        if (qlge->mtu == JUMBO_MTU) {
                qlge->rx_coalesce_usecs = DFLT_RX_COALESCE_WAIT_JUMBO;
                qlge->tx_coalesce_usecs = DFLT_TX_COALESCE_WAIT_JUMBO;
                qlge->rx_max_coalesced_frames = DFLT_RX_INTER_FRAME_WAIT_JUMBO;
                qlge->tx_max_coalesced_frames = DFLT_TX_INTER_FRAME_WAIT_JUMBO;
        }


        /* Get pause mode, default is Per Priority mode. */
        qlge->pause = PAUSE_MODE_PER_PRIORITY;
        data = ql_get_prop(qlge, "pause");
        if (data <= PAUSE_MODE_PER_PRIORITY) {
                if (qlge->pause != data) {
                        qlge->pause = data;
                        cmn_err(CE_NOTE, "new pause mode %d\n", qlge->pause);
                }
        }
        /* Receive interrupt delay */
        qlge->rx_coalesce_usecs = DFLT_RX_COALESCE_WAIT;
        data = ql_get_prop(qlge, "rx_intr_delay");
        /* if data is valid */
        if ((data != 0xffffffff) && data) {
                if (qlge->rx_coalesce_usecs != data) {
                        qlge->rx_coalesce_usecs = (uint16_t)data;
                }
        }
        /* Rx inter-packet delay. */
        qlge->rx_max_coalesced_frames = DFLT_RX_INTER_FRAME_WAIT;
        data = ql_get_prop(qlge, "rx_ipkt_delay");
        /* if data is valid */
        if ((data != 0xffffffff) && data) {
                if (qlge->rx_max_coalesced_frames != data) {
                        qlge->rx_max_coalesced_frames = (uint16_t)data;
                }
        }
        /* Transmit interrupt delay */
        qlge->tx_coalesce_usecs = DFLT_TX_COALESCE_WAIT;
        data = ql_get_prop(qlge, "tx_intr_delay");
        /* if data is valid */
        if ((data != 0xffffffff) && data) {
                if (qlge->tx_coalesce_usecs != data) {
                        qlge->tx_coalesce_usecs = (uint16_t)data;
                }
        }
        /* Tx inter-packet delay. */
        qlge->tx_max_coalesced_frames = DFLT_TX_INTER_FRAME_WAIT;
        data = ql_get_prop(qlge, "tx_ipkt_delay");
        /* if data is valid */
        if ((data != 0xffffffff) && data) {
                if (qlge->tx_max_coalesced_frames != data) {
                        qlge->tx_max_coalesced_frames = (uint16_t)data;
                }
        }

        /* Get split header payload_copy_thresh. */
        qlge->payload_copy_thresh = DFLT_PAYLOAD_COPY_THRESH;
        data = ql_get_prop(qlge, "payload_copy_thresh");
        /* if data is valid */
        if ((data != 0xffffffff) && (data != 0)) {
                if (qlge->payload_copy_thresh != data) {
                        qlge->payload_copy_thresh = data;
                }
        }

        /* large send offload (LSO) capability. */
        qlge->lso_enable = 1;
        data = ql_get_prop(qlge, "lso_enable");
        /* if data is valid */
        if ((data == 0) || (data == 1)) {
                if (qlge->lso_enable != data) {
                        qlge->lso_enable = (uint16_t)data;
                }
        }

        /* dcbx capability. */
        qlge->dcbx_enable = 1;
        data = ql_get_prop(qlge, "dcbx_enable");
        /* if data is valid */
        if ((data == 0) || (data == 1)) {
                if (qlge->dcbx_enable != data) {
                        qlge->dcbx_enable = (uint16_t)data;
                }
        }
        /* fault management enable */
        qlge->fm_enable = B_TRUE;
        data = ql_get_prop(qlge, "fm-enable");
        if ((data == 0x1) || (data == 0)) {
                qlge->fm_enable = (boolean_t)data;
        }

}

/*
 * Enable global interrupt
 */
static void
ql_enable_global_interrupt(qlge_t *qlge)
{
        ql_write_reg(qlge, REG_INTERRUPT_ENABLE,
            (INTR_EN_EI << 16) | INTR_EN_EI);
        qlge->flags |= INTERRUPTS_ENABLED;
}

/*
 * Disable global interrupt
 */
static void
ql_disable_global_interrupt(qlge_t *qlge)
{
        ql_write_reg(qlge, REG_INTERRUPT_ENABLE, (INTR_EN_EI << 16));
        qlge->flags &= ~INTERRUPTS_ENABLED;
}

/*
 * Enable one ring interrupt
 */
void
ql_enable_completion_interrupt(qlge_t *qlge, uint32_t intr)
{
        struct intr_ctx *ctx = qlge->intr_ctx + intr;

        QL_PRINT(DBG_INTR, ("%s(%d): To enable intr %d, irq_cnt %d \n",
            __func__, qlge->instance, intr, ctx->irq_cnt));

        if ((qlge->intr_type == DDI_INTR_TYPE_MSIX) && intr) {
                /*
                 * Always enable if we're MSIX multi interrupts and
                 * it's not the default (zeroeth) interrupt.
                 */
                ql_write_reg(qlge, REG_INTERRUPT_ENABLE, ctx->intr_en_mask);
                return;
        }

        if (!atomic_dec_32_nv(&ctx->irq_cnt)) {
                mutex_enter(&qlge->hw_mutex);
                ql_write_reg(qlge, REG_INTERRUPT_ENABLE, ctx->intr_en_mask);
                mutex_exit(&qlge->hw_mutex);
                QL_PRINT(DBG_INTR,
                    ("%s(%d): write %x to intr enable register \n",
                    __func__, qlge->instance, ctx->intr_en_mask));
        }
}

/*
 * ql_forced_disable_completion_interrupt
 * Used by call from OS, may be called without
 * a pending interrupt so force the disable
 */
uint32_t
ql_forced_disable_completion_interrupt(qlge_t *qlge, uint32_t intr)
{
        uint32_t var = 0;
        struct intr_ctx *ctx = qlge->intr_ctx + intr;

        QL_PRINT(DBG_INTR, ("%s(%d): To disable intr %d, irq_cnt %d \n",
            __func__, qlge->instance, intr, ctx->irq_cnt));

        if ((qlge->intr_type == DDI_INTR_TYPE_MSIX) && intr) {
                ql_write_reg(qlge, REG_INTERRUPT_ENABLE, ctx->intr_dis_mask);
                var = ql_read_reg(qlge, REG_STATUS);
                return (var);
        }

        mutex_enter(&qlge->hw_mutex);
        ql_write_reg(qlge, REG_INTERRUPT_ENABLE, ctx->intr_dis_mask);
        var = ql_read_reg(qlge, REG_STATUS);
        mutex_exit(&qlge->hw_mutex);

        return (var);
}

/*
 * Disable a completion interrupt
 */
void
ql_disable_completion_interrupt(qlge_t *qlge, uint32_t intr)
{
        struct intr_ctx *ctx;

        ctx = qlge->intr_ctx + intr;
        QL_PRINT(DBG_INTR, ("%s(%d): To disable intr %d, irq_cnt %d \n",
            __func__, qlge->instance, intr, ctx->irq_cnt));
        /*
         * HW disables for us if we're MSIX multi interrupts and
         * it's not the default (zeroeth) interrupt.
         */
        if ((qlge->intr_type == DDI_INTR_TYPE_MSIX) && (intr != 0))
                return;

        if (ql_atomic_read_32(&ctx->irq_cnt) == 0) {
                mutex_enter(&qlge->hw_mutex);
                ql_write_reg(qlge, REG_INTERRUPT_ENABLE, ctx->intr_dis_mask);
                mutex_exit(&qlge->hw_mutex);
        }
        atomic_inc_32(&ctx->irq_cnt);
}

/*
 * Enable all completion interrupts
 */
static void
ql_enable_all_completion_interrupts(qlge_t *qlge)
{
        int i;
        uint32_t value = 1;

        for (i = 0; i < qlge->intr_cnt; i++) {
                /*
                 * Set the count to 1 for Legacy / MSI interrupts or for the
                 * default interrupt (0)
                 */
                if ((qlge->intr_type != DDI_INTR_TYPE_MSIX) || i == 0) {
                        ql_atomic_set_32(&qlge->intr_ctx[i].irq_cnt, value);
                }
                ql_enable_completion_interrupt(qlge, i);
        }
}

/*
 * Disable all completion interrupts
 */
static void
ql_disable_all_completion_interrupts(qlge_t *qlge)
{
        int i;
        uint32_t value = 0;

        for (i = 0; i < qlge->intr_cnt; i++) {

                /*
                 * Set the count to 0 for Legacy / MSI interrupts or for the
                 * default interrupt (0)
                 */
                if ((qlge->intr_type != DDI_INTR_TYPE_MSIX) || i == 0)
                        ql_atomic_set_32(&qlge->intr_ctx[i].irq_cnt, value);

                ql_disable_completion_interrupt(qlge, i);
        }
}

/*
 * Update small buffer queue producer index
 */
static void
ql_update_sbq_prod_idx(qlge_t *qlge, struct rx_ring *rx_ring)
{
        /* Update the buffer producer index */
        QL_PRINT(DBG_RX, ("sbq: updating prod idx = %d.\n",
            rx_ring->sbq_prod_idx));
        ql_write_doorbell_reg(qlge, rx_ring->sbq_prod_idx_db_reg,
            rx_ring->sbq_prod_idx);
}

/*
 * Update large buffer queue producer index
 */
static void
ql_update_lbq_prod_idx(qlge_t *qlge, struct rx_ring *rx_ring)
{
        /* Update the buffer producer index */
        QL_PRINT(DBG_RX, ("lbq: updating prod idx = %d.\n",
            rx_ring->lbq_prod_idx));
        ql_write_doorbell_reg(qlge, rx_ring->lbq_prod_idx_db_reg,
            rx_ring->lbq_prod_idx);
}

/*
 * Adds a small buffer descriptor to end of its in use list,
 * assumes sbq_lock is already taken
 */
static void
ql_add_sbuf_to_in_use_list(struct rx_ring *rx_ring,
    struct bq_desc *sbq_desc)
{
        uint32_t inuse_idx = rx_ring->sbq_use_tail;

        rx_ring->sbuf_in_use[inuse_idx] = sbq_desc;
        inuse_idx++;
        if (inuse_idx >= rx_ring->sbq_len)
                inuse_idx = 0;
        rx_ring->sbq_use_tail = inuse_idx;
        atomic_inc_32(&rx_ring->sbuf_in_use_count);
        ASSERT(rx_ring->sbuf_in_use_count <= rx_ring->sbq_len);
}

/*
 * Get a small buffer descriptor from its in use list
 */
static struct bq_desc *
ql_get_sbuf_from_in_use_list(struct rx_ring *rx_ring)
{
        struct bq_desc *sbq_desc = NULL;
        uint32_t inuse_idx;

        /* Pick from head of in use list */
        inuse_idx = rx_ring->sbq_use_head;
        sbq_desc = rx_ring->sbuf_in_use[inuse_idx];
        rx_ring->sbuf_in_use[inuse_idx] = NULL;

        if (sbq_desc != NULL) {
                inuse_idx++;
                if (inuse_idx >= rx_ring->sbq_len)
                        inuse_idx = 0;
                rx_ring->sbq_use_head = inuse_idx;
                atomic_dec_32(&rx_ring->sbuf_in_use_count);
                atomic_inc_32(&rx_ring->rx_indicate);
                sbq_desc->upl_inuse = 1;
                /* if mp is NULL */
                if (sbq_desc->mp == NULL) {
                        /* try to remap mp again */
                        sbq_desc->mp =
                            desballoc((unsigned char *)(sbq_desc->bd_dma.vaddr),
                            rx_ring->sbq_buf_size, 0, &sbq_desc->rx_recycle);
                }
        }

        return (sbq_desc);
}

/*
 * Add a small buffer descriptor to its free list
 */
static void
ql_add_sbuf_to_free_list(struct rx_ring *rx_ring,
    struct bq_desc *sbq_desc)
{
        uint32_t free_idx;

        /* Add to the end of free list */
        free_idx = rx_ring->sbq_free_tail;
        rx_ring->sbuf_free[free_idx] = sbq_desc;
        ASSERT(rx_ring->sbuf_free_count <= rx_ring->sbq_len);
        free_idx++;
        if (free_idx >= rx_ring->sbq_len)
                free_idx = 0;
        rx_ring->sbq_free_tail = free_idx;
        atomic_inc_32(&rx_ring->sbuf_free_count);
}

/*
 * Get a small buffer descriptor from its free list
 */
static struct bq_desc *
ql_get_sbuf_from_free_list(struct rx_ring *rx_ring)
{
        struct bq_desc *sbq_desc;
        uint32_t free_idx;

        free_idx = rx_ring->sbq_free_head;
        /* Pick from top of free list */
        sbq_desc = rx_ring->sbuf_free[free_idx];
        rx_ring->sbuf_free[free_idx] = NULL;
        if (sbq_desc != NULL) {
                free_idx++;
                if (free_idx >= rx_ring->sbq_len)
                        free_idx = 0;
                rx_ring->sbq_free_head = free_idx;
                atomic_dec_32(&rx_ring->sbuf_free_count);
        }
        return (sbq_desc);
}

/*
 * Add a large buffer descriptor to its in use list
 */
static void
ql_add_lbuf_to_in_use_list(struct rx_ring *rx_ring,
    struct bq_desc *lbq_desc)
{
        uint32_t inuse_idx;

        inuse_idx = rx_ring->lbq_use_tail;

        rx_ring->lbuf_in_use[inuse_idx] = lbq_desc;
        inuse_idx++;
        if (inuse_idx >= rx_ring->lbq_len)
                inuse_idx = 0;
        rx_ring->lbq_use_tail = inuse_idx;
        atomic_inc_32(&rx_ring->lbuf_in_use_count);
}

/*
 * Get a large buffer descriptor from in use list
 */
static struct bq_desc *
ql_get_lbuf_from_in_use_list(struct rx_ring *rx_ring)
{
        struct bq_desc *lbq_desc;
        uint32_t inuse_idx;

        /* Pick from head of in use list */
        inuse_idx = rx_ring->lbq_use_head;
        lbq_desc = rx_ring->lbuf_in_use[inuse_idx];
        rx_ring->lbuf_in_use[inuse_idx] = NULL;

        if (lbq_desc != NULL) {
                inuse_idx++;
                if (inuse_idx >= rx_ring->lbq_len)
                        inuse_idx = 0;
                rx_ring->lbq_use_head = inuse_idx;
                atomic_dec_32(&rx_ring->lbuf_in_use_count);
                atomic_inc_32(&rx_ring->rx_indicate);
                lbq_desc->upl_inuse = 1;

                /* if mp is NULL */
                if (lbq_desc->mp == NULL) {
                        /* try to remap mp again */
                        lbq_desc->mp =
                            desballoc((unsigned char *)(lbq_desc->bd_dma.vaddr),
                            rx_ring->lbq_buf_size, 0, &lbq_desc->rx_recycle);
                }
        }
        return (lbq_desc);
}

/*
 * Add a large buffer descriptor to free list
 */
static void
ql_add_lbuf_to_free_list(struct rx_ring *rx_ring,
    struct bq_desc *lbq_desc)
{
        uint32_t free_idx;

        /* Add to the end of free list */
        free_idx = rx_ring->lbq_free_tail;
        rx_ring->lbuf_free[free_idx] = lbq_desc;
        free_idx++;
        if (free_idx >= rx_ring->lbq_len)
                free_idx = 0;
        rx_ring->lbq_free_tail = free_idx;
        atomic_inc_32(&rx_ring->lbuf_free_count);
        ASSERT(rx_ring->lbuf_free_count <= rx_ring->lbq_len);
}

/*
 * Get a large buffer descriptor from its free list
 */
static struct bq_desc *
ql_get_lbuf_from_free_list(struct rx_ring *rx_ring)
{
        struct bq_desc *lbq_desc;
        uint32_t free_idx;

        free_idx = rx_ring->lbq_free_head;
        /* Pick from head of free list */
        lbq_desc = rx_ring->lbuf_free[free_idx];
        rx_ring->lbuf_free[free_idx] = NULL;

        if (lbq_desc != NULL) {
                free_idx++;
                if (free_idx >= rx_ring->lbq_len)
                        free_idx = 0;
                rx_ring->lbq_free_head = free_idx;
                atomic_dec_32(&rx_ring->lbuf_free_count);
        }
        return (lbq_desc);
}

/*
 * Add a small buffer descriptor to free list
 */
static void
ql_refill_sbuf_free_list(struct bq_desc *sbq_desc, boolean_t alloc_memory)
{
        struct rx_ring *rx_ring = sbq_desc->rx_ring;
        uint64_t *sbq_entry;
        qlge_t *qlge = (qlge_t *)rx_ring->qlge;
        /*
         * Sync access
         */
        mutex_enter(&rx_ring->sbq_lock);

        sbq_desc->upl_inuse = 0;

        /*
         * If we are freeing the buffers as a result of adapter unload, get out
         */
        if ((sbq_desc->free_buf != 0) ||
            (qlge->mac_flags == QL_MAC_DETACH)) {
                if (sbq_desc->free_buf == 0)
                        atomic_dec_32(&rx_ring->rx_indicate);
                mutex_exit(&rx_ring->sbq_lock);
                return;
        }
#ifdef QLGE_LOAD_UNLOAD
        if (rx_ring->rx_indicate == 0)
                cmn_err(CE_WARN, "sbq: indicate wrong");
#endif
#ifdef QLGE_TRACK_BUFFER_USAGE
        uint32_t sb_consumer_idx;
        uint32_t sb_producer_idx;
        uint32_t num_free_buffers;
        uint32_t temp;

        temp = ql_read_doorbell_reg(qlge, rx_ring->sbq_prod_idx_db_reg);
        sb_producer_idx = temp & 0x0000ffff;
        sb_consumer_idx = (temp >> 16);

        if (sb_consumer_idx > sb_producer_idx)
                num_free_buffers = NUM_SMALL_BUFFERS -
                    (sb_consumer_idx - sb_producer_idx);
        else
                num_free_buffers = sb_producer_idx - sb_consumer_idx;

        if (num_free_buffers < qlge->rx_sb_low_count[rx_ring->cq_id])
                qlge->rx_sb_low_count[rx_ring->cq_id] = num_free_buffers;

#endif

#ifdef QLGE_LOAD_UNLOAD
        if (rx_ring->rx_indicate > 0xFF000000)
                cmn_err(CE_WARN, "sbq: indicate(%d) wrong: %d mac_flags %d,"
                    " sbq_desc index %d.",
                    rx_ring->cq_id, rx_ring->rx_indicate, rx_ring->mac_flags,
                    sbq_desc->index);
#endif
        if (alloc_memory) {
                sbq_desc->mp =
                    desballoc((unsigned char *)(sbq_desc->bd_dma.vaddr),
                    rx_ring->sbq_buf_size, 0, &sbq_desc->rx_recycle);
                if (sbq_desc->mp == NULL) {
                        rx_ring->rx_failed_sbq_allocs++;
                }
        }

        /* Got the packet from the stack decrement rx_indicate count */
        atomic_dec_32(&rx_ring->rx_indicate);

        ql_add_sbuf_to_free_list(rx_ring, sbq_desc);

        /* Rearm if possible */
        if ((rx_ring->sbuf_free_count >= MIN_BUFFERS_FREE_COUNT) &&
            (qlge->mac_flags == QL_MAC_STARTED)) {
                sbq_entry = rx_ring->sbq_dma.vaddr;
                sbq_entry += rx_ring->sbq_prod_idx;

                while (rx_ring->sbuf_free_count > MIN_BUFFERS_ARM_COUNT) {
                        /* Get first one from free list */
                        sbq_desc = ql_get_sbuf_from_free_list(rx_ring);

                        *sbq_entry = cpu_to_le64(sbq_desc->bd_dma.dma_addr);
                        sbq_entry++;
                        rx_ring->sbq_prod_idx++;
                        if (rx_ring->sbq_prod_idx >= rx_ring->sbq_len) {
                                rx_ring->sbq_prod_idx = 0;
                                sbq_entry = rx_ring->sbq_dma.vaddr;
                        }
                        /* Add to end of in use list */
                        ql_add_sbuf_to_in_use_list(rx_ring, sbq_desc);
                }

                /* Update small buffer queue producer index */
                ql_update_sbq_prod_idx(qlge, rx_ring);
        }

        mutex_exit(&rx_ring->sbq_lock);
        QL_PRINT(DBG_RX_RING, ("%s(%d) exited, sbuf_free_count %d\n",
            __func__, qlge->instance, rx_ring->sbuf_free_count));
}

/*
 * rx recycle call back function
 */
static void
ql_release_to_sbuf_free_list(caddr_t p)
{
        struct bq_desc *sbq_desc = (struct bq_desc *)(void *)p;

        if (sbq_desc == NULL)
                return;
        ql_refill_sbuf_free_list(sbq_desc, B_TRUE);
}

/*
 * Add a large buffer descriptor to free list
 */
static void
ql_refill_lbuf_free_list(struct bq_desc *lbq_desc, boolean_t alloc_memory)
{
        struct rx_ring *rx_ring = lbq_desc->rx_ring;
        uint64_t *lbq_entry;
        qlge_t *qlge = rx_ring->qlge;

        /* Sync access */
        mutex_enter(&rx_ring->lbq_lock);

        lbq_desc->upl_inuse = 0;
        /*
         * If we are freeing the buffers as a result of adapter unload, get out
         */
        if ((lbq_desc->free_buf != 0) ||
            (qlge->mac_flags == QL_MAC_DETACH)) {
                if (lbq_desc->free_buf == 0)
                        atomic_dec_32(&rx_ring->rx_indicate);
                mutex_exit(&rx_ring->lbq_lock);
                return;
        }
#ifdef QLGE_LOAD_UNLOAD
        if (rx_ring->rx_indicate == 0)
                cmn_err(CE_WARN, "lbq: indicate wrong");
#endif
#ifdef QLGE_TRACK_BUFFER_USAGE
        uint32_t lb_consumer_idx;
        uint32_t lb_producer_idx;
        uint32_t num_free_buffers;
        uint32_t temp;

        temp = ql_read_doorbell_reg(qlge, rx_ring->lbq_prod_idx_db_reg);

        lb_producer_idx = temp & 0x0000ffff;
        lb_consumer_idx = (temp >> 16);

        if (lb_consumer_idx > lb_producer_idx)
                num_free_buffers = NUM_LARGE_BUFFERS -
                    (lb_consumer_idx - lb_producer_idx);
        else
                num_free_buffers = lb_producer_idx - lb_consumer_idx;

        if (num_free_buffers < qlge->rx_lb_low_count[rx_ring->cq_id]) {
                qlge->rx_lb_low_count[rx_ring->cq_id] = num_free_buffers;
        }
#endif

#ifdef QLGE_LOAD_UNLOAD
        if (rx_ring->rx_indicate > 0xFF000000)
                cmn_err(CE_WARN, "lbq: indicate(%d) wrong: %d mac_flags %d,"
                    "lbq_desc index %d",
                    rx_ring->cq_id, rx_ring->rx_indicate, rx_ring->mac_flags,
                    lbq_desc->index);
#endif
        if (alloc_memory) {
                lbq_desc->mp =
                    desballoc((unsigned char *)(lbq_desc->bd_dma.vaddr),
                    rx_ring->lbq_buf_size, 0, &lbq_desc->rx_recycle);
                if (lbq_desc->mp == NULL) {
                        rx_ring->rx_failed_lbq_allocs++;
                }
        }

        /* Got the packet from the stack decrement rx_indicate count */
        atomic_dec_32(&rx_ring->rx_indicate);

        ql_add_lbuf_to_free_list(rx_ring, lbq_desc);

        /* Rearm if possible */
        if ((rx_ring->lbuf_free_count >= MIN_BUFFERS_FREE_COUNT) &&
            (qlge->mac_flags == QL_MAC_STARTED)) {
                lbq_entry = rx_ring->lbq_dma.vaddr;
                lbq_entry += rx_ring->lbq_prod_idx;
                while (rx_ring->lbuf_free_count > MIN_BUFFERS_ARM_COUNT) {
                        /* Get first one from free list */
                        lbq_desc = ql_get_lbuf_from_free_list(rx_ring);

                        *lbq_entry = cpu_to_le64(lbq_desc->bd_dma.dma_addr);
                        lbq_entry++;
                        rx_ring->lbq_prod_idx++;
                        if (rx_ring->lbq_prod_idx >= rx_ring->lbq_len) {
                                rx_ring->lbq_prod_idx = 0;
                                lbq_entry = rx_ring->lbq_dma.vaddr;
                        }

                        /* Add to end of in use list */
                        ql_add_lbuf_to_in_use_list(rx_ring, lbq_desc);
                }

                /* Update large buffer queue producer index */
                ql_update_lbq_prod_idx(rx_ring->qlge, rx_ring);
        }

        mutex_exit(&rx_ring->lbq_lock);
        QL_PRINT(DBG_RX_RING, ("%s exitd, lbuf_free_count %d\n",
            __func__, rx_ring->lbuf_free_count));
}
/*
 * rx recycle call back function
 */
static void
ql_release_to_lbuf_free_list(caddr_t p)
{
        struct bq_desc *lbq_desc = (struct bq_desc *)(void *)p;

        if (lbq_desc == NULL)
                return;
        ql_refill_lbuf_free_list(lbq_desc, B_TRUE);
}

/*
 * free small buffer queue buffers
 */
static void
ql_free_sbq_buffers(struct rx_ring *rx_ring)
{
        struct bq_desc *sbq_desc;
        uint32_t i;
        uint32_t j = rx_ring->sbq_free_head;
        int  force_cnt = 0;

        for (i = 0; i < rx_ring->sbuf_free_count; i++) {
                sbq_desc = rx_ring->sbuf_free[j];
                sbq_desc->free_buf = 1;
                j++;
                if (j >= rx_ring->sbq_len) {
                        j = 0;
                }
                if (sbq_desc->mp != NULL) {
                        freemsg(sbq_desc->mp);
                        sbq_desc->mp = NULL;
                }
        }
        rx_ring->sbuf_free_count = 0;

        j = rx_ring->sbq_use_head;
        for (i = 0; i < rx_ring->sbuf_in_use_count; i++) {
                sbq_desc = rx_ring->sbuf_in_use[j];
                sbq_desc->free_buf = 1;
                j++;
                if (j >= rx_ring->sbq_len) {
                        j = 0;
                }
                if (sbq_desc->mp != NULL) {
                        freemsg(sbq_desc->mp);
                        sbq_desc->mp = NULL;
                }
        }
        rx_ring->sbuf_in_use_count = 0;

        sbq_desc = &rx_ring->sbq_desc[0];
        for (i = 0; i < rx_ring->sbq_len; i++, sbq_desc++) {
                /*
                 * Set flag so that the callback does not allocate a new buffer
                 */
                sbq_desc->free_buf = 1;
                if (sbq_desc->upl_inuse != 0) {
                        force_cnt++;
                }
                if (sbq_desc->bd_dma.dma_handle != NULL) {
                        ql_free_phys(&sbq_desc->bd_dma.dma_handle,
                            &sbq_desc->bd_dma.acc_handle);
                        sbq_desc->bd_dma.dma_handle = NULL;
                        sbq_desc->bd_dma.acc_handle = NULL;
                }
        }
#ifdef QLGE_LOAD_UNLOAD
        cmn_err(CE_NOTE, "sbq: free %d inuse %d force %d\n",
            rx_ring->sbuf_free_count, rx_ring->sbuf_in_use_count, force_cnt);
#endif
        if (rx_ring->sbuf_in_use != NULL) {
                kmem_free(rx_ring->sbuf_in_use, (rx_ring->sbq_len *
                    sizeof (struct bq_desc *)));
                rx_ring->sbuf_in_use = NULL;
        }

        if (rx_ring->sbuf_free != NULL) {
                kmem_free(rx_ring->sbuf_free, (rx_ring->sbq_len *
                    sizeof (struct bq_desc *)));
                rx_ring->sbuf_free = NULL;
        }
}

/* Allocate small buffers */
static int
ql_alloc_sbufs(qlge_t *qlge, struct rx_ring *rx_ring)
{
        struct bq_desc *sbq_desc;
        int i;
        ddi_dma_cookie_t dma_cookie;

        rx_ring->sbq_use_head = 0;
        rx_ring->sbq_use_tail = 0;
        rx_ring->sbuf_in_use_count = 0;
        rx_ring->sbq_free_head = 0;
        rx_ring->sbq_free_tail = 0;
        rx_ring->sbuf_free_count = 0;
        rx_ring->sbuf_free = kmem_zalloc(rx_ring->sbq_len *
            sizeof (struct bq_desc *), KM_NOSLEEP);
        if (rx_ring->sbuf_free == NULL) {
                cmn_err(CE_WARN,
                    "!%s: sbuf_free_list alloc: failed",
                    __func__);
                goto alloc_sbuf_err;
        }

        rx_ring->sbuf_in_use = kmem_zalloc(rx_ring->sbq_len *
            sizeof (struct bq_desc *), KM_NOSLEEP);
        if (rx_ring->sbuf_in_use == NULL) {
                cmn_err(CE_WARN,
                    "!%s: sbuf_inuse_list alloc: failed",
                    __func__);
                goto alloc_sbuf_err;
        }

        sbq_desc = &rx_ring->sbq_desc[0];

        for (i = 0; i < rx_ring->sbq_len; i++, sbq_desc++) {
                /* Allocate buffer */
                if (ql_alloc_phys_rbuf(qlge->dip, &sbq_desc->bd_dma.dma_handle,
                    &ql_buf_acc_attr,
                    DDI_DMA_READ | DDI_DMA_STREAMING,
                    &sbq_desc->bd_dma.acc_handle,
                    (size_t)rx_ring->sbq_buf_size,      /* mem size */
                    (size_t)0,                          /* default alignment */
                    (caddr_t *)&sbq_desc->bd_dma.vaddr,
                    &dma_cookie) != 0) {
                        cmn_err(CE_WARN,
                            "!%s: ddi_dma_alloc_handle: failed",
                            __func__);
                        goto alloc_sbuf_err;
                }

                /* Set context for Return buffer callback */
                sbq_desc->bd_dma.dma_addr = dma_cookie.dmac_laddress;
                sbq_desc->rx_recycle.free_func = ql_release_to_sbuf_free_list;
                sbq_desc->rx_recycle.free_arg  = (caddr_t)sbq_desc;
                sbq_desc->rx_ring = rx_ring;
                sbq_desc->upl_inuse = 0;
                sbq_desc->free_buf = 0;

                sbq_desc->mp =
                    desballoc((unsigned char *)(sbq_desc->bd_dma.vaddr),
                    rx_ring->sbq_buf_size, 0, &sbq_desc->rx_recycle);
                if (sbq_desc->mp == NULL) {
                        cmn_err(CE_WARN, "%s: desballoc() failed", __func__);
                        goto alloc_sbuf_err;
                }
                ql_add_sbuf_to_free_list(rx_ring, sbq_desc);
        }

        return (DDI_SUCCESS);

alloc_sbuf_err:
        ql_free_sbq_buffers(rx_ring);
        return (DDI_FAILURE);
}

static void
ql_free_lbq_buffers(struct rx_ring *rx_ring)
{
        struct bq_desc *lbq_desc;
        uint32_t i, j;
        int force_cnt = 0;

        j = rx_ring->lbq_free_head;
        for (i = 0; i < rx_ring->lbuf_free_count; i++) {
                lbq_desc = rx_ring->lbuf_free[j];
                lbq_desc->free_buf = 1;
                j++;
                if (j >= rx_ring->lbq_len)
                        j = 0;
                if (lbq_desc->mp != NULL) {
                        freemsg(lbq_desc->mp);
                        lbq_desc->mp = NULL;
                }
        }
        rx_ring->lbuf_free_count = 0;

        j = rx_ring->lbq_use_head;
        for (i = 0; i < rx_ring->lbuf_in_use_count; i++) {
                lbq_desc = rx_ring->lbuf_in_use[j];
                lbq_desc->free_buf = 1;
                j++;
                if (j >= rx_ring->lbq_len) {
                        j = 0;
                }
                if (lbq_desc->mp != NULL) {
                        freemsg(lbq_desc->mp);
                        lbq_desc->mp = NULL;
                }
        }
        rx_ring->lbuf_in_use_count = 0;

        lbq_desc = &rx_ring->lbq_desc[0];
        for (i = 0; i < rx_ring->lbq_len; i++, lbq_desc++) {
                /* Set flag so that callback will not allocate a new buffer */
                lbq_desc->free_buf = 1;
                if (lbq_desc->upl_inuse != 0) {
                        force_cnt++;
                }
                if (lbq_desc->bd_dma.dma_handle != NULL) {
                        ql_free_phys(&lbq_desc->bd_dma.dma_handle,
                            &lbq_desc->bd_dma.acc_handle);
                        lbq_desc->bd_dma.dma_handle = NULL;
                        lbq_desc->bd_dma.acc_handle = NULL;
                }
        }
#ifdef QLGE_LOAD_UNLOAD
        if (force_cnt) {
                cmn_err(CE_WARN, "lbq: free %d inuse %d force %d",
                    rx_ring->lbuf_free_count, rx_ring->lbuf_in_use_count,
                    force_cnt);
        }
#endif
        if (rx_ring->lbuf_in_use != NULL) {
                kmem_free(rx_ring->lbuf_in_use, (rx_ring->lbq_len *
                    sizeof (struct bq_desc *)));
                rx_ring->lbuf_in_use = NULL;
        }

        if (rx_ring->lbuf_free != NULL) {
                kmem_free(rx_ring->lbuf_free, (rx_ring->lbq_len *
                    sizeof (struct bq_desc *)));
                rx_ring->lbuf_free = NULL;
        }
}

/* Allocate large buffers */
static int
ql_alloc_lbufs(qlge_t *qlge, struct rx_ring *rx_ring)
{
        struct bq_desc *lbq_desc;
        ddi_dma_cookie_t dma_cookie;
        int i;
        uint32_t lbq_buf_size;

        rx_ring->lbq_use_head = 0;
        rx_ring->lbq_use_tail = 0;
        rx_ring->lbuf_in_use_count = 0;
        rx_ring->lbq_free_head = 0;
        rx_ring->lbq_free_tail = 0;
        rx_ring->lbuf_free_count = 0;
        rx_ring->lbuf_free = kmem_zalloc(rx_ring->lbq_len *
            sizeof (struct bq_desc *), KM_NOSLEEP);
        if (rx_ring->lbuf_free == NULL) {
                cmn_err(CE_WARN,
                    "!%s: lbuf_free_list alloc: failed",
                    __func__);
                goto alloc_lbuf_err;
        }

        rx_ring->lbuf_in_use = kmem_zalloc(rx_ring->lbq_len *
            sizeof (struct bq_desc *), KM_NOSLEEP);

        if (rx_ring->lbuf_in_use == NULL) {
                cmn_err(CE_WARN,
                    "!%s: lbuf_inuse_list alloc: failed",
                    __func__);
                goto alloc_lbuf_err;
        }

        lbq_buf_size = (qlge->mtu == ETHERMTU) ?
            LRG_BUF_NORMAL_SIZE : LRG_BUF_JUMBO_SIZE;

        lbq_desc = &rx_ring->lbq_desc[0];
        for (i = 0; i < rx_ring->lbq_len; i++, lbq_desc++) {
                rx_ring->lbq_buf_size = lbq_buf_size;
                /* Allocate buffer */
                if (ql_alloc_phys_rbuf(qlge->dip, &lbq_desc->bd_dma.dma_handle,
                    &ql_buf_acc_attr,
                    DDI_DMA_READ | DDI_DMA_STREAMING,
                    &lbq_desc->bd_dma.acc_handle,
                    (size_t)rx_ring->lbq_buf_size,  /* mem size */
                    (size_t)0, /* default alignment */
                    (caddr_t *)&lbq_desc->bd_dma.vaddr,
                    &dma_cookie) != 0) {
                        cmn_err(CE_WARN,
                            "!%s: ddi_dma_alloc_handle: failed",
                            __func__);
                        goto alloc_lbuf_err;
                }

                /* Set context for Return buffer callback */
                lbq_desc->bd_dma.dma_addr = dma_cookie.dmac_laddress;
                lbq_desc->rx_recycle.free_func = ql_release_to_lbuf_free_list;
                lbq_desc->rx_recycle.free_arg  = (caddr_t)lbq_desc;
                lbq_desc->rx_ring = rx_ring;
                lbq_desc->upl_inuse = 0;
                lbq_desc->free_buf = 0;

                lbq_desc->mp =
                    desballoc((unsigned char *)(lbq_desc->bd_dma.vaddr),
                    rx_ring->lbq_buf_size, 0, &lbq_desc->rx_recycle);
                if (lbq_desc->mp == NULL) {
                        cmn_err(CE_WARN, "%s: desballoc() failed", __func__);
                        goto alloc_lbuf_err;
                }
                ql_add_lbuf_to_free_list(rx_ring, lbq_desc);
        } /* For all large buffers */

        return (DDI_SUCCESS);

alloc_lbuf_err:
        ql_free_lbq_buffers(rx_ring);
        return (DDI_FAILURE);
}

/*
 * Free rx buffers
 */
static void
ql_free_rx_buffers(qlge_t *qlge)
{
        int i;
        struct rx_ring *rx_ring;

        for (i = 0; i < qlge->rx_ring_count; i++) {
                rx_ring = &qlge->rx_ring[i];
                if (rx_ring->type != TX_Q) {
                        ql_free_lbq_buffers(rx_ring);
                        ql_free_sbq_buffers(rx_ring);
                }
        }
}

/*
 * Allocate rx buffers
 */
static int
ql_alloc_rx_buffers(qlge_t *qlge)
{
        struct rx_ring *rx_ring;
        int i;

        for (i = 0; i < qlge->rx_ring_count; i++) {
                rx_ring = &qlge->rx_ring[i];
                if (rx_ring->type != TX_Q) {
                        if (ql_alloc_sbufs(qlge, rx_ring) != DDI_SUCCESS)
                                goto alloc_err;
                        if (ql_alloc_lbufs(qlge, rx_ring) != DDI_SUCCESS)
                                goto alloc_err;
                }
        }
#ifdef QLGE_TRACK_BUFFER_USAGE
        for (i = 0; i < qlge->rx_ring_count; i++) {
                if (qlge->rx_ring[i].type == RX_Q) {
                        qlge->rx_sb_low_count[i] = NUM_SMALL_BUFFERS;
                        qlge->rx_lb_low_count[i] = NUM_LARGE_BUFFERS;
                }
                qlge->cq_low_count[i] = NUM_RX_RING_ENTRIES;
        }
#endif
        return (DDI_SUCCESS);

alloc_err:
        ql_free_rx_buffers(qlge);
        return (DDI_FAILURE);
}

/*
 * Initialize large buffer queue ring
 */
static void
ql_init_lbq_ring(struct rx_ring *rx_ring)
{
        uint16_t i;
        struct bq_desc *lbq_desc;

        bzero(rx_ring->lbq_desc, rx_ring->lbq_len * sizeof (struct bq_desc));
        for (i = 0; i < rx_ring->lbq_len; i++) {
                lbq_desc = &rx_ring->lbq_desc[i];
                lbq_desc->index = i;
        }
}

/*
 * Initialize small buffer queue ring
 */
static void
ql_init_sbq_ring(struct rx_ring *rx_ring)
{
        uint16_t i;
        struct bq_desc *sbq_desc;

        bzero(rx_ring->sbq_desc, rx_ring->sbq_len * sizeof (struct bq_desc));
        for (i = 0; i < rx_ring->sbq_len; i++) {
                sbq_desc = &rx_ring->sbq_desc[i];
                sbq_desc->index = i;
        }
}

/*
 * Calculate the pseudo-header checksum if hardware can not do
 */
static void
ql_pseudo_cksum(uint8_t *buf)
{
        uint32_t cksum;
        uint16_t iphl;
        uint16_t proto;

        iphl = (uint16_t)(4 * (buf[0] & 0xF));
        cksum = (((uint16_t)buf[2])<<8) + buf[3] - iphl;
        cksum += proto = buf[9];
        cksum += (((uint16_t)buf[12])<<8) + buf[13];
        cksum += (((uint16_t)buf[14])<<8) + buf[15];
        cksum += (((uint16_t)buf[16])<<8) + buf[17];
        cksum += (((uint16_t)buf[18])<<8) + buf[19];
        cksum = (cksum>>16) + (cksum & 0xFFFF);
        cksum = (cksum>>16) + (cksum & 0xFFFF);

        /*
         * Point it to the TCP/UDP header, and
         * update the checksum field.
         */
        buf += iphl + ((proto == IPPROTO_TCP) ?
            TCP_CKSUM_OFFSET : UDP_CKSUM_OFFSET);

        *(uint16_t *)(void *)buf = (uint16_t)htons((uint16_t)cksum);

}

/*
 * Transmit an incoming packet.
 */
mblk_t *
ql_ring_tx(void *arg, mblk_t *mp)
{
        struct tx_ring *tx_ring = (struct tx_ring *)arg;
        qlge_t *qlge = tx_ring->qlge;
        mblk_t *next;
        int rval;
        uint32_t tx_count = 0;

        if (qlge->port_link_state == LS_DOWN) {
                /* can not send message while link is down */
                mblk_t *tp;

                while (mp != NULL) {
                        tp = mp->b_next;
                        mp->b_next = NULL;
                        freemsg(mp);
                        mp = tp;
                }
                goto exit;
        }

        mutex_enter(&tx_ring->tx_lock);
        /* if mac is not started, driver is not ready, can not send */
        if (tx_ring->mac_flags != QL_MAC_STARTED) {
                cmn_err(CE_WARN, "%s(%d)ring not started, mode %d "
                    " return packets",
                    __func__, qlge->instance, tx_ring->mac_flags);
                mutex_exit(&tx_ring->tx_lock);
                goto exit;
        }

        /* we must try to send all */
        while (mp != NULL) {
                /*
                 * if number of available slots is less than a threshold,
                 * then quit
                 */
                if (tx_ring->tx_free_count <= TX_STOP_THRESHOLD) {
                        tx_ring->queue_stopped = 1;
                        rval = DDI_FAILURE;
#ifdef QLGE_LOAD_UNLOAD
                        cmn_err(CE_WARN, "%s(%d) no resources",
                            __func__, qlge->instance);
#endif
                        tx_ring->defer++;
                        /*
                         * If we return the buffer back we are expected to call
                         * mac_tx_ring_update() when resources are available
                         */
                        break;
                }

                next = mp->b_next;
                mp->b_next = NULL;

                rval = ql_send_common(tx_ring, mp);

                if (rval != DDI_SUCCESS) {
                        mp->b_next = next;
                        break;
                }
                tx_count++;
                mp = next;
        }

        /*
         * After all msg blocks are mapped or copied to tx buffer,
         * trigger the hardware to send!
         */
        if (tx_count > 0) {
                ql_write_doorbell_reg(tx_ring->qlge, tx_ring->prod_idx_db_reg,
                    tx_ring->prod_idx);
        }

        mutex_exit(&tx_ring->tx_lock);
exit:
        return (mp);
}


/*
 * This function builds an mblk list for the given inbound
 * completion.
 */

static mblk_t *
ql_build_rx_mp(qlge_t *qlge, struct rx_ring *rx_ring,
    struct ib_mac_iocb_rsp *ib_mac_rsp)
{
        mblk_t *mp = NULL;
        mblk_t *mp1 = NULL;     /* packet header */
        mblk_t *mp2 = NULL;     /* packet content */
        struct bq_desc *lbq_desc;
        struct bq_desc *sbq_desc;
        uint32_t err_flag = (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_ERR_MASK);
        uint32_t payload_len = le32_to_cpu(ib_mac_rsp->data_len);
        uint32_t header_len = le32_to_cpu(ib_mac_rsp->hdr_len);
        uint32_t pkt_len = payload_len + header_len;
        uint32_t done;
        uint64_t *curr_ial_ptr;
        uint32_t ial_data_addr_low;
        uint32_t actual_data_addr_low;
        mblk_t *mp_ial = NULL;  /* ial chained packets */
        uint32_t size;
        uint32_t cp_offset;
        boolean_t rx_copy = B_FALSE;
        mblk_t *tp = NULL;

        /*
         * Check if error flags are set
         */
        if (err_flag != 0) {
                if ((err_flag & IB_MAC_IOCB_RSP_ERR_OVERSIZE) != 0)
                        rx_ring->frame_too_long++;
                if ((err_flag & IB_MAC_IOCB_RSP_ERR_UNDERSIZE) != 0)
                        rx_ring->frame_too_short++;
                if ((err_flag & IB_MAC_IOCB_RSP_ERR_CRC) != 0)
                        rx_ring->fcs_err++;
#ifdef QLGE_LOAD_UNLOAD
                cmn_err(CE_WARN, "bad packet, type 0x%x", err_flag);
#endif
                QL_DUMP(DBG_RX, "qlge_ring_rx: bad response iocb dump\n",
                    (uint8_t *)ib_mac_rsp, 8,
                    (size_t)sizeof (struct ib_mac_iocb_rsp));
        }

        /* header should not be in large buffer */
        if (ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HL) {
                cmn_err(CE_WARN, "header in large buffer or invalid!");
                err_flag |= 1;
        }
        /* if whole packet is too big than rx buffer size */
        if (pkt_len > qlge->max_frame_size) {
                cmn_err(CE_WARN, "ql_build_rx_mpframe too long(%d)!", pkt_len);
                err_flag |= 1;
        }
        if (qlge->rx_copy ||
            (rx_ring->sbuf_in_use_count <= qlge->rx_copy_threshold) ||
            (rx_ring->lbuf_in_use_count <= qlge->rx_copy_threshold)) {
                rx_copy = B_TRUE;
        }

        /* if using rx copy mode, we need to allocate a big enough buffer */
        if (rx_copy) {
                qlge->stats.norcvbuf++;
                tp = allocb(payload_len + header_len + qlge->ip_hdr_offset,
                    BPRI_MED);
                if (tp == NULL) {
                        cmn_err(CE_WARN, "rx copy failed to allocate memory");
                } else {
                        tp->b_rptr += qlge->ip_hdr_offset;
                }
        }
        /*
         * Handle the header buffer if present.
         * packet header must be valid and saved in one small buffer
         * broadcast/multicast packets' headers not splitted
         */
        if ((ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HV) &&
            (ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HS)) {
                QL_PRINT(DBG_RX, ("Header of %d bytes in small buffer.\n",
                    header_len));
                /* Sync access */
                sbq_desc = ql_get_sbuf_from_in_use_list(rx_ring);

                ASSERT(sbq_desc != NULL);

                /*
                 * Validate addresses from the ASIC with the
                 * expected sbuf address
                 */
                if (cpu_to_le64(sbq_desc->bd_dma.dma_addr)
                    != ib_mac_rsp->hdr_addr) {
                        /* Small buffer address mismatch */
                        cmn_err(CE_WARN, "%s(%d) ring%d packet saved"
                            " in wrong small buffer",
                            __func__, qlge->instance, rx_ring->cq_id);
                        goto fatal_error;
                }
                /* get this packet */
                mp1 = sbq_desc->mp;
                /* Flush DMA'd data */
                (void) ddi_dma_sync(sbq_desc->bd_dma.dma_handle,
                    0, header_len, DDI_DMA_SYNC_FORKERNEL);

                if ((err_flag != 0)|| (mp1 == NULL)) {
                        /* failed on this packet, put it back for re-arming */
#ifdef QLGE_LOAD_UNLOAD
                        cmn_err(CE_WARN, "get header from small buffer fail");
#endif
                        ql_refill_sbuf_free_list(sbq_desc, B_FALSE);
                        mp1 = NULL;
                } else if (rx_copy) {
                        if (tp != NULL) {
                                bcopy(sbq_desc->bd_dma.vaddr, tp->b_rptr,
                                    header_len);
                        }
                        ql_refill_sbuf_free_list(sbq_desc, B_FALSE);
                        mp1 = NULL;
                } else {
                        if ((qlge->ip_hdr_offset != 0)&&
                            (header_len < SMALL_BUFFER_SIZE)) {
                                /*
                                 * copy entire header to a 2 bytes boundary
                                 * address for 8100 adapters so that the IP
                                 * header can be on a 4 byte boundary address
                                 */
                                bcopy(mp1->b_rptr,
                                    (mp1->b_rptr + SMALL_BUFFER_SIZE +
                                    qlge->ip_hdr_offset),
                                    header_len);
                                mp1->b_rptr += SMALL_BUFFER_SIZE +
                                    qlge->ip_hdr_offset;
                        }

                        /*
                         * Adjust the mp payload_len to match
                         * the packet header payload_len
                         */
                        mp1->b_wptr = mp1->b_rptr + header_len;
                        mp1->b_next = mp1->b_cont = NULL;
                        QL_DUMP(DBG_RX, "\t RX packet header dump:\n",
                            (uint8_t *)mp1->b_rptr, 8, header_len);
                }
        }

        /*
         * packet data or whole packet can be in small or one or
         * several large buffer(s)
         */
        if (ib_mac_rsp->flags3 & IB_MAC_IOCB_RSP_DS) {
                /*
                 * The data is in a single small buffer.
                 */
                sbq_desc = ql_get_sbuf_from_in_use_list(rx_ring);

                ASSERT(sbq_desc != NULL);

                QL_PRINT(DBG_RX,
                    ("%d bytes in a single small buffer, sbq_desc = %p, "
                    "sbq_desc->bd_dma.dma_addr = %x,"
                    " ib_mac_rsp->data_addr = %x, mp = %p\n",
                    payload_len, sbq_desc, sbq_desc->bd_dma.dma_addr,
                    ib_mac_rsp->data_addr, sbq_desc->mp));

                /*
                 * Validate  addresses from the ASIC with the
                 * expected sbuf address
                 */
                if (cpu_to_le64(sbq_desc->bd_dma.dma_addr)
                    != ib_mac_rsp->data_addr) {
                        /* Small buffer address mismatch */
                        cmn_err(CE_WARN, "%s(%d) ring%d packet saved"
                            " in wrong small buffer",
                            __func__, qlge->instance, rx_ring->cq_id);
                        goto fatal_error;
                }
                /* get this packet */
                mp2 = sbq_desc->mp;
                (void) ddi_dma_sync(sbq_desc->bd_dma.dma_handle,
                    0, payload_len, DDI_DMA_SYNC_FORKERNEL);
                if ((err_flag != 0) || (mp2 == NULL)) {
#ifdef QLGE_LOAD_UNLOAD
                        /* failed on this packet, put it back for re-arming */
                        cmn_err(CE_WARN, "ignore bad data from small buffer");
#endif
                        ql_refill_sbuf_free_list(sbq_desc, B_FALSE);
                        mp2 = NULL;
                } else if (rx_copy) {
                        if (tp != NULL) {
                                bcopy(sbq_desc->bd_dma.vaddr,
                                    tp->b_rptr + header_len, payload_len);
                                tp->b_wptr =
                                    tp->b_rptr + header_len + payload_len;
                        }
                        ql_refill_sbuf_free_list(sbq_desc, B_FALSE);
                        mp2 = NULL;
                } else {
                        /* Adjust the buffer length to match the payload_len */
                        mp2->b_wptr = mp2->b_rptr + payload_len;
                        mp2->b_next = mp2->b_cont = NULL;
                        /* Flush DMA'd data */
                        QL_DUMP(DBG_RX, "\t RX packet payload dump:\n",
                            (uint8_t *)mp2->b_rptr, 8, payload_len);
                        /*
                         * if payload is too small , copy to
                         * the end of packet header
                         */
                        if ((mp1 != NULL) &&
                            (payload_len <= qlge->payload_copy_thresh) &&
                            (pkt_len <
                            (SMALL_BUFFER_SIZE - qlge->ip_hdr_offset))) {
                                bcopy(mp2->b_rptr, mp1->b_wptr, payload_len);
                                mp1->b_wptr += payload_len;
                                freemsg(mp2);
                                mp2 = NULL;
                        }
                }
        } else if (ib_mac_rsp->flags3 & IB_MAC_IOCB_RSP_DL) {
                /*
                 * The data is in a single large buffer.
                 */
                lbq_desc = ql_get_lbuf_from_in_use_list(rx_ring);

                QL_PRINT(DBG_RX,
                    ("%d bytes in a single large buffer, lbq_desc = %p, "
                    "lbq_desc->bd_dma.dma_addr = %x,"
                    " ib_mac_rsp->data_addr = %x, mp = %p\n",
                    payload_len, lbq_desc, lbq_desc->bd_dma.dma_addr,
                    ib_mac_rsp->data_addr, lbq_desc->mp));

                ASSERT(lbq_desc != NULL);

                /*
                 * Validate  addresses from the ASIC with
                 * the expected lbuf address
                 */
                if (cpu_to_le64(lbq_desc->bd_dma.dma_addr)
                    != ib_mac_rsp->data_addr) {
                        /* Large buffer address mismatch */
                        cmn_err(CE_WARN, "%s(%d) ring%d packet saved"
                            " in wrong large buffer",
                            __func__, qlge->instance, rx_ring->cq_id);
                        goto fatal_error;
                }
                mp2 = lbq_desc->mp;
                /* Flush DMA'd data */
                (void) ddi_dma_sync(lbq_desc->bd_dma.dma_handle,
                    0, payload_len, DDI_DMA_SYNC_FORKERNEL);
                if ((err_flag != 0) || (mp2 == NULL)) {
#ifdef QLGE_LOAD_UNLOAD
                        cmn_err(CE_WARN, "ignore bad data from large buffer");
#endif
                        /* failed on this packet, put it back for re-arming */
                        ql_refill_lbuf_free_list(lbq_desc, B_FALSE);
                        mp2 = NULL;
                } else if (rx_copy) {
                        if (tp != NULL) {
                                bcopy(lbq_desc->bd_dma.vaddr,
                                    tp->b_rptr + header_len, payload_len);
                                tp->b_wptr =
                                    tp->b_rptr + header_len + payload_len;
                        }
                        ql_refill_lbuf_free_list(lbq_desc, B_FALSE);
                        mp2 = NULL;
                } else {
                        /*
                         * Adjust the buffer length to match
                         * the packet payload_len
                         */
                        mp2->b_wptr = mp2->b_rptr + payload_len;
                        mp2->b_next = mp2->b_cont = NULL;
                        QL_DUMP(DBG_RX, "\t RX packet payload dump:\n",
                            (uint8_t *)mp2->b_rptr, 8, payload_len);
                        /*
                         * if payload is too small , copy to
                         * the end of packet header
                         */
                        if ((mp1 != NULL) &&
                            (payload_len <= qlge->payload_copy_thresh) &&
                            (pkt_len<
                            (SMALL_BUFFER_SIZE - qlge->ip_hdr_offset))) {
                                bcopy(mp2->b_rptr, mp1->b_wptr, payload_len);
                                mp1->b_wptr += payload_len;
                                freemsg(mp2);
                                mp2 = NULL;
                        }
                }
        } else if (payload_len) { /* ial case */
                /*
                 * payload available but not in sml nor lrg buffer,
                 * so, it is saved in IAL
                 */
#ifdef QLGE_LOAD_UNLOAD
                cmn_err(CE_NOTE, "packet chained in IAL \n");
#endif
                /* lrg buf addresses are saved in one small buffer */
                sbq_desc = ql_get_sbuf_from_in_use_list(rx_ring);
                curr_ial_ptr = (uint64_t *)sbq_desc->bd_dma.vaddr;
                done = 0;
                cp_offset = 0;

                while (!done) {
                        ial_data_addr_low =
                            (uint32_t)(le64_to_cpu(*curr_ial_ptr) &
                            0xFFFFFFFE);
                        /* check if this is the last packet fragment */
                        done = (uint32_t)(le64_to_cpu(*curr_ial_ptr) & 1);
                        curr_ial_ptr++;
                        /*
                         * The data is in one or several large buffer(s).
                         */
                        lbq_desc = ql_get_lbuf_from_in_use_list(rx_ring);
                        actual_data_addr_low =
                            (uint32_t)(lbq_desc->bd_dma.dma_addr &
                            0xFFFFFFFE);
                        if (ial_data_addr_low != actual_data_addr_low) {
                                cmn_err(CE_WARN,
                                    "packet saved in wrong ial lrg buffer"
                                    " expected %x, actual %lx",
                                    ial_data_addr_low,
                                    (uintptr_t)lbq_desc->bd_dma.dma_addr);
                                goto fatal_error;
                        }

                        size = (payload_len < rx_ring->lbq_buf_size)?
                            payload_len : rx_ring->lbq_buf_size;
                        payload_len -= size;
                        mp2 = lbq_desc->mp;
                        if ((err_flag != 0) || (mp2 == NULL)) {
#ifdef QLGE_LOAD_UNLOAD
                                cmn_err(CE_WARN,
                                    "ignore bad data from large buffer");
#endif
                                ql_refill_lbuf_free_list(lbq_desc, B_FALSE);
                                mp2 = NULL;
                        } else if (rx_copy) {
                                if (tp != NULL) {
                                        (void) ddi_dma_sync(
                                            lbq_desc->bd_dma.dma_handle,
                                            0, size, DDI_DMA_SYNC_FORKERNEL);
                                        bcopy(lbq_desc->bd_dma.vaddr,
                                            tp->b_rptr + header_len + cp_offset,
                                            size);
                                        tp->b_wptr =
                                            tp->b_rptr + size + cp_offset +
                                            header_len;
                                        cp_offset += size;
                                }
                                ql_refill_lbuf_free_list(lbq_desc, B_FALSE);
                                mp2 = NULL;
                        } else {
                                if (mp_ial == NULL) {
                                        mp_ial = mp2;
                                } else {
                                        linkb(mp_ial, mp2);
                                }

                                mp2->b_next = NULL;
                                mp2->b_cont = NULL;
                                mp2->b_wptr = mp2->b_rptr + size;
                                /* Flush DMA'd data */
                                (void) ddi_dma_sync(lbq_desc->bd_dma.dma_handle,
                                    0, size, DDI_DMA_SYNC_FORKERNEL);
                                QL_PRINT(DBG_RX, ("ial %d payload received \n",
                                    size));
                                QL_DUMP(DBG_RX, "\t Mac data dump:\n",
                                    (uint8_t *)mp2->b_rptr, 8, size);
                        }
                }
                if (err_flag != 0) {
#ifdef QLGE_LOAD_UNLOAD
                        /* failed on this packet, put it back for re-arming */
                        cmn_err(CE_WARN, "ignore bad data from small buffer");
#endif
                        ql_refill_sbuf_free_list(sbq_desc, B_FALSE);
                } else {
                        mp2 = mp_ial;
                        freemsg(sbq_desc->mp);
                }
        }
        /*
         * some packets' hdr not split, then send mp2 upstream, otherwise,
         * concatenate message block mp2 to the tail of message header, mp1
         */
        if (!err_flag) {
                if (rx_copy) {
                        if (tp != NULL) {
                                tp->b_next = NULL;
                                tp->b_cont = NULL;
                                tp->b_wptr = tp->b_rptr +
                                    header_len + payload_len;
                        }
                        mp = tp;
                } else {
                        if (mp1) {
                                if (mp2) {
                                        QL_PRINT(DBG_RX,
                                            ("packet in mp1 and mp2\n"));
                                        /* mp1->b_cont = mp2; */
                                        linkb(mp1, mp2);
                                        mp = mp1;
                                } else {
                                        QL_PRINT(DBG_RX,
                                            ("packet in mp1 only\n"));
                                        mp = mp1;
                                }
                        } else if (mp2) {
                                QL_PRINT(DBG_RX, ("packet in mp2 only\n"));
                                mp = mp2;
                        }
                }
        }
        return (mp);

fatal_error:
        /* fatal Error! */
        if (qlge->fm_enable) {
                ddi_fm_service_impact(qlge->dip, DDI_SERVICE_DEGRADED);
                ql_fm_ereport(qlge, DDI_FM_DEVICE_INVAL_STATE);
                atomic_or_32(&qlge->flags, ADAPTER_ERROR);
        }
        if (tp) {
                freemsg(tp);
        }

        /* *mp->b_wptr = 0; */
        ql_wake_asic_reset_soft_intr(qlge);
        return (NULL);

}

/*
 * Bump completion queue consumer index.
 */
static void
ql_update_cq(struct rx_ring *rx_ring)
{
        rx_ring->cnsmr_idx++;
        rx_ring->curr_entry++;
        if (rx_ring->cnsmr_idx >= rx_ring->cq_len) {
                rx_ring->cnsmr_idx = 0;
                rx_ring->curr_entry = rx_ring->cq_dma.vaddr;
        }
}

/*
 * Update completion queue consumer index.
 */
static void
ql_write_cq_idx(struct rx_ring *rx_ring)
{
        qlge_t *qlge = rx_ring->qlge;

        ql_write_doorbell_reg(qlge, rx_ring->cnsmr_idx_db_reg,
            rx_ring->cnsmr_idx);
}

/*
 * Processes a SYS-Chip Event Notification Completion Event.
 * The incoming notification event that describes a link up/down
 * or some sorts of error happens.
 */
static void
ql_process_chip_ae_intr(qlge_t *qlge,
    struct ib_sys_event_iocb_rsp *ib_sys_event_rsp_ptr)
{
        uint8_t eventType = ib_sys_event_rsp_ptr->event_type;
        uint32_t soft_req = 0;

        switch (eventType) {
                case SYS_EVENT_PORT_LINK_UP:    /* 0x0h */
                        QL_PRINT(DBG_MBX, ("Port Link Up\n"));
                        break;

                case SYS_EVENT_PORT_LINK_DOWN:  /* 0x1h */
                        QL_PRINT(DBG_MBX, ("Port Link Down\n"));
                        break;

                case SYS_EVENT_MULTIPLE_CAM_HITS : /* 0x6h */
                        cmn_err(CE_WARN, "A multiple CAM hits look up error "
                            "occurred");
                        soft_req |= NEED_HW_RESET;
                        break;

                case SYS_EVENT_SOFT_ECC_ERR:    /* 0x7h */
                        cmn_err(CE_WARN, "Soft ECC error detected");
                        soft_req |= NEED_HW_RESET;
                        break;

                case SYS_EVENT_MGMT_FATAL_ERR:  /* 0x8h */
                        cmn_err(CE_WARN, "Management (MPI) Processor fatal"
                            " error occured");
                        soft_req |= NEED_MPI_RESET;
                        break;

                case SYS_EVENT_MAC_INTERRUPT:   /* 0x9h */
                        QL_PRINT(DBG_MBX, ("MAC Interrupt"));
                        break;

                case SYS_EVENT_PCI_ERR_READING_SML_LRG_BUF:     /* 0x40h */
                        cmn_err(CE_WARN, "PCI Error reading small/large "
                            "buffers occured");
                        soft_req |= NEED_HW_RESET;
                        break;

                default:
                        QL_PRINT(DBG_RX, ("%s(%d) unknown Sys Event: "
                            "type 0x%x occured",
                            __func__, qlge->instance, eventType));
                        break;
        }

        if ((soft_req & NEED_MPI_RESET) != 0) {
                ql_wake_mpi_reset_soft_intr(qlge);
                if (qlge->fm_enable) {
                        ql_fm_ereport(qlge, DDI_FM_DEVICE_INVAL_STATE);
                        ddi_fm_service_impact(qlge->dip, DDI_SERVICE_DEGRADED);
                }
        } else if ((soft_req & NEED_HW_RESET) != 0) {
                ql_wake_asic_reset_soft_intr(qlge);
                if (qlge->fm_enable) {
                        ql_fm_ereport(qlge, DDI_FM_DEVICE_INVAL_STATE);
                        ddi_fm_service_impact(qlge->dip, DDI_SERVICE_DEGRADED);
                }
        }
}

/*
 * set received packet checksum flag
 */
void
ql_set_rx_cksum(mblk_t *mp, struct ib_mac_iocb_rsp *net_rsp)
{
        uint32_t flags;

        /* Not TCP or UDP packet? nothing more to do */
        if (((net_rsp->flags2 & IB_MAC_IOCB_RSP_T) == 0) &&
            ((net_rsp->flags2 & IB_MAC_IOCB_RSP_U) == 0))
                return;

        /* No CKO support for IPv6 */
        if ((net_rsp->flags3 & IB_MAC_IOCB_RSP_V6) != 0)
                return;

        /*
         * If checksum error, don't set flags; stack will calculate
         * checksum, detect the error and update statistics
         */
        if (((net_rsp->flags1 & IB_MAC_IOCB_RSP_TE) != 0) ||
            ((net_rsp->flags1 & IB_MAC_IOCB_RSP_IE) != 0))
                return;

        /* TCP or UDP packet and checksum valid */
        if (((net_rsp->flags2 & IB_MAC_IOCB_RSP_T) != 0) &&
            ((net_rsp->flags1 & IB_MAC_IOCB_RSP_NU) == 0)) {
                flags = HCK_FULLCKSUM_OK;
                mac_hcksum_set(mp, 0, 0, 0, 0, flags);
        }
        if (((net_rsp->flags2 & IB_MAC_IOCB_RSP_U) != 0) &&
            ((net_rsp->flags1 & IB_MAC_IOCB_RSP_NU) == 0)) {
                flags = HCK_FULLCKSUM_OK;
                mac_hcksum_set(mp, 0, 0, 0, 0, flags);
        }
}

/*
 * This function goes through h/w descriptor in one specified rx ring,
 * receives the data if the descriptor status shows the data is ready.
 * It returns a chain of mblks containing the received data, to be
 * passed up to mac_rx_ring().
 */
mblk_t *
ql_ring_rx(struct rx_ring *rx_ring, int poll_bytes)
{
        qlge_t *qlge = rx_ring->qlge;
        uint32_t prod = ql_read_sh_reg(qlge, rx_ring);
        struct ib_mac_iocb_rsp *net_rsp;
        mblk_t *mp;
        mblk_t *mblk_head;
        mblk_t **mblk_tail;
        uint32_t received_bytes = 0;
        uint32_t length;
#ifdef QLGE_PERFORMANCE
        uint32_t pkt_ct = 0;
#endif

#ifdef QLGE_TRACK_BUFFER_USAGE
        uint32_t consumer_idx;
        uint32_t producer_idx;
        uint32_t num_free_entries;
        uint32_t temp;

        temp = ql_read_doorbell_reg(qlge, rx_ring->cnsmr_idx_db_reg);
        consumer_idx = temp & 0x0000ffff;
        producer_idx = (temp >> 16);

        if (consumer_idx > producer_idx)
                num_free_entries = (consumer_idx - producer_idx);
        else
                num_free_entries = NUM_RX_RING_ENTRIES - (
                    producer_idx - consumer_idx);

        if (num_free_entries < qlge->cq_low_count[rx_ring->cq_id])
                qlge->cq_low_count[rx_ring->cq_id] = num_free_entries;

#endif
        mblk_head = NULL;
        mblk_tail = &mblk_head;

        while ((prod != rx_ring->cnsmr_idx)) {
                QL_PRINT(DBG_RX,
                    ("%s cq_id = %d, prod = %d, cnsmr = %d.\n",
                    __func__, rx_ring->cq_id, prod, rx_ring->cnsmr_idx));

                net_rsp = (struct ib_mac_iocb_rsp *)rx_ring->curr_entry;
                (void) ddi_dma_sync(rx_ring->cq_dma.dma_handle,
                    (off_t)((uintptr_t)net_rsp -
                    (uintptr_t)rx_ring->cq_dma.vaddr),
                    (size_t)sizeof (*net_rsp), DDI_DMA_SYNC_FORKERNEL);
                QL_DUMP(DBG_RX, "qlge_ring_rx: rx completion iocb\n",
                    rx_ring->curr_entry, 8, (size_t)sizeof (*net_rsp));

                switch (net_rsp->opcode) {

                case OPCODE_IB_MAC_IOCB:
                        /* Adding length of pkt header and payload */
                        length = le32_to_cpu(net_rsp->data_len) +
                            le32_to_cpu(net_rsp->hdr_len);
                        if ((poll_bytes != QLGE_POLL_ALL) &&
                            ((received_bytes + length) > poll_bytes)) {
                                continue;
                        }
                        received_bytes += length;

#ifdef QLGE_PERFORMANCE
                        pkt_ct++;
#endif
                        mp = ql_build_rx_mp(qlge, rx_ring, net_rsp);
                        if (mp != NULL) {
                                if (rx_ring->mac_flags != QL_MAC_STARTED) {
                                        /*
                                         * Increment number of packets we have
                                         * indicated to the stack, should be
                                         * decremented when we get it back
                                         * or when freemsg is called
                                         */
                                        ASSERT(rx_ring->rx_indicate
                                            <= rx_ring->cq_len);
#ifdef QLGE_LOAD_UNLOAD
                                        cmn_err(CE_WARN, "%s do not send to OS,"
                                            " mac_flags %d, indicate %d",
                                            __func__, rx_ring->mac_flags,
                                            rx_ring->rx_indicate);
#endif
                                        QL_PRINT(DBG_RX,
                                            ("cq_id = %d, packet "
                                            "dropped, mac not "
                                            "enabled.\n",
                                            rx_ring->cq_id));
                                        rx_ring->rx_pkt_dropped_mac_unenabled++;

                                        /* rx_lock is expected to be held */
                                        mutex_exit(&rx_ring->rx_lock);
                                        freemsg(mp);
                                        mutex_enter(&rx_ring->rx_lock);
                                        mp = NULL;
                                }

                                if (mp != NULL) {
                                        /*
                                         * IP full packet has been
                                         * successfully verified by
                                         * H/W and is correct
                                         */
                                        ql_set_rx_cksum(mp, net_rsp);

                                        rx_ring->rx_packets++;
                                        rx_ring->rx_bytes += length;
                                        *mblk_tail = mp;
                                        mblk_tail = &mp->b_next;
                                }
                        } else {
                                QL_PRINT(DBG_RX,
                                    ("cq_id = %d, packet dropped\n",
                                    rx_ring->cq_id));
                                rx_ring->rx_packets_dropped_no_buffer++;
                        }
                        break;

                case OPCODE_IB_SYS_EVENT_IOCB:
                        ql_process_chip_ae_intr(qlge,
                            (struct ib_sys_event_iocb_rsp *)
                            net_rsp);
                        break;

                default:
                        cmn_err(CE_WARN,
                            "%s Ring(%d)Hit default case, not handled!"
                            " dropping the packet, "
                            "opcode = %x.", __func__, rx_ring->cq_id,
                            net_rsp->opcode);
                        break;
                }
                /* increment cnsmr_idx and curr_entry */
                ql_update_cq(rx_ring);
                prod = ql_read_sh_reg(qlge, rx_ring);

        }

#ifdef QLGE_PERFORMANCE
        if (pkt_ct >= 7)
                rx_ring->hist[7]++;
        else if (pkt_ct == 6)
                rx_ring->hist[6]++;
        else if (pkt_ct == 5)
                rx_ring->hist[5]++;
        else if (pkt_ct == 4)
                rx_ring->hist[4]++;
        else if (pkt_ct == 3)
                rx_ring->hist[3]++;
        else if (pkt_ct == 2)
                rx_ring->hist[2]++;
        else if (pkt_ct == 1)
                rx_ring->hist[1]++;
        else if (pkt_ct == 0)
                rx_ring->hist[0]++;
#endif

        /* update cnsmr_idx */
        ql_write_cq_idx(rx_ring);
        /* do not enable interrupt for polling mode */
        if (poll_bytes == QLGE_POLL_ALL)
                ql_enable_completion_interrupt(rx_ring->qlge, rx_ring->irq);
        return (mblk_head);
}

/* Process an outbound completion from an rx ring. */
static void
ql_process_mac_tx_intr(qlge_t *qlge, struct ob_mac_iocb_rsp *mac_rsp)
{
        struct tx_ring *tx_ring;
        struct tx_ring_desc *tx_ring_desc;
        int j;

        tx_ring = &qlge->tx_ring[mac_rsp->txq_idx];
        tx_ring_desc = tx_ring->wq_desc;
        tx_ring_desc += mac_rsp->tid;

        if (tx_ring_desc->tx_type == USE_DMA) {
                QL_PRINT(DBG_TX, ("%s(%d): tx type USE_DMA\n",
                    __func__, qlge->instance));

                /*
                 * Release the DMA resource that is used for
                 * DMA binding.
                 */
                for (j = 0; j < tx_ring_desc->tx_dma_handle_used; j++) {
                        (void) ddi_dma_unbind_handle(
                            tx_ring_desc->tx_dma_handle[j]);
                }

                tx_ring_desc->tx_dma_handle_used = 0;
                /*
                 * Free the mblk after sending completed
                 */
                if (tx_ring_desc->mp != NULL) {
                        freemsg(tx_ring_desc->mp);
                        tx_ring_desc->mp = NULL;
                }
        }

        tx_ring->obytes += tx_ring_desc->tx_bytes;
        tx_ring->opackets++;

        if (mac_rsp->flags1 & (OB_MAC_IOCB_RSP_E | OB_MAC_IOCB_RSP_S |
            OB_MAC_IOCB_RSP_L | OB_MAC_IOCB_RSP_B)) {
                tx_ring->errxmt++;
                if (mac_rsp->flags1 & OB_MAC_IOCB_RSP_E) {
                        /* EMPTY */
                        QL_PRINT(DBG_TX,
                            ("Total descriptor length did not match "
                            "transfer length.\n"));
                }
                if (mac_rsp->flags1 & OB_MAC_IOCB_RSP_S) {
                        /* EMPTY */
                        QL_PRINT(DBG_TX,
                            ("Frame too short to be legal, not sent.\n"));
                }
                if (mac_rsp->flags1 & OB_MAC_IOCB_RSP_L) {
                        /* EMPTY */
                        QL_PRINT(DBG_TX,
                            ("Frame too long, but sent anyway.\n"));
                }
                if (mac_rsp->flags3 & OB_MAC_IOCB_RSP_B) {
                        /* EMPTY */
                        QL_PRINT(DBG_TX,
                            ("PCI backplane error. Frame not sent.\n"));
                }
        }
        atomic_inc_32(&tx_ring->tx_free_count);
}

/*
 * clean up tx completion iocbs
 */
int
ql_clean_outbound_rx_ring(struct rx_ring *rx_ring)
{
        qlge_t *qlge = rx_ring->qlge;
        uint32_t prod = ql_read_sh_reg(qlge, rx_ring);
        struct ob_mac_iocb_rsp *net_rsp = NULL;
        int count = 0;
        struct tx_ring *tx_ring;
        boolean_t resume_tx = B_FALSE;

        mutex_enter(&rx_ring->rx_lock);
#ifdef QLGE_TRACK_BUFFER_USAGE
        {
        uint32_t consumer_idx;
        uint32_t producer_idx;
        uint32_t num_free_entries;
        uint32_t temp;

        temp = ql_read_doorbell_reg(qlge, rx_ring->cnsmr_idx_db_reg);
        consumer_idx = temp & 0x0000ffff;
        producer_idx = (temp >> 16);

        if (consumer_idx > producer_idx)
                num_free_entries = (consumer_idx - producer_idx);
        else
                num_free_entries = NUM_RX_RING_ENTRIES -
                    (producer_idx - consumer_idx);

        if (num_free_entries < qlge->cq_low_count[rx_ring->cq_id])
                qlge->cq_low_count[rx_ring->cq_id] = num_free_entries;

        }
#endif
        /* While there are entries in the completion queue. */
        while (prod != rx_ring->cnsmr_idx) {

                QL_PRINT(DBG_RX,
                    ("%s cq_id = %d, prod = %d, cnsmr = %d.\n", __func__,
                    rx_ring->cq_id, prod, rx_ring->cnsmr_idx));

                net_rsp = (struct ob_mac_iocb_rsp *)rx_ring->curr_entry;
                (void) ddi_dma_sync(rx_ring->cq_dma.dma_handle,
                    (off_t)((uintptr_t)net_rsp -
                    (uintptr_t)rx_ring->cq_dma.vaddr),
                    (size_t)sizeof (*net_rsp), DDI_DMA_SYNC_FORKERNEL);

                QL_DUMP(DBG_RX, "ql_clean_outbound_rx_ring: "
                    "response packet data\n",
                    rx_ring->curr_entry, 8,
                    (size_t)sizeof (*net_rsp));

                switch (net_rsp->opcode) {

                case OPCODE_OB_MAC_OFFLOAD_IOCB:
                case OPCODE_OB_MAC_IOCB:
                        ql_process_mac_tx_intr(qlge, net_rsp);
                        break;

                default:
                        cmn_err(CE_WARN,
                            "%s Hit default case, not handled! "
                            "dropping the packet,"
                            " opcode = %x.",
                            __func__, net_rsp->opcode);
                        break;
                }
                count++;
                ql_update_cq(rx_ring);
                prod = ql_read_sh_reg(qlge, rx_ring);
        }
        ql_write_cq_idx(rx_ring);

        mutex_exit(&rx_ring->rx_lock);

        net_rsp = (struct ob_mac_iocb_rsp *)rx_ring->curr_entry;
        tx_ring = &qlge->tx_ring[net_rsp->txq_idx];

        mutex_enter(&tx_ring->tx_lock);

        if (tx_ring->queue_stopped &&
            (tx_ring->tx_free_count > TX_RESUME_THRESHOLD)) {
                /*
                 * The queue got stopped because the tx_ring was full.
                 * Wake it up, because it's now at least 25% empty.
                 */
                tx_ring->queue_stopped = 0;
                resume_tx = B_TRUE;
        }

        mutex_exit(&tx_ring->tx_lock);
        /* Don't hold the lock during OS callback */
        if (resume_tx)
                RESUME_TX(tx_ring);
        return (count);
}

/*
 * reset asic when error happens
 */
/* ARGSUSED */
static uint_t
ql_asic_reset_work(caddr_t arg1, caddr_t arg2)
{
        qlge_t *qlge = (qlge_t *)((void *)arg1);
        int status;

        mutex_enter(&qlge->gen_mutex);
        (void) ql_do_stop(qlge);
        /*
         * Write default ethernet address to chip register Mac
         * Address slot 0 and Enable Primary Mac Function.
         */
        mutex_enter(&qlge->hw_mutex);
        (void) ql_unicst_set(qlge,
            (uint8_t *)qlge->unicst_addr[0].addr.ether_addr_octet, 0);
        mutex_exit(&qlge->hw_mutex);
        qlge->mac_flags = QL_MAC_INIT;
        status = ql_do_start(qlge);
        if (status != DDI_SUCCESS)
                goto error;
        qlge->mac_flags = QL_MAC_STARTED;
        mutex_exit(&qlge->gen_mutex);
        ddi_fm_service_impact(qlge->dip, DDI_SERVICE_RESTORED);

        return (DDI_INTR_CLAIMED);

error:
        mutex_exit(&qlge->gen_mutex);
        cmn_err(CE_WARN,
            "qlge up/down cycle failed, closing device");
        if (qlge->fm_enable) {
                ql_fm_ereport(qlge, DDI_FM_DEVICE_INVAL_STATE);
                ddi_fm_service_impact(qlge->dip, DDI_SERVICE_LOST);
                atomic_or_32(&qlge->flags, ADAPTER_ERROR);
        }
        return (DDI_INTR_CLAIMED);
}

/*
 * Reset MPI
 */
/* ARGSUSED */
static uint_t
ql_mpi_reset_work(caddr_t arg1, caddr_t arg2)
{
        qlge_t *qlge = (qlge_t *)((void *)arg1);

        (void) ql_reset_mpi_risc(qlge);
        return (DDI_INTR_CLAIMED);
}

/*
 * Process MPI mailbox messages
 */
/* ARGSUSED */
static uint_t
ql_mpi_event_work(caddr_t arg1, caddr_t arg2)
{
        qlge_t *qlge = (qlge_t *)((void *)arg1);

        ql_do_mpi_intr(qlge);
        return (DDI_INTR_CLAIMED);
}

/* Fire up a handler to reset the MPI processor. */
void
ql_wake_asic_reset_soft_intr(qlge_t *qlge)
{
        (void) ddi_intr_trigger_softint(qlge->asic_reset_intr_hdl, NULL);
}

static void
ql_wake_mpi_reset_soft_intr(qlge_t *qlge)
{
        (void) ddi_intr_trigger_softint(qlge->mpi_reset_intr_hdl, NULL);
}

static void
ql_wake_mpi_event_soft_intr(qlge_t *qlge)
{
        (void) ddi_intr_trigger_softint(qlge->mpi_event_intr_hdl, NULL);
}

/*
 * This handles a fatal error, MPI activity, and the default
 * rx_ring in an MSI-X multiple interrupt vector environment.
 * In MSI/Legacy environment it also process the rest of
 * the rx_rings.
 */
/* ARGSUSED */
static uint_t
ql_isr(caddr_t arg1, caddr_t arg2)
{
        struct rx_ring *rx_ring = (struct rx_ring *)((void *)arg1);
        struct rx_ring *ob_ring;
        qlge_t *qlge = rx_ring->qlge;
        struct intr_ctx *intr_ctx = &qlge->intr_ctx[0];
        uint32_t var, prod;
        int i;
        int work_done = 0;

        mblk_t *mp;

        _NOTE(ARGUNUSED(arg2));

        ++qlge->rx_interrupts[rx_ring->cq_id];

        if (ql_atomic_read_32(&qlge->intr_ctx[0].irq_cnt)) {
                ql_write_reg(qlge, REG_RSVD7, 0xfeed0002);
                var = ql_read_reg(qlge, REG_ERROR_STATUS);
                var = ql_read_reg(qlge, REG_STATUS);
                var = ql_read_reg(qlge, REG_INTERRUPT_STATUS_1);
                return (DDI_INTR_CLAIMED);
        }

        ql_disable_completion_interrupt(qlge, intr_ctx->intr);

        /*
         * process send completes on first stride tx ring if available
         */
        if (qlge->isr_stride) {
                ob_ring = &qlge->rx_ring[qlge->isr_stride];
                if (ql_read_sh_reg(qlge, ob_ring) !=
                    ob_ring->cnsmr_idx) {
                        (void) ql_clean_outbound_rx_ring(ob_ring);
                }
        }
        /*
         * Check the default queue and wake handler if active.
         */
        rx_ring = &qlge->rx_ring[0];
        prod = ql_read_sh_reg(qlge, rx_ring);
        QL_PRINT(DBG_INTR, ("rx-ring[0] prod index 0x%x, consumer 0x%x ",
            prod, rx_ring->cnsmr_idx));
        /* check if interrupt is due to incoming packet */
        if (prod != rx_ring->cnsmr_idx) {
                QL_PRINT(DBG_INTR, ("Waking handler for rx_ring[0].\n"));
                ql_disable_completion_interrupt(qlge, intr_ctx->intr);
                mutex_enter(&rx_ring->rx_lock);
                mp = ql_ring_rx(rx_ring, QLGE_POLL_ALL);
                mutex_exit(&rx_ring->rx_lock);

                if (mp != NULL)
                        RX_UPSTREAM(rx_ring, mp);
                work_done++;
        } else {
                /*
                 * If interrupt is not due to incoming packet, read status
                 * register to see if error happens or mailbox interrupt.
                 */
                var = ql_read_reg(qlge, REG_STATUS);
                if ((var & STATUS_FE) != 0) {
                        ql_write_reg(qlge, REG_RSVD7, 0xfeed0003);
                        if (qlge->fm_enable) {
                                atomic_or_32(&qlge->flags, ADAPTER_ERROR);
                                ql_fm_ereport(qlge, DDI_FM_DEVICE_INVAL_STATE);
                                ddi_fm_service_impact(qlge->dip,
                                    DDI_SERVICE_LOST);
                        }
                        cmn_err(CE_WARN, "Got fatal error, STS = %x.", var);
                        var = ql_read_reg(qlge, REG_ERROR_STATUS);
                        cmn_err(CE_WARN,
                            "Resetting chip. Error Status Register = 0x%x",
                            var);
                        ql_wake_asic_reset_soft_intr(qlge);
                        return (DDI_INTR_CLAIMED);
                }

                /*
                 * Check MPI processor activity.
                 */
                if ((var & STATUS_PI) != 0) {
                        /*
                         * We've got an async event or mailbox completion.
                         * Handle it and clear the source of the interrupt.
                         */
                        ql_write_reg(qlge, REG_RSVD7, 0xfeed0004);

                        QL_PRINT(DBG_INTR, ("Got MPI processor interrupt.\n"));
                        ql_disable_completion_interrupt(qlge, intr_ctx->intr);
                        ql_wake_mpi_event_soft_intr(qlge);
                        work_done++;
                }
        }


        if (qlge->intr_type != DDI_INTR_TYPE_MSIX) {
                /*
                 * Start the DPC for each active queue.
                 */
                for (i = 1; i < qlge->rx_ring_count; i++) {
                        rx_ring = &qlge->rx_ring[i];

                        if (ql_read_sh_reg(qlge, rx_ring) !=
                            rx_ring->cnsmr_idx) {
                                QL_PRINT(DBG_INTR,
                                    ("Waking handler for rx_ring[%d].\n", i));

                                ql_disable_completion_interrupt(qlge,
                                    rx_ring->irq);
                                if (rx_ring->type == TX_Q) {
                                        (void) ql_clean_outbound_rx_ring(
                                            rx_ring);
                                        ql_enable_completion_interrupt(
                                            rx_ring->qlge, rx_ring->irq);
                                } else {
                                        mutex_enter(&rx_ring->rx_lock);
                                        mp = ql_ring_rx(rx_ring, QLGE_POLL_ALL);
                                        mutex_exit(&rx_ring->rx_lock);
                                        if (mp != NULL)
                                                RX_UPSTREAM(rx_ring, mp);
#ifdef QLGE_LOAD_UNLOAD
                                        if (rx_ring->mac_flags ==
                                            QL_MAC_STOPPED)
                                                cmn_err(CE_NOTE,
                                                    "%s rx_indicate(%d) %d\n",
                                                    __func__, i,
                                                    rx_ring->rx_indicate);
#endif
                                }
                                work_done++;
                        }
                }
        }

        ql_enable_completion_interrupt(qlge, intr_ctx->intr);

        return (work_done ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
}

/*
 * MSI-X Multiple Vector Interrupt Handler for outbound (TX) completions.
 */
/* ARGSUSED */
static uint_t
ql_msix_tx_isr(caddr_t arg1, caddr_t arg2)
{
        struct rx_ring *rx_ring = (struct rx_ring *)((void *)arg1);
        qlge_t *qlge = rx_ring->qlge;
        _NOTE(ARGUNUSED(arg2));

        ++qlge->rx_interrupts[rx_ring->cq_id];
        (void) ql_clean_outbound_rx_ring(rx_ring);
        ql_enable_completion_interrupt(rx_ring->qlge, rx_ring->irq);

        return (DDI_INTR_CLAIMED);
}

/*
 * MSI-X Multiple Vector Interrupt Handler
 */
/* ARGSUSED */
static uint_t
ql_msix_isr(caddr_t arg1, caddr_t arg2)
{
        struct rx_ring *rx_ring = (struct rx_ring *)((void *)arg1);
        struct rx_ring *ob_ring;
        qlge_t *qlge = rx_ring->qlge;
        mblk_t *mp;
        _NOTE(ARGUNUSED(arg2));

        QL_PRINT(DBG_INTR, ("%s for ring %d\n", __func__, rx_ring->cq_id));

        ql_disable_completion_interrupt(qlge, rx_ring->irq);

        /*
         * process send completes on stride tx ring if available
         */
        if (qlge->isr_stride) {
                ob_ring = rx_ring + qlge->isr_stride;
                if (ql_read_sh_reg(qlge, ob_ring) !=
                    ob_ring->cnsmr_idx) {
                        ++qlge->rx_interrupts[ob_ring->cq_id];
                        (void) ql_clean_outbound_rx_ring(ob_ring);
                }
        }

        ++qlge->rx_interrupts[rx_ring->cq_id];

        mutex_enter(&rx_ring->rx_lock);
        mp = ql_ring_rx(rx_ring, QLGE_POLL_ALL);
        mutex_exit(&rx_ring->rx_lock);

        if (mp != NULL)
                RX_UPSTREAM(rx_ring, mp);

        return (DDI_INTR_CLAIMED);
}

/*
 * Poll n_bytes of chained incoming packets
 */
mblk_t *
ql_ring_rx_poll(void *arg, int n_bytes)
{
        struct rx_ring *rx_ring = (struct rx_ring *)arg;
        qlge_t *qlge = rx_ring->qlge;
        mblk_t *mp = NULL;
        uint32_t var;

        ASSERT(n_bytes >= 0);
        QL_PRINT(DBG_GLD, ("%s for ring(%d) to read max %d bytes\n",
            __func__, rx_ring->cq_id, n_bytes));

        ++qlge->rx_polls[rx_ring->cq_id];

        if (n_bytes == 0)
                return (mp);
        mutex_enter(&rx_ring->rx_lock);
        mp = ql_ring_rx(rx_ring, n_bytes);
        mutex_exit(&rx_ring->rx_lock);

        if ((rx_ring->cq_id == 0) && (mp == NULL)) {
                var = ql_read_reg(qlge, REG_STATUS);
                /*
                 * Check for fatal error.
                 */
                if ((var & STATUS_FE) != 0) {
                        ql_write_reg(qlge, REG_RSVD7, 0xfeed0003);
                        var = ql_read_reg(qlge, REG_ERROR_STATUS);
                        cmn_err(CE_WARN, "Got fatal error %x.", var);
                        ql_wake_asic_reset_soft_intr(qlge);
                        if (qlge->fm_enable) {
                                atomic_or_32(&qlge->flags, ADAPTER_ERROR);
                                ql_fm_ereport(qlge, DDI_FM_DEVICE_INVAL_STATE);
                                ddi_fm_service_impact(qlge->dip,
                                    DDI_SERVICE_LOST);
                        }
                }
                /*
                 * Check MPI processor activity.
                 */
                if ((var & STATUS_PI) != 0) {
                        /*
                         * We've got an async event or mailbox completion.
                         * Handle it and clear the source of the interrupt.
                         */
                        ql_write_reg(qlge, REG_RSVD7, 0xfeed0004);
                        ql_do_mpi_intr(qlge);
                }
        }

        return (mp);
}

/*
 * MSI-X Multiple Vector Interrupt Handler for inbound (RX) completions.
 */
/* ARGSUSED */
static uint_t
ql_msix_rx_isr(caddr_t arg1, caddr_t arg2)
{
        struct rx_ring *rx_ring = (struct rx_ring *)((void *)arg1);
        qlge_t *qlge = rx_ring->qlge;
        mblk_t *mp;
        _NOTE(ARGUNUSED(arg2));

        QL_PRINT(DBG_INTR, ("%s for ring %d\n", __func__, rx_ring->cq_id));

        ++qlge->rx_interrupts[rx_ring->cq_id];

        mutex_enter(&rx_ring->rx_lock);
        mp = ql_ring_rx(rx_ring, QLGE_POLL_ALL);
        mutex_exit(&rx_ring->rx_lock);

        if (mp != NULL)
                RX_UPSTREAM(rx_ring, mp);

        return (DDI_INTR_CLAIMED);
}


/*
 *
 * Allocate DMA Buffer for ioctl service
 *
 */
static int
ql_alloc_ioctl_dma_buf(qlge_t *qlge)
{
        uint64_t phy_addr;
        uint64_t alloc_size;
        ddi_dma_cookie_t dma_cookie;

        alloc_size = qlge->ioctl_buf_dma_attr.mem_len =
            max(WCS_MPI_CODE_RAM_LENGTH, MEMC_MPI_RAM_LENGTH);
        if (ql_alloc_phys(qlge->dip, &qlge->ioctl_buf_dma_attr.dma_handle,
            &ql_buf_acc_attr,
            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
            &qlge->ioctl_buf_dma_attr.acc_handle,
            (size_t)alloc_size,  /* mem size */
            (size_t)0,  /* alignment */
            (caddr_t *)&qlge->ioctl_buf_dma_attr.vaddr,
            &dma_cookie) != 0) {
                cmn_err(CE_WARN, "%s(%d): ioctl DMA allocation failed.",
                    __func__, qlge->instance);
                return (DDI_FAILURE);
        }

        phy_addr = dma_cookie.dmac_laddress;

        if (qlge->ioctl_buf_dma_attr.vaddr == NULL) {
                cmn_err(CE_WARN, "%s(%d): failed.", __func__, qlge->instance);
                return (DDI_FAILURE);
        }

        qlge->ioctl_buf_dma_attr.dma_addr = phy_addr;

        QL_PRINT(DBG_MBX, ("%s: ioctl_dma_buf_virt_addr = 0x%lx, "
            "phy_addr = 0x%lx\n",
            __func__, qlge->ioctl_buf_dma_attr.vaddr, phy_addr));

        return (DDI_SUCCESS);
}


/*
 * Function to free physical memory.
 */
static void
ql_free_phys(ddi_dma_handle_t *dma_handle, ddi_acc_handle_t *acc_handle)
{
        if (*dma_handle != NULL) {
                (void) ddi_dma_unbind_handle(*dma_handle);
                if (*acc_handle != NULL)
                        ddi_dma_mem_free(acc_handle);
                ddi_dma_free_handle(dma_handle);
                *acc_handle = NULL;
                *dma_handle = NULL;
        }
}

/*
 * Function to free ioctl dma buffer.
 */
static void
ql_free_ioctl_dma_buf(qlge_t *qlge)
{
        if (qlge->ioctl_buf_dma_attr.dma_handle != NULL) {
                ql_free_phys(&qlge->ioctl_buf_dma_attr.dma_handle,
                    &qlge->ioctl_buf_dma_attr.acc_handle);

                qlge->ioctl_buf_dma_attr.vaddr = NULL;
                qlge->ioctl_buf_dma_attr.dma_handle = NULL;
        }
}

/*
 * Free shadow register space used for request and completion queues
 */
static void
ql_free_shadow_space(qlge_t *qlge)
{
        if (qlge->host_copy_shadow_dma_attr.dma_handle != NULL) {
                ql_free_phys(&qlge->host_copy_shadow_dma_attr.dma_handle,
                    &qlge->host_copy_shadow_dma_attr.acc_handle);
                bzero(&qlge->host_copy_shadow_dma_attr,
                    sizeof (qlge->host_copy_shadow_dma_attr));
        }

        if (qlge->buf_q_ptr_base_addr_dma_attr.dma_handle != NULL) {
                ql_free_phys(&qlge->buf_q_ptr_base_addr_dma_attr.dma_handle,
                    &qlge->buf_q_ptr_base_addr_dma_attr.acc_handle);
                bzero(&qlge->buf_q_ptr_base_addr_dma_attr,
                    sizeof (qlge->buf_q_ptr_base_addr_dma_attr));
        }
}

/*
 * Allocate shadow register space for request and completion queues
 */
static int
ql_alloc_shadow_space(qlge_t *qlge)
{
        ddi_dma_cookie_t dma_cookie;

        if (ql_alloc_phys(qlge->dip,
            &qlge->host_copy_shadow_dma_attr.dma_handle,
            &ql_dev_acc_attr,
            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
            &qlge->host_copy_shadow_dma_attr.acc_handle,
            (size_t)VM_PAGE_SIZE,  /* mem size */
            (size_t)4, /* 4 bytes alignment */
            (caddr_t *)&qlge->host_copy_shadow_dma_attr.vaddr,
            &dma_cookie) != 0) {
                bzero(&qlge->host_copy_shadow_dma_attr,
                    sizeof (qlge->host_copy_shadow_dma_attr));

                cmn_err(CE_WARN, "%s(%d): Unable to allocate DMA memory for "
                    "response shadow registers", __func__, qlge->instance);
                return (DDI_FAILURE);
        }

        qlge->host_copy_shadow_dma_attr.dma_addr = dma_cookie.dmac_laddress;

        if (ql_alloc_phys(qlge->dip,
            &qlge->buf_q_ptr_base_addr_dma_attr.dma_handle,
            &ql_desc_acc_attr,
            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
            &qlge->buf_q_ptr_base_addr_dma_attr.acc_handle,
            (size_t)VM_PAGE_SIZE,  /* mem size */
            (size_t)4, /* 4 bytes alignment */
            (caddr_t *)&qlge->buf_q_ptr_base_addr_dma_attr.vaddr,
            &dma_cookie) != 0) {
                bzero(&qlge->buf_q_ptr_base_addr_dma_attr,
                    sizeof (qlge->buf_q_ptr_base_addr_dma_attr));

                cmn_err(CE_WARN, "%s(%d): Unable to allocate DMA memory "
                    "for request shadow registers",
                    __func__, qlge->instance);
                goto err_wqp_sh_area;
        }
        qlge->buf_q_ptr_base_addr_dma_attr.dma_addr = dma_cookie.dmac_laddress;

        return (DDI_SUCCESS);

err_wqp_sh_area:
        ql_free_phys(&qlge->host_copy_shadow_dma_attr.dma_handle,
            &qlge->host_copy_shadow_dma_attr.acc_handle);
        bzero(&qlge->host_copy_shadow_dma_attr,
            sizeof (qlge->host_copy_shadow_dma_attr));

        return (DDI_FAILURE);
}

/*
 * Initialize a tx ring
 */
static void
ql_init_tx_ring(struct tx_ring *tx_ring)
{
        int i;
        struct ob_mac_iocb_req *mac_iocb_ptr = tx_ring->wq_dma.vaddr;
        struct tx_ring_desc *tx_ring_desc = tx_ring->wq_desc;

        for (i = 0; i < tx_ring->wq_len; i++) {
                tx_ring_desc->index = i;
                tx_ring_desc->queue_entry = mac_iocb_ptr;
                mac_iocb_ptr++;
                tx_ring_desc++;
        }
        tx_ring->tx_free_count = tx_ring->wq_len;
        tx_ring->queue_stopped = 0;
}

/*
 * Free one tx ring resources
 */
static void
ql_free_tx_resources(struct tx_ring *tx_ring)
{
        struct tx_ring_desc *tx_ring_desc;
        int i, j;

        if (tx_ring->wq_dma.dma_handle != NULL) {
                ql_free_phys(&tx_ring->wq_dma.dma_handle,
                    &tx_ring->wq_dma.acc_handle);
                bzero(&tx_ring->wq_dma, sizeof (tx_ring->wq_dma));
        }
        if (tx_ring->wq_desc != NULL) {
                tx_ring_desc = tx_ring->wq_desc;
                for (i = 0; i < tx_ring->wq_len; i++, tx_ring_desc++) {
                        for (j = 0; j < QL_MAX_TX_DMA_HANDLES; j++) {
                                if (tx_ring_desc->tx_dma_handle[j]) {
                                        /*
                                         * The unbinding will happen in tx
                                         * completion, here we just free the
                                         * handles
                                         */
                                        ddi_dma_free_handle(
                                            &(tx_ring_desc->tx_dma_handle[j]));
                                        tx_ring_desc->tx_dma_handle[j] = NULL;
                                }
                        }
                        if (tx_ring_desc->oal != NULL) {
                                tx_ring_desc->oal_dma_addr = 0;
                                tx_ring_desc->oal = NULL;
                                tx_ring_desc->copy_buffer = NULL;
                                tx_ring_desc->copy_buffer_dma_addr = 0;

                                ql_free_phys(&tx_ring_desc->oal_dma.dma_handle,
                                    &tx_ring_desc->oal_dma.acc_handle);
                        }
                }
                kmem_free(tx_ring->wq_desc,
                    tx_ring->wq_len * sizeof (struct tx_ring_desc));
                tx_ring->wq_desc = NULL;
        }
        /* free the wqicb struct */
        if (tx_ring->wqicb_dma.dma_handle) {
                ql_free_phys(&tx_ring->wqicb_dma.dma_handle,
                    &tx_ring->wqicb_dma.acc_handle);
                bzero(&tx_ring->wqicb_dma, sizeof (tx_ring->wqicb_dma));
        }
}

/*
 * Allocate work (request) queue memory and transmit
 * descriptors for this transmit ring
 */
static int
ql_alloc_tx_resources(qlge_t *qlge, struct tx_ring *tx_ring)
{
        ddi_dma_cookie_t dma_cookie;
        struct tx_ring_desc *tx_ring_desc;
        int i, j;
        uint32_t length;

        /* allocate dma buffers for obiocbs */
        if (ql_alloc_phys(qlge->dip, &tx_ring->wq_dma.dma_handle,
            &ql_desc_acc_attr,
            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
            &tx_ring->wq_dma.acc_handle,
            (size_t)tx_ring->wq_size,   /* mem size */
            (size_t)128, /* alignment:128 bytes boundary */
            (caddr_t *)&tx_ring->wq_dma.vaddr,
            &dma_cookie) != 0) {
                bzero(&tx_ring->wq_dma, sizeof (tx_ring->wq_dma));
                cmn_err(CE_WARN, "%s(%d): reqQ allocation failed.",
                    __func__, qlge->instance);
                return (DDI_FAILURE);
        }
        tx_ring->wq_dma.dma_addr = dma_cookie.dmac_laddress;

        tx_ring->wq_desc =
            kmem_zalloc(tx_ring->wq_len * sizeof (struct tx_ring_desc),
            KM_NOSLEEP);
        if (tx_ring->wq_desc == NULL) {
                goto err;
        } else {
                tx_ring_desc = tx_ring->wq_desc;
                /*
                 * Allocate a large enough structure to hold the following
                 * 1. oal buffer MAX_SGELEMENTS * sizeof (oal_entry) bytes
                 * 2. copy buffer of QL_MAX_COPY_LENGTH bytes
                 */
                length = (sizeof (struct oal_entry) * MAX_SG_ELEMENTS)
                    + QL_MAX_COPY_LENGTH;
                for (i = 0; i < tx_ring->wq_len; i++, tx_ring_desc++) {

                        if (ql_alloc_phys(qlge->dip,
                            &tx_ring_desc->oal_dma.dma_handle,
                            &ql_desc_acc_attr,
                            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
                            &tx_ring_desc->oal_dma.acc_handle,
                            (size_t)length,     /* mem size */
                            (size_t)0, /* default alignment:8 bytes boundary */
                            (caddr_t *)&tx_ring_desc->oal_dma.vaddr,
                            &dma_cookie) != 0) {
                                bzero(&tx_ring_desc->oal_dma,
                                    sizeof (tx_ring_desc->oal_dma));
                                cmn_err(CE_WARN, "%s(%d): reqQ tx buf &"
                                    "oal alloc failed.",
                                    __func__, qlge->instance);
                                goto err;
                        }

                        tx_ring_desc->oal = tx_ring_desc->oal_dma.vaddr;
                        tx_ring_desc->oal_dma_addr = dma_cookie.dmac_laddress;
                        tx_ring_desc->copy_buffer =
                            (caddr_t)((uint8_t *)tx_ring_desc->oal
                            + (sizeof (struct oal_entry) * MAX_SG_ELEMENTS));
                        tx_ring_desc->copy_buffer_dma_addr =
                            (tx_ring_desc->oal_dma_addr
                            + (sizeof (struct oal_entry) * MAX_SG_ELEMENTS));

                        /* Allocate dma handles for transmit buffers */
                        for (j = 0; j < QL_MAX_TX_DMA_HANDLES; j++) {
                                if (ddi_dma_alloc_handle(qlge->dip,
                                    &tx_mapping_dma_attr,
                                    DDI_DMA_DONTWAIT,
                                    0, &tx_ring_desc->tx_dma_handle[j])
                                    != DDI_SUCCESS) {
                                        tx_ring_desc->tx_dma_handle[j] = NULL;
                                        cmn_err(CE_WARN,
                                            "!%s: ddi_dma_alloc_handle: "
                                            "tx_dma_handle "
                                            "alloc failed", __func__);
                                        ql_free_phys(
                                            &tx_ring_desc->oal_dma.dma_handle,
                                            &tx_ring_desc->oal_dma.acc_handle);
                                        goto err;
                                }
                        }
                }
        }
        /* alloc a wqicb control block to load this tx ring to hw */
        if (ql_alloc_phys(qlge->dip, &tx_ring->wqicb_dma.dma_handle,
            &ql_desc_acc_attr,
            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
            &tx_ring->wqicb_dma.acc_handle,
            (size_t)sizeof (struct wqicb_t),    /* mem size */
            (size_t)0, /* alignment:128 bytes boundary */
            (caddr_t *)&tx_ring->wqicb_dma.vaddr,
            &dma_cookie) != 0) {
                bzero(&tx_ring->wqicb_dma, sizeof (tx_ring->wqicb_dma));
                cmn_err(CE_WARN, "%s(%d): wqicb allocation failed.",
                    __func__, qlge->instance);
                goto err;
        }
        tx_ring->wqicb_dma.dma_addr = dma_cookie.dmac_laddress;

        return (DDI_SUCCESS);

err:
        ql_free_tx_resources(tx_ring);
        return (DDI_FAILURE);
}

/*
 * Free one rx ring resources
 */
static void
ql_free_rx_resources(struct rx_ring *rx_ring)
{
        /* Free the small buffer queue. */
        if (rx_ring->sbq_dma.dma_handle) {
                ql_free_phys(&rx_ring->sbq_dma.dma_handle,
                    &rx_ring->sbq_dma.acc_handle);
                bzero(&rx_ring->sbq_dma, sizeof (rx_ring->sbq_dma));
        }

        /* Free the small buffer queue control blocks. */
        if (rx_ring->sbq_desc != NULL) {
                kmem_free(rx_ring->sbq_desc, rx_ring->sbq_len *
                    sizeof (struct bq_desc));
                rx_ring->sbq_desc = NULL;
        }

        /* Free the large buffer queue. */
        if (rx_ring->lbq_dma.dma_handle) {
                ql_free_phys(&rx_ring->lbq_dma.dma_handle,
                    &rx_ring->lbq_dma.acc_handle);
                bzero(&rx_ring->lbq_dma, sizeof (rx_ring->lbq_dma));
        }

        /* Free the large buffer queue control blocks. */
        if (rx_ring->lbq_desc != NULL) {
                kmem_free(rx_ring->lbq_desc, rx_ring->lbq_len *
                    sizeof (struct bq_desc));
                rx_ring->lbq_desc = NULL;
        }

        /* Free cqicb struct */
        if (rx_ring->cqicb_dma.dma_handle) {
                ql_free_phys(&rx_ring->cqicb_dma.dma_handle,
                    &rx_ring->cqicb_dma.acc_handle);
                bzero(&rx_ring->cqicb_dma, sizeof (rx_ring->cqicb_dma));
        }
        /* Free the rx queue. */
        if (rx_ring->cq_dma.dma_handle) {
                ql_free_phys(&rx_ring->cq_dma.dma_handle,
                    &rx_ring->cq_dma.acc_handle);
                bzero(&rx_ring->cq_dma, sizeof (rx_ring->cq_dma));
        }
}

/*
 * Allocate queues and buffers for this completions queue based
 * on the values in the parameter structure.
 */
static int
ql_alloc_rx_resources(qlge_t *qlge, struct rx_ring *rx_ring)
{
        ddi_dma_cookie_t dma_cookie;

        if (ql_alloc_phys(qlge->dip, &rx_ring->cq_dma.dma_handle,
            &ql_desc_acc_attr,
            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
            &rx_ring->cq_dma.acc_handle,
            (size_t)rx_ring->cq_size,  /* mem size */
            (size_t)128, /* alignment:128 bytes boundary */
            (caddr_t *)&rx_ring->cq_dma.vaddr,
            &dma_cookie) != 0)  {
                bzero(&rx_ring->cq_dma, sizeof (rx_ring->cq_dma));
                cmn_err(CE_WARN, "%s(%d): rspQ allocation failed.",
                    __func__, qlge->instance);
                return (DDI_FAILURE);
        }
        rx_ring->cq_dma.dma_addr = dma_cookie.dmac_laddress;

        if (rx_ring->sbq_len != 0) {
                /*
                 * Allocate small buffer queue.
                 */
                if (ql_alloc_phys(qlge->dip, &rx_ring->sbq_dma.dma_handle,
                    &ql_desc_acc_attr,
                    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
                    &rx_ring->sbq_dma.acc_handle,
                    (size_t)rx_ring->sbq_size,  /* mem size */
                    (size_t)128, /* alignment:128 bytes boundary */
                    (caddr_t *)&rx_ring->sbq_dma.vaddr,
                    &dma_cookie) != 0) {
                        bzero(&rx_ring->sbq_dma, sizeof (rx_ring->sbq_dma));
                        cmn_err(CE_WARN,
                            "%s(%d): small buffer queue allocation failed.",
                            __func__, qlge->instance);
                        goto err_mem;
                }
                rx_ring->sbq_dma.dma_addr = dma_cookie.dmac_laddress;

                /*
                 * Allocate small buffer queue control blocks.
                 */
                rx_ring->sbq_desc =
                    kmem_zalloc(rx_ring->sbq_len * sizeof (struct bq_desc),
                    KM_NOSLEEP);
                if (rx_ring->sbq_desc == NULL) {
                        cmn_err(CE_WARN,
                            "sbq control block allocation failed.");
                        goto err_mem;
                }

                ql_init_sbq_ring(rx_ring);
        }

        if (rx_ring->lbq_len != 0) {
                /*
                 * Allocate large buffer queue.
                 */
                if (ql_alloc_phys(qlge->dip, &rx_ring->lbq_dma.dma_handle,
                    &ql_desc_acc_attr,
                    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
                    &rx_ring->lbq_dma.acc_handle,
                    (size_t)rx_ring->lbq_size,  /* mem size */
                    (size_t)128, /* alignment:128 bytes boundary */
                    (caddr_t *)&rx_ring->lbq_dma.vaddr,
                    &dma_cookie) != 0) {
                        bzero(&rx_ring->lbq_dma, sizeof (rx_ring->lbq_dma));
                        cmn_err(CE_WARN, "%s(%d): lbq allocation failed.",
                            __func__, qlge->instance);
                        goto err_mem;
                }
                rx_ring->lbq_dma.dma_addr = dma_cookie.dmac_laddress;

                /*
                 * Allocate large buffer queue control blocks.
                 */
                rx_ring->lbq_desc =
                    kmem_zalloc(rx_ring->lbq_len * sizeof (struct bq_desc),
                    KM_NOSLEEP);
                if (rx_ring->lbq_desc == NULL) {
                        cmn_err(CE_WARN,
                            "Large buffer queue control block allocation "
                            "failed.");
                        goto err_mem;
                }
                ql_init_lbq_ring(rx_ring);
        }

        if (ql_alloc_phys(qlge->dip, &rx_ring->cqicb_dma.dma_handle,
            &ql_desc_acc_attr,
            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
            &rx_ring->cqicb_dma.acc_handle,
            (size_t)sizeof (struct cqicb_t),  /* mem size */
            (size_t)0, /* alignment:128 bytes boundary */
            (caddr_t *)&rx_ring->cqicb_dma.vaddr,
            &dma_cookie) != 0) {
                bzero(&rx_ring->cqicb_dma, sizeof (rx_ring->cqicb_dma));
                cmn_err(CE_WARN, "%s(%d): cqicb allocation failed.",
                    __func__, qlge->instance);
                goto err_mem;
        }
        rx_ring->cqicb_dma.dma_addr = dma_cookie.dmac_laddress;

        return (DDI_SUCCESS);

err_mem:
        ql_free_rx_resources(rx_ring);
        return (DDI_FAILURE);
}

/*
 * Frees tx/rx queues memory resources
 */
static void
ql_free_mem_resources(qlge_t *qlge)
{
        int i;

        if (qlge->ricb_dma.dma_handle) {
                /* free the ricb struct */
                ql_free_phys(&qlge->ricb_dma.dma_handle,
                    &qlge->ricb_dma.acc_handle);
                bzero(&qlge->ricb_dma, sizeof (qlge->ricb_dma));
        }

        ql_free_rx_buffers(qlge);

        ql_free_ioctl_dma_buf(qlge);

        for (i = 0; i < qlge->tx_ring_count; i++)
                ql_free_tx_resources(&qlge->tx_ring[i]);

        for (i = 0; i < qlge->rx_ring_count; i++)
                ql_free_rx_resources(&qlge->rx_ring[i]);

        ql_free_shadow_space(qlge);
}

/*
 * Allocate buffer queues, large buffers and small buffers etc
 *
 * This API is called in the gld_attach member function. It is called
 * only once.  Later reset,reboot should not re-allocate all rings and
 * buffers.
 */
static int
ql_alloc_mem_resources(qlge_t *qlge)
{
        int i;
        ddi_dma_cookie_t dma_cookie;

        /* Allocate space for our shadow registers */
        if (ql_alloc_shadow_space(qlge))
                return (DDI_FAILURE);

        for (i = 0; i < qlge->rx_ring_count; i++) {
                if (ql_alloc_rx_resources(qlge, &qlge->rx_ring[i]) != 0) {
                        cmn_err(CE_WARN, "RX resource allocation failed.");
                        goto err_mem;
                }
        }
        /* Allocate tx queue resources */
        for (i = 0; i < qlge->tx_ring_count; i++) {
                if (ql_alloc_tx_resources(qlge, &qlge->tx_ring[i]) != 0) {
                        cmn_err(CE_WARN, "Tx resource allocation failed.");
                        goto err_mem;
                }
        }

        if (ql_alloc_ioctl_dma_buf(qlge) != DDI_SUCCESS) {
                goto err_mem;
        }

        if (ql_alloc_rx_buffers(qlge) != DDI_SUCCESS) {
                cmn_err(CE_WARN, "?%s(%d): ql_alloc_rx_buffers failed",
                    __func__, qlge->instance);
                goto err_mem;
        }

        qlge->sequence |= INIT_ALLOC_RX_BUF;

        if (ql_alloc_phys(qlge->dip, &qlge->ricb_dma.dma_handle,
            &ql_desc_acc_attr,
            DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
            &qlge->ricb_dma.acc_handle,
            (size_t)sizeof (struct ricb),  /* mem size */
            (size_t)0, /* alignment:128 bytes boundary */
            (caddr_t *)&qlge->ricb_dma.vaddr,
            &dma_cookie) != 0) {
                bzero(&qlge->ricb_dma, sizeof (qlge->ricb_dma));
                cmn_err(CE_WARN, "%s(%d): ricb allocation failed.",
                    __func__, qlge->instance);
                goto err_mem;
        }
        qlge->ricb_dma.dma_addr = dma_cookie.dmac_laddress;

        return (DDI_SUCCESS);

err_mem:
        ql_free_mem_resources(qlge);
        return (DDI_FAILURE);
}


/*
 * Function used to allocate physical memory and zero it.
 */

static int
ql_alloc_phys_rbuf(dev_info_t *dip, ddi_dma_handle_t *dma_handle,
    ddi_device_acc_attr_t *device_acc_attr,
    uint_t dma_flags,
    ddi_acc_handle_t *acc_handle,
    size_t size,
    size_t alignment,
    caddr_t *vaddr,
    ddi_dma_cookie_t *dma_cookie)
{
        size_t rlen;
        uint_t cnt;

        /*
         * Workaround for SUN XMITS buffer must end and start on 8 byte
         * boundary. Else, hardware will overrun the buffer. Simple fix is
         * to make sure buffer has enough room for overrun.
         */
        if (size & 7) {
                size += 8 - (size & 7);
        }

        /* Adjust the alignment if requested */
        if (alignment) {
                dma_attr.dma_attr_align = alignment;
        }

        /*
         * Allocate DMA handle
         */
        if (ddi_dma_alloc_handle(dip, &dma_attr_rbuf, DDI_DMA_DONTWAIT, NULL,
            dma_handle) != DDI_SUCCESS) {
                cmn_err(CE_WARN, QL_BANG "%s:  ddi_dma_alloc_handle FAILED",
                    __func__);
                *dma_handle = NULL;
                return (QL_ERROR);
        }
        /*
         * Allocate DMA memory
         */
        if (ddi_dma_mem_alloc(*dma_handle, size, device_acc_attr,
            dma_flags & (DDI_DMA_CONSISTENT|DDI_DMA_STREAMING),
            DDI_DMA_DONTWAIT,
            NULL, vaddr, &rlen, acc_handle) != DDI_SUCCESS) {
                cmn_err(CE_WARN, "alloc_phys: DMA Memory alloc Failed");
                ddi_dma_free_handle(dma_handle);
                *acc_handle = NULL;
                *dma_handle = NULL;
                return (QL_ERROR);
        }

        if (ddi_dma_addr_bind_handle(*dma_handle, NULL, *vaddr, rlen,
            dma_flags, DDI_DMA_DONTWAIT, NULL,
            dma_cookie, &cnt) != DDI_DMA_MAPPED) {
                ddi_dma_mem_free(acc_handle);

                ddi_dma_free_handle(dma_handle);
                cmn_err(CE_WARN, "%s ddi_dma_addr_bind_handle FAILED",
                    __func__);
                *acc_handle = NULL;
                *dma_handle = NULL;
                return (QL_ERROR);
        }

        if (cnt != 1) {

                ql_free_phys(dma_handle, acc_handle);

                cmn_err(CE_WARN, "%s: cnt != 1; Failed segment count",
                    __func__);
                return (QL_ERROR);
        }

        bzero((caddr_t)*vaddr, rlen);

        return (0);
}

/*
 * Function used to allocate physical memory and zero it.
 */
static int
ql_alloc_phys(dev_info_t *dip, ddi_dma_handle_t *dma_handle,
    ddi_device_acc_attr_t *device_acc_attr,
    uint_t dma_flags,
    ddi_acc_handle_t *acc_handle,
    size_t size,
    size_t alignment,
    caddr_t *vaddr,
    ddi_dma_cookie_t *dma_cookie)
{
        size_t rlen;
        uint_t cnt;

        /*
         * Workaround for SUN XMITS buffer must end and start on 8 byte
         * boundary. Else, hardware will overrun the buffer. Simple fix is
         * to make sure buffer has enough room for overrun.
         */
        if (size & 7) {
                size += 8 - (size & 7);
        }

        /* Adjust the alignment if requested */
        if (alignment) {
                dma_attr.dma_attr_align = alignment;
        }

        /*
         * Allocate DMA handle
         */
        if (ddi_dma_alloc_handle(dip, &dma_attr, DDI_DMA_DONTWAIT, NULL,
            dma_handle) != DDI_SUCCESS) {
                cmn_err(CE_WARN, QL_BANG "%s:  ddi_dma_alloc_handle FAILED",
                    __func__);
                *dma_handle = NULL;
                return (QL_ERROR);
        }
        /*
         * Allocate DMA memory
         */
        if (ddi_dma_mem_alloc(*dma_handle, size, device_acc_attr,
            dma_flags & (DDI_DMA_CONSISTENT|DDI_DMA_STREAMING),
            DDI_DMA_DONTWAIT,
            NULL, vaddr, &rlen, acc_handle) != DDI_SUCCESS) {
                cmn_err(CE_WARN, "alloc_phys: DMA Memory alloc Failed");
                ddi_dma_free_handle(dma_handle);
                *acc_handle = NULL;
                *dma_handle = NULL;
                return (QL_ERROR);
        }

        if (ddi_dma_addr_bind_handle(*dma_handle, NULL, *vaddr, rlen,
            dma_flags, DDI_DMA_DONTWAIT, NULL,
            dma_cookie, &cnt) != DDI_DMA_MAPPED) {
                ddi_dma_mem_free(acc_handle);
                ddi_dma_free_handle(dma_handle);
                cmn_err(CE_WARN, "%s ddi_dma_addr_bind_handle FAILED",
                    __func__);
                *acc_handle = NULL;
                *dma_handle = NULL;
                return (QL_ERROR);
        }

        if (cnt != 1) {

                ql_free_phys(dma_handle, acc_handle);

                cmn_err(CE_WARN, "%s: cnt != 1; Failed segment count",
                    __func__);
                return (QL_ERROR);
        }

        bzero((caddr_t)*vaddr, rlen);

        return (0);
}

/*
 * Add interrupt handlers based on the interrupt type.
 * Before adding the interrupt handlers, the interrupt vectors should
 * have been allocated, and the rx/tx rings have also been allocated.
 */
static int
ql_add_intr_handlers(qlge_t *qlge)
{
        int vector = 0;
        int rc, i;
        uint32_t value;
        struct intr_ctx *intr_ctx = &qlge->intr_ctx[0];

        switch (qlge->intr_type) {
        case DDI_INTR_TYPE_MSIX:
                /*
                 * Add interrupt handler for rx and tx rings: vector[0 -
                 * (qlge->intr_cnt -1)].
                 */
                value = 0;
                for (vector = 0; vector < qlge->intr_cnt; vector++) {
                        ql_atomic_set_32(&intr_ctx->irq_cnt, value);

                        /*
                         * associate interrupt vector with interrupt handler
                         */
                        rc = ddi_intr_add_handler(qlge->htable[vector],
                            (ddi_intr_handler_t *)intr_ctx->handler,
                            (void *)&qlge->rx_ring[vector], NULL);

                        QL_PRINT(DBG_INIT, ("rx_ring[%d] 0x%p\n",
                            vector, &qlge->rx_ring[vector]));
                        if (rc != DDI_SUCCESS) {
                                QL_PRINT(DBG_INIT,
                                    ("Add rx interrupt handler failed. "
                                    "return: %d, vector: %d", rc, vector));
                                for (vector--; vector >= 0; vector--) {
                                        (void) ddi_intr_remove_handler(
                                            qlge->htable[vector]);
                                }
                                return (DDI_FAILURE);
                        }
                        intr_ctx++;
                }
                break;

        case DDI_INTR_TYPE_MSI:
                /*
                 * Add interrupt handlers for the only vector
                 */
                ql_atomic_set_32(&intr_ctx->irq_cnt, value);

                rc = ddi_intr_add_handler(qlge->htable[vector],
                    ql_isr,
                    (caddr_t)&qlge->rx_ring[0], NULL);

                if (rc != DDI_SUCCESS) {
                        QL_PRINT(DBG_INIT,
                            ("Add MSI interrupt handler failed: %d\n", rc));
                        return (DDI_FAILURE);
                }
                break;

        case DDI_INTR_TYPE_FIXED:
                /*
                 * Add interrupt handlers for the only vector
                 */
                ql_atomic_set_32(&intr_ctx->irq_cnt, value);

                rc = ddi_intr_add_handler(qlge->htable[vector],
                    ql_isr,
                    (caddr_t)&qlge->rx_ring[0], NULL);

                if (rc != DDI_SUCCESS) {
                        QL_PRINT(DBG_INIT,
                            ("Add legacy interrupt handler failed: %d\n", rc));
                        return (DDI_FAILURE);
                }
                break;

        default:
                return (DDI_FAILURE);
        }

        /* Enable interrupts */
        /* Block enable */
        if (qlge->intr_cap & DDI_INTR_FLAG_BLOCK) {
                QL_PRINT(DBG_INIT, ("Block enabling %d interrupt(s)\n",
                    qlge->intr_cnt));
                (void) ddi_intr_block_enable(qlge->htable, qlge->intr_cnt);
        } else { /* Non block enable */
                for (i = 0; i < qlge->intr_cnt; i++) {
                        QL_PRINT(DBG_INIT, ("Non Block Enabling interrupt %d "
                            "handle 0x%x\n", i, qlge->htable[i]));
                        (void) ddi_intr_enable(qlge->htable[i]);
                }
        }
        qlge->sequence |= INIT_INTR_ENABLED;

        return (DDI_SUCCESS);
}

/*
 * Here we build the intr_ctx structures based on
 * our rx_ring count and intr vector count.
 * The intr_ctx structure is used to hook each vector
 * to possibly different handlers.
 */
static void
ql_resolve_queues_to_irqs(qlge_t *qlge)
{
        int i = 0;
        struct intr_ctx *intr_ctx = &qlge->intr_ctx[0];

        if (qlge->intr_type == DDI_INTR_TYPE_MSIX) {
                /*
                 * Each rx_ring has its own intr_ctx since we
                 * have separate vectors for each queue.
                 * This only true when MSI-X is enabled.
                 */
                for (i = 0; i < qlge->intr_cnt; i++, intr_ctx++) {
                        qlge->rx_ring[i].irq = i;
                        intr_ctx->intr = i;
                        intr_ctx->qlge = qlge;

                        /*
                         * We set up each vectors enable/disable/read bits so
                         * there's no bit/mask calculations in critical path.
                         */
                        intr_ctx->intr_en_mask =
                            INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                            INTR_EN_TYPE_ENABLE | INTR_EN_IHD_MASK |
                            INTR_EN_IHD | i;
                        intr_ctx->intr_dis_mask =
                            INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                            INTR_EN_TYPE_DISABLE | INTR_EN_IHD_MASK |
                            INTR_EN_IHD | i;
                        intr_ctx->intr_read_mask =
                            INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                            INTR_EN_TYPE_READ | INTR_EN_IHD_MASK | INTR_EN_IHD
                            | i;

                        if (i == 0) {
                                /*
                                 * Default queue handles bcast/mcast plus
                                 * async events.
                                 */
                                intr_ctx->handler = ql_isr;
                        } else if (qlge->rx_ring[i].type == TX_Q) {
                                /*
                                 * Outbound queue is for outbound completions
                                 * only.
                                 */
                                if (qlge->isr_stride)
                                        intr_ctx->handler = ql_msix_isr;
                                else
                                        intr_ctx->handler = ql_msix_tx_isr;
                        } else {
                                /*
                                 * Inbound queues handle unicast frames only.
                                 */
                                if (qlge->isr_stride)
                                        intr_ctx->handler = ql_msix_isr;
                                else
                                        intr_ctx->handler = ql_msix_rx_isr;
                        }
                }
                i = qlge->intr_cnt;
                for (; i < qlge->rx_ring_count; i++, intr_ctx++) {
                        int iv = i - qlge->isr_stride;
                        qlge->rx_ring[i].irq = iv;
                        intr_ctx->intr = iv;
                        intr_ctx->qlge = qlge;

                        /*
                         * We set up each vectors enable/disable/read bits so
                         * there's no bit/mask calculations in critical path.
                         */
                        intr_ctx->intr_en_mask =
                            INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                            INTR_EN_TYPE_ENABLE | INTR_EN_IHD_MASK |
                            INTR_EN_IHD | iv;
                        intr_ctx->intr_dis_mask =
                            INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                            INTR_EN_TYPE_DISABLE | INTR_EN_IHD_MASK |
                            INTR_EN_IHD | iv;
                        intr_ctx->intr_read_mask =
                            INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                            INTR_EN_TYPE_READ | INTR_EN_IHD_MASK | INTR_EN_IHD
                            | iv;

                        if (qlge->rx_ring[i].type == TX_Q) {
                                /*
                                 * Outbound queue is for outbound completions
                                 * only.
                                 */
                                intr_ctx->handler = ql_msix_isr;
                        } else {
                                /*
                                 * Inbound queues handle unicast frames only.
                                 */
                                intr_ctx->handler = ql_msix_rx_isr;
                        }
                }
        } else {
                /*
                 * All rx_rings use the same intr_ctx since
                 * there is only one vector.
                 */
                intr_ctx->intr = 0;
                intr_ctx->qlge = qlge;
                /*
                 * We set up each vectors enable/disable/read bits so
                 * there's no bit/mask calculations in the critical path.
                 */
                intr_ctx->intr_en_mask =
                    INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                    INTR_EN_TYPE_ENABLE;
                intr_ctx->intr_dis_mask =
                    INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                    INTR_EN_TYPE_DISABLE;
                intr_ctx->intr_read_mask =
                    INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                    INTR_EN_TYPE_READ;
                /*
                 * Single interrupt means one handler for all rings.
                 */
                intr_ctx->handler = ql_isr;
                for (i = 0; i < qlge->rx_ring_count; i++)
                        qlge->rx_ring[i].irq = 0;
        }
}


/*
 * Free allocated interrupts.
 */
static void
ql_free_irq_vectors(qlge_t *qlge)
{
        int i;
        int rc;

        if (qlge->sequence & INIT_INTR_ENABLED) {
                /* Disable all interrupts */
                if (qlge->intr_cap & DDI_INTR_FLAG_BLOCK) {
                        /* Call ddi_intr_block_disable() */
                        (void) ddi_intr_block_disable(qlge->htable,
                            qlge->intr_cnt);
                } else {
                        for (i = 0; i < qlge->intr_cnt; i++) {
                                (void) ddi_intr_disable(qlge->htable[i]);
                        }
                }

                qlge->sequence &= ~INIT_INTR_ENABLED;
        }

        for (i = 0; i < qlge->intr_cnt; i++) {

                if (qlge->sequence & INIT_ADD_INTERRUPT)
                        (void) ddi_intr_remove_handler(qlge->htable[i]);

                if (qlge->sequence & INIT_INTR_ALLOC) {
                        rc = ddi_intr_free(qlge->htable[i]);
                        if (rc != DDI_SUCCESS) {
                                /* EMPTY */
                                QL_PRINT(DBG_INIT, ("Free intr failed: %d",
                                    rc));
                        }
                }
        }
        if (qlge->sequence & INIT_INTR_ALLOC)
                qlge->sequence &= ~INIT_INTR_ALLOC;

        if (qlge->sequence & INIT_ADD_INTERRUPT)
                qlge->sequence &= ~INIT_ADD_INTERRUPT;

        if (qlge->htable) {
                kmem_free(qlge->htable, qlge->intr_size);
                qlge->htable = NULL;
        }
}

/*
 * Allocate interrupt vectors
 * For legacy and MSI, only 1 handle is needed.
 * For MSI-X, if fewer than 2 vectors are available, return failure.
 * Upon success, this maps the vectors to rx and tx rings for
 * interrupts.
 */
static int
ql_request_irq_vectors(qlge_t *qlge, int intr_type)
{
        dev_info_t *devinfo;
        uint32_t request, orig;
        int count, avail, actual;
        int minimum;
        int rc;

        devinfo = qlge->dip;

        switch (intr_type) {
        case DDI_INTR_TYPE_FIXED:
                request = 1;    /* Request 1 legacy interrupt handle */
                minimum = 1;
                QL_PRINT(DBG_INIT, ("interrupt type: legacy\n"));
                break;

        case DDI_INTR_TYPE_MSI:
                request = 1;    /* Request 1 MSI interrupt handle */
                minimum = 1;
                QL_PRINT(DBG_INIT, ("interrupt type: MSI\n"));
                break;

        case DDI_INTR_TYPE_MSIX:
                /*
                 * Ideal number of vectors for the adapter is
                 * # rss rings + tx completion rings for default completion
                 * queue.
                 */
                request = qlge->rx_ring_count;

                orig = request;
                if (request > (MAX_RX_RINGS))
                        request = MAX_RX_RINGS;
                minimum = 2;
                QL_PRINT(DBG_INIT, ("interrupt type: MSI-X\n"));
                break;

        default:
                QL_PRINT(DBG_INIT, ("Invalid parameter\n"));
                return (DDI_FAILURE);
        }

        QL_PRINT(DBG_INIT, ("interrupt handles requested: %d  minimum: %d\n",
            request, minimum));

        /*
         * Get number of supported interrupts
         */
        rc = ddi_intr_get_nintrs(devinfo, intr_type, &count);
        if ((rc != DDI_SUCCESS) || (count < minimum)) {
                QL_PRINT(DBG_INIT, ("Get interrupt number failed. Return: %d, "
                    "count: %d\n", rc, count));
                return (DDI_FAILURE);
        }
        QL_PRINT(DBG_INIT, ("interrupts supported: %d\n", count));

        /*
         * Get number of available interrupts
         */
        rc = ddi_intr_get_navail(devinfo, intr_type, &avail);
        if ((rc != DDI_SUCCESS) || (avail < minimum)) {
                QL_PRINT(DBG_INIT,
                    ("Get interrupt available number failed. Return:"
                    " %d, available: %d\n", rc, avail));
                return (DDI_FAILURE);
        }
        QL_PRINT(DBG_INIT, ("interrupts available: %d\n", avail));

        if (avail < request) {
                QL_PRINT(DBG_INIT, ("Request %d handles, %d available\n",
                    request, avail));
                request = avail;
        }

        actual = 0;
        qlge->intr_cnt = 0;

        /*
         * Allocate an array of interrupt handles
         */
        qlge->intr_size = (size_t)(request * sizeof (ddi_intr_handle_t));
        qlge->htable = kmem_alloc(qlge->intr_size, KM_SLEEP);

        rc = ddi_intr_alloc(devinfo, qlge->htable, intr_type, 0,
            (int)request, &actual, DDI_INTR_ALLOC_NORMAL);
        if (rc != DDI_SUCCESS) {
                cmn_err(CE_WARN, "%s(%d) Allocate interrupts failed. return:"
                    " %d, request: %d, actual: %d",
                    __func__, qlge->instance, rc, request, actual);
                goto ql_intr_alloc_fail;
        }
        qlge->intr_cnt = actual;

        qlge->sequence |= INIT_INTR_ALLOC;

        /*
         * If the actual number of vectors is less than the minumum
         * then fail.
         */
        if (actual < minimum) {
                cmn_err(CE_WARN,
                    "Insufficient interrupt handles available: %d", actual);
                goto ql_intr_alloc_fail;
        }

        /*
         * For MSI-X, actual might force us to reduce number of tx & rx rings
         */
        if ((intr_type == DDI_INTR_TYPE_MSIX) && (orig > actual)) {
                if (actual >= (orig / 2)) {
                        count = orig / 2;
                        qlge->rss_ring_count = count;
                        qlge->tx_ring_count = count;
                        qlge->isr_stride = count;
                } else if (actual >= (orig / 4)) {
                        count = orig / 4;
                        qlge->rss_ring_count = count;
                        qlge->tx_ring_count = count;
                        qlge->isr_stride = count;
                } else if (actual >= (orig / 8)) {
                        count = orig / 8;
                        qlge->rss_ring_count = count;
                        qlge->tx_ring_count = count;
                        qlge->isr_stride = count;
                } else if (actual < MAX_RX_RINGS) {
                        qlge->tx_ring_count = 1;
                        qlge->rss_ring_count = actual - 1;
                }
                qlge->intr_cnt = count;
                qlge->rx_ring_count = qlge->tx_ring_count +
                    qlge->rss_ring_count;
        }
        cmn_err(CE_NOTE, "!qlge(%d) tx %d, rss %d, stride %d\n", qlge->instance,
            qlge->tx_ring_count, qlge->rss_ring_count, qlge->isr_stride);

        /*
         * Get priority for first vector, assume remaining are all the same
         */
        rc = ddi_intr_get_pri(qlge->htable[0], &qlge->intr_pri);
        if (rc != DDI_SUCCESS) {
                QL_PRINT(DBG_INIT, ("Get interrupt priority failed: %d\n", rc));
                goto ql_intr_alloc_fail;
        }

        rc = ddi_intr_get_cap(qlge->htable[0], &qlge->intr_cap);
        if (rc != DDI_SUCCESS) {
                QL_PRINT(DBG_INIT, ("Get interrupt cap failed: %d\n", rc));
                goto ql_intr_alloc_fail;
        }

        qlge->intr_type = intr_type;

        return (DDI_SUCCESS);

ql_intr_alloc_fail:
        ql_free_irq_vectors(qlge);

        return (DDI_FAILURE);
}

/*
 * Allocate interrupt vector(s) for one of the following interrupt types, MSI-X,
 * MSI or Legacy. In MSI and Legacy modes we only support a single receive and
 * transmit queue.
 */
int
ql_alloc_irqs(qlge_t *qlge)
{
        int intr_types;
        int rval;

        /*
         * Get supported interrupt types
         */
        if (ddi_intr_get_supported_types(qlge->dip, &intr_types)
            != DDI_SUCCESS) {
                cmn_err(CE_WARN, "%s(%d):ddi_intr_get_supported_types failed",
                    __func__, qlge->instance);

                return (DDI_FAILURE);
        }

        QL_PRINT(DBG_INIT, ("%s(%d) Interrupt types supported %d\n",
            __func__, qlge->instance, intr_types));

        /* Install MSI-X interrupts */
        if ((intr_types & DDI_INTR_TYPE_MSIX) != 0) {
                QL_PRINT(DBG_INIT, ("%s(%d) MSI-X interrupt supported %d\n",
                    __func__, qlge->instance, intr_types));
                rval = ql_request_irq_vectors(qlge, DDI_INTR_TYPE_MSIX);
                if (rval == DDI_SUCCESS) {
                        return (rval);
                }
                QL_PRINT(DBG_INIT, ("%s(%d) MSI-X interrupt allocation failed,"
                    " trying MSI interrupts ...\n", __func__, qlge->instance));
        }

        /*
         * We will have 2 completion queues in MSI / Legacy mode,
         * Queue 0 for default completions
         * Queue 1 for transmit completions
         */
        qlge->rss_ring_count = 1; /* Default completion queue (0) for all */
        qlge->tx_ring_count = 1; /* Single tx completion queue */
        qlge->rx_ring_count = qlge->tx_ring_count + qlge->rss_ring_count;

        QL_PRINT(DBG_INIT, ("%s(%d) Falling back to single completion queue \n",
            __func__, qlge->instance));
        /*
         * Add the h/w interrupt handler and initialise mutexes
         */
        rval = DDI_FAILURE;

        /*
         * If OS supports MSIX interrupt but fails to allocate, then try
         * MSI interrupt. If MSI interrupt allocation fails also, then roll
         * back to fixed interrupt.
         */
        if (intr_types & DDI_INTR_TYPE_MSI) {
                rval = ql_request_irq_vectors(qlge, DDI_INTR_TYPE_MSI);
                if (rval == DDI_SUCCESS) {
                        qlge->intr_type = DDI_INTR_TYPE_MSI;
                        QL_PRINT(DBG_INIT, ("%s(%d) use MSI Interrupt \n",
                            __func__, qlge->instance));
                }
        }

        /* Try Fixed interrupt Legacy mode */
        if (rval != DDI_SUCCESS) {
                rval = ql_request_irq_vectors(qlge, DDI_INTR_TYPE_FIXED);
                if (rval != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "%s(%d):Legacy mode interrupt "
                            "allocation failed",
                            __func__, qlge->instance);
                } else {
                        qlge->intr_type = DDI_INTR_TYPE_FIXED;
                        QL_PRINT(DBG_INIT, ("%s(%d) use Fixed Interrupt \n",
                            __func__, qlge->instance));
                }
        }

        return (rval);
}

static void
ql_free_rx_tx_locks(qlge_t *qlge)
{
        int i;
        struct rx_ring *rx_ring;
        struct tx_ring *tx_ring;

        for (i = 0; i < qlge->tx_ring_count; i++) {
                tx_ring = &qlge->tx_ring[i];
                mutex_destroy(&tx_ring->tx_lock);
        }

        for (i = 0; i < qlge->rx_ring_count; i++) {
                rx_ring = &qlge->rx_ring[i];
                mutex_destroy(&rx_ring->rx_lock);
                mutex_destroy(&rx_ring->sbq_lock);
                mutex_destroy(&rx_ring->lbq_lock);
        }
}

/*
 * Frees all resources allocated during attach.
 *
 * Input:
 * dip = pointer to device information structure.
 * sequence = bits indicating resources to free.
 *
 * Context:
 * Kernel context.
 */
static void
ql_free_resources(qlge_t *qlge)
{

        /* Disable driver timer */
        ql_stop_timer(qlge);

        if (qlge->sequence & INIT_MAC_REGISTERED) {
                (void) mac_unregister(qlge->mh);
                qlge->sequence &= ~INIT_MAC_REGISTERED;
        }

        if (qlge->sequence & INIT_MAC_ALLOC) {
                /* Nothing to do, macp is already freed */
                qlge->sequence &= ~INIT_MAC_ALLOC;
        }

        if (qlge->sequence & INIT_PCI_CONFIG_SETUP) {
                pci_config_teardown(&qlge->pci_handle);
                qlge->sequence &= ~INIT_PCI_CONFIG_SETUP;
        }

        if (qlge->sequence & INIT_INTR_ALLOC) {
                ql_free_irq_vectors(qlge);
                qlge->sequence &= ~INIT_ADD_INTERRUPT;
        }

        if (qlge->sequence & INIT_ADD_SOFT_INTERRUPT) {
                (void) ddi_intr_remove_softint(qlge->mpi_event_intr_hdl);
                (void) ddi_intr_remove_softint(qlge->mpi_reset_intr_hdl);
                (void) ddi_intr_remove_softint(qlge->asic_reset_intr_hdl);
                qlge->sequence &= ~INIT_ADD_SOFT_INTERRUPT;
        }

        if (qlge->sequence & INIT_KSTATS) {
                ql_fini_kstats(qlge);
                qlge->sequence &= ~INIT_KSTATS;
        }

        if (qlge->sequence & INIT_MUTEX) {
                mutex_destroy(&qlge->gen_mutex);
                mutex_destroy(&qlge->hw_mutex);
                mutex_destroy(&qlge->mbx_mutex);
                cv_destroy(&qlge->cv_mbx_intr);
                qlge->sequence &= ~INIT_MUTEX;
        }

        if (qlge->sequence & INIT_LOCKS_CREATED) {
                ql_free_rx_tx_locks(qlge);
                qlge->sequence &= ~INIT_LOCKS_CREATED;
        }

        if (qlge->sequence & INIT_MEMORY_ALLOC) {
                ql_free_mem_resources(qlge);
                qlge->sequence &= ~INIT_MEMORY_ALLOC;
        }

        if (qlge->sequence & INIT_REGS_SETUP) {
                ddi_regs_map_free(&qlge->dev_handle);
                qlge->sequence &= ~INIT_REGS_SETUP;
        }

        if (qlge->sequence & INIT_DOORBELL_REGS_SETUP) {
                ddi_regs_map_free(&qlge->dev_doorbell_reg_handle);
                qlge->sequence &= ~INIT_DOORBELL_REGS_SETUP;
        }

        /*
         * free flash flt table that allocated in attach stage
         */
        if ((qlge->flt.ql_flt_entry_ptr != NULL)&&
            (qlge->flt.header.length != 0)) {
                kmem_free(qlge->flt.ql_flt_entry_ptr, qlge->flt.header.length);
                qlge->flt.ql_flt_entry_ptr = NULL;
        }

        if (qlge->sequence & INIT_FM) {
                ql_fm_fini(qlge);
                qlge->sequence &= ~INIT_FM;
        }

        ddi_prop_remove_all(qlge->dip);
        ddi_set_driver_private(qlge->dip, NULL);

        /* finally, free qlge structure */
        if (qlge->sequence & INIT_SOFTSTATE_ALLOC) {
                kmem_free(qlge, sizeof (qlge_t));
        }
}

/*
 * Set promiscuous mode of the driver
 * Caller must catch HW_LOCK
 */
void
ql_set_promiscuous(qlge_t *qlge, int mode)
{
        if (mode) {
                (void) ql_set_routing_reg(qlge, RT_IDX_PROMISCUOUS_SLOT,
                    RT_IDX_VALID, 1);
        } else {
                (void) ql_set_routing_reg(qlge, RT_IDX_PROMISCUOUS_SLOT,
                    RT_IDX_VALID, 0);
        }
}
/*
 * Write 'data1' to Mac Protocol Address Index Register and
 * 'data2' to Mac Protocol Address Data Register
 *  Assuming that the Mac Protocol semaphore lock has been acquired.
 */
static int
ql_write_mac_proto_regs(qlge_t *qlge, uint32_t data1, uint32_t data2)
{
        int return_value = DDI_SUCCESS;

        if (ql_wait_reg_bit(qlge, REG_MAC_PROTOCOL_ADDRESS_INDEX,
            MAC_PROTOCOL_ADDRESS_INDEX_MW, BIT_SET, 5) != DDI_SUCCESS) {
                cmn_err(CE_WARN, "Wait for MAC_PROTOCOL Address Register "
                    "timeout.");
                return_value = DDI_FAILURE;
                goto out;
        }
        ql_write_reg(qlge, REG_MAC_PROTOCOL_ADDRESS_INDEX /* A8 */, data1);
        ql_write_reg(qlge, REG_MAC_PROTOCOL_DATA /* 0xAC */, data2);
out:
        return (return_value);
}
/*
 * Enable the 'index'ed multicast address in the host memory's multicast_list
 */
int
ql_add_multicast_address(qlge_t *qlge, int index)
{
        int rtn_val = DDI_FAILURE;
        uint32_t offset;
        uint32_t value1, value2;

        /* Acquire the required semaphore */
        if (ql_sem_spinlock(qlge, QL_MAC_PROTOCOL_SEM_MASK) != DDI_SUCCESS) {
                return (rtn_val);
        }

        /* Program Offset0 - lower 32 bits of the MAC address */
        offset = 0;
        value1 = MAC_PROTOCOL_ADDRESS_ENABLE | MAC_PROTOCOL_TYPE_MULTICAST |
            (index << 4) | offset;
        value2 = ((qlge->multicast_list[index].addr.ether_addr_octet[2] << 24)
            |(qlge->multicast_list[index].addr.ether_addr_octet[3] << 16)
            |(qlge->multicast_list[index].addr.ether_addr_octet[4] << 8)
            |(qlge->multicast_list[index].addr.ether_addr_octet[5]));
        if (ql_write_mac_proto_regs(qlge, value1, value2) != DDI_SUCCESS)
                goto out;

        /* Program offset1: upper 16 bits of the MAC address */
        offset = 1;
        value1 = MAC_PROTOCOL_ADDRESS_ENABLE | MAC_PROTOCOL_TYPE_MULTICAST |
            (index<<4) | offset;
        value2 = ((qlge->multicast_list[index].addr.ether_addr_octet[0] << 8)
            |qlge->multicast_list[index].addr.ether_addr_octet[1]);
        if (ql_write_mac_proto_regs(qlge, value1, value2) != DDI_SUCCESS) {
                goto out;
        }
        rtn_val = DDI_SUCCESS;
out:
        ql_sem_unlock(qlge, QL_MAC_PROTOCOL_SEM_MASK);
        return (rtn_val);
}

/*
 * Disable the 'index'ed multicast address in the host memory's multicast_list
 */
int
ql_remove_multicast_address(qlge_t *qlge, int index)
{
        int rtn_val = DDI_FAILURE;
        uint32_t offset;
        uint32_t value1, value2;

        /* Acquire the required semaphore */
        if (ql_sem_spinlock(qlge, QL_MAC_PROTOCOL_SEM_MASK) != DDI_SUCCESS) {
                return (rtn_val);
        }
        /* Program Offset0 - lower 32 bits of the MAC address */
        offset = 0;
        value1 = (MAC_PROTOCOL_TYPE_MULTICAST | offset)|(index<<4);
        value2 =
            ((qlge->multicast_list[index].addr.ether_addr_octet[2] << 24)
            |(qlge->multicast_list[index].addr.ether_addr_octet[3] << 16)
            |(qlge->multicast_list[index].addr.ether_addr_octet[4] << 8)
            |(qlge->multicast_list[index].addr.ether_addr_octet[5]));
        if (ql_write_mac_proto_regs(qlge, value1, value2) != DDI_SUCCESS) {
                goto out;
        }
        /* Program offset1: upper 16 bits of the MAC address */
        offset = 1;
        value1 = (MAC_PROTOCOL_TYPE_MULTICAST | offset)|(index<<4);
        value2 = 0;
        if (ql_write_mac_proto_regs(qlge, value1, value2) != DDI_SUCCESS) {
                goto out;
        }
        rtn_val = DDI_SUCCESS;
out:
        ql_sem_unlock(qlge, QL_MAC_PROTOCOL_SEM_MASK);
        return (rtn_val);
}

/*
 * Add a new multicast address to the list of supported list
 * This API is called after OS called gld_set_multicast (GLDv2)
 * or m_multicst (GLDv3)
 *
 * Restriction:
 * The number of maximum multicast address is limited by hardware.
 */
int
ql_add_to_multicast_list(qlge_t *qlge, uint8_t *ep)
{
        uint32_t index = qlge->multicast_list_count;
        int rval = DDI_SUCCESS;
        int status;

        if ((ep[0] & 01) == 0) {
                rval = EINVAL;
                goto exit;
        }

        /* if there is an availabe space in multicast_list, then add it */
        if (index < MAX_MULTICAST_LIST_SIZE) {
                bcopy(ep, qlge->multicast_list[index].addr.ether_addr_octet,
                    ETHERADDRL);
                /* increment the total number of addresses in multicast list */
                (void) ql_add_multicast_address(qlge, index);
                qlge->multicast_list_count++;
                QL_PRINT(DBG_GLD,
                    ("%s(%d): added to index of multicast list= 0x%x, "
                    "total %d\n", __func__, qlge->instance, index,
                    qlge->multicast_list_count));

                if (index > MAX_MULTICAST_HW_SIZE) {
                        if (!qlge->multicast_promisc) {
                                status = ql_set_routing_reg(qlge,
                                    RT_IDX_ALLMULTI_SLOT,
                                    RT_IDX_MCAST, 1);
                                if (status) {
                                        cmn_err(CE_WARN,
                                            "Failed to init routing reg "
                                            "for mcast promisc mode.");
                                        rval = ENOENT;
                                        goto exit;
                                }
                                qlge->multicast_promisc = B_TRUE;
                        }
                }
        } else {
                rval = ENOENT;
        }
exit:
        return (rval);
}

/*
 * Remove an old multicast address from the list of supported multicast
 * addresses. This API is called after OS called gld_set_multicast (GLDv2)
 * or m_multicst (GLDv3)
 * The number of maximum multicast address is limited by hardware.
 */
int
ql_remove_from_multicast_list(qlge_t *qlge, uint8_t *ep)
{
        uint32_t total = qlge->multicast_list_count;
        int i = 0;
        int rmv_index = 0;
        size_t length = sizeof (ql_multicast_addr);
        int status;

        for (i = 0; i < total; i++) {
                if (bcmp(ep, &qlge->multicast_list[i].addr, ETHERADDRL) != 0) {
                        continue;
                }

                rmv_index = i;
                /* block move the reset of other multicast address forward */
                length = ((total -1) -i) * sizeof (ql_multicast_addr);
                if (length > 0) {
                        bcopy(&qlge->multicast_list[i+1],
                            &qlge->multicast_list[i], length);
                }
                qlge->multicast_list_count--;
                if (qlge->multicast_list_count <= MAX_MULTICAST_HW_SIZE) {
                        /*
                         * there is a deletion in multicast list table,
                         * re-enable them
                         */
                        for (i = rmv_index; i < qlge->multicast_list_count;
                            i++) {
                                (void) ql_add_multicast_address(qlge, i);
                        }
                        /* and disable the last one */
                        (void) ql_remove_multicast_address(qlge, i);

                        /* disable multicast promiscuous mode */
                        if (qlge->multicast_promisc) {
                                status = ql_set_routing_reg(qlge,
                                    RT_IDX_ALLMULTI_SLOT,
                                    RT_IDX_MCAST, 0);
                                if (status) {
                                        cmn_err(CE_WARN,
                                            "Failed to init routing reg for "
                                            "mcast promisc mode.");
                                        goto exit;
                                }
                                /* write to config register */
                                qlge->multicast_promisc = B_FALSE;
                        }
                }
                break;
        }
exit:
        return (DDI_SUCCESS);
}

/*
 * Read a XGMAC register
 */
int
ql_read_xgmac_reg(qlge_t *qlge, uint32_t addr, uint32_t *val)
{
        int rtn_val = DDI_FAILURE;

        /* wait for XGMAC Address register RDY bit set */
        if (ql_wait_reg_bit(qlge, REG_XGMAC_ADDRESS, XGMAC_ADDRESS_RDY,
            BIT_SET, 10) != DDI_SUCCESS) {
                goto out;
        }
        /* start rx transaction */
        ql_write_reg(qlge, REG_XGMAC_ADDRESS, addr|XGMAC_ADDRESS_READ_TRANSACT);

        /*
         * wait for XGMAC Address register RDY bit set,
         * which indicates data is ready
         */
        if (ql_wait_reg_bit(qlge, REG_XGMAC_ADDRESS, XGMAC_ADDRESS_RDY,
            BIT_SET, 10) != DDI_SUCCESS) {
                goto out;
        }
        /* read data from XGAMC_DATA register */
        *val = ql_read_reg(qlge, REG_XGMAC_DATA);
        rtn_val = DDI_SUCCESS;
out:
        return (rtn_val);
}

/*
 * Implement checksum offload for IPv4 IP packets
 */
static void
ql_hw_csum_setup(qlge_t *qlge, uint32_t pflags, caddr_t bp,
    struct ob_mac_iocb_req *mac_iocb_ptr)
{
        struct ip *iphdr = NULL;
        struct ether_header *ethhdr;
        struct ether_vlan_header *ethvhdr;
        struct tcphdr *tcp_hdr;
        uint32_t etherType;
        int mac_hdr_len, ip_hdr_len, tcp_udp_hdr_len;
        int ip_hdr_off, tcp_udp_hdr_off, hdr_off;

        ethhdr  = (struct ether_header *)((void *)bp);
        ethvhdr = (struct ether_vlan_header *)((void *)bp);
        /* Is this vlan packet? */
        if (ntohs(ethvhdr->ether_tpid) == ETHERTYPE_VLAN) {
                mac_hdr_len = sizeof (struct ether_vlan_header);
                etherType = ntohs(ethvhdr->ether_type);
        } else {
                mac_hdr_len = sizeof (struct ether_header);
                etherType = ntohs(ethhdr->ether_type);
        }
        /* Is this IPv4 or IPv6 packet? */
        if (IPH_HDR_VERSION((ipha_t *)(void *)(bp+mac_hdr_len)) ==
            IPV4_VERSION) {
                if (etherType == ETHERTYPE_IP /* 0800 */) {
                        iphdr = (struct ip *)(void *)(bp+mac_hdr_len);
                } else {
                        /* EMPTY */
                        QL_PRINT(DBG_TX,
                            ("%s(%d) : IPv4 None IP packet type 0x%x\n",
                            __func__, qlge->instance, etherType));
                }
        }
        /* ipV4 packets */
        if (iphdr != NULL) {

                ip_hdr_len = IPH_HDR_LENGTH(iphdr);
                QL_PRINT(DBG_TX,
                    ("%s(%d) : IPv4 header length using IPH_HDR_LENGTH:"
                    " %d bytes \n", __func__, qlge->instance, ip_hdr_len));

                ip_hdr_off = mac_hdr_len;
                QL_PRINT(DBG_TX, ("%s(%d) : ip_hdr_len=%d\n",
                    __func__, qlge->instance, ip_hdr_len));

                mac_iocb_ptr->flag0 = (uint8_t)(mac_iocb_ptr->flag0 |
                    OB_MAC_IOCB_REQ_IPv4);

                if (pflags & HCK_IPV4_HDRCKSUM) {
                        QL_PRINT(DBG_TX, ("%s(%d) : Do IPv4 header checksum\n",
                            __func__, qlge->instance));
                        mac_iocb_ptr->opcode = OPCODE_OB_MAC_OFFLOAD_IOCB;
                        mac_iocb_ptr->flag2 = (uint8_t)(mac_iocb_ptr->flag2 |
                            OB_MAC_IOCB_REQ_IC);
                        iphdr->ip_sum = 0;
                        mac_iocb_ptr->hdr_off = (uint16_t)
                            cpu_to_le16(ip_hdr_off);
                }
                if (pflags & HCK_FULLCKSUM) {
                        if (iphdr->ip_p == IPPROTO_TCP) {
                                tcp_hdr =
                                    (struct tcphdr *)(void *)
                                    ((uint8_t *)(void *)iphdr + ip_hdr_len);
                                QL_PRINT(DBG_TX, ("%s(%d) : Do TCP checksum\n",
                                    __func__, qlge->instance));
                                mac_iocb_ptr->opcode =
                                    OPCODE_OB_MAC_OFFLOAD_IOCB;
                                mac_iocb_ptr->flag1 =
                                    (uint8_t)(mac_iocb_ptr->flag1 |
                                    OB_MAC_IOCB_REQ_TC);
                                mac_iocb_ptr->flag2 =
                                    (uint8_t)(mac_iocb_ptr->flag2 |
                                    OB_MAC_IOCB_REQ_IC);
                                iphdr->ip_sum = 0;
                                tcp_udp_hdr_off = mac_hdr_len+ip_hdr_len;
                                tcp_udp_hdr_len = tcp_hdr->th_off*4;
                                QL_PRINT(DBG_TX, ("%s(%d): tcp header len:%d\n",
                                    __func__, qlge->instance, tcp_udp_hdr_len));
                                hdr_off = ip_hdr_off;
                                tcp_udp_hdr_off <<= 6;
                                hdr_off |= tcp_udp_hdr_off;
                                mac_iocb_ptr->hdr_off =
                                    (uint16_t)cpu_to_le16(hdr_off);
                                mac_iocb_ptr->protocol_hdr_len = (uint16_t)
                                    cpu_to_le16(mac_hdr_len + ip_hdr_len +
                                    tcp_udp_hdr_len);

                                /*
                                 * if the chip is unable to do pseudo header
                                 * cksum calculation, do it in then put the
                                 * result to the data passed to the chip
                                 */
                                if (qlge->cfg_flags &
                                    CFG_HW_UNABLE_PSEUDO_HDR_CKSUM) {
                                        ql_pseudo_cksum((uint8_t *)iphdr);
                                }
                        } else if (iphdr->ip_p == IPPROTO_UDP) {
                                QL_PRINT(DBG_TX, ("%s(%d) : Do UDP checksum\n",
                                    __func__, qlge->instance));
                                mac_iocb_ptr->opcode =
                                    OPCODE_OB_MAC_OFFLOAD_IOCB;
                                mac_iocb_ptr->flag1 =
                                    (uint8_t)(mac_iocb_ptr->flag1 |
                                    OB_MAC_IOCB_REQ_UC);
                                mac_iocb_ptr->flag2 =
                                    (uint8_t)(mac_iocb_ptr->flag2 |
                                    OB_MAC_IOCB_REQ_IC);
                                iphdr->ip_sum = 0;
                                tcp_udp_hdr_off = mac_hdr_len + ip_hdr_len;
                                tcp_udp_hdr_len = sizeof (struct udphdr);
                                QL_PRINT(DBG_TX, ("%s(%d):udp header len:%d\n",
                                    __func__, qlge->instance, tcp_udp_hdr_len));
                                hdr_off = ip_hdr_off;
                                tcp_udp_hdr_off <<= 6;
                                hdr_off |= tcp_udp_hdr_off;
                                mac_iocb_ptr->hdr_off =
                                    (uint16_t)cpu_to_le16(hdr_off);
                                mac_iocb_ptr->protocol_hdr_len = (uint16_t)
                                    cpu_to_le16(mac_hdr_len + ip_hdr_len
                                    + tcp_udp_hdr_len);

                                /*
                                 * if the chip is unable to calculate pseudo
                                 * hdr cksum,do it in then put the result to
                                 * the data passed to the chip
                                 */
                                if (qlge->cfg_flags &
                                    CFG_HW_UNABLE_PSEUDO_HDR_CKSUM) {
                                        ql_pseudo_cksum((uint8_t *)iphdr);
                                }
                        }
                }
        }
}

/*
 * For TSO/LSO:
 * MAC frame transmission with TCP large segment offload is performed in the
 * same way as the MAC frame transmission with checksum offload with the
 * exception that the maximum TCP segment size (MSS) must be specified to
 * allow the chip to segment the data into legal sized frames.
 * The host also needs to calculate a pseudo-header checksum over the
 * following fields:
 * Source IP Address, Destination IP Address, and the Protocol.
 * The TCP length is not included in the pseudo-header calculation.
 * The pseudo-header checksum is place in the TCP checksum field of the
 * prototype header.
 */
static void
ql_lso_pseudo_cksum(uint8_t *buf)
{
        uint32_t cksum;
        uint16_t iphl;
        uint16_t proto;

        /*
         * Calculate the LSO pseudo-header checksum.
         */
        iphl = (uint16_t)(4 * (buf[0] & 0xF));
        cksum = proto = buf[9];
        cksum += (((uint16_t)buf[12])<<8) + buf[13];
        cksum += (((uint16_t)buf[14])<<8) + buf[15];
        cksum += (((uint16_t)buf[16])<<8) + buf[17];
        cksum += (((uint16_t)buf[18])<<8) + buf[19];
        cksum = (cksum>>16) + (cksum & 0xFFFF);
        cksum = (cksum>>16) + (cksum & 0xFFFF);

        /*
         * Point it to the TCP/UDP header, and
         * update the checksum field.
         */
        buf += iphl + ((proto == IPPROTO_TCP) ?
            TCP_CKSUM_OFFSET : UDP_CKSUM_OFFSET);

        *(uint16_t *)(void *)buf = (uint16_t)htons((uint16_t)cksum);
}

/*
 * For IPv4 IP packets, distribute the tx packets evenly among tx rings
 */
typedef uint32_t        ub4; /* unsigned 4-byte quantities */
typedef uint8_t         ub1;

#define hashsize(n)     ((ub4)1<<(n))
#define hashmask(n)     (hashsize(n)-1)

#define mix(a, b, c) \
{ \
        a -= b; a -= c; a ^= (c>>13); \
        b -= c; b -= a; b ^= (a<<8); \
        c -= a; c -= b; c ^= (b>>13); \
        a -= b; a -= c; a ^= (c>>12);  \
        b -= c; b -= a; b ^= (a<<16); \
        c -= a; c -= b; c ^= (b>>5); \
        a -= b; a -= c; a ^= (c>>3);  \
        b -= c; b -= a; b ^= (a<<10); \
        c -= a; c -= b; c ^= (b>>15); \
}

ub4
hash(ub1 *k, ub4 length, ub4 initval)
{
        ub4 a, b, c, len;

        /* Set up the internal state */
        len = length;
        a = b = 0x9e3779b9;     /* the golden ratio; an arbitrary value */
        c = initval;            /* the previous hash value */

        /* handle most of the key */
        while (len >= 12) {
                a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
                b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
                c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
                mix(a, b, c);
                k += 12;
                len -= 12;
        }

        /* handle the last 11 bytes */
        c += length;
        /* all the case statements fall through */
        switch (len) {
                /* FALLTHRU */
        case 11: c += ((ub4)k[10]<<24);
                /* FALLTHRU */
        case 10: c += ((ub4)k[9]<<16);
                /* FALLTHRU */
        case 9 : c += ((ub4)k[8]<<8);
        /* the first byte of c is reserved for the length */
                /* FALLTHRU */
        case 8 : b += ((ub4)k[7]<<24);
                /* FALLTHRU */
        case 7 : b += ((ub4)k[6]<<16);
                /* FALLTHRU */
        case 6 : b += ((ub4)k[5]<<8);
                /* FALLTHRU */
        case 5 : b += k[4];
                /* FALLTHRU */
        case 4 : a += ((ub4)k[3]<<24);
                /* FALLTHRU */
        case 3 : a += ((ub4)k[2]<<16);
                /* FALLTHRU */
        case 2 : a += ((ub4)k[1]<<8);
                /* FALLTHRU */
        case 1 : a += k[0];
        /* case 0: nothing left to add */
        }
        mix(a, b, c);
        /* report the result */
        return (c);
}

uint8_t
ql_tx_hashing(qlge_t *qlge, caddr_t bp)
{
        struct ip *iphdr = NULL;
        struct ether_header *ethhdr;
        struct ether_vlan_header *ethvhdr;
        struct tcphdr *tcp_hdr;
        struct udphdr *udp_hdr;
        uint32_t etherType;
        int mac_hdr_len, ip_hdr_len;
        uint32_t h = 0; /* 0 by default */
        uint8_t tx_ring_id = 0;
        uint32_t ip_src_addr = 0;
        uint32_t ip_desc_addr = 0;
        uint16_t src_port = 0;
        uint16_t dest_port = 0;
        uint8_t key[12];
        QL_PRINT(DBG_TX, ("%s(%d) entered \n", __func__, qlge->instance));

        ethhdr = (struct ether_header *)((void *)bp);
        ethvhdr = (struct ether_vlan_header *)((void *)bp);

        if (qlge->tx_ring_count == 1)
                return (tx_ring_id);

        /* Is this vlan packet? */
        if (ntohs(ethvhdr->ether_tpid) == ETHERTYPE_VLAN) {
                mac_hdr_len = sizeof (struct ether_vlan_header);
                etherType = ntohs(ethvhdr->ether_type);
        } else {
                mac_hdr_len = sizeof (struct ether_header);
                etherType = ntohs(ethhdr->ether_type);
        }
        /* Is this IPv4 or IPv6 packet? */
        if (etherType == ETHERTYPE_IP /* 0800 */) {
                if (IPH_HDR_VERSION((ipha_t *)(void *)(bp+mac_hdr_len))
                    == IPV4_VERSION) {
                        iphdr = (struct ip *)(void *)(bp+mac_hdr_len);
                }
                if (((unsigned long)iphdr) & 0x3) {
                        /*  IP hdr not 4-byte aligned */
                        return (tx_ring_id);
                }
        }
        /* ipV4 packets */
        if (iphdr) {

                ip_hdr_len = IPH_HDR_LENGTH(iphdr);
                ip_src_addr = iphdr->ip_src.s_addr;
                ip_desc_addr = iphdr->ip_dst.s_addr;

                if (iphdr->ip_p == IPPROTO_TCP) {
                        tcp_hdr = (struct tcphdr *)(void *)
                            ((uint8_t *)iphdr + ip_hdr_len);
                        src_port = tcp_hdr->th_sport;
                        dest_port = tcp_hdr->th_dport;
                } else if (iphdr->ip_p == IPPROTO_UDP) {
                        udp_hdr = (struct udphdr *)(void *)
                            ((uint8_t *)iphdr + ip_hdr_len);
                        src_port = udp_hdr->uh_sport;
                        dest_port = udp_hdr->uh_dport;
                }
                key[0] = (uint8_t)((ip_src_addr) &0xFF);
                key[1] = (uint8_t)((ip_src_addr >> 8) &0xFF);
                key[2] = (uint8_t)((ip_src_addr >> 16) &0xFF);
                key[3] = (uint8_t)((ip_src_addr >> 24) &0xFF);
                key[4] = (uint8_t)((ip_desc_addr) &0xFF);
                key[5] = (uint8_t)((ip_desc_addr >> 8) &0xFF);
                key[6] = (uint8_t)((ip_desc_addr >> 16) &0xFF);
                key[7] = (uint8_t)((ip_desc_addr >> 24) &0xFF);
                key[8] = (uint8_t)((src_port) &0xFF);
                key[9] = (uint8_t)((src_port >> 8) &0xFF);
                key[10] = (uint8_t)((dest_port) &0xFF);
                key[11] = (uint8_t)((dest_port >> 8) &0xFF);
                h = hash(key, 12, 0); /* return 32 bit */
                tx_ring_id = (h & (qlge->tx_ring_count - 1));
                if (tx_ring_id >= qlge->tx_ring_count) {
                        cmn_err(CE_WARN, "%s bad tx_ring_id %d\n",
                            __func__, tx_ring_id);
                        tx_ring_id = 0;
                }
        }
        return (tx_ring_id);
}

/*
 * Tell the hardware to do Large Send Offload (LSO)
 *
 * Some fields in ob_mac_iocb need to be set so hardware can know what is
 * the incoming packet, TCP or UDP, whether a VLAN tag needs to be inserted
 * in the right place of the packet etc, thus, hardware can process the
 * packet correctly.
 */
static void
ql_hw_lso_setup(qlge_t *qlge, uint32_t mss, caddr_t bp,
    struct ob_mac_iocb_req *mac_iocb_ptr)
{
        struct ip *iphdr = NULL;
        struct ether_header *ethhdr;
        struct ether_vlan_header *ethvhdr;
        struct tcphdr *tcp_hdr;
        struct udphdr *udp_hdr;
        uint32_t etherType;
        uint16_t mac_hdr_len, ip_hdr_len, tcp_udp_hdr_len;
        uint16_t ip_hdr_off, tcp_udp_hdr_off, hdr_off;

        ethhdr = (struct ether_header *)(void *)bp;
        ethvhdr = (struct ether_vlan_header *)(void *)bp;

        /* Is this vlan packet? */
        if (ntohs(ethvhdr->ether_tpid) == ETHERTYPE_VLAN) {
                mac_hdr_len = sizeof (struct ether_vlan_header);
                etherType = ntohs(ethvhdr->ether_type);
        } else {
                mac_hdr_len = sizeof (struct ether_header);
                etherType = ntohs(ethhdr->ether_type);
        }
        /* Is this IPv4 or IPv6 packet? */
        if (IPH_HDR_VERSION((ipha_t *)(void *)(bp + mac_hdr_len)) ==
            IPV4_VERSION) {
                if (etherType == ETHERTYPE_IP /* 0800 */) {
                        iphdr = (struct ip *)(void *)(bp+mac_hdr_len);
                } else {
                        /* EMPTY */
                        QL_PRINT(DBG_TX, ("%s(%d) : IPv4 None IP packet"
                            " type 0x%x\n",
                            __func__, qlge->instance, etherType));
                }
        }

        if (iphdr != NULL) { /* ipV4 packets */
                ip_hdr_len = (uint16_t)IPH_HDR_LENGTH(iphdr);
                QL_PRINT(DBG_TX,
                    ("%s(%d) : IPv4 header length using IPH_HDR_LENGTH: %d"
                    " bytes \n", __func__, qlge->instance, ip_hdr_len));

                ip_hdr_off = mac_hdr_len;
                QL_PRINT(DBG_TX, ("%s(%d) : ip_hdr_len=%d\n",
                    __func__, qlge->instance, ip_hdr_len));

                mac_iocb_ptr->flag0 = (uint8_t)(mac_iocb_ptr->flag0 |
                    OB_MAC_IOCB_REQ_IPv4);
                if (qlge->cfg_flags & CFG_CKSUM_FULL_IPv4) {
                        if (iphdr->ip_p == IPPROTO_TCP) {
                                tcp_hdr = (struct tcphdr *)(void *)
                                    ((uint8_t *)(void *)iphdr +
                                    ip_hdr_len);
                                QL_PRINT(DBG_TX, ("%s(%d) : Do TSO on TCP "
                                    "packet\n",
                                    __func__, qlge->instance));
                                mac_iocb_ptr->opcode =
                                    OPCODE_OB_MAC_OFFLOAD_IOCB;
                                mac_iocb_ptr->flag1 =
                                    (uint8_t)(mac_iocb_ptr->flag1 |
                                    OB_MAC_IOCB_REQ_LSO);
                                iphdr->ip_sum = 0;
                                tcp_udp_hdr_off =
                                    (uint16_t)(mac_hdr_len+ip_hdr_len);
                                tcp_udp_hdr_len =
                                    (uint16_t)(tcp_hdr->th_off*4);
                                QL_PRINT(DBG_TX, ("%s(%d): tcp header len:%d\n",
                                    __func__, qlge->instance, tcp_udp_hdr_len));
                                hdr_off = ip_hdr_off;
                                tcp_udp_hdr_off <<= 6;
                                hdr_off |= tcp_udp_hdr_off;
                                mac_iocb_ptr->hdr_off =
                                    (uint16_t)cpu_to_le16(hdr_off);
                                mac_iocb_ptr->protocol_hdr_len = (uint16_t)
                                    cpu_to_le16(mac_hdr_len + ip_hdr_len +
                                    tcp_udp_hdr_len);
                                mac_iocb_ptr->mss = (uint16_t)cpu_to_le16(mss);

                                /*
                                 * if the chip is unable to calculate pseudo
                                 * header checksum, do it in then put the result
                                 * to the data passed to the chip
                                 */
                                if (qlge->cfg_flags &
                                    CFG_HW_UNABLE_PSEUDO_HDR_CKSUM)
                                        ql_lso_pseudo_cksum((uint8_t *)iphdr);
                        } else if (iphdr->ip_p == IPPROTO_UDP) {
                                udp_hdr = (struct udphdr *)(void *)
                                    ((uint8_t *)(void *)iphdr
                                    + ip_hdr_len);
                                QL_PRINT(DBG_TX, ("%s(%d) : Do TSO on UDP "
                                    "packet\n",
                                    __func__, qlge->instance));
                                mac_iocb_ptr->opcode =
                                    OPCODE_OB_MAC_OFFLOAD_IOCB;
                                mac_iocb_ptr->flag1 =
                                    (uint8_t)(mac_iocb_ptr->flag1 |
                                    OB_MAC_IOCB_REQ_LSO);
                                iphdr->ip_sum = 0;
                                tcp_udp_hdr_off =
                                    (uint16_t)(mac_hdr_len+ip_hdr_len);
                                tcp_udp_hdr_len =
                                    (uint16_t)(udp_hdr->uh_ulen*4);
                                QL_PRINT(DBG_TX, ("%s(%d):udp header len:%d\n",
                                    __func__, qlge->instance, tcp_udp_hdr_len));
                                hdr_off = ip_hdr_off;
                                tcp_udp_hdr_off <<= 6;
                                hdr_off |= tcp_udp_hdr_off;
                                mac_iocb_ptr->hdr_off =
                                    (uint16_t)cpu_to_le16(hdr_off);
                                mac_iocb_ptr->protocol_hdr_len = (uint16_t)
                                    cpu_to_le16(mac_hdr_len + ip_hdr_len +
                                    tcp_udp_hdr_len);
                                mac_iocb_ptr->mss = (uint16_t)cpu_to_le16(mss);

                                /*
                                 * if the chip is unable to do pseudo header
                                 * checksum calculation, do it here then put the
                                 * result to the data passed to the chip
                                 */
                                if (qlge->cfg_flags &
                                    CFG_HW_UNABLE_PSEUDO_HDR_CKSUM)
                                        ql_lso_pseudo_cksum((uint8_t *)iphdr);
                        }
                }
        }
}

/*
 * Generic packet sending function which is used to send one packet.
 */
int
ql_send_common(struct tx_ring *tx_ring, mblk_t *mp)
{
        struct tx_ring_desc *tx_cb;
        struct ob_mac_iocb_req *mac_iocb_ptr;
        mblk_t *tp;
        size_t msg_len = 0;
        size_t off;
        caddr_t bp;
        size_t nbyte, total_len;
        uint_t i = 0;
        int j = 0, frags = 0;
        uint32_t phy_addr_low, phy_addr_high;
        uint64_t phys_addr;
        clock_t now;
        uint32_t pflags = 0;
        uint32_t mss = 0;
        enum tx_mode_t tx_mode;
        struct oal_entry *oal_entry;
        int status;
        uint_t ncookies, oal_entries, max_oal_entries;
        size_t max_seg_len = 0;
        boolean_t use_lso = B_FALSE;
        struct oal_entry *tx_entry = NULL;
        struct oal_entry *last_oal_entry;
        qlge_t *qlge = tx_ring->qlge;
        ddi_dma_cookie_t dma_cookie;
        size_t tx_buf_len = QL_MAX_COPY_LENGTH;
        int force_pullup = 0;

        tp = mp;
        total_len = msg_len = 0;
        max_oal_entries = TX_DESC_PER_IOCB + MAX_SG_ELEMENTS-1;

        /* Calculate number of data and segments in the incoming message */
        for (tp = mp; tp != NULL; tp = tp->b_cont) {
                nbyte = MBLKL(tp);
                total_len += nbyte;
                max_seg_len = max(nbyte, max_seg_len);
                QL_PRINT(DBG_TX, ("Requested sending data in %d segments, "
                    "total length: %d\n", frags, nbyte));
                frags++;
        }

        if (total_len >= QL_LSO_MAX) {
                freemsg(mp);
#ifdef QLGE_LOAD_UNLOAD
                cmn_err(CE_NOTE, "%s: quit, packet oversize %d\n",
                    __func__, (int)total_len);
#endif
                return (0);
        }

        bp = (caddr_t)mp->b_rptr;
        if (bp[0] & 1) {
                if (bcmp(bp, ql_ether_broadcast_addr.ether_addr_octet,
                    ETHERADDRL) == 0) {
                        QL_PRINT(DBG_TX, ("Broadcast packet\n"));
                        tx_ring->brdcstxmt++;
                } else {
                        QL_PRINT(DBG_TX, ("multicast packet\n"));
                        tx_ring->multixmt++;
                }
        }

        tx_ring->obytes += total_len;
        tx_ring->opackets ++;

        QL_PRINT(DBG_TX, ("total requested sending data length: %d, in %d segs,"
            " max seg len: %d\n", total_len, frags, max_seg_len));

        /* claim a free slot in tx ring */
        tx_cb = &tx_ring->wq_desc[tx_ring->prod_idx];

        /* get the tx descriptor */
        mac_iocb_ptr = tx_cb->queue_entry;

        bzero((void *)mac_iocb_ptr, 20);

        ASSERT(tx_cb->mp == NULL);

        /*
         * Decide to use DMA map or copy mode.
         * DMA map mode must be used when the total msg length is more than the
         * tx buffer length.
         */

        if (total_len > tx_buf_len)
                tx_mode = USE_DMA;
        else if (max_seg_len > QL_MAX_COPY_LENGTH)
                tx_mode = USE_DMA;
        else
                tx_mode = USE_COPY;

        if (qlge->chksum_cap) {
                mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &pflags);
                QL_PRINT(DBG_TX, ("checksum flag is :0x%x, card capability "
                    "is 0x%x \n", pflags, qlge->chksum_cap));
                if (qlge->lso_enable) {
                        uint32_t lso_flags = 0;
                        mac_lso_get(mp, &mss, &lso_flags);
                        use_lso = (lso_flags == HW_LSO);
                }
                QL_PRINT(DBG_TX, ("mss :%d, use_lso %x \n",
                    mss, use_lso));
        }

do_pullup:

        /* concatenate all frags into one large packet if too fragmented */
        if (((tx_mode == USE_DMA)&&(frags > QL_MAX_TX_DMA_HANDLES)) ||
            force_pullup) {
                mblk_t *mp1;
                if ((mp1 = msgpullup(mp, -1)) != NULL) {
                        freemsg(mp);
                        mp = mp1;
                        frags = 1;
                } else {
                        tx_ring->tx_fail_dma_bind++;
                        goto bad;
                }
        }

        tx_cb->tx_bytes = (uint32_t)total_len;
        tx_cb->mp = mp;
        tx_cb->tx_dma_handle_used = 0;

        if (tx_mode == USE_DMA) {
                msg_len = total_len;

                mac_iocb_ptr->opcode = OPCODE_OB_MAC_IOCB;
                mac_iocb_ptr->tid = tx_ring->prod_idx;
                mac_iocb_ptr->frame_len = (uint32_t)cpu_to_le32(msg_len);
                mac_iocb_ptr->txq_idx = tx_ring->wq_id;

                tx_entry = &mac_iocb_ptr->oal_entry[0];
                oal_entry = NULL;

                for (tp = mp, oal_entries = j = 0; tp != NULL;
                    tp = tp->b_cont) {
                        /* if too many tx dma handles needed */
                        if (j >= QL_MAX_TX_DMA_HANDLES) {
                                tx_ring->tx_no_dma_handle++;
                                if (!force_pullup) {
                                        force_pullup = 1;
                                        goto do_pullup;
                                } else {
                                        goto bad;
                                }
                        }
                        nbyte = (uint16_t)MBLKL(tp);
                        if (nbyte == 0)
                                continue;

                        status = ddi_dma_addr_bind_handle(
                            tx_cb->tx_dma_handle[j], NULL,
                            (caddr_t)tp->b_rptr, nbyte,
                            DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT,
                            0, &dma_cookie, &ncookies);

                        QL_PRINT(DBG_TX, ("map sending data segment: %d, "
                            "length: %d, spans in %d cookies\n",
                            j, nbyte, ncookies));

                        if (status != DDI_DMA_MAPPED) {
                                goto bad;
                        }
                        /*
                         * Each fragment can span several cookies. One cookie
                         * will use one tx descriptor to transmit.
                         */
                        for (i = ncookies; i > 0; i--, tx_entry++,
                            oal_entries++) {
                                /*
                                 * The number of TX descriptors that can be
                                 *  saved in tx iocb and oal list is limited
                                 */
                                if (oal_entries > max_oal_entries) {
                                        tx_ring->tx_no_dma_cookie++;
                                        if (!force_pullup) {
                                                force_pullup = 1;
                                                goto do_pullup;
                                        } else {
                                                goto bad;
                                        }
                                }

                                if ((oal_entries == TX_DESC_PER_IOCB) &&
                                    !oal_entry) {
                                        /*
                                         * Time to switch to an oal list
                                         * The last entry should be copied
                                         * to first entry in the oal list
                                         */
                                        oal_entry = tx_cb->oal;
                                        tx_entry =
                                            &mac_iocb_ptr->oal_entry[
                                            TX_DESC_PER_IOCB-1];
                                        bcopy(tx_entry, oal_entry,
                                            sizeof (*oal_entry));

                                        /*
                                         * last entry should be updated to
                                         * point to the extended oal list itself
                                         */
                                        tx_entry->buf_addr_low =
                                            cpu_to_le32(
                                            LS_64BITS(tx_cb->oal_dma_addr));
                                        tx_entry->buf_addr_high =
                                            cpu_to_le32(
                                            MS_64BITS(tx_cb->oal_dma_addr));
                                        /*
                                         * Point tx_entry to the oal list
                                         * second entry
                                         */
                                        tx_entry = &oal_entry[1];
                                }

                                tx_entry->buf_len =
                                    (uint32_t)cpu_to_le32(dma_cookie.dmac_size);
                                phys_addr = dma_cookie.dmac_laddress;
                                tx_entry->buf_addr_low =
                                    cpu_to_le32(LS_64BITS(phys_addr));
                                tx_entry->buf_addr_high =
                                    cpu_to_le32(MS_64BITS(phys_addr));

                                last_oal_entry = tx_entry;

                                if (i > 1)
                                        ddi_dma_nextcookie(
                                            tx_cb->tx_dma_handle[j],
                                            &dma_cookie);
                        }
                        j++;
                }
                /*
                 * if OAL is used, the last oal entry in tx iocb indicates
                 * number of additional address/len pairs in OAL
                 */
                if (oal_entries > TX_DESC_PER_IOCB) {
                        tx_entry = &mac_iocb_ptr->oal_entry[TX_DESC_PER_IOCB-1];
                        tx_entry->buf_len = (uint32_t)
                            (cpu_to_le32((sizeof (struct oal_entry) *
                            (oal_entries -TX_DESC_PER_IOCB+1))|OAL_CONT_ENTRY));
                }
                last_oal_entry->buf_len = cpu_to_le32(
                    le32_to_cpu(last_oal_entry->buf_len)|OAL_LAST_ENTRY);

                tx_cb->tx_dma_handle_used = j;
                QL_PRINT(DBG_TX, ("total tx_dma_handle_used %d cookies %d \n",
                    j, oal_entries));

                bp = (caddr_t)mp->b_rptr;
        }
        if (tx_mode == USE_COPY) {
                bp = tx_cb->copy_buffer;
                off = 0;
                nbyte = 0;
                frags = 0;
                /*
                 * Copy up to tx_buf_len of the transmit data
                 * from mp to tx buffer
                 */
                for (tp = mp; tp != NULL; tp = tp->b_cont) {
                        nbyte = MBLKL(tp);
                        if ((off + nbyte) <= tx_buf_len) {
                                bcopy(tp->b_rptr, &bp[off], nbyte);
                                off += nbyte;
                                frags ++;
                        }
                }

                msg_len = off;

                mac_iocb_ptr->opcode = OPCODE_OB_MAC_IOCB;
                mac_iocb_ptr->tid = tx_ring->prod_idx;
                mac_iocb_ptr->frame_len = (uint32_t)cpu_to_le32(msg_len);
                mac_iocb_ptr->txq_idx = tx_ring->wq_id;

                QL_PRINT(DBG_TX, ("Copy Mode:actual sent data length is: %d, "
                    "from %d segaments\n", msg_len, frags));

                phys_addr = tx_cb->copy_buffer_dma_addr;
                phy_addr_low = cpu_to_le32(LS_64BITS(phys_addr));
                phy_addr_high = cpu_to_le32(MS_64BITS(phys_addr));

                QL_DUMP(DBG_TX, "\t requested sending data:\n",
                    (uint8_t *)tx_cb->copy_buffer, 8, total_len);

                mac_iocb_ptr->oal_entry[0].buf_len = (uint32_t)
                    cpu_to_le32(msg_len | OAL_LAST_ENTRY);
                mac_iocb_ptr->oal_entry[0].buf_addr_low  = phy_addr_low;
                mac_iocb_ptr->oal_entry[0].buf_addr_high = phy_addr_high;

                freemsg(mp); /* no need, we have copied */
                tx_cb->mp = NULL;
        } /* End of Copy Mode */

        /* Do TSO/LSO on TCP packet? */
        if (use_lso && mss) {
                ql_hw_lso_setup(qlge, mss, bp, mac_iocb_ptr);
        } else if (pflags & qlge->chksum_cap) {
                /* Do checksum offloading */
                ql_hw_csum_setup(qlge, pflags, bp, mac_iocb_ptr);
        }

        /* let device know the latest outbound IOCB */
        (void) ddi_dma_sync(tx_ring->wq_dma.dma_handle,
            (off_t)((uintptr_t)mac_iocb_ptr - (uintptr_t)tx_ring->wq_dma.vaddr),
            (size_t)sizeof (*mac_iocb_ptr), DDI_DMA_SYNC_FORDEV);

        if (tx_mode == USE_DMA) {
                /* let device know the latest outbound OAL if necessary */
                if (oal_entries > TX_DESC_PER_IOCB) {
                        (void) ddi_dma_sync(tx_cb->oal_dma.dma_handle,
                            (off_t)0,
                            (sizeof (struct oal_entry) *
                            (oal_entries -TX_DESC_PER_IOCB+1)),
                            DDI_DMA_SYNC_FORDEV);
                }
        } else { /* for USE_COPY mode, tx buffer has changed */
                /* let device know the latest change */
                (void) ddi_dma_sync(tx_cb->oal_dma.dma_handle,
                /* copy buf offset */
                    (off_t)(sizeof (oal_entry) * MAX_SG_ELEMENTS),
                    msg_len, DDI_DMA_SYNC_FORDEV);
        }

        /* save how the packet was sent */
        tx_cb->tx_type = tx_mode;

        QL_DUMP_REQ_PKT(qlge, mac_iocb_ptr, tx_cb->oal, oal_entries);
        /* reduce the number of available tx slot */
        atomic_dec_32(&tx_ring->tx_free_count);

        tx_ring->prod_idx++;
        if (tx_ring->prod_idx >= tx_ring->wq_len)
                tx_ring->prod_idx = 0;

        now = ddi_get_lbolt();
        qlge->last_tx_time = now;

        return (DDI_SUCCESS);

bad:
        /*
         * if for any reason driver can not send, delete
         * the message pointer, mp
         */
        now = ddi_get_lbolt();
        freemsg(mp);
        mp = NULL;
        tx_cb->mp = NULL;
        for (i = 0; i < j; i++)
                (void) ddi_dma_unbind_handle(tx_cb->tx_dma_handle[i]);

        QL_PRINT(DBG_TX, ("%s(%d) failed at 0x%x",
            __func__, qlge->instance, (int)now));

        return (DDI_SUCCESS);
}


/*
 * Initializes hardware and driver software flags before the driver
 * is finally ready to work.
 */
int
ql_do_start(qlge_t *qlge)
{
        int i;
        struct rx_ring *rx_ring;
        uint16_t lbq_buf_size;
        int rings_done;

        ASSERT(qlge != NULL);

        mutex_enter(&qlge->hw_mutex);

        /* Reset adapter */
        (void) ql_asic_reset(qlge);

        lbq_buf_size = (uint16_t)
            ((qlge->mtu == ETHERMTU)? LRG_BUF_NORMAL_SIZE : LRG_BUF_JUMBO_SIZE);
        if (qlge->rx_ring[0].lbq_buf_size != lbq_buf_size) {
#ifdef QLGE_LOAD_UNLOAD
                cmn_err(CE_NOTE, "realloc buffers old: %d new: %d\n",
                    qlge->rx_ring[0].lbq_buf_size, lbq_buf_size);
#endif
                /*
                 * Check if any ring has buffers still with upper layers
                 * If buffers are pending with upper layers, we use the
                 * existing buffers and don't reallocate new ones
                 * Unfortunately there is no way to evict buffers from
                 * upper layers. Using buffers with the current size may
                 * cause slightly sub-optimal performance, but that seems
                 * to be the easiest way to handle this situation.
                 */
                rings_done = 0;
                for (i = 0; i < qlge->rx_ring_count; i++) {
                        rx_ring = &qlge->rx_ring[i];
                        if (rx_ring->rx_indicate == 0)
                                rings_done++;
                        else
                                break;
                }
                /*
                 * No buffers pending with upper layers;
                 * reallocte them for new MTU size
                 */
                if (rings_done >= qlge->rx_ring_count) {
                        /* free large buffer pool */
                        for (i = 0; i < qlge->rx_ring_count; i++) {
                                rx_ring = &qlge->rx_ring[i];
                                if (rx_ring->type != TX_Q) {
                                        ql_free_sbq_buffers(rx_ring);
                                        ql_free_lbq_buffers(rx_ring);
                                }
                        }
                        /* reallocate large buffer pool */
                        for (i = 0; i < qlge->rx_ring_count; i++) {
                                rx_ring = &qlge->rx_ring[i];
                                if (rx_ring->type != TX_Q) {
                                        (void) ql_alloc_sbufs(qlge, rx_ring);
                                        (void) ql_alloc_lbufs(qlge, rx_ring);
                                }
                        }
                }
        }

        if (ql_bringup_adapter(qlge) != DDI_SUCCESS) {
                cmn_err(CE_WARN, "qlge bringup adapter failed");
                mutex_exit(&qlge->hw_mutex);
                if (qlge->fm_enable) {
                        atomic_or_32(&qlge->flags, ADAPTER_ERROR);
                        ddi_fm_service_impact(qlge->dip, DDI_SERVICE_LOST);
                }
                return (DDI_FAILURE);
        }

        mutex_exit(&qlge->hw_mutex);
        /* if adapter is up successfully but was bad before */
        if (qlge->flags & ADAPTER_ERROR) {
                atomic_and_32(&qlge->flags, ~ADAPTER_ERROR);
                if (qlge->fm_enable) {
                        ddi_fm_service_impact(qlge->dip, DDI_SERVICE_RESTORED);
                }
        }

        /* Get current link state */
        qlge->port_link_state = ql_get_link_state(qlge);

        if (qlge->port_link_state == LS_UP) {
                QL_PRINT(DBG_GLD, ("%s(%d) Link UP !!\n",
                    __func__, qlge->instance));
                /* If driver detects a carrier on */
                CARRIER_ON(qlge);
        } else {
                QL_PRINT(DBG_GLD, ("%s(%d) Link down\n",
                    __func__, qlge->instance));
                /* If driver detects a lack of carrier */
                CARRIER_OFF(qlge);
        }
        qlge->mac_flags = QL_MAC_STARTED;
        return (DDI_SUCCESS);
}

/*
 * Stop currently running driver
 * Driver needs to stop routing new packets to driver and wait until
 * all pending tx/rx buffers to be free-ed.
 */
int
ql_do_stop(qlge_t *qlge)
{
        int rc = DDI_FAILURE;
        uint32_t i, j, k;
        struct bq_desc *sbq_desc, *lbq_desc;
        struct rx_ring *rx_ring;

        ASSERT(qlge != NULL);

        CARRIER_OFF(qlge);

        rc = ql_bringdown_adapter(qlge);
        if (rc != DDI_SUCCESS) {
                cmn_err(CE_WARN, "qlge bringdown adapter failed.");
        } else
                rc = DDI_SUCCESS;

        for (k = 0; k < qlge->rx_ring_count; k++) {
                rx_ring = &qlge->rx_ring[k];
                if (rx_ring->type != TX_Q) {
                        j = rx_ring->lbq_use_head;
#ifdef QLGE_LOAD_UNLOAD
                        cmn_err(CE_NOTE, "ring %d: move %d lbufs in use list"
                            " to free list %d\n total %d\n",
                            k, rx_ring->lbuf_in_use_count,
                            rx_ring->lbuf_free_count,
                            rx_ring->lbuf_in_use_count +
                            rx_ring->lbuf_free_count);
#endif
                        for (i = 0; i < rx_ring->lbuf_in_use_count; i++) {
                                lbq_desc = rx_ring->lbuf_in_use[j];
                                j++;
                                if (j >= rx_ring->lbq_len) {
                                        j = 0;
                                }
                                if (lbq_desc->mp) {
                                        atomic_inc_32(&rx_ring->rx_indicate);
                                        freemsg(lbq_desc->mp);
                                }
                        }
                        rx_ring->lbq_use_head = j;
                        rx_ring->lbq_use_tail = j;
                        rx_ring->lbuf_in_use_count = 0;
                        j = rx_ring->sbq_use_head;
#ifdef QLGE_LOAD_UNLOAD
                        cmn_err(CE_NOTE, "ring %d: move %d sbufs in use list,"
                            " to free list %d\n total %d \n",
                            k, rx_ring->sbuf_in_use_count,
                            rx_ring->sbuf_free_count,
                            rx_ring->sbuf_in_use_count +
                            rx_ring->sbuf_free_count);
#endif
                        for (i = 0; i < rx_ring->sbuf_in_use_count; i++) {
                                sbq_desc = rx_ring->sbuf_in_use[j];
                                j++;
                                if (j >= rx_ring->sbq_len) {
                                        j = 0;
                                }
                                if (sbq_desc->mp) {
                                        atomic_inc_32(&rx_ring->rx_indicate);
                                        freemsg(sbq_desc->mp);
                                }
                        }
                        rx_ring->sbq_use_head = j;
                        rx_ring->sbq_use_tail = j;
                        rx_ring->sbuf_in_use_count = 0;
                }
        }

        qlge->mac_flags = QL_MAC_STOPPED;

        return (rc);
}

/*
 * Support
 */

void
ql_disable_isr(qlge_t *qlge)
{
        /*
         * disable the hardware interrupt
         */
        ISP_DISABLE_GLOBAL_INTRS(qlge);

        qlge->flags &= ~INTERRUPTS_ENABLED;
}



/*
 * busy wait for 'usecs' microseconds.
 */
void
qlge_delay(clock_t usecs)
{
        drv_usecwait(usecs);
}

/*
 * retrieve firmware details.
 */

pci_cfg_t *
ql_get_pci_config(qlge_t *qlge)
{
        return (&(qlge->pci_cfg));
}

/*
 * Get current Link status
 */
static uint32_t
ql_get_link_state(qlge_t *qlge)
{
        uint32_t bitToCheck = 0;
        uint32_t temp, linkState;

        if (qlge->func_number == qlge->fn0_net) {
                bitToCheck = STS_PL0;
        } else {
                bitToCheck = STS_PL1;
        }
        temp = ql_read_reg(qlge, REG_STATUS);
        QL_PRINT(DBG_GLD, ("%s(%d) chip status reg: 0x%x\n",
            __func__, qlge->instance, temp));

        if (temp & bitToCheck) {
                linkState = LS_UP;
        } else {
                linkState = LS_DOWN;
        }
        if (CFG_IST(qlge, CFG_CHIP_8100)) {
                /* for Schultz, link Speed is fixed to 10G, full duplex */
                qlge->speed  = SPEED_10G;
                qlge->duplex = 1;
        }
        return (linkState);
}
/*
 * Get current link status and report to OS
 */
static void
ql_get_and_report_link_state(qlge_t *qlge)
{
        uint32_t cur_link_state;

        /* Get current link state */
        cur_link_state = ql_get_link_state(qlge);
        /* if link state has changed */
        if (cur_link_state != qlge->port_link_state) {

                qlge->port_link_state = cur_link_state;

                if (qlge->port_link_state == LS_UP) {
                        QL_PRINT(DBG_GLD, ("%s(%d) Link UP !!\n",
                            __func__, qlge->instance));
                        /* If driver detects a carrier on */
                        CARRIER_ON(qlge);
                } else {
                        QL_PRINT(DBG_GLD, ("%s(%d) Link down\n",
                            __func__, qlge->instance));
                        /* If driver detects a lack of carrier */
                        CARRIER_OFF(qlge);
                }
        }
}

/*
 * timer callback function executed after timer expires
 */
static void
ql_timer(void* arg)
{
        ql_get_and_report_link_state((qlge_t *)arg);
}

/*
 * stop the running timer if activated
 */
static void
ql_stop_timer(qlge_t *qlge)
{
        timeout_id_t timer_id;
        /* Disable driver timer */
        if (qlge->ql_timer_timeout_id != NULL) {
                timer_id = qlge->ql_timer_timeout_id;
                qlge->ql_timer_timeout_id = NULL;
                (void) untimeout(timer_id);
        }
}

/*
 * stop then restart timer
 */
void
ql_restart_timer(qlge_t *qlge)
{
        ql_stop_timer(qlge);
        qlge->ql_timer_ticks = TICKS_PER_SEC / 4;
        qlge->ql_timer_timeout_id = timeout(ql_timer,
            (void *)qlge, qlge->ql_timer_ticks);
}

/* ************************************************************************* */
/*
 *              Hardware K-Stats Data Structures and Subroutines
 */
/* ************************************************************************* */
static const ql_ksindex_t ql_kstats_hw[] = {
        /* PCI related hardware information */
        { 0, "Vendor Id"                        },
        { 1, "Device Id"                        },
        { 2, "Command"                          },
        { 3, "Status"                           },
        { 4, "Revision Id"                      },
        { 5, "Cache Line Size"                  },
        { 6, "Latency Timer"                    },
        { 7, "Header Type"                      },
        { 9, "I/O base addr"                    },
        { 10, "Control Reg Base addr low"       },
        { 11, "Control Reg Base addr high"      },
        { 12, "Doorbell Reg Base addr low"      },
        { 13, "Doorbell Reg Base addr high"     },
        { 14, "Subsystem Vendor Id"             },
        { 15, "Subsystem Device ID"             },
        { 16, "PCIe Device Control"             },
        { 17, "PCIe Link Status"                },

        { -1,   NULL                            },
};

/*
 * kstat update function for PCI registers
 */
static int
ql_kstats_get_pci_regs(kstat_t *ksp, int flag)
{
        qlge_t *qlge;
        kstat_named_t *knp;

        if (flag != KSTAT_READ)
                return (EACCES);

        qlge = ksp->ks_private;
        knp = ksp->ks_data;
        (knp++)->value.ui32 = qlge->pci_cfg.vendor_id;
        (knp++)->value.ui32 = qlge->pci_cfg.device_id;
        (knp++)->value.ui32 = qlge->pci_cfg.command;
        (knp++)->value.ui32 = qlge->pci_cfg.status;
        (knp++)->value.ui32 = qlge->pci_cfg.revision;
        (knp++)->value.ui32 = qlge->pci_cfg.cache_line_size;
        (knp++)->value.ui32 = qlge->pci_cfg.latency_timer;
        (knp++)->value.ui32 = qlge->pci_cfg.header_type;
        (knp++)->value.ui32 = qlge->pci_cfg.io_base_address;
        (knp++)->value.ui32 =
            qlge->pci_cfg.pci_cntl_reg_set_mem_base_address_lower;
        (knp++)->value.ui32 =
            qlge->pci_cfg.pci_cntl_reg_set_mem_base_address_upper;
        (knp++)->value.ui32 =
            qlge->pci_cfg.pci_doorbell_mem_base_address_lower;
        (knp++)->value.ui32 =
            qlge->pci_cfg.pci_doorbell_mem_base_address_upper;
        (knp++)->value.ui32 = qlge->pci_cfg.sub_vendor_id;
        (knp++)->value.ui32 = qlge->pci_cfg.sub_device_id;
        (knp++)->value.ui32 = qlge->pci_cfg.pcie_device_control;
        (knp++)->value.ui32 = qlge->pci_cfg.link_status;

        return (0);
}

static const ql_ksindex_t ql_kstats_mii[] = {
        /* MAC/MII related hardware information */
        { 0, "mtu"},

        { -1, NULL},
};


/*
 * kstat update function for MII related information.
 */
static int
ql_kstats_mii_update(kstat_t *ksp, int flag)
{
        qlge_t *qlge;
        kstat_named_t *knp;

        if (flag != KSTAT_READ)
                return (EACCES);

        qlge = ksp->ks_private;
        knp = ksp->ks_data;

        (knp++)->value.ui32 = qlge->mtu;

        return (0);
}

static const ql_ksindex_t ql_kstats_reg[] = {
        /* Register information */
        { 0, "System (0x08)"                    },
        { 1, "Reset/Fail Over(0x0Ch"            },
        { 2, "Function Specific Control(0x10)"  },
        { 3, "Status (0x30)"                    },
        { 4, "Intr Enable (0x34)"               },
        { 5, "Intr Status1 (0x3C)"              },
        { 6, "Error Status (0x54)"              },
        { 7, "XGMAC Flow Control(0x11C)"        },
        { 8, "XGMAC Tx Pause Frames(0x230)"     },
        { 9, "XGMAC Rx Pause Frames(0x388)"     },
        { 10, "XGMAC Rx FIFO Drop Count(0x5B8)" },
        { 11, "interrupts actually allocated"   },
        { 12, "interrupts on rx ring 0"         },
        { 13, "interrupts on rx ring 1"         },
        { 14, "interrupts on rx ring 2"         },
        { 15, "interrupts on rx ring 3"         },
        { 16, "interrupts on rx ring 4"         },
        { 17, "interrupts on rx ring 5"         },
        { 18, "interrupts on rx ring 6"         },
        { 19, "interrupts on rx ring 7"         },
        { 20, "polls on rx ring 0"              },
        { 21, "polls on rx ring 1"              },
        { 22, "polls on rx ring 2"              },
        { 23, "polls on rx ring 3"              },
        { 24, "polls on rx ring 4"              },
        { 25, "polls on rx ring 5"              },
        { 26, "polls on rx ring 6"              },
        { 27, "polls on rx ring 7"              },
        { 28, "tx no resource on ring 0"        },
        { 29, "tx dma bind fail on ring 0"      },
        { 30, "tx dma no handle on ring 0"      },
        { 31, "tx dma no cookie on ring 0"      },
        { 32, "MPI firmware major version"      },
        { 33, "MPI firmware minor version"      },
        { 34, "MPI firmware sub version"        },
        { 35, "rx no resource"                  },

        { -1, NULL},
};


/*
 * kstat update function for device register set
 */
static int
ql_kstats_get_reg_and_dev_stats(kstat_t *ksp, int flag)
{
        qlge_t *qlge;
        kstat_named_t *knp;
        uint32_t val32;
        int i = 0;
        struct tx_ring *tx_ring;
        struct rx_ring *rx_ring;

        if (flag != KSTAT_READ)
                return (EACCES);

        qlge = ksp->ks_private;
        knp = ksp->ks_data;

        (knp++)->value.ui32 = ql_read_reg(qlge, REG_SYSTEM);
        (knp++)->value.ui32 = ql_read_reg(qlge, REG_RESET_FAILOVER);
        (knp++)->value.ui32 = ql_read_reg(qlge, REG_FUNCTION_SPECIFIC_CONTROL);
        (knp++)->value.ui32 = ql_read_reg(qlge, REG_STATUS);
        (knp++)->value.ui32 = ql_read_reg(qlge, REG_INTERRUPT_ENABLE);
        (knp++)->value.ui32 = ql_read_reg(qlge, REG_INTERRUPT_STATUS_1);
        (knp++)->value.ui32 = ql_read_reg(qlge, REG_ERROR_STATUS);

        if (ql_sem_spinlock(qlge, qlge->xgmac_sem_mask)) {
                return (0);
        }
        (void) ql_read_xgmac_reg(qlge, REG_XGMAC_FLOW_CONTROL, &val32);
        (knp++)->value.ui32 = val32;

        (void) ql_read_xgmac_reg(qlge, REG_XGMAC_MAC_TX_PAUSE_PKTS, &val32);
        (knp++)->value.ui32 = val32;

        (void) ql_read_xgmac_reg(qlge, REG_XGMAC_MAC_RX_PAUSE_PKTS, &val32);
        (knp++)->value.ui32 = val32;

        (void) ql_read_xgmac_reg(qlge, REG_XGMAC_MAC_RX_FIFO_DROPS, &val32);
        (knp++)->value.ui32 = val32;

        ql_sem_unlock(qlge, qlge->xgmac_sem_mask);

        (knp++)->value.ui32 = qlge->intr_cnt;

        for (i = 0; i < 8; i++) {
                (knp++)->value.ui32 = qlge->rx_interrupts[i];
        }

        for (i = 0; i < 8; i++) {
                (knp++)->value.ui32 = qlge->rx_polls[i];
        }

        tx_ring = &qlge->tx_ring[0];
        (knp++)->value.ui32 = tx_ring->defer;
        (knp++)->value.ui32 = tx_ring->tx_fail_dma_bind;
        (knp++)->value.ui32 = tx_ring->tx_no_dma_handle;
        (knp++)->value.ui32 = tx_ring->tx_no_dma_cookie;

        (knp++)->value.ui32 = qlge->fw_version_info.major_version;
        (knp++)->value.ui32 = qlge->fw_version_info.minor_version;
        (knp++)->value.ui32 = qlge->fw_version_info.sub_minor_version;

        for (i = 0; i < qlge->rx_ring_count; i++) {
                rx_ring = &qlge->rx_ring[i];
                val32 += rx_ring->rx_packets_dropped_no_buffer;
        }
        (knp++)->value.ui32 = val32;

        return (0);
}


static kstat_t *
ql_setup_named_kstat(qlge_t *qlge, int instance, char *name,
    const ql_ksindex_t *ksip, size_t size, int (*update)(kstat_t *, int))
{
        kstat_t *ksp;
        kstat_named_t *knp;
        char *np;
        int type;

        size /= sizeof (ql_ksindex_t);
        ksp = kstat_create(ADAPTER_NAME, instance, name, "net",
            KSTAT_TYPE_NAMED, ((uint32_t)size) - 1, KSTAT_FLAG_PERSISTENT);
        if (ksp == NULL)
                return (NULL);

        ksp->ks_private = qlge;
        ksp->ks_update = update;
        for (knp = ksp->ks_data; (np = ksip->name) != NULL; ++knp, ++ksip) {
                switch (*np) {
                default:
                        type = KSTAT_DATA_UINT32;
                        break;
                case '&':
                        np += 1;
                        type = KSTAT_DATA_CHAR;
                        break;
                }
                kstat_named_init(knp, np, (uint8_t)type);
        }
        kstat_install(ksp);

        return (ksp);
}

/*
 * Setup various kstat
 */
int
ql_init_kstats(qlge_t *qlge)
{
        /* Hardware KStats */
        qlge->ql_kstats[QL_KSTAT_CHIP] = ql_setup_named_kstat(qlge,
            qlge->instance, "chip", ql_kstats_hw,
            sizeof (ql_kstats_hw), ql_kstats_get_pci_regs);
        if (qlge->ql_kstats[QL_KSTAT_CHIP] == NULL) {
                return (DDI_FAILURE);
        }

        /* MII KStats */
        qlge->ql_kstats[QL_KSTAT_LINK] = ql_setup_named_kstat(qlge,
            qlge->instance, "mii", ql_kstats_mii,
            sizeof (ql_kstats_mii), ql_kstats_mii_update);
        if (qlge->ql_kstats[QL_KSTAT_LINK] == NULL) {
                return (DDI_FAILURE);
        }

        /* REG KStats */
        qlge->ql_kstats[QL_KSTAT_REG] = ql_setup_named_kstat(qlge,
            qlge->instance, "reg", ql_kstats_reg,
            sizeof (ql_kstats_reg), ql_kstats_get_reg_and_dev_stats);
        if (qlge->ql_kstats[QL_KSTAT_REG] == NULL) {
                return (DDI_FAILURE);
        }
        return (DDI_SUCCESS);
}

/*
 * delete all kstat
 */
void
ql_fini_kstats(qlge_t *qlge)
{
        int i;

        for (i = 0; i < QL_KSTAT_COUNT; i++) {
                if (qlge->ql_kstats[i] != NULL)
                        kstat_delete(qlge->ql_kstats[i]);
        }
}

/* ************************************************************************* */
/*
 *                                 kstat end
 */
/* ************************************************************************* */

/*
 * Setup the parameters for receive and transmit rings including buffer sizes
 * and completion queue sizes
 */
static int
ql_setup_rings(qlge_t *qlge)
{
        uint8_t i;
        struct rx_ring *rx_ring;
        struct tx_ring *tx_ring;
        uint16_t lbq_buf_size;

        lbq_buf_size = (uint16_t)
            ((qlge->mtu == ETHERMTU)? LRG_BUF_NORMAL_SIZE : LRG_BUF_JUMBO_SIZE);

        /*
         * rx_ring[0] is always the default queue.
         */
        /*
         * qlge->rx_ring_count:
         * Total number of rx_rings. This includes a number
         * of outbound completion handler rx_rings, and a
         * number of inbound completion handler rx_rings.
         * rss is only enabled if we have more than 1 rx completion
         * queue. If we have a single rx completion queue
         * then all rx completions go to this queue and
         * the last completion queue
         */

        qlge->tx_ring_first_cq_id = qlge->rss_ring_count;

        for (i = 0; i < qlge->tx_ring_count; i++) {
                tx_ring = &qlge->tx_ring[i];
                bzero((void *)tx_ring, sizeof (*tx_ring));
                tx_ring->qlge = qlge;
                tx_ring->wq_id = i;
                tx_ring->wq_len = qlge->tx_ring_size;
                tx_ring->wq_size = (uint32_t)(
                    tx_ring->wq_len * sizeof (struct ob_mac_iocb_req));

                /*
                 * The completion queue ID for the tx rings start
                 * immediately after the last rss completion queue.
                 */
                tx_ring->cq_id = (uint16_t)(i + qlge->tx_ring_first_cq_id);
        }

        for (i = 0; i < qlge->rx_ring_count; i++) {
                rx_ring = &qlge->rx_ring[i];
                bzero((void *)rx_ring, sizeof (*rx_ring));
                rx_ring->qlge = qlge;
                rx_ring->cq_id = i;
                if (i != 0)
                        rx_ring->cpu = (i) % qlge->rx_ring_count;
                else
                        rx_ring->cpu = 0;

                if (i < qlge->rss_ring_count) {
                        /*
                         * Inbound completions (RSS) queues
                         * Default queue is queue 0 which handles
                         * unicast plus bcast/mcast and async events.
                         * Other inbound queues handle unicast frames only.
                         */
                        rx_ring->cq_len = qlge->rx_ring_size;
                        rx_ring->cq_size = (uint32_t)
                            (rx_ring->cq_len * sizeof (struct net_rsp_iocb));
                        rx_ring->lbq_len = NUM_LARGE_BUFFERS;
                        rx_ring->lbq_size = (uint32_t)
                            (rx_ring->lbq_len * sizeof (uint64_t));
                        rx_ring->lbq_buf_size = lbq_buf_size;
                        rx_ring->sbq_len = NUM_SMALL_BUFFERS;
                        rx_ring->sbq_size = (uint32_t)
                            (rx_ring->sbq_len * sizeof (uint64_t));
                        rx_ring->sbq_buf_size = SMALL_BUFFER_SIZE * 2;
                        rx_ring->type = RX_Q;

                        QL_PRINT(DBG_GLD,
                            ("%s(%d)Allocating rss completion queue %d "
                            "on cpu %d\n", __func__, qlge->instance,
                            rx_ring->cq_id, rx_ring->cpu));
                } else {
                        /*
                         * Outbound queue handles outbound completions only
                         */
                        /* outbound cq is same size as tx_ring it services. */
                        QL_PRINT(DBG_INIT, ("rx_ring 0x%p i %d\n", rx_ring, i));
                        rx_ring->cq_len = qlge->tx_ring_size;
                        rx_ring->cq_size = (uint32_t)
                            (rx_ring->cq_len * sizeof (struct net_rsp_iocb));
                        rx_ring->lbq_len = 0;
                        rx_ring->lbq_size = 0;
                        rx_ring->lbq_buf_size = 0;
                        rx_ring->sbq_len = 0;
                        rx_ring->sbq_size = 0;
                        rx_ring->sbq_buf_size = 0;
                        rx_ring->type = TX_Q;

                        QL_PRINT(DBG_GLD,
                            ("%s(%d)Allocating TX completion queue %d on"
                            " cpu %d\n", __func__, qlge->instance,
                            rx_ring->cq_id, rx_ring->cpu));
                }
        }

        return (DDI_SUCCESS);
}

static int
ql_start_rx_ring(qlge_t *qlge, struct rx_ring *rx_ring)
{
        struct cqicb_t *cqicb = (struct cqicb_t *)rx_ring->cqicb_dma.vaddr;
        void *shadow_reg = (uint8_t *)qlge->host_copy_shadow_dma_attr.vaddr +
            (rx_ring->cq_id * sizeof (uint64_t) * RX_TX_RING_SHADOW_SPACE)
        /* first shadow area is used by wqicb's host copy of consumer index */
            + sizeof (uint64_t);
        uint64_t shadow_reg_dma = qlge->host_copy_shadow_dma_attr.dma_addr +
            (rx_ring->cq_id * sizeof (uint64_t) * RX_TX_RING_SHADOW_SPACE)
            + sizeof (uint64_t);
        /* lrg/sml bufq pointers */
        uint8_t *buf_q_base_reg =
            (uint8_t *)qlge->buf_q_ptr_base_addr_dma_attr.vaddr +
            (rx_ring->cq_id * sizeof (uint64_t) * BUF_Q_PTR_SPACE);
        uint64_t buf_q_base_reg_dma =
            qlge->buf_q_ptr_base_addr_dma_attr.dma_addr +
            (rx_ring->cq_id * sizeof (uint64_t) * BUF_Q_PTR_SPACE);
        caddr_t doorbell_area =
            qlge->doorbell_reg_iobase + (VM_PAGE_SIZE * (128 + rx_ring->cq_id));
        int err = 0;
        uint16_t bq_len;
        uint64_t tmp;
        uint64_t *base_indirect_ptr;
        int page_entries;

        /* Set up the shadow registers for this ring. */
        rx_ring->prod_idx_sh_reg = shadow_reg;
        rx_ring->prod_idx_sh_reg_dma = shadow_reg_dma;
        rx_ring->prod_idx_sh_reg_offset = (off_t)(((rx_ring->cq_id *
            sizeof (uint64_t) * RX_TX_RING_SHADOW_SPACE) + sizeof (uint64_t)));

        rx_ring->lbq_base_indirect = (uint64_t *)(void *)buf_q_base_reg;
        rx_ring->lbq_base_indirect_dma = buf_q_base_reg_dma;

        QL_PRINT(DBG_INIT, ("%s rx ring(%d): prod_idx virtual addr = 0x%lx,"
            " phys_addr 0x%lx\n", __func__, rx_ring->cq_id,
            rx_ring->prod_idx_sh_reg, rx_ring->prod_idx_sh_reg_dma));

        buf_q_base_reg += ((BUF_Q_PTR_SPACE / 2) * sizeof (uint64_t));
        buf_q_base_reg_dma += ((BUF_Q_PTR_SPACE / 2) * sizeof (uint64_t));
        rx_ring->sbq_base_indirect = (uint64_t *)(void *)buf_q_base_reg;
        rx_ring->sbq_base_indirect_dma = buf_q_base_reg_dma;

        /* PCI doorbell mem area + 0x00 for consumer index register */
        rx_ring->cnsmr_idx_db_reg = (uint32_t *)(void *)doorbell_area;
        rx_ring->cnsmr_idx = 0;
        *rx_ring->prod_idx_sh_reg = 0;
        rx_ring->curr_entry = rx_ring->cq_dma.vaddr;

        /* PCI doorbell mem area + 0x04 for valid register */
        rx_ring->valid_db_reg = (uint32_t *)(void *)
            ((uint8_t *)(void *)doorbell_area + 0x04);

        /* PCI doorbell mem area + 0x18 for large buffer consumer */
        rx_ring->lbq_prod_idx_db_reg = (uint32_t *)(void *)
            ((uint8_t *)(void *)doorbell_area + 0x18);

        /* PCI doorbell mem area + 0x1c */
        rx_ring->sbq_prod_idx_db_reg = (uint32_t *)(void *)
            ((uint8_t *)(void *)doorbell_area + 0x1c);

        bzero((void *)cqicb, sizeof (*cqicb));

        cqicb->msix_vect = (uint8_t)rx_ring->irq;

        bq_len = (uint16_t)((rx_ring->cq_len == 65536) ?
            (uint16_t)0 : (uint16_t)rx_ring->cq_len);
        cqicb->len = (uint16_t)cpu_to_le16(bq_len | LEN_V | LEN_CPP_CONT);

        cqicb->cq_base_addr_lo =
            cpu_to_le32(LS_64BITS(rx_ring->cq_dma.dma_addr));
        cqicb->cq_base_addr_hi =
            cpu_to_le32(MS_64BITS(rx_ring->cq_dma.dma_addr));

        cqicb->prod_idx_addr_lo =
            cpu_to_le32(LS_64BITS(rx_ring->prod_idx_sh_reg_dma));
        cqicb->prod_idx_addr_hi =
            cpu_to_le32(MS_64BITS(rx_ring->prod_idx_sh_reg_dma));

        /*
         * Set up the control block load flags.
         */
        cqicb->flags = FLAGS_LC | /* Load queue base address */
            FLAGS_LV | /* Load MSI-X vector */
            FLAGS_LI;  /* Load irq delay values */
        if (rx_ring->lbq_len) {
                /* Load lbq values */
                cqicb->flags = (uint8_t)(cqicb->flags | FLAGS_LL);
                tmp = (uint64_t)rx_ring->lbq_dma.dma_addr;
                base_indirect_ptr = (uint64_t *)rx_ring->lbq_base_indirect;
                page_entries = 0;
                do {
                        *base_indirect_ptr = cpu_to_le64(tmp);
                        tmp += VM_PAGE_SIZE;
                        base_indirect_ptr++;
                        page_entries++;
                } while (page_entries < (int)(
                    ((rx_ring->lbq_len * sizeof (uint64_t)) / VM_PAGE_SIZE)));

                cqicb->lbq_addr_lo =
                    cpu_to_le32(LS_64BITS(rx_ring->lbq_base_indirect_dma));
                cqicb->lbq_addr_hi =
                    cpu_to_le32(MS_64BITS(rx_ring->lbq_base_indirect_dma));
                bq_len = (uint16_t)((rx_ring->lbq_buf_size == 65536) ?
                    (uint16_t)0 : (uint16_t)rx_ring->lbq_buf_size);
                cqicb->lbq_buf_size = (uint16_t)cpu_to_le16(bq_len);
                bq_len = (uint16_t)((rx_ring->lbq_len == 65536) ? (uint16_t)0 :
                    (uint16_t)rx_ring->lbq_len);
                cqicb->lbq_len = (uint16_t)cpu_to_le16(bq_len);
                rx_ring->lbq_prod_idx = 0;
                rx_ring->lbq_curr_idx = 0;
        }
        if (rx_ring->sbq_len) {
                /* Load sbq values */
                cqicb->flags = (uint8_t)(cqicb->flags | FLAGS_LS);
                tmp = (uint64_t)rx_ring->sbq_dma.dma_addr;
                base_indirect_ptr = (uint64_t *)rx_ring->sbq_base_indirect;
                page_entries = 0;

                do {
                        *base_indirect_ptr = cpu_to_le64(tmp);
                        tmp += VM_PAGE_SIZE;
                        base_indirect_ptr++;
                        page_entries++;
                } while (page_entries < (uint32_t)
                    (((rx_ring->sbq_len * sizeof (uint64_t)) / VM_PAGE_SIZE)));

                cqicb->sbq_addr_lo =
                    cpu_to_le32(LS_64BITS(rx_ring->sbq_base_indirect_dma));
                cqicb->sbq_addr_hi =
                    cpu_to_le32(MS_64BITS(rx_ring->sbq_base_indirect_dma));
                cqicb->sbq_buf_size = (uint16_t)
                    cpu_to_le16((uint16_t)(rx_ring->sbq_buf_size/2));
                bq_len = (uint16_t)((rx_ring->sbq_len == 65536) ?
                    (uint16_t)0 : (uint16_t)rx_ring->sbq_len);
                cqicb->sbq_len = (uint16_t)cpu_to_le16(bq_len);
                rx_ring->sbq_prod_idx = 0;
                rx_ring->sbq_curr_idx = 0;
        }
        switch (rx_ring->type) {
        case TX_Q:
                cqicb->irq_delay = (uint16_t)
                    cpu_to_le16(qlge->tx_coalesce_usecs);
                cqicb->pkt_delay = (uint16_t)
                    cpu_to_le16(qlge->tx_max_coalesced_frames);
                break;

        case DEFAULT_Q:
                cqicb->irq_delay = (uint16_t)
                    cpu_to_le16(qlge->rx_coalesce_usecs);
                cqicb->pkt_delay = (uint16_t)
                    cpu_to_le16(qlge->rx_max_coalesced_frames);
                break;

        case RX_Q:
                /*
                 * Inbound completion handling rx_rings run in
                 * separate NAPI contexts.
                 */
                cqicb->irq_delay = (uint16_t)
                    cpu_to_le16(qlge->rx_coalesce_usecs);
                cqicb->pkt_delay = (uint16_t)
                    cpu_to_le16(qlge->rx_max_coalesced_frames);
                break;
        default:
                cmn_err(CE_WARN, "Invalid rx_ring->type = %d.",
                    rx_ring->type);
        }
        QL_PRINT(DBG_INIT, ("Initializing rx completion queue %d.\n",
            rx_ring->cq_id));
        /* QL_DUMP_CQICB(qlge, cqicb); */
        err = ql_write_cfg(qlge, CFG_LCQ, rx_ring->cqicb_dma.dma_addr,
            rx_ring->cq_id);
        if (err) {
                cmn_err(CE_WARN, "Failed to load CQICB.");
                return (err);
        }

        rx_ring->rx_packets_dropped_no_buffer = 0;
        rx_ring->rx_pkt_dropped_mac_unenabled = 0;
        rx_ring->rx_failed_sbq_allocs = 0;
        rx_ring->rx_failed_lbq_allocs = 0;
        rx_ring->rx_packets = 0;
        rx_ring->rx_bytes = 0;
        rx_ring->frame_too_long = 0;
        rx_ring->frame_too_short = 0;
        rx_ring->fcs_err = 0;

        return (err);
}

/*
 * start RSS
 */
static int
ql_start_rss(qlge_t *qlge)
{
        struct ricb *ricb = (struct ricb *)qlge->ricb_dma.vaddr;
        int status = 0;
        int i;
        uint8_t *hash_id = (uint8_t *)ricb->hash_cq_id;

        bzero((void *)ricb, sizeof (*ricb));

        ricb->base_cq = RSS_L4K;
        ricb->flags =
            (RSS_L6K | RSS_LI | RSS_LB | RSS_LM | RSS_RI4 | RSS_RI6 | RSS_RT4 |
            RSS_RT6);
        ricb->mask = (uint16_t)cpu_to_le16(RSS_HASH_CQ_ID_MAX - 1);

        /*
         * Fill out the Indirection Table.
         */
        for (i = 0; i < RSS_HASH_CQ_ID_MAX; i++)
                hash_id[i] = (uint8_t)(i & (qlge->rss_ring_count - 1));

        (void) memcpy(&ricb->ipv6_hash_key[0], key_data, 40);
        (void) memcpy(&ricb->ipv4_hash_key[0], key_data, 16);

        QL_PRINT(DBG_INIT, ("Initializing RSS.\n"));

        status = ql_write_cfg(qlge, CFG_LR, qlge->ricb_dma.dma_addr, 0);
        if (status) {
                cmn_err(CE_WARN, "Failed to load RICB.");
                return (status);
        }

        return (status);
}

/*
 * load a tx ring control block to hw and start this ring
 */
static int
ql_start_tx_ring(qlge_t *qlge, struct tx_ring *tx_ring)
{
        struct wqicb_t *wqicb = (struct wqicb_t *)tx_ring->wqicb_dma.vaddr;
        caddr_t doorbell_area =
            qlge->doorbell_reg_iobase + (VM_PAGE_SIZE * tx_ring->wq_id);
        void *shadow_reg = (uint8_t *)qlge->host_copy_shadow_dma_attr.vaddr +
            (tx_ring->wq_id * sizeof (uint64_t)) * RX_TX_RING_SHADOW_SPACE;
        uint64_t shadow_reg_dma = qlge->host_copy_shadow_dma_attr.dma_addr +
            (tx_ring->wq_id * sizeof (uint64_t)) * RX_TX_RING_SHADOW_SPACE;
        int err = 0;

        /*
         * Assign doorbell registers for this tx_ring.
         */

        /* TX PCI doorbell mem area for tx producer index */
        tx_ring->prod_idx_db_reg = (uint32_t *)(void *)doorbell_area;
        tx_ring->prod_idx = 0;
        /* TX PCI doorbell mem area + 0x04 */
        tx_ring->valid_db_reg = (uint32_t *)(void *)
            ((uint8_t *)(void *)doorbell_area + 0x04);

        /*
         * Assign shadow registers for this tx_ring.
         */
        tx_ring->cnsmr_idx_sh_reg = shadow_reg;
        tx_ring->cnsmr_idx_sh_reg_dma = shadow_reg_dma;
        *tx_ring->cnsmr_idx_sh_reg = 0;

        QL_PRINT(DBG_INIT, ("%s tx ring(%d): cnsmr_idx virtual addr = 0x%lx,"
            " phys_addr 0x%lx\n",
            __func__, tx_ring->wq_id, tx_ring->cnsmr_idx_sh_reg,
            tx_ring->cnsmr_idx_sh_reg_dma));

        wqicb->len =
            (uint16_t)cpu_to_le16(tx_ring->wq_len | Q_LEN_V | Q_LEN_CPP_CONT);
        wqicb->flags = cpu_to_le16(Q_FLAGS_LC |
            Q_FLAGS_LB | Q_FLAGS_LI | Q_FLAGS_LO);
        wqicb->cq_id_rss = (uint16_t)cpu_to_le16(tx_ring->cq_id);
        wqicb->rid = 0;
        wqicb->wq_addr_lo = cpu_to_le32(LS_64BITS(tx_ring->wq_dma.dma_addr));
        wqicb->wq_addr_hi = cpu_to_le32(MS_64BITS(tx_ring->wq_dma.dma_addr));
        wqicb->cnsmr_idx_addr_lo =
            cpu_to_le32(LS_64BITS(tx_ring->cnsmr_idx_sh_reg_dma));
        wqicb->cnsmr_idx_addr_hi =
            cpu_to_le32(MS_64BITS(tx_ring->cnsmr_idx_sh_reg_dma));

        ql_init_tx_ring(tx_ring);
        /* QL_DUMP_WQICB(qlge, wqicb); */
        err = ql_write_cfg(qlge, CFG_LRQ, tx_ring->wqicb_dma.dma_addr,
            tx_ring->wq_id);

        if (err) {
                cmn_err(CE_WARN, "Failed to load WQICB.");
                return (err);
        }
        return (err);
}

/*
 * Set up a MAC, multicast or VLAN address for the
 * inbound frame matching.
 */
int
ql_set_mac_addr_reg(qlge_t *qlge, uint8_t *addr, uint32_t type,
    uint16_t index)
{
        uint32_t offset = 0;
        int status = DDI_SUCCESS;

        switch (type) {
        case MAC_ADDR_TYPE_MULTI_MAC:
        case MAC_ADDR_TYPE_CAM_MAC: {
                uint32_t cam_output;
                uint32_t upper = (addr[0] << 8) | addr[1];
                uint32_t lower =
                    (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) |
                    (addr[5]);

                QL_PRINT(DBG_INIT, ("Adding %s ", (type ==
                    MAC_ADDR_TYPE_MULTI_MAC) ?
                    "MULTICAST" : "UNICAST"));
                QL_PRINT(DBG_INIT,
                    ("addr %02x %02x %02x %02x %02x %02x at index %d in "
                    "the CAM.\n",
                    addr[0], addr[1], addr[2], addr[3], addr[4],
                    addr[5], index));

                status = ql_wait_reg_rdy(qlge,
                    REG_MAC_PROTOCOL_ADDRESS_INDEX, MAC_ADDR_MW, 0);
                if (status)
                        goto exit;
                /* offset 0 - lower 32 bits of the MAC address */
                ql_write_reg(qlge, REG_MAC_PROTOCOL_ADDRESS_INDEX,
                    (offset++) |
                    (index << MAC_ADDR_IDX_SHIFT) | /* index */
                    type);      /* type */
                ql_write_reg(qlge, REG_MAC_PROTOCOL_DATA, lower);
                status = ql_wait_reg_rdy(qlge,
                    REG_MAC_PROTOCOL_ADDRESS_INDEX, MAC_ADDR_MW, 0);
                if (status)
                        goto exit;
                /* offset 1 - upper 16 bits of the MAC address */
                ql_write_reg(qlge, REG_MAC_PROTOCOL_ADDRESS_INDEX,
                    (offset++) |
                    (index << MAC_ADDR_IDX_SHIFT) | /* index */
                    type);      /* type */
                ql_write_reg(qlge, REG_MAC_PROTOCOL_DATA, upper);
                status = ql_wait_reg_rdy(qlge,
                    REG_MAC_PROTOCOL_ADDRESS_INDEX, MAC_ADDR_MW, 0);
                if (status)
                        goto exit;
                /* offset 2 - CQ ID associated with this MAC address */
                ql_write_reg(qlge, REG_MAC_PROTOCOL_ADDRESS_INDEX,
                    (offset) | (index << MAC_ADDR_IDX_SHIFT) |  /* index */
                    type);      /* type */
                /*
                 * This field should also include the queue id
                 * and possibly the function id.  Right now we hardcode
                 * the route field to NIC core.
                 */
                if (type == MAC_ADDR_TYPE_CAM_MAC) {
                        cam_output = (CAM_OUT_ROUTE_NIC |
                            (qlge->func_number << CAM_OUT_FUNC_SHIFT) |
                            (0 <<
                            CAM_OUT_CQ_ID_SHIFT));

                        /* route to NIC core */
                        ql_write_reg(qlge, REG_MAC_PROTOCOL_DATA,
                            cam_output);
                        }
                break;
                }
        default:
                cmn_err(CE_WARN,
                    "Address type %d not yet supported.", type);
                status = DDI_FAILURE;
        }
exit:
        return (status);
}

/*
 * The NIC function for this chip has 16 routing indexes.  Each one can be used
 * to route different frame types to various inbound queues.  We send broadcast
 * multicast/error frames to the default queue for slow handling,
 * and CAM hit/RSS frames to the fast handling queues.
 */
static int
ql_set_routing_reg(qlge_t *qlge, uint32_t index, uint32_t mask, int enable)
{
        int status;
        uint32_t value = 0;

        QL_PRINT(DBG_INIT,
            ("%s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s mask %s the routing reg.\n",
            (enable ? "Adding" : "Removing"),
            ((index == RT_IDX_ALL_ERR_SLOT) ? "MAC ERROR/ALL ERROR" : ""),
            ((index == RT_IDX_IP_CSUM_ERR_SLOT) ? "IP CSUM ERROR" : ""),
            ((index ==
            RT_IDX_TCP_UDP_CSUM_ERR_SLOT) ? "TCP/UDP CSUM ERROR" : ""),
            ((index == RT_IDX_BCAST_SLOT) ? "BROADCAST" : ""),
            ((index == RT_IDX_MCAST_MATCH_SLOT) ? "MULTICAST MATCH" : ""),
            ((index == RT_IDX_ALLMULTI_SLOT) ? "ALL MULTICAST MATCH" : ""),
            ((index == RT_IDX_UNUSED6_SLOT) ? "UNUSED6" : ""),
            ((index == RT_IDX_UNUSED7_SLOT) ? "UNUSED7" : ""),
            ((index == RT_IDX_RSS_MATCH_SLOT) ? "RSS ALL/IPV4 MATCH" : ""),
            ((index == RT_IDX_RSS_IPV6_SLOT) ? "RSS IPV6" : ""),
            ((index == RT_IDX_RSS_TCP4_SLOT) ? "RSS TCP4" : ""),
            ((index == RT_IDX_RSS_TCP6_SLOT) ? "RSS TCP6" : ""),
            ((index == RT_IDX_CAM_HIT_SLOT) ? "CAM HIT" : ""),
            ((index == RT_IDX_UNUSED013) ? "UNUSED13" : ""),
            ((index == RT_IDX_UNUSED014) ? "UNUSED14" : ""),
            ((index == RT_IDX_PROMISCUOUS_SLOT) ? "PROMISCUOUS" : ""),
            (enable ? "to" : "from")));

        switch (mask) {
        case RT_IDX_CAM_HIT:
                value = RT_IDX_DST_CAM_Q | /* dest */
                    RT_IDX_TYPE_NICQ | /* type */
                    (RT_IDX_CAM_HIT_SLOT << RT_IDX_IDX_SHIFT); /* index */
                break;

        case RT_IDX_VALID: /* Promiscuous Mode frames. */
                value = RT_IDX_DST_DFLT_Q |     /* dest */
                    RT_IDX_TYPE_NICQ |  /* type */
                    (RT_IDX_PROMISCUOUS_SLOT << RT_IDX_IDX_SHIFT); /* index */
                break;

        case RT_IDX_ERR:        /* Pass up MAC,IP,TCP/UDP error frames. */
                value = RT_IDX_DST_DFLT_Q |     /* dest */
                    RT_IDX_TYPE_NICQ |  /* type */
                    (RT_IDX_ALL_ERR_SLOT << RT_IDX_IDX_SHIFT); /* index */
                break;

        case RT_IDX_BCAST:      /* Pass up Broadcast frames to default Q. */
                value = RT_IDX_DST_DFLT_Q |     /* dest */
                    RT_IDX_TYPE_NICQ |  /* type */
                    (RT_IDX_BCAST_SLOT << RT_IDX_IDX_SHIFT); /* index */
                break;

        case RT_IDX_MCAST:      /* Pass up All Multicast frames. */
                value = RT_IDX_DST_CAM_Q |      /* dest */
                    RT_IDX_TYPE_NICQ |  /* type */
                    (RT_IDX_ALLMULTI_SLOT << RT_IDX_IDX_SHIFT); /* index */
                break;

        case RT_IDX_MCAST_MATCH:        /* Pass up matched Multicast frames. */
                value = RT_IDX_DST_CAM_Q |      /* dest */
                    RT_IDX_TYPE_NICQ |  /* type */
                    (RT_IDX_MCAST_MATCH_SLOT << RT_IDX_IDX_SHIFT); /* index */
                break;

        case RT_IDX_RSS_MATCH:  /* Pass up matched RSS frames. */
                value = RT_IDX_DST_RSS |        /* dest */
                    RT_IDX_TYPE_NICQ |  /* type */
                    (RT_IDX_RSS_MATCH_SLOT << RT_IDX_IDX_SHIFT); /* index */
                break;

        case 0: /* Clear the E-bit on an entry. */
                value = RT_IDX_DST_DFLT_Q |     /* dest */
                    RT_IDX_TYPE_NICQ |  /* type */
                    (index << RT_IDX_IDX_SHIFT); /* index */
                break;

        default:
                cmn_err(CE_WARN, "Mask type %d not yet supported.",
                    mask);
                status = -EPERM;
                goto exit;
        }

        if (value != 0) {
                status = ql_wait_reg_rdy(qlge, REG_ROUTING_INDEX, RT_IDX_MW, 0);
                if (status)
                        goto exit;
                value |= (enable ? RT_IDX_E : 0);
                ql_write_reg(qlge, REG_ROUTING_INDEX, value);
                ql_write_reg(qlge, REG_ROUTING_DATA, enable ? mask : 0);
        }

exit:
        return (status);
}

/*
 * Clear all the entries in the routing table.
 * Caller must get semaphore in advance.
 */

static int
ql_stop_routing(qlge_t *qlge)
{
        int status = 0;
        int i;
        /* Clear all the entries in the routing table. */
        for (i = 0; i < 16; i++) {
                status = ql_set_routing_reg(qlge, i, 0, 0);
                if (status) {
                        cmn_err(CE_WARN, "Stop routing failed. ");
                }
        }
        return (status);
}

/* Initialize the frame-to-queue routing. */
int
ql_route_initialize(qlge_t *qlge)
{
        int status = 0;

        status = ql_sem_spinlock(qlge, SEM_RT_IDX_MASK);
        if (status != DDI_SUCCESS)
                return (status);

        /* Clear all the entries in the routing table. */
        status = ql_stop_routing(qlge);
        if (status) {
                goto exit;
        }
        status = ql_set_routing_reg(qlge, RT_IDX_BCAST_SLOT, RT_IDX_BCAST, 1);
        if (status) {
                cmn_err(CE_WARN,
                    "Failed to init routing register for broadcast packets.");
                goto exit;
        }
        /*
         * If we have more than one inbound queue, then turn on RSS in the
         * routing block.
         */
        if (qlge->rss_ring_count > 1) {
                status = ql_set_routing_reg(qlge, RT_IDX_RSS_MATCH_SLOT,
                    RT_IDX_RSS_MATCH, 1);
                if (status) {
                        cmn_err(CE_WARN,
                            "Failed to init routing register for MATCH RSS "
                            "packets.");
                        goto exit;
                }
        }

        status = ql_set_routing_reg(qlge, RT_IDX_CAM_HIT_SLOT,
            RT_IDX_CAM_HIT, 1);
        if (status) {
                cmn_err(CE_WARN,
                    "Failed to init routing register for CAM packets.");
                goto exit;
        }

        status = ql_set_routing_reg(qlge, RT_IDX_MCAST_MATCH_SLOT,
            RT_IDX_MCAST_MATCH, 1);
        if (status) {
                cmn_err(CE_WARN,
                    "Failed to init routing register for Multicast "
                    "packets.");
        }

exit:
        ql_sem_unlock(qlge, SEM_RT_IDX_MASK);
        return (status);
}

/*
 * Initialize hardware
 */
static int
ql_device_initialize(qlge_t *qlge)
{
        uint32_t value, mask;
        int i;
        int status = 0;
        uint16_t pause = PAUSE_MODE_DISABLED;
        boolean_t update_port_config = B_FALSE;
        uint32_t pause_bit_mask;
        boolean_t dcbx_enable = B_FALSE;
        uint32_t dcbx_bit_mask = 0x10;
        /*
         * Set up the System register to halt on errors.
         */
        value = SYS_EFE | SYS_FAE;
        mask = value << 16;
        ql_write_reg(qlge, REG_SYSTEM, mask | value);

        /* Set the default queue. */
        value = NIC_RCV_CFG_DFQ;
        mask = NIC_RCV_CFG_DFQ_MASK;

        ql_write_reg(qlge, REG_NIC_RECEIVE_CONFIGURATION, mask | value);

        /* Enable the MPI interrupt. */
        ql_write_reg(qlge, REG_INTERRUPT_MASK, (INTR_MASK_PI << 16)
            | INTR_MASK_PI);
        /* Enable the function, set pagesize, enable error checking. */
        value = FSC_FE | FSC_EPC_INBOUND | FSC_EPC_OUTBOUND |
            FSC_EC | FSC_VM_PAGE_4K | FSC_DBRST_1024;
        /* Set/clear header splitting. */
        if (CFG_IST(qlge, CFG_ENABLE_SPLIT_HEADER)) {
                value |= FSC_SH;
                ql_write_reg(qlge, REG_SPLIT_HEADER, SMALL_BUFFER_SIZE);
        }
        mask = FSC_VM_PAGESIZE_MASK |
            FSC_DBL_MASK | FSC_DBRST_MASK | (value << 16);
        ql_write_reg(qlge, REG_FUNCTION_SPECIFIC_CONTROL, mask | value);
        /*
         * check current port max frame size, if different from OS setting,
         * then we need to change
         */
        qlge->max_frame_size =
            (qlge->mtu == ETHERMTU)? NORMAL_FRAME_SIZE : JUMBO_FRAME_SIZE;

        mutex_enter(&qlge->mbx_mutex);
        status = ql_get_port_cfg(qlge);
        mutex_exit(&qlge->mbx_mutex);

        if (status == DDI_SUCCESS) {
                /* if current frame size is smaller than required size */
                if (qlge->port_cfg_info.max_frame_size <
                    qlge->max_frame_size) {
                        QL_PRINT(DBG_MBX,
                            ("update frame size, current %d, new %d\n",
                            qlge->port_cfg_info.max_frame_size,
                            qlge->max_frame_size));
                        qlge->port_cfg_info.max_frame_size =
                            qlge->max_frame_size;
                        qlge->port_cfg_info.link_cfg |= ENABLE_JUMBO;
                        update_port_config = B_TRUE;
                }

                if (qlge->port_cfg_info.link_cfg & STD_PAUSE)
                        pause = PAUSE_MODE_STANDARD;
                else if (qlge->port_cfg_info.link_cfg & PP_PAUSE)
                        pause = PAUSE_MODE_PER_PRIORITY;

                if (pause != qlge->pause) {
                        pause_bit_mask = 0x60;  /* bit 5-6 */
                        /* clear pause bits */
                        qlge->port_cfg_info.link_cfg &= ~pause_bit_mask;
                        if (qlge->pause == PAUSE_MODE_STANDARD)
                                qlge->port_cfg_info.link_cfg |= STD_PAUSE;
                        else if (qlge->pause == PAUSE_MODE_PER_PRIORITY)
                                qlge->port_cfg_info.link_cfg |= PP_PAUSE;
                        update_port_config = B_TRUE;
                }

                if (qlge->port_cfg_info.link_cfg & DCBX_ENABLE)
                        dcbx_enable = B_TRUE;
                if (dcbx_enable != qlge->dcbx_enable) {
                        qlge->port_cfg_info.link_cfg &= ~dcbx_bit_mask;
                        if (qlge->dcbx_enable)
                                qlge->port_cfg_info.link_cfg |= DCBX_ENABLE;
                }

                update_port_config = B_TRUE;

                /* if need to update port configuration */
                if (update_port_config) {
                        mutex_enter(&qlge->mbx_mutex);
                        (void) ql_set_mpi_port_config(qlge,
                            qlge->port_cfg_info);
                        mutex_exit(&qlge->mbx_mutex);
                }
        } else
                cmn_err(CE_WARN, "ql_get_port_cfg failed");

        /* Start up the rx queues. */
        for (i = 0; i < qlge->rx_ring_count; i++) {
                status = ql_start_rx_ring(qlge, &qlge->rx_ring[i]);
                if (status) {
                        cmn_err(CE_WARN,
                            "Failed to start rx ring[%d]", i);
                        return (status);
                }
        }

        /*
         * If there is more than one inbound completion queue
         * then download a RICB to configure RSS.
         */
        if (qlge->rss_ring_count > 1) {
                status = ql_start_rss(qlge);
                if (status) {
                        cmn_err(CE_WARN, "Failed to start RSS.");
                        return (status);
                }
        }

        /* Start up the tx queues. */
        for (i = 0; i < qlge->tx_ring_count; i++) {
                status = ql_start_tx_ring(qlge, &qlge->tx_ring[i]);
                if (status) {
                        cmn_err(CE_WARN,
                            "Failed to start tx ring[%d]", i);
                        return (status);
                }
        }
        qlge->selected_tx_ring = 0;
        /* Set the frame routing filter. */
        status = ql_route_initialize(qlge);
        if (status) {
                cmn_err(CE_WARN,
                    "Failed to init CAM/Routing tables.");
                return (status);
        }

        return (status);
}
/*
 * Issue soft reset to chip.
 */
static int
ql_asic_reset(qlge_t *qlge)
{
        int status = DDI_SUCCESS;

        ql_write_reg(qlge, REG_RESET_FAILOVER, FUNCTION_RESET_MASK
            |FUNCTION_RESET);

        if (ql_wait_reg_bit(qlge, REG_RESET_FAILOVER, FUNCTION_RESET,
            BIT_RESET, 0) != DDI_SUCCESS) {
                cmn_err(CE_WARN,
                    "TIMEOUT!!! errored out of resetting the chip!");
                status = DDI_FAILURE;
        }

        return (status);
}

/*
 * If there are more than MIN_BUFFERS_ARM_COUNT small buffer descriptors in
 * its free list, move xMIN_BUFFERS_ARM_COUNT descriptors to its in use list
 * to be used by hardware.
 */
static void
ql_arm_sbuf(qlge_t *qlge, struct rx_ring *rx_ring)
{
        struct bq_desc *sbq_desc;
        int i;
        uint64_t *sbq_entry = rx_ring->sbq_dma.vaddr;
        uint32_t arm_count;

        if (rx_ring->sbuf_free_count > rx_ring->sbq_len-MIN_BUFFERS_ARM_COUNT)
                arm_count = (rx_ring->sbq_len-MIN_BUFFERS_ARM_COUNT);
        else {
                /* Adjust to a multiple of 16 */
                arm_count = (rx_ring->sbuf_free_count / 16) * 16;
#ifdef QLGE_LOAD_UNLOAD
                cmn_err(CE_NOTE, "adjust sbuf arm_count %d\n", arm_count);
#endif
        }
        for (i = 0; i < arm_count; i++) {
                sbq_desc = ql_get_sbuf_from_free_list(rx_ring);
                if (sbq_desc == NULL)
                        break;
                /* Arm asic */
                *sbq_entry = cpu_to_le64(sbq_desc->bd_dma.dma_addr);
                sbq_entry++;

                /* link the descriptors to in_use_list */
                ql_add_sbuf_to_in_use_list(rx_ring, sbq_desc);
                rx_ring->sbq_prod_idx++;
        }
        ql_update_sbq_prod_idx(qlge, rx_ring);
}

/*
 * If there are more than MIN_BUFFERS_ARM_COUNT large buffer descriptors in
 * its free list, move xMIN_BUFFERS_ARM_COUNT descriptors to its in use list
 * to be used by hardware.
 */
static void
ql_arm_lbuf(qlge_t *qlge, struct rx_ring *rx_ring)
{
        struct bq_desc *lbq_desc;
        int i;
        uint64_t *lbq_entry = rx_ring->lbq_dma.vaddr;
        uint32_t arm_count;

        if (rx_ring->lbuf_free_count > rx_ring->lbq_len-MIN_BUFFERS_ARM_COUNT)
                arm_count = (rx_ring->lbq_len-MIN_BUFFERS_ARM_COUNT);
        else {
                /* Adjust to a multiple of 16 */
                arm_count = (rx_ring->lbuf_free_count / 16) * 16;
#ifdef QLGE_LOAD_UNLOAD
                cmn_err(CE_NOTE, "adjust lbuf arm_count %d\n", arm_count);
#endif
        }
        for (i = 0; i < arm_count; i++) {
                lbq_desc = ql_get_lbuf_from_free_list(rx_ring);
                if (lbq_desc == NULL)
                        break;
                /* Arm asic */
                *lbq_entry = cpu_to_le64(lbq_desc->bd_dma.dma_addr);
                lbq_entry++;

                /* link the descriptors to in_use_list */
                ql_add_lbuf_to_in_use_list(rx_ring, lbq_desc);
                rx_ring->lbq_prod_idx++;
        }
        ql_update_lbq_prod_idx(qlge, rx_ring);
}


/*
 * Initializes the adapter by configuring request and response queues,
 * allocates and ARMs small and large receive buffers to the
 * hardware
 */
static int
ql_bringup_adapter(qlge_t *qlge)
{
        int i;

        if (ql_device_initialize(qlge) != DDI_SUCCESS) {
                cmn_err(CE_WARN, "?%s(%d): ql_device_initialize failed",
                    __func__, qlge->instance);
                goto err_bringup;
        }
        qlge->sequence |= INIT_ADAPTER_UP;

#ifdef QLGE_TRACK_BUFFER_USAGE
        for (i = 0; i < qlge->rx_ring_count; i++) {
                if (qlge->rx_ring[i].type != TX_Q) {
                        qlge->rx_sb_low_count[i] = NUM_SMALL_BUFFERS;
                        qlge->rx_lb_low_count[i] = NUM_LARGE_BUFFERS;
                }
                qlge->cq_low_count[i] = NUM_RX_RING_ENTRIES;
        }
#endif
        /* Arm buffers */
        for (i = 0; i < qlge->rx_ring_count; i++) {
                if (qlge->rx_ring[i].type != TX_Q) {
                        ql_arm_sbuf(qlge, &qlge->rx_ring[i]);
                        ql_arm_lbuf(qlge, &qlge->rx_ring[i]);
                }
        }

        /* Enable work/request queues */
        for (i = 0; i < qlge->tx_ring_count; i++) {
                if (qlge->tx_ring[i].valid_db_reg)
                        ql_write_doorbell_reg(qlge,
                            qlge->tx_ring[i].valid_db_reg,
                            REQ_Q_VALID);
        }

        /* Enable completion queues */
        for (i = 0; i < qlge->rx_ring_count; i++) {
                if (qlge->rx_ring[i].valid_db_reg)
                        ql_write_doorbell_reg(qlge,
                            qlge->rx_ring[i].valid_db_reg,
                            RSP_Q_VALID);
        }

        for (i = 0; i < qlge->tx_ring_count; i++) {
                mutex_enter(&qlge->tx_ring[i].tx_lock);
                qlge->tx_ring[i].mac_flags = QL_MAC_STARTED;
                mutex_exit(&qlge->tx_ring[i].tx_lock);
        }

        for (i = 0; i < qlge->rx_ring_count; i++) {
                mutex_enter(&qlge->rx_ring[i].rx_lock);
                qlge->rx_ring[i].mac_flags = QL_MAC_STARTED;
                mutex_exit(&qlge->rx_ring[i].rx_lock);
        }

        /* This mutex will get re-acquired in enable_completion interrupt */
        mutex_exit(&qlge->hw_mutex);
        /* Traffic can start flowing now */
        ql_enable_all_completion_interrupts(qlge);
        mutex_enter(&qlge->hw_mutex);

        ql_enable_global_interrupt(qlge);

        qlge->sequence |= ADAPTER_INIT;
        return (DDI_SUCCESS);

err_bringup:
        (void) ql_asic_reset(qlge);
        return (DDI_FAILURE);
}

/*
 * Initialize mutexes of each rx/tx rings
 */
static int
ql_init_rx_tx_locks(qlge_t *qlge)
{
        struct tx_ring *tx_ring;
        struct rx_ring *rx_ring;
        int i;

        for (i = 0; i < qlge->tx_ring_count; i++) {
                tx_ring = &qlge->tx_ring[i];
                mutex_init(&tx_ring->tx_lock, NULL, MUTEX_DRIVER,
                    DDI_INTR_PRI(qlge->intr_pri));
        }

        for (i = 0; i < qlge->rx_ring_count; i++) {
                rx_ring = &qlge->rx_ring[i];
                mutex_init(&rx_ring->rx_lock, NULL, MUTEX_DRIVER,
                    DDI_INTR_PRI(qlge->intr_pri));
                mutex_init(&rx_ring->sbq_lock, NULL, MUTEX_DRIVER,
                    DDI_INTR_PRI(qlge->intr_pri));
                mutex_init(&rx_ring->lbq_lock, NULL, MUTEX_DRIVER,
                    DDI_INTR_PRI(qlge->intr_pri));
        }

        return (DDI_SUCCESS);
}

/*ARGSUSED*/
/*
 * Simply call pci_ereport_post which generates ereports for errors
 * that occur in the PCI local bus configuration status registers.
 */
static int
ql_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
{
        pci_ereport_post(dip, err, NULL);
        return (err->fme_status);
}

static void
ql_fm_init(qlge_t *qlge)
{
        ddi_iblock_cookie_t iblk;

        QL_PRINT(DBG_INIT, ("ql_fm_init(%d) entered, FMA capability %x\n",
            qlge->instance, qlge->fm_capabilities));
        /*
         * Register capabilities with IO Fault Services. The capabilities
         * set above may not be supported by the parent nexus, in that case
         * some capability bits may be cleared.
         */
        if (qlge->fm_capabilities)
                ddi_fm_init(qlge->dip, &qlge->fm_capabilities, &iblk);

        /*
         * Initialize pci ereport capabilities if ereport capable
         */
        if (DDI_FM_EREPORT_CAP(qlge->fm_capabilities) ||
            DDI_FM_ERRCB_CAP(qlge->fm_capabilities)) {
                pci_ereport_setup(qlge->dip);
        }

        /* Register error callback if error callback capable */
        if (DDI_FM_ERRCB_CAP(qlge->fm_capabilities)) {
                ddi_fm_handler_register(qlge->dip,
                    ql_fm_error_cb, (void*) qlge);
        }

        /*
         * DDI_FLGERR_ACC indicates:
         *  Driver will check its access handle(s) for faults on
         *   a regular basis by calling ddi_fm_acc_err_get
         *  Driver is able to cope with incorrect results of I/O
         *   operations resulted from an I/O fault
         */
        if (DDI_FM_ACC_ERR_CAP(qlge->fm_capabilities)) {
                ql_dev_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC;
        }

        /*
         * DDI_DMA_FLAGERR indicates:
         *  Driver will check its DMA handle(s) for faults on a
         *   regular basis using ddi_fm_dma_err_get
         *  Driver is able to cope with incorrect results of DMA
         *   operations resulted from an I/O fault
         */
        if (DDI_FM_DMA_ERR_CAP(qlge->fm_capabilities)) {
                tx_mapping_dma_attr.dma_attr_flags = DDI_DMA_FLAGERR;
                dma_attr.dma_attr_flags = DDI_DMA_FLAGERR;
        }
        QL_PRINT(DBG_INIT, ("ql_fm_init(%d) done\n",
            qlge->instance));
}

static void
ql_fm_fini(qlge_t *qlge)
{
        QL_PRINT(DBG_INIT, ("ql_fm_fini(%d) entered\n",
            qlge->instance));
        /* Only unregister FMA capabilities if we registered some */
        if (qlge->fm_capabilities) {

                /*
                 * Release any resources allocated by pci_ereport_setup()
                 */
                if (DDI_FM_EREPORT_CAP(qlge->fm_capabilities) ||
                    DDI_FM_ERRCB_CAP(qlge->fm_capabilities))
                        pci_ereport_teardown(qlge->dip);

                /*
                 * Un-register error callback if error callback capable
                 */
                if (DDI_FM_ERRCB_CAP(qlge->fm_capabilities))
                        ddi_fm_handler_unregister(qlge->dip);

                /* Unregister from IO Fault Services */
                ddi_fm_fini(qlge->dip);
        }
        QL_PRINT(DBG_INIT, ("ql_fm_fini(%d) done\n",
            qlge->instance));
}
/*
 * ql_attach - Driver attach.
 */
static int
ql_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
        int instance;
        qlge_t *qlge = NULL;
        int rval;
        uint16_t w;
        mac_register_t *macp = NULL;
        uint32_t data;

        rval = DDI_FAILURE;

        /* first get the instance */
        instance = ddi_get_instance(dip);

        switch (cmd) {
        case DDI_ATTACH:
                /*
                 * Allocate our per-device-instance structure
                 */
                qlge = (qlge_t *)kmem_zalloc(sizeof (*qlge), KM_SLEEP);
                ASSERT(qlge != NULL);
                qlge->sequence |= INIT_SOFTSTATE_ALLOC;

                qlge->dip = dip;
                qlge->instance = instance;
                /* Set up the coalescing parameters. */
                qlge->ql_dbgprnt = 0;
#if QL_DEBUG
                qlge->ql_dbgprnt = QL_DEBUG;
#endif /* QL_DEBUG */

                /*
                 * Initialize for fma support
                 */
                /* fault management (fm) capabilities. */
                qlge->fm_capabilities =
                    DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE;
                data = ql_get_prop(qlge, "fm-capable");
                if (data <= 0xf) {
                        qlge->fm_capabilities = data;
                }
                ql_fm_init(qlge);
                qlge->sequence |= INIT_FM;
                QL_PRINT(DBG_INIT, ("ql_attach(%d): fma init done\n",
                    qlge->instance));

                /*
                 * Setup the ISP8x00 registers address mapping to be
                 * accessed by this particular driver.
                 * 0x0   Configuration Space
                 * 0x1   I/O Space
                 * 0x2   1st Memory Space address - Control Register Set
                 * 0x3   2nd Memory Space address - Doorbell Memory Space
                 */
                w = 2;
                if (ddi_regs_map_setup(dip, w, (caddr_t *)&qlge->iobase, 0,
                    sizeof (dev_reg_t), &ql_dev_acc_attr,
                    &qlge->dev_handle) != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "%s(%d): Unable to map device "
                            "registers", ADAPTER_NAME, instance);
                        break;
                }
                QL_PRINT(DBG_GLD, ("ql_attach: I/O base = 0x%x\n",
                    qlge->iobase));
                qlge->sequence |= INIT_REGS_SETUP;

                /* map Doorbell memory space */
                w = 3;
                if (ddi_regs_map_setup(dip, w,
                    (caddr_t *)&qlge->doorbell_reg_iobase, 0,
                    0x100000 /* sizeof (dev_doorbell_reg_t) */,
                    &ql_dev_acc_attr,
                    &qlge->dev_doorbell_reg_handle) != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "%s(%d): Unable to map Doorbell "
                            "registers",
                            ADAPTER_NAME, instance);
                        break;
                }
                QL_PRINT(DBG_GLD, ("ql_attach: Doorbell I/O base = 0x%x\n",
                    qlge->doorbell_reg_iobase));
                qlge->sequence |= INIT_DOORBELL_REGS_SETUP;

                /*
                 * Allocate a macinfo structure for this instance
                 */
                if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
                        cmn_err(CE_WARN, "%s(%d): mac_alloc failed",
                            __func__, instance);
                        break;
                }
                /* save adapter status to dip private data */
                ddi_set_driver_private(dip, qlge);
                QL_PRINT(DBG_INIT, ("%s(%d): Allocate macinfo structure done\n",
                    ADAPTER_NAME, instance));
                qlge->sequence |= INIT_MAC_ALLOC;

                /*
                 * Attach this instance of the device
                 */
                /* Setup PCI Local Bus Configuration resource. */
                if (pci_config_setup(dip, &qlge->pci_handle) != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "%s(%d):Unable to get PCI resources",
                            ADAPTER_NAME, instance);
                        if (qlge->fm_enable) {
                                ql_fm_ereport(qlge, DDI_FM_DEVICE_INVAL_STATE);
                                ddi_fm_service_impact(qlge->dip,
                                    DDI_SERVICE_LOST);
                        }
                        break;
                }
                qlge->sequence |= INIT_PCI_CONFIG_SETUP;
                QL_PRINT(DBG_GLD, ("ql_attach(%d): pci_config_setup done\n",
                    instance));

                if (ql_init_instance(qlge) != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "%s(%d): Unable to initialize device "
                            "instance", ADAPTER_NAME, instance);
                        if (qlge->fm_enable) {
                                ql_fm_ereport(qlge, DDI_FM_DEVICE_INVAL_STATE);
                                ddi_fm_service_impact(qlge->dip,
                                    DDI_SERVICE_LOST);
                        }
                        break;
                }
                QL_PRINT(DBG_GLD, ("ql_attach(%d): ql_init_instance done\n",
                    instance));

                /* Setup interrupt vectors */
                if (ql_alloc_irqs(qlge) != DDI_SUCCESS) {
                        break;
                }
                qlge->sequence |= INIT_INTR_ALLOC;
                QL_PRINT(DBG_GLD, ("ql_attach(%d): ql_alloc_irqs done\n",
                    instance));

                /* Configure queues */
                if (ql_setup_rings(qlge) != DDI_SUCCESS) {
                        break;
                }
                qlge->sequence |= INIT_SETUP_RINGS;
                QL_PRINT(DBG_GLD, ("ql_attach(%d): setup rings done\n",
                    instance));

                /*
                 * Allocate memory resources
                 */
                if (ql_alloc_mem_resources(qlge) != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "%s(%d): memory allocation failed",
                            __func__, qlge->instance);
                        break;
                }
                qlge->sequence |= INIT_MEMORY_ALLOC;
                QL_PRINT(DBG_GLD, ("ql_alloc_mem_resources(%d) done\n",
                    instance));

                /*
                 * Map queues to interrupt vectors
                 */
                ql_resolve_queues_to_irqs(qlge);

                /* Initialize mutex, need the interrupt priority */
                (void) ql_init_rx_tx_locks(qlge);
                qlge->sequence |= INIT_LOCKS_CREATED;
                QL_PRINT(DBG_INIT, ("%s(%d): ql_init_rx_tx_locks done\n",
                    ADAPTER_NAME, instance));

                /*
                 * Use a soft interrupt to do something that we do not want
                 * to do in regular network functions or with mutexs being held
                 */
                if (ddi_intr_add_softint(qlge->dip, &qlge->mpi_event_intr_hdl,
                    DDI_INTR_SOFTPRI_MIN, ql_mpi_event_work, (caddr_t)qlge)
                    != DDI_SUCCESS) {
                        break;
                }

                if (ddi_intr_add_softint(qlge->dip, &qlge->asic_reset_intr_hdl,
                    DDI_INTR_SOFTPRI_MIN, ql_asic_reset_work, (caddr_t)qlge)
                    != DDI_SUCCESS) {
                        break;
                }

                if (ddi_intr_add_softint(qlge->dip, &qlge->mpi_reset_intr_hdl,
                    DDI_INTR_SOFTPRI_MIN, ql_mpi_reset_work, (caddr_t)qlge)
                    != DDI_SUCCESS) {
                        break;
                }
                qlge->sequence |= INIT_ADD_SOFT_INTERRUPT;
                QL_PRINT(DBG_INIT, ("%s(%d): ddi_intr_add_softint done\n",
                    ADAPTER_NAME, instance));

                /*
                 * mutex to protect the adapter state structure.
                 * initialize mutexes according to the interrupt priority
                 */
                mutex_init(&qlge->gen_mutex, NULL, MUTEX_DRIVER,
                    DDI_INTR_PRI(qlge->intr_pri));
                mutex_init(&qlge->hw_mutex, NULL, MUTEX_DRIVER,
                    DDI_INTR_PRI(qlge->intr_pri));
                mutex_init(&qlge->mbx_mutex, NULL, MUTEX_DRIVER,
                    DDI_INTR_PRI(qlge->intr_pri));

                /* Mailbox wait and interrupt conditional variable. */
                cv_init(&qlge->cv_mbx_intr, NULL, CV_DRIVER, NULL);
                qlge->sequence |= INIT_MUTEX;
                QL_PRINT(DBG_INIT, ("%s(%d): mutex_init done\n",
                    ADAPTER_NAME, instance));

                /*
                 * KStats
                 */
                if (ql_init_kstats(qlge) != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "%s(%d): KState initialization failed",
                            ADAPTER_NAME, instance);
                        break;
                }
                qlge->sequence |= INIT_KSTATS;
                QL_PRINT(DBG_INIT, ("%s(%d): ql_init_kstats done\n",
                    ADAPTER_NAME, instance));

                /*
                 * Initialize gld macinfo structure
                 */
                ql_gld3_init(qlge, macp);
                /*
                 * Add interrupt handlers
                 */
                if (ql_add_intr_handlers(qlge) != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "Failed to add interrupt "
                            "handlers");
                        break;
                }
                qlge->sequence |= INIT_ADD_INTERRUPT;
                QL_PRINT(DBG_INIT, ("%s(%d): Add interrupt handler done\n",
                    ADAPTER_NAME, instance));

                /*
                 * MAC Register
                 */
                if (mac_register(macp, &qlge->mh) != DDI_SUCCESS) {
                        cmn_err(CE_WARN, "%s(%d): mac_register failed",
                            __func__, instance);
                        break;
                }
                qlge->sequence |= INIT_MAC_REGISTERED;
                QL_PRINT(DBG_GLD, ("%s(%d): mac_register done\n",
                    ADAPTER_NAME, instance));

                mac_free(macp);
                macp = NULL;

                qlge->mac_flags = QL_MAC_ATTACHED;

                ddi_report_dev(dip);

                rval = DDI_SUCCESS;

        break;
/*
 * DDI_RESUME
 * When called  with  cmd  set  to  DDI_RESUME,  attach()  must
 * restore  the hardware state of a device (power may have been
 * removed from the device), allow  pending  requests  to  con-
 * tinue,  and  service  new requests. In this case, the driver
 * must not  make  any  assumptions  about  the  state  of  the
 * hardware,  but  must  restore the state of the device except
 * for the power level of components.
 *
 */
        case DDI_RESUME:

                if ((qlge = (qlge_t *)QL_GET_DEV(dip)) == NULL)
                        return (DDI_FAILURE);

                QL_PRINT(DBG_GLD, ("%s(%d)-DDI_RESUME\n",
                    __func__, qlge->instance));

                mutex_enter(&qlge->gen_mutex);
                rval = ql_do_start(qlge);
                mutex_exit(&qlge->gen_mutex);
                break;

        default:
                break;
        }

        /* if failed to attach */
        if ((cmd == DDI_ATTACH) && (rval != DDI_SUCCESS) && (qlge != NULL)) {
                cmn_err(CE_WARN, "qlge driver attach failed, sequence %x",
                    qlge->sequence);
                ql_free_resources(qlge);
        }

        return (rval);
}

/*
 * Unbind all pending tx dma handles during driver bring down
 */
static void
ql_unbind_pending_tx_dma_handle(struct tx_ring *tx_ring)
{
        struct tx_ring_desc *tx_ring_desc;
        int i, j;

        if (tx_ring->wq_desc) {
                tx_ring_desc = tx_ring->wq_desc;
                for (i = 0; i < tx_ring->wq_len; i++, tx_ring_desc++) {
                        for (j = 0; j < tx_ring_desc->tx_dma_handle_used; j++) {
                                if (tx_ring_desc->tx_dma_handle[j]) {
                                        (void) ddi_dma_unbind_handle(
                                            tx_ring_desc->tx_dma_handle[j]);
                                }
                        }
                        tx_ring_desc->tx_dma_handle_used = 0;
                } /* end of for loop */
        }
}
/*
 * Wait for all the packets sent to the chip to finish transmission
 * to prevent buffers to be unmapped before or during a transmit operation
 */
static int
ql_wait_tx_quiesce(qlge_t *qlge)
{
        int count = MAX_TX_WAIT_COUNT, i;
        int rings_done;
        volatile struct tx_ring *tx_ring;
        uint32_t consumer_idx;
        uint32_t producer_idx;
        uint32_t temp;
        int done = 0;
        int rval = DDI_FAILURE;

        while (!done) {
                rings_done = 0;

                for (i = 0; i < qlge->tx_ring_count; i++) {
                        tx_ring = &qlge->tx_ring[i];
                        temp = ql_read_doorbell_reg(qlge,
                            tx_ring->prod_idx_db_reg);
                        producer_idx = temp & 0x0000ffff;
                        consumer_idx = (temp >> 16);

                        if (qlge->isr_stride) {
                                struct rx_ring *ob_ring;
                                ob_ring = &qlge->rx_ring[tx_ring->cq_id];
                                if (producer_idx != ob_ring->cnsmr_idx) {
                                        cmn_err(CE_NOTE, " force clean \n");
                                        (void) ql_clean_outbound_rx_ring(
                                            ob_ring);
                                }
                        }
                        /*
                         * Get the pending iocb count, ones which have not been
                         * pulled down by the chip
                         */
                        if (producer_idx >= consumer_idx)
                                temp = (producer_idx - consumer_idx);
                        else
                                temp = (tx_ring->wq_len - consumer_idx) +
                                    producer_idx;

                        if ((tx_ring->tx_free_count + temp) >= tx_ring->wq_len)
                                rings_done++;
                        else {
                                done = 1;
                                break;
                        }
                }

                /* If all the rings are done */
                if (rings_done >= qlge->tx_ring_count) {
#ifdef QLGE_LOAD_UNLOAD
                        cmn_err(CE_NOTE, "%s(%d) done successfully \n",
                            __func__, qlge->instance);
#endif
                        rval = DDI_SUCCESS;
                        break;
                }

                qlge_delay(100);

                count--;
                if (!count) {

                        count = MAX_TX_WAIT_COUNT;
#ifdef QLGE_LOAD_UNLOAD
                        volatile struct rx_ring *rx_ring;
                        cmn_err(CE_NOTE, "%s(%d): Waiting for %d pending"
                            " Transmits on queue %d to complete .\n",
                            __func__, qlge->instance,
                            (qlge->tx_ring[i].wq_len -
                            qlge->tx_ring[i].tx_free_count),
                            i);

                        rx_ring = &qlge->rx_ring[i+1];
                        temp = ql_read_doorbell_reg(qlge,
                            rx_ring->cnsmr_idx_db_reg);
                        consumer_idx = temp & 0x0000ffff;
                        producer_idx = (temp >> 16);
                        cmn_err(CE_NOTE, "%s(%d): Transmit completion queue %d,"
                            " Producer %d, Consumer %d\n",
                            __func__, qlge->instance,
                            i+1,
                            producer_idx, consumer_idx);

                        temp = ql_read_doorbell_reg(qlge,
                            tx_ring->prod_idx_db_reg);
                        producer_idx = temp & 0x0000ffff;
                        consumer_idx = (temp >> 16);
                        cmn_err(CE_NOTE, "%s(%d): Transmit request queue %d,"
                            " Producer %d, Consumer %d\n",
                            __func__, qlge->instance, i,
                            producer_idx, consumer_idx);
#endif

                        /* For now move on */
                        break;
                }
        }
        /* Stop the request queue */
        mutex_enter(&qlge->hw_mutex);
        for (i = 0; i < qlge->tx_ring_count; i++) {
                if (qlge->tx_ring[i].valid_db_reg) {
                        ql_write_doorbell_reg(qlge,
                            qlge->tx_ring[i].valid_db_reg, 0);
                }
        }
        mutex_exit(&qlge->hw_mutex);
        return (rval);
}

/*
 * Wait for all the receives indicated to the stack to come back
 */
static int
ql_wait_rx_complete(qlge_t *qlge)
{
        int i;
        /* Disable all the completion queues */
        mutex_enter(&qlge->hw_mutex);
        for (i = 0; i < qlge->rx_ring_count; i++) {
                if (qlge->rx_ring[i].valid_db_reg) {
                        ql_write_doorbell_reg(qlge,
                            qlge->rx_ring[i].valid_db_reg, 0);
                }
        }
        mutex_exit(&qlge->hw_mutex);

        /* Wait for OS to return all rx buffers */
        qlge_delay(QL_ONE_SEC_DELAY);
        return (DDI_SUCCESS);
}

/*
 * stop the driver
 */
static int
ql_bringdown_adapter(qlge_t *qlge)
{
        int i;
        int status = DDI_SUCCESS;

        qlge->mac_flags = QL_MAC_BRINGDOWN;
        if (qlge->sequence & ADAPTER_INIT) {
                /* stop forwarding external packets to driver */
                status = ql_sem_spinlock(qlge, SEM_RT_IDX_MASK);
                if (status)
                        return (status);
                (void) ql_stop_routing(qlge);
                ql_sem_unlock(qlge, SEM_RT_IDX_MASK);
                /*
                 * Set the flag for receive and transmit
                 * operations to cease
                 */
                for (i = 0; i < qlge->tx_ring_count; i++) {
                        mutex_enter(&qlge->tx_ring[i].tx_lock);
                        qlge->tx_ring[i].mac_flags = QL_MAC_STOPPED;
                        mutex_exit(&qlge->tx_ring[i].tx_lock);
                }

                for (i = 0; i < qlge->rx_ring_count; i++) {
                        mutex_enter(&qlge->rx_ring[i].rx_lock);
                        qlge->rx_ring[i].mac_flags = QL_MAC_STOPPED;
                        mutex_exit(&qlge->rx_ring[i].rx_lock);
                }

                /*
                 * Need interrupts to be running while the transmit
                 * completions are cleared. Wait for the packets
                 * queued to the chip to be sent out
                 */
                (void) ql_wait_tx_quiesce(qlge);
                /* Interrupts not needed from now */
                ql_disable_all_completion_interrupts(qlge);

                mutex_enter(&qlge->hw_mutex);
                /* Disable Global interrupt */
                ql_disable_global_interrupt(qlge);
                mutex_exit(&qlge->hw_mutex);

                /* Wait for all the indicated packets to come back */
                status = ql_wait_rx_complete(qlge);

                mutex_enter(&qlge->hw_mutex);
                /* Reset adapter */
                (void) ql_asic_reset(qlge);
                /*
                 * Unbind all tx dma handles to prevent pending tx descriptors'
                 * dma handles from being re-used.
                 */
                for (i = 0; i < qlge->tx_ring_count; i++) {
                        ql_unbind_pending_tx_dma_handle(&qlge->tx_ring[i]);
                }

                qlge->sequence &= ~ADAPTER_INIT;

                mutex_exit(&qlge->hw_mutex);
        }
        return (status);
}

/*
 * ql_detach
 * Used to remove all the states associated with a given
 * instances of a device node prior to the removal of that
 * instance from the system.
 */
static int
ql_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
        qlge_t *qlge;
        int rval;

        rval = DDI_SUCCESS;

        switch (cmd) {
        case DDI_DETACH:

                if ((qlge = QL_GET_DEV(dip)) == NULL)
                        return (DDI_FAILURE);
                rval = ql_bringdown_adapter(qlge);
                if (rval != DDI_SUCCESS)
                        break;

                qlge->mac_flags = QL_MAC_DETACH;

                /* free memory resources */
                if (qlge->sequence & INIT_MEMORY_ALLOC) {
                        ql_free_mem_resources(qlge);
                        qlge->sequence &= ~INIT_MEMORY_ALLOC;
                }
                ql_free_resources(qlge);

                break;

        case DDI_SUSPEND:
                if ((qlge = QL_GET_DEV(dip)) == NULL)
                        return (DDI_FAILURE);

                mutex_enter(&qlge->gen_mutex);
                if ((qlge->mac_flags == QL_MAC_ATTACHED) ||
                    (qlge->mac_flags == QL_MAC_STARTED)) {
                        (void) ql_do_stop(qlge);
                }
                qlge->mac_flags = QL_MAC_SUSPENDED;
                mutex_exit(&qlge->gen_mutex);

                break;
        default:
                rval = DDI_FAILURE;
                break;
        }

        return (rval);
}

/*
 * quiesce(9E) entry point.
 *
 * This function is called when the system is single-threaded at high
 * PIL with preemption disabled. Therefore, this function must not be
 * blocked.
 *
 * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
 */
int
ql_quiesce(dev_info_t *dip)
{
        qlge_t *qlge;
        int i;

        if ((qlge = QL_GET_DEV(dip)) == NULL)
                return (DDI_FAILURE);

        if (CFG_IST(qlge, CFG_CHIP_8100)) {
                /* stop forwarding external packets to driver */
                (void) ql_sem_spinlock(qlge, SEM_RT_IDX_MASK);
                (void) ql_stop_routing(qlge);
                ql_sem_unlock(qlge, SEM_RT_IDX_MASK);
                /* Stop all the request queues */
                for (i = 0; i < qlge->tx_ring_count; i++) {
                        if (qlge->tx_ring[i].valid_db_reg) {
                                ql_write_doorbell_reg(qlge,
                                    qlge->tx_ring[i].valid_db_reg, 0);
                        }
                }
                qlge_delay(QL_ONE_SEC_DELAY/4);
                /* Interrupts not needed from now */
                /* Disable MPI interrupt */
                ql_write_reg(qlge, REG_INTERRUPT_MASK,
                    (INTR_MASK_PI << 16));
                ql_disable_global_interrupt(qlge);

                /* Disable all the rx completion queues */
                for (i = 0; i < qlge->rx_ring_count; i++) {
                        if (qlge->rx_ring[i].valid_db_reg) {
                                ql_write_doorbell_reg(qlge,
                                    qlge->rx_ring[i].valid_db_reg, 0);
                        }
                }
                qlge_delay(QL_ONE_SEC_DELAY/4);
                qlge->mac_flags = QL_MAC_STOPPED;
                /* Reset adapter */
                (void) ql_asic_reset(qlge);
                qlge_delay(100);
        }

        return (DDI_SUCCESS);
}

QL_STREAM_OPS(ql_ops, ql_attach, ql_detach);

/*
 * Loadable Driver Interface Structures.
 * Declare and initialize the module configuration section...
 */
static struct modldrv modldrv = {
        &mod_driverops,         /* type of module: driver */
        version,                /* name of module */
        &ql_ops                 /* driver dev_ops */
};

static struct modlinkage modlinkage = {
        MODREV_1,       &modldrv,       NULL
};

/*
 * Loadable Module Routines
 */

/*
 * _init
 * Initializes a loadable module. It is called before any other
 * routine in a loadable module.
 */
int
_init(void)
{
        int rval;

        mac_init_ops(&ql_ops, ADAPTER_NAME);
        rval = mod_install(&modlinkage);
        if (rval != DDI_SUCCESS) {
                mac_fini_ops(&ql_ops);
                cmn_err(CE_WARN, "?Unable to install/attach driver '%s'",
                    ADAPTER_NAME);
        }

        return (rval);
}

/*
 * _fini
 * Prepares a module for unloading. It is called when the system
 * wants to unload a module. If the module determines that it can
 * be unloaded, then _fini() returns the value returned by
 * mod_remove(). Upon successful return from _fini() no other
 * routine in the module will be called before _init() is called.
 */
int
_fini(void)
{
        int rval;

        rval = mod_remove(&modlinkage);
        if (rval == DDI_SUCCESS) {
                mac_fini_ops(&ql_ops);
        }

        return (rval);
}

/*
 * _info
 * Returns information about loadable module.
 */
int
_info(struct modinfo *modinfop)
{
        return (mod_info(&modlinkage, modinfop));
}