2 * Copyright (c) 2010-2011 Solarflare Communications, Inc.
5 * This software was developed in part by Philip Paeps under contract for
6 * Solarflare Communications, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/queue.h>
40 #include <sys/syslog.h>
41 #include <sys/taskqueue.h>
43 #include <machine/bus.h>
44 #include <machine/resource.h>
46 #include <dev/pci/pcireg.h>
47 #include <dev/pci/pcivar.h>
49 #include "common/efx.h"
54 sfxge_intr_line_filter(void *arg)
56 struct sfxge_evq *evq;
57 struct sfxge_softc *sc;
59 struct sfxge_intr *intr;
63 evq = (struct sfxge_evq *)arg;
68 KASSERT(intr != NULL, ("intr == NULL"));
69 KASSERT(intr->type == EFX_INTR_LINE,
70 ("intr->type != EFX_INTR_LINE"));
72 if (intr->state != SFXGE_INTR_STARTED)
73 return (FILTER_STRAY);
75 (void)efx_intr_status_line(enp, &fatal, &qmask);
78 (void) efx_intr_disable(enp);
79 (void) efx_intr_fatal(enp);
80 return (FILTER_HANDLED);
85 return (FILTER_SCHEDULE_THREAD);
88 /* SF bug 15783: If the function is not asserting its IRQ and
89 * we read the queue mask on the cycle before a flag is added
90 * to the mask, this inhibits the function from asserting the
91 * IRQ even though we don't see the flag set. To work around
92 * this, we must re-prime all event queues and report the IRQ
93 * as handled when we see a mask of zero. To allow for shared
94 * IRQs, we don't repeat this if we see a mask of zero twice
97 if (intr->zero_count++ == 0) {
98 if (evq->init_state == SFXGE_EVQ_STARTED) {
99 if (efx_ev_qpending(evq->common, evq->read_ptr))
100 return (FILTER_SCHEDULE_THREAD);
101 efx_ev_qprime(evq->common, evq->read_ptr);
102 return (FILTER_HANDLED);
106 return (FILTER_STRAY);
110 sfxge_intr_line(void *arg)
112 struct sfxge_evq *evq = arg;
113 struct sfxge_softc *sc = evq->sc;
115 (void)sfxge_ev_qpoll(sc, 0);
119 sfxge_intr_message(void *arg)
121 struct sfxge_evq *evq;
122 struct sfxge_softc *sc;
124 struct sfxge_intr *intr;
128 evq = (struct sfxge_evq *)arg;
134 KASSERT(intr != NULL, ("intr == NULL"));
135 KASSERT(intr->type == EFX_INTR_MESSAGE,
136 ("intr->type != EFX_INTR_MESSAGE"));
138 if (intr->state != SFXGE_INTR_STARTED)
141 (void)efx_intr_status_message(enp, index, &fatal);
144 (void)efx_intr_disable(enp);
145 (void)efx_intr_fatal(enp);
149 (void)sfxge_ev_qpoll(sc, index);
153 sfxge_intr_bus_enable(struct sfxge_softc *sc)
155 struct sfxge_intr *intr;
156 struct sfxge_intr_hdl *table;
157 driver_filter_t *filter;
158 driver_intr_t *handler;
165 switch (intr->type) {
166 case EFX_INTR_MESSAGE:
167 filter = NULL; /* not shared */
168 handler = sfxge_intr_message;
172 filter = sfxge_intr_line_filter;
173 handler = sfxge_intr_line;
177 KASSERT(0, ("Invalid interrupt type"));
181 /* Try to add the handlers */
182 for (index = 0; index < intr->n_alloc; index++) {
183 if ((err = bus_setup_intr(sc->dev, table[index].eih_res,
184 INTR_MPSAFE|INTR_TYPE_NET, filter, handler,
185 sc->evq[index], &table[index].eih_tag)) != 0) {
188 #ifdef SFXGE_HAVE_DESCRIBE_INTR
189 if (intr->n_alloc > 1)
190 bus_describe_intr(sc->dev, table[index].eih_res,
191 table[index].eih_tag, "%d", index);
193 bus_bind_intr(sc->dev, table[index].eih_res, index);
200 /* Remove remaining handlers */
202 bus_teardown_intr(sc->dev, table[index].eih_res,
203 table[index].eih_tag);
209 sfxge_intr_bus_disable(struct sfxge_softc *sc)
211 struct sfxge_intr *intr;
212 struct sfxge_intr_hdl *table;
218 /* Remove all handlers */
219 for (i = 0; i < intr->n_alloc; i++)
220 bus_teardown_intr(sc->dev, table[i].eih_res,
225 sfxge_intr_alloc(struct sfxge_softc *sc, int count)
228 struct sfxge_intr_hdl *table;
229 struct sfxge_intr *intr;
230 struct resource *res;
239 table = malloc(count * sizeof(struct sfxge_intr_hdl),
243 for (i = 0; i < count; i++) {
245 res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
246 RF_SHAREABLE | RF_ACTIVE);
248 device_printf(dev, "Couldn't allocate interrupts for "
249 "message %d\n", rid);
253 table[i].eih_rid = rid;
254 table[i].eih_res = res;
259 for (i = 0; i < count; i++)
260 bus_release_resource(dev, SYS_RES_IRQ,
261 table[i].eih_rid, table[i].eih_res);
268 sfxge_intr_teardown_msix(struct sfxge_softc *sc)
271 struct resource *resp;
275 resp = sc->intr.msix_res;
277 rid = rman_get_rid(resp);
278 bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
282 sfxge_intr_setup_msix(struct sfxge_softc *sc)
284 struct sfxge_intr *intr;
285 struct resource *resp;
293 /* Check if MSI-X is available. */
294 count = pci_msix_count(dev);
298 /* Limit the number of interrupts to the number of CPUs. */
299 if (count > mp_ncpus)
302 /* Not very likely these days... */
303 if (count > EFX_MAXRSS)
307 resp = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
311 if (pci_alloc_msix(dev, &count) != 0) {
312 bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
316 /* Allocate interrupt handlers. */
317 if (sfxge_intr_alloc(sc, count) != 0) {
318 bus_release_resource(dev, SYS_RES_MEMORY, rid, resp);
319 pci_release_msi(dev);
323 intr->type = EFX_INTR_MESSAGE;
324 intr->n_alloc = count;
325 intr->msix_res = resp;
331 sfxge_intr_setup_msi(struct sfxge_softc *sc)
333 struct sfxge_intr_hdl *table;
334 struct sfxge_intr *intr;
344 * Check if MSI is available. All messages must be written to
345 * the same address and on x86 this means the IRQs have the
346 * same CPU affinity. So we only ever allocate 1.
348 count = pci_msi_count(dev) ? 1 : 0;
352 if ((error = pci_alloc_msi(dev, &count)) != 0)
355 /* Allocate interrupt handler. */
356 if (sfxge_intr_alloc(sc, count) != 0) {
357 pci_release_msi(dev);
361 intr->type = EFX_INTR_MESSAGE;
362 intr->n_alloc = count;
368 sfxge_intr_setup_fixed(struct sfxge_softc *sc)
370 struct sfxge_intr_hdl *table;
371 struct sfxge_intr *intr;
372 struct resource *res;
380 res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
381 RF_SHAREABLE | RF_ACTIVE);
385 table = malloc(sizeof(struct sfxge_intr_hdl), M_SFXGE, M_WAITOK);
386 table[0].eih_rid = rid;
387 table[0].eih_res = res;
389 intr->type = EFX_INTR_LINE;
396 static const char *const __sfxge_err[] = {
398 "SRAM out-of-bounds",
399 "Buffer ID out-of-bounds",
400 "Internal memory parity",
401 "Receive buffer ownership",
402 "Transmit buffer ownership",
403 "Receive descriptor ownership",
404 "Transmit descriptor ownership",
405 "Event queue ownership",
406 "Event queue FIFO overflow",
412 sfxge_err(efsys_identifier_t *arg, unsigned int code, uint32_t dword0,
415 struct sfxge_softc *sc = (struct sfxge_softc *)arg;
416 device_t dev = sc->dev;
418 log(LOG_WARNING, "[%s%d] FATAL ERROR: %s (0x%08x%08x)",
419 device_get_name(dev), device_get_unit(dev),
420 __sfxge_err[code], dword1, dword0);
424 sfxge_intr_stop(struct sfxge_softc *sc)
426 struct sfxge_intr *intr;
430 KASSERT(intr->state == SFXGE_INTR_STARTED,
431 ("Interrupts not started"));
433 intr->state = SFXGE_INTR_INITIALIZED;
435 /* Disable interrupts at the NIC */
436 efx_intr_disable(sc->enp);
438 /* Disable interrupts at the bus */
439 sfxge_intr_bus_disable(sc);
441 /* Tear down common code interrupt bits. */
442 efx_intr_fini(sc->enp);
446 sfxge_intr_start(struct sfxge_softc *sc)
448 struct sfxge_intr *intr;
453 esmp = &intr->status;
455 KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
456 ("Interrupts not initialized"));
458 /* Zero the memory. */
459 (void)memset(esmp->esm_base, 0, EFX_INTR_SIZE);
461 /* Initialize common code interrupt bits. */
462 (void)efx_intr_init(sc->enp, intr->type, esmp);
464 /* Enable interrupts at the bus */
465 if ((rc = sfxge_intr_bus_enable(sc)) != 0)
468 intr->state = SFXGE_INTR_STARTED;
470 /* Enable interrupts at the NIC */
471 efx_intr_enable(sc->enp);
476 /* Tear down common code interrupt bits. */
477 efx_intr_fini(sc->enp);
479 intr->state = SFXGE_INTR_INITIALIZED;
485 sfxge_intr_fini(struct sfxge_softc *sc)
487 struct sfxge_intr_hdl *table;
488 struct sfxge_intr *intr;
495 esmp = &intr->status;
498 KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
499 ("intr->state != SFXGE_INTR_INITIALIZED"));
501 /* Free DMA memory. */
502 sfxge_dma_free(esmp);
504 /* Free interrupt handles. */
505 for (i = 0; i < intr->n_alloc; i++)
506 bus_release_resource(dev, SYS_RES_IRQ,
507 table[i].eih_rid, table[i].eih_res);
509 if (table[0].eih_rid != 0)
510 pci_release_msi(dev);
512 if (intr->msix_res != NULL)
513 sfxge_intr_teardown_msix(sc);
515 /* Free the handle table */
516 free(table, M_SFXGE);
520 /* Clear the interrupt type */
521 intr->type = EFX_INTR_INVALID;
523 intr->state = SFXGE_INTR_UNINITIALIZED;
527 sfxge_intr_init(struct sfxge_softc *sc)
530 struct sfxge_intr *intr;
536 esmp = &intr->status;
538 KASSERT(intr->state == SFXGE_INTR_UNINITIALIZED,
539 ("Interrupts already initialized"));
541 /* Try to setup MSI-X or MSI interrupts if available. */
542 if ((rc = sfxge_intr_setup_msix(sc)) == 0)
543 device_printf(dev, "Using MSI-X interrupts\n");
544 else if ((rc = sfxge_intr_setup_msi(sc)) == 0)
545 device_printf(dev, "Using MSI interrupts\n");
546 else if ((rc = sfxge_intr_setup_fixed(sc)) == 0) {
547 device_printf(dev, "Using fixed interrupts\n");
549 device_printf(dev, "Couldn't setup interrupts\n");
553 /* Set up DMA for interrupts. */
554 if ((rc = sfxge_dma_alloc(sc, EFX_INTR_SIZE, esmp)) != 0)
557 intr->state = SFXGE_INTR_INITIALIZED;