--- /dev/null	Tue Mar 22 18:24:28 2016
+++ new/usr/src/uts/common/io/sfxge/sfxge_intr.c	Tue Mar 22 18:24:28 2016
@@ -0,0 +1,807 @@
+/*
+ * Copyright (c) 2008-2015 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/atomic.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/ethernet.h>
+#include <sys/pci.h>
+#include <sys/pcie.h>
+
+#include "sfxge.h"
+
+#include "efx.h"
+
+
+/* Interrupt table DMA attributes */
+static ddi_device_acc_attr_t sfxge_intr_devacc = {
+
+	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
+	DDI_NEVERSWAP_ACC,	/* devacc_attr_endian_flags */
+	DDI_STRICTORDER_ACC	/* devacc_attr_dataorder */
+};
+
+static ddi_dma_attr_t sfxge_intr_dma_attr = {
+	DMA_ATTR_V0,		/* dma_attr_version	*/
+	0,			/* dma_attr_addr_lo	*/
+	0xffffffffffffffffull,	/* dma_attr_addr_hi	*/
+	0xffffffffffffffffull,	/* dma_attr_count_max	*/
+	EFX_INTR_SIZE,		/* dma_attr_align	*/
+	0xffffffff,		/* dma_attr_burstsizes	*/
+	1,			/* dma_attr_minxfer	*/
+	0xffffffffffffffffull,	/* dma_attr_maxxfer	*/
+	0xffffffffffffffffull,	/* dma_attr_seg		*/
+	1,			/* dma_attr_sgllen	*/
+	1,			/* dma_attr_granular	*/
+	0			/* dma_attr_flags	*/
+};
+
+static unsigned int
+sfxge_intr_line(caddr_t arg1, caddr_t arg2)
+{
+	sfxge_t *sp = (void *)arg1;
+	efx_nic_t *enp = sp->s_enp;
+	sfxge_intr_t *sip = &(sp->s_intr);
+	unsigned int index;
+	boolean_t fatal;
+	uint32_t qmask;
+	int rc;
+
+	_NOTE(ARGUNUSED(arg2))
+
+	ASSERT3U(sip->si_type, ==, EFX_INTR_LINE);
+
+	if (sip->si_state != SFXGE_INTR_STARTED &&
+	    sip->si_state != SFXGE_INTR_TESTING) {
+		rc = DDI_INTR_UNCLAIMED;
+		goto done;
+	}
+
+	if (sip->si_state == SFXGE_INTR_TESTING) {
+		sip->si_mask |= 1;	/* only one interrupt */
+		rc = DDI_INTR_CLAIMED;
+		goto done;
+	}
+
+	efx_intr_status_line(enp, &fatal, &qmask);
+
+	if (fatal) {
+		sfxge_intr_fatal(sp);
+
+		rc = DDI_INTR_CLAIMED;
+		goto done;
+	}
+
+	if (qmask != 0) {
+		for (index = 0; index < EFX_INTR_NEVQS; index++) {
+			if (qmask & (1 << index))
+				(void) sfxge_ev_qpoll(sp, index);
+		}
+
+		sip->si_zero_count = 0;
+		sfxge_gld_rx_push(sp);
+		rc = DDI_INTR_CLAIMED;
+		goto done;
+	}
+
+	/*
+	 * bug15671/bug17203 workaround. Return CLAIMED for the first ISR=0
+	 * interrupt, and poll all evqs for work. For subsequent ISR=0
+	 * interrupts (the line must be shared in this case), just rearm the
+	 * event queues to ensure we don't miss an interrupt.
+	 */
+	if (sip->si_zero_count++ == 0) {
+		for (index = 0; index < EFX_INTR_NEVQS; index++) {
+			if (sp->s_sep[index] != NULL)
+				(void) sfxge_ev_qpoll(sp, index);
+		}
+
+		rc = DDI_INTR_CLAIMED;
+	} else {
+		for (index = 0; index < EFX_INTR_NEVQS; index++) {
+			if (sp->s_sep[index] != NULL)
+				(void) sfxge_ev_qprime(sp, index);
+		}
+
+		rc = DDI_INTR_UNCLAIMED;
+	}
+
+done:
+	return (rc);
+}
+
+static unsigned int
+sfxge_intr_message(caddr_t arg1, caddr_t arg2)
+{
+	sfxge_t *sp = (void *)arg1;
+	efx_nic_t *enp = sp->s_enp;
+	sfxge_intr_t *sip = &(sp->s_intr);
+	unsigned int index = (unsigned int)(uintptr_t)arg2;
+	boolean_t fatal;
+	int rc;
+
+	ASSERT3U(sip->si_type, ==, EFX_INTR_MESSAGE);
+
+	if (sip->si_state != SFXGE_INTR_STARTED &&
+	    sip->si_state != SFXGE_INTR_TESTING) {
+		rc = DDI_INTR_UNCLAIMED;
+		goto done;
+	}
+
+	if (sip->si_state == SFXGE_INTR_TESTING) {
+		uint64_t mask;
+
+		do {
+			mask = sip->si_mask;
+		} while (atomic_cas_64(&(sip->si_mask), mask,
+		    mask | (1 << index)) != mask);
+
+		rc = DDI_INTR_CLAIMED;
+		goto done;
+	}
+
+	efx_intr_status_message(enp, index, &fatal);
+
+	if (fatal) {
+		sfxge_intr_fatal(sp);
+
+		rc = DDI_INTR_CLAIMED;
+		goto done;
+	}
+
+	(void) sfxge_ev_qpoll(sp, index);
+
+	sfxge_gld_rx_push(sp);
+	rc = DDI_INTR_CLAIMED;
+
+done:
+	return (rc);
+}
+
+static int
+sfxge_intr_bus_enable(sfxge_t *sp)
+{
+	sfxge_intr_t *sip = &(sp->s_intr);
+	ddi_intr_handler_t *handler;
+	int add_index;
+	int en_index;
+	int err;
+	int rc;
+
+	/* Serialise all instances to avoid problems seen in bug31184. */
+	mutex_enter(&sfxge_global_lock);
+
+	switch (sip->si_type) {
+	case EFX_INTR_MESSAGE:
+		handler = sfxge_intr_message;
+		break;
+
+	case EFX_INTR_LINE:
+		handler = sfxge_intr_line;
+		break;
+
+	default:
+		dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+		    "bus_enable: unknown intr type (si_type=%d nalloc=%d)",
+		    sip->si_type, sip->si_nalloc);
+		ASSERT(B_FALSE);
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	/* Try to add the handlers */
+	for (add_index = 0; add_index < sip->si_nalloc; add_index++) {
+		unsigned int pri;
+
+		(void) ddi_intr_get_pri(sip->si_table[add_index], &pri);
+		DTRACE_PROBE2(pri, unsigned int, add_index, unsigned int, pri);
+
+		err = ddi_intr_add_handler(sip->si_table[add_index], handler,
+		    (caddr_t)sp, (caddr_t)(uintptr_t)add_index);
+		if (err != DDI_SUCCESS) {
+			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+			    "bus_enable: ddi_intr_add_handler failed"
+			    " err=%d (h=%p idx=%d nalloc=%d)",
+			    err, (void *)sip->si_table[add_index], add_index,
+			    sip->si_nalloc);
+
+			rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
+			goto fail2;
+		}
+	}
+
+	/* Get interrupt capabilities */
+	err = ddi_intr_get_cap(sip->si_table[0], &(sip->si_cap));
+	if (err != DDI_SUCCESS) {
+		dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+		    "bus_enable: ddi_intr_get_cap failed"
+		    " err=%d (h=%p idx=%d nalloc=%d)",
+		    err, (void *)sip->si_table[0], 0, sip->si_nalloc);
+
+		if (err == DDI_EINVAL)
+			rc = EINVAL;
+		else if (err == DDI_ENOTSUP)
+			rc = ENOTSUP;
+		else
+			rc = EFAULT;
+
+		goto fail3;
+	}
+
+	/* Enable interrupts at the bus  */
+	if (sip->si_cap & DDI_INTR_FLAG_BLOCK) {
+		en_index = 0; /* Silence gcc */
+		err = ddi_intr_block_enable(sip->si_table, sip->si_nalloc);
+		if (err != DDI_SUCCESS) {
+			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+			    "bus_enable: ddi_intr_block_enable failed"
+			    " err=%d (table=%p nalloc=%d)",
+			    err, (void *)sip->si_table, sip->si_nalloc);
+
+			rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
+			goto fail4;
+		}
+	} else {
+		for (en_index = 0; en_index < sip->si_nalloc; en_index++) {
+			err = ddi_intr_enable(sip->si_table[en_index]);
+			if (err != DDI_SUCCESS) {
+				dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+				    "bus_enable: ddi_intr_enable failed"
+				    " err=%d (h=%p idx=%d nalloc=%d)",
+				    err, (void *)sip->si_table[en_index],
+				    en_index, sip->si_nalloc);
+
+				rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
+				goto fail4;
+			}
+		}
+	}
+
+	mutex_exit(&sfxge_global_lock);
+	return (0);
+
+fail4:
+	DTRACE_PROBE(fail4);
+
+	/* Disable the enabled handlers */
+	if (!(sip->si_cap & DDI_INTR_FLAG_BLOCK)) {
+		while (--en_index >= 0) {
+			err = ddi_intr_disable(sip->si_table[en_index]);
+			if (err != DDI_SUCCESS) {
+				dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+				    "bus_enable: ddi_intr_disable"
+				    " failed err=%d (h=%p idx=%d nalloc=%d)",
+				    err, (void *)sip->si_table[en_index],
+				    en_index, sip->si_nalloc);
+			}
+		}
+	}
+
+fail3:
+	DTRACE_PROBE(fail3);
+
+	/* Remove all handlers */
+	add_index = sip->si_nalloc;
+
+fail2:
+	DTRACE_PROBE(fail2);
+
+	/* Remove remaining handlers */
+	while (--add_index >= 0) {
+		err = ddi_intr_remove_handler(sip->si_table[add_index]);
+		if (err != DDI_SUCCESS) {
+			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+			    "bus_enable: ddi_intr_remove_handler"
+			    " failed err=%d (h=%p idx=%d nalloc=%d)",
+			    err, (void *)sip->si_table[add_index], add_index,
+			    sip->si_nalloc);
+		}
+	}
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	mutex_exit(&sfxge_global_lock);
+	return (rc);
+}
+
+static void
+sfxge_intr_bus_disable(sfxge_t *sp)
+{
+	sfxge_intr_t *sip = &(sp->s_intr);
+	int index;
+	int err;
+
+	/* Serialise all instances to avoid problems seen in bug31184. */
+	mutex_enter(&sfxge_global_lock);
+
+	/* Disable interrupts at the bus */
+	if (sip->si_cap & DDI_INTR_FLAG_BLOCK) {
+		err = ddi_intr_block_disable(sip->si_table, sip->si_nalloc);
+		if (err != DDI_SUCCESS) {
+			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+			    "bus_disable: ddi_intr_block_disable"
+			    " failed err=%d (table=%p nalloc=%d)",
+			    err, (void *)sip->si_table, sip->si_nalloc);
+		}
+	} else {
+		index = sip->si_nalloc;
+		while (--index >= 0) {
+			err = ddi_intr_disable(sip->si_table[index]);
+			if (err != DDI_SUCCESS) {
+				dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+				    "bus_disable: ddi_intr_disable"
+				    " failed err=%d (h=%p idx=%d nalloc=%d)",
+				    err, (void *)sip->si_table[index], index,
+				    sip->si_nalloc);
+			}
+		}
+	}
+
+	sip->si_cap = 0;
+
+	/* Remove all handlers */
+	index = sip->si_nalloc;
+	while (--index >= 0) {
+		err = ddi_intr_remove_handler(sip->si_table[index]);
+		if (err != DDI_SUCCESS) {
+			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+			    "bus_disable: ddi_intr_remove_handler"
+			    " failed err=%d (h=%p idx=%d nalloc=%d)",
+			    err, (void *)sip->si_table[index], index,
+			    sip->si_nalloc);
+		}
+	}
+
+	mutex_exit(&sfxge_global_lock);
+}
+
+static int
+sfxge_intr_nic_enable(sfxge_t *sp)
+{
+	sfxge_intr_t *sip = &(sp->s_intr);
+	efsys_mem_t *esmp = &(sip->si_mem);
+	efx_nic_t *enp = sp->s_enp;
+	unsigned int index;
+	uint64_t mask;
+	unsigned int count;
+	int rc;
+
+	/* Zero the memory */
+	bzero(esmp->esm_base, EFX_INTR_SIZE);
+
+	/* Enable interrupts at the NIC */
+	if ((rc = efx_intr_init(enp, sip->si_type, esmp)) != 0)
+		goto fail1;
+
+	efx_intr_enable(enp);
+
+	/* FIXME FIXME FIXME */
+	if (sp->s_family == EFX_FAMILY_HUNTINGTON) {
+		/* Disable interrupt test until supported on Huntington. */
+		return (0);
+	}
+	/* FIXME FIXME FIXME */
+
+	/* Test the interrupts */
+	mask = 0;
+	for (index = 0; index < sip->si_nalloc; index++) {
+		mask |= (1 << index);
+
+		rc = efx_intr_trigger(enp, index);
+		ASSERT3U(rc, ==, 0);
+	}
+
+	/* Wait for the tests to complete */
+	count = 0;
+	do {
+		DTRACE_PROBE1(wait, unsigned int, count);
+
+		/* Spin for 1 ms */
+		drv_usecwait(1000);
+
+		/*
+		 * Check to see that all the test interrupts have been
+		 * processed.
+		 */
+		if ((mask & sip->si_mask) == mask)
+			goto done;
+
+	} while (++count < 20);
+
+	rc = ETIMEDOUT;
+	goto fail2;
+
+done:
+	return (0);
+
+fail2:
+	DTRACE_PROBE(fail2);
+
+	dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+	    "Interrupt test failed (mask=%"PRIx64" got=%"
+	    PRIx64"). NIC is disabled",
+	    mask, sip->si_mask);
+
+	DTRACE_PROBE2(int_test_fail, uint64_t, mask, uint64_t, sip->si_mask);
+
+	sip->si_mask = 0;
+
+	/* Disable interrupts at the NIC */
+	efx_intr_disable(enp);
+	efx_intr_fini(enp);
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	return (rc);
+}
+
+static void
+sfxge_intr_nic_disable(sfxge_t *sp)
+{
+	sfxge_intr_t *sip = &(sp->s_intr);
+	efx_nic_t *enp = sp->s_enp;
+
+	sip->si_mask = 0;
+
+	/* Disable interrupts at the NIC */
+	efx_intr_disable(enp);
+	efx_intr_fini(enp);
+}
+
+inline unsigned pow2_le(unsigned long n) {
+	unsigned int order = 1;
+	ASSERT3U(n, >, 0);
+	while ((1ul << order) <= n) ++order;
+	return (1ul << (order - 1));
+}
+
+int
+sfxge_intr_init(sfxge_t *sp)
+{
+	dev_info_t *dip = sp->s_dip;
+	sfxge_intr_t *sip = &(sp->s_intr);
+	efsys_mem_t *esmp = &(sip->si_mem);
+	sfxge_dma_buffer_attr_t dma_attr;
+	int err;
+	int rc;
+	int types;
+	int type;
+	int index;
+	unsigned int nalloc;
+	int navail;
+
+	SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
+
+	ASSERT3U(sip->si_state, ==, SFXGE_INTR_UNINITIALIZED);
+
+#ifdef __sparc
+	/* PSARC 2007/453 */
+	(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+	    "#msix-request", NULL, 0);
+#endif
+
+	/* Get the map of supported interrupt types */
+	err = ddi_intr_get_supported_types(dip, &types);
+	if (err != DDI_SUCCESS) {
+		dev_err(dip, CE_WARN, SFXGE_CMN_ERR
+		    "intr_init: ddi_intr_get_supported_types failed err=%d",
+		    err);
+
+		if (err == DDI_EINVAL)
+			rc = EINVAL;
+		else if (err == DDI_INTR_NOTFOUND)
+			rc = ENOENT;
+		else
+			rc = EFAULT;
+
+		goto fail1;
+	}
+
+	/* Choose most favourable type */
+	if (types & DDI_INTR_TYPE_MSIX) {
+		DTRACE_PROBE(msix);
+
+		type = DDI_INTR_TYPE_MSIX;
+		sip->si_type = EFX_INTR_MESSAGE;
+	} else {
+		DTRACE_PROBE(fixed);
+
+		ASSERT(types & DDI_INTR_TYPE_FIXED);
+
+		type = DDI_INTR_TYPE_FIXED;
+		sip->si_type = EFX_INTR_LINE;
+	}
+
+	/* Get the number of available interrupts */
+	navail = 0;
+	err = ddi_intr_get_navail(dip, type, &navail);
+	if (err != DDI_SUCCESS) {
+		dev_err(dip, CE_WARN, SFXGE_CMN_ERR
+		    "intr_init: ddi_intr_get_navail failed err=%d", err);
+
+		if (err == DDI_EINVAL)
+			rc = EINVAL;
+		else if (err == DDI_INTR_NOTFOUND)
+			rc = ENOENT;
+		else
+			rc = EFAULT;
+
+		goto fail2;
+	}
+
+	/* Double-check */
+	if (navail == 0) {
+		rc = ENOENT;
+		goto fail2;
+	}
+
+	/*
+	 * Allow greater number of MSI-X interrupts than CPUs.
+	 * This can be useful to prevent RX no desc drops; See task 32179.
+	 * Limit non MSI-X interrupts to a single instance.
+	 */
+	if (type != DDI_INTR_TYPE_MSIX)
+		navail = 1;
+	else
+		navail = min(navail, sfxge_rx_scale_prop_get(sp));
+
+	DTRACE_PROBE1(navail, unsigned int, navail);
+
+	/* Allocate a handle table */
+	sip->si_table_size = navail * sizeof (ddi_intr_handle_t);
+	sip->si_table = kmem_zalloc(sip->si_table_size, KM_SLEEP);
+
+	/*
+	 * Allocate interrupt handles.
+	 * Serialise all device instances to avoid problems seen in bug31184.
+	 */
+	mutex_enter(&sfxge_global_lock);
+
+	err = ddi_intr_alloc(dip, sip->si_table, type, 0,
+	    navail, &(sip->si_nalloc), DDI_INTR_ALLOC_NORMAL);
+
+	mutex_exit(&sfxge_global_lock);
+
+	if (err != DDI_SUCCESS) {
+		dev_err(dip, CE_WARN, SFXGE_CMN_ERR
+		    "intr_init: ddi_intr_alloc failed err=%d"
+		    " (navail=%d nalloc=%d)",
+		    err, navail, sip->si_nalloc);
+
+		if (err == DDI_EINVAL)
+			rc = EINVAL;
+		else if (err == DDI_EAGAIN)
+			rc = EAGAIN;
+		else if (err == DDI_INTR_NOTFOUND)
+			rc = ENOENT;
+		else
+			rc = EFAULT;
+
+		goto fail3;
+	}
+
+	/* Double-check */
+	if (sip->si_nalloc == 0) {
+		rc = ENOENT;
+		goto fail3;
+	}
+
+	/* Round down to a power of 2 */
+	nalloc = pow2_le(sip->si_nalloc);
+
+	/* Free off any excess handles */
+	mutex_enter(&sfxge_global_lock);
+
+	index = sip->si_nalloc;
+	while (--index >= nalloc) {
+		(void) ddi_intr_free(sip->si_table[index]);
+		sip->si_table[index] = NULL;
+	}
+
+	mutex_exit(&sfxge_global_lock);
+
+	sip->si_nalloc = nalloc;
+	DTRACE_PROBE1(nalloc, unsigned int, sip->si_nalloc);
+
+	dma_attr.sdba_dip	 = sp->s_dip;
+	dma_attr.sdba_dattrp	 = &sfxge_intr_dma_attr;
+	dma_attr.sdba_callback	 = DDI_DMA_SLEEP;
+	dma_attr.sdba_length	 = EFX_INTR_SIZE;
+	dma_attr.sdba_memflags	 = DDI_DMA_CONSISTENT;
+	dma_attr.sdba_devaccp	 = &sfxge_intr_devacc;
+	dma_attr.sdba_bindflags	 = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
+	dma_attr.sdba_maxcookies = 1;
+	dma_attr.sdba_zeroinit	 = B_TRUE;
+
+	if ((rc = sfxge_dma_buffer_create(esmp, &dma_attr)) != 0)
+		goto fail4;
+
+	/* Store the highest priority for convenience */
+	sip->si_intr_pri = 0;
+	for (index = 0; index < sip->si_nalloc; index++) {
+		uint_t pri;
+		if ((rc = ddi_intr_get_pri(sip->si_table[index], &pri)) !=  0)
+			goto fail5;
+		if (pri > sip->si_intr_pri)
+			sip->si_intr_pri = pri;
+	}
+
+	sip->si_state = SFXGE_INTR_INITIALIZED;
+	return (0);
+
+fail5:
+	DTRACE_PROBE(fail5);
+
+fail4:
+	DTRACE_PROBE(fail4);
+
+	/* Free interrupt handles */
+	mutex_exit(&sfxge_global_lock);
+
+	index = sip->si_nalloc;
+	while (--index >= 0) {
+		err = ddi_intr_free(sip->si_table[index]);
+		if (err != DDI_SUCCESS) {
+			dev_err(dip, CE_WARN, SFXGE_CMN_ERR
+			    "intr_init: ddi_intr_free failed err=%d"
+			    " (h=%p idx=%d nalloc=%d)",
+			    err, (void *)sip->si_table[index], index,
+			    sip->si_nalloc);
+		}
+		sip->si_table[index] = NULL;
+	}
+	sip->si_nalloc = 0;
+
+	mutex_exit(&sfxge_global_lock);
+
+fail3:
+	DTRACE_PROBE(fail3);
+
+	/* Free the handle table */
+	kmem_free(sip->si_table, sip->si_table_size);
+	sip->si_table = NULL;
+	sip->si_table_size = 0;
+
+fail2:
+	DTRACE_PROBE(fail2);
+
+	/* Clear the interrupt type */
+	sip->si_type = EFX_INTR_INVALID;
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
+
+	return (rc);
+}
+
+int
+sfxge_intr_start(sfxge_t *sp)
+{
+	sfxge_intr_t *sip = &(sp->s_intr);
+	int rc;
+
+	ASSERT3U(sip->si_state, ==, SFXGE_INTR_INITIALIZED);
+
+	/* Enable interrupts at the bus */
+	if ((rc = sfxge_intr_bus_enable(sp)) != 0)
+		goto fail1;
+
+	sip->si_state = SFXGE_INTR_TESTING;
+
+	/* Enable interrupts at the NIC */
+	if ((rc = sfxge_intr_nic_enable(sp)) != 0)
+		goto fail2;
+
+	sip->si_state = SFXGE_INTR_STARTED;
+
+	return (0);
+
+fail2:
+	DTRACE_PROBE(fail2);
+
+	/* Disable interrupts at the bus */
+	sfxge_intr_bus_disable(sp);
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	sip->si_state = SFXGE_INTR_INITIALIZED;
+
+	return (rc);
+}
+
+void
+sfxge_intr_stop(sfxge_t *sp)
+{
+	sfxge_intr_t *sip = &(sp->s_intr);
+
+	ASSERT3U(sip->si_state, ==, SFXGE_INTR_STARTED);
+
+	sip->si_state = SFXGE_INTR_INITIALIZED;
+
+	/* Disable interrupts at the NIC */
+	sfxge_intr_nic_disable(sp);
+
+	/* Disable interrupts at the bus */
+	sfxge_intr_bus_disable(sp);
+}
+
+void
+sfxge_intr_fini(sfxge_t *sp)
+{
+	sfxge_intr_t *sip = &(sp->s_intr);
+	efsys_mem_t *esmp = &(sip->si_mem);
+	int index;
+	int err;
+
+	ASSERT3U(sip->si_state, ==, SFXGE_INTR_INITIALIZED);
+
+	sip->si_state = SFXGE_INTR_UNINITIALIZED;
+
+	/* Tear down dma setup */
+	sfxge_dma_buffer_destroy(esmp);
+
+
+	/* Free interrupt handles */
+	mutex_enter(&sfxge_global_lock);
+
+	index = sip->si_nalloc;
+	while (--index >= 0) {
+		err = ddi_intr_free(sip->si_table[index]);
+		if (err != DDI_SUCCESS) {
+			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
+			    "intr_fini: ddi_intr_free failed err=%d"
+			    " (h=%p idx=%d nalloc=%d)",
+			    err, (void *)sip->si_table[index],
+			    index, sip->si_nalloc);
+		}
+		sip->si_table[index] = NULL;
+	}
+	sip->si_nalloc = 0;
+
+	mutex_exit(&sfxge_global_lock);
+
+	/* Free the handle table */
+	kmem_free(sip->si_table, sip->si_table_size);
+	sip->si_table = NULL;
+	sip->si_table_size = 0;
+
+	/* Clear the interrupt type */
+	sip->si_type = EFX_INTR_INVALID;
+
+	SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
+}
