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