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