1227569Sphilip/*-
2301388Sarybchik * Copyright (c) 2010-2016 Solarflare Communications Inc.
3227569Sphilip * All rights reserved.
4227569Sphilip *
5227569Sphilip * This software was developed in part by Philip Paeps under contract for
6227569Sphilip * Solarflare Communications, Inc.
7227569Sphilip *
8227569Sphilip * Redistribution and use in source and binary forms, with or without
9284555Sarybchik * modification, are permitted provided that the following conditions are met:
10227569Sphilip *
11284555Sarybchik * 1. Redistributions of source code must retain the above copyright notice,
12284555Sarybchik *    this list of conditions and the following disclaimer.
13284555Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice,
14284555Sarybchik *    this list of conditions and the following disclaimer in the documentation
15284555Sarybchik *    and/or other materials provided with the distribution.
16284555Sarybchik *
17284555Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18284555Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19284555Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20284555Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21284555Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22284555Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23284555Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24284555Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25284555Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26284555Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27284555Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28284555Sarybchik *
29284555Sarybchik * The views and conclusions contained in the software and documentation are
30284555Sarybchik * those of the authors and should not be interpreted as representing official
31284555Sarybchik * policies, either expressed or implied, of the FreeBSD Project.
32227569Sphilip */
33227569Sphilip
34227569Sphilip#include <sys/cdefs.h>
35227569Sphilip__FBSDID("$FreeBSD$");
36227569Sphilip
37227569Sphilip#include <sys/param.h>
38227569Sphilip#include <sys/bus.h>
39227569Sphilip#include <sys/rman.h>
40227569Sphilip#include <sys/syslog.h>
41227569Sphilip
42227569Sphilip#include <machine/bus.h>
43227569Sphilip#include <machine/resource.h>
44227569Sphilip
45227569Sphilip#include <dev/pci/pcireg.h>
46227569Sphilip#include <dev/pci/pcivar.h>
47227569Sphilip
48227569Sphilip#include "common/efx.h"
49227569Sphilip
50227569Sphilip#include "sfxge.h"
51227569Sphilip
52227569Sphilipstatic int
53227569Sphilipsfxge_intr_line_filter(void *arg)
54227569Sphilip{
55227569Sphilip	struct sfxge_evq *evq;
56227569Sphilip	struct sfxge_softc *sc;
57227569Sphilip	efx_nic_t *enp;
58227569Sphilip	struct sfxge_intr *intr;
59227569Sphilip	boolean_t fatal;
60227569Sphilip	uint32_t qmask;
61227569Sphilip
62227569Sphilip	evq = (struct sfxge_evq *)arg;
63227569Sphilip	sc = evq->sc;
64227569Sphilip	enp = sc->enp;
65227569Sphilip	intr = &sc->intr;
66227569Sphilip
67227569Sphilip	KASSERT(intr != NULL, ("intr == NULL"));
68227569Sphilip	KASSERT(intr->type == EFX_INTR_LINE,
69227569Sphilip	    ("intr->type != EFX_INTR_LINE"));
70227569Sphilip
71227700Sphilip	if (intr->state != SFXGE_INTR_STARTED)
72280501Sarybchik		return (FILTER_STRAY);
73227569Sphilip
74227569Sphilip	(void)efx_intr_status_line(enp, &fatal, &qmask);
75227569Sphilip
76227569Sphilip	if (fatal) {
77227569Sphilip		(void) efx_intr_disable(enp);
78227569Sphilip		(void) efx_intr_fatal(enp);
79280501Sarybchik		return (FILTER_HANDLED);
80227569Sphilip	}
81227569Sphilip
82227569Sphilip	if (qmask != 0) {
83227569Sphilip		intr->zero_count = 0;
84280501Sarybchik		return (FILTER_SCHEDULE_THREAD);
85227569Sphilip	}
86227569Sphilip
87227569Sphilip	/* SF bug 15783: If the function is not asserting its IRQ and
88227569Sphilip	 * we read the queue mask on the cycle before a flag is added
89227569Sphilip	 * to the mask, this inhibits the function from asserting the
90227569Sphilip	 * IRQ even though we don't see the flag set.  To work around
91227569Sphilip	 * this, we must re-prime all event queues and report the IRQ
92227569Sphilip	 * as handled when we see a mask of zero.  To allow for shared
93227569Sphilip	 * IRQs, we don't repeat this if we see a mask of zero twice
94227569Sphilip	 * or more in a row.
95227569Sphilip	 */
96227569Sphilip	if (intr->zero_count++ == 0) {
97227569Sphilip		if (evq->init_state == SFXGE_EVQ_STARTED) {
98227569Sphilip			if (efx_ev_qpending(evq->common, evq->read_ptr))
99280501Sarybchik				return (FILTER_SCHEDULE_THREAD);
100227569Sphilip			efx_ev_qprime(evq->common, evq->read_ptr);
101280501Sarybchik			return (FILTER_HANDLED);
102227569Sphilip		}
103227569Sphilip	}
104227569Sphilip
105280501Sarybchik	return (FILTER_STRAY);
106227569Sphilip}
107227569Sphilip
108227569Sphilipstatic void
109227569Sphilipsfxge_intr_line(void *arg)
110227569Sphilip{
111227569Sphilip	struct sfxge_evq *evq = arg;
112227569Sphilip
113280508Sarybchik	(void)sfxge_ev_qpoll(evq);
114227569Sphilip}
115227569Sphilip
116227569Sphilipstatic void
117227569Sphilipsfxge_intr_message(void *arg)
118227569Sphilip{
119227569Sphilip	struct sfxge_evq *evq;
120227569Sphilip	struct sfxge_softc *sc;
121227569Sphilip	efx_nic_t *enp;
122227569Sphilip	struct sfxge_intr *intr;
123227569Sphilip	unsigned int index;
124227569Sphilip	boolean_t fatal;
125227569Sphilip
126227569Sphilip	evq = (struct sfxge_evq *)arg;
127227569Sphilip	sc = evq->sc;
128227569Sphilip	enp = sc->enp;
129227569Sphilip	intr = &sc->intr;
130227569Sphilip	index = evq->index;
131227569Sphilip
132227569Sphilip	KASSERT(intr != NULL, ("intr == NULL"));
133227569Sphilip	KASSERT(intr->type == EFX_INTR_MESSAGE,
134227569Sphilip	    ("intr->type != EFX_INTR_MESSAGE"));
135227569Sphilip
136280596Sarybchik	if (__predict_false(intr->state != SFXGE_INTR_STARTED))
137227569Sphilip		return;
138227569Sphilip
139227569Sphilip	(void)efx_intr_status_message(enp, index, &fatal);
140227569Sphilip
141227569Sphilip	if (fatal) {
142227569Sphilip		(void)efx_intr_disable(enp);
143227569Sphilip		(void)efx_intr_fatal(enp);
144227569Sphilip		return;
145227569Sphilip	}
146227569Sphilip
147280508Sarybchik	(void)sfxge_ev_qpoll(evq);
148227569Sphilip}
149227569Sphilip
150227569Sphilipstatic int
151227569Sphilipsfxge_intr_bus_enable(struct sfxge_softc *sc)
152227569Sphilip{
153227569Sphilip	struct sfxge_intr *intr;
154227569Sphilip	struct sfxge_intr_hdl *table;
155227569Sphilip	driver_filter_t *filter;
156227569Sphilip	driver_intr_t *handler;
157227569Sphilip	int index;
158227569Sphilip	int err;
159227569Sphilip
160227569Sphilip	intr = &sc->intr;
161227569Sphilip	table = intr->table;
162227569Sphilip
163227569Sphilip	switch (intr->type) {
164227569Sphilip	case EFX_INTR_MESSAGE:
165227569Sphilip		filter = NULL; /* not shared */
166227569Sphilip		handler = sfxge_intr_message;
167227569Sphilip		break;
168227569Sphilip
169227569Sphilip	case EFX_INTR_LINE:
170227569Sphilip		filter = sfxge_intr_line_filter;
171227569Sphilip		handler = sfxge_intr_line;
172227569Sphilip		break;
173227569Sphilip
174227569Sphilip	default:
175227569Sphilip		KASSERT(0, ("Invalid interrupt type"));
176280501Sarybchik		return (EINVAL);
177227569Sphilip	}
178227569Sphilip
179227569Sphilip	/* Try to add the handlers */
180227569Sphilip	for (index = 0; index < intr->n_alloc; index++) {
181227569Sphilip		if ((err = bus_setup_intr(sc->dev, table[index].eih_res,
182227569Sphilip			    INTR_MPSAFE|INTR_TYPE_NET, filter, handler,
183227569Sphilip			    sc->evq[index], &table[index].eih_tag)) != 0) {
184227569Sphilip			goto fail;
185227569Sphilip		}
186227569Sphilip#ifdef SFXGE_HAVE_DESCRIBE_INTR
187227569Sphilip		if (intr->n_alloc > 1)
188227569Sphilip			bus_describe_intr(sc->dev, table[index].eih_res,
189227569Sphilip			    table[index].eih_tag, "%d", index);
190227569Sphilip#endif
191227569Sphilip		bus_bind_intr(sc->dev, table[index].eih_res, index);
192227569Sphilip
193227569Sphilip	}
194227569Sphilip
195227569Sphilip	return (0);
196227569Sphilip
197227569Sphilipfail:
198227569Sphilip	/* Remove remaining handlers */
199227569Sphilip	while (--index >= 0)
200227569Sphilip		bus_teardown_intr(sc->dev, table[index].eih_res,
201227569Sphilip		    table[index].eih_tag);
202227569Sphilip
203227569Sphilip	return (err);
204227569Sphilip}
205227569Sphilip
206227569Sphilipstatic void
207227569Sphilipsfxge_intr_bus_disable(struct sfxge_softc *sc)
208227569Sphilip{
209227569Sphilip	struct sfxge_intr *intr;
210227569Sphilip	struct sfxge_intr_hdl *table;
211227569Sphilip	int i;
212227569Sphilip
213227569Sphilip	intr = &sc->intr;
214227569Sphilip	table = intr->table;
215227569Sphilip
216227569Sphilip	/* Remove all handlers */
217227569Sphilip	for (i = 0; i < intr->n_alloc; i++)
218227569Sphilip		bus_teardown_intr(sc->dev, table[i].eih_res,
219227569Sphilip		    table[i].eih_tag);
220227569Sphilip}
221227569Sphilip
222227569Sphilipstatic int
223227569Sphilipsfxge_intr_alloc(struct sfxge_softc *sc, int count)
224227569Sphilip{
225227569Sphilip	device_t dev;
226227569Sphilip	struct sfxge_intr_hdl *table;
227227569Sphilip	struct sfxge_intr *intr;
228227569Sphilip	struct resource *res;
229227569Sphilip	int rid;
230227569Sphilip	int error;
231227569Sphilip	int i;
232227569Sphilip
233227569Sphilip	dev = sc->dev;
234227569Sphilip	intr = &sc->intr;
235227569Sphilip	error = 0;
236227569Sphilip
237227569Sphilip	table = malloc(count * sizeof(struct sfxge_intr_hdl),
238227569Sphilip	    M_SFXGE, M_WAITOK);
239227569Sphilip	intr->table = table;
240227569Sphilip
241227569Sphilip	for (i = 0; i < count; i++) {
242227569Sphilip		rid = i + 1;
243227569Sphilip		res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
244227569Sphilip		    RF_SHAREABLE | RF_ACTIVE);
245227569Sphilip		if (res == NULL) {
246227569Sphilip			device_printf(dev, "Couldn't allocate interrupts for "
247227569Sphilip			    "message %d\n", rid);
248227569Sphilip			error = ENOMEM;
249227569Sphilip			break;
250227569Sphilip		}
251227569Sphilip		table[i].eih_rid = rid;
252227569Sphilip		table[i].eih_res = res;
253227569Sphilip	}
254227569Sphilip
255280501Sarybchik	if (error != 0) {
256227569Sphilip		count = i - 1;
257227569Sphilip		for (i = 0; i < count; i++)
258227569Sphilip			bus_release_resource(dev, SYS_RES_IRQ,
259227569Sphilip			    table[i].eih_rid, table[i].eih_res);
260227569Sphilip	}
261227569Sphilip
262227569Sphilip	return (error);
263227569Sphilip}
264227569Sphilip
265227569Sphilipstatic void
266227569Sphilipsfxge_intr_teardown_msix(struct sfxge_softc *sc)
267227569Sphilip{
268227569Sphilip	device_t dev;
269227569Sphilip	struct resource *resp;
270227569Sphilip	int rid;
271227569Sphilip
272227569Sphilip	dev = sc->dev;
273227569Sphilip	resp = sc->intr.msix_res;
274227569Sphilip
275227569Sphilip	rid = rman_get_rid(resp);
276227569Sphilip	bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
277227569Sphilip}
278227569Sphilip
279227569Sphilipstatic int
280227569Sphilipsfxge_intr_setup_msix(struct sfxge_softc *sc)
281227569Sphilip{
282227569Sphilip	struct sfxge_intr *intr;
283227569Sphilip	struct resource *resp;
284227569Sphilip	device_t dev;
285227569Sphilip	int count;
286227569Sphilip	int rid;
287227569Sphilip
288227569Sphilip	dev = sc->dev;
289227569Sphilip	intr = &sc->intr;
290227569Sphilip
291227569Sphilip	/* Check if MSI-X is available. */
292227569Sphilip	count = pci_msix_count(dev);
293227569Sphilip	if (count == 0)
294227569Sphilip		return (EINVAL);
295227569Sphilip
296284555Sarybchik	/* Do not try to allocate more than already estimated EVQ maximum */
297284555Sarybchik	KASSERT(sc->evq_max > 0, ("evq_max is zero"));
298284555Sarybchik	count = MIN(count, sc->evq_max);
299227569Sphilip
300227569Sphilip	rid = PCIR_BAR(4);
301227569Sphilip	resp = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
302227569Sphilip	if (resp == NULL)
303227569Sphilip		return (ENOMEM);
304227569Sphilip
305227569Sphilip	if (pci_alloc_msix(dev, &count) != 0) {
306227569Sphilip		bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
307227569Sphilip		return (ENOMEM);
308227569Sphilip	}
309227569Sphilip
310227569Sphilip	/* Allocate interrupt handlers. */
311227569Sphilip	if (sfxge_intr_alloc(sc, count) != 0) {
312227569Sphilip		bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
313227569Sphilip		pci_release_msi(dev);
314227569Sphilip		return (ENOMEM);
315227569Sphilip	}
316227569Sphilip
317227569Sphilip	intr->type = EFX_INTR_MESSAGE;
318227569Sphilip	intr->n_alloc = count;
319227569Sphilip	intr->msix_res = resp;
320227569Sphilip
321227569Sphilip	return (0);
322227569Sphilip}
323227569Sphilip
324227569Sphilipstatic int
325227569Sphilipsfxge_intr_setup_msi(struct sfxge_softc *sc)
326227569Sphilip{
327227569Sphilip	struct sfxge_intr_hdl *table;
328227569Sphilip	struct sfxge_intr *intr;
329227569Sphilip	device_t dev;
330227569Sphilip	int count;
331227569Sphilip	int error;
332227569Sphilip
333227569Sphilip	dev = sc->dev;
334227569Sphilip	intr = &sc->intr;
335227569Sphilip	table = intr->table;
336227569Sphilip
337227569Sphilip	/*
338227569Sphilip	 * Check if MSI is available.  All messages must be written to
339227569Sphilip	 * the same address and on x86 this means the IRQs have the
340227569Sphilip	 * same CPU affinity.  So we only ever allocate 1.
341227569Sphilip	 */
342227569Sphilip	count = pci_msi_count(dev) ? 1 : 0;
343227569Sphilip	if (count == 0)
344227569Sphilip		return (EINVAL);
345227569Sphilip
346280501Sarybchik	if ((error = pci_alloc_msi(dev, &count)) != 0)
347227569Sphilip		return (ENOMEM);
348227569Sphilip
349227569Sphilip	/* Allocate interrupt handler. */
350227569Sphilip	if (sfxge_intr_alloc(sc, count) != 0) {
351227569Sphilip		pci_release_msi(dev);
352227569Sphilip		return (ENOMEM);
353227569Sphilip	}
354227569Sphilip
355227569Sphilip	intr->type = EFX_INTR_MESSAGE;
356227569Sphilip	intr->n_alloc = count;
357227569Sphilip
358227569Sphilip	return (0);
359227569Sphilip}
360227569Sphilip
361227569Sphilipstatic int
362227569Sphilipsfxge_intr_setup_fixed(struct sfxge_softc *sc)
363227569Sphilip{
364227569Sphilip	struct sfxge_intr_hdl *table;
365227569Sphilip	struct sfxge_intr *intr;
366227569Sphilip	struct resource *res;
367227569Sphilip	device_t dev;
368227569Sphilip	int rid;
369227569Sphilip
370227569Sphilip	dev = sc->dev;
371227569Sphilip	intr = &sc->intr;
372227569Sphilip
373227569Sphilip	rid = 0;
374227569Sphilip	res =  bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
375227569Sphilip	    RF_SHAREABLE | RF_ACTIVE);
376227569Sphilip	if (res == NULL)
377227569Sphilip		return (ENOMEM);
378227569Sphilip
379227569Sphilip	table = malloc(sizeof(struct sfxge_intr_hdl), M_SFXGE, M_WAITOK);
380227569Sphilip	table[0].eih_rid = rid;
381227569Sphilip	table[0].eih_res = res;
382227569Sphilip
383227569Sphilip	intr->type = EFX_INTR_LINE;
384227569Sphilip	intr->n_alloc = 1;
385227569Sphilip	intr->table = table;
386227569Sphilip
387227569Sphilip	return (0);
388227569Sphilip}
389227569Sphilip
390227569Sphilipstatic const char *const __sfxge_err[] = {
391227569Sphilip	"",
392227569Sphilip	"SRAM out-of-bounds",
393227569Sphilip	"Buffer ID out-of-bounds",
394227569Sphilip	"Internal memory parity",
395227569Sphilip	"Receive buffer ownership",
396227569Sphilip	"Transmit buffer ownership",
397227569Sphilip	"Receive descriptor ownership",
398227569Sphilip	"Transmit descriptor ownership",
399227569Sphilip	"Event queue ownership",
400227569Sphilip	"Event queue FIFO overflow",
401227569Sphilip	"Illegal address",
402227569Sphilip	"SRAM parity"
403227569Sphilip};
404227569Sphilip
405227569Sphilipvoid
406227569Sphilipsfxge_err(efsys_identifier_t *arg, unsigned int code, uint32_t dword0,
407284555Sarybchik	  uint32_t dword1)
408227569Sphilip{
409227569Sphilip	struct sfxge_softc *sc = (struct sfxge_softc *)arg;
410227569Sphilip	device_t dev = sc->dev;
411227569Sphilip
412227569Sphilip	log(LOG_WARNING, "[%s%d] FATAL ERROR: %s (0x%08x%08x)",
413227569Sphilip	    device_get_name(dev), device_get_unit(dev),
414227569Sphilip		__sfxge_err[code], dword1, dword0);
415227569Sphilip}
416227569Sphilip
417227569Sphilipvoid
418227569Sphilipsfxge_intr_stop(struct sfxge_softc *sc)
419227569Sphilip{
420227569Sphilip	struct sfxge_intr *intr;
421280501Sarybchik
422227569Sphilip	intr = &sc->intr;
423227569Sphilip
424227569Sphilip	KASSERT(intr->state == SFXGE_INTR_STARTED,
425227569Sphilip	    ("Interrupts not started"));
426227569Sphilip
427227569Sphilip	intr->state = SFXGE_INTR_INITIALIZED;
428227569Sphilip
429227569Sphilip	/* Disable interrupts at the NIC */
430227569Sphilip	efx_intr_disable(sc->enp);
431227569Sphilip
432227569Sphilip	/* Disable interrupts at the bus */
433227569Sphilip	sfxge_intr_bus_disable(sc);
434227569Sphilip
435227569Sphilip	/* Tear down common code interrupt bits. */
436227569Sphilip	efx_intr_fini(sc->enp);
437227569Sphilip}
438227569Sphilip
439227569Sphilipint
440227569Sphilipsfxge_intr_start(struct sfxge_softc *sc)
441227569Sphilip{
442227569Sphilip	struct sfxge_intr *intr;
443227569Sphilip	efsys_mem_t *esmp;
444227569Sphilip	int rc;
445227569Sphilip
446227569Sphilip	intr = &sc->intr;
447227569Sphilip	esmp = &intr->status;
448227569Sphilip
449227569Sphilip	KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
450227569Sphilip	    ("Interrupts not initialized"));
451227569Sphilip
452227569Sphilip	/* Zero the memory. */
453227569Sphilip	(void)memset(esmp->esm_base, 0, EFX_INTR_SIZE);
454227569Sphilip
455227569Sphilip	/* Initialize common code interrupt bits. */
456227569Sphilip	(void)efx_intr_init(sc->enp, intr->type, esmp);
457227569Sphilip
458227569Sphilip	/* Enable interrupts at the bus */
459227569Sphilip	if ((rc = sfxge_intr_bus_enable(sc)) != 0)
460227569Sphilip		goto fail;
461227569Sphilip
462227700Sphilip	intr->state = SFXGE_INTR_STARTED;
463227569Sphilip
464227569Sphilip	/* Enable interrupts at the NIC */
465227569Sphilip	efx_intr_enable(sc->enp);
466227569Sphilip
467227569Sphilip	return (0);
468227569Sphilip
469227569Sphilipfail:
470227569Sphilip	/* Tear down common code interrupt bits. */
471227569Sphilip	efx_intr_fini(sc->enp);
472227569Sphilip
473227569Sphilip	intr->state = SFXGE_INTR_INITIALIZED;
474227569Sphilip
475227569Sphilip	return (rc);
476227569Sphilip}
477227569Sphilip
478227569Sphilipvoid
479227569Sphilipsfxge_intr_fini(struct sfxge_softc *sc)
480227569Sphilip{
481227569Sphilip	struct sfxge_intr_hdl *table;
482227569Sphilip	struct sfxge_intr *intr;
483227569Sphilip	efsys_mem_t *esmp;
484227569Sphilip	device_t dev;
485227569Sphilip	int i;
486227569Sphilip
487227569Sphilip	dev = sc->dev;
488227569Sphilip	intr = &sc->intr;
489227569Sphilip	esmp = &intr->status;
490227569Sphilip	table = intr->table;
491227569Sphilip
492227569Sphilip	KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
493227569Sphilip	    ("intr->state != SFXGE_INTR_INITIALIZED"));
494227569Sphilip
495227569Sphilip	/* Free DMA memory. */
496227569Sphilip	sfxge_dma_free(esmp);
497227569Sphilip
498227569Sphilip	/* Free interrupt handles. */
499227569Sphilip	for (i = 0; i < intr->n_alloc; i++)
500227569Sphilip		bus_release_resource(dev, SYS_RES_IRQ,
501227569Sphilip		    table[i].eih_rid, table[i].eih_res);
502227569Sphilip
503227569Sphilip	if (table[0].eih_rid != 0)
504227569Sphilip		pci_release_msi(dev);
505227569Sphilip
506227569Sphilip	if (intr->msix_res != NULL)
507227569Sphilip		sfxge_intr_teardown_msix(sc);
508227569Sphilip
509227569Sphilip	/* Free the handle table */
510227569Sphilip	free(table, M_SFXGE);
511227569Sphilip	intr->table = NULL;
512227569Sphilip	intr->n_alloc = 0;
513227569Sphilip
514227569Sphilip	/* Clear the interrupt type */
515227569Sphilip	intr->type = EFX_INTR_INVALID;
516227569Sphilip
517227569Sphilip	intr->state = SFXGE_INTR_UNINITIALIZED;
518227569Sphilip}
519227569Sphilip
520227569Sphilipint
521227569Sphilipsfxge_intr_init(struct sfxge_softc *sc)
522227569Sphilip{
523227569Sphilip	device_t dev;
524227569Sphilip	struct sfxge_intr *intr;
525227569Sphilip	efsys_mem_t *esmp;
526227569Sphilip	int rc;
527227569Sphilip
528227569Sphilip	dev = sc->dev;
529227569Sphilip	intr = &sc->intr;
530227569Sphilip	esmp = &intr->status;
531227569Sphilip
532227569Sphilip	KASSERT(intr->state == SFXGE_INTR_UNINITIALIZED,
533227569Sphilip	    ("Interrupts already initialized"));
534227569Sphilip
535227569Sphilip	/* Try to setup MSI-X or MSI interrupts if available. */
536227569Sphilip	if ((rc = sfxge_intr_setup_msix(sc)) == 0)
537227569Sphilip		device_printf(dev, "Using MSI-X interrupts\n");
538227569Sphilip	else if ((rc = sfxge_intr_setup_msi(sc)) == 0)
539227569Sphilip		device_printf(dev, "Using MSI interrupts\n");
540227569Sphilip	else if ((rc = sfxge_intr_setup_fixed(sc)) == 0) {
541227569Sphilip		device_printf(dev, "Using fixed interrupts\n");
542227569Sphilip	} else {
543227569Sphilip		device_printf(dev, "Couldn't setup interrupts\n");
544227569Sphilip		return (ENOMEM);
545227569Sphilip	}
546227569Sphilip
547227569Sphilip	/* Set up DMA for interrupts. */
548227569Sphilip	if ((rc = sfxge_dma_alloc(sc, EFX_INTR_SIZE, esmp)) != 0)
549227569Sphilip		return (ENOMEM);
550227569Sphilip
551227569Sphilip	intr->state = SFXGE_INTR_INITIALIZED;
552227569Sphilip
553227569Sphilip	return (0);
554227569Sphilip}
555