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