xref: /freebsd/sys/dev/sfxge/sfxge_intr.c (revision b64af6b0fb7ed582df33173adc5efcae39c91ecd)
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>
35eedc7fd9SGleb Smirnoff #include <sys/kernel.h>
36eedc7fd9SGleb Smirnoff #include <sys/malloc.h>
37eedc7fd9SGleb Smirnoff #include <sys/queue.h>
38e948693eSPhilip Paeps #include <sys/rman.h>
39e948693eSPhilip Paeps #include <sys/smp.h>
40e948693eSPhilip Paeps #include <sys/syslog.h>
41eedc7fd9SGleb 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)
73b7b0edd1SGeorge V. Neville-Neil 		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);
80b7b0edd1SGeorge V. Neville-Neil 		return (FILTER_HANDLED);
81e948693eSPhilip Paeps 	}
82e948693eSPhilip Paeps 
83e948693eSPhilip Paeps 	if (qmask != 0) {
84e948693eSPhilip Paeps 		intr->zero_count = 0;
85b7b0edd1SGeorge V. Neville-Neil 		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))
100b7b0edd1SGeorge V. Neville-Neil 				return (FILTER_SCHEDULE_THREAD);
101e948693eSPhilip Paeps 			efx_ev_qprime(evq->common, evq->read_ptr);
102b7b0edd1SGeorge V. Neville-Neil 			return (FILTER_HANDLED);
103e948693eSPhilip Paeps 		}
104e948693eSPhilip Paeps 	}
105e948693eSPhilip Paeps 
106b7b0edd1SGeorge V. Neville-Neil 	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 
114*b64af6b0SAndrew Rybchenko 	(void)sfxge_ev_qpoll(evq);
115e948693eSPhilip Paeps }
116e948693eSPhilip Paeps 
117e948693eSPhilip Paeps static void
118e948693eSPhilip Paeps sfxge_intr_message(void *arg)
119e948693eSPhilip Paeps {
120e948693eSPhilip Paeps 	struct sfxge_evq *evq;
121e948693eSPhilip Paeps 	struct sfxge_softc *sc;
122e948693eSPhilip Paeps 	efx_nic_t *enp;
123e948693eSPhilip Paeps 	struct sfxge_intr *intr;
124e948693eSPhilip Paeps 	unsigned int index;
125e948693eSPhilip Paeps 	boolean_t fatal;
126e948693eSPhilip Paeps 
127e948693eSPhilip Paeps 	evq = (struct sfxge_evq *)arg;
128e948693eSPhilip Paeps 	sc = evq->sc;
129e948693eSPhilip Paeps 	enp = sc->enp;
130e948693eSPhilip Paeps 	intr = &sc->intr;
131e948693eSPhilip Paeps 	index = evq->index;
132e948693eSPhilip Paeps 
133e948693eSPhilip Paeps 	KASSERT(intr != NULL, ("intr == NULL"));
134e948693eSPhilip Paeps 	KASSERT(intr->type == EFX_INTR_MESSAGE,
135e948693eSPhilip Paeps 	    ("intr->type != EFX_INTR_MESSAGE"));
136e948693eSPhilip Paeps 
13776a86938SPhilip Paeps 	if (intr->state != SFXGE_INTR_STARTED)
138e948693eSPhilip Paeps 		return;
139e948693eSPhilip Paeps 
140e948693eSPhilip Paeps 	(void)efx_intr_status_message(enp, index, &fatal);
141e948693eSPhilip Paeps 
142e948693eSPhilip Paeps 	if (fatal) {
143e948693eSPhilip Paeps 		(void)efx_intr_disable(enp);
144e948693eSPhilip Paeps 		(void)efx_intr_fatal(enp);
145e948693eSPhilip Paeps 		return;
146e948693eSPhilip Paeps 	}
147e948693eSPhilip Paeps 
148*b64af6b0SAndrew Rybchenko 	(void)sfxge_ev_qpoll(evq);
149e948693eSPhilip Paeps }
150e948693eSPhilip Paeps 
151e948693eSPhilip Paeps static int
152e948693eSPhilip Paeps sfxge_intr_bus_enable(struct sfxge_softc *sc)
153e948693eSPhilip Paeps {
154e948693eSPhilip Paeps 	struct sfxge_intr *intr;
155e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
156e948693eSPhilip Paeps 	driver_filter_t *filter;
157e948693eSPhilip Paeps 	driver_intr_t *handler;
158e948693eSPhilip Paeps 	int index;
159e948693eSPhilip Paeps 	int err;
160e948693eSPhilip Paeps 
161e948693eSPhilip Paeps 	intr = &sc->intr;
162e948693eSPhilip Paeps 	table = intr->table;
163e948693eSPhilip Paeps 
164e948693eSPhilip Paeps 	switch (intr->type) {
165e948693eSPhilip Paeps 	case EFX_INTR_MESSAGE:
166e948693eSPhilip Paeps 		filter = NULL; /* not shared */
167e948693eSPhilip Paeps 		handler = sfxge_intr_message;
168e948693eSPhilip Paeps 		break;
169e948693eSPhilip Paeps 
170e948693eSPhilip Paeps 	case EFX_INTR_LINE:
171e948693eSPhilip Paeps 		filter = sfxge_intr_line_filter;
172e948693eSPhilip Paeps 		handler = sfxge_intr_line;
173e948693eSPhilip Paeps 		break;
174e948693eSPhilip Paeps 
175e948693eSPhilip Paeps 	default:
176e948693eSPhilip Paeps 		KASSERT(0, ("Invalid interrupt type"));
177b7b0edd1SGeorge V. Neville-Neil 		return (EINVAL);
178e948693eSPhilip Paeps 	}
179e948693eSPhilip Paeps 
180e948693eSPhilip Paeps 	/* Try to add the handlers */
181e948693eSPhilip Paeps 	for (index = 0; index < intr->n_alloc; index++) {
182e948693eSPhilip Paeps 		if ((err = bus_setup_intr(sc->dev, table[index].eih_res,
183e948693eSPhilip Paeps 			    INTR_MPSAFE|INTR_TYPE_NET, filter, handler,
184e948693eSPhilip Paeps 			    sc->evq[index], &table[index].eih_tag)) != 0) {
185e948693eSPhilip Paeps 			goto fail;
186e948693eSPhilip Paeps 		}
187e948693eSPhilip Paeps #ifdef SFXGE_HAVE_DESCRIBE_INTR
188e948693eSPhilip Paeps 		if (intr->n_alloc > 1)
189e948693eSPhilip Paeps 			bus_describe_intr(sc->dev, table[index].eih_res,
190e948693eSPhilip Paeps 			    table[index].eih_tag, "%d", index);
191e948693eSPhilip Paeps #endif
192e948693eSPhilip Paeps 		bus_bind_intr(sc->dev, table[index].eih_res, index);
193e948693eSPhilip Paeps 
194e948693eSPhilip Paeps 	}
195e948693eSPhilip Paeps 
196e948693eSPhilip Paeps 	return (0);
197e948693eSPhilip Paeps 
198e948693eSPhilip Paeps fail:
199e948693eSPhilip Paeps 	/* Remove remaining handlers */
200e948693eSPhilip Paeps 	while (--index >= 0)
201e948693eSPhilip Paeps 		bus_teardown_intr(sc->dev, table[index].eih_res,
202e948693eSPhilip Paeps 		    table[index].eih_tag);
203e948693eSPhilip Paeps 
204e948693eSPhilip Paeps 	return (err);
205e948693eSPhilip Paeps }
206e948693eSPhilip Paeps 
207e948693eSPhilip Paeps static void
208e948693eSPhilip Paeps sfxge_intr_bus_disable(struct sfxge_softc *sc)
209e948693eSPhilip Paeps {
210e948693eSPhilip Paeps 	struct sfxge_intr *intr;
211e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
212e948693eSPhilip Paeps 	int i;
213e948693eSPhilip Paeps 
214e948693eSPhilip Paeps 	intr = &sc->intr;
215e948693eSPhilip Paeps 	table = intr->table;
216e948693eSPhilip Paeps 
217e948693eSPhilip Paeps 	/* Remove all handlers */
218e948693eSPhilip Paeps 	for (i = 0; i < intr->n_alloc; i++)
219e948693eSPhilip Paeps 		bus_teardown_intr(sc->dev, table[i].eih_res,
220e948693eSPhilip Paeps 		    table[i].eih_tag);
221e948693eSPhilip Paeps }
222e948693eSPhilip Paeps 
223e948693eSPhilip Paeps static int
224e948693eSPhilip Paeps sfxge_intr_alloc(struct sfxge_softc *sc, int count)
225e948693eSPhilip Paeps {
226e948693eSPhilip Paeps 	device_t dev;
227e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
228e948693eSPhilip Paeps 	struct sfxge_intr *intr;
229e948693eSPhilip Paeps 	struct resource *res;
230e948693eSPhilip Paeps 	int rid;
231e948693eSPhilip Paeps 	int error;
232e948693eSPhilip Paeps 	int i;
233e948693eSPhilip Paeps 
234e948693eSPhilip Paeps 	dev = sc->dev;
235e948693eSPhilip Paeps 	intr = &sc->intr;
236e948693eSPhilip Paeps 	error = 0;
237e948693eSPhilip Paeps 
238e948693eSPhilip Paeps 	table = malloc(count * sizeof(struct sfxge_intr_hdl),
239e948693eSPhilip Paeps 	    M_SFXGE, M_WAITOK);
240e948693eSPhilip Paeps 	intr->table = table;
241e948693eSPhilip Paeps 
242e948693eSPhilip Paeps 	for (i = 0; i < count; i++) {
243e948693eSPhilip Paeps 		rid = i + 1;
244e948693eSPhilip Paeps 		res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
245e948693eSPhilip Paeps 		    RF_SHAREABLE | RF_ACTIVE);
246e948693eSPhilip Paeps 		if (res == NULL) {
247e948693eSPhilip Paeps 			device_printf(dev, "Couldn't allocate interrupts for "
248e948693eSPhilip Paeps 			    "message %d\n", rid);
249e948693eSPhilip Paeps 			error = ENOMEM;
250e948693eSPhilip Paeps 			break;
251e948693eSPhilip Paeps 		}
252e948693eSPhilip Paeps 		table[i].eih_rid = rid;
253e948693eSPhilip Paeps 		table[i].eih_res = res;
254e948693eSPhilip Paeps 	}
255e948693eSPhilip Paeps 
256b7b0edd1SGeorge V. Neville-Neil 	if (error != 0) {
257e948693eSPhilip Paeps 		count = i - 1;
258e948693eSPhilip Paeps 		for (i = 0; i < count; i++)
259e948693eSPhilip Paeps 			bus_release_resource(dev, SYS_RES_IRQ,
260e948693eSPhilip Paeps 			    table[i].eih_rid, table[i].eih_res);
261e948693eSPhilip Paeps 	}
262e948693eSPhilip Paeps 
263e948693eSPhilip Paeps 	return (error);
264e948693eSPhilip Paeps }
265e948693eSPhilip Paeps 
266e948693eSPhilip Paeps static void
267e948693eSPhilip Paeps sfxge_intr_teardown_msix(struct sfxge_softc *sc)
268e948693eSPhilip Paeps {
269e948693eSPhilip Paeps 	device_t dev;
270e948693eSPhilip Paeps 	struct resource *resp;
271e948693eSPhilip Paeps 	int rid;
272e948693eSPhilip Paeps 
273e948693eSPhilip Paeps 	dev = sc->dev;
274e948693eSPhilip Paeps 	resp = sc->intr.msix_res;
275e948693eSPhilip Paeps 
276e948693eSPhilip Paeps 	rid = rman_get_rid(resp);
277e948693eSPhilip Paeps 	bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
278e948693eSPhilip Paeps }
279e948693eSPhilip Paeps 
280e948693eSPhilip Paeps static int
281e948693eSPhilip Paeps sfxge_intr_setup_msix(struct sfxge_softc *sc)
282e948693eSPhilip Paeps {
283e948693eSPhilip Paeps 	struct sfxge_intr *intr;
284e948693eSPhilip Paeps 	struct resource *resp;
285e948693eSPhilip Paeps 	device_t dev;
286e948693eSPhilip Paeps 	int count;
287e948693eSPhilip Paeps 	int rid;
288e948693eSPhilip Paeps 
289e948693eSPhilip Paeps 	dev = sc->dev;
290e948693eSPhilip Paeps 	intr = &sc->intr;
291e948693eSPhilip Paeps 
292e948693eSPhilip Paeps 	/* Check if MSI-X is available. */
293e948693eSPhilip Paeps 	count = pci_msix_count(dev);
294e948693eSPhilip Paeps 	if (count == 0)
295e948693eSPhilip Paeps 		return (EINVAL);
296e948693eSPhilip Paeps 
297e948693eSPhilip Paeps 	/* Limit the number of interrupts to the number of CPUs. */
298e948693eSPhilip Paeps 	if (count > mp_ncpus)
299e948693eSPhilip Paeps 		count = mp_ncpus;
300e948693eSPhilip Paeps 
301e948693eSPhilip Paeps 	/* Not very likely these days... */
302e948693eSPhilip Paeps 	if (count > EFX_MAXRSS)
303e948693eSPhilip Paeps 		count = EFX_MAXRSS;
304e948693eSPhilip Paeps 
305e948693eSPhilip Paeps 	rid = PCIR_BAR(4);
306e948693eSPhilip Paeps 	resp = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
307e948693eSPhilip Paeps 	if (resp == NULL)
308e948693eSPhilip Paeps 		return (ENOMEM);
309e948693eSPhilip Paeps 
310e948693eSPhilip Paeps 	if (pci_alloc_msix(dev, &count) != 0) {
311e948693eSPhilip Paeps 		bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
312e948693eSPhilip Paeps 		return (ENOMEM);
313e948693eSPhilip Paeps 	}
314e948693eSPhilip Paeps 
315e948693eSPhilip Paeps 	/* Allocate interrupt handlers. */
316e948693eSPhilip Paeps 	if (sfxge_intr_alloc(sc, count) != 0) {
317e948693eSPhilip Paeps 		bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
318e948693eSPhilip Paeps 		pci_release_msi(dev);
319e948693eSPhilip Paeps 		return (ENOMEM);
320e948693eSPhilip Paeps 	}
321e948693eSPhilip Paeps 
322e948693eSPhilip Paeps 	intr->type = EFX_INTR_MESSAGE;
323e948693eSPhilip Paeps 	intr->n_alloc = count;
324e948693eSPhilip Paeps 	intr->msix_res = resp;
325e948693eSPhilip Paeps 
326e948693eSPhilip Paeps 	return (0);
327e948693eSPhilip Paeps }
328e948693eSPhilip Paeps 
329e948693eSPhilip Paeps static int
330e948693eSPhilip Paeps sfxge_intr_setup_msi(struct sfxge_softc *sc)
331e948693eSPhilip Paeps {
332e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
333e948693eSPhilip Paeps 	struct sfxge_intr *intr;
334e948693eSPhilip Paeps 	device_t dev;
335e948693eSPhilip Paeps 	int count;
336e948693eSPhilip Paeps 	int error;
337e948693eSPhilip Paeps 
338e948693eSPhilip Paeps 	dev = sc->dev;
339e948693eSPhilip Paeps 	intr = &sc->intr;
340e948693eSPhilip Paeps 	table = intr->table;
341e948693eSPhilip Paeps 
342e948693eSPhilip Paeps 	/*
343e948693eSPhilip Paeps 	 * Check if MSI is available.  All messages must be written to
344e948693eSPhilip Paeps 	 * the same address and on x86 this means the IRQs have the
345e948693eSPhilip Paeps 	 * same CPU affinity.  So we only ever allocate 1.
346e948693eSPhilip Paeps 	 */
347e948693eSPhilip Paeps 	count = pci_msi_count(dev) ? 1 : 0;
348e948693eSPhilip Paeps 	if (count == 0)
349e948693eSPhilip Paeps 		return (EINVAL);
350e948693eSPhilip Paeps 
351e948693eSPhilip Paeps 	if ((error = pci_alloc_msi(dev, &count)) != 0)
352e948693eSPhilip Paeps 		return (ENOMEM);
353e948693eSPhilip Paeps 
354e948693eSPhilip Paeps 	/* Allocate interrupt handler. */
355e948693eSPhilip Paeps 	if (sfxge_intr_alloc(sc, count) != 0) {
356e948693eSPhilip Paeps 		pci_release_msi(dev);
357e948693eSPhilip Paeps 		return (ENOMEM);
358e948693eSPhilip Paeps 	}
359e948693eSPhilip Paeps 
360e948693eSPhilip Paeps 	intr->type = EFX_INTR_MESSAGE;
361e948693eSPhilip Paeps 	intr->n_alloc = count;
362e948693eSPhilip Paeps 
363e948693eSPhilip Paeps 	return (0);
364e948693eSPhilip Paeps }
365e948693eSPhilip Paeps 
366e948693eSPhilip Paeps static int
367e948693eSPhilip Paeps sfxge_intr_setup_fixed(struct sfxge_softc *sc)
368e948693eSPhilip Paeps {
369e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
370e948693eSPhilip Paeps 	struct sfxge_intr *intr;
371e948693eSPhilip Paeps 	struct resource *res;
372e948693eSPhilip Paeps 	device_t dev;
373e948693eSPhilip Paeps 	int rid;
374e948693eSPhilip Paeps 
375e948693eSPhilip Paeps 	dev = sc->dev;
376e948693eSPhilip Paeps 	intr = &sc->intr;
377e948693eSPhilip Paeps 
378e948693eSPhilip Paeps 	rid = 0;
379e948693eSPhilip Paeps 	res =  bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
380e948693eSPhilip Paeps 	    RF_SHAREABLE | RF_ACTIVE);
381e948693eSPhilip Paeps 	if (res == NULL)
382e948693eSPhilip Paeps 		return (ENOMEM);
383e948693eSPhilip Paeps 
384e948693eSPhilip Paeps 	table = malloc(sizeof(struct sfxge_intr_hdl), M_SFXGE, M_WAITOK);
385e948693eSPhilip Paeps 	table[0].eih_rid = rid;
386e948693eSPhilip Paeps 	table[0].eih_res = res;
387e948693eSPhilip Paeps 
388e948693eSPhilip Paeps 	intr->type = EFX_INTR_LINE;
389e948693eSPhilip Paeps 	intr->n_alloc = 1;
390e948693eSPhilip Paeps 	intr->table = table;
391e948693eSPhilip Paeps 
392e948693eSPhilip Paeps 	return (0);
393e948693eSPhilip Paeps }
394e948693eSPhilip Paeps 
395e948693eSPhilip Paeps static const char *const __sfxge_err[] = {
396e948693eSPhilip Paeps 	"",
397e948693eSPhilip Paeps 	"SRAM out-of-bounds",
398e948693eSPhilip Paeps 	"Buffer ID out-of-bounds",
399e948693eSPhilip Paeps 	"Internal memory parity",
400e948693eSPhilip Paeps 	"Receive buffer ownership",
401e948693eSPhilip Paeps 	"Transmit buffer ownership",
402e948693eSPhilip Paeps 	"Receive descriptor ownership",
403e948693eSPhilip Paeps 	"Transmit descriptor ownership",
404e948693eSPhilip Paeps 	"Event queue ownership",
405e948693eSPhilip Paeps 	"Event queue FIFO overflow",
406e948693eSPhilip Paeps 	"Illegal address",
407e948693eSPhilip Paeps 	"SRAM parity"
408e948693eSPhilip Paeps };
409e948693eSPhilip Paeps 
410e948693eSPhilip Paeps void
411e948693eSPhilip Paeps sfxge_err(efsys_identifier_t *arg, unsigned int code, uint32_t dword0,
412e948693eSPhilip Paeps     uint32_t dword1)
413e948693eSPhilip Paeps {
414e948693eSPhilip Paeps 	struct sfxge_softc *sc = (struct sfxge_softc *)arg;
415e948693eSPhilip Paeps 	device_t dev = sc->dev;
416e948693eSPhilip Paeps 
417e948693eSPhilip Paeps 	log(LOG_WARNING, "[%s%d] FATAL ERROR: %s (0x%08x%08x)",
418e948693eSPhilip Paeps 	    device_get_name(dev), device_get_unit(dev),
419e948693eSPhilip Paeps 		__sfxge_err[code], dword1, dword0);
420e948693eSPhilip Paeps }
421e948693eSPhilip Paeps 
422e948693eSPhilip Paeps void
423e948693eSPhilip Paeps sfxge_intr_stop(struct sfxge_softc *sc)
424e948693eSPhilip Paeps {
425e948693eSPhilip Paeps 	struct sfxge_intr *intr;
426e948693eSPhilip Paeps 
427e948693eSPhilip Paeps 	intr = &sc->intr;
428e948693eSPhilip Paeps 
429e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_STARTED,
430e948693eSPhilip Paeps 	    ("Interrupts not started"));
431e948693eSPhilip Paeps 
432e948693eSPhilip Paeps 	intr->state = SFXGE_INTR_INITIALIZED;
433e948693eSPhilip Paeps 
434e948693eSPhilip Paeps 	/* Disable interrupts at the NIC */
435e948693eSPhilip Paeps 	efx_intr_disable(sc->enp);
436e948693eSPhilip Paeps 
437e948693eSPhilip Paeps 	/* Disable interrupts at the bus */
438e948693eSPhilip Paeps 	sfxge_intr_bus_disable(sc);
439e948693eSPhilip Paeps 
440e948693eSPhilip Paeps 	/* Tear down common code interrupt bits. */
441e948693eSPhilip Paeps 	efx_intr_fini(sc->enp);
442e948693eSPhilip Paeps }
443e948693eSPhilip Paeps 
444e948693eSPhilip Paeps int
445e948693eSPhilip Paeps sfxge_intr_start(struct sfxge_softc *sc)
446e948693eSPhilip Paeps {
447e948693eSPhilip Paeps 	struct sfxge_intr *intr;
448e948693eSPhilip Paeps 	efsys_mem_t *esmp;
449e948693eSPhilip Paeps 	int rc;
450e948693eSPhilip Paeps 
451e948693eSPhilip Paeps 	intr = &sc->intr;
452e948693eSPhilip Paeps 	esmp = &intr->status;
453e948693eSPhilip Paeps 
454e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
455e948693eSPhilip Paeps 	    ("Interrupts not initialized"));
456e948693eSPhilip Paeps 
457e948693eSPhilip Paeps 	/* Zero the memory. */
458e948693eSPhilip Paeps 	(void)memset(esmp->esm_base, 0, EFX_INTR_SIZE);
459e948693eSPhilip Paeps 
460e948693eSPhilip Paeps 	/* Initialize common code interrupt bits. */
461e948693eSPhilip Paeps 	(void)efx_intr_init(sc->enp, intr->type, esmp);
462e948693eSPhilip Paeps 
463e948693eSPhilip Paeps 	/* Enable interrupts at the bus */
464e948693eSPhilip Paeps 	if ((rc = sfxge_intr_bus_enable(sc)) != 0)
465e948693eSPhilip Paeps 		goto fail;
466e948693eSPhilip Paeps 
46776a86938SPhilip Paeps 	intr->state = SFXGE_INTR_STARTED;
468e948693eSPhilip Paeps 
469e948693eSPhilip Paeps 	/* Enable interrupts at the NIC */
470e948693eSPhilip Paeps 	efx_intr_enable(sc->enp);
471e948693eSPhilip Paeps 
472e948693eSPhilip Paeps 	return (0);
473e948693eSPhilip Paeps 
474e948693eSPhilip Paeps fail:
475e948693eSPhilip Paeps 	/* Tear down common code interrupt bits. */
476e948693eSPhilip Paeps 	efx_intr_fini(sc->enp);
477e948693eSPhilip Paeps 
478e948693eSPhilip Paeps 	intr->state = SFXGE_INTR_INITIALIZED;
479e948693eSPhilip Paeps 
480e948693eSPhilip Paeps 	return (rc);
481e948693eSPhilip Paeps }
482e948693eSPhilip Paeps 
483e948693eSPhilip Paeps void
484e948693eSPhilip Paeps sfxge_intr_fini(struct sfxge_softc *sc)
485e948693eSPhilip Paeps {
486e948693eSPhilip Paeps 	struct sfxge_intr_hdl *table;
487e948693eSPhilip Paeps 	struct sfxge_intr *intr;
488e948693eSPhilip Paeps 	efsys_mem_t *esmp;
489e948693eSPhilip Paeps 	device_t dev;
490e948693eSPhilip Paeps 	int i;
491e948693eSPhilip Paeps 
492e948693eSPhilip Paeps 	dev = sc->dev;
493e948693eSPhilip Paeps 	intr = &sc->intr;
494e948693eSPhilip Paeps 	esmp = &intr->status;
495e948693eSPhilip Paeps 	table = intr->table;
496e948693eSPhilip Paeps 
497e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
498e948693eSPhilip Paeps 	    ("intr->state != SFXGE_INTR_INITIALIZED"));
499e948693eSPhilip Paeps 
500e948693eSPhilip Paeps 	/* Free DMA memory. */
501e948693eSPhilip Paeps 	sfxge_dma_free(esmp);
502e948693eSPhilip Paeps 
503e948693eSPhilip Paeps 	/* Free interrupt handles. */
504e948693eSPhilip Paeps 	for (i = 0; i < intr->n_alloc; i++)
505e948693eSPhilip Paeps 		bus_release_resource(dev, SYS_RES_IRQ,
506e948693eSPhilip Paeps 		    table[i].eih_rid, table[i].eih_res);
507e948693eSPhilip Paeps 
508e948693eSPhilip Paeps 	if (table[0].eih_rid != 0)
509e948693eSPhilip Paeps 		pci_release_msi(dev);
510e948693eSPhilip Paeps 
511e948693eSPhilip Paeps 	if (intr->msix_res != NULL)
512e948693eSPhilip Paeps 		sfxge_intr_teardown_msix(sc);
513e948693eSPhilip Paeps 
514e948693eSPhilip Paeps 	/* Free the handle table */
515e948693eSPhilip Paeps 	free(table, M_SFXGE);
516e948693eSPhilip Paeps 	intr->table = NULL;
517e948693eSPhilip Paeps 	intr->n_alloc = 0;
518e948693eSPhilip Paeps 
519e948693eSPhilip Paeps 	/* Clear the interrupt type */
520e948693eSPhilip Paeps 	intr->type = EFX_INTR_INVALID;
521e948693eSPhilip Paeps 
522e948693eSPhilip Paeps 	intr->state = SFXGE_INTR_UNINITIALIZED;
523e948693eSPhilip Paeps }
524e948693eSPhilip Paeps 
525e948693eSPhilip Paeps int
526e948693eSPhilip Paeps sfxge_intr_init(struct sfxge_softc *sc)
527e948693eSPhilip Paeps {
528e948693eSPhilip Paeps 	device_t dev;
529e948693eSPhilip Paeps 	struct sfxge_intr *intr;
530e948693eSPhilip Paeps 	efsys_mem_t *esmp;
531e948693eSPhilip Paeps 	int rc;
532e948693eSPhilip Paeps 
533e948693eSPhilip Paeps 	dev = sc->dev;
534e948693eSPhilip Paeps 	intr = &sc->intr;
535e948693eSPhilip Paeps 	esmp = &intr->status;
536e948693eSPhilip Paeps 
537e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_UNINITIALIZED,
538e948693eSPhilip Paeps 	    ("Interrupts already initialized"));
539e948693eSPhilip Paeps 
540e948693eSPhilip Paeps 	/* Try to setup MSI-X or MSI interrupts if available. */
541e948693eSPhilip Paeps 	if ((rc = sfxge_intr_setup_msix(sc)) == 0)
542e948693eSPhilip Paeps 		device_printf(dev, "Using MSI-X interrupts\n");
543e948693eSPhilip Paeps 	else if ((rc = sfxge_intr_setup_msi(sc)) == 0)
544e948693eSPhilip Paeps 		device_printf(dev, "Using MSI interrupts\n");
545e948693eSPhilip Paeps 	else if ((rc = sfxge_intr_setup_fixed(sc)) == 0) {
546e948693eSPhilip Paeps 		device_printf(dev, "Using fixed interrupts\n");
547e948693eSPhilip Paeps 	} else {
548e948693eSPhilip Paeps 		device_printf(dev, "Couldn't setup interrupts\n");
549e948693eSPhilip Paeps 		return (ENOMEM);
550e948693eSPhilip Paeps 	}
551e948693eSPhilip Paeps 
552e948693eSPhilip Paeps 	/* Set up DMA for interrupts. */
553e948693eSPhilip Paeps 	if ((rc = sfxge_dma_alloc(sc, EFX_INTR_SIZE, esmp)) != 0)
554e948693eSPhilip Paeps 		return (ENOMEM);
555e948693eSPhilip Paeps 
556e948693eSPhilip Paeps 	intr->state = SFXGE_INTR_INITIALIZED;
557e948693eSPhilip Paeps 
558e948693eSPhilip Paeps 	return (0);
559e948693eSPhilip Paeps }
560