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