view usr/src/uts/common/io/fibre-channel/fca/qlge/qlge.c @ 20600:a9ce21b31e86

10081 smatch indenting fixes for usr/src/uts Reviewed by: Toomas Soome <tsoome@me.com> Reviewed by: Peter Tribble <peter.tribble@gmail.com> Reviewed by: Andy Fiddaman <andy@omniosce.org> Approved by: Robert Mustacchi <rm@joyent.com>
author John Levon <john.levon@joyent.com>
date Mon, 14 Jan 2019 19:42:58 +0000
parents 7f829eb09eb7
children 7616bf36dcb7
line wrap: on
line source

/*
 * 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 != NULL) ||
	    (qlge->mac_flags == QL_MAC_DETACH)) {
		if (sbq_desc->free_buf == NULL)
			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 != NULL) ||
	    (qlge->mac_flags == QL_MAC_DETACH)) {
		if (lbq_desc->free_buf == NULL)
			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(k, length, initval)
register ub1 *k;	/* the key */
register ub4 length;	/* the length of the key */
register ub4 initval;	/* the previous hash, or an arbitrary value */
{
	register 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 (NULL);
	}

	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));
}