xref: /freebsd/sys/dev/sfxge/sfxge_intr.c (revision eedc7fd9e87e86d636304611a29e53cecb3a57a1)
1e948693eSPhilip Paeps /*-
2e948693eSPhilip Paeps  * Copyright (c) 2010-2011 Solarflare Communications, Inc.
3e948693eSPhilip Paeps  * All rights reserved.
4e948693eSPhilip Paeps  *
5e948693eSPhilip Paeps  * This software was developed in part by Philip Paeps under contract for
6e948693eSPhilip Paeps  * Solarflare Communications, Inc.
7e948693eSPhilip Paeps  *
8e948693eSPhilip Paeps  * Redistribution and use in source and binary forms, with or without
9e948693eSPhilip Paeps  * modification, are permitted provided that the following conditions
10e948693eSPhilip Paeps  * are met:
11e948693eSPhilip Paeps  * 1. Redistributions of source code must retain the above copyright
12e948693eSPhilip Paeps  *    notice, this list of conditions and the following disclaimer.
13e948693eSPhilip Paeps  * 2. Redistributions in binary form must reproduce the above copyright
14e948693eSPhilip Paeps  *    notice, this list of conditions and the following disclaimer in the
15e948693eSPhilip Paeps  *    documentation and/or other materials provided with the distribution.
16e948693eSPhilip Paeps  *
17e948693eSPhilip Paeps  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18e948693eSPhilip Paeps  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19e948693eSPhilip Paeps  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20e948693eSPhilip Paeps  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21e948693eSPhilip Paeps  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e948693eSPhilip Paeps  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23e948693eSPhilip Paeps  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24e948693eSPhilip Paeps  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25e948693eSPhilip Paeps  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26e948693eSPhilip Paeps  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27e948693eSPhilip Paeps  * SUCH DAMAGE.
28e948693eSPhilip Paeps  */
29e948693eSPhilip Paeps 
30e948693eSPhilip Paeps #include <sys/cdefs.h>
31e948693eSPhilip Paeps __FBSDID("$FreeBSD$");
32e948693eSPhilip Paeps 
33e948693eSPhilip Paeps #include <sys/param.h>
34e948693eSPhilip Paeps #include <sys/bus.h>
35*eedc7fd9SGleb Smirnoff #include <sys/kernel.h>
36*eedc7fd9SGleb Smirnoff #include <sys/malloc.h>
37*eedc7fd9SGleb Smirnoff #include <sys/queue.h>
38e948693eSPhilip Paeps #include <sys/rman.h>
39e948693eSPhilip Paeps #include <sys/smp.h>
40e948693eSPhilip Paeps #include <sys/syslog.h>
41*eedc7fd9SGleb Smirnoff #include <sys/taskqueue.h>
42e948693eSPhilip Paeps 
43e948693eSPhilip Paeps #include <machine/bus.h>
44e948693eSPhilip Paeps #include <machine/resource.h>
45e948693eSPhilip Paeps 
46e948693eSPhilip Paeps #include <dev/pci/pcireg.h>
47e948693eSPhilip Paeps #include <dev/pci/pcivar.h>
48e948693eSPhilip Paeps 
49e948693eSPhilip Paeps #include "common/efx.h"
50e948693eSPhilip Paeps 
51e948693eSPhilip Paeps #include "sfxge.h"
52e948693eSPhilip Paeps 
53e948693eSPhilip Paeps static int
54e948693eSPhilip Paeps sfxge_intr_line_filter(void *arg)
55e948693eSPhilip Paeps {
56e948693eSPhilip Paeps 	struct sfxge_evq *evq;
57e948693eSPhilip Paeps 	struct sfxge_softc *sc;
58e948693eSPhilip Paeps 	efx_nic_t *enp;
59e948693eSPhilip Paeps 	struct sfxge_intr *intr;
60e948693eSPhilip Paeps 	boolean_t fatal;
61e948693eSPhilip Paeps 	uint32_t qmask;
62e948693eSPhilip Paeps 
63e948693eSPhilip Paeps 	evq = (struct sfxge_evq *)arg;
64e948693eSPhilip Paeps 	sc = evq->sc;
65e948693eSPhilip Paeps 	enp = sc->enp;
66e948693eSPhilip Paeps 	intr = &sc->intr;
67e948693eSPhilip Paeps 
68e948693eSPhilip Paeps 	KASSERT(intr != NULL, ("intr == NULL"));
69e948693eSPhilip Paeps 	KASSERT(intr->type == EFX_INTR_LINE,
70e948693eSPhilip Paeps 	    ("intr->type != EFX_INTR_LINE"));
71e948693eSPhilip Paeps 
7276a86938SPhilip Paeps 	if (intr->state != SFXGE_INTR_STARTED)
73e948693eSPhilip Paeps 		return FILTER_STRAY;
74e948693eSPhilip Paeps 
75e948693eSPhilip Paeps 	(void)efx_intr_status_line(enp, &fatal, &qmask);
76e948693eSPhilip Paeps 
77e948693eSPhilip Paeps 	if (fatal) {
78e948693eSPhilip Paeps 		(void) efx_intr_disable(enp);
79e948693eSPhilip Paeps 		(void) efx_intr_fatal(enp);
80e948693eSPhilip Paeps 		return FILTER_HANDLED;
81e948693eSPhilip Paeps 	}
82e948693eSPhilip Paeps 
83e948693eSPhilip Paeps 	if (qmask != 0) {
84e948693eSPhilip Paeps 		intr->zero_count = 0;
85e948693eSPhilip Paeps 		return FILTER_SCHEDULE_THREAD;
86e948693eSPhilip Paeps 	}
87e948693eSPhilip Paeps 
88e948693eSPhilip Paeps 	/* SF bug 15783: If the function is not asserting its IRQ and
89e948693eSPhilip Paeps 	 * we read the queue mask on the cycle before a flag is added
90e948693eSPhilip Paeps 	 * to the mask, this inhibits the function from asserting the
91e948693eSPhilip Paeps 	 * IRQ even though we don't see the flag set.  To work around
92e948693eSPhilip Paeps 	 * this, we must re-prime all event queues and report the IRQ
93e948693eSPhilip Paeps 	 * as handled when we see a mask of zero.  To allow for shared
94e948693eSPhilip Paeps 	 * IRQs, we don't repeat this if we see a mask of zero twice
95e948693eSPhilip Paeps 	 * or more in a row.
96e948693eSPhilip Paeps 	 */
97e948693eSPhilip Paeps 	if (intr->zero_count++ == 0) {
98e948693eSPhilip Paeps 		if (evq->init_state == SFXGE_EVQ_STARTED) {
99e948693eSPhilip Paeps 			if (efx_ev_qpending(evq->common, evq->read_ptr))
100e948693eSPhilip Paeps 				return FILTER_SCHEDULE_THREAD;
101e948693eSPhilip Paeps 			efx_ev_qprime(evq->common, evq->read_ptr);
102e948693eSPhilip Paeps 			return FILTER_HANDLED;
103e948693eSPhilip Paeps 		}
104e948693eSPhilip Paeps 	}
105e948693eSPhilip Paeps 
106e948693eSPhilip Paeps 	return FILTER_STRAY;
107e948693eSPhilip Paeps }
108e948693eSPhilip Paeps 
109e948693eSPhilip Paeps static void
110e948693eSPhilip Paeps sfxge_intr_line(void *arg)
111e948693eSPhilip Paeps {
112e948693eSPhilip Paeps 	struct sfxge_evq *evq = arg;
113e948693eSPhilip Paeps 	struct sfxge_softc *sc = evq->sc;
114e948693eSPhilip Paeps 
115e948693eSPhilip Paeps 	(void)sfxge_ev_qpoll(sc, 0);
116e948693eSPhilip Paeps }
117e948693eSPhilip Paeps 
118e948693eSPhilip Paeps static void
119e948693eSPhilip Paeps sfxge_intr_message(void *arg)
120e948693eSPhilip Paeps {
121e948693eSPhilip Paeps 	struct sfxge_evq *evq;
122e948693eSPhilip Paeps 	struct sfxge_softc *sc;
123e948693eSPhilip Paeps 	efx_nic_t *enp;
124e948693eSPhilip Paeps 	struct sfxge_intr *intr;
125e948693eSPhilip Paeps 	unsigned int index;
126e948693eSPhilip Paeps 	boolean_t fatal;
127e948693eSPhilip Paeps 
128e948693eSPhilip Paeps 	evq = (struct sfxge_evq *)arg;
129e948693eSPhilip Paeps 	sc = evq->sc;
130e948693eSPhilip Paeps 	enp = sc->enp;
131e948693eSPhilip Paeps 	intr = &sc->intr;
132e948693eSPhilip Paeps 	index = evq->index;
133e948693eSPhilip Paeps 
134e948693eSPhilip Paeps 	KASSERT(intr != NULL, ("intr == NULL"));
135e948693eSPhilip Paeps 	KASSERT(intr->type == EFX_INTR_MESSAGE,
136e948693eSPhilip Paeps 	    ("intr->type != EFX_INTR_MESSAGE"));
137e948693eSPhilip Paeps 
13876a86938SPhilip Paeps 	if (intr->state != SFXGE_INTR_STARTED)
139e948693eSPhilip Paeps 		return;
140e948693eSPhilip Paeps 
141e948693eSPhilip Paeps 	(void)efx_intr_status_message(enp, index, &fatal);
142e948693eSPhilip Paeps 
143e948693eSPhilip Paeps 	if (fatal) {
144e948693eSPhilip Paeps 		(void)efx_intr_disable(enp);
145e948693eSPhilip Paeps 		(void)efx_intr_fatal(enp);
146e948693eSPhilip Paeps 		return;
147e948693eSPhilip Paeps 	}
148e948693eSPhilip Paeps 
149e948693eSPhilip Paeps 	(void)sfxge_ev_qpoll(sc, index);
150e948693eSPhilip Paeps }
151e948693eSPhilip Paeps 
152e948693eSPhilip Paeps static int
153e948693eSPhilip Paeps sfxge_intr_bus_enable(struct sfxge_softc *sc)
154e948693eSPhilip Paeps {
155e948693eSPhilip Paeps 	struct sfxge_intr *intr;
156e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
157e948693eSPhilip Paeps 	driver_filter_t *filter;
158e948693eSPhilip Paeps 	driver_intr_t *handler;
159e948693eSPhilip Paeps 	int index;
160e948693eSPhilip Paeps 	int err;
161e948693eSPhilip Paeps 
162e948693eSPhilip Paeps 	intr = &sc->intr;
163e948693eSPhilip Paeps 	table = intr->table;
164e948693eSPhilip Paeps 
165e948693eSPhilip Paeps 	switch (intr->type) {
166e948693eSPhilip Paeps 	case EFX_INTR_MESSAGE:
167e948693eSPhilip Paeps 		filter = NULL; /* not shared */
168e948693eSPhilip Paeps 		handler = sfxge_intr_message;
169e948693eSPhilip Paeps 		break;
170e948693eSPhilip Paeps 
171e948693eSPhilip Paeps 	case EFX_INTR_LINE:
172e948693eSPhilip Paeps 		filter = sfxge_intr_line_filter;
173e948693eSPhilip Paeps 		handler = sfxge_intr_line;
174e948693eSPhilip Paeps 		break;
175e948693eSPhilip Paeps 
176e948693eSPhilip Paeps 	default:
177e948693eSPhilip Paeps 		KASSERT(0, ("Invalid interrupt type"));
178e948693eSPhilip Paeps 		return EINVAL;
179e948693eSPhilip Paeps 	}
180e948693eSPhilip Paeps 
181e948693eSPhilip Paeps 	/* Try to add the handlers */
182e948693eSPhilip Paeps 	for (index = 0; index < intr->n_alloc; index++) {
183e948693eSPhilip Paeps 		if ((err = bus_setup_intr(sc->dev, table[index].eih_res,
184e948693eSPhilip Paeps 			    INTR_MPSAFE|INTR_TYPE_NET, filter, handler,
185e948693eSPhilip Paeps 			    sc->evq[index], &table[index].eih_tag)) != 0) {
186e948693eSPhilip Paeps 			goto fail;
187e948693eSPhilip Paeps 		}
188e948693eSPhilip Paeps #ifdef SFXGE_HAVE_DESCRIBE_INTR
189e948693eSPhilip Paeps 		if (intr->n_alloc > 1)
190e948693eSPhilip Paeps 			bus_describe_intr(sc->dev, table[index].eih_res,
191e948693eSPhilip Paeps 			    table[index].eih_tag, "%d", index);
192e948693eSPhilip Paeps #endif
193e948693eSPhilip Paeps 		bus_bind_intr(sc->dev, table[index].eih_res, index);
194e948693eSPhilip Paeps 
195e948693eSPhilip Paeps 	}
196e948693eSPhilip Paeps 
197e948693eSPhilip Paeps 	return (0);
198e948693eSPhilip Paeps 
199e948693eSPhilip Paeps fail:
200e948693eSPhilip Paeps 	/* Remove remaining handlers */
201e948693eSPhilip Paeps 	while (--index >= 0)
202e948693eSPhilip Paeps 		bus_teardown_intr(sc->dev, table[index].eih_res,
203e948693eSPhilip Paeps 		    table[index].eih_tag);
204e948693eSPhilip Paeps 
205e948693eSPhilip Paeps 	return (err);
206e948693eSPhilip Paeps }
207e948693eSPhilip Paeps 
208e948693eSPhilip Paeps static void
209e948693eSPhilip Paeps sfxge_intr_bus_disable(struct sfxge_softc *sc)
210e948693eSPhilip Paeps {
211e948693eSPhilip Paeps 	struct sfxge_intr *intr;
212e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
213e948693eSPhilip Paeps 	int i;
214e948693eSPhilip Paeps 
215e948693eSPhilip Paeps 	intr = &sc->intr;
216e948693eSPhilip Paeps 	table = intr->table;
217e948693eSPhilip Paeps 
218e948693eSPhilip Paeps 	/* Remove all handlers */
219e948693eSPhilip Paeps 	for (i = 0; i < intr->n_alloc; i++)
220e948693eSPhilip Paeps 		bus_teardown_intr(sc->dev, table[i].eih_res,
221e948693eSPhilip Paeps 		    table[i].eih_tag);
222e948693eSPhilip Paeps }
223e948693eSPhilip Paeps 
224e948693eSPhilip Paeps static int
225e948693eSPhilip Paeps sfxge_intr_alloc(struct sfxge_softc *sc, int count)
226e948693eSPhilip Paeps {
227e948693eSPhilip Paeps 	device_t dev;
228e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
229e948693eSPhilip Paeps 	struct sfxge_intr *intr;
230e948693eSPhilip Paeps 	struct resource *res;
231e948693eSPhilip Paeps 	int rid;
232e948693eSPhilip Paeps 	int error;
233e948693eSPhilip Paeps 	int i;
234e948693eSPhilip Paeps 
235e948693eSPhilip Paeps 	dev = sc->dev;
236e948693eSPhilip Paeps 	intr = &sc->intr;
237e948693eSPhilip Paeps 	error = 0;
238e948693eSPhilip Paeps 
239e948693eSPhilip Paeps 	table = malloc(count * sizeof(struct sfxge_intr_hdl),
240e948693eSPhilip Paeps 	    M_SFXGE, M_WAITOK);
241e948693eSPhilip Paeps 	intr->table = table;
242e948693eSPhilip Paeps 
243e948693eSPhilip Paeps 	for (i = 0; i < count; i++) {
244e948693eSPhilip Paeps 		rid = i + 1;
245e948693eSPhilip Paeps 		res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
246e948693eSPhilip Paeps 		    RF_SHAREABLE | RF_ACTIVE);
247e948693eSPhilip Paeps 		if (res == NULL) {
248e948693eSPhilip Paeps 			device_printf(dev, "Couldn't allocate interrupts for "
249e948693eSPhilip Paeps 			    "message %d\n", rid);
250e948693eSPhilip Paeps 			error = ENOMEM;
251e948693eSPhilip Paeps 			break;
252e948693eSPhilip Paeps 		}
253e948693eSPhilip Paeps 		table[i].eih_rid = rid;
254e948693eSPhilip Paeps 		table[i].eih_res = res;
255e948693eSPhilip Paeps 	}
256e948693eSPhilip Paeps 
257e948693eSPhilip Paeps 	if (error) {
258e948693eSPhilip Paeps 		count = i - 1;
259e948693eSPhilip Paeps 		for (i = 0; i < count; i++)
260e948693eSPhilip Paeps 			bus_release_resource(dev, SYS_RES_IRQ,
261e948693eSPhilip Paeps 			    table[i].eih_rid, table[i].eih_res);
262e948693eSPhilip Paeps 	}
263e948693eSPhilip Paeps 
264e948693eSPhilip Paeps 	return (error);
265e948693eSPhilip Paeps }
266e948693eSPhilip Paeps 
267e948693eSPhilip Paeps static void
268e948693eSPhilip Paeps sfxge_intr_teardown_msix(struct sfxge_softc *sc)
269e948693eSPhilip Paeps {
270e948693eSPhilip Paeps 	device_t dev;
271e948693eSPhilip Paeps 	struct resource *resp;
272e948693eSPhilip Paeps 	int rid;
273e948693eSPhilip Paeps 
274e948693eSPhilip Paeps 	dev = sc->dev;
275e948693eSPhilip Paeps 	resp = sc->intr.msix_res;
276e948693eSPhilip Paeps 
277e948693eSPhilip Paeps 	rid = rman_get_rid(resp);
278e948693eSPhilip Paeps 	bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
279e948693eSPhilip Paeps }
280e948693eSPhilip Paeps 
281e948693eSPhilip Paeps static int
282e948693eSPhilip Paeps sfxge_intr_setup_msix(struct sfxge_softc *sc)
283e948693eSPhilip Paeps {
284e948693eSPhilip Paeps 	struct sfxge_intr *intr;
285e948693eSPhilip Paeps 	struct resource *resp;
286e948693eSPhilip Paeps 	device_t dev;
287e948693eSPhilip Paeps 	int count;
288e948693eSPhilip Paeps 	int rid;
289e948693eSPhilip Paeps 
290e948693eSPhilip Paeps 	dev = sc->dev;
291e948693eSPhilip Paeps 	intr = &sc->intr;
292e948693eSPhilip Paeps 
293e948693eSPhilip Paeps 	/* Check if MSI-X is available. */
294e948693eSPhilip Paeps 	count = pci_msix_count(dev);
295e948693eSPhilip Paeps 	if (count == 0)
296e948693eSPhilip Paeps 		return (EINVAL);
297e948693eSPhilip Paeps 
298e948693eSPhilip Paeps 	/* Limit the number of interrupts to the number of CPUs. */
299e948693eSPhilip Paeps 	if (count > mp_ncpus)
300e948693eSPhilip Paeps 		count = mp_ncpus;
301e948693eSPhilip Paeps 
302e948693eSPhilip Paeps 	/* Not very likely these days... */
303e948693eSPhilip Paeps 	if (count > EFX_MAXRSS)
304e948693eSPhilip Paeps 		count = EFX_MAXRSS;
305e948693eSPhilip Paeps 
306e948693eSPhilip Paeps 	rid = PCIR_BAR(4);
307e948693eSPhilip Paeps 	resp = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
308e948693eSPhilip Paeps 	if (resp == NULL)
309e948693eSPhilip Paeps 		return (ENOMEM);
310e948693eSPhilip Paeps 
311e948693eSPhilip Paeps 	if (pci_alloc_msix(dev, &count) != 0) {
312e948693eSPhilip Paeps 		bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
313e948693eSPhilip Paeps 		return (ENOMEM);
314e948693eSPhilip Paeps 	}
315e948693eSPhilip Paeps 
316e948693eSPhilip Paeps 	/* Allocate interrupt handlers. */
317e948693eSPhilip Paeps 	if (sfxge_intr_alloc(sc, count) != 0) {
318e948693eSPhilip Paeps 		bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
319e948693eSPhilip Paeps 		pci_release_msi(dev);
320e948693eSPhilip Paeps 		return (ENOMEM);
321e948693eSPhilip Paeps 	}
322e948693eSPhilip Paeps 
323e948693eSPhilip Paeps 	intr->type = EFX_INTR_MESSAGE;
324e948693eSPhilip Paeps 	intr->n_alloc = count;
325e948693eSPhilip Paeps 	intr->msix_res = resp;
326e948693eSPhilip Paeps 
327e948693eSPhilip Paeps 	return (0);
328e948693eSPhilip Paeps }
329e948693eSPhilip Paeps 
330e948693eSPhilip Paeps static int
331e948693eSPhilip Paeps sfxge_intr_setup_msi(struct sfxge_softc *sc)
332e948693eSPhilip Paeps {
333e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
334e948693eSPhilip Paeps 	struct sfxge_intr *intr;
335e948693eSPhilip Paeps 	device_t dev;
336e948693eSPhilip Paeps 	int count;
337e948693eSPhilip Paeps 	int error;
338e948693eSPhilip Paeps 
339e948693eSPhilip Paeps 	dev = sc->dev;
340e948693eSPhilip Paeps 	intr = &sc->intr;
341e948693eSPhilip Paeps 	table = intr->table;
342e948693eSPhilip Paeps 
343e948693eSPhilip Paeps 	/*
344e948693eSPhilip Paeps 	 * Check if MSI is available.  All messages must be written to
345e948693eSPhilip Paeps 	 * the same address and on x86 this means the IRQs have the
346e948693eSPhilip Paeps 	 * same CPU affinity.  So we only ever allocate 1.
347e948693eSPhilip Paeps 	 */
348e948693eSPhilip Paeps 	count = pci_msi_count(dev) ? 1 : 0;
349e948693eSPhilip Paeps 	if (count == 0)
350e948693eSPhilip Paeps 		return (EINVAL);
351e948693eSPhilip Paeps 
352e948693eSPhilip Paeps 	if ((error = pci_alloc_msi(dev, &count)) != 0)
353e948693eSPhilip Paeps 		return (ENOMEM);
354e948693eSPhilip Paeps 
355e948693eSPhilip Paeps 	/* Allocate interrupt handler. */
356e948693eSPhilip Paeps 	if (sfxge_intr_alloc(sc, count) != 0) {
357e948693eSPhilip Paeps 		pci_release_msi(dev);
358e948693eSPhilip Paeps 		return (ENOMEM);
359e948693eSPhilip Paeps 	}
360e948693eSPhilip Paeps 
361e948693eSPhilip Paeps 	intr->type = EFX_INTR_MESSAGE;
362e948693eSPhilip Paeps 	intr->n_alloc = count;
363e948693eSPhilip Paeps 
364e948693eSPhilip Paeps 	return (0);
365e948693eSPhilip Paeps }
366e948693eSPhilip Paeps 
367e948693eSPhilip Paeps static int
368e948693eSPhilip Paeps sfxge_intr_setup_fixed(struct sfxge_softc *sc)
369e948693eSPhilip Paeps {
370e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
371e948693eSPhilip Paeps 	struct sfxge_intr *intr;
372e948693eSPhilip Paeps 	struct resource *res;
373e948693eSPhilip Paeps 	device_t dev;
374e948693eSPhilip Paeps 	int rid;
375e948693eSPhilip Paeps 
376e948693eSPhilip Paeps 	dev = sc->dev;
377e948693eSPhilip Paeps 	intr = &sc->intr;
378e948693eSPhilip Paeps 
379e948693eSPhilip Paeps 	rid = 0;
380e948693eSPhilip Paeps 	res =  bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
381e948693eSPhilip Paeps 	    RF_SHAREABLE | RF_ACTIVE);
382e948693eSPhilip Paeps 	if (res == NULL)
383e948693eSPhilip Paeps 		return (ENOMEM);
384e948693eSPhilip Paeps 
385e948693eSPhilip Paeps 	table = malloc(sizeof(struct sfxge_intr_hdl), M_SFXGE, M_WAITOK);
386e948693eSPhilip Paeps 	table[0].eih_rid = rid;
387e948693eSPhilip Paeps 	table[0].eih_res = res;
388e948693eSPhilip Paeps 
389e948693eSPhilip Paeps 	intr->type = EFX_INTR_LINE;
390e948693eSPhilip Paeps 	intr->n_alloc = 1;
391e948693eSPhilip Paeps 	intr->table = table;
392e948693eSPhilip Paeps 
393e948693eSPhilip Paeps 	return (0);
394e948693eSPhilip Paeps }
395e948693eSPhilip Paeps 
396e948693eSPhilip Paeps static const char *const __sfxge_err[] = {
397e948693eSPhilip Paeps 	"",
398e948693eSPhilip Paeps 	"SRAM out-of-bounds",
399e948693eSPhilip Paeps 	"Buffer ID out-of-bounds",
400e948693eSPhilip Paeps 	"Internal memory parity",
401e948693eSPhilip Paeps 	"Receive buffer ownership",
402e948693eSPhilip Paeps 	"Transmit buffer ownership",
403e948693eSPhilip Paeps 	"Receive descriptor ownership",
404e948693eSPhilip Paeps 	"Transmit descriptor ownership",
405e948693eSPhilip Paeps 	"Event queue ownership",
406e948693eSPhilip Paeps 	"Event queue FIFO overflow",
407e948693eSPhilip Paeps 	"Illegal address",
408e948693eSPhilip Paeps 	"SRAM parity"
409e948693eSPhilip Paeps };
410e948693eSPhilip Paeps 
411e948693eSPhilip Paeps void
412e948693eSPhilip Paeps sfxge_err(efsys_identifier_t *arg, unsigned int code, uint32_t dword0,
413e948693eSPhilip Paeps     uint32_t dword1)
414e948693eSPhilip Paeps {
415e948693eSPhilip Paeps 	struct sfxge_softc *sc = (struct sfxge_softc *)arg;
416e948693eSPhilip Paeps 	device_t dev = sc->dev;
417e948693eSPhilip Paeps 
418e948693eSPhilip Paeps 	log(LOG_WARNING, "[%s%d] FATAL ERROR: %s (0x%08x%08x)",
419e948693eSPhilip Paeps 	    device_get_name(dev), device_get_unit(dev),
420e948693eSPhilip Paeps 		__sfxge_err[code], dword1, dword0);
421e948693eSPhilip Paeps }
422e948693eSPhilip Paeps 
423e948693eSPhilip Paeps void
424e948693eSPhilip Paeps sfxge_intr_stop(struct sfxge_softc *sc)
425e948693eSPhilip Paeps {
426e948693eSPhilip Paeps 	struct sfxge_intr *intr;
427e948693eSPhilip Paeps 
428e948693eSPhilip Paeps 	intr = &sc->intr;
429e948693eSPhilip Paeps 
430e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_STARTED,
431e948693eSPhilip Paeps 	    ("Interrupts not started"));
432e948693eSPhilip Paeps 
433e948693eSPhilip Paeps 	intr->state = SFXGE_INTR_INITIALIZED;
434e948693eSPhilip Paeps 
435e948693eSPhilip Paeps 	/* Disable interrupts at the NIC */
436e948693eSPhilip Paeps 	efx_intr_disable(sc->enp);
437e948693eSPhilip Paeps 
438e948693eSPhilip Paeps 	/* Disable interrupts at the bus */
439e948693eSPhilip Paeps 	sfxge_intr_bus_disable(sc);
440e948693eSPhilip Paeps 
441e948693eSPhilip Paeps 	/* Tear down common code interrupt bits. */
442e948693eSPhilip Paeps 	efx_intr_fini(sc->enp);
443e948693eSPhilip Paeps }
444e948693eSPhilip Paeps 
445e948693eSPhilip Paeps int
446e948693eSPhilip Paeps sfxge_intr_start(struct sfxge_softc *sc)
447e948693eSPhilip Paeps {
448e948693eSPhilip Paeps 	struct sfxge_intr *intr;
449e948693eSPhilip Paeps 	efsys_mem_t *esmp;
450e948693eSPhilip Paeps 	int rc;
451e948693eSPhilip Paeps 
452e948693eSPhilip Paeps 	intr = &sc->intr;
453e948693eSPhilip Paeps 	esmp = &intr->status;
454e948693eSPhilip Paeps 
455e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
456e948693eSPhilip Paeps 	    ("Interrupts not initialized"));
457e948693eSPhilip Paeps 
458e948693eSPhilip Paeps 	/* Zero the memory. */
459e948693eSPhilip Paeps 	(void)memset(esmp->esm_base, 0, EFX_INTR_SIZE);
460e948693eSPhilip Paeps 
461e948693eSPhilip Paeps 	/* Initialize common code interrupt bits. */
462e948693eSPhilip Paeps 	(void)efx_intr_init(sc->enp, intr->type, esmp);
463e948693eSPhilip Paeps 
464e948693eSPhilip Paeps 	/* Enable interrupts at the bus */
465e948693eSPhilip Paeps 	if ((rc = sfxge_intr_bus_enable(sc)) != 0)
466e948693eSPhilip Paeps 		goto fail;
467e948693eSPhilip Paeps 
46876a86938SPhilip Paeps 	intr->state = SFXGE_INTR_STARTED;
469e948693eSPhilip Paeps 
470e948693eSPhilip Paeps 	/* Enable interrupts at the NIC */
471e948693eSPhilip Paeps 	efx_intr_enable(sc->enp);
472e948693eSPhilip Paeps 
473e948693eSPhilip Paeps 	return (0);
474e948693eSPhilip Paeps 
475e948693eSPhilip Paeps fail:
476e948693eSPhilip Paeps 	/* Tear down common code interrupt bits. */
477e948693eSPhilip Paeps 	efx_intr_fini(sc->enp);
478e948693eSPhilip Paeps 
479e948693eSPhilip Paeps 	intr->state = SFXGE_INTR_INITIALIZED;
480e948693eSPhilip Paeps 
481e948693eSPhilip Paeps 	return (rc);
482e948693eSPhilip Paeps }
483e948693eSPhilip Paeps 
484e948693eSPhilip Paeps void
485e948693eSPhilip Paeps sfxge_intr_fini(struct sfxge_softc *sc)
486e948693eSPhilip Paeps {
487e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
488e948693eSPhilip Paeps 	struct sfxge_intr *intr;
489e948693eSPhilip Paeps 	efsys_mem_t *esmp;
490e948693eSPhilip Paeps 	device_t dev;
491e948693eSPhilip Paeps 	int i;
492e948693eSPhilip Paeps 
493e948693eSPhilip Paeps 	dev = sc->dev;
494e948693eSPhilip Paeps 	intr = &sc->intr;
495e948693eSPhilip Paeps 	esmp = &intr->status;
496e948693eSPhilip Paeps 	table = intr->table;
497e948693eSPhilip Paeps 
498e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
499e948693eSPhilip Paeps 	    ("intr->state != SFXGE_INTR_INITIALIZED"));
500e948693eSPhilip Paeps 
501e948693eSPhilip Paeps 	/* Free DMA memory. */
502e948693eSPhilip Paeps 	sfxge_dma_free(esmp);
503e948693eSPhilip Paeps 
504e948693eSPhilip Paeps 	/* Free interrupt handles. */
505e948693eSPhilip Paeps 	for (i = 0; i < intr->n_alloc; i++)
506e948693eSPhilip Paeps 		bus_release_resource(dev, SYS_RES_IRQ,
507e948693eSPhilip Paeps 		    table[i].eih_rid, table[i].eih_res);
508e948693eSPhilip Paeps 
509e948693eSPhilip Paeps 	if (table[0].eih_rid != 0)
510e948693eSPhilip Paeps 		pci_release_msi(dev);
511e948693eSPhilip Paeps 
512e948693eSPhilip Paeps 	if (intr->msix_res != NULL)
513e948693eSPhilip Paeps 		sfxge_intr_teardown_msix(sc);
514e948693eSPhilip Paeps 
515e948693eSPhilip Paeps 	/* Free the handle table */
516e948693eSPhilip Paeps 	free(table, M_SFXGE);
517e948693eSPhilip Paeps 	intr->table = NULL;
518e948693eSPhilip Paeps 	intr->n_alloc = 0;
519e948693eSPhilip Paeps 
520e948693eSPhilip Paeps 	/* Clear the interrupt type */
521e948693eSPhilip Paeps 	intr->type = EFX_INTR_INVALID;
522e948693eSPhilip Paeps 
523e948693eSPhilip Paeps 	intr->state = SFXGE_INTR_UNINITIALIZED;
524e948693eSPhilip Paeps }
525e948693eSPhilip Paeps 
526e948693eSPhilip Paeps int
527e948693eSPhilip Paeps sfxge_intr_init(struct sfxge_softc *sc)
528e948693eSPhilip Paeps {
529e948693eSPhilip Paeps 	device_t dev;
530e948693eSPhilip Paeps 	struct sfxge_intr *intr;
531e948693eSPhilip Paeps 	efsys_mem_t *esmp;
532e948693eSPhilip Paeps 	int rc;
533e948693eSPhilip Paeps 
534e948693eSPhilip Paeps 	dev = sc->dev;
535e948693eSPhilip Paeps 	intr = &sc->intr;
536e948693eSPhilip Paeps 	esmp = &intr->status;
537e948693eSPhilip Paeps 
538e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_UNINITIALIZED,
539e948693eSPhilip Paeps 	    ("Interrupts already initialized"));
540e948693eSPhilip Paeps 
541e948693eSPhilip Paeps 	/* Try to setup MSI-X or MSI interrupts if available. */
542e948693eSPhilip Paeps 	if ((rc = sfxge_intr_setup_msix(sc)) == 0)
543e948693eSPhilip Paeps 		device_printf(dev, "Using MSI-X interrupts\n");
544e948693eSPhilip Paeps 	else if ((rc = sfxge_intr_setup_msi(sc)) == 0)
545e948693eSPhilip Paeps 		device_printf(dev, "Using MSI interrupts\n");
546e948693eSPhilip Paeps 	else if ((rc = sfxge_intr_setup_fixed(sc)) == 0) {
547e948693eSPhilip Paeps 		device_printf(dev, "Using fixed interrupts\n");
548e948693eSPhilip Paeps 	} else {
549e948693eSPhilip Paeps 		device_printf(dev, "Couldn't setup interrupts\n");
550e948693eSPhilip Paeps 		return (ENOMEM);
551e948693eSPhilip Paeps 	}
552e948693eSPhilip Paeps 
553e948693eSPhilip Paeps 	/* Set up DMA for interrupts. */
554e948693eSPhilip Paeps 	if ((rc = sfxge_dma_alloc(sc, EFX_INTR_SIZE, esmp)) != 0)
555e948693eSPhilip Paeps 		return (ENOMEM);
556e948693eSPhilip Paeps 
557e948693eSPhilip Paeps 	intr->state = SFXGE_INTR_INITIALIZED;
558e948693eSPhilip Paeps 
559e948693eSPhilip Paeps 	return (0);
560e948693eSPhilip Paeps }
561