16d87a65dSAndrew Gallatin /******************************************************************************
2*4d846d26SWarner Losh SPDX-License-Identifier: BSD-2-Clause
3b2fc195eSAndrew Gallatin
4cabc512fSAndrew Gallatin Copyright (c) 2006-2013, Myricom Inc.
5b2fc195eSAndrew Gallatin All rights reserved.
6b2fc195eSAndrew Gallatin
7b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
8b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
9b2fc195eSAndrew Gallatin
10b2fc195eSAndrew Gallatin 1. Redistributions of source code must retain the above copyright notice,
11b2fc195eSAndrew Gallatin this list of conditions and the following disclaimer.
12b2fc195eSAndrew Gallatin
13eb8e82f5SAndrew Gallatin 2. Neither the name of the Myricom Inc, nor the names of its
14b2fc195eSAndrew Gallatin contributors may be used to endorse or promote products derived from
15b2fc195eSAndrew Gallatin this software without specific prior written permission.
16b2fc195eSAndrew Gallatin
17b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
28b2fc195eSAndrew Gallatin
29b2fc195eSAndrew Gallatin ***************************************************************************/
30b2fc195eSAndrew Gallatin
31b2fc195eSAndrew Gallatin #include <sys/param.h>
32b2fc195eSAndrew Gallatin #include <sys/systm.h>
33b2fc195eSAndrew Gallatin #include <sys/linker.h>
34b2fc195eSAndrew Gallatin #include <sys/firmware.h>
35b2fc195eSAndrew Gallatin #include <sys/endian.h>
36b2fc195eSAndrew Gallatin #include <sys/sockio.h>
37b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
38b2fc195eSAndrew Gallatin #include <sys/malloc.h>
39b2fc195eSAndrew Gallatin #include <sys/kdb.h>
40b2fc195eSAndrew Gallatin #include <sys/kernel.h>
414e7f640dSJohn Baldwin #include <sys/lock.h>
42b2fc195eSAndrew Gallatin #include <sys/module.h>
43b2fc195eSAndrew Gallatin #include <sys/socket.h>
44b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
45b2fc195eSAndrew Gallatin #include <sys/sx.h>
4672c042dfSAndrew Gallatin #include <sys/taskqueue.h>
471dbf944aSXin LI #include <contrib/zlib/zlib.h>
481dbf944aSXin LI #include <dev/zlib/zcalloc.h>
49b2fc195eSAndrew Gallatin
50b2fc195eSAndrew Gallatin #include <net/if.h>
5176039bc8SGleb Smirnoff #include <net/if_var.h>
52b2fc195eSAndrew Gallatin #include <net/if_arp.h>
53b2fc195eSAndrew Gallatin #include <net/ethernet.h>
54b2fc195eSAndrew Gallatin #include <net/if_dl.h>
55b2fc195eSAndrew Gallatin #include <net/if_media.h>
56b2fc195eSAndrew Gallatin
57b2fc195eSAndrew Gallatin #include <net/bpf.h>
58b2fc195eSAndrew Gallatin
59b2fc195eSAndrew Gallatin #include <net/if_types.h>
60b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
61b2fc195eSAndrew Gallatin
62b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
63b2fc195eSAndrew Gallatin #include <netinet/in.h>
64b2fc195eSAndrew Gallatin #include <netinet/ip.h>
650a7a780eSAndrew Gallatin #include <netinet/ip6.h>
66aed8e389SAndrew Gallatin #include <netinet/tcp.h>
6726dd49c6SAndrew Gallatin #include <netinet/tcp_lro.h>
680a7a780eSAndrew Gallatin #include <netinet6/ip6_var.h>
69b2fc195eSAndrew Gallatin
70b2fc195eSAndrew Gallatin #include <machine/bus.h>
71053e637fSAndrew Gallatin #include <machine/in_cksum.h>
72b2fc195eSAndrew Gallatin #include <machine/resource.h>
73b2fc195eSAndrew Gallatin #include <sys/bus.h>
74b2fc195eSAndrew Gallatin #include <sys/rman.h>
751e413cf9SAndrew Gallatin #include <sys/smp.h>
76b2fc195eSAndrew Gallatin
77b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
78b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
79e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
80b2fc195eSAndrew Gallatin
81b2fc195eSAndrew Gallatin #include <vm/vm.h> /* for pmap_mapdev() */
82b2fc195eSAndrew Gallatin #include <vm/pmap.h>
83b2fc195eSAndrew Gallatin
84c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
85c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
86c2c14a69SAndrew Gallatin #endif
87c2c14a69SAndrew Gallatin
886d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
896d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
901e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
916d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
92869c7348SAndrew Gallatin #include <sys/buf_ring.h>
93b2fc195eSAndrew Gallatin
94eb6219e3SAndrew Gallatin #include "opt_inet.h"
950a7a780eSAndrew Gallatin #include "opt_inet6.h"
96eb6219e3SAndrew Gallatin
97b2fc195eSAndrew Gallatin /* tunable params */
986d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
99d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
1006d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1015e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1026d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1035e7d8541SAndrew Gallatin static int mxge_verbose = 0;
104dce01b9bSAndrew Gallatin static int mxge_ticks;
1051e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1065769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
1071e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
108f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
10965c69066SAndrew Gallatin static int mxge_throttle = 0;
1106d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1116d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1121e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1131e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
114b2fc195eSAndrew Gallatin
1156d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1166d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1176d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1186d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1196d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
120b2fc195eSAndrew Gallatin
1216d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
122b2fc195eSAndrew Gallatin {
123b2fc195eSAndrew Gallatin /* Device interface */
1246d87a65dSAndrew Gallatin DEVMETHOD(device_probe, mxge_probe),
1256d87a65dSAndrew Gallatin DEVMETHOD(device_attach, mxge_attach),
1266d87a65dSAndrew Gallatin DEVMETHOD(device_detach, mxge_detach),
1276d87a65dSAndrew Gallatin DEVMETHOD(device_shutdown, mxge_shutdown),
12861bfd867SSofian Brabez
12961bfd867SSofian Brabez DEVMETHOD_END
130b2fc195eSAndrew Gallatin };
131b2fc195eSAndrew Gallatin
1326d87a65dSAndrew Gallatin static driver_t mxge_driver =
133b2fc195eSAndrew Gallatin {
1346d87a65dSAndrew Gallatin "mxge",
1356d87a65dSAndrew Gallatin mxge_methods,
1366d87a65dSAndrew Gallatin sizeof(mxge_softc_t),
137b2fc195eSAndrew Gallatin };
138b2fc195eSAndrew Gallatin
139b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
14099b29e34SJohn Baldwin DRIVER_MODULE(mxge, pci, mxge_driver, 0, 0);
1416d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
142f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
143b2fc195eSAndrew Gallatin
1441e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1458fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
146a393336bSAndrew Gallatin static int mxge_close(mxge_softc_t *sc, int down);
147276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
148276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1498fe615baSAndrew Gallatin
150b2fc195eSAndrew Gallatin static int
mxge_probe(device_t dev)1516d87a65dSAndrew Gallatin mxge_probe(device_t dev)
152b2fc195eSAndrew Gallatin {
15301638550SAndrew Gallatin int rev;
15401638550SAndrew Gallatin
1556d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
156f1544498SAndrew Gallatin ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
157f1544498SAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
15801638550SAndrew Gallatin rev = pci_get_revid(dev);
15901638550SAndrew Gallatin switch (rev) {
16001638550SAndrew Gallatin case MXGE_PCI_REV_Z8E:
161b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A");
16201638550SAndrew Gallatin break;
16301638550SAndrew Gallatin case MXGE_PCI_REV_Z8ES:
16401638550SAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8B");
16501638550SAndrew Gallatin break;
16601638550SAndrew Gallatin default:
16701638550SAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8??");
16801638550SAndrew Gallatin device_printf(dev, "Unrecognized rev %d NIC\n",
16901638550SAndrew Gallatin rev);
17001638550SAndrew Gallatin break;
17101638550SAndrew Gallatin }
172b2fc195eSAndrew Gallatin return 0;
173b2fc195eSAndrew Gallatin }
174b2fc195eSAndrew Gallatin return ENXIO;
175b2fc195eSAndrew Gallatin }
176b2fc195eSAndrew Gallatin
177b2fc195eSAndrew Gallatin static void
mxge_enable_wc(mxge_softc_t * sc)1786d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
179b2fc195eSAndrew Gallatin {
180f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
181b2fc195eSAndrew Gallatin vm_offset_t len;
18247c2e987SAndrew Gallatin int err;
183b2fc195eSAndrew Gallatin
1844d69a9d0SAndrew Gallatin sc->wc = 1;
185b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res);
186c2c14a69SAndrew Gallatin err = pmap_change_attr((vm_offset_t) sc->sram,
187c2c14a69SAndrew Gallatin len, PAT_WRITE_COMBINING);
18847c2e987SAndrew Gallatin if (err != 0) {
189c2c14a69SAndrew Gallatin device_printf(sc->dev, "pmap_change_attr failed, %d\n",
190c2c14a69SAndrew Gallatin err);
1914d69a9d0SAndrew Gallatin sc->wc = 0;
192b2fc195eSAndrew Gallatin }
193f9ae0280SAndrew Gallatin #endif
194b2fc195eSAndrew Gallatin }
195b2fc195eSAndrew Gallatin
196b2fc195eSAndrew Gallatin /* callback to get our DMA address */
197b2fc195eSAndrew Gallatin static void
mxge_dmamap_callback(void * arg,bus_dma_segment_t * segs,int nsegs,int error)1986d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
199b2fc195eSAndrew Gallatin int error)
200b2fc195eSAndrew Gallatin {
201b2fc195eSAndrew Gallatin if (error == 0) {
202b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr;
203b2fc195eSAndrew Gallatin }
204b2fc195eSAndrew Gallatin }
205b2fc195eSAndrew Gallatin
206b2fc195eSAndrew Gallatin static int
mxge_dma_alloc(mxge_softc_t * sc,mxge_dma_t * dma,size_t bytes,bus_size_t alignment)2076d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
208b2fc195eSAndrew Gallatin bus_size_t alignment)
209b2fc195eSAndrew Gallatin {
210b2fc195eSAndrew Gallatin int err;
211b2fc195eSAndrew Gallatin device_t dev = sc->dev;
2121e413cf9SAndrew Gallatin bus_size_t boundary, maxsegsize;
2131e413cf9SAndrew Gallatin
2141e413cf9SAndrew Gallatin if (bytes > 4096 && alignment == 4096) {
2151e413cf9SAndrew Gallatin boundary = 0;
2161e413cf9SAndrew Gallatin maxsegsize = bytes;
2171e413cf9SAndrew Gallatin } else {
2181e413cf9SAndrew Gallatin boundary = 4096;
2191e413cf9SAndrew Gallatin maxsegsize = 4096;
2201e413cf9SAndrew Gallatin }
221b2fc195eSAndrew Gallatin
222b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */
223b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */
224b2fc195eSAndrew Gallatin alignment, /* alignment */
2251e413cf9SAndrew Gallatin boundary, /* boundary */
226b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */
227b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */
228b2fc195eSAndrew Gallatin NULL, NULL, /* filter */
229b2fc195eSAndrew Gallatin bytes, /* maxsize */
230b2fc195eSAndrew Gallatin 1, /* num segs */
2311e413cf9SAndrew Gallatin maxsegsize, /* maxsegsize */
232b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */
233b2fc195eSAndrew Gallatin NULL, NULL, /* lock */
234b2fc195eSAndrew Gallatin &dma->dmat); /* tag */
235b2fc195eSAndrew Gallatin if (err != 0) {
236b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
237b2fc195eSAndrew Gallatin return err;
238b2fc195eSAndrew Gallatin }
239b2fc195eSAndrew Gallatin
240b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */
241b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr,
242b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT
243b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map);
244b2fc195eSAndrew Gallatin if (err != 0) {
245b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
246b2fc195eSAndrew Gallatin goto abort_with_dmat;
247b2fc195eSAndrew Gallatin }
248b2fc195eSAndrew Gallatin
249b2fc195eSAndrew Gallatin /* load the memory */
250b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2516d87a65dSAndrew Gallatin mxge_dmamap_callback,
252b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0);
253b2fc195eSAndrew Gallatin if (err != 0) {
254b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err);
255b2fc195eSAndrew Gallatin goto abort_with_mem;
256b2fc195eSAndrew Gallatin }
257b2fc195eSAndrew Gallatin return 0;
258b2fc195eSAndrew Gallatin
259b2fc195eSAndrew Gallatin abort_with_mem:
260b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map);
261b2fc195eSAndrew Gallatin abort_with_dmat:
262b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat);
263b2fc195eSAndrew Gallatin return err;
264b2fc195eSAndrew Gallatin }
265b2fc195eSAndrew Gallatin
266b2fc195eSAndrew Gallatin static void
mxge_dma_free(mxge_dma_t * dma)2676d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
268b2fc195eSAndrew Gallatin {
269b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map);
270b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map);
271b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat);
272b2fc195eSAndrew Gallatin }
273b2fc195eSAndrew Gallatin
274b2fc195eSAndrew Gallatin /*
275b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format
276b2fc195eSAndrew Gallatin * SN=x\0
277b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0
278b2fc195eSAndrew Gallatin * PC=text\0
279b2fc195eSAndrew Gallatin */
280b2fc195eSAndrew Gallatin
281b2fc195eSAndrew Gallatin static int
mxge_parse_strings(mxge_softc_t * sc)2826d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
283b2fc195eSAndrew Gallatin {
284dedbe836SAndrew Gallatin char *ptr;
285a4b233ddSAndrew Gallatin int i, found_mac, found_sn2;
286dedbe836SAndrew Gallatin char *endptr;
287b2fc195eSAndrew Gallatin
288b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings;
289b2fc195eSAndrew Gallatin found_mac = 0;
290a4b233ddSAndrew Gallatin found_sn2 = 0;
291dedbe836SAndrew Gallatin while (*ptr != '\0') {
292dedbe836SAndrew Gallatin if (strncmp(ptr, "MAC=", 4) == 0) {
293dedbe836SAndrew Gallatin ptr += 4;
294dedbe836SAndrew Gallatin for (i = 0;;) {
295dedbe836SAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
296dedbe836SAndrew Gallatin if (endptr - ptr != 2)
297b2fc195eSAndrew Gallatin goto abort;
298dedbe836SAndrew Gallatin ptr = endptr;
299dedbe836SAndrew Gallatin if (++i == 6)
300dedbe836SAndrew Gallatin break;
301dedbe836SAndrew Gallatin if (*ptr++ != ':')
302dedbe836SAndrew Gallatin goto abort;
303b2fc195eSAndrew Gallatin }
304dedbe836SAndrew Gallatin found_mac = 1;
305dedbe836SAndrew Gallatin } else if (strncmp(ptr, "PC=", 3) == 0) {
3065e7d8541SAndrew Gallatin ptr += 3;
307dedbe836SAndrew Gallatin strlcpy(sc->product_code_string, ptr,
308dedbe836SAndrew Gallatin sizeof(sc->product_code_string));
309dedbe836SAndrew Gallatin } else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
3105e7d8541SAndrew Gallatin ptr += 3;
311dedbe836SAndrew Gallatin strlcpy(sc->serial_number_string, ptr,
312dedbe836SAndrew Gallatin sizeof(sc->serial_number_string));
313dedbe836SAndrew Gallatin } else if (strncmp(ptr, "SN2=", 4) == 0) {
314a4b233ddSAndrew Gallatin /* SN2 takes precedence over SN */
315a4b233ddSAndrew Gallatin ptr += 4;
316a4b233ddSAndrew Gallatin found_sn2 = 1;
317dedbe836SAndrew Gallatin strlcpy(sc->serial_number_string, ptr,
318dedbe836SAndrew Gallatin sizeof(sc->serial_number_string));
319b2fc195eSAndrew Gallatin }
320dedbe836SAndrew Gallatin while (*ptr++ != '\0') {}
321b2fc195eSAndrew Gallatin }
322b2fc195eSAndrew Gallatin
323b2fc195eSAndrew Gallatin if (found_mac)
324b2fc195eSAndrew Gallatin return 0;
325b2fc195eSAndrew Gallatin
326b2fc195eSAndrew Gallatin abort:
327b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n");
328b2fc195eSAndrew Gallatin
329b2fc195eSAndrew Gallatin return ENXIO;
330b2fc195eSAndrew Gallatin }
331b2fc195eSAndrew Gallatin
3320d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3338fe615baSAndrew Gallatin static void
mxge_enable_nvidia_ecrc(mxge_softc_t * sc)3348fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
335b2fc195eSAndrew Gallatin {
336b2fc195eSAndrew Gallatin uint32_t val;
3378fe615baSAndrew Gallatin unsigned long base, off;
338b2fc195eSAndrew Gallatin char *va, *cfgptr;
3398fe615baSAndrew Gallatin device_t pdev, mcp55;
3408fe615baSAndrew Gallatin uint16_t vendor_id, device_id, word;
341b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev;
342b2fc195eSAndrew Gallatin uint32_t *ptr32;
343b2fc195eSAndrew Gallatin
3448fe615baSAndrew Gallatin if (!mxge_nvidia_ecrc_enable)
3458fe615baSAndrew Gallatin return;
3468fe615baSAndrew Gallatin
3478fe615baSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev));
3488fe615baSAndrew Gallatin if (pdev == NULL) {
3498fe615baSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n");
3508fe615baSAndrew Gallatin return;
3518fe615baSAndrew Gallatin }
3528fe615baSAndrew Gallatin vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3538fe615baSAndrew Gallatin device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3548fe615baSAndrew Gallatin
3558fe615baSAndrew Gallatin if (vendor_id != 0x10de)
3568fe615baSAndrew Gallatin return;
3578fe615baSAndrew Gallatin
3588fe615baSAndrew Gallatin base = 0;
3598fe615baSAndrew Gallatin
3608fe615baSAndrew Gallatin if (device_id == 0x005d) {
3618fe615baSAndrew Gallatin /* ck804, base address is magic */
3628fe615baSAndrew Gallatin base = 0xe0000000UL;
3638fe615baSAndrew Gallatin } else if (device_id >= 0x0374 && device_id <= 0x378) {
3648fe615baSAndrew Gallatin /* mcp55, base address stored in chipset */
3658fe615baSAndrew Gallatin mcp55 = pci_find_bsf(0, 0, 0);
3668fe615baSAndrew Gallatin if (mcp55 &&
3678fe615baSAndrew Gallatin 0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3688fe615baSAndrew Gallatin 0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3698fe615baSAndrew Gallatin word = pci_read_config(mcp55, 0x90, 2);
3708fe615baSAndrew Gallatin base = ((unsigned long)word & 0x7ffeU) << 25;
3718fe615baSAndrew Gallatin }
3728fe615baSAndrew Gallatin }
3738fe615baSAndrew Gallatin if (!base)
3748fe615baSAndrew Gallatin return;
3758fe615baSAndrew Gallatin
376b2fc195eSAndrew Gallatin /* XXXX
377b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing
378b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space
379b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove
380b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when
381b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access
382b2fc195eSAndrew Gallatin */
383b2fc195eSAndrew Gallatin #if 0
384b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended
385b2fc195eSAndrew Gallatin config space */
386b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4);
387b2fc195eSAndrew Gallatin if (val != 0xffffffff) {
388b2fc195eSAndrew Gallatin val |= 0x40;
389b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4);
3908fe615baSAndrew Gallatin return;
391b2fc195eSAndrew Gallatin }
392b2fc195eSAndrew Gallatin #endif
393b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must
394b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on
395b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is
396b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI
397b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge
398b2fc195eSAndrew Gallatin * and things behind them are not visible by this method.
399b2fc195eSAndrew Gallatin */
400b2fc195eSAndrew Gallatin
401b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev,
402b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus);
403b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev,
404b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot);
405b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev,
406b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func);
407b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev,
408b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend);
409b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev,
410b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev);
411b2fc195eSAndrew Gallatin
4128fe615baSAndrew Gallatin off = base
413b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus
414b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func
415b2fc195eSAndrew Gallatin + 8 * slot);
416b2fc195eSAndrew Gallatin
417b2fc195eSAndrew Gallatin /* map it into the kernel */
418b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
419b2fc195eSAndrew Gallatin
420b2fc195eSAndrew Gallatin if (va == NULL) {
421b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4228fe615baSAndrew Gallatin return;
423b2fc195eSAndrew Gallatin }
424b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */
425b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK);
426b2fc195eSAndrew Gallatin
427b2fc195eSAndrew Gallatin /* make sure that we can really access it */
428b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
429b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
430b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) {
431b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
432b2fc195eSAndrew Gallatin vendor_id, device_id);
4337ae99f80SJohn Baldwin pmap_unmapdev(va, PAGE_SIZE);
4348fe615baSAndrew Gallatin return;
435b2fc195eSAndrew Gallatin }
436b2fc195eSAndrew Gallatin
437b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178);
438b2fc195eSAndrew Gallatin val = *ptr32;
439b2fc195eSAndrew Gallatin
440b2fc195eSAndrew Gallatin if (val == 0xffffffff) {
441b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n");
4427ae99f80SJohn Baldwin pmap_unmapdev(va, PAGE_SIZE);
4438fe615baSAndrew Gallatin return;
444b2fc195eSAndrew Gallatin }
445b2fc195eSAndrew Gallatin *ptr32 = val | 0x40;
4467ae99f80SJohn Baldwin pmap_unmapdev(va, PAGE_SIZE);
4475e7d8541SAndrew Gallatin if (mxge_verbose)
448b2fc195eSAndrew Gallatin device_printf(sc->dev,
4495e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge "
4505e7d8541SAndrew Gallatin "at %d:%d:%d\n",
451b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func);
4528fe615baSAndrew Gallatin return;
453b2fc195eSAndrew Gallatin }
454b2fc195eSAndrew Gallatin #else
4558fe615baSAndrew Gallatin static void
mxge_enable_nvidia_ecrc(mxge_softc_t * sc)456f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
457b2fc195eSAndrew Gallatin {
458b2fc195eSAndrew Gallatin device_printf(sc->dev,
459b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4608fe615baSAndrew Gallatin return;
461b2fc195eSAndrew Gallatin }
462b2fc195eSAndrew Gallatin #endif
4638fe615baSAndrew Gallatin
4648fe615baSAndrew Gallatin static int
mxge_dma_test(mxge_softc_t * sc,int test_type)4658fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4668fe615baSAndrew Gallatin {
4678fe615baSAndrew Gallatin mxge_cmd_t cmd;
4688fe615baSAndrew Gallatin bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4698fe615baSAndrew Gallatin int status;
4708fe615baSAndrew Gallatin uint32_t len;
4718fe615baSAndrew Gallatin char *test = " ";
4728fe615baSAndrew Gallatin
4738fe615baSAndrew Gallatin /* Run a small DMA test.
4748fe615baSAndrew Gallatin * The magic multipliers to the length tell the firmware
4758fe615baSAndrew Gallatin * to do DMA read, write, or read+write tests. The
4768fe615baSAndrew Gallatin * results are returned in cmd.data0. The upper 16
4778fe615baSAndrew Gallatin * bits of the return is the number of transfers completed.
4788fe615baSAndrew Gallatin * The lower 16 bits is the time in 0.5us ticks that the
4798fe615baSAndrew Gallatin * transfers took to complete.
4808fe615baSAndrew Gallatin */
4818fe615baSAndrew Gallatin
4821e413cf9SAndrew Gallatin len = sc->tx_boundary;
4838fe615baSAndrew Gallatin
4848fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4858fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4868fe615baSAndrew Gallatin cmd.data2 = len * 0x10000;
4878fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd);
4888fe615baSAndrew Gallatin if (status != 0) {
4898fe615baSAndrew Gallatin test = "read";
4908fe615baSAndrew Gallatin goto abort;
4918fe615baSAndrew Gallatin }
4928fe615baSAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * len * 2) /
4938fe615baSAndrew Gallatin (cmd.data0 & 0xffff);
4948fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4958fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4968fe615baSAndrew Gallatin cmd.data2 = len * 0x1;
4978fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd);
4988fe615baSAndrew Gallatin if (status != 0) {
4998fe615baSAndrew Gallatin test = "write";
5008fe615baSAndrew Gallatin goto abort;
5018fe615baSAndrew Gallatin }
5028fe615baSAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * len * 2) /
5038fe615baSAndrew Gallatin (cmd.data0 & 0xffff);
5048fe615baSAndrew Gallatin
5058fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5068fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5078fe615baSAndrew Gallatin cmd.data2 = len * 0x10001;
5088fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd);
5098fe615baSAndrew Gallatin if (status != 0) {
5108fe615baSAndrew Gallatin test = "read/write";
5118fe615baSAndrew Gallatin goto abort;
5128fe615baSAndrew Gallatin }
5138fe615baSAndrew Gallatin sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5148fe615baSAndrew Gallatin (cmd.data0 & 0xffff);
5158fe615baSAndrew Gallatin
5168fe615baSAndrew Gallatin abort:
5178fe615baSAndrew Gallatin if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5188fe615baSAndrew Gallatin device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5198fe615baSAndrew Gallatin test, status);
5208fe615baSAndrew Gallatin
5218fe615baSAndrew Gallatin return status;
5228fe615baSAndrew Gallatin }
5238fe615baSAndrew Gallatin
524b2fc195eSAndrew Gallatin /*
525b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
526b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte
527b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on
528b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling
529b2fc195eSAndrew Gallatin * ECRC generation (if supported).
530b2fc195eSAndrew Gallatin *
531b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more
532b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
533b2fc195eSAndrew Gallatin *
534b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has
535b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works
536b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should
537b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is
5381e413cf9SAndrew Gallatin * larger than 2KB by setting the tx_boundary to 2KB. If ECRC is
539b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat)
5401e413cf9SAndrew Gallatin * firmware image, and set tx_boundary to 4KB.
541b2fc195eSAndrew Gallatin */
542b2fc195eSAndrew Gallatin
5438fe615baSAndrew Gallatin static int
mxge_firmware_probe(mxge_softc_t * sc)5448fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5458fe615baSAndrew Gallatin {
5468fe615baSAndrew Gallatin device_t dev = sc->dev;
5478fe615baSAndrew Gallatin int reg, status;
5488fe615baSAndrew Gallatin uint16_t pectl;
5498fe615baSAndrew Gallatin
5501e413cf9SAndrew Gallatin sc->tx_boundary = 4096;
5518fe615baSAndrew Gallatin /*
5528fe615baSAndrew Gallatin * Verify the max read request size was set to 4KB
5538fe615baSAndrew Gallatin * before trying the test with 4KB.
5548fe615baSAndrew Gallatin */
5553b0a4aefSJohn Baldwin if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) {
5568fe615baSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2);
5578fe615baSAndrew Gallatin if ((pectl & (5 << 12)) != (5 << 12)) {
5588fe615baSAndrew Gallatin device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5598fe615baSAndrew Gallatin pectl);
5601e413cf9SAndrew Gallatin sc->tx_boundary = 2048;
5618fe615baSAndrew Gallatin }
5628fe615baSAndrew Gallatin }
5638fe615baSAndrew Gallatin
5648fe615baSAndrew Gallatin /*
5658fe615baSAndrew Gallatin * load the optimized firmware (which assumes aligned PCIe
5668fe615baSAndrew Gallatin * completions) in order to see if it works on this host.
5678fe615baSAndrew Gallatin */
5688fe615baSAndrew Gallatin sc->fw_name = mxge_fw_aligned;
5691e413cf9SAndrew Gallatin status = mxge_load_firmware(sc, 1);
5708fe615baSAndrew Gallatin if (status != 0) {
5718fe615baSAndrew Gallatin return status;
5728fe615baSAndrew Gallatin }
5738fe615baSAndrew Gallatin
5748fe615baSAndrew Gallatin /*
5758fe615baSAndrew Gallatin * Enable ECRC if possible
5768fe615baSAndrew Gallatin */
5778fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(sc);
5788fe615baSAndrew Gallatin
5798fe615baSAndrew Gallatin /*
5808fe615baSAndrew Gallatin * Run a DMA test which watches for unaligned completions and
581a4b233ddSAndrew Gallatin * aborts on the first one seen. Not required on Z8ES or newer.
5828fe615baSAndrew Gallatin */
583a4b233ddSAndrew Gallatin if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
584a4b233ddSAndrew Gallatin return 0;
5858fe615baSAndrew Gallatin status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5868fe615baSAndrew Gallatin if (status == 0)
5878fe615baSAndrew Gallatin return 0; /* keep the aligned firmware */
5888fe615baSAndrew Gallatin
5898fe615baSAndrew Gallatin if (status != E2BIG)
5908fe615baSAndrew Gallatin device_printf(dev, "DMA test failed: %d\n", status);
5918fe615baSAndrew Gallatin if (status == ENOSYS)
5928fe615baSAndrew Gallatin device_printf(dev, "Falling back to ethp! "
5938fe615baSAndrew Gallatin "Please install up to date fw\n");
5948fe615baSAndrew Gallatin return status;
5958fe615baSAndrew Gallatin }
5968fe615baSAndrew Gallatin
5978fe615baSAndrew Gallatin static int
mxge_select_firmware(mxge_softc_t * sc)5986d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
599b2fc195eSAndrew Gallatin {
6008fe615baSAndrew Gallatin int aligned = 0;
60165c69066SAndrew Gallatin int force_firmware = mxge_force_firmware;
602b2fc195eSAndrew Gallatin
60365c69066SAndrew Gallatin if (sc->throttle)
60465c69066SAndrew Gallatin force_firmware = sc->throttle;
605d91b1b49SAndrew Gallatin
60665c69066SAndrew Gallatin if (force_firmware != 0) {
60765c69066SAndrew Gallatin if (force_firmware == 1)
608d91b1b49SAndrew Gallatin aligned = 1;
609d91b1b49SAndrew Gallatin else
610d91b1b49SAndrew Gallatin aligned = 0;
611d91b1b49SAndrew Gallatin if (mxge_verbose)
612d91b1b49SAndrew Gallatin device_printf(sc->dev,
613d91b1b49SAndrew Gallatin "Assuming %s completions (forced)\n",
614d91b1b49SAndrew Gallatin aligned ? "aligned" : "unaligned");
615d91b1b49SAndrew Gallatin goto abort;
616d91b1b49SAndrew Gallatin }
617d91b1b49SAndrew Gallatin
618d91b1b49SAndrew Gallatin /* if the PCIe link width is 4 or less, we can use the aligned
619d91b1b49SAndrew Gallatin firmware and skip any checks */
620d91b1b49SAndrew Gallatin if (sc->link_width != 0 && sc->link_width <= 4) {
621d91b1b49SAndrew Gallatin device_printf(sc->dev,
622d91b1b49SAndrew Gallatin "PCIe x%d Link, expect reduced performance\n",
623d91b1b49SAndrew Gallatin sc->link_width);
624d91b1b49SAndrew Gallatin aligned = 1;
625d91b1b49SAndrew Gallatin goto abort;
626d91b1b49SAndrew Gallatin }
627d91b1b49SAndrew Gallatin
6288fe615baSAndrew Gallatin if (0 == mxge_firmware_probe(sc))
6298fe615baSAndrew Gallatin return 0;
630b2fc195eSAndrew Gallatin
631b2fc195eSAndrew Gallatin abort:
632b2fc195eSAndrew Gallatin if (aligned) {
6336d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned;
6341e413cf9SAndrew Gallatin sc->tx_boundary = 4096;
635b2fc195eSAndrew Gallatin } else {
6366d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned;
6371e413cf9SAndrew Gallatin sc->tx_boundary = 2048;
638b2fc195eSAndrew Gallatin }
6391e413cf9SAndrew Gallatin return (mxge_load_firmware(sc, 0));
640b2fc195eSAndrew Gallatin }
641b2fc195eSAndrew Gallatin
6424da0d523SAndrew Gallatin static int
mxge_validate_firmware(mxge_softc_t * sc,const mcp_gen_header_t * hdr)6434da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6444da0d523SAndrew Gallatin {
645b824b7d8SAndrew Gallatin
6464da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6474da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6484da0d523SAndrew Gallatin be32toh(hdr->mcp_type));
6494da0d523SAndrew Gallatin return EIO;
6504da0d523SAndrew Gallatin }
6514da0d523SAndrew Gallatin
6524da0d523SAndrew Gallatin /* save firmware version for sysctl */
653dedbe836SAndrew Gallatin strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6544da0d523SAndrew Gallatin if (mxge_verbose)
6554da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6564da0d523SAndrew Gallatin
657b824b7d8SAndrew Gallatin sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
658b824b7d8SAndrew Gallatin &sc->fw_ver_minor, &sc->fw_ver_tiny);
6594da0d523SAndrew Gallatin
660b824b7d8SAndrew Gallatin if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
661b824b7d8SAndrew Gallatin && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6624da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n",
6634da0d523SAndrew Gallatin sc->fw_version);
6644da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n",
6654da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6664da0d523SAndrew Gallatin return EINVAL;
6674da0d523SAndrew Gallatin }
6684da0d523SAndrew Gallatin return 0;
6694da0d523SAndrew Gallatin
6704da0d523SAndrew Gallatin }
671b2fc195eSAndrew Gallatin
672b2fc195eSAndrew Gallatin static int
mxge_load_firmware_helper(mxge_softc_t * sc,uint32_t * limit)6736d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
674b2fc195eSAndrew Gallatin {
675f9ae0280SAndrew Gallatin z_stream zs;
676f9ae0280SAndrew Gallatin char *inflate_buffer;
67733d54970SLuigi Rizzo const struct firmware *fw;
678b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr;
679b2fc195eSAndrew Gallatin unsigned hdr_offset;
680b2fc195eSAndrew Gallatin int status;
6814da0d523SAndrew Gallatin unsigned int i;
682f9ae0280SAndrew Gallatin size_t fw_len;
683b2fc195eSAndrew Gallatin
684b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name);
685b2fc195eSAndrew Gallatin if (fw == NULL) {
686b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n",
687b2fc195eSAndrew Gallatin sc->fw_name);
688b2fc195eSAndrew Gallatin return ENOENT;
689b2fc195eSAndrew Gallatin }
690b2fc195eSAndrew Gallatin
691f9ae0280SAndrew Gallatin /* setup zlib and decompress f/w */
692f9ae0280SAndrew Gallatin bzero(&zs, sizeof (zs));
6931dbf944aSXin LI zs.zalloc = zcalloc_nowait;
6941dbf944aSXin LI zs.zfree = zcfree;
695f9ae0280SAndrew Gallatin status = inflateInit(&zs);
696f9ae0280SAndrew Gallatin if (status != Z_OK) {
697b2fc195eSAndrew Gallatin status = EIO;
698b2fc195eSAndrew Gallatin goto abort_with_fw;
699b2fc195eSAndrew Gallatin }
700f9ae0280SAndrew Gallatin
701f9ae0280SAndrew Gallatin /* the uncompressed size is stored as the firmware version,
702f9ae0280SAndrew Gallatin which would otherwise go unused */
703f9ae0280SAndrew Gallatin fw_len = (size_t) fw->version;
704f9ae0280SAndrew Gallatin inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
705f9ae0280SAndrew Gallatin if (inflate_buffer == NULL)
706f9ae0280SAndrew Gallatin goto abort_with_zs;
707f9ae0280SAndrew Gallatin zs.avail_in = fw->datasize;
708f9ae0280SAndrew Gallatin zs.next_in = __DECONST(char *, fw->data);
709f9ae0280SAndrew Gallatin zs.avail_out = fw_len;
710f9ae0280SAndrew Gallatin zs.next_out = inflate_buffer;
711f9ae0280SAndrew Gallatin status = inflate(&zs, Z_FINISH);
712f9ae0280SAndrew Gallatin if (status != Z_STREAM_END) {
713f9ae0280SAndrew Gallatin device_printf(sc->dev, "zlib %d\n", status);
714f9ae0280SAndrew Gallatin status = EIO;
715f9ae0280SAndrew Gallatin goto abort_with_buffer;
716f9ae0280SAndrew Gallatin }
717f9ae0280SAndrew Gallatin
718f9ae0280SAndrew Gallatin /* check id */
719f9ae0280SAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *)
720f9ae0280SAndrew Gallatin (inflate_buffer + MCP_HEADER_PTR_OFFSET));
721f9ae0280SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
722f9ae0280SAndrew Gallatin device_printf(sc->dev, "Bad firmware file");
723f9ae0280SAndrew Gallatin status = EIO;
724f9ae0280SAndrew Gallatin goto abort_with_buffer;
725f9ae0280SAndrew Gallatin }
726f9ae0280SAndrew Gallatin hdr = (const void*)(inflate_buffer + hdr_offset);
727b2fc195eSAndrew Gallatin
7284da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr);
7294da0d523SAndrew Gallatin if (status != 0)
730f9ae0280SAndrew Gallatin goto abort_with_buffer;
731b2fc195eSAndrew Gallatin
732b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */
733f9ae0280SAndrew Gallatin for (i = 0; i < fw_len; i += 256) {
7344da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
735f9ae0280SAndrew Gallatin inflate_buffer + i,
736f9ae0280SAndrew Gallatin min(256U, (unsigned)(fw_len - i)));
73773c7c83fSAndrew Gallatin wmb();
738b08a56adSJohn Baldwin (void)*sc->sram;
73973c7c83fSAndrew Gallatin wmb();
7404da0d523SAndrew Gallatin }
741b2fc195eSAndrew Gallatin
742f9ae0280SAndrew Gallatin *limit = fw_len;
743b2fc195eSAndrew Gallatin status = 0;
744f9ae0280SAndrew Gallatin abort_with_buffer:
745f9ae0280SAndrew Gallatin free(inflate_buffer, M_TEMP);
746f9ae0280SAndrew Gallatin abort_with_zs:
747f9ae0280SAndrew Gallatin inflateEnd(&zs);
748b2fc195eSAndrew Gallatin abort_with_fw:
749b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD);
750b2fc195eSAndrew Gallatin return status;
751b2fc195eSAndrew Gallatin }
752b2fc195eSAndrew Gallatin
753b2fc195eSAndrew Gallatin /*
754b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain
755b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages
756b2fc195eSAndrew Gallatin */
757b2fc195eSAndrew Gallatin
758b2fc195eSAndrew Gallatin static void
mxge_dummy_rdma(mxge_softc_t * sc,int enable)7596d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
760b2fc195eSAndrew Gallatin {
761b2fc195eSAndrew Gallatin char buf_bytes[72];
762b2fc195eSAndrew Gallatin volatile uint32_t *confirm;
763b2fc195eSAndrew Gallatin volatile char *submit;
764b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high;
765b2fc195eSAndrew Gallatin int i;
766b2fc195eSAndrew Gallatin
767aa54c242SJohn Baldwin buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
768b2fc195eSAndrew Gallatin
769b2fc195eSAndrew Gallatin /* clear confirmation addr */
770b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd;
771b2fc195eSAndrew Gallatin *confirm = 0;
77273c7c83fSAndrew Gallatin wmb();
773b2fc195eSAndrew Gallatin
774b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the
775b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should
776b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well
777b2fc195eSAndrew Gallatin */
778b2fc195eSAndrew Gallatin
7796d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7806d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
781b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */
782b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */
783b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */
7846d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7856d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
786b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */
787b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */
788b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */
789b2fc195eSAndrew Gallatin
7900fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
791b2fc195eSAndrew Gallatin
7926d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64);
79373c7c83fSAndrew Gallatin wmb();
794b2fc195eSAndrew Gallatin DELAY(1000);
79573c7c83fSAndrew Gallatin wmb();
796b2fc195eSAndrew Gallatin i = 0;
797b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) {
798b2fc195eSAndrew Gallatin DELAY(1000);
799b2fc195eSAndrew Gallatin i++;
800b2fc195eSAndrew Gallatin }
801b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) {
802b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
803b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm,
804b2fc195eSAndrew Gallatin *confirm);
805b2fc195eSAndrew Gallatin }
806b2fc195eSAndrew Gallatin return;
807b2fc195eSAndrew Gallatin }
808b2fc195eSAndrew Gallatin
809b2fc195eSAndrew Gallatin static int
mxge_send_cmd(mxge_softc_t * sc,uint32_t cmd,mxge_cmd_t * data)8106d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
811b2fc195eSAndrew Gallatin {
812b2fc195eSAndrew Gallatin mcp_cmd_t *buf;
813b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8];
814b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd;
8150fa7f681SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
816b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high;
817e0501fd0SAndrew Gallatin int err, sleep_total = 0;
818b2fc195eSAndrew Gallatin
819b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */
820aa54c242SJohn Baldwin buf = (mcp_cmd_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
821b2fc195eSAndrew Gallatin
822b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0);
823b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1);
824b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2);
825b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd);
8266d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8276d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
828b2fc195eSAndrew Gallatin
829b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low);
830b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high);
831a98d6cd7SAndrew Gallatin mtx_lock(&sc->cmd_mtx);
832b2fc195eSAndrew Gallatin response->result = 0xffffffff;
83373c7c83fSAndrew Gallatin wmb();
8346d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
835b2fc195eSAndrew Gallatin
8365e7d8541SAndrew Gallatin /* wait up to 20ms */
837e0501fd0SAndrew Gallatin err = EAGAIN;
8385e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) {
839b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat,
840b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
84173c7c83fSAndrew Gallatin wmb();
842e0501fd0SAndrew Gallatin switch (be32toh(response->result)) {
843e0501fd0SAndrew Gallatin case 0:
844b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data);
845e0501fd0SAndrew Gallatin err = 0;
846e0501fd0SAndrew Gallatin break;
847e0501fd0SAndrew Gallatin case 0xffffffff:
848e0501fd0SAndrew Gallatin DELAY(1000);
849e0501fd0SAndrew Gallatin break;
850e0501fd0SAndrew Gallatin case MXGEFW_CMD_UNKNOWN:
851e0501fd0SAndrew Gallatin err = ENOSYS;
852e0501fd0SAndrew Gallatin break;
853e0501fd0SAndrew Gallatin case MXGEFW_CMD_ERROR_UNALIGNED:
854e0501fd0SAndrew Gallatin err = E2BIG;
855e0501fd0SAndrew Gallatin break;
856c587e59fSAndrew Gallatin case MXGEFW_CMD_ERROR_BUSY:
857c587e59fSAndrew Gallatin err = EBUSY;
858c587e59fSAndrew Gallatin break;
859c406ad2eSAndrew Gallatin case MXGEFW_CMD_ERROR_I2C_ABSENT:
860c406ad2eSAndrew Gallatin err = ENXIO;
861c406ad2eSAndrew Gallatin break;
862e0501fd0SAndrew Gallatin default:
863b2fc195eSAndrew Gallatin device_printf(sc->dev,
8646d87a65dSAndrew Gallatin "mxge: command %d "
865b2fc195eSAndrew Gallatin "failed, result = %d\n",
866b2fc195eSAndrew Gallatin cmd, be32toh(response->result));
867e0501fd0SAndrew Gallatin err = ENXIO;
868e0501fd0SAndrew Gallatin break;
869b2fc195eSAndrew Gallatin }
870e0501fd0SAndrew Gallatin if (err != EAGAIN)
871e0501fd0SAndrew Gallatin break;
872b2fc195eSAndrew Gallatin }
873e0501fd0SAndrew Gallatin if (err == EAGAIN)
8746d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out"
875b2fc195eSAndrew Gallatin "result = %d\n",
876b2fc195eSAndrew Gallatin cmd, be32toh(response->result));
877e0501fd0SAndrew Gallatin mtx_unlock(&sc->cmd_mtx);
878e0501fd0SAndrew Gallatin return err;
879b2fc195eSAndrew Gallatin }
880b2fc195eSAndrew Gallatin
8814da0d523SAndrew Gallatin static int
mxge_adopt_running_firmware(mxge_softc_t * sc)8824da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8834da0d523SAndrew Gallatin {
8844da0d523SAndrew Gallatin struct mcp_gen_header *hdr;
8854da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header);
8864da0d523SAndrew Gallatin size_t hdr_offset;
8874da0d523SAndrew Gallatin int status;
8884da0d523SAndrew Gallatin
8894da0d523SAndrew Gallatin /* find running firmware header */
8904da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *)
8914da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET));
8924da0d523SAndrew Gallatin
8934da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8944da0d523SAndrew Gallatin device_printf(sc->dev,
8954da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n",
8964da0d523SAndrew Gallatin (int)hdr_offset);
8974da0d523SAndrew Gallatin return EIO;
8984da0d523SAndrew Gallatin }
8994da0d523SAndrew Gallatin
9004da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to
9014da0d523SAndrew Gallatin * validate firmware */
9024da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9034da0d523SAndrew Gallatin if (hdr == NULL) {
9044da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n");
9054da0d523SAndrew Gallatin return ENOMEM;
9064da0d523SAndrew Gallatin }
9074da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9084da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res),
9094da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes);
9104da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr);
9114da0d523SAndrew Gallatin free(hdr, M_DEVBUF);
912b824b7d8SAndrew Gallatin
913b824b7d8SAndrew Gallatin /*
914b824b7d8SAndrew Gallatin * check to see if adopted firmware has bug where adopting
915b824b7d8SAndrew Gallatin * it will cause broadcasts to be filtered unless the NIC
916b824b7d8SAndrew Gallatin * is kept in ALLMULTI mode
917b824b7d8SAndrew Gallatin */
918b824b7d8SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
919b824b7d8SAndrew Gallatin sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
920b824b7d8SAndrew Gallatin sc->adopted_rx_filter_bug = 1;
921b824b7d8SAndrew Gallatin device_printf(sc->dev, "Adopting fw %d.%d.%d: "
922b824b7d8SAndrew Gallatin "working around rx filter bug\n",
923b824b7d8SAndrew Gallatin sc->fw_ver_major, sc->fw_ver_minor,
924b824b7d8SAndrew Gallatin sc->fw_ver_tiny);
925b824b7d8SAndrew Gallatin }
926b824b7d8SAndrew Gallatin
9274da0d523SAndrew Gallatin return status;
9284da0d523SAndrew Gallatin }
9294da0d523SAndrew Gallatin
930b2fc195eSAndrew Gallatin static int
mxge_load_firmware(mxge_softc_t * sc,int adopt)9311e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
932b2fc195eSAndrew Gallatin {
933b2fc195eSAndrew Gallatin volatile uint32_t *confirm;
934b2fc195eSAndrew Gallatin volatile char *submit;
935b2fc195eSAndrew Gallatin char buf_bytes[72];
936b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high;
937b2fc195eSAndrew Gallatin int status, i;
938b2fc195eSAndrew Gallatin
939aa54c242SJohn Baldwin buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
940b2fc195eSAndrew Gallatin
941b2fc195eSAndrew Gallatin size = sc->sram_size;
9426d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size);
943b2fc195eSAndrew Gallatin if (status) {
9441e413cf9SAndrew Gallatin if (!adopt)
9451e413cf9SAndrew Gallatin return status;
9464da0d523SAndrew Gallatin /* Try to use the currently running firmware, if
9474da0d523SAndrew Gallatin it is new enough */
9484da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc);
9494da0d523SAndrew Gallatin if (status) {
9504da0d523SAndrew Gallatin device_printf(sc->dev,
9514da0d523SAndrew Gallatin "failed to adopt running firmware\n");
952b2fc195eSAndrew Gallatin return status;
953b2fc195eSAndrew Gallatin }
9544da0d523SAndrew Gallatin device_printf(sc->dev,
9554da0d523SAndrew Gallatin "Successfully adopted running firmware\n");
9561e413cf9SAndrew Gallatin if (sc->tx_boundary == 4096) {
9574da0d523SAndrew Gallatin device_printf(sc->dev,
9584da0d523SAndrew Gallatin "Using firmware currently running on NIC"
9594da0d523SAndrew Gallatin ". For optimal\n");
9604da0d523SAndrew Gallatin device_printf(sc->dev,
9614da0d523SAndrew Gallatin "performance consider loading optimized "
9624da0d523SAndrew Gallatin "firmware\n");
9634da0d523SAndrew Gallatin }
964d91b1b49SAndrew Gallatin sc->fw_name = mxge_fw_unaligned;
9651e413cf9SAndrew Gallatin sc->tx_boundary = 2048;
966d91b1b49SAndrew Gallatin return 0;
9674da0d523SAndrew Gallatin }
968b2fc195eSAndrew Gallatin /* clear confirmation addr */
969b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd;
970b2fc195eSAndrew Gallatin *confirm = 0;
97173c7c83fSAndrew Gallatin wmb();
972b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the
973b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should
974b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well
975b2fc195eSAndrew Gallatin */
976b2fc195eSAndrew Gallatin
9776d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9786d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
979b2fc195eSAndrew Gallatin
980b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */
981b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */
982b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */
983b2fc195eSAndrew Gallatin
984b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of
985b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces
986b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes
987b2fc195eSAndrew Gallatin */
988b2fc195eSAndrew Gallatin /* where the code starts*/
9896d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8);
990b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */
991b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */
992b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */
993b2fc195eSAndrew Gallatin
9940fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9956d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64);
99673c7c83fSAndrew Gallatin wmb();
997b2fc195eSAndrew Gallatin DELAY(1000);
99873c7c83fSAndrew Gallatin wmb();
999b2fc195eSAndrew Gallatin i = 0;
1000b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) {
1001b2fc195eSAndrew Gallatin DELAY(1000*10);
1002b2fc195eSAndrew Gallatin i++;
1003b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat,
1004b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1005b2fc195eSAndrew Gallatin }
1006b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) {
1007b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1008b2fc195eSAndrew Gallatin confirm, *confirm);
1009b2fc195eSAndrew Gallatin
1010b2fc195eSAndrew Gallatin return ENXIO;
1011b2fc195eSAndrew Gallatin }
1012b2fc195eSAndrew Gallatin return 0;
1013b2fc195eSAndrew Gallatin }
1014b2fc195eSAndrew Gallatin
1015b2fc195eSAndrew Gallatin static int
mxge_update_mac_address(mxge_softc_t * sc)10166d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1017b2fc195eSAndrew Gallatin {
10186d87a65dSAndrew Gallatin mxge_cmd_t cmd;
1019b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr;
1020b2fc195eSAndrew Gallatin int status;
1021b2fc195eSAndrew Gallatin
1022b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1023b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]);
1024b2fc195eSAndrew Gallatin
1025b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5]));
1026b2fc195eSAndrew Gallatin
10275e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1028b2fc195eSAndrew Gallatin return status;
1029b2fc195eSAndrew Gallatin }
1030b2fc195eSAndrew Gallatin
1031b2fc195eSAndrew Gallatin static int
mxge_change_pause(mxge_softc_t * sc,int pause)10326d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1033b2fc195eSAndrew Gallatin {
10346d87a65dSAndrew Gallatin mxge_cmd_t cmd;
1035b2fc195eSAndrew Gallatin int status;
1036b2fc195eSAndrew Gallatin
1037b2fc195eSAndrew Gallatin if (pause)
10385e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1039b2fc195eSAndrew Gallatin &cmd);
1040b2fc195eSAndrew Gallatin else
10415e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1042b2fc195eSAndrew Gallatin &cmd);
1043b2fc195eSAndrew Gallatin
1044b2fc195eSAndrew Gallatin if (status) {
1045b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n");
1046b2fc195eSAndrew Gallatin return ENXIO;
1047b2fc195eSAndrew Gallatin }
1048b2fc195eSAndrew Gallatin sc->pause = pause;
1049b2fc195eSAndrew Gallatin return 0;
1050b2fc195eSAndrew Gallatin }
1051b2fc195eSAndrew Gallatin
1052b2fc195eSAndrew Gallatin static void
mxge_change_promisc(mxge_softc_t * sc,int promisc)10536d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1054b2fc195eSAndrew Gallatin {
10556d87a65dSAndrew Gallatin mxge_cmd_t cmd;
1056b2fc195eSAndrew Gallatin int status;
1057b2fc195eSAndrew Gallatin
10581e413cf9SAndrew Gallatin if (mxge_always_promisc)
10591e413cf9SAndrew Gallatin promisc = 1;
10601e413cf9SAndrew Gallatin
1061b2fc195eSAndrew Gallatin if (promisc)
10625e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1063b2fc195eSAndrew Gallatin &cmd);
1064b2fc195eSAndrew Gallatin else
10655e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1066b2fc195eSAndrew Gallatin &cmd);
1067b2fc195eSAndrew Gallatin
1068b2fc195eSAndrew Gallatin if (status) {
1069b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n");
1070b2fc195eSAndrew Gallatin }
1071b2fc195eSAndrew Gallatin }
1072b2fc195eSAndrew Gallatin
10732a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx {
10742a0f8518SGleb Smirnoff mxge_softc_t *sc;
10752a0f8518SGleb Smirnoff int error;
10762a0f8518SGleb Smirnoff };
10772a0f8518SGleb Smirnoff
10782a0f8518SGleb Smirnoff static u_int
mxge_add_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)10792a0f8518SGleb Smirnoff mxge_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
10802a0f8518SGleb Smirnoff {
10812a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx *ctx = arg;
10822a0f8518SGleb Smirnoff mxge_cmd_t cmd;
10832a0f8518SGleb Smirnoff
10842a0f8518SGleb Smirnoff if (ctx->error != 0)
10852a0f8518SGleb Smirnoff return (0);
10862a0f8518SGleb Smirnoff bcopy(LLADDR(sdl), &cmd.data0, 4);
10872a0f8518SGleb Smirnoff bcopy(LLADDR(sdl) + 4, &cmd.data1, 2);
10882a0f8518SGleb Smirnoff cmd.data0 = htonl(cmd.data0);
10892a0f8518SGleb Smirnoff cmd.data1 = htonl(cmd.data1);
10902a0f8518SGleb Smirnoff
10912a0f8518SGleb Smirnoff ctx->error = mxge_send_cmd(ctx->sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10922a0f8518SGleb Smirnoff
10932a0f8518SGleb Smirnoff return (1);
10942a0f8518SGleb Smirnoff }
10952a0f8518SGleb Smirnoff
10960fa7f681SAndrew Gallatin static void
mxge_set_multicast_list(mxge_softc_t * sc)10970fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10980fa7f681SAndrew Gallatin {
10992a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx ctx;
110093037a67SJustin Hibbits if_t ifp = sc->ifp;
11012a0f8518SGleb Smirnoff mxge_cmd_t cmd;
11020fa7f681SAndrew Gallatin int err;
11030fa7f681SAndrew Gallatin
11040fa7f681SAndrew Gallatin /* This firmware is known to not support multicast */
11050fa7f681SAndrew Gallatin if (!sc->fw_multicast_support)
11060fa7f681SAndrew Gallatin return;
11070fa7f681SAndrew Gallatin
11080fa7f681SAndrew Gallatin /* Disable multicast filtering while we play with the lists*/
11090fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11100fa7f681SAndrew Gallatin if (err != 0) {
11110fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11120fa7f681SAndrew Gallatin " error status: %d\n", err);
11130fa7f681SAndrew Gallatin return;
11140fa7f681SAndrew Gallatin }
11150fa7f681SAndrew Gallatin
1116b824b7d8SAndrew Gallatin if (sc->adopted_rx_filter_bug)
1117b824b7d8SAndrew Gallatin return;
11180fa7f681SAndrew Gallatin
111993037a67SJustin Hibbits if (if_getflags(ifp) & IFF_ALLMULTI)
11200fa7f681SAndrew Gallatin /* request to disable multicast filtering, so quit here */
11210fa7f681SAndrew Gallatin return;
11220fa7f681SAndrew Gallatin
11230fa7f681SAndrew Gallatin /* Flush all the filters */
11240fa7f681SAndrew Gallatin
11250fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11260fa7f681SAndrew Gallatin if (err != 0) {
11270fa7f681SAndrew Gallatin device_printf(sc->dev,
11280fa7f681SAndrew Gallatin "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11290fa7f681SAndrew Gallatin ", error status: %d\n", err);
11300fa7f681SAndrew Gallatin return;
11310fa7f681SAndrew Gallatin }
11320fa7f681SAndrew Gallatin
11330fa7f681SAndrew Gallatin /* Walk the multicast list, and add each address */
11342a0f8518SGleb Smirnoff ctx.sc = sc;
11352a0f8518SGleb Smirnoff ctx.error = 0;
11362a0f8518SGleb Smirnoff if_foreach_llmaddr(ifp, mxge_add_maddr, &ctx);
11372a0f8518SGleb Smirnoff if (ctx.error != 0) {
11382a0f8518SGleb Smirnoff device_printf(sc->dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
11392a0f8518SGleb Smirnoff "error status:" "%d\t", ctx.error);
11400fa7f681SAndrew Gallatin /* abort, leaving multicast filtering off */
11410fa7f681SAndrew Gallatin return;
11420fa7f681SAndrew Gallatin }
11432a0f8518SGleb Smirnoff
11440fa7f681SAndrew Gallatin /* Enable multicast filtering */
11450fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11460fa7f681SAndrew Gallatin if (err != 0) {
11470fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11480fa7f681SAndrew Gallatin ", error status: %d\n", err);
11490fa7f681SAndrew Gallatin }
11500fa7f681SAndrew Gallatin }
11510fa7f681SAndrew Gallatin
1152b2fc195eSAndrew Gallatin static int
mxge_max_mtu(mxge_softc_t * sc)1153053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1154053e637fSAndrew Gallatin {
1155053e637fSAndrew Gallatin mxge_cmd_t cmd;
1156053e637fSAndrew Gallatin int status;
1157053e637fSAndrew Gallatin
1158c792928fSAndrew Gallatin if (MJUMPAGESIZE - MXGEFW_PAD > MXGEFW_MAX_MTU)
1159c792928fSAndrew Gallatin return MXGEFW_MAX_MTU - MXGEFW_PAD;
1160053e637fSAndrew Gallatin
1161053e637fSAndrew Gallatin /* try to set nbufs to see if it we can
1162053e637fSAndrew Gallatin use virtually contiguous jumbos */
1163053e637fSAndrew Gallatin cmd.data0 = 0;
1164053e637fSAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1165053e637fSAndrew Gallatin &cmd);
1166053e637fSAndrew Gallatin if (status == 0)
1167c792928fSAndrew Gallatin return MXGEFW_MAX_MTU - MXGEFW_PAD;
1168053e637fSAndrew Gallatin
1169053e637fSAndrew Gallatin /* otherwise, we're limited to MJUMPAGESIZE */
1170053e637fSAndrew Gallatin return MJUMPAGESIZE - MXGEFW_PAD;
1171053e637fSAndrew Gallatin }
1172053e637fSAndrew Gallatin
1173053e637fSAndrew Gallatin static int
mxge_reset(mxge_softc_t * sc,int interrupts_setup)1174adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1175b2fc195eSAndrew Gallatin {
11761e413cf9SAndrew Gallatin struct mxge_slice_state *ss;
11771e413cf9SAndrew Gallatin mxge_rx_done_t *rx_done;
11781e413cf9SAndrew Gallatin volatile uint32_t *irq_claim;
11796d87a65dSAndrew Gallatin mxge_cmd_t cmd;
11801e413cf9SAndrew Gallatin int slice, status;
1181b2fc195eSAndrew Gallatin
1182b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it
1183b2fc195eSAndrew Gallatin is alive */
1184b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd));
11855e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1186b2fc195eSAndrew Gallatin if (status != 0) {
1187b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n");
1188b2fc195eSAndrew Gallatin return ENXIO;
1189b2fc195eSAndrew Gallatin }
1190b2fc195eSAndrew Gallatin
1191091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1);
1192091feecdSAndrew Gallatin
11931e413cf9SAndrew Gallatin /* set the intrq size */
11941e413cf9SAndrew Gallatin cmd.data0 = sc->rx_ring_size;
11951e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11961e413cf9SAndrew Gallatin
11971e413cf9SAndrew Gallatin /*
11981e413cf9SAndrew Gallatin * Even though we already know how many slices are supported
11991e413cf9SAndrew Gallatin * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12001e413cf9SAndrew Gallatin * has magic side effects, and must be called after a reset.
12011e413cf9SAndrew Gallatin * It must be called prior to calling any RSS related cmds,
12021e413cf9SAndrew Gallatin * including assigning an interrupt queue for anything but
12031e413cf9SAndrew Gallatin * slice 0. It must also be called *after*
12041e413cf9SAndrew Gallatin * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12051e413cf9SAndrew Gallatin * the firmware to compute offsets.
12061e413cf9SAndrew Gallatin */
12071e413cf9SAndrew Gallatin
12081e413cf9SAndrew Gallatin if (sc->num_slices > 1) {
12091e413cf9SAndrew Gallatin /* ask the maximum number of slices it supports */
12101e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12111e413cf9SAndrew Gallatin &cmd);
12121e413cf9SAndrew Gallatin if (status != 0) {
12131e413cf9SAndrew Gallatin device_printf(sc->dev,
12141e413cf9SAndrew Gallatin "failed to get number of slices\n");
12151e413cf9SAndrew Gallatin return status;
12161e413cf9SAndrew Gallatin }
12171e413cf9SAndrew Gallatin /*
12181e413cf9SAndrew Gallatin * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12191e413cf9SAndrew Gallatin * to setting up the interrupt queue DMA
12201e413cf9SAndrew Gallatin */
12211e413cf9SAndrew Gallatin cmd.data0 = sc->num_slices;
12221e413cf9SAndrew Gallatin cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1223c6cb3e3fSAndrew Gallatin cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
12241e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12251e413cf9SAndrew Gallatin &cmd);
12261e413cf9SAndrew Gallatin if (status != 0) {
12271e413cf9SAndrew Gallatin device_printf(sc->dev,
12281e413cf9SAndrew Gallatin "failed to set number of slices\n");
12291e413cf9SAndrew Gallatin return status;
12301e413cf9SAndrew Gallatin }
12311e413cf9SAndrew Gallatin }
12321e413cf9SAndrew Gallatin
1233adae7080SAndrew Gallatin if (interrupts_setup) {
1234b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */
12351e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
12361e413cf9SAndrew Gallatin rx_done = &sc->ss[slice].rx_done;
12371e413cf9SAndrew Gallatin memset(rx_done->entry, 0, sc->rx_ring_size);
12381e413cf9SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12391e413cf9SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12401e413cf9SAndrew Gallatin cmd.data2 = slice;
12411e413cf9SAndrew Gallatin status |= mxge_send_cmd(sc,
12421e413cf9SAndrew Gallatin MXGEFW_CMD_SET_INTRQ_DMA,
12431e413cf9SAndrew Gallatin &cmd);
12441e413cf9SAndrew Gallatin }
1245adae7080SAndrew Gallatin }
1246b2fc195eSAndrew Gallatin
12476d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc,
12485e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12495e7d8541SAndrew Gallatin
12505e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12515e7d8541SAndrew Gallatin
12525e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12531e413cf9SAndrew Gallatin irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12545e7d8541SAndrew Gallatin
12555e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12566d87a65dSAndrew Gallatin &cmd);
12575e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1258b2fc195eSAndrew Gallatin if (status != 0) {
1259b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n");
1260b2fc195eSAndrew Gallatin return status;
1261b2fc195eSAndrew Gallatin }
1262b2fc195eSAndrew Gallatin
12635e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12645e7d8541SAndrew Gallatin
12655e7d8541SAndrew Gallatin /* run a DMA benchmark */
12668fe615baSAndrew Gallatin (void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12675e7d8541SAndrew Gallatin
12681e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
12691e413cf9SAndrew Gallatin ss = &sc->ss[slice];
12701e413cf9SAndrew Gallatin
12711e413cf9SAndrew Gallatin ss->irq_claim = irq_claim + (2 * slice);
1272b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */
12731e413cf9SAndrew Gallatin ss->rx_done.idx = 0;
12741e413cf9SAndrew Gallatin ss->rx_done.cnt = 0;
12751e413cf9SAndrew Gallatin ss->tx.req = 0;
12761e413cf9SAndrew Gallatin ss->tx.done = 0;
12771e413cf9SAndrew Gallatin ss->tx.pkt_done = 0;
1278c6cb3e3fSAndrew Gallatin ss->tx.queue_active = 0;
1279c6cb3e3fSAndrew Gallatin ss->tx.activate = 0;
1280c6cb3e3fSAndrew Gallatin ss->tx.deactivate = 0;
12811e413cf9SAndrew Gallatin ss->tx.wake = 0;
12821e413cf9SAndrew Gallatin ss->tx.defrag = 0;
12831e413cf9SAndrew Gallatin ss->tx.stall = 0;
12841e413cf9SAndrew Gallatin ss->rx_big.cnt = 0;
12851e413cf9SAndrew Gallatin ss->rx_small.cnt = 0;
128626dd49c6SAndrew Gallatin ss->lc.lro_bad_csum = 0;
128726dd49c6SAndrew Gallatin ss->lc.lro_queued = 0;
128826dd49c6SAndrew Gallatin ss->lc.lro_flushed = 0;
12891e413cf9SAndrew Gallatin if (ss->fw_stats != NULL) {
1290a393336bSAndrew Gallatin bzero(ss->fw_stats, sizeof *ss->fw_stats);
12911e413cf9SAndrew Gallatin }
12921e413cf9SAndrew Gallatin }
1293b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15;
12946d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc);
129593037a67SJustin Hibbits mxge_change_promisc(sc, if_getflags(sc->ifp) & IFF_PROMISC);
12966d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause);
12970fa7f681SAndrew Gallatin mxge_set_multicast_list(sc);
129865c69066SAndrew Gallatin if (sc->throttle) {
129965c69066SAndrew Gallatin cmd.data0 = sc->throttle;
130065c69066SAndrew Gallatin if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
130165c69066SAndrew Gallatin &cmd)) {
130265c69066SAndrew Gallatin device_printf(sc->dev,
130365c69066SAndrew Gallatin "can't enable throttle\n");
130465c69066SAndrew Gallatin }
130565c69066SAndrew Gallatin }
1306b2fc195eSAndrew Gallatin return status;
1307b2fc195eSAndrew Gallatin }
1308b2fc195eSAndrew Gallatin
1309b2fc195eSAndrew Gallatin static int
mxge_change_throttle(SYSCTL_HANDLER_ARGS)131065c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
131165c69066SAndrew Gallatin {
131265c69066SAndrew Gallatin mxge_cmd_t cmd;
131365c69066SAndrew Gallatin mxge_softc_t *sc;
131465c69066SAndrew Gallatin int err;
131565c69066SAndrew Gallatin unsigned int throttle;
131665c69066SAndrew Gallatin
131765c69066SAndrew Gallatin sc = arg1;
131865c69066SAndrew Gallatin throttle = sc->throttle;
131965c69066SAndrew Gallatin err = sysctl_handle_int(oidp, &throttle, arg2, req);
132065c69066SAndrew Gallatin if (err != 0) {
132165c69066SAndrew Gallatin return err;
132265c69066SAndrew Gallatin }
132365c69066SAndrew Gallatin
132465c69066SAndrew Gallatin if (throttle == sc->throttle)
132565c69066SAndrew Gallatin return 0;
132665c69066SAndrew Gallatin
132765c69066SAndrew Gallatin if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
132865c69066SAndrew Gallatin return EINVAL;
132965c69066SAndrew Gallatin
133065c69066SAndrew Gallatin mtx_lock(&sc->driver_mtx);
133165c69066SAndrew Gallatin cmd.data0 = throttle;
133265c69066SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
133365c69066SAndrew Gallatin if (err == 0)
133465c69066SAndrew Gallatin sc->throttle = throttle;
133565c69066SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
133665c69066SAndrew Gallatin return err;
133765c69066SAndrew Gallatin }
133865c69066SAndrew Gallatin
133965c69066SAndrew Gallatin static int
mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)13406d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1341b2fc195eSAndrew Gallatin {
13426d87a65dSAndrew Gallatin mxge_softc_t *sc;
1343b2fc195eSAndrew Gallatin unsigned int intr_coal_delay;
1344b2fc195eSAndrew Gallatin int err;
1345b2fc195eSAndrew Gallatin
1346b2fc195eSAndrew Gallatin sc = arg1;
1347b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay;
1348b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1349b2fc195eSAndrew Gallatin if (err != 0) {
1350b2fc195eSAndrew Gallatin return err;
1351b2fc195eSAndrew Gallatin }
1352b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay)
1353b2fc195eSAndrew Gallatin return 0;
1354b2fc195eSAndrew Gallatin
1355b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1356b2fc195eSAndrew Gallatin return EINVAL;
1357b2fc195eSAndrew Gallatin
1358a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx);
13595e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1360b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay;
13615e7d8541SAndrew Gallatin
1362a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
1363b2fc195eSAndrew Gallatin return err;
1364b2fc195eSAndrew Gallatin }
1365b2fc195eSAndrew Gallatin
1366b2fc195eSAndrew Gallatin static int
mxge_change_flow_control(SYSCTL_HANDLER_ARGS)13676d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1368b2fc195eSAndrew Gallatin {
13696d87a65dSAndrew Gallatin mxge_softc_t *sc;
1370b2fc195eSAndrew Gallatin unsigned int enabled;
1371b2fc195eSAndrew Gallatin int err;
1372b2fc195eSAndrew Gallatin
1373b2fc195eSAndrew Gallatin sc = arg1;
1374b2fc195eSAndrew Gallatin enabled = sc->pause;
1375b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req);
1376b2fc195eSAndrew Gallatin if (err != 0) {
1377b2fc195eSAndrew Gallatin return err;
1378b2fc195eSAndrew Gallatin }
1379b2fc195eSAndrew Gallatin if (enabled == sc->pause)
1380b2fc195eSAndrew Gallatin return 0;
1381b2fc195eSAndrew Gallatin
1382a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx);
13836d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled);
1384a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
1385b2fc195eSAndrew Gallatin return err;
1386b2fc195eSAndrew Gallatin }
1387b2fc195eSAndrew Gallatin
1388b2fc195eSAndrew Gallatin static int
mxge_handle_be32(SYSCTL_HANDLER_ARGS)13896d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1390b2fc195eSAndrew Gallatin {
1391b2fc195eSAndrew Gallatin int err;
1392b2fc195eSAndrew Gallatin
1393b2fc195eSAndrew Gallatin if (arg1 == NULL)
1394b2fc195eSAndrew Gallatin return EFAULT;
1395b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1);
1396b2fc195eSAndrew Gallatin arg1 = NULL;
1397b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req);
1398b2fc195eSAndrew Gallatin
1399b2fc195eSAndrew Gallatin return err;
1400b2fc195eSAndrew Gallatin }
1401b2fc195eSAndrew Gallatin
1402b2fc195eSAndrew Gallatin static void
mxge_rem_sysctls(mxge_softc_t * sc)14031e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14041e413cf9SAndrew Gallatin {
14051e413cf9SAndrew Gallatin struct mxge_slice_state *ss;
14061e413cf9SAndrew Gallatin int slice;
14071e413cf9SAndrew Gallatin
14081e413cf9SAndrew Gallatin if (sc->slice_sysctl_tree == NULL)
14091e413cf9SAndrew Gallatin return;
14101e413cf9SAndrew Gallatin
14111e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
14121e413cf9SAndrew Gallatin ss = &sc->ss[slice];
14131e413cf9SAndrew Gallatin if (ss == NULL || ss->sysctl_tree == NULL)
14141e413cf9SAndrew Gallatin continue;
14151e413cf9SAndrew Gallatin sysctl_ctx_free(&ss->sysctl_ctx);
14161e413cf9SAndrew Gallatin ss->sysctl_tree = NULL;
14171e413cf9SAndrew Gallatin }
14181e413cf9SAndrew Gallatin sysctl_ctx_free(&sc->slice_sysctl_ctx);
14191e413cf9SAndrew Gallatin sc->slice_sysctl_tree = NULL;
14201e413cf9SAndrew Gallatin }
14211e413cf9SAndrew Gallatin
14221e413cf9SAndrew Gallatin static void
mxge_add_sysctls(mxge_softc_t * sc)14236d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1424b2fc195eSAndrew Gallatin {
1425b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx;
1426b2fc195eSAndrew Gallatin struct sysctl_oid_list *children;
14275e7d8541SAndrew Gallatin mcp_irq_data_t *fw;
14281e413cf9SAndrew Gallatin struct mxge_slice_state *ss;
14291e413cf9SAndrew Gallatin int slice;
14301e413cf9SAndrew Gallatin char slice_num[8];
1431b2fc195eSAndrew Gallatin
1432b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev);
1433b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14341e413cf9SAndrew Gallatin fw = sc->ss[0].fw_stats;
1435b2fc195eSAndrew Gallatin
14365e7d8541SAndrew Gallatin /* random information */
14375e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14385e7d8541SAndrew Gallatin "firmware_version",
1439f0188618SHans Petter Selasky CTLFLAG_RD, sc->fw_version,
14405e7d8541SAndrew Gallatin 0, "firmware version");
14415e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14425e7d8541SAndrew Gallatin "serial_number",
1443f0188618SHans Petter Selasky CTLFLAG_RD, sc->serial_number_string,
14445e7d8541SAndrew Gallatin 0, "serial number");
14455e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14465e7d8541SAndrew Gallatin "product_code",
1447f0188618SHans Petter Selasky CTLFLAG_RD, sc->product_code_string,
14485e7d8541SAndrew Gallatin 0, "product_code");
14495e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1450d91b1b49SAndrew Gallatin "pcie_link_width",
1451d91b1b49SAndrew Gallatin CTLFLAG_RD, &sc->link_width,
1452d91b1b49SAndrew Gallatin 0, "tx_boundary");
1453d91b1b49SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14545e7d8541SAndrew Gallatin "tx_boundary",
14551e413cf9SAndrew Gallatin CTLFLAG_RD, &sc->tx_boundary,
14565e7d8541SAndrew Gallatin 0, "tx_boundary");
14575e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1458091feecdSAndrew Gallatin "write_combine",
1459091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc,
1460091feecdSAndrew Gallatin 0, "write combining PIO?");
1461091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14625e7d8541SAndrew Gallatin "read_dma_MBs",
14635e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma,
14645e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s");
14655e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14665e7d8541SAndrew Gallatin "write_dma_MBs",
14675e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma,
14685e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s");
14695e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14705e7d8541SAndrew Gallatin "read_write_dma_MBs",
14715e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma,
14725e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s");
1473a393336bSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1474a393336bSAndrew Gallatin "watchdog_resets",
1475a393336bSAndrew Gallatin CTLFLAG_RD, &sc->watchdog_resets,
1476a393336bSAndrew Gallatin 0, "Number of times NIC was reset");
14775e7d8541SAndrew Gallatin
14785e7d8541SAndrew Gallatin /* performance related tunables */
1479b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14807029da5cSPawel Biernacki "intr_coal_delay", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
14817029da5cSPawel Biernacki sc, 0, mxge_change_intr_coal, "I",
14827029da5cSPawel Biernacki "interrupt coalescing delay in usecs");
1483b2fc195eSAndrew Gallatin
1484b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14857029da5cSPawel Biernacki "throttle", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14867029da5cSPawel Biernacki mxge_change_throttle, "I", "transmit throttling");
148765c69066SAndrew Gallatin
148865c69066SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1489b2fc195eSAndrew Gallatin "flow_control_enabled",
14907029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14917029da5cSPawel Biernacki mxge_change_flow_control, "I",
14927029da5cSPawel Biernacki "interrupt coalescing delay in usecs");
1493b2fc195eSAndrew Gallatin
1494b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14955e7d8541SAndrew Gallatin "deassert_wait",
14965e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait,
14975e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler");
1498b2fc195eSAndrew Gallatin
1499b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order.
1500b2fc195eSAndrew Gallatin Need to swap it */
1501b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15027029da5cSPawel Biernacki "link_up", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15037029da5cSPawel Biernacki &fw->link_up, 0, mxge_handle_be32, "I", "link up");
1504b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15057029da5cSPawel Biernacki "rdma_tags_available", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15067029da5cSPawel Biernacki &fw->rdma_tags_available, 0, mxge_handle_be32, "I",
15077029da5cSPawel Biernacki "rdma_tags_available");
1508b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15097029da5cSPawel Biernacki "dropped_bad_crc32", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15107029da5cSPawel Biernacki &fw->dropped_bad_crc32, 0, mxge_handle_be32, "I",
15117029da5cSPawel Biernacki "dropped_bad_crc32");
1512adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15137029da5cSPawel Biernacki "dropped_bad_phy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15147029da5cSPawel Biernacki &fw->dropped_bad_phy, 0, mxge_handle_be32, "I", "dropped_bad_phy");
1515b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1516b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered",
15177029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15187029da5cSPawel Biernacki &fw->dropped_link_error_or_filtered, 0, mxge_handle_be32, "I",
15197029da5cSPawel Biernacki "dropped_link_error_or_filtered");
1520b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1521adae7080SAndrew Gallatin "dropped_link_overflow",
15227029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15237029da5cSPawel Biernacki &fw->dropped_link_overflow, 0, mxge_handle_be32, "I",
15247029da5cSPawel Biernacki "dropped_link_overflow");
1525adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15260fa7f681SAndrew Gallatin "dropped_multicast_filtered",
15277029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15287029da5cSPawel Biernacki &fw->dropped_multicast_filtered, 0, mxge_handle_be32, "I",
15297029da5cSPawel Biernacki "dropped_multicast_filtered");
15300fa7f681SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1531adae7080SAndrew Gallatin "dropped_no_big_buffer",
15327029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15337029da5cSPawel Biernacki &fw->dropped_no_big_buffer, 0, mxge_handle_be32, "I",
15347029da5cSPawel Biernacki "dropped_no_big_buffer");
1535b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1536b2fc195eSAndrew Gallatin "dropped_no_small_buffer",
15377029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15387029da5cSPawel Biernacki &fw->dropped_no_small_buffer, 0, mxge_handle_be32, "I",
15397029da5cSPawel Biernacki "dropped_no_small_buffer");
1540b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1541adae7080SAndrew Gallatin "dropped_overrun",
15427029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15437029da5cSPawel Biernacki &fw->dropped_overrun, 0, mxge_handle_be32, "I",
15447029da5cSPawel Biernacki "dropped_overrun");
1545adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15467029da5cSPawel Biernacki "dropped_pause", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15477029da5cSPawel Biernacki &fw->dropped_pause, 0, mxge_handle_be32, "I", "dropped_pause");
1548adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15497029da5cSPawel Biernacki "dropped_runt", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15507029da5cSPawel Biernacki &fw->dropped_runt, 0, mxge_handle_be32, "I", "dropped_runt");
1551b2fc195eSAndrew Gallatin
1552a0394e33SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1553a0394e33SAndrew Gallatin "dropped_unicast_filtered",
15547029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15557029da5cSPawel Biernacki &fw->dropped_unicast_filtered, 0, mxge_handle_be32, "I",
15567029da5cSPawel Biernacki "dropped_unicast_filtered");
1557a0394e33SAndrew Gallatin
15585e7d8541SAndrew Gallatin /* verbose printing? */
1559b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15605e7d8541SAndrew Gallatin "verbose",
15615e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose,
15625e7d8541SAndrew Gallatin 0, "verbose printing");
1563b2fc195eSAndrew Gallatin
15641e413cf9SAndrew Gallatin /* add counters exported for debugging from all slices */
15651e413cf9SAndrew Gallatin sysctl_ctx_init(&sc->slice_sysctl_ctx);
15661e413cf9SAndrew Gallatin sc->slice_sysctl_tree =
15671e413cf9SAndrew Gallatin SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15687029da5cSPawel Biernacki "slice", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15691e413cf9SAndrew Gallatin
15701e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
15711e413cf9SAndrew Gallatin ss = &sc->ss[slice];
15721e413cf9SAndrew Gallatin sysctl_ctx_init(&ss->sysctl_ctx);
15731e413cf9SAndrew Gallatin ctx = &ss->sysctl_ctx;
15741e413cf9SAndrew Gallatin children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
15751e413cf9SAndrew Gallatin sprintf(slice_num, "%d", slice);
15761e413cf9SAndrew Gallatin ss->sysctl_tree =
15771e413cf9SAndrew Gallatin SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
15787029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15791e413cf9SAndrew Gallatin children = SYSCTL_CHILDREN(ss->sysctl_tree);
1580053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15811e413cf9SAndrew Gallatin "rx_small_cnt",
15821e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->rx_small.cnt,
15831e413cf9SAndrew Gallatin 0, "rx_small_cnt");
15841e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15851e413cf9SAndrew Gallatin "rx_big_cnt",
15861e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->rx_big.cnt,
15871e413cf9SAndrew Gallatin 0, "rx_small_cnt");
1588e936121dSHans Petter Selasky SYSCTL_ADD_U64(ctx, children, OID_AUTO,
158926dd49c6SAndrew Gallatin "lro_flushed", CTLFLAG_RD, &ss->lc.lro_flushed,
1590053e637fSAndrew Gallatin 0, "number of lro merge queues flushed");
1591053e637fSAndrew Gallatin
1592e936121dSHans Petter Selasky SYSCTL_ADD_U64(ctx, children, OID_AUTO,
159326dd49c6SAndrew Gallatin "lro_bad_csum", CTLFLAG_RD, &ss->lc.lro_bad_csum,
159426dd49c6SAndrew Gallatin 0, "number of bad csums preventing LRO");
159526dd49c6SAndrew Gallatin
1596e936121dSHans Petter Selasky SYSCTL_ADD_U64(ctx, children, OID_AUTO,
159726dd49c6SAndrew Gallatin "lro_queued", CTLFLAG_RD, &ss->lc.lro_queued,
15981e413cf9SAndrew Gallatin 0, "number of frames appended to lro merge"
15991e413cf9SAndrew Gallatin "queues");
1600053e637fSAndrew Gallatin
1601c6cb3e3fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1602c6cb3e3fSAndrew Gallatin "tx_req",
1603c6cb3e3fSAndrew Gallatin CTLFLAG_RD, &ss->tx.req,
1604c6cb3e3fSAndrew Gallatin 0, "tx_req");
16051e413cf9SAndrew Gallatin
16061e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16071e413cf9SAndrew Gallatin "tx_done",
16081e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.done,
16091e413cf9SAndrew Gallatin 0, "tx_done");
16101e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16111e413cf9SAndrew Gallatin "tx_pkt_done",
16121e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.pkt_done,
16131e413cf9SAndrew Gallatin 0, "tx_done");
16141e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16151e413cf9SAndrew Gallatin "tx_stall",
16161e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.stall,
16171e413cf9SAndrew Gallatin 0, "tx_stall");
16181e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16191e413cf9SAndrew Gallatin "tx_wake",
16201e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.wake,
16211e413cf9SAndrew Gallatin 0, "tx_wake");
16221e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16231e413cf9SAndrew Gallatin "tx_defrag",
16241e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.defrag,
16251e413cf9SAndrew Gallatin 0, "tx_defrag");
1626c6cb3e3fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1627c6cb3e3fSAndrew Gallatin "tx_queue_active",
1628c6cb3e3fSAndrew Gallatin CTLFLAG_RD, &ss->tx.queue_active,
1629c6cb3e3fSAndrew Gallatin 0, "tx_queue_active");
1630c6cb3e3fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1631c6cb3e3fSAndrew Gallatin "tx_activate",
1632c6cb3e3fSAndrew Gallatin CTLFLAG_RD, &ss->tx.activate,
1633c6cb3e3fSAndrew Gallatin 0, "tx_activate");
1634c6cb3e3fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1635c6cb3e3fSAndrew Gallatin "tx_deactivate",
1636c6cb3e3fSAndrew Gallatin CTLFLAG_RD, &ss->tx.deactivate,
1637c6cb3e3fSAndrew Gallatin 0, "tx_deactivate");
16381e413cf9SAndrew Gallatin }
1639b2fc195eSAndrew Gallatin }
1640b2fc195eSAndrew Gallatin
1641b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy
1642b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */
1643b2fc195eSAndrew Gallatin
1644b2fc195eSAndrew Gallatin static inline void
mxge_submit_req_backwards(mxge_tx_ring_t * tx,mcp_kreq_ether_send_t * src,int cnt)16451e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1646b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt)
1647b2fc195eSAndrew Gallatin {
1648b2fc195eSAndrew Gallatin int idx, starting_slot;
1649b2fc195eSAndrew Gallatin starting_slot = tx->req;
1650b2fc195eSAndrew Gallatin while (cnt > 1) {
1651b2fc195eSAndrew Gallatin cnt--;
1652b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask;
16536d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx],
1654b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src));
165573c7c83fSAndrew Gallatin wmb();
1656b2fc195eSAndrew Gallatin }
1657b2fc195eSAndrew Gallatin }
1658b2fc195eSAndrew Gallatin
1659b2fc195eSAndrew Gallatin /*
1660b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy
1661b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software
1662b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags
1663b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain
1664b2fc195eSAndrew Gallatin */
1665b2fc195eSAndrew Gallatin
1666b2fc195eSAndrew Gallatin static inline void
mxge_submit_req(mxge_tx_ring_t * tx,mcp_kreq_ether_send_t * src,int cnt)16671e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1668b2fc195eSAndrew Gallatin int cnt)
1669b2fc195eSAndrew Gallatin {
1670b2fc195eSAndrew Gallatin int idx, i;
1671b2fc195eSAndrew Gallatin uint32_t *src_ints;
1672b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints;
1673b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp;
1674b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst;
16755e7d8541SAndrew Gallatin uint8_t last_flags;
1676b2fc195eSAndrew Gallatin
1677b2fc195eSAndrew Gallatin idx = tx->req & tx->mask;
1678b2fc195eSAndrew Gallatin
16795e7d8541SAndrew Gallatin last_flags = src->flags;
16805e7d8541SAndrew Gallatin src->flags = 0;
168173c7c83fSAndrew Gallatin wmb();
1682b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx];
1683b2fc195eSAndrew Gallatin srcp = src;
1684b2fc195eSAndrew Gallatin
1685b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) {
1686b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) {
16876d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
168873c7c83fSAndrew Gallatin wmb(); /* force write every 32 bytes */
1689b2fc195eSAndrew Gallatin srcp += 2;
1690b2fc195eSAndrew Gallatin dstp += 2;
1691b2fc195eSAndrew Gallatin }
1692b2fc195eSAndrew Gallatin } else {
1693b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure
1694b2fc195eSAndrew Gallatin that it is submitted below */
16956d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt);
1696b2fc195eSAndrew Gallatin i = 0;
1697b2fc195eSAndrew Gallatin }
1698b2fc195eSAndrew Gallatin if (i < cnt) {
1699b2fc195eSAndrew Gallatin /* submit the first request */
17006d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src));
170173c7c83fSAndrew Gallatin wmb(); /* barrier before setting valid flag */
1702b2fc195eSAndrew Gallatin }
1703b2fc195eSAndrew Gallatin
1704b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */
17055e7d8541SAndrew Gallatin src->flags = last_flags;
1706b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src;
1707b2fc195eSAndrew Gallatin src_ints+=3;
1708b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst;
1709b2fc195eSAndrew Gallatin dst_ints+=3;
1710b2fc195eSAndrew Gallatin *dst_ints = *src_ints;
1711b2fc195eSAndrew Gallatin tx->req += cnt;
171273c7c83fSAndrew Gallatin wmb();
1713b2fc195eSAndrew Gallatin }
1714b2fc195eSAndrew Gallatin
17150a7a780eSAndrew Gallatin static int
mxge_parse_tx(struct mxge_slice_state * ss,struct mbuf * m,struct mxge_pkt_info * pi)17160a7a780eSAndrew Gallatin mxge_parse_tx(struct mxge_slice_state *ss, struct mbuf *m,
17170a7a780eSAndrew Gallatin struct mxge_pkt_info *pi)
17180a7a780eSAndrew Gallatin {
17190a7a780eSAndrew Gallatin struct ether_vlan_header *eh;
17200a7a780eSAndrew Gallatin uint16_t etype;
17210a7a780eSAndrew Gallatin int tso = m->m_pkthdr.csum_flags & (CSUM_TSO);
17220a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17230a7a780eSAndrew Gallatin int nxt;
17240a7a780eSAndrew Gallatin #endif
17250a7a780eSAndrew Gallatin
17260a7a780eSAndrew Gallatin eh = mtod(m, struct ether_vlan_header *);
17270a7a780eSAndrew Gallatin if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
17280a7a780eSAndrew Gallatin etype = ntohs(eh->evl_proto);
17290a7a780eSAndrew Gallatin pi->ip_off = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
17300a7a780eSAndrew Gallatin } else {
17310a7a780eSAndrew Gallatin etype = ntohs(eh->evl_encap_proto);
17320a7a780eSAndrew Gallatin pi->ip_off = ETHER_HDR_LEN;
17330a7a780eSAndrew Gallatin }
17340a7a780eSAndrew Gallatin
17350a7a780eSAndrew Gallatin switch (etype) {
17360a7a780eSAndrew Gallatin case ETHERTYPE_IP:
17370a7a780eSAndrew Gallatin /*
17380a7a780eSAndrew Gallatin * ensure ip header is in first mbuf, copy it to a
17390a7a780eSAndrew Gallatin * scratch buffer if not
17400a7a780eSAndrew Gallatin */
17410a7a780eSAndrew Gallatin pi->ip = (struct ip *)(m->m_data + pi->ip_off);
17420a7a780eSAndrew Gallatin pi->ip6 = NULL;
17430a7a780eSAndrew Gallatin if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip))) {
17440a7a780eSAndrew Gallatin m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip),
17450a7a780eSAndrew Gallatin ss->scratch);
17460a7a780eSAndrew Gallatin pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17470a7a780eSAndrew Gallatin }
17480a7a780eSAndrew Gallatin pi->ip_hlen = pi->ip->ip_hl << 2;
17490a7a780eSAndrew Gallatin if (!tso)
17500a7a780eSAndrew Gallatin return 0;
17510a7a780eSAndrew Gallatin
17520a7a780eSAndrew Gallatin if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17530a7a780eSAndrew Gallatin sizeof(struct tcphdr))) {
17540a7a780eSAndrew Gallatin m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17550a7a780eSAndrew Gallatin sizeof(struct tcphdr), ss->scratch);
17560a7a780eSAndrew Gallatin pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17570a7a780eSAndrew Gallatin }
17580a7a780eSAndrew Gallatin pi->tcp = (struct tcphdr *)((char *)pi->ip + pi->ip_hlen);
17590a7a780eSAndrew Gallatin break;
17600a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17610a7a780eSAndrew Gallatin case ETHERTYPE_IPV6:
17620a7a780eSAndrew Gallatin pi->ip6 = (struct ip6_hdr *)(m->m_data + pi->ip_off);
17630a7a780eSAndrew Gallatin if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip6))) {
17640a7a780eSAndrew Gallatin m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip6),
17650a7a780eSAndrew Gallatin ss->scratch);
17660a7a780eSAndrew Gallatin pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17670a7a780eSAndrew Gallatin }
17680a7a780eSAndrew Gallatin nxt = 0;
17690a7a780eSAndrew Gallatin pi->ip_hlen = ip6_lasthdr(m, pi->ip_off, IPPROTO_IPV6, &nxt);
17700a7a780eSAndrew Gallatin pi->ip_hlen -= pi->ip_off;
17710a7a780eSAndrew Gallatin if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
17720a7a780eSAndrew Gallatin return EINVAL;
17730a7a780eSAndrew Gallatin
17740a7a780eSAndrew Gallatin if (!tso)
17750a7a780eSAndrew Gallatin return 0;
17760a7a780eSAndrew Gallatin
17770a7a780eSAndrew Gallatin if (pi->ip_off + pi->ip_hlen > ss->sc->max_tso6_hlen)
17780a7a780eSAndrew Gallatin return EINVAL;
17790a7a780eSAndrew Gallatin
17800a7a780eSAndrew Gallatin if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17810a7a780eSAndrew Gallatin sizeof(struct tcphdr))) {
17820a7a780eSAndrew Gallatin m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17830a7a780eSAndrew Gallatin sizeof(struct tcphdr), ss->scratch);
17840a7a780eSAndrew Gallatin pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17850a7a780eSAndrew Gallatin }
17860a7a780eSAndrew Gallatin pi->tcp = (struct tcphdr *)((char *)pi->ip6 + pi->ip_hlen);
17870a7a780eSAndrew Gallatin break;
17880a7a780eSAndrew Gallatin #endif
17890a7a780eSAndrew Gallatin default:
17900a7a780eSAndrew Gallatin return EINVAL;
17910a7a780eSAndrew Gallatin }
17920a7a780eSAndrew Gallatin return 0;
17930a7a780eSAndrew Gallatin }
17940a7a780eSAndrew Gallatin
179537d89b0cSAndrew Gallatin #if IFCAP_TSO4
179637d89b0cSAndrew Gallatin
1797b2fc195eSAndrew Gallatin static void
mxge_encap_tso(struct mxge_slice_state * ss,struct mbuf * m,int busdma_seg_cnt,struct mxge_pkt_info * pi)17981e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
17990a7a780eSAndrew Gallatin int busdma_seg_cnt, struct mxge_pkt_info *pi)
1800aed8e389SAndrew Gallatin {
18011e413cf9SAndrew Gallatin mxge_tx_ring_t *tx;
1802aed8e389SAndrew Gallatin mcp_kreq_ether_send_t *req;
1803aed8e389SAndrew Gallatin bus_dma_segment_t *seg;
1804aed8e389SAndrew Gallatin uint32_t low, high_swapped;
1805aed8e389SAndrew Gallatin int len, seglen, cum_len, cum_len_next;
1806aed8e389SAndrew Gallatin int next_is_first, chop, cnt, rdma_count, small;
18070a7a780eSAndrew Gallatin uint16_t pseudo_hdr_offset, cksum_offset, mss, sum;
1808aed8e389SAndrew Gallatin uint8_t flags, flags_next;
1809aed8e389SAndrew Gallatin static int once;
1810aed8e389SAndrew Gallatin
1811aed8e389SAndrew Gallatin mss = m->m_pkthdr.tso_segsz;
1812aed8e389SAndrew Gallatin
1813aed8e389SAndrew Gallatin /* negative cum_len signifies to the
1814aed8e389SAndrew Gallatin * send loop that we are still in the
1815aed8e389SAndrew Gallatin * header portion of the TSO packet.
1816aed8e389SAndrew Gallatin */
1817aed8e389SAndrew Gallatin
18180a7a780eSAndrew Gallatin cksum_offset = pi->ip_off + pi->ip_hlen;
18190a7a780eSAndrew Gallatin cum_len = -(cksum_offset + (pi->tcp->th_off << 2));
1820aed8e389SAndrew Gallatin
1821aed8e389SAndrew Gallatin /* TSO implies checksum offload on this hardware */
18220a7a780eSAndrew Gallatin if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) == 0)) {
18234ed8ca8fSAndrew Gallatin /*
18244ed8ca8fSAndrew Gallatin * If packet has full TCP csum, replace it with pseudo hdr
18254ed8ca8fSAndrew Gallatin * sum that the NIC expects, otherwise the NIC will emit
18264ed8ca8fSAndrew Gallatin * packets with bad TCP checksums.
18274ed8ca8fSAndrew Gallatin */
18284ed8ca8fSAndrew Gallatin m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
18290a7a780eSAndrew Gallatin if (pi->ip6) {
18300a7a780eSAndrew Gallatin #if (CSUM_TCP_IPV6 != 0) && defined(INET6)
18310a7a780eSAndrew Gallatin m->m_pkthdr.csum_flags |= CSUM_TCP_IPV6;
18320a7a780eSAndrew Gallatin sum = in6_cksum_pseudo(pi->ip6,
18330a7a780eSAndrew Gallatin m->m_pkthdr.len - cksum_offset,
18340a7a780eSAndrew Gallatin IPPROTO_TCP, 0);
18350a7a780eSAndrew Gallatin #endif
18360a7a780eSAndrew Gallatin } else {
1837abc5b96bSAndrew Gallatin #ifdef INET
18380a7a780eSAndrew Gallatin m->m_pkthdr.csum_flags |= CSUM_TCP;
18390a7a780eSAndrew Gallatin sum = in_pseudo(pi->ip->ip_src.s_addr,
18400a7a780eSAndrew Gallatin pi->ip->ip_dst.s_addr,
18410a7a780eSAndrew Gallatin htons(IPPROTO_TCP + (m->m_pkthdr.len -
18420a7a780eSAndrew Gallatin cksum_offset)));
1843abc5b96bSAndrew Gallatin #endif
18440a7a780eSAndrew Gallatin }
18450a7a780eSAndrew Gallatin m_copyback(m, offsetof(struct tcphdr, th_sum) +
18460a7a780eSAndrew Gallatin cksum_offset, sizeof(sum), (caddr_t)&sum);
18474ed8ca8fSAndrew Gallatin }
1848aed8e389SAndrew Gallatin flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1849aed8e389SAndrew Gallatin
1850aed8e389SAndrew Gallatin /* for TSO, pseudo_hdr_offset holds mss.
1851aed8e389SAndrew Gallatin * The firmware figures out where to put
1852aed8e389SAndrew Gallatin * the checksum by parsing the header. */
1853aed8e389SAndrew Gallatin pseudo_hdr_offset = htobe16(mss);
1854aed8e389SAndrew Gallatin
18550a7a780eSAndrew Gallatin if (pi->ip6) {
18560a7a780eSAndrew Gallatin /*
18570a7a780eSAndrew Gallatin * for IPv6 TSO, the "checksum offset" is re-purposed
18580a7a780eSAndrew Gallatin * to store the TCP header len
18590a7a780eSAndrew Gallatin */
18600a7a780eSAndrew Gallatin cksum_offset = (pi->tcp->th_off << 2);
18610a7a780eSAndrew Gallatin }
18620a7a780eSAndrew Gallatin
18631e413cf9SAndrew Gallatin tx = &ss->tx;
1864aed8e389SAndrew Gallatin req = tx->req_list;
1865aed8e389SAndrew Gallatin seg = tx->seg_list;
1866aed8e389SAndrew Gallatin cnt = 0;
1867aed8e389SAndrew Gallatin rdma_count = 0;
1868aed8e389SAndrew Gallatin /* "rdma_count" is the number of RDMAs belonging to the
1869aed8e389SAndrew Gallatin * current packet BEFORE the current send request. For
1870aed8e389SAndrew Gallatin * non-TSO packets, this is equal to "count".
1871aed8e389SAndrew Gallatin * For TSO packets, rdma_count needs to be reset
1872aed8e389SAndrew Gallatin * to 0 after a segment cut.
1873aed8e389SAndrew Gallatin *
1874aed8e389SAndrew Gallatin * The rdma_count field of the send request is
1875aed8e389SAndrew Gallatin * the number of RDMAs of the packet starting at
1876aed8e389SAndrew Gallatin * that request. For TSO send requests with one ore more cuts
1877aed8e389SAndrew Gallatin * in the middle, this is the number of RDMAs starting
1878aed8e389SAndrew Gallatin * after the last cut in the request. All previous
1879aed8e389SAndrew Gallatin * segments before the last cut implicitly have 1 RDMA.
1880aed8e389SAndrew Gallatin *
1881aed8e389SAndrew Gallatin * Since the number of RDMAs is not known beforehand,
1882aed8e389SAndrew Gallatin * it must be filled-in retroactively - after each
1883aed8e389SAndrew Gallatin * segmentation cut or at the end of the entire packet.
1884aed8e389SAndrew Gallatin */
1885aed8e389SAndrew Gallatin
1886aed8e389SAndrew Gallatin while (busdma_seg_cnt) {
1887aed8e389SAndrew Gallatin /* Break the busdma segment up into pieces*/
1888aed8e389SAndrew Gallatin low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1889aed8e389SAndrew Gallatin high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1890e39a0a37SAndrew Gallatin len = seg->ds_len;
1891aed8e389SAndrew Gallatin
1892aed8e389SAndrew Gallatin while (len) {
1893aed8e389SAndrew Gallatin flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1894e39a0a37SAndrew Gallatin seglen = len;
1895aed8e389SAndrew Gallatin cum_len_next = cum_len + seglen;
1896aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count + 1;
1897aed8e389SAndrew Gallatin if (__predict_true(cum_len >= 0)) {
1898aed8e389SAndrew Gallatin /* payload */
1899aed8e389SAndrew Gallatin chop = (cum_len_next > mss);
1900aed8e389SAndrew Gallatin cum_len_next = cum_len_next % mss;
1901aed8e389SAndrew Gallatin next_is_first = (cum_len_next == 0);
1902aed8e389SAndrew Gallatin flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1903aed8e389SAndrew Gallatin flags_next |= next_is_first *
1904aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST;
1905aed8e389SAndrew Gallatin rdma_count |= -(chop | next_is_first);
1906aed8e389SAndrew Gallatin rdma_count += chop & !next_is_first;
1907aed8e389SAndrew Gallatin } else if (cum_len_next >= 0) {
1908aed8e389SAndrew Gallatin /* header ends */
1909aed8e389SAndrew Gallatin rdma_count = -1;
1910aed8e389SAndrew Gallatin cum_len_next = 0;
1911aed8e389SAndrew Gallatin seglen = -cum_len;
1912aed8e389SAndrew Gallatin small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1913aed8e389SAndrew Gallatin flags_next = MXGEFW_FLAGS_TSO_PLD |
1914aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST |
1915aed8e389SAndrew Gallatin (small * MXGEFW_FLAGS_SMALL);
1916aed8e389SAndrew Gallatin }
1917aed8e389SAndrew Gallatin
1918aed8e389SAndrew Gallatin req->addr_high = high_swapped;
1919aed8e389SAndrew Gallatin req->addr_low = htobe32(low);
1920aed8e389SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset;
1921aed8e389SAndrew Gallatin req->pad = 0;
1922aed8e389SAndrew Gallatin req->rdma_count = 1;
1923aed8e389SAndrew Gallatin req->length = htobe16(seglen);
1924aed8e389SAndrew Gallatin req->cksum_offset = cksum_offset;
1925aed8e389SAndrew Gallatin req->flags = flags | ((cum_len & 1) *
1926aed8e389SAndrew Gallatin MXGEFW_FLAGS_ALIGN_ODD);
1927aed8e389SAndrew Gallatin low += seglen;
1928aed8e389SAndrew Gallatin len -= seglen;
1929aed8e389SAndrew Gallatin cum_len = cum_len_next;
1930aed8e389SAndrew Gallatin flags = flags_next;
1931aed8e389SAndrew Gallatin req++;
1932aed8e389SAndrew Gallatin cnt++;
1933aed8e389SAndrew Gallatin rdma_count++;
19340a7a780eSAndrew Gallatin if (cksum_offset != 0 && !pi->ip6) {
1935aed8e389SAndrew Gallatin if (__predict_false(cksum_offset > seglen))
1936aed8e389SAndrew Gallatin cksum_offset -= seglen;
1937aed8e389SAndrew Gallatin else
1938aed8e389SAndrew Gallatin cksum_offset = 0;
19390a7a780eSAndrew Gallatin }
1940adae7080SAndrew Gallatin if (__predict_false(cnt > tx->max_desc))
1941aed8e389SAndrew Gallatin goto drop;
1942aed8e389SAndrew Gallatin }
1943aed8e389SAndrew Gallatin busdma_seg_cnt--;
1944aed8e389SAndrew Gallatin seg++;
1945aed8e389SAndrew Gallatin }
1946aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count;
1947aed8e389SAndrew Gallatin
1948aed8e389SAndrew Gallatin do {
1949aed8e389SAndrew Gallatin req--;
1950aed8e389SAndrew Gallatin req->flags |= MXGEFW_FLAGS_TSO_LAST;
1951aed8e389SAndrew Gallatin } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1952aed8e389SAndrew Gallatin
1953aed8e389SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1954aed8e389SAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt);
19556147584bSElliott Mitchell
1956c6cb3e3fSAndrew Gallatin if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1957c6cb3e3fSAndrew Gallatin /* tell the NIC to start polling this slice */
1958c6cb3e3fSAndrew Gallatin *tx->send_go = 1;
1959c6cb3e3fSAndrew Gallatin tx->queue_active = 1;
1960c6cb3e3fSAndrew Gallatin tx->activate++;
1961c6cb3e3fSAndrew Gallatin wmb();
1962c6cb3e3fSAndrew Gallatin }
19636147584bSElliott Mitchell
1964aed8e389SAndrew Gallatin return;
1965aed8e389SAndrew Gallatin
1966aed8e389SAndrew Gallatin drop:
1967e39a0a37SAndrew Gallatin bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1968aed8e389SAndrew Gallatin m_freem(m);
1969c6cb3e3fSAndrew Gallatin ss->oerrors++;
1970aed8e389SAndrew Gallatin if (!once) {
1971adae7080SAndrew Gallatin printf("tx->max_desc exceeded via TSO!\n");
1972adae7080SAndrew Gallatin printf("mss = %d, %ld, %d!\n", mss,
1973adae7080SAndrew Gallatin (long)seg - (long)tx->seg_list, tx->max_desc);
1974aed8e389SAndrew Gallatin once = 1;
1975aed8e389SAndrew Gallatin }
1976aed8e389SAndrew Gallatin return;
1977aed8e389SAndrew Gallatin
1978aed8e389SAndrew Gallatin }
1979aed8e389SAndrew Gallatin
198037d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
198137d89b0cSAndrew Gallatin
198237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1983c792928fSAndrew Gallatin /*
1984c792928fSAndrew Gallatin * We reproduce the software vlan tag insertion from
1985c792928fSAndrew Gallatin * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1986c792928fSAndrew Gallatin * vlan tag insertion. We need to advertise this in order to have the
1987c792928fSAndrew Gallatin * vlan interface respect our csum offload flags.
1988c792928fSAndrew Gallatin */
1989c792928fSAndrew Gallatin static struct mbuf *
mxge_vlan_tag_insert(struct mbuf * m)1990c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1991c792928fSAndrew Gallatin {
1992c792928fSAndrew Gallatin struct ether_vlan_header *evl;
1993c792928fSAndrew Gallatin
1994c6499eccSGleb Smirnoff M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
1995c792928fSAndrew Gallatin if (__predict_false(m == NULL))
1996c792928fSAndrew Gallatin return NULL;
1997c792928fSAndrew Gallatin if (m->m_len < sizeof(*evl)) {
1998c792928fSAndrew Gallatin m = m_pullup(m, sizeof(*evl));
1999c792928fSAndrew Gallatin if (__predict_false(m == NULL))
2000c792928fSAndrew Gallatin return NULL;
2001c792928fSAndrew Gallatin }
2002c792928fSAndrew Gallatin /*
2003c792928fSAndrew Gallatin * Transform the Ethernet header into an Ethernet header
2004c792928fSAndrew Gallatin * with 802.1Q encapsulation.
2005c792928fSAndrew Gallatin */
2006c792928fSAndrew Gallatin evl = mtod(m, struct ether_vlan_header *);
2007c792928fSAndrew Gallatin bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2008c792928fSAndrew Gallatin (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2009c792928fSAndrew Gallatin evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2010c792928fSAndrew Gallatin evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2011c792928fSAndrew Gallatin m->m_flags &= ~M_VLANTAG;
2012c792928fSAndrew Gallatin return m;
2013c792928fSAndrew Gallatin }
201437d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2015c792928fSAndrew Gallatin
2016aed8e389SAndrew Gallatin static void
mxge_encap(struct mxge_slice_state * ss,struct mbuf * m)20171e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2018b2fc195eSAndrew Gallatin {
20190a7a780eSAndrew Gallatin struct mxge_pkt_info pi = {0,0,0,0};
20201e413cf9SAndrew Gallatin mxge_softc_t *sc;
2021b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req;
2022b2fc195eSAndrew Gallatin bus_dma_segment_t *seg;
2023b2fc195eSAndrew Gallatin struct mbuf *m_tmp;
20241e413cf9SAndrew Gallatin mxge_tx_ring_t *tx;
20250a7a780eSAndrew Gallatin int cnt, cum_len, err, i, idx, odd_flag;
2026aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset;
2027aed8e389SAndrew Gallatin uint8_t flags, cksum_offset;
2028b2fc195eSAndrew Gallatin
20291e413cf9SAndrew Gallatin sc = ss->sc;
20301e413cf9SAndrew Gallatin tx = &ss->tx;
2031b2fc195eSAndrew Gallatin
203237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2033c792928fSAndrew Gallatin if (m->m_flags & M_VLANTAG) {
2034c792928fSAndrew Gallatin m = mxge_vlan_tag_insert(m);
2035c792928fSAndrew Gallatin if (__predict_false(m == NULL))
20360a7a780eSAndrew Gallatin goto drop_without_m;
2037c792928fSAndrew Gallatin }
203837d89b0cSAndrew Gallatin #endif
20390a7a780eSAndrew Gallatin if (m->m_pkthdr.csum_flags &
20400a7a780eSAndrew Gallatin (CSUM_TSO | CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
20410a7a780eSAndrew Gallatin if (mxge_parse_tx(ss, m, &pi))
20420a7a780eSAndrew Gallatin goto drop;
20430a7a780eSAndrew Gallatin }
20440a7a780eSAndrew Gallatin
2045b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */
2046b2fc195eSAndrew Gallatin idx = tx->req & tx->mask;
2047b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2048aed8e389SAndrew Gallatin m, tx->seg_list, &cnt,
2049b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT);
2050adae7080SAndrew Gallatin if (__predict_false(err == EFBIG)) {
2051b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try
2052b2fc195eSAndrew Gallatin to defrag */
2053b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT);
2054b2fc195eSAndrew Gallatin if (m_tmp == NULL) {
2055b2fc195eSAndrew Gallatin goto drop;
2056b2fc195eSAndrew Gallatin }
20571e413cf9SAndrew Gallatin ss->tx.defrag++;
2058b2fc195eSAndrew Gallatin m = m_tmp;
2059b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat,
2060b2fc195eSAndrew Gallatin tx->info[idx].map,
2061aed8e389SAndrew Gallatin m, tx->seg_list, &cnt,
2062b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT);
2063b2fc195eSAndrew Gallatin }
2064adae7080SAndrew Gallatin if (__predict_false(err != 0)) {
2065aed8e389SAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2066aed8e389SAndrew Gallatin " packet len = %d\n", err, m->m_pkthdr.len);
2067b2fc195eSAndrew Gallatin goto drop;
2068b2fc195eSAndrew Gallatin }
2069b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2070b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE);
20715e7d8541SAndrew Gallatin tx->info[idx].m = m;
2072b2fc195eSAndrew Gallatin
207337d89b0cSAndrew Gallatin #if IFCAP_TSO4
2074aed8e389SAndrew Gallatin /* TSO is different enough, we handle it in another routine */
2075aed8e389SAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20760a7a780eSAndrew Gallatin mxge_encap_tso(ss, m, cnt, &pi);
2077aed8e389SAndrew Gallatin return;
2078aed8e389SAndrew Gallatin }
207937d89b0cSAndrew Gallatin #endif
2080aed8e389SAndrew Gallatin
2081b2fc195eSAndrew Gallatin req = tx->req_list;
2082b2fc195eSAndrew Gallatin cksum_offset = 0;
20835e7d8541SAndrew Gallatin pseudo_hdr_offset = 0;
20845e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO;
2085b2fc195eSAndrew Gallatin
2086b2fc195eSAndrew Gallatin /* checksum offloading? */
20870a7a780eSAndrew Gallatin if (m->m_pkthdr.csum_flags &
20880a7a780eSAndrew Gallatin (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
2089aed8e389SAndrew Gallatin /* ensure ip header is in first mbuf, copy
2090aed8e389SAndrew Gallatin it to a scratch buffer if not */
20910a7a780eSAndrew Gallatin cksum_offset = pi.ip_off + pi.ip_hlen;
2092b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data;
20935e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2094b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset;
20955e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM;
2096aed8e389SAndrew Gallatin odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2097aed8e389SAndrew Gallatin } else {
2098aed8e389SAndrew Gallatin odd_flag = 0;
2099b2fc195eSAndrew Gallatin }
21005e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21015e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL;
2102b2fc195eSAndrew Gallatin
2103b2fc195eSAndrew Gallatin /* convert segments into a request list */
2104b2fc195eSAndrew Gallatin cum_len = 0;
2105aed8e389SAndrew Gallatin seg = tx->seg_list;
21065e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST;
2107b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) {
2108b2fc195eSAndrew Gallatin req->addr_low =
21096d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2110b2fc195eSAndrew Gallatin req->addr_high =
21116d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2112b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len);
2113b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset;
2114b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len)
2115b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len;
2116b2fc195eSAndrew Gallatin else
2117b2fc195eSAndrew Gallatin cksum_offset = 0;
21185e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset;
21195e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */
21205e7d8541SAndrew Gallatin req->rdma_count = 1;
2121aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag);
2122b2fc195eSAndrew Gallatin cum_len += seg->ds_len;
2123b2fc195eSAndrew Gallatin seg++;
2124b2fc195eSAndrew Gallatin req++;
2125b2fc195eSAndrew Gallatin req->flags = 0;
2126b2fc195eSAndrew Gallatin }
2127b2fc195eSAndrew Gallatin req--;
2128b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */
2129b2fc195eSAndrew Gallatin if (cum_len < 60) {
2130b2fc195eSAndrew Gallatin req++;
2131b2fc195eSAndrew Gallatin req->addr_low =
21326d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2133b2fc195eSAndrew Gallatin req->addr_high =
21346d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2135b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len);
21365e7d8541SAndrew Gallatin req->cksum_offset = 0;
21375e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset;
21385e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */
21395e7d8541SAndrew Gallatin req->rdma_count = 1;
2140aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag);
2141b2fc195eSAndrew Gallatin cnt++;
2142b2fc195eSAndrew Gallatin }
21435e7d8541SAndrew Gallatin
21445e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt;
21455e7d8541SAndrew Gallatin #if 0
21465e7d8541SAndrew Gallatin /* print what the firmware will see */
21475e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) {
21485e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21495e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n",
21505e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high),
21515e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low),
21525e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length),
21535e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21545e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21555e7d8541SAndrew Gallatin tx->req_list[i].rdma_count);
21565e7d8541SAndrew Gallatin }
21575e7d8541SAndrew Gallatin printf("--------------\n");
21585e7d8541SAndrew Gallatin #endif
21595e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21606d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt);
21616147584bSElliott Mitchell
2162c6cb3e3fSAndrew Gallatin if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2163c6cb3e3fSAndrew Gallatin /* tell the NIC to start polling this slice */
2164c6cb3e3fSAndrew Gallatin *tx->send_go = 1;
2165c6cb3e3fSAndrew Gallatin tx->queue_active = 1;
2166c6cb3e3fSAndrew Gallatin tx->activate++;
2167c6cb3e3fSAndrew Gallatin wmb();
2168c6cb3e3fSAndrew Gallatin }
21696147584bSElliott Mitchell
2170b2fc195eSAndrew Gallatin return;
2171b2fc195eSAndrew Gallatin
2172b2fc195eSAndrew Gallatin drop:
2173b2fc195eSAndrew Gallatin m_freem(m);
21740a7a780eSAndrew Gallatin drop_without_m:
2175c6cb3e3fSAndrew Gallatin ss->oerrors++;
2176b2fc195eSAndrew Gallatin return;
2177b2fc195eSAndrew Gallatin }
2178b2fc195eSAndrew Gallatin
2179c6cb3e3fSAndrew Gallatin static void
mxge_qflush(if_t ifp)218093037a67SJustin Hibbits mxge_qflush(if_t ifp)
2181c6cb3e3fSAndrew Gallatin {
218293037a67SJustin Hibbits mxge_softc_t *sc = if_getsoftc(ifp);
2183c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx;
2184c6cb3e3fSAndrew Gallatin struct mbuf *m;
2185c6cb3e3fSAndrew Gallatin int slice;
2186b2fc195eSAndrew Gallatin
2187c6cb3e3fSAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
2188c6cb3e3fSAndrew Gallatin tx = &sc->ss[slice].tx;
2189c6cb3e3fSAndrew Gallatin mtx_lock(&tx->mtx);
2190c6cb3e3fSAndrew Gallatin while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2191c6cb3e3fSAndrew Gallatin m_freem(m);
2192c6cb3e3fSAndrew Gallatin mtx_unlock(&tx->mtx);
2193c6cb3e3fSAndrew Gallatin }
2194c6cb3e3fSAndrew Gallatin if_qflush(ifp);
2195c6cb3e3fSAndrew Gallatin }
21966d914a32SAndrew Gallatin
2197c6cb3e3fSAndrew Gallatin static inline void
mxge_start_locked(struct mxge_slice_state * ss)2198c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2199c6cb3e3fSAndrew Gallatin {
2200c6cb3e3fSAndrew Gallatin mxge_softc_t *sc;
2201c6cb3e3fSAndrew Gallatin struct mbuf *m;
220293037a67SJustin Hibbits if_t ifp;
2203c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx;
2204c6cb3e3fSAndrew Gallatin
2205c6cb3e3fSAndrew Gallatin sc = ss->sc;
2206c6cb3e3fSAndrew Gallatin ifp = sc->ifp;
2207c6cb3e3fSAndrew Gallatin tx = &ss->tx;
2208c6cb3e3fSAndrew Gallatin
2209c6cb3e3fSAndrew Gallatin while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2210c6cb3e3fSAndrew Gallatin m = drbr_dequeue(ifp, tx->br);
2211c6cb3e3fSAndrew Gallatin if (m == NULL) {
2212c6cb3e3fSAndrew Gallatin return;
2213c6cb3e3fSAndrew Gallatin }
2214c6cb3e3fSAndrew Gallatin /* let BPF see it */
2215c6cb3e3fSAndrew Gallatin BPF_MTAP(ifp, m);
2216c6cb3e3fSAndrew Gallatin
2217c6cb3e3fSAndrew Gallatin /* give it to the nic */
2218c6cb3e3fSAndrew Gallatin mxge_encap(ss, m);
2219c6cb3e3fSAndrew Gallatin }
2220c6cb3e3fSAndrew Gallatin /* ran out of transmit slots */
2221c6cb3e3fSAndrew Gallatin if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2222c6cb3e3fSAndrew Gallatin && (!drbr_empty(ifp, tx->br))) {
2223c6cb3e3fSAndrew Gallatin ss->if_drv_flags |= IFF_DRV_OACTIVE;
2224c6cb3e3fSAndrew Gallatin tx->stall++;
2225c6cb3e3fSAndrew Gallatin }
2226c6cb3e3fSAndrew Gallatin }
2227c6cb3e3fSAndrew Gallatin
2228c6cb3e3fSAndrew Gallatin static int
mxge_transmit_locked(struct mxge_slice_state * ss,struct mbuf * m)2229c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2230c6cb3e3fSAndrew Gallatin {
2231c6cb3e3fSAndrew Gallatin mxge_softc_t *sc;
223293037a67SJustin Hibbits if_t ifp;
2233c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx;
2234c6cb3e3fSAndrew Gallatin int err;
2235c6cb3e3fSAndrew Gallatin
2236c6cb3e3fSAndrew Gallatin sc = ss->sc;
2237c6cb3e3fSAndrew Gallatin ifp = sc->ifp;
2238c6cb3e3fSAndrew Gallatin tx = &ss->tx;
2239c6cb3e3fSAndrew Gallatin
2240c6cb3e3fSAndrew Gallatin if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2241c6cb3e3fSAndrew Gallatin IFF_DRV_RUNNING) {
2242c6cb3e3fSAndrew Gallatin err = drbr_enqueue(ifp, tx->br, m);
2243c6cb3e3fSAndrew Gallatin return (err);
2244c6cb3e3fSAndrew Gallatin }
2245c6cb3e3fSAndrew Gallatin
2246193cbc4dSMax Laier if (!drbr_needs_enqueue(ifp, tx->br) &&
2247c6cb3e3fSAndrew Gallatin ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2248c6cb3e3fSAndrew Gallatin /* let BPF see it */
2249c6cb3e3fSAndrew Gallatin BPF_MTAP(ifp, m);
2250c6cb3e3fSAndrew Gallatin /* give it to the nic */
2251c6cb3e3fSAndrew Gallatin mxge_encap(ss, m);
2252c6cb3e3fSAndrew Gallatin } else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2253c6cb3e3fSAndrew Gallatin return (err);
2254c6cb3e3fSAndrew Gallatin }
2255c6cb3e3fSAndrew Gallatin if (!drbr_empty(ifp, tx->br))
2256c6cb3e3fSAndrew Gallatin mxge_start_locked(ss);
2257c6cb3e3fSAndrew Gallatin return (0);
2258c6cb3e3fSAndrew Gallatin }
2259c6cb3e3fSAndrew Gallatin
2260c6cb3e3fSAndrew Gallatin static int
mxge_transmit(if_t ifp,struct mbuf * m)226193037a67SJustin Hibbits mxge_transmit(if_t ifp, struct mbuf *m)
2262c6cb3e3fSAndrew Gallatin {
226393037a67SJustin Hibbits mxge_softc_t *sc = if_getsoftc(ifp);
2264c6cb3e3fSAndrew Gallatin struct mxge_slice_state *ss;
2265c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx;
2266c6cb3e3fSAndrew Gallatin int err = 0;
2267c6cb3e3fSAndrew Gallatin int slice;
2268c6cb3e3fSAndrew Gallatin
2269c6cb3e3fSAndrew Gallatin slice = m->m_pkthdr.flowid;
2270c6cb3e3fSAndrew Gallatin slice &= (sc->num_slices - 1); /* num_slices always power of 2 */
2271c6cb3e3fSAndrew Gallatin
2272c6cb3e3fSAndrew Gallatin ss = &sc->ss[slice];
2273c6cb3e3fSAndrew Gallatin tx = &ss->tx;
2274c6cb3e3fSAndrew Gallatin
2275c6cb3e3fSAndrew Gallatin if (mtx_trylock(&tx->mtx)) {
2276c6cb3e3fSAndrew Gallatin err = mxge_transmit_locked(ss, m);
2277c6cb3e3fSAndrew Gallatin mtx_unlock(&tx->mtx);
2278c6cb3e3fSAndrew Gallatin } else {
2279c6cb3e3fSAndrew Gallatin err = drbr_enqueue(ifp, tx->br, m);
2280c6cb3e3fSAndrew Gallatin }
2281c6cb3e3fSAndrew Gallatin
2282c6cb3e3fSAndrew Gallatin return (err);
2283c6cb3e3fSAndrew Gallatin }
2284c6cb3e3fSAndrew Gallatin
2285b2fc195eSAndrew Gallatin static void
mxge_start(if_t ifp)228693037a67SJustin Hibbits mxge_start(if_t ifp)
2287b2fc195eSAndrew Gallatin {
228893037a67SJustin Hibbits mxge_softc_t *sc = if_getsoftc(ifp);
22891e413cf9SAndrew Gallatin struct mxge_slice_state *ss;
2290b2fc195eSAndrew Gallatin
22911e413cf9SAndrew Gallatin /* only use the first slice for now */
22921e413cf9SAndrew Gallatin ss = &sc->ss[0];
22931e413cf9SAndrew Gallatin mtx_lock(&ss->tx.mtx);
22941e413cf9SAndrew Gallatin mxge_start_locked(ss);
22951e413cf9SAndrew Gallatin mtx_unlock(&ss->tx.mtx);
2296b2fc195eSAndrew Gallatin }
2297b2fc195eSAndrew Gallatin
22985e7d8541SAndrew Gallatin /*
22995e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy
23005e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software
23015e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low
23025e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk
23035e7d8541SAndrew Gallatin * in a burst
23045e7d8541SAndrew Gallatin */
23055e7d8541SAndrew Gallatin static inline void
mxge_submit_8rx(volatile mcp_kreq_ether_recv_t * dst,mcp_kreq_ether_recv_t * src)23065e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23075e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src)
23085e7d8541SAndrew Gallatin {
23095e7d8541SAndrew Gallatin uint32_t low;
23105e7d8541SAndrew Gallatin
23115e7d8541SAndrew Gallatin low = src->addr_low;
23125e7d8541SAndrew Gallatin src->addr_low = 0xffffffff;
2313a1480dfbSAndrew Gallatin mxge_pio_copy(dst, src, 4 * sizeof (*src));
231473c7c83fSAndrew Gallatin wmb();
2315a1480dfbSAndrew Gallatin mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
231673c7c83fSAndrew Gallatin wmb();
231740385a5fSAndrew Gallatin src->addr_low = low;
23185e7d8541SAndrew Gallatin dst->addr_low = low;
231973c7c83fSAndrew Gallatin wmb();
23205e7d8541SAndrew Gallatin }
23215e7d8541SAndrew Gallatin
2322b2fc195eSAndrew Gallatin static int
mxge_get_buf_small(struct mxge_slice_state * ss,bus_dmamap_t map,int idx)23231e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2324b2fc195eSAndrew Gallatin {
2325b2fc195eSAndrew Gallatin bus_dma_segment_t seg;
2326b2fc195eSAndrew Gallatin struct mbuf *m;
23271e413cf9SAndrew Gallatin mxge_rx_ring_t *rx = &ss->rx_small;
2328b2fc195eSAndrew Gallatin int cnt, err;
2329b2fc195eSAndrew Gallatin
2330c6499eccSGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA);
2331b2fc195eSAndrew Gallatin if (m == NULL) {
2332b2fc195eSAndrew Gallatin rx->alloc_fail++;
2333b2fc195eSAndrew Gallatin err = ENOBUFS;
2334b2fc195eSAndrew Gallatin goto done;
2335b2fc195eSAndrew Gallatin }
2336b2fc195eSAndrew Gallatin m->m_len = MHLEN;
2337b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2338b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT);
2339b2fc195eSAndrew Gallatin if (err != 0) {
2340b2fc195eSAndrew Gallatin m_free(m);
2341b2fc195eSAndrew Gallatin goto done;
2342b2fc195eSAndrew Gallatin }
2343b2fc195eSAndrew Gallatin rx->info[idx].m = m;
2344b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low =
23456d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2346b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high =
23476d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2348b2fc195eSAndrew Gallatin
2349b2fc195eSAndrew Gallatin done:
2350adae7080SAndrew Gallatin if ((idx & 7) == 7)
2351adae7080SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2352b2fc195eSAndrew Gallatin return err;
2353b2fc195eSAndrew Gallatin }
2354b2fc195eSAndrew Gallatin
2355b2fc195eSAndrew Gallatin static int
mxge_get_buf_big(struct mxge_slice_state * ss,bus_dmamap_t map,int idx)23561e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2357b2fc195eSAndrew Gallatin {
2358053e637fSAndrew Gallatin bus_dma_segment_t seg[3];
2359b2fc195eSAndrew Gallatin struct mbuf *m;
23601e413cf9SAndrew Gallatin mxge_rx_ring_t *rx = &ss->rx_big;
2361053e637fSAndrew Gallatin int cnt, err, i;
2362b2fc195eSAndrew Gallatin
2363c6499eccSGleb Smirnoff m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2364b2fc195eSAndrew Gallatin if (m == NULL) {
2365b2fc195eSAndrew Gallatin rx->alloc_fail++;
2366b2fc195eSAndrew Gallatin err = ENOBUFS;
2367b2fc195eSAndrew Gallatin goto done;
2368b2fc195eSAndrew Gallatin }
23694d9a5852SAndrew Gallatin m->m_len = rx->mlen;
2370b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2371053e637fSAndrew Gallatin seg, &cnt, BUS_DMA_NOWAIT);
2372b2fc195eSAndrew Gallatin if (err != 0) {
2373b2fc195eSAndrew Gallatin m_free(m);
2374b2fc195eSAndrew Gallatin goto done;
2375b2fc195eSAndrew Gallatin }
2376b2fc195eSAndrew Gallatin rx->info[idx].m = m;
2377b0f7b922SAndrew Gallatin rx->shadow[idx].addr_low =
2378b0f7b922SAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2379b0f7b922SAndrew Gallatin rx->shadow[idx].addr_high =
2380b0f7b922SAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2381053e637fSAndrew Gallatin
2382b2fc195eSAndrew Gallatin done:
2383053e637fSAndrew Gallatin for (i = 0; i < rx->nbufs; i++) {
2384b2fc195eSAndrew Gallatin if ((idx & 7) == 7) {
23855e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7],
23865e7d8541SAndrew Gallatin &rx->shadow[idx - 7]);
2387b2fc195eSAndrew Gallatin }
2388053e637fSAndrew Gallatin idx++;
2389053e637fSAndrew Gallatin }
2390b2fc195eSAndrew Gallatin return err;
2391b2fc195eSAndrew Gallatin }
2392b2fc195eSAndrew Gallatin
239326dd49c6SAndrew Gallatin #ifdef INET6
239426dd49c6SAndrew Gallatin
239526dd49c6SAndrew Gallatin static uint16_t
mxge_csum_generic(uint16_t * raw,int len)239626dd49c6SAndrew Gallatin mxge_csum_generic(uint16_t *raw, int len)
239726dd49c6SAndrew Gallatin {
239826dd49c6SAndrew Gallatin uint32_t csum;
239926dd49c6SAndrew Gallatin
240026dd49c6SAndrew Gallatin csum = 0;
240126dd49c6SAndrew Gallatin while (len > 0) {
240226dd49c6SAndrew Gallatin csum += *raw;
240326dd49c6SAndrew Gallatin raw++;
240426dd49c6SAndrew Gallatin len -= 2;
240526dd49c6SAndrew Gallatin }
240626dd49c6SAndrew Gallatin csum = (csum >> 16) + (csum & 0xffff);
240726dd49c6SAndrew Gallatin csum = (csum >> 16) + (csum & 0xffff);
240826dd49c6SAndrew Gallatin return (uint16_t)csum;
240926dd49c6SAndrew Gallatin }
241026dd49c6SAndrew Gallatin
241126dd49c6SAndrew Gallatin static inline uint16_t
mxge_rx_csum6(void * p,struct mbuf * m,uint32_t csum)241226dd49c6SAndrew Gallatin mxge_rx_csum6(void *p, struct mbuf *m, uint32_t csum)
241326dd49c6SAndrew Gallatin {
241426dd49c6SAndrew Gallatin uint32_t partial;
241526dd49c6SAndrew Gallatin int nxt, cksum_offset;
241626dd49c6SAndrew Gallatin struct ip6_hdr *ip6 = p;
241726dd49c6SAndrew Gallatin uint16_t c;
241826dd49c6SAndrew Gallatin
241926dd49c6SAndrew Gallatin nxt = ip6->ip6_nxt;
242026dd49c6SAndrew Gallatin cksum_offset = sizeof (*ip6) + ETHER_HDR_LEN;
242126dd49c6SAndrew Gallatin if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) {
242226dd49c6SAndrew Gallatin cksum_offset = ip6_lasthdr(m, ETHER_HDR_LEN,
242326dd49c6SAndrew Gallatin IPPROTO_IPV6, &nxt);
242426dd49c6SAndrew Gallatin if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
242526dd49c6SAndrew Gallatin return (1);
242626dd49c6SAndrew Gallatin }
242726dd49c6SAndrew Gallatin
242826dd49c6SAndrew Gallatin /*
242926dd49c6SAndrew Gallatin * IPv6 headers do not contain a checksum, and hence
243026dd49c6SAndrew Gallatin * do not checksum to zero, so they don't "fall out"
243126dd49c6SAndrew Gallatin * of the partial checksum calculation like IPv4
243226dd49c6SAndrew Gallatin * headers do. We need to fix the partial checksum by
243326dd49c6SAndrew Gallatin * subtracting the checksum of the IPv6 header.
243426dd49c6SAndrew Gallatin */
243526dd49c6SAndrew Gallatin
243626dd49c6SAndrew Gallatin partial = mxge_csum_generic((uint16_t *)ip6, cksum_offset -
243726dd49c6SAndrew Gallatin ETHER_HDR_LEN);
243826dd49c6SAndrew Gallatin csum += ~partial;
243926dd49c6SAndrew Gallatin csum += (csum < ~partial);
244026dd49c6SAndrew Gallatin csum = (csum >> 16) + (csum & 0xFFFF);
244126dd49c6SAndrew Gallatin csum = (csum >> 16) + (csum & 0xFFFF);
244226dd49c6SAndrew Gallatin c = in6_cksum_pseudo(ip6, m->m_pkthdr.len - cksum_offset, nxt,
244326dd49c6SAndrew Gallatin csum);
244426dd49c6SAndrew Gallatin c ^= 0xffff;
244526dd49c6SAndrew Gallatin return (c);
244626dd49c6SAndrew Gallatin }
244726dd49c6SAndrew Gallatin #endif /* INET6 */
24489b03b0f3SAndrew Gallatin /*
24499b03b0f3SAndrew Gallatin * Myri10GE hardware checksums are not valid if the sender
24509b03b0f3SAndrew Gallatin * padded the frame with non-zero padding. This is because
24519b03b0f3SAndrew Gallatin * the firmware just does a simple 16-bit 1s complement
24529b03b0f3SAndrew Gallatin * checksum across the entire frame, excluding the first 14
2453053e637fSAndrew Gallatin * bytes. It is best to simply to check the checksum and
2454053e637fSAndrew Gallatin * tell the stack about it only if the checksum is good
24559b03b0f3SAndrew Gallatin */
24569b03b0f3SAndrew Gallatin
2457053e637fSAndrew Gallatin static inline uint16_t
mxge_rx_csum(struct mbuf * m,int csum)2458053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2459053e637fSAndrew Gallatin {
2460053e637fSAndrew Gallatin struct ether_header *eh;
246126dd49c6SAndrew Gallatin #ifdef INET
2462053e637fSAndrew Gallatin struct ip *ip;
246326dd49c6SAndrew Gallatin #endif
2464abc5b96bSAndrew Gallatin #if defined(INET) || defined(INET6)
246593037a67SJustin Hibbits int cap = if_getcapenable(m->m_pkthdr.rcvif);
2466abc5b96bSAndrew Gallatin #endif
246726dd49c6SAndrew Gallatin uint16_t c, etype;
246826dd49c6SAndrew Gallatin
2469053e637fSAndrew Gallatin eh = mtod(m, struct ether_header *);
247026dd49c6SAndrew Gallatin etype = ntohs(eh->ether_type);
247126dd49c6SAndrew Gallatin switch (etype) {
2472eb6219e3SAndrew Gallatin #ifdef INET
247326dd49c6SAndrew Gallatin case ETHERTYPE_IP:
247426dd49c6SAndrew Gallatin if ((cap & IFCAP_RXCSUM) == 0)
247526dd49c6SAndrew Gallatin return (1);
247626dd49c6SAndrew Gallatin ip = (struct ip *)(eh + 1);
247726dd49c6SAndrew Gallatin if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP)
247826dd49c6SAndrew Gallatin return (1);
2479053e637fSAndrew Gallatin c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
248026dd49c6SAndrew Gallatin htonl(ntohs(csum) + ntohs(ip->ip_len) -
248126dd49c6SAndrew Gallatin (ip->ip_hl << 2) + ip->ip_p));
2482053e637fSAndrew Gallatin c ^= 0xffff;
248326dd49c6SAndrew Gallatin break;
248426dd49c6SAndrew Gallatin #endif
248526dd49c6SAndrew Gallatin #ifdef INET6
248626dd49c6SAndrew Gallatin case ETHERTYPE_IPV6:
248726dd49c6SAndrew Gallatin if ((cap & IFCAP_RXCSUM_IPV6) == 0)
248826dd49c6SAndrew Gallatin return (1);
248926dd49c6SAndrew Gallatin c = mxge_rx_csum6((eh + 1), m, csum);
249026dd49c6SAndrew Gallatin break;
249126dd49c6SAndrew Gallatin #endif
249226dd49c6SAndrew Gallatin default:
249326dd49c6SAndrew Gallatin c = 1;
249426dd49c6SAndrew Gallatin }
2495053e637fSAndrew Gallatin return (c);
24965e7d8541SAndrew Gallatin }
2497053e637fSAndrew Gallatin
2498c792928fSAndrew Gallatin static void
mxge_vlan_tag_remove(struct mbuf * m,uint32_t * csum)2499c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2500c792928fSAndrew Gallatin {
2501c792928fSAndrew Gallatin struct ether_vlan_header *evl;
2502c792928fSAndrew Gallatin uint32_t partial;
2503c792928fSAndrew Gallatin
2504c792928fSAndrew Gallatin evl = mtod(m, struct ether_vlan_header *);
2505c792928fSAndrew Gallatin
2506c792928fSAndrew Gallatin /*
2507c792928fSAndrew Gallatin * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2508c792928fSAndrew Gallatin * after what the firmware thought was the end of the ethernet
2509c792928fSAndrew Gallatin * header.
2510c792928fSAndrew Gallatin */
2511c792928fSAndrew Gallatin
2512c792928fSAndrew Gallatin /* put checksum into host byte order */
2513c792928fSAndrew Gallatin *csum = ntohs(*csum);
2514c792928fSAndrew Gallatin partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2515c792928fSAndrew Gallatin (*csum) += ~partial;
2516c792928fSAndrew Gallatin (*csum) += ((*csum) < ~partial);
2517c792928fSAndrew Gallatin (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2518c792928fSAndrew Gallatin (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2519c792928fSAndrew Gallatin
2520c792928fSAndrew Gallatin /* restore checksum to network byte order;
2521c792928fSAndrew Gallatin later consumers expect this */
2522c792928fSAndrew Gallatin *csum = htons(*csum);
2523c792928fSAndrew Gallatin
2524c792928fSAndrew Gallatin /* save the tag */
252537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2526c792928fSAndrew Gallatin m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
252737d89b0cSAndrew Gallatin #else
252837d89b0cSAndrew Gallatin {
252937d89b0cSAndrew Gallatin struct m_tag *mtag;
253037d89b0cSAndrew Gallatin mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
253137d89b0cSAndrew Gallatin M_NOWAIT);
253237d89b0cSAndrew Gallatin if (mtag == NULL)
253337d89b0cSAndrew Gallatin return;
253437d89b0cSAndrew Gallatin VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
253537d89b0cSAndrew Gallatin m_tag_prepend(m, mtag);
253637d89b0cSAndrew Gallatin }
253737d89b0cSAndrew Gallatin
253837d89b0cSAndrew Gallatin #endif
253937d89b0cSAndrew Gallatin m->m_flags |= M_VLANTAG;
2540c792928fSAndrew Gallatin
2541c792928fSAndrew Gallatin /*
2542c792928fSAndrew Gallatin * Remove the 802.1q header by copying the Ethernet
2543c792928fSAndrew Gallatin * addresses over it and adjusting the beginning of
2544c792928fSAndrew Gallatin * the data in the mbuf. The encapsulated Ethernet
2545c792928fSAndrew Gallatin * type field is already in place.
2546c792928fSAndrew Gallatin */
2547c792928fSAndrew Gallatin bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2548c792928fSAndrew Gallatin ETHER_HDR_LEN - ETHER_TYPE_LEN);
2549c792928fSAndrew Gallatin m_adj(m, ETHER_VLAN_ENCAP_LEN);
2550c792928fSAndrew Gallatin }
2551c792928fSAndrew Gallatin
25525e7d8541SAndrew Gallatin static inline void
mxge_rx_done_big(struct mxge_slice_state * ss,uint32_t len,uint32_t csum,int lro)255326dd49c6SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len,
255426dd49c6SAndrew Gallatin uint32_t csum, int lro)
2555b2fc195eSAndrew Gallatin {
25561e413cf9SAndrew Gallatin mxge_softc_t *sc;
255793037a67SJustin Hibbits if_t ifp;
2558053e637fSAndrew Gallatin struct mbuf *m;
2559c792928fSAndrew Gallatin struct ether_header *eh;
25601e413cf9SAndrew Gallatin mxge_rx_ring_t *rx;
2561053e637fSAndrew Gallatin bus_dmamap_t old_map;
2562b2fc195eSAndrew Gallatin int idx;
2563b2fc195eSAndrew Gallatin
25641e413cf9SAndrew Gallatin sc = ss->sc;
2565b2fc195eSAndrew Gallatin ifp = sc->ifp;
25661e413cf9SAndrew Gallatin rx = &ss->rx_big;
2567b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask;
2568053e637fSAndrew Gallatin rx->cnt += rx->nbufs;
2569b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */
2570b2fc195eSAndrew Gallatin m = rx->info[idx].m;
2571b2fc195eSAndrew Gallatin /* try to replace the received mbuf */
25721e413cf9SAndrew Gallatin if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2573053e637fSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */
2574f3f040d9SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2575053e637fSAndrew Gallatin return;
2576b2fc195eSAndrew Gallatin }
2577053e637fSAndrew Gallatin
2578b2fc195eSAndrew Gallatin /* unmap the received buffer */
2579b2fc195eSAndrew Gallatin old_map = rx->info[idx].map;
2580b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2581b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map);
2582b2fc195eSAndrew Gallatin
2583b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */
2584b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map;
2585b2fc195eSAndrew Gallatin rx->extra_map = old_map;
2586b2fc195eSAndrew Gallatin
2587053e637fSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly
2588053e637fSAndrew Gallatin * aligned */
25895e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD;
2590b2fc195eSAndrew Gallatin
2591053e637fSAndrew Gallatin m->m_pkthdr.rcvif = ifp;
2592053e637fSAndrew Gallatin m->m_len = m->m_pkthdr.len = len;
25931e413cf9SAndrew Gallatin ss->ipackets++;
2594c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *);
2595c792928fSAndrew Gallatin if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2596c792928fSAndrew Gallatin mxge_vlan_tag_remove(m, &csum);
2597c792928fSAndrew Gallatin }
2598eacb70baSSepherosa Ziehau /* flowid only valid if RSS hashing is enabled */
2599eacb70baSSepherosa Ziehau if (sc->num_slices > 1) {
2600eacb70baSSepherosa Ziehau m->m_pkthdr.flowid = (ss - sc->ss);
2601eacb70baSSepherosa Ziehau M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2602eacb70baSSepherosa Ziehau }
2603b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */
260493037a67SJustin Hibbits if ((if_getcapenable(ifp) & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
260526dd49c6SAndrew Gallatin (0 == mxge_rx_csum(m, csum))) {
260626dd49c6SAndrew Gallatin /* Tell the stack that the checksum is good */
2607053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff;
260826dd49c6SAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
260926dd49c6SAndrew Gallatin CSUM_DATA_VALID;
261026dd49c6SAndrew Gallatin
261126dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
261226dd49c6SAndrew Gallatin if (lro && (0 == tcp_lro_rx(&ss->lc, m, 0)))
261326dd49c6SAndrew Gallatin return;
261426dd49c6SAndrew Gallatin #endif
2615b2fc195eSAndrew Gallatin }
2616053e637fSAndrew Gallatin /* pass the frame up the stack */
261793037a67SJustin Hibbits if_input(ifp, m);
2618b2fc195eSAndrew Gallatin }
2619b2fc195eSAndrew Gallatin
2620b2fc195eSAndrew Gallatin static inline void
mxge_rx_done_small(struct mxge_slice_state * ss,uint32_t len,uint32_t csum,int lro)262126dd49c6SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len,
262226dd49c6SAndrew Gallatin uint32_t csum, int lro)
2623b2fc195eSAndrew Gallatin {
26241e413cf9SAndrew Gallatin mxge_softc_t *sc;
262593037a67SJustin Hibbits if_t ifp;
2626c792928fSAndrew Gallatin struct ether_header *eh;
2627b2fc195eSAndrew Gallatin struct mbuf *m;
26281e413cf9SAndrew Gallatin mxge_rx_ring_t *rx;
2629b2fc195eSAndrew Gallatin bus_dmamap_t old_map;
2630b2fc195eSAndrew Gallatin int idx;
2631b2fc195eSAndrew Gallatin
26321e413cf9SAndrew Gallatin sc = ss->sc;
2633b2fc195eSAndrew Gallatin ifp = sc->ifp;
26341e413cf9SAndrew Gallatin rx = &ss->rx_small;
2635b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask;
2636b2fc195eSAndrew Gallatin rx->cnt++;
2637b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */
2638b2fc195eSAndrew Gallatin m = rx->info[idx].m;
2639b2fc195eSAndrew Gallatin /* try to replace the received mbuf */
26401e413cf9SAndrew Gallatin if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2641b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */
2642f3f040d9SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2643b2fc195eSAndrew Gallatin return;
2644b2fc195eSAndrew Gallatin }
2645b2fc195eSAndrew Gallatin
2646b2fc195eSAndrew Gallatin /* unmap the received buffer */
2647b2fc195eSAndrew Gallatin old_map = rx->info[idx].map;
2648b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2649b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map);
2650b2fc195eSAndrew Gallatin
2651b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */
2652b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map;
2653b2fc195eSAndrew Gallatin rx->extra_map = old_map;
2654b2fc195eSAndrew Gallatin
2655b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly
2656b2fc195eSAndrew Gallatin * aligned */
26575e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD;
2658b2fc195eSAndrew Gallatin
26599b03b0f3SAndrew Gallatin m->m_pkthdr.rcvif = ifp;
26609b03b0f3SAndrew Gallatin m->m_len = m->m_pkthdr.len = len;
26611e413cf9SAndrew Gallatin ss->ipackets++;
2662c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *);
2663c792928fSAndrew Gallatin if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2664c792928fSAndrew Gallatin mxge_vlan_tag_remove(m, &csum);
2665c792928fSAndrew Gallatin }
2666eacb70baSSepherosa Ziehau /* flowid only valid if RSS hashing is enabled */
2667eacb70baSSepherosa Ziehau if (sc->num_slices > 1) {
2668eacb70baSSepherosa Ziehau m->m_pkthdr.flowid = (ss - sc->ss);
2669eacb70baSSepherosa Ziehau M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2670eacb70baSSepherosa Ziehau }
2671b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */
267293037a67SJustin Hibbits if ((if_getcapenable(ifp) & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
267326dd49c6SAndrew Gallatin (0 == mxge_rx_csum(m, csum))) {
267426dd49c6SAndrew Gallatin /* Tell the stack that the checksum is good */
2675053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff;
267626dd49c6SAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
267726dd49c6SAndrew Gallatin CSUM_DATA_VALID;
267826dd49c6SAndrew Gallatin
267926dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
268026dd49c6SAndrew Gallatin if (lro && (0 == tcp_lro_rx(&ss->lc, m, csum)))
268126dd49c6SAndrew Gallatin return;
268226dd49c6SAndrew Gallatin #endif
2683053e637fSAndrew Gallatin }
2684b2fc195eSAndrew Gallatin /* pass the frame up the stack */
268593037a67SJustin Hibbits if_input(ifp, m);
2686b2fc195eSAndrew Gallatin }
2687b2fc195eSAndrew Gallatin
2688b2fc195eSAndrew Gallatin static inline void
mxge_clean_rx_done(struct mxge_slice_state * ss)26891e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
26905e7d8541SAndrew Gallatin {
26911e413cf9SAndrew Gallatin mxge_rx_done_t *rx_done = &ss->rx_done;
26925e7d8541SAndrew Gallatin int limit = 0;
26935e7d8541SAndrew Gallatin uint16_t length;
26945e7d8541SAndrew Gallatin uint16_t checksum;
269526dd49c6SAndrew Gallatin int lro;
26965e7d8541SAndrew Gallatin
269793037a67SJustin Hibbits lro = if_getcapenable(ss->sc->ifp) & IFCAP_LRO;
26985e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) {
26995e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length);
27005e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0;
2701053e637fSAndrew Gallatin checksum = rx_done->entry[rx_done->idx].checksum;
2702b4db9009SAndrew Gallatin if (length <= (MHLEN - MXGEFW_PAD))
270326dd49c6SAndrew Gallatin mxge_rx_done_small(ss, length, checksum, lro);
27045e7d8541SAndrew Gallatin else
270526dd49c6SAndrew Gallatin mxge_rx_done_big(ss, length, checksum, lro);
27065e7d8541SAndrew Gallatin rx_done->cnt++;
2707adae7080SAndrew Gallatin rx_done->idx = rx_done->cnt & rx_done->mask;
27085e7d8541SAndrew Gallatin
27095e7d8541SAndrew Gallatin /* limit potential for livelock */
2710f616ebc7SAndrew Gallatin if (__predict_false(++limit > rx_done->mask / 2))
27115e7d8541SAndrew Gallatin break;
2712053e637fSAndrew Gallatin }
271326dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
27146dd38b87SSepherosa Ziehau tcp_lro_flush_all(&ss->lc);
2715eb6219e3SAndrew Gallatin #endif
27165e7d8541SAndrew Gallatin }
27175e7d8541SAndrew Gallatin
27185e7d8541SAndrew Gallatin static inline void
mxge_tx_done(struct mxge_slice_state * ss,uint32_t mcp_idx)27191e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2720b2fc195eSAndrew Gallatin {
272193037a67SJustin Hibbits if_t ifp __unused;
27221e413cf9SAndrew Gallatin mxge_tx_ring_t *tx;
2723b2fc195eSAndrew Gallatin struct mbuf *m;
2724b2fc195eSAndrew Gallatin bus_dmamap_t map;
2725f616ebc7SAndrew Gallatin int idx;
2726c6cb3e3fSAndrew Gallatin int *flags;
2727b2fc195eSAndrew Gallatin
27281e413cf9SAndrew Gallatin tx = &ss->tx;
27291e413cf9SAndrew Gallatin ifp = ss->sc->ifp;
27305e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) {
2731b2fc195eSAndrew Gallatin idx = tx->done & tx->mask;
2732b2fc195eSAndrew Gallatin tx->done++;
2733b2fc195eSAndrew Gallatin m = tx->info[idx].m;
2734b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first
2735b2fc195eSAndrew Gallatin segment per-mbuf */
2736b2fc195eSAndrew Gallatin if (m != NULL) {
273771032832SAndrew Gallatin ss->obytes += m->m_pkthdr.len;
273871032832SAndrew Gallatin if (m->m_flags & M_MCAST)
273971032832SAndrew Gallatin ss->omcasts++;
2740c6cb3e3fSAndrew Gallatin ss->opackets++;
2741b2fc195eSAndrew Gallatin tx->info[idx].m = NULL;
2742b2fc195eSAndrew Gallatin map = tx->info[idx].map;
2743b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map);
2744b2fc195eSAndrew Gallatin m_freem(m);
2745b2fc195eSAndrew Gallatin }
27465e7d8541SAndrew Gallatin if (tx->info[idx].flag) {
27475e7d8541SAndrew Gallatin tx->info[idx].flag = 0;
27485e7d8541SAndrew Gallatin tx->pkt_done++;
27495e7d8541SAndrew Gallatin }
2750b2fc195eSAndrew Gallatin }
2751b2fc195eSAndrew Gallatin
2752b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that
2753b2fc195eSAndrew Gallatin its OK to send packets */
2754c6cb3e3fSAndrew Gallatin flags = &ss->if_drv_flags;
27551e413cf9SAndrew Gallatin mtx_lock(&ss->tx.mtx);
2756c6cb3e3fSAndrew Gallatin if ((*flags) & IFF_DRV_OACTIVE &&
2757c6cb3e3fSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) {
2758c6cb3e3fSAndrew Gallatin *(flags) &= ~IFF_DRV_OACTIVE;
27591e413cf9SAndrew Gallatin ss->tx.wake++;
27601e413cf9SAndrew Gallatin mxge_start_locked(ss);
2761b2fc195eSAndrew Gallatin }
2762c6cb3e3fSAndrew Gallatin if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2763c6cb3e3fSAndrew Gallatin /* let the NIC stop polling this queue, since there
2764c6cb3e3fSAndrew Gallatin * are no more transmits pending */
2765c6cb3e3fSAndrew Gallatin if (tx->req == tx->done) {
2766c6cb3e3fSAndrew Gallatin *tx->send_stop = 1;
2767c6cb3e3fSAndrew Gallatin tx->queue_active = 0;
2768c6cb3e3fSAndrew Gallatin tx->deactivate++;
2769c6cb3e3fSAndrew Gallatin wmb();
2770c6cb3e3fSAndrew Gallatin }
2771c6cb3e3fSAndrew Gallatin }
2772c6cb3e3fSAndrew Gallatin mtx_unlock(&ss->tx.mtx);
2773b2fc195eSAndrew Gallatin }
2774b2fc195eSAndrew Gallatin
277501638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2776c587e59fSAndrew Gallatin {
2777c587e59fSAndrew Gallatin {IFM_10G_CX4, 0x7f, "10GBASE-CX4 (module)"},
2778c587e59fSAndrew Gallatin {IFM_10G_SR, (1 << 7), "10GBASE-SR"},
2779c587e59fSAndrew Gallatin {IFM_10G_LR, (1 << 6), "10GBASE-LR"},
2780c587e59fSAndrew Gallatin {0, (1 << 5), "10GBASE-ER"},
278101638550SAndrew Gallatin {IFM_10G_LRM, (1 << 4), "10GBASE-LRM"},
2782c587e59fSAndrew Gallatin {0, (1 << 3), "10GBASE-SW"},
2783c587e59fSAndrew Gallatin {0, (1 << 2), "10GBASE-LW"},
2784c587e59fSAndrew Gallatin {0, (1 << 1), "10GBASE-EW"},
2785c587e59fSAndrew Gallatin {0, (1 << 0), "Reserved"}
2786c587e59fSAndrew Gallatin };
278701638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
278801638550SAndrew Gallatin {
278951bc2092SAndrew Gallatin {IFM_10G_TWINAX, 0, "10GBASE-Twinax"},
27904ae3322fSAndrew Gallatin {0, (1 << 7), "Reserved"},
279101638550SAndrew Gallatin {IFM_10G_LRM, (1 << 6), "10GBASE-LRM"},
279201638550SAndrew Gallatin {IFM_10G_LR, (1 << 5), "10GBASE-LR"},
279356b67858SAndrew Gallatin {IFM_10G_SR, (1 << 4), "10GBASE-SR"},
279456b67858SAndrew Gallatin {IFM_10G_TWINAX,(1 << 0), "10GBASE-Twinax"}
279501638550SAndrew Gallatin };
2796c587e59fSAndrew Gallatin
2797c587e59fSAndrew Gallatin static void
mxge_media_set(mxge_softc_t * sc,int media_type)2798c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type)
2799c587e59fSAndrew Gallatin {
2800c406ad2eSAndrew Gallatin
2801c406ad2eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type,
2802c406ad2eSAndrew Gallatin 0, NULL);
2803c406ad2eSAndrew Gallatin ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
2804c406ad2eSAndrew Gallatin sc->current_media = media_type;
2805c406ad2eSAndrew Gallatin sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
2806c587e59fSAndrew Gallatin }
2807c587e59fSAndrew Gallatin
2808c587e59fSAndrew Gallatin static void
mxge_media_init(mxge_softc_t * sc)2809c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc)
2810c587e59fSAndrew Gallatin {
2811c587e59fSAndrew Gallatin char *ptr;
2812c406ad2eSAndrew Gallatin int i;
2813c587e59fSAndrew Gallatin
2814c406ad2eSAndrew Gallatin ifmedia_removeall(&sc->media);
2815c406ad2eSAndrew Gallatin mxge_media_set(sc, IFM_AUTO);
2816c587e59fSAndrew Gallatin
2817c587e59fSAndrew Gallatin /*
2818c587e59fSAndrew Gallatin * parse the product code to deterimine the interface type
2819c587e59fSAndrew Gallatin * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2820c587e59fSAndrew Gallatin * after the 3rd dash in the driver's cached copy of the
2821c587e59fSAndrew Gallatin * EEPROM's product code string.
2822c587e59fSAndrew Gallatin */
2823c587e59fSAndrew Gallatin ptr = sc->product_code_string;
2824c587e59fSAndrew Gallatin if (ptr == NULL) {
2825c587e59fSAndrew Gallatin device_printf(sc->dev, "Missing product code\n");
2826c406ad2eSAndrew Gallatin return;
2827c587e59fSAndrew Gallatin }
2828c587e59fSAndrew Gallatin
2829c587e59fSAndrew Gallatin for (i = 0; i < 3; i++, ptr++) {
2830dc15eac0SEd Schouten ptr = strchr(ptr, '-');
2831c587e59fSAndrew Gallatin if (ptr == NULL) {
2832c587e59fSAndrew Gallatin device_printf(sc->dev,
2833c587e59fSAndrew Gallatin "only %d dashes in PC?!?\n", i);
2834c587e59fSAndrew Gallatin return;
2835c587e59fSAndrew Gallatin }
2836c587e59fSAndrew Gallatin }
283730882b10SAndrew Gallatin if (*ptr == 'C' || *(ptr +1) == 'C') {
283801638550SAndrew Gallatin /* -C is CX4 */
2839c406ad2eSAndrew Gallatin sc->connector = MXGE_CX4;
2840c406ad2eSAndrew Gallatin mxge_media_set(sc, IFM_10G_CX4);
2841c406ad2eSAndrew Gallatin } else if (*ptr == 'Q') {
284201638550SAndrew Gallatin /* -Q is Quad Ribbon Fiber */
2843c406ad2eSAndrew Gallatin sc->connector = MXGE_QRF;
2844c587e59fSAndrew Gallatin device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2845c587e59fSAndrew Gallatin /* FreeBSD has no media type for Quad ribbon fiber */
2846c406ad2eSAndrew Gallatin } else if (*ptr == 'R') {
2847c406ad2eSAndrew Gallatin /* -R is XFP */
2848c406ad2eSAndrew Gallatin sc->connector = MXGE_XFP;
2849c406ad2eSAndrew Gallatin } else if (*ptr == 'S' || *(ptr +1) == 'S') {
2850c406ad2eSAndrew Gallatin /* -S or -2S is SFP+ */
2851c406ad2eSAndrew Gallatin sc->connector = MXGE_SFP;
2852c406ad2eSAndrew Gallatin } else {
2853c406ad2eSAndrew Gallatin device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2854c406ad2eSAndrew Gallatin }
2855c587e59fSAndrew Gallatin }
2856c587e59fSAndrew Gallatin
2857c406ad2eSAndrew Gallatin /*
2858c406ad2eSAndrew Gallatin * Determine the media type for a NIC. Some XFPs will identify
2859c406ad2eSAndrew Gallatin * themselves only when their link is up, so this is initiated via a
2860c406ad2eSAndrew Gallatin * link up interrupt. However, this can potentially take up to
2861c406ad2eSAndrew Gallatin * several milliseconds, so it is run via the watchdog routine, rather
2862c406ad2eSAndrew Gallatin * than in the interrupt handler itself.
2863c406ad2eSAndrew Gallatin */
2864c406ad2eSAndrew Gallatin static void
mxge_media_probe(mxge_softc_t * sc)2865c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2866c406ad2eSAndrew Gallatin {
2867c406ad2eSAndrew Gallatin mxge_cmd_t cmd;
2868c406ad2eSAndrew Gallatin char *cage_type;
2869c406ad2eSAndrew Gallatin
2870c406ad2eSAndrew Gallatin struct mxge_media_type *mxge_media_types = NULL;
2871c406ad2eSAndrew Gallatin int i, err, ms, mxge_media_type_entries;
2872c406ad2eSAndrew Gallatin uint32_t byte;
2873c406ad2eSAndrew Gallatin
2874c406ad2eSAndrew Gallatin sc->need_media_probe = 0;
2875c406ad2eSAndrew Gallatin
2876c406ad2eSAndrew Gallatin if (sc->connector == MXGE_XFP) {
287701638550SAndrew Gallatin /* -R is XFP */
287801638550SAndrew Gallatin mxge_media_types = mxge_xfp_media_types;
287901638550SAndrew Gallatin mxge_media_type_entries =
288073a1170aSPedro F. Giffuni nitems(mxge_xfp_media_types);
288101638550SAndrew Gallatin byte = MXGE_XFP_COMPLIANCE_BYTE;
288201638550SAndrew Gallatin cage_type = "XFP";
2883c406ad2eSAndrew Gallatin } else if (sc->connector == MXGE_SFP) {
288401638550SAndrew Gallatin /* -S or -2S is SFP+ */
288501638550SAndrew Gallatin mxge_media_types = mxge_sfp_media_types;
288601638550SAndrew Gallatin mxge_media_type_entries =
288773a1170aSPedro F. Giffuni nitems(mxge_sfp_media_types);
288801638550SAndrew Gallatin cage_type = "SFP+";
288901638550SAndrew Gallatin byte = 3;
2890c406ad2eSAndrew Gallatin } else {
2891c406ad2eSAndrew Gallatin /* nothing to do; media type cannot change */
2892c587e59fSAndrew Gallatin return;
2893c587e59fSAndrew Gallatin }
2894c587e59fSAndrew Gallatin
2895c587e59fSAndrew Gallatin /*
2896c587e59fSAndrew Gallatin * At this point we know the NIC has an XFP cage, so now we
2897c587e59fSAndrew Gallatin * try to determine what is in the cage by using the
2898c587e59fSAndrew Gallatin * firmware's XFP I2C commands to read the XFP 10GbE compilance
2899c587e59fSAndrew Gallatin * register. We read just one byte, which may take over
2900c587e59fSAndrew Gallatin * a millisecond
2901c587e59fSAndrew Gallatin */
2902c587e59fSAndrew Gallatin
2903c587e59fSAndrew Gallatin cmd.data0 = 0; /* just fetch 1 byte, not all 256 */
290401638550SAndrew Gallatin cmd.data1 = byte;
290501638550SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
290601638550SAndrew Gallatin if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2907c587e59fSAndrew Gallatin device_printf(sc->dev, "failed to read XFP\n");
2908c587e59fSAndrew Gallatin }
290901638550SAndrew Gallatin if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
291001638550SAndrew Gallatin device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2911c587e59fSAndrew Gallatin }
2912c587e59fSAndrew Gallatin if (err != MXGEFW_CMD_OK) {
2913c587e59fSAndrew Gallatin return;
2914c587e59fSAndrew Gallatin }
2915c587e59fSAndrew Gallatin
2916c587e59fSAndrew Gallatin /* now we wait for the data to be cached */
291701638550SAndrew Gallatin cmd.data0 = byte;
291801638550SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2919c587e59fSAndrew Gallatin for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2920c587e59fSAndrew Gallatin DELAY(1000);
292101638550SAndrew Gallatin cmd.data0 = byte;
292201638550SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2923c587e59fSAndrew Gallatin }
2924c587e59fSAndrew Gallatin if (err != MXGEFW_CMD_OK) {
292501638550SAndrew Gallatin device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
292601638550SAndrew Gallatin cage_type, err, ms);
2927c587e59fSAndrew Gallatin return;
2928c587e59fSAndrew Gallatin }
2929c587e59fSAndrew Gallatin
2930c587e59fSAndrew Gallatin if (cmd.data0 == mxge_media_types[0].bitmask) {
2931c587e59fSAndrew Gallatin if (mxge_verbose)
293201638550SAndrew Gallatin device_printf(sc->dev, "%s:%s\n", cage_type,
2933c587e59fSAndrew Gallatin mxge_media_types[0].name);
2934c406ad2eSAndrew Gallatin if (sc->current_media != mxge_media_types[0].flag) {
2935c406ad2eSAndrew Gallatin mxge_media_init(sc);
2936c406ad2eSAndrew Gallatin mxge_media_set(sc, mxge_media_types[0].flag);
2937c406ad2eSAndrew Gallatin }
2938c587e59fSAndrew Gallatin return;
2939c587e59fSAndrew Gallatin }
294001638550SAndrew Gallatin for (i = 1; i < mxge_media_type_entries; i++) {
2941c587e59fSAndrew Gallatin if (cmd.data0 & mxge_media_types[i].bitmask) {
2942c587e59fSAndrew Gallatin if (mxge_verbose)
294301638550SAndrew Gallatin device_printf(sc->dev, "%s:%s\n",
294401638550SAndrew Gallatin cage_type,
2945c587e59fSAndrew Gallatin mxge_media_types[i].name);
2946c587e59fSAndrew Gallatin
2947c406ad2eSAndrew Gallatin if (sc->current_media != mxge_media_types[i].flag) {
2948c406ad2eSAndrew Gallatin mxge_media_init(sc);
2949c406ad2eSAndrew Gallatin mxge_media_set(sc, mxge_media_types[i].flag);
2950c406ad2eSAndrew Gallatin }
2951c587e59fSAndrew Gallatin return;
2952c587e59fSAndrew Gallatin }
2953c587e59fSAndrew Gallatin }
2954c406ad2eSAndrew Gallatin if (mxge_verbose)
2955c406ad2eSAndrew Gallatin device_printf(sc->dev, "%s media 0x%x unknown\n",
2956c406ad2eSAndrew Gallatin cage_type, cmd.data0);
2957c587e59fSAndrew Gallatin
2958c587e59fSAndrew Gallatin return;
2959c587e59fSAndrew Gallatin }
2960c587e59fSAndrew Gallatin
2961b2fc195eSAndrew Gallatin static void
mxge_intr(void * arg)29626d87a65dSAndrew Gallatin mxge_intr(void *arg)
2963b2fc195eSAndrew Gallatin {
29641e413cf9SAndrew Gallatin struct mxge_slice_state *ss = arg;
29651e413cf9SAndrew Gallatin mxge_softc_t *sc = ss->sc;
29661e413cf9SAndrew Gallatin mcp_irq_data_t *stats = ss->fw_stats;
29671e413cf9SAndrew Gallatin mxge_tx_ring_t *tx = &ss->tx;
29681e413cf9SAndrew Gallatin mxge_rx_done_t *rx_done = &ss->rx_done;
29695e7d8541SAndrew Gallatin uint32_t send_done_count;
29705e7d8541SAndrew Gallatin uint8_t valid;
2971b2fc195eSAndrew Gallatin
29725e7d8541SAndrew Gallatin /* make sure the DMA has finished */
29735e7d8541SAndrew Gallatin if (!stats->valid) {
29745e7d8541SAndrew Gallatin return;
2975b2fc195eSAndrew Gallatin }
29765e7d8541SAndrew Gallatin valid = stats->valid;
2977b2fc195eSAndrew Gallatin
297891ed8913SAndrew Gallatin if (sc->legacy_irq) {
29795e7d8541SAndrew Gallatin /* lower legacy IRQ */
29805e7d8541SAndrew Gallatin *sc->irq_deassert = 0;
29815e7d8541SAndrew Gallatin if (!mxge_deassert_wait)
29825e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */
29835e7d8541SAndrew Gallatin stats->valid = 0;
2984dc8731d4SAndrew Gallatin } else {
2985dc8731d4SAndrew Gallatin stats->valid = 0;
2986dc8731d4SAndrew Gallatin }
2987dc8731d4SAndrew Gallatin
2988dc8731d4SAndrew Gallatin /* loop while waiting for legacy irq deassertion */
29895e7d8541SAndrew Gallatin do {
29905e7d8541SAndrew Gallatin /* check for transmit completes and receives */
29915e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count);
29925e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) ||
29935e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) {
2994c6cb3e3fSAndrew Gallatin if (send_done_count != tx->pkt_done)
29951e413cf9SAndrew Gallatin mxge_tx_done(ss, (int)send_done_count);
29961e413cf9SAndrew Gallatin mxge_clean_rx_done(ss);
29975e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count);
2998b2fc195eSAndrew Gallatin }
299991ed8913SAndrew Gallatin if (sc->legacy_irq && mxge_deassert_wait)
300073c7c83fSAndrew Gallatin wmb();
30015e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid));
3002b2fc195eSAndrew Gallatin
3003c6cb3e3fSAndrew Gallatin /* fw link & error stats meaningful only on the first slice */
3004c6cb3e3fSAndrew Gallatin if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
30055e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) {
30065e7d8541SAndrew Gallatin sc->link_state = stats->link_up;
3007b2fc195eSAndrew Gallatin if (sc->link_state) {
30085e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP);
30095e7d8541SAndrew Gallatin if (mxge_verbose)
30105e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n");
3011b2fc195eSAndrew Gallatin } else {
30125e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN);
30135e7d8541SAndrew Gallatin if (mxge_verbose)
30145e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n");
3015b2fc195eSAndrew Gallatin }
3016c587e59fSAndrew Gallatin sc->need_media_probe = 1;
3017b2fc195eSAndrew Gallatin }
3018b2fc195eSAndrew Gallatin if (sc->rdma_tags_available !=
30191e413cf9SAndrew Gallatin be32toh(stats->rdma_tags_available)) {
3020b2fc195eSAndrew Gallatin sc->rdma_tags_available =
30211e413cf9SAndrew Gallatin be32toh(stats->rdma_tags_available);
30225e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags "
30235e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available);
30245e7d8541SAndrew Gallatin }
3025c587e59fSAndrew Gallatin
3026c587e59fSAndrew Gallatin if (stats->link_down) {
30275e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down;
3028c587e59fSAndrew Gallatin sc->link_state = 0;
3029c587e59fSAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3030c587e59fSAndrew Gallatin }
3031b2fc195eSAndrew Gallatin }
3032b2fc195eSAndrew Gallatin
30335e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */
30345e7d8541SAndrew Gallatin if (valid & 0x1)
30351e413cf9SAndrew Gallatin *ss->irq_claim = be32toh(3);
30361e413cf9SAndrew Gallatin *(ss->irq_claim + 1) = be32toh(3);
3037b2fc195eSAndrew Gallatin }
3038b2fc195eSAndrew Gallatin
3039b2fc195eSAndrew Gallatin static void
mxge_init(void * arg)30406d87a65dSAndrew Gallatin mxge_init(void *arg)
3041b2fc195eSAndrew Gallatin {
30423cae7311SAndrew Gallatin mxge_softc_t *sc = arg;
304393037a67SJustin Hibbits if_t ifp = sc->ifp;
30443cae7311SAndrew Gallatin
30453cae7311SAndrew Gallatin mtx_lock(&sc->driver_mtx);
304693037a67SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
30473cae7311SAndrew Gallatin (void) mxge_open(sc);
30483cae7311SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
3049b2fc195eSAndrew Gallatin }
3050b2fc195eSAndrew Gallatin
3051b2fc195eSAndrew Gallatin static void
mxge_free_slice_mbufs(struct mxge_slice_state * ss)30521e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
30531e413cf9SAndrew Gallatin {
30541e413cf9SAndrew Gallatin int i;
30551e413cf9SAndrew Gallatin
305626dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
305726dd49c6SAndrew Gallatin tcp_lro_free(&ss->lc);
305826dd49c6SAndrew Gallatin #endif
30591e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i++) {
30601e413cf9SAndrew Gallatin if (ss->rx_big.info[i].m == NULL)
30611e413cf9SAndrew Gallatin continue;
30621e413cf9SAndrew Gallatin bus_dmamap_unload(ss->rx_big.dmat,
30631e413cf9SAndrew Gallatin ss->rx_big.info[i].map);
30641e413cf9SAndrew Gallatin m_freem(ss->rx_big.info[i].m);
30651e413cf9SAndrew Gallatin ss->rx_big.info[i].m = NULL;
30661e413cf9SAndrew Gallatin }
30671e413cf9SAndrew Gallatin
30681e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_small.mask; i++) {
30691e413cf9SAndrew Gallatin if (ss->rx_small.info[i].m == NULL)
30701e413cf9SAndrew Gallatin continue;
30711e413cf9SAndrew Gallatin bus_dmamap_unload(ss->rx_small.dmat,
30721e413cf9SAndrew Gallatin ss->rx_small.info[i].map);
30731e413cf9SAndrew Gallatin m_freem(ss->rx_small.info[i].m);
30741e413cf9SAndrew Gallatin ss->rx_small.info[i].m = NULL;
30751e413cf9SAndrew Gallatin }
30761e413cf9SAndrew Gallatin
30771e413cf9SAndrew Gallatin /* transmit ring used only on the first slice */
30781e413cf9SAndrew Gallatin if (ss->tx.info == NULL)
30791e413cf9SAndrew Gallatin return;
30801e413cf9SAndrew Gallatin
30811e413cf9SAndrew Gallatin for (i = 0; i <= ss->tx.mask; i++) {
30821e413cf9SAndrew Gallatin ss->tx.info[i].flag = 0;
30831e413cf9SAndrew Gallatin if (ss->tx.info[i].m == NULL)
30841e413cf9SAndrew Gallatin continue;
30851e413cf9SAndrew Gallatin bus_dmamap_unload(ss->tx.dmat,
30861e413cf9SAndrew Gallatin ss->tx.info[i].map);
30871e413cf9SAndrew Gallatin m_freem(ss->tx.info[i].m);
30881e413cf9SAndrew Gallatin ss->tx.info[i].m = NULL;
30891e413cf9SAndrew Gallatin }
30901e413cf9SAndrew Gallatin }
30911e413cf9SAndrew Gallatin
30921e413cf9SAndrew Gallatin static void
mxge_free_mbufs(mxge_softc_t * sc)30936d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3094b2fc195eSAndrew Gallatin {
30951e413cf9SAndrew Gallatin int slice;
30961e413cf9SAndrew Gallatin
30971e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++)
30981e413cf9SAndrew Gallatin mxge_free_slice_mbufs(&sc->ss[slice]);
30991e413cf9SAndrew Gallatin }
31001e413cf9SAndrew Gallatin
31011e413cf9SAndrew Gallatin static void
mxge_free_slice_rings(struct mxge_slice_state * ss)31021e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
31031e413cf9SAndrew Gallatin {
3104b2fc195eSAndrew Gallatin int i;
3105b2fc195eSAndrew Gallatin
31061e413cf9SAndrew Gallatin if (ss->rx_done.entry != NULL)
31071e413cf9SAndrew Gallatin mxge_dma_free(&ss->rx_done.dma);
31081e413cf9SAndrew Gallatin ss->rx_done.entry = NULL;
3109b2fc195eSAndrew Gallatin
31101e413cf9SAndrew Gallatin if (ss->tx.req_bytes != NULL)
31111e413cf9SAndrew Gallatin free(ss->tx.req_bytes, M_DEVBUF);
31121e413cf9SAndrew Gallatin ss->tx.req_bytes = NULL;
31131e413cf9SAndrew Gallatin
31141e413cf9SAndrew Gallatin if (ss->tx.seg_list != NULL)
31151e413cf9SAndrew Gallatin free(ss->tx.seg_list, M_DEVBUF);
31161e413cf9SAndrew Gallatin ss->tx.seg_list = NULL;
31171e413cf9SAndrew Gallatin
31181e413cf9SAndrew Gallatin if (ss->rx_small.shadow != NULL)
31191e413cf9SAndrew Gallatin free(ss->rx_small.shadow, M_DEVBUF);
31201e413cf9SAndrew Gallatin ss->rx_small.shadow = NULL;
31211e413cf9SAndrew Gallatin
31221e413cf9SAndrew Gallatin if (ss->rx_big.shadow != NULL)
31231e413cf9SAndrew Gallatin free(ss->rx_big.shadow, M_DEVBUF);
31241e413cf9SAndrew Gallatin ss->rx_big.shadow = NULL;
31251e413cf9SAndrew Gallatin
31261e413cf9SAndrew Gallatin if (ss->tx.info != NULL) {
31271e413cf9SAndrew Gallatin if (ss->tx.dmat != NULL) {
31281e413cf9SAndrew Gallatin for (i = 0; i <= ss->tx.mask; i++) {
31291e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->tx.dmat,
31301e413cf9SAndrew Gallatin ss->tx.info[i].map);
3131b2fc195eSAndrew Gallatin }
31321e413cf9SAndrew Gallatin bus_dma_tag_destroy(ss->tx.dmat);
31331e413cf9SAndrew Gallatin }
31341e413cf9SAndrew Gallatin free(ss->tx.info, M_DEVBUF);
31351e413cf9SAndrew Gallatin }
31361e413cf9SAndrew Gallatin ss->tx.info = NULL;
31371e413cf9SAndrew Gallatin
31381e413cf9SAndrew Gallatin if (ss->rx_small.info != NULL) {
31391e413cf9SAndrew Gallatin if (ss->rx_small.dmat != NULL) {
31401e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_small.mask; i++) {
31411e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->rx_small.dmat,
31421e413cf9SAndrew Gallatin ss->rx_small.info[i].map);
31431e413cf9SAndrew Gallatin }
31441e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->rx_small.dmat,
31451e413cf9SAndrew Gallatin ss->rx_small.extra_map);
31461e413cf9SAndrew Gallatin bus_dma_tag_destroy(ss->rx_small.dmat);
31471e413cf9SAndrew Gallatin }
31481e413cf9SAndrew Gallatin free(ss->rx_small.info, M_DEVBUF);
31491e413cf9SAndrew Gallatin }
31501e413cf9SAndrew Gallatin ss->rx_small.info = NULL;
31511e413cf9SAndrew Gallatin
31521e413cf9SAndrew Gallatin if (ss->rx_big.info != NULL) {
31531e413cf9SAndrew Gallatin if (ss->rx_big.dmat != NULL) {
31541e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i++) {
31551e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->rx_big.dmat,
31561e413cf9SAndrew Gallatin ss->rx_big.info[i].map);
31571e413cf9SAndrew Gallatin }
31581e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->rx_big.dmat,
31591e413cf9SAndrew Gallatin ss->rx_big.extra_map);
31601e413cf9SAndrew Gallatin bus_dma_tag_destroy(ss->rx_big.dmat);
31611e413cf9SAndrew Gallatin }
31621e413cf9SAndrew Gallatin free(ss->rx_big.info, M_DEVBUF);
31631e413cf9SAndrew Gallatin }
31641e413cf9SAndrew Gallatin ss->rx_big.info = NULL;
3165b2fc195eSAndrew Gallatin }
3166b2fc195eSAndrew Gallatin
3167b2fc195eSAndrew Gallatin static void
mxge_free_rings(mxge_softc_t * sc)31686d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3169b2fc195eSAndrew Gallatin {
31701e413cf9SAndrew Gallatin int slice;
3171b2fc195eSAndrew Gallatin
31721e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++)
31731e413cf9SAndrew Gallatin mxge_free_slice_rings(&sc->ss[slice]);
3174c2657176SAndrew Gallatin }
3175b2fc195eSAndrew Gallatin
3176b2fc195eSAndrew Gallatin static int
mxge_alloc_slice_rings(struct mxge_slice_state * ss,int rx_ring_entries,int tx_ring_entries)31771e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
31781e413cf9SAndrew Gallatin int tx_ring_entries)
3179b2fc195eSAndrew Gallatin {
31801e413cf9SAndrew Gallatin mxge_softc_t *sc = ss->sc;
31811e413cf9SAndrew Gallatin size_t bytes;
31821e413cf9SAndrew Gallatin int err, i;
3183b2fc195eSAndrew Gallatin
31841e413cf9SAndrew Gallatin /* allocate per-slice receive resources */
3185adae7080SAndrew Gallatin
31861e413cf9SAndrew Gallatin ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
31871e413cf9SAndrew Gallatin ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3188aed8e389SAndrew Gallatin
3189b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */
31901e413cf9SAndrew Gallatin bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
31911e413cf9SAndrew Gallatin ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3192b2fc195eSAndrew Gallatin
31931e413cf9SAndrew Gallatin bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
31941e413cf9SAndrew Gallatin ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3195b2fc195eSAndrew Gallatin
31961e413cf9SAndrew Gallatin /* allocate the rx host info rings */
31971e413cf9SAndrew Gallatin bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
31981e413cf9SAndrew Gallatin ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3199b2fc195eSAndrew Gallatin
32001e413cf9SAndrew Gallatin bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
32011e413cf9SAndrew Gallatin ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3202b2fc195eSAndrew Gallatin
32031e413cf9SAndrew Gallatin /* allocate the rx busdma resources */
3204b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */
3205b2fc195eSAndrew Gallatin 1, /* alignment */
3206b2fc195eSAndrew Gallatin 4096, /* boundary */
3207b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */
3208b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */
3209b2fc195eSAndrew Gallatin NULL, NULL, /* filter */
3210b2fc195eSAndrew Gallatin MHLEN, /* maxsize */
3211b2fc195eSAndrew Gallatin 1, /* num segs */
3212b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */
3213b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */
3214b2fc195eSAndrew Gallatin NULL, NULL, /* lock */
32151e413cf9SAndrew Gallatin &ss->rx_small.dmat); /* tag */
3216b2fc195eSAndrew Gallatin if (err != 0) {
3217b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3218b2fc195eSAndrew Gallatin err);
3219c2ede4b3SMartin Blapp return err;
3220b2fc195eSAndrew Gallatin }
3221b2fc195eSAndrew Gallatin
3222b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */
3223b2fc195eSAndrew Gallatin 1, /* alignment */
3224b0f7b922SAndrew Gallatin 0, /* boundary */
3225b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */
3226b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */
3227b2fc195eSAndrew Gallatin NULL, NULL, /* filter */
3228053e637fSAndrew Gallatin 3*4096, /* maxsize */
3229b0f7b922SAndrew Gallatin 1, /* num segs */
3230b0f7b922SAndrew Gallatin MJUM9BYTES, /* maxsegsize*/
3231b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */
3232b2fc195eSAndrew Gallatin NULL, NULL, /* lock */
32331e413cf9SAndrew Gallatin &ss->rx_big.dmat); /* tag */
3234b2fc195eSAndrew Gallatin if (err != 0) {
3235b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3236b2fc195eSAndrew Gallatin err);
3237c2ede4b3SMartin Blapp return err;
3238b2fc195eSAndrew Gallatin }
32391e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_small.mask; i++) {
32401e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->rx_small.dmat, 0,
32411e413cf9SAndrew Gallatin &ss->rx_small.info[i].map);
3242b2fc195eSAndrew Gallatin if (err != 0) {
3243b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n",
3244b2fc195eSAndrew Gallatin err);
3245c2ede4b3SMartin Blapp return err;
3246b2fc195eSAndrew Gallatin }
3247b2fc195eSAndrew Gallatin }
32481e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->rx_small.dmat, 0,
32491e413cf9SAndrew Gallatin &ss->rx_small.extra_map);
3250b2fc195eSAndrew Gallatin if (err != 0) {
3251b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3252b2fc195eSAndrew Gallatin err);
3253c2ede4b3SMartin Blapp return err;
3254b2fc195eSAndrew Gallatin }
3255b2fc195eSAndrew Gallatin
32561e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i++) {
32571e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->rx_big.dmat, 0,
32581e413cf9SAndrew Gallatin &ss->rx_big.info[i].map);
3259b2fc195eSAndrew Gallatin if (err != 0) {
3260b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n",
3261b2fc195eSAndrew Gallatin err);
3262c2ede4b3SMartin Blapp return err;
3263b2fc195eSAndrew Gallatin }
3264b2fc195eSAndrew Gallatin }
32651e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->rx_big.dmat, 0,
32661e413cf9SAndrew Gallatin &ss->rx_big.extra_map);
3267b2fc195eSAndrew Gallatin if (err != 0) {
3268b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3269b2fc195eSAndrew Gallatin err);
3270c2ede4b3SMartin Blapp return err;
32711e413cf9SAndrew Gallatin }
32721e413cf9SAndrew Gallatin
3273b78540b1SGabor Kovesdan /* now allocate TX resources */
32741e413cf9SAndrew Gallatin
32751e413cf9SAndrew Gallatin ss->tx.mask = tx_ring_entries - 1;
32761e413cf9SAndrew Gallatin ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
32771e413cf9SAndrew Gallatin
32781e413cf9SAndrew Gallatin /* allocate the tx request copy block */
32791e413cf9SAndrew Gallatin bytes = 8 +
32801e413cf9SAndrew Gallatin sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
32811e413cf9SAndrew Gallatin ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
32821e413cf9SAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */
32831e413cf9SAndrew Gallatin ss->tx.req_list = (mcp_kreq_ether_send_t *)
3284aa54c242SJohn Baldwin ((uintptr_t)(ss->tx.req_bytes + 7) & ~7UL);
32851e413cf9SAndrew Gallatin
32861e413cf9SAndrew Gallatin /* allocate the tx busdma segment list */
32871e413cf9SAndrew Gallatin bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
32881e413cf9SAndrew Gallatin ss->tx.seg_list = (bus_dma_segment_t *)
32891e413cf9SAndrew Gallatin malloc(bytes, M_DEVBUF, M_WAITOK);
32901e413cf9SAndrew Gallatin
32911e413cf9SAndrew Gallatin /* allocate the tx host info ring */
32921e413cf9SAndrew Gallatin bytes = tx_ring_entries * sizeof (*ss->tx.info);
32931e413cf9SAndrew Gallatin ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32941e413cf9SAndrew Gallatin
32951e413cf9SAndrew Gallatin /* allocate the tx busdma resources */
32961e413cf9SAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */
32971e413cf9SAndrew Gallatin 1, /* alignment */
32981e413cf9SAndrew Gallatin sc->tx_boundary, /* boundary */
32991e413cf9SAndrew Gallatin BUS_SPACE_MAXADDR, /* low */
33001e413cf9SAndrew Gallatin BUS_SPACE_MAXADDR, /* high */
33011e413cf9SAndrew Gallatin NULL, NULL, /* filter */
33021e413cf9SAndrew Gallatin 65536 + 256, /* maxsize */
33031e413cf9SAndrew Gallatin ss->tx.max_desc - 2, /* num segs */
33041e413cf9SAndrew Gallatin sc->tx_boundary, /* maxsegsz */
33051e413cf9SAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */
33061e413cf9SAndrew Gallatin NULL, NULL, /* lock */
33071e413cf9SAndrew Gallatin &ss->tx.dmat); /* tag */
33081e413cf9SAndrew Gallatin
33091e413cf9SAndrew Gallatin if (err != 0) {
33101e413cf9SAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n",
33111e413cf9SAndrew Gallatin err);
3312c2ede4b3SMartin Blapp return err;
33131e413cf9SAndrew Gallatin }
33141e413cf9SAndrew Gallatin
33151e413cf9SAndrew Gallatin /* now use these tags to setup dmamaps for each slot
33161e413cf9SAndrew Gallatin in the ring */
33171e413cf9SAndrew Gallatin for (i = 0; i <= ss->tx.mask; i++) {
33181e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->tx.dmat, 0,
33191e413cf9SAndrew Gallatin &ss->tx.info[i].map);
33201e413cf9SAndrew Gallatin if (err != 0) {
33211e413cf9SAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n",
33221e413cf9SAndrew Gallatin err);
3323c2ede4b3SMartin Blapp return err;
33241e413cf9SAndrew Gallatin }
3325b2fc195eSAndrew Gallatin }
3326b2fc195eSAndrew Gallatin return 0;
3327b2fc195eSAndrew Gallatin
3328b2fc195eSAndrew Gallatin }
3329b2fc195eSAndrew Gallatin
33301e413cf9SAndrew Gallatin static int
mxge_alloc_rings(mxge_softc_t * sc)33311e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
33321e413cf9SAndrew Gallatin {
33331e413cf9SAndrew Gallatin mxge_cmd_t cmd;
33341e413cf9SAndrew Gallatin int tx_ring_size;
33351e413cf9SAndrew Gallatin int tx_ring_entries, rx_ring_entries;
33361e413cf9SAndrew Gallatin int err, slice;
33371e413cf9SAndrew Gallatin
33381e413cf9SAndrew Gallatin /* get ring sizes */
33391e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33401e413cf9SAndrew Gallatin tx_ring_size = cmd.data0;
33411e413cf9SAndrew Gallatin if (err != 0) {
33421e413cf9SAndrew Gallatin device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33431e413cf9SAndrew Gallatin goto abort;
33441e413cf9SAndrew Gallatin }
33451e413cf9SAndrew Gallatin
33461e413cf9SAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
33471e413cf9SAndrew Gallatin rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
334893037a67SJustin Hibbits if_setsendqlen(sc->ifp, tx_ring_entries - 1);
334993037a67SJustin Hibbits if_setsendqready(sc->ifp);
33501e413cf9SAndrew Gallatin
33511e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
33521e413cf9SAndrew Gallatin err = mxge_alloc_slice_rings(&sc->ss[slice],
33531e413cf9SAndrew Gallatin rx_ring_entries,
33541e413cf9SAndrew Gallatin tx_ring_entries);
33551e413cf9SAndrew Gallatin if (err != 0)
33561e413cf9SAndrew Gallatin goto abort;
33571e413cf9SAndrew Gallatin }
33581e413cf9SAndrew Gallatin return 0;
33591e413cf9SAndrew Gallatin
33601e413cf9SAndrew Gallatin abort:
33611e413cf9SAndrew Gallatin mxge_free_rings(sc);
33621e413cf9SAndrew Gallatin return err;
33631e413cf9SAndrew Gallatin
33641e413cf9SAndrew Gallatin }
33651e413cf9SAndrew Gallatin
3366053e637fSAndrew Gallatin static void
mxge_choose_params(int mtu,int * big_buf_size,int * cl_size,int * nbufs)3367053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3368053e637fSAndrew Gallatin {
3369c792928fSAndrew Gallatin int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3370053e637fSAndrew Gallatin
3371053e637fSAndrew Gallatin if (bufsize < MCLBYTES) {
3372053e637fSAndrew Gallatin /* easy, everything fits in a single buffer */
3373053e637fSAndrew Gallatin *big_buf_size = MCLBYTES;
3374053e637fSAndrew Gallatin *cl_size = MCLBYTES;
3375053e637fSAndrew Gallatin *nbufs = 1;
3376053e637fSAndrew Gallatin return;
3377053e637fSAndrew Gallatin }
3378053e637fSAndrew Gallatin
3379053e637fSAndrew Gallatin if (bufsize < MJUMPAGESIZE) {
3380053e637fSAndrew Gallatin /* still easy, everything still fits in a single buffer */
3381053e637fSAndrew Gallatin *big_buf_size = MJUMPAGESIZE;
3382053e637fSAndrew Gallatin *cl_size = MJUMPAGESIZE;
3383053e637fSAndrew Gallatin *nbufs = 1;
3384053e637fSAndrew Gallatin return;
3385053e637fSAndrew Gallatin }
3386b0f7b922SAndrew Gallatin *cl_size = MJUM9BYTES;
3387b0f7b922SAndrew Gallatin *big_buf_size = MJUM9BYTES;
3388b0f7b922SAndrew Gallatin *nbufs = 1;
3389053e637fSAndrew Gallatin }
3390053e637fSAndrew Gallatin
3391b2fc195eSAndrew Gallatin static int
mxge_slice_open(struct mxge_slice_state * ss,int nbufs,int cl_size)33921e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3393b2fc195eSAndrew Gallatin {
33941e413cf9SAndrew Gallatin mxge_softc_t *sc;
33956d87a65dSAndrew Gallatin mxge_cmd_t cmd;
3396b2fc195eSAndrew Gallatin bus_dmamap_t map;
33971e413cf9SAndrew Gallatin int err, i, slice;
3398b2fc195eSAndrew Gallatin
33991e413cf9SAndrew Gallatin sc = ss->sc;
34001e413cf9SAndrew Gallatin slice = ss - sc->ss;
34011e413cf9SAndrew Gallatin
340226dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
340326dd49c6SAndrew Gallatin (void)tcp_lro_init(&ss->lc);
340426dd49c6SAndrew Gallatin #endif
340526dd49c6SAndrew Gallatin ss->lc.ifp = sc->ifp;
3406053e637fSAndrew Gallatin
34071e413cf9SAndrew Gallatin /* get the lanai pointers to the send and receive rings */
34081e413cf9SAndrew Gallatin
34091e413cf9SAndrew Gallatin err = 0;
34106147584bSElliott Mitchell
34111e413cf9SAndrew Gallatin cmd.data0 = slice;
34121e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
34131e413cf9SAndrew Gallatin ss->tx.lanai =
34141e413cf9SAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3415c6cb3e3fSAndrew Gallatin ss->tx.send_go = (volatile uint32_t *)
3416c6cb3e3fSAndrew Gallatin (sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3417c6cb3e3fSAndrew Gallatin ss->tx.send_stop = (volatile uint32_t *)
3418c6cb3e3fSAndrew Gallatin (sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
34196147584bSElliott Mitchell
34201e413cf9SAndrew Gallatin cmd.data0 = slice;
34211e413cf9SAndrew Gallatin err |= mxge_send_cmd(sc,
34221e413cf9SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
34231e413cf9SAndrew Gallatin ss->rx_small.lanai =
34241e413cf9SAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34251e413cf9SAndrew Gallatin cmd.data0 = slice;
34261e413cf9SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
34271e413cf9SAndrew Gallatin ss->rx_big.lanai =
34281e413cf9SAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34291e413cf9SAndrew Gallatin
34301e413cf9SAndrew Gallatin if (err != 0) {
34311e413cf9SAndrew Gallatin device_printf(sc->dev,
34321e413cf9SAndrew Gallatin "failed to get ring sizes or locations\n");
34331e413cf9SAndrew Gallatin return EIO;
34341e413cf9SAndrew Gallatin }
34351e413cf9SAndrew Gallatin
34361e413cf9SAndrew Gallatin /* stock receive rings */
34371e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_small.mask; i++) {
34381e413cf9SAndrew Gallatin map = ss->rx_small.info[i].map;
34391e413cf9SAndrew Gallatin err = mxge_get_buf_small(ss, map, i);
34401e413cf9SAndrew Gallatin if (err) {
34411e413cf9SAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n",
34421e413cf9SAndrew Gallatin i, ss->rx_small.mask + 1);
34431e413cf9SAndrew Gallatin return ENOMEM;
34441e413cf9SAndrew Gallatin }
34451e413cf9SAndrew Gallatin }
34461e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i++) {
34471e413cf9SAndrew Gallatin ss->rx_big.shadow[i].addr_low = 0xffffffff;
34481e413cf9SAndrew Gallatin ss->rx_big.shadow[i].addr_high = 0xffffffff;
34491e413cf9SAndrew Gallatin }
34501e413cf9SAndrew Gallatin ss->rx_big.nbufs = nbufs;
34511e413cf9SAndrew Gallatin ss->rx_big.cl_size = cl_size;
345293037a67SJustin Hibbits ss->rx_big.mlen = if_getmtu(ss->sc->ifp) + ETHER_HDR_LEN +
34534d9a5852SAndrew Gallatin ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
34541e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
34551e413cf9SAndrew Gallatin map = ss->rx_big.info[i].map;
34561e413cf9SAndrew Gallatin err = mxge_get_buf_big(ss, map, i);
34571e413cf9SAndrew Gallatin if (err) {
34581e413cf9SAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n",
34591e413cf9SAndrew Gallatin i, ss->rx_big.mask + 1);
34601e413cf9SAndrew Gallatin return ENOMEM;
34611e413cf9SAndrew Gallatin }
34621e413cf9SAndrew Gallatin }
34631e413cf9SAndrew Gallatin return 0;
34641e413cf9SAndrew Gallatin }
34651e413cf9SAndrew Gallatin
34661e413cf9SAndrew Gallatin static int
mxge_open(mxge_softc_t * sc)34671e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
34681e413cf9SAndrew Gallatin {
34691e413cf9SAndrew Gallatin mxge_cmd_t cmd;
34701e413cf9SAndrew Gallatin int err, big_bytes, nbufs, slice, cl_size, i;
34711e413cf9SAndrew Gallatin bus_addr_t bus;
34721e413cf9SAndrew Gallatin volatile uint8_t *itable;
3473c6cb3e3fSAndrew Gallatin struct mxge_slice_state *ss;
3474b2fc195eSAndrew Gallatin
34757d542e2dSAndrew Gallatin /* Copy the MAC address in case it was overridden */
347693037a67SJustin Hibbits bcopy(if_getlladdr(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
34777d542e2dSAndrew Gallatin
3478adae7080SAndrew Gallatin err = mxge_reset(sc, 1);
3479b2fc195eSAndrew Gallatin if (err != 0) {
3480b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n");
3481b2fc195eSAndrew Gallatin return EIO;
3482b2fc195eSAndrew Gallatin }
3483b2fc195eSAndrew Gallatin
34841e413cf9SAndrew Gallatin if (sc->num_slices > 1) {
34851e413cf9SAndrew Gallatin /* setup the indirection table */
34861e413cf9SAndrew Gallatin cmd.data0 = sc->num_slices;
34871e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
34881e413cf9SAndrew Gallatin &cmd);
3489b2fc195eSAndrew Gallatin
34901e413cf9SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
34911e413cf9SAndrew Gallatin &cmd);
34921e413cf9SAndrew Gallatin if (err != 0) {
34931e413cf9SAndrew Gallatin device_printf(sc->dev,
34941e413cf9SAndrew Gallatin "failed to setup rss tables\n");
34951e413cf9SAndrew Gallatin return err;
34961e413cf9SAndrew Gallatin }
34971e413cf9SAndrew Gallatin
34981e413cf9SAndrew Gallatin /* just enable an identity mapping */
34991e413cf9SAndrew Gallatin itable = sc->sram + cmd.data0;
35001e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++)
35011e413cf9SAndrew Gallatin itable[i] = (uint8_t)i;
35021e413cf9SAndrew Gallatin
35031e413cf9SAndrew Gallatin cmd.data0 = 1;
35041e413cf9SAndrew Gallatin cmd.data1 = mxge_rss_hash_type;
35051e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
35061e413cf9SAndrew Gallatin if (err != 0) {
35071e413cf9SAndrew Gallatin device_printf(sc->dev, "failed to enable slices\n");
35081e413cf9SAndrew Gallatin return err;
35091e413cf9SAndrew Gallatin }
35101e413cf9SAndrew Gallatin }
35111e413cf9SAndrew Gallatin
351293037a67SJustin Hibbits mxge_choose_params(if_getmtu(sc->ifp), &big_bytes, &cl_size, &nbufs);
35131e413cf9SAndrew Gallatin
35141e413cf9SAndrew Gallatin cmd.data0 = nbufs;
3515053e637fSAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3516053e637fSAndrew Gallatin &cmd);
3517053e637fSAndrew Gallatin /* error is only meaningful if we're trying to set
3518053e637fSAndrew Gallatin MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
35191e413cf9SAndrew Gallatin if (err && nbufs > 1) {
3520053e637fSAndrew Gallatin device_printf(sc->dev,
3521053e637fSAndrew Gallatin "Failed to set alway-use-n to %d\n",
35221e413cf9SAndrew Gallatin nbufs);
3523053e637fSAndrew Gallatin return EIO;
3524053e637fSAndrew Gallatin }
3525b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer
3526b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power
3527b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */
352893037a67SJustin Hibbits cmd.data0 = if_getmtu(sc->ifp) + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
35295e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3530b4db9009SAndrew Gallatin cmd.data0 = MHLEN - MXGEFW_PAD;
35315e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3532b2fc195eSAndrew Gallatin &cmd);
3533053e637fSAndrew Gallatin cmd.data0 = big_bytes;
35345e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
35350fa7f681SAndrew Gallatin
35360fa7f681SAndrew Gallatin if (err != 0) {
35370fa7f681SAndrew Gallatin device_printf(sc->dev, "failed to setup params\n");
35380fa7f681SAndrew Gallatin goto abort;
35390fa7f681SAndrew Gallatin }
35400fa7f681SAndrew Gallatin
3541b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */
35426147584bSElliott Mitchell for (slice = 0; slice < sc->num_slices; slice++) {
3543c6cb3e3fSAndrew Gallatin ss = &sc->ss[slice];
3544c6cb3e3fSAndrew Gallatin cmd.data0 =
3545c6cb3e3fSAndrew Gallatin MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3546c6cb3e3fSAndrew Gallatin cmd.data1 =
3547c6cb3e3fSAndrew Gallatin MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
35480fa7f681SAndrew Gallatin cmd.data2 = sizeof(struct mcp_irq_data);
3549c6cb3e3fSAndrew Gallatin cmd.data2 |= (slice << 16);
3550c6cb3e3fSAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3551c6cb3e3fSAndrew Gallatin }
35520fa7f681SAndrew Gallatin
35530fa7f681SAndrew Gallatin if (err != 0) {
35541e413cf9SAndrew Gallatin bus = sc->ss->fw_stats_dma.bus_addr;
35550fa7f681SAndrew Gallatin bus += offsetof(struct mcp_irq_data, send_done_count);
35560fa7f681SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(bus);
35570fa7f681SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
35580fa7f681SAndrew Gallatin err = mxge_send_cmd(sc,
35590fa7f681SAndrew Gallatin MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
35600fa7f681SAndrew Gallatin &cmd);
35610fa7f681SAndrew Gallatin /* Firmware cannot support multicast without STATS_DMA_V2 */
35620fa7f681SAndrew Gallatin sc->fw_multicast_support = 0;
35630fa7f681SAndrew Gallatin } else {
35640fa7f681SAndrew Gallatin sc->fw_multicast_support = 1;
35650fa7f681SAndrew Gallatin }
3566b2fc195eSAndrew Gallatin
3567b2fc195eSAndrew Gallatin if (err != 0) {
3568b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n");
3569b2fc195eSAndrew Gallatin goto abort;
3570b2fc195eSAndrew Gallatin }
3571b2fc195eSAndrew Gallatin
35721e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
35731e413cf9SAndrew Gallatin err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
35741e413cf9SAndrew Gallatin if (err != 0) {
35751e413cf9SAndrew Gallatin device_printf(sc->dev, "couldn't open slice %d\n",
35761e413cf9SAndrew Gallatin slice);
35771e413cf9SAndrew Gallatin goto abort;
35781e413cf9SAndrew Gallatin }
35791e413cf9SAndrew Gallatin }
35801e413cf9SAndrew Gallatin
3581b2fc195eSAndrew Gallatin /* Finally, start the firmware running */
35825e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3583b2fc195eSAndrew Gallatin if (err) {
3584b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n");
3585b2fc195eSAndrew Gallatin goto abort;
3586b2fc195eSAndrew Gallatin }
3587c6cb3e3fSAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
3588c6cb3e3fSAndrew Gallatin ss = &sc->ss[slice];
3589c6cb3e3fSAndrew Gallatin ss->if_drv_flags |= IFF_DRV_RUNNING;
3590c6cb3e3fSAndrew Gallatin ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3591c6cb3e3fSAndrew Gallatin }
359293037a67SJustin Hibbits if_setdrvflagbits(sc->ifp, IFF_DRV_RUNNING, 0);
359393037a67SJustin Hibbits if_setdrvflagbits(sc->ifp, 0, IFF_DRV_OACTIVE);
3594b2fc195eSAndrew Gallatin
3595b2fc195eSAndrew Gallatin return 0;
3596b2fc195eSAndrew Gallatin
3597b2fc195eSAndrew Gallatin abort:
35986d87a65dSAndrew Gallatin mxge_free_mbufs(sc);
3599a98d6cd7SAndrew Gallatin
3600b2fc195eSAndrew Gallatin return err;
3601b2fc195eSAndrew Gallatin }
3602b2fc195eSAndrew Gallatin
3603b2fc195eSAndrew Gallatin static int
mxge_close(mxge_softc_t * sc,int down)3604a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3605b2fc195eSAndrew Gallatin {
36066d87a65dSAndrew Gallatin mxge_cmd_t cmd;
3607b2fc195eSAndrew Gallatin int err, old_down_cnt;
3608c6cb3e3fSAndrew Gallatin struct mxge_slice_state *ss;
3609c6cb3e3fSAndrew Gallatin int slice;
3610b2fc195eSAndrew Gallatin
3611c6cb3e3fSAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) {
3612c6cb3e3fSAndrew Gallatin ss = &sc->ss[slice];
3613c6cb3e3fSAndrew Gallatin ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3614c6cb3e3fSAndrew Gallatin }
361593037a67SJustin Hibbits if_setdrvflagbits(sc->ifp, 0, IFF_DRV_RUNNING);
3616a393336bSAndrew Gallatin if (!down) {
3617b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt;
361873c7c83fSAndrew Gallatin wmb();
36195e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3620b2fc195eSAndrew Gallatin if (err) {
3621a393336bSAndrew Gallatin device_printf(sc->dev,
3622a393336bSAndrew Gallatin "Couldn't bring down link\n");
3623b2fc195eSAndrew Gallatin }
3624b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) {
3625b2fc195eSAndrew Gallatin /* wait for down irq */
3626dce01b9bSAndrew Gallatin DELAY(10 * sc->intr_coal_delay);
3627b2fc195eSAndrew Gallatin }
362873c7c83fSAndrew Gallatin wmb();
3629b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) {
3630b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n");
3631b2fc195eSAndrew Gallatin }
3632a393336bSAndrew Gallatin }
36336d87a65dSAndrew Gallatin mxge_free_mbufs(sc);
3634a98d6cd7SAndrew Gallatin
3635b2fc195eSAndrew Gallatin return 0;
3636b2fc195eSAndrew Gallatin }
3637b2fc195eSAndrew Gallatin
3638dce01b9bSAndrew Gallatin static void
mxge_setup_cfg_space(mxge_softc_t * sc)3639dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3640dce01b9bSAndrew Gallatin {
3641dce01b9bSAndrew Gallatin device_t dev = sc->dev;
3642dce01b9bSAndrew Gallatin int reg;
3643c68534f1SScott Long uint16_t lnk, pectl;
3644dce01b9bSAndrew Gallatin
3645dce01b9bSAndrew Gallatin /* find the PCIe link width and set max read request to 4KB*/
36463b0a4aefSJohn Baldwin if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) {
3647dce01b9bSAndrew Gallatin lnk = pci_read_config(dev, reg + 0x12, 2);
3648dce01b9bSAndrew Gallatin sc->link_width = (lnk >> 4) & 0x3f;
3649dce01b9bSAndrew Gallatin
365083d54b59SAndrew Gallatin if (sc->pectl == 0) {
3651dce01b9bSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2);
3652dce01b9bSAndrew Gallatin pectl = (pectl & ~0x7000) | (5 << 12);
3653dce01b9bSAndrew Gallatin pci_write_config(dev, reg + 0x8, pectl, 2);
365483d54b59SAndrew Gallatin sc->pectl = pectl;
365583d54b59SAndrew Gallatin } else {
365683d54b59SAndrew Gallatin /* restore saved pectl after watchdog reset */
365783d54b59SAndrew Gallatin pci_write_config(dev, reg + 0x8, sc->pectl, 2);
365883d54b59SAndrew Gallatin }
3659dce01b9bSAndrew Gallatin }
3660dce01b9bSAndrew Gallatin
3661dce01b9bSAndrew Gallatin /* Enable DMA and Memory space access */
3662dce01b9bSAndrew Gallatin pci_enable_busmaster(dev);
3663dce01b9bSAndrew Gallatin }
3664dce01b9bSAndrew Gallatin
3665dce01b9bSAndrew Gallatin static uint32_t
mxge_read_reboot(mxge_softc_t * sc)3666dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3667dce01b9bSAndrew Gallatin {
3668dce01b9bSAndrew Gallatin device_t dev = sc->dev;
3669dce01b9bSAndrew Gallatin uint32_t vs;
3670dce01b9bSAndrew Gallatin
3671dce01b9bSAndrew Gallatin /* find the vendor specific offset */
36723b0a4aefSJohn Baldwin if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) {
3673dce01b9bSAndrew Gallatin device_printf(sc->dev,
3674dce01b9bSAndrew Gallatin "could not find vendor specific offset\n");
3675dce01b9bSAndrew Gallatin return (uint32_t)-1;
3676dce01b9bSAndrew Gallatin }
3677dce01b9bSAndrew Gallatin /* enable read32 mode */
3678dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x10, 0x3, 1);
3679dce01b9bSAndrew Gallatin /* tell NIC which register to read */
3680dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3681dce01b9bSAndrew Gallatin return (pci_read_config(dev, vs + 0x14, 4));
3682dce01b9bSAndrew Gallatin }
3683dce01b9bSAndrew Gallatin
368472c042dfSAndrew Gallatin static void
mxge_watchdog_reset(mxge_softc_t * sc)368572c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3686dce01b9bSAndrew Gallatin {
3687e749ef6bSAndrew Gallatin struct pci_devinfo *dinfo;
3688a393336bSAndrew Gallatin struct mxge_slice_state *ss;
3689a393336bSAndrew Gallatin int err, running, s, num_tx_slices = 1;
3690dce01b9bSAndrew Gallatin uint32_t reboot;
3691dce01b9bSAndrew Gallatin uint16_t cmd;
3692dce01b9bSAndrew Gallatin
3693dce01b9bSAndrew Gallatin err = ENXIO;
3694dce01b9bSAndrew Gallatin
3695dce01b9bSAndrew Gallatin device_printf(sc->dev, "Watchdog reset!\n");
3696dce01b9bSAndrew Gallatin
3697dce01b9bSAndrew Gallatin /*
3698dce01b9bSAndrew Gallatin * check to see if the NIC rebooted. If it did, then all of
3699dce01b9bSAndrew Gallatin * PCI config space has been reset, and things like the
3700dce01b9bSAndrew Gallatin * busmaster bit will be zero. If this is the case, then we
3701dce01b9bSAndrew Gallatin * must restore PCI config space before the NIC can be used
3702dce01b9bSAndrew Gallatin * again
3703dce01b9bSAndrew Gallatin */
3704dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3705dce01b9bSAndrew Gallatin if (cmd == 0xffff) {
3706dce01b9bSAndrew Gallatin /*
3707dce01b9bSAndrew Gallatin * maybe the watchdog caught the NIC rebooting; wait
3708dce01b9bSAndrew Gallatin * up to 100ms for it to finish. If it does not come
3709dce01b9bSAndrew Gallatin * back, then give up
3710dce01b9bSAndrew Gallatin */
3711dce01b9bSAndrew Gallatin DELAY(1000*100);
3712dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3713dce01b9bSAndrew Gallatin if (cmd == 0xffff) {
3714dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC disappeared!\n");
3715dce01b9bSAndrew Gallatin }
3716dce01b9bSAndrew Gallatin }
3717dce01b9bSAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3718dce01b9bSAndrew Gallatin /* print the reboot status */
3719dce01b9bSAndrew Gallatin reboot = mxge_read_reboot(sc);
3720dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3721dce01b9bSAndrew Gallatin reboot);
372293037a67SJustin Hibbits running = if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING;
3723a393336bSAndrew Gallatin if (running) {
3724a393336bSAndrew Gallatin /*
3725a393336bSAndrew Gallatin * quiesce NIC so that TX routines will not try to
3726a393336bSAndrew Gallatin * xmit after restoration of BAR
3727a393336bSAndrew Gallatin */
3728a393336bSAndrew Gallatin
3729a393336bSAndrew Gallatin /* Mark the link as down */
3730a393336bSAndrew Gallatin if (sc->link_state) {
3731a393336bSAndrew Gallatin sc->link_state = 0;
3732a393336bSAndrew Gallatin if_link_state_change(sc->ifp,
3733a393336bSAndrew Gallatin LINK_STATE_DOWN);
3734a393336bSAndrew Gallatin }
37356147584bSElliott Mitchell
3736a393336bSAndrew Gallatin num_tx_slices = sc->num_slices;
37376147584bSElliott Mitchell
3738a393336bSAndrew Gallatin /* grab all TX locks to ensure no tx */
3739a393336bSAndrew Gallatin for (s = 0; s < num_tx_slices; s++) {
3740a393336bSAndrew Gallatin ss = &sc->ss[s];
3741a393336bSAndrew Gallatin mtx_lock(&ss->tx.mtx);
3742a393336bSAndrew Gallatin }
3743a393336bSAndrew Gallatin mxge_close(sc, 1);
3744a393336bSAndrew Gallatin }
3745dce01b9bSAndrew Gallatin /* restore PCI configuration space */
3746e749ef6bSAndrew Gallatin dinfo = device_get_ivars(sc->dev);
3747e749ef6bSAndrew Gallatin pci_cfg_restore(sc->dev, dinfo);
3748dce01b9bSAndrew Gallatin
3749dce01b9bSAndrew Gallatin /* and redo any changes we made to our config space */
3750dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc);
375110882804SAndrew Gallatin
3752a393336bSAndrew Gallatin /* reload f/w */
3753a393336bSAndrew Gallatin err = mxge_load_firmware(sc, 0);
3754a393336bSAndrew Gallatin if (err) {
3755a393336bSAndrew Gallatin device_printf(sc->dev,
3756a393336bSAndrew Gallatin "Unable to re-load f/w\n");
375710882804SAndrew Gallatin }
3758a393336bSAndrew Gallatin if (running) {
3759a393336bSAndrew Gallatin if (!err)
3760a393336bSAndrew Gallatin err = mxge_open(sc);
3761a393336bSAndrew Gallatin /* release all TX locks */
3762a393336bSAndrew Gallatin for (s = 0; s < num_tx_slices; s++) {
3763a393336bSAndrew Gallatin ss = &sc->ss[s];
376483d54b59SAndrew Gallatin mxge_start_locked(ss);
3765a393336bSAndrew Gallatin mtx_unlock(&ss->tx.mtx);
3766a393336bSAndrew Gallatin }
3767a393336bSAndrew Gallatin }
3768a393336bSAndrew Gallatin sc->watchdog_resets++;
3769dce01b9bSAndrew Gallatin } else {
3770c6cb3e3fSAndrew Gallatin device_printf(sc->dev,
377172c042dfSAndrew Gallatin "NIC did not reboot, not resetting\n");
377272c042dfSAndrew Gallatin err = 0;
377372c042dfSAndrew Gallatin }
377472c042dfSAndrew Gallatin if (err) {
377572c042dfSAndrew Gallatin device_printf(sc->dev, "watchdog reset failed\n");
377672c042dfSAndrew Gallatin } else {
37776b484a49SAndrew Gallatin if (sc->dying == 2)
37786b484a49SAndrew Gallatin sc->dying = 0;
37796b484a49SAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
378072c042dfSAndrew Gallatin }
378172c042dfSAndrew Gallatin }
378272c042dfSAndrew Gallatin
378372c042dfSAndrew Gallatin static void
mxge_watchdog_task(void * arg,int pending)378472c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
378572c042dfSAndrew Gallatin {
378672c042dfSAndrew Gallatin mxge_softc_t *sc = arg;
378772c042dfSAndrew Gallatin
378872c042dfSAndrew Gallatin mtx_lock(&sc->driver_mtx);
378972c042dfSAndrew Gallatin mxge_watchdog_reset(sc);
379072c042dfSAndrew Gallatin mtx_unlock(&sc->driver_mtx);
379172c042dfSAndrew Gallatin }
379272c042dfSAndrew Gallatin
379372c042dfSAndrew Gallatin static void
mxge_warn_stuck(mxge_softc_t * sc,mxge_tx_ring_t * tx,int slice)379472c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
379572c042dfSAndrew Gallatin {
379672c042dfSAndrew Gallatin tx = &sc->ss[slice].tx;
379772c042dfSAndrew Gallatin device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3798c6cb3e3fSAndrew Gallatin device_printf(sc->dev,
3799c6cb3e3fSAndrew Gallatin "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3800c6cb3e3fSAndrew Gallatin tx->req, tx->done, tx->queue_active);
3801c6cb3e3fSAndrew Gallatin device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3802c6cb3e3fSAndrew Gallatin tx->activate, tx->deactivate);
3803dce01b9bSAndrew Gallatin device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3804c6cb3e3fSAndrew Gallatin tx->pkt_done,
38051e413cf9SAndrew Gallatin be32toh(sc->ss->fw_stats->send_done_count));
3806dce01b9bSAndrew Gallatin }
3807dce01b9bSAndrew Gallatin
3808e749ef6bSAndrew Gallatin static int
mxge_watchdog(mxge_softc_t * sc)3809dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3810dce01b9bSAndrew Gallatin {
3811c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx;
38121e413cf9SAndrew Gallatin uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3813c6cb3e3fSAndrew Gallatin int i, err = 0;
3814dce01b9bSAndrew Gallatin
3815dce01b9bSAndrew Gallatin /* see if we have outstanding transmits, which
3816dce01b9bSAndrew Gallatin have been pending for more than mxge_ticks */
38176147584bSElliott Mitchell for (i = 0; (i < sc->num_slices) && (err == 0); i++) {
3818c6cb3e3fSAndrew Gallatin tx = &sc->ss[i].tx;
3819dce01b9bSAndrew Gallatin if (tx->req != tx->done &&
3820dce01b9bSAndrew Gallatin tx->watchdog_req != tx->watchdog_done &&
3821c587e59fSAndrew Gallatin tx->done == tx->watchdog_done) {
3822c587e59fSAndrew Gallatin /* check for pause blocking before resetting */
382372c042dfSAndrew Gallatin if (tx->watchdog_rx_pause == rx_pause) {
382472c042dfSAndrew Gallatin mxge_warn_stuck(sc, tx, i);
382572c042dfSAndrew Gallatin taskqueue_enqueue(sc->tq, &sc->watchdog_task);
382672c042dfSAndrew Gallatin return (ENXIO);
382772c042dfSAndrew Gallatin }
3828c587e59fSAndrew Gallatin else
3829c587e59fSAndrew Gallatin device_printf(sc->dev, "Flow control blocking "
3830c587e59fSAndrew Gallatin "xmits, check link partner\n");
3831c587e59fSAndrew Gallatin }
3832dce01b9bSAndrew Gallatin
3833dce01b9bSAndrew Gallatin tx->watchdog_req = tx->req;
3834dce01b9bSAndrew Gallatin tx->watchdog_done = tx->done;
3835c587e59fSAndrew Gallatin tx->watchdog_rx_pause = rx_pause;
3836c6cb3e3fSAndrew Gallatin }
3837c587e59fSAndrew Gallatin
3838c587e59fSAndrew Gallatin if (sc->need_media_probe)
3839c587e59fSAndrew Gallatin mxge_media_probe(sc);
3840e749ef6bSAndrew Gallatin return (err);
3841dce01b9bSAndrew Gallatin }
3842dce01b9bSAndrew Gallatin
3843f3f040d9SGleb Smirnoff static uint64_t
mxge_get_counter(if_t ifp,ift_counter cnt)384493037a67SJustin Hibbits mxge_get_counter(if_t ifp, ift_counter cnt)
38451e413cf9SAndrew Gallatin {
3846f3f040d9SGleb Smirnoff struct mxge_softc *sc;
3847f3f040d9SGleb Smirnoff uint64_t rv;
38481e413cf9SAndrew Gallatin
3849f3f040d9SGleb Smirnoff sc = if_getsoftc(ifp);
3850f3f040d9SGleb Smirnoff rv = 0;
3851f3f040d9SGleb Smirnoff
3852f3f040d9SGleb Smirnoff switch (cnt) {
3853f3f040d9SGleb Smirnoff case IFCOUNTER_IPACKETS:
3854f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++)
3855f3f040d9SGleb Smirnoff rv += sc->ss[s].ipackets;
3856f3f040d9SGleb Smirnoff return (rv);
3857f3f040d9SGleb Smirnoff case IFCOUNTER_OPACKETS:
3858f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++)
3859f3f040d9SGleb Smirnoff rv += sc->ss[s].opackets;
3860f3f040d9SGleb Smirnoff return (rv);
3861f3f040d9SGleb Smirnoff case IFCOUNTER_OERRORS:
3862f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++)
3863f3f040d9SGleb Smirnoff rv += sc->ss[s].oerrors;
3864f3f040d9SGleb Smirnoff return (rv);
3865f3f040d9SGleb Smirnoff case IFCOUNTER_OBYTES:
3866f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++)
3867f3f040d9SGleb Smirnoff rv += sc->ss[s].obytes;
3868f3f040d9SGleb Smirnoff return (rv);
3869f3f040d9SGleb Smirnoff case IFCOUNTER_OMCASTS:
3870f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++)
3871f3f040d9SGleb Smirnoff rv += sc->ss[s].omcasts;
3872f3f040d9SGleb Smirnoff return (rv);
3873f3f040d9SGleb Smirnoff case IFCOUNTER_OQDROPS:
3874f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++)
3875f3f040d9SGleb Smirnoff rv += sc->ss[s].tx.br->br_drops;
3876f3f040d9SGleb Smirnoff return (rv);
3877f3f040d9SGleb Smirnoff default:
3878f3f040d9SGleb Smirnoff return (if_get_counter_default(ifp, cnt));
38791e413cf9SAndrew Gallatin }
38801e413cf9SAndrew Gallatin }
3881c6cb3e3fSAndrew Gallatin
38821e413cf9SAndrew Gallatin static void
mxge_tick(void * arg)3883dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3884dce01b9bSAndrew Gallatin {
3885dce01b9bSAndrew Gallatin mxge_softc_t *sc = arg;
38866b484a49SAndrew Gallatin u_long pkts = 0;
3887e749ef6bSAndrew Gallatin int err = 0;
38886b484a49SAndrew Gallatin int running, ticks;
38896b484a49SAndrew Gallatin uint16_t cmd;
3890dce01b9bSAndrew Gallatin
38916b484a49SAndrew Gallatin ticks = mxge_ticks;
389293037a67SJustin Hibbits running = if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING;
38936b484a49SAndrew Gallatin if (running) {
38941e413cf9SAndrew Gallatin if (!sc->watchdog_countdown) {
3895e749ef6bSAndrew Gallatin err = mxge_watchdog(sc);
38961e413cf9SAndrew Gallatin sc->watchdog_countdown = 4;
38971e413cf9SAndrew Gallatin }
38981e413cf9SAndrew Gallatin sc->watchdog_countdown--;
38996b484a49SAndrew Gallatin }
39006b484a49SAndrew Gallatin if (pkts == 0) {
39016b484a49SAndrew Gallatin /* ensure NIC did not suffer h/w fault while idle */
39026b484a49SAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
39036b484a49SAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
39046b484a49SAndrew Gallatin sc->dying = 2;
39056b484a49SAndrew Gallatin taskqueue_enqueue(sc->tq, &sc->watchdog_task);
39066b484a49SAndrew Gallatin err = ENXIO;
39076b484a49SAndrew Gallatin }
39086b484a49SAndrew Gallatin /* look less often if NIC is idle */
39096b484a49SAndrew Gallatin ticks *= 4;
39106b484a49SAndrew Gallatin }
39116b484a49SAndrew Gallatin
3912e749ef6bSAndrew Gallatin if (err == 0)
39136b484a49SAndrew Gallatin callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
3914e749ef6bSAndrew Gallatin
3915dce01b9bSAndrew Gallatin }
3916b2fc195eSAndrew Gallatin
3917b2fc195eSAndrew Gallatin static int
mxge_media_change(if_t ifp)391893037a67SJustin Hibbits mxge_media_change(if_t ifp)
3919b2fc195eSAndrew Gallatin {
3920b2fc195eSAndrew Gallatin return EINVAL;
3921b2fc195eSAndrew Gallatin }
3922b2fc195eSAndrew Gallatin
3923b2fc195eSAndrew Gallatin static int
mxge_change_mtu(mxge_softc_t * sc,int mtu)39246d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3925b2fc195eSAndrew Gallatin {
392693037a67SJustin Hibbits if_t ifp = sc->ifp;
3927b2fc195eSAndrew Gallatin int real_mtu, old_mtu;
3928b2fc195eSAndrew Gallatin int err = 0;
3929b2fc195eSAndrew Gallatin
3930c792928fSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3931053e637fSAndrew Gallatin if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3932b2fc195eSAndrew Gallatin return EINVAL;
3933a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx);
393493037a67SJustin Hibbits old_mtu = if_getmtu(ifp);
393593037a67SJustin Hibbits if_setmtu(ifp, mtu);
393693037a67SJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
3937a393336bSAndrew Gallatin mxge_close(sc, 0);
39386d87a65dSAndrew Gallatin err = mxge_open(sc);
3939b2fc195eSAndrew Gallatin if (err != 0) {
394093037a67SJustin Hibbits if_setmtu(ifp, old_mtu);
3941a393336bSAndrew Gallatin mxge_close(sc, 0);
39426d87a65dSAndrew Gallatin (void) mxge_open(sc);
3943b2fc195eSAndrew Gallatin }
3944b2fc195eSAndrew Gallatin }
3945a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
3946b2fc195eSAndrew Gallatin return err;
3947b2fc195eSAndrew Gallatin }
3948b2fc195eSAndrew Gallatin
3949b2fc195eSAndrew Gallatin static void
mxge_media_status(if_t ifp,struct ifmediareq * ifmr)395093037a67SJustin Hibbits mxge_media_status(if_t ifp, struct ifmediareq *ifmr)
3951b2fc195eSAndrew Gallatin {
395293037a67SJustin Hibbits mxge_softc_t *sc = if_getsoftc(ifp);
3953b2fc195eSAndrew Gallatin
3954b2fc195eSAndrew Gallatin if (sc == NULL)
3955b2fc195eSAndrew Gallatin return;
3956b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID;
3957c406ad2eSAndrew Gallatin ifmr->ifm_active = IFM_ETHER | IFM_FDX;
3958c587e59fSAndrew Gallatin ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3959c406ad2eSAndrew Gallatin ifmr->ifm_active |= sc->current_media;
3960b2fc195eSAndrew Gallatin }
3961b2fc195eSAndrew Gallatin
3962b2fc195eSAndrew Gallatin static int
mxge_fetch_i2c(mxge_softc_t * sc,struct ifi2creq * i2c)3963df131e84SAndrew Gallatin mxge_fetch_i2c(mxge_softc_t *sc, struct ifi2creq *i2c)
3964df131e84SAndrew Gallatin {
3965df131e84SAndrew Gallatin mxge_cmd_t cmd;
3966df131e84SAndrew Gallatin uint32_t i2c_args;
3967df131e84SAndrew Gallatin int i, ms, err;
3968df131e84SAndrew Gallatin
3969df131e84SAndrew Gallatin if (i2c->dev_addr != 0xA0 &&
3970df131e84SAndrew Gallatin i2c->dev_addr != 0xA2)
3971df131e84SAndrew Gallatin return (EINVAL);
3972df131e84SAndrew Gallatin if (i2c->len > sizeof(i2c->data))
3973df131e84SAndrew Gallatin return (EINVAL);
3974df131e84SAndrew Gallatin
3975df131e84SAndrew Gallatin for (i = 0; i < i2c->len; i++) {
3976df131e84SAndrew Gallatin i2c_args = i2c->dev_addr << 0x8;
3977df131e84SAndrew Gallatin i2c_args |= i2c->offset + i;
3978df131e84SAndrew Gallatin cmd.data0 = 0; /* just fetch 1 byte, not all 256 */
3979df131e84SAndrew Gallatin cmd.data1 = i2c_args;
3980df131e84SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
3981df131e84SAndrew Gallatin
3982df131e84SAndrew Gallatin if (err != MXGEFW_CMD_OK)
3983df131e84SAndrew Gallatin return (EIO);
3984df131e84SAndrew Gallatin /* now we wait for the data to be cached */
3985df131e84SAndrew Gallatin cmd.data0 = i2c_args & 0xff;
3986df131e84SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3987df131e84SAndrew Gallatin for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
3988df131e84SAndrew Gallatin cmd.data0 = i2c_args & 0xff;
3989df131e84SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3990df131e84SAndrew Gallatin if (err == EBUSY)
3991df131e84SAndrew Gallatin DELAY(1000);
3992df131e84SAndrew Gallatin }
3993df131e84SAndrew Gallatin if (err != MXGEFW_CMD_OK)
3994df131e84SAndrew Gallatin return (EIO);
3995df131e84SAndrew Gallatin i2c->data[i] = cmd.data0;
3996df131e84SAndrew Gallatin }
3997df131e84SAndrew Gallatin return (0);
3998df131e84SAndrew Gallatin }
3999df131e84SAndrew Gallatin
4000df131e84SAndrew Gallatin static int
mxge_ioctl(if_t ifp,u_long command,caddr_t data)400193037a67SJustin Hibbits mxge_ioctl(if_t ifp, u_long command, caddr_t data)
4002b2fc195eSAndrew Gallatin {
400393037a67SJustin Hibbits mxge_softc_t *sc = if_getsoftc(ifp);
4004b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data;
4005df131e84SAndrew Gallatin struct ifi2creq i2c;
4006b2fc195eSAndrew Gallatin int err, mask;
4007b2fc195eSAndrew Gallatin
4008b2fc195eSAndrew Gallatin err = 0;
4009b2fc195eSAndrew Gallatin switch (command) {
4010b2fc195eSAndrew Gallatin case SIOCSIFMTU:
40116d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu);
4012b2fc195eSAndrew Gallatin break;
4013b2fc195eSAndrew Gallatin
4014b2fc195eSAndrew Gallatin case SIOCSIFFLAGS:
4015a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx);
40168c5d766cSAndrew Gallatin if (sc->dying) {
40178c5d766cSAndrew Gallatin mtx_unlock(&sc->driver_mtx);
40188c5d766cSAndrew Gallatin return EINVAL;
40198c5d766cSAndrew Gallatin }
402093037a67SJustin Hibbits if (if_getflags(ifp) & IFF_UP) {
402193037a67SJustin Hibbits if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) {
40226d87a65dSAndrew Gallatin err = mxge_open(sc);
4023dce01b9bSAndrew Gallatin } else {
40240fa7f681SAndrew Gallatin /* take care of promis can allmulti
40250fa7f681SAndrew Gallatin flag chages */
40260fa7f681SAndrew Gallatin mxge_change_promisc(sc,
402793037a67SJustin Hibbits if_getflags(ifp) & IFF_PROMISC);
40280fa7f681SAndrew Gallatin mxge_set_multicast_list(sc);
40290fa7f681SAndrew Gallatin }
4030b2fc195eSAndrew Gallatin } else {
403193037a67SJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
4032a393336bSAndrew Gallatin mxge_close(sc, 0);
4033dce01b9bSAndrew Gallatin }
4034b2fc195eSAndrew Gallatin }
4035a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4036b2fc195eSAndrew Gallatin break;
4037b2fc195eSAndrew Gallatin
4038b2fc195eSAndrew Gallatin case SIOCADDMULTI:
4039b2fc195eSAndrew Gallatin case SIOCDELMULTI:
4040a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx);
4041c0a1f0afSAndrew Gallatin if (sc->dying) {
4042c0a1f0afSAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4043c0a1f0afSAndrew Gallatin return (EINVAL);
4044c0a1f0afSAndrew Gallatin }
40450fa7f681SAndrew Gallatin mxge_set_multicast_list(sc);
4046a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4047b2fc195eSAndrew Gallatin break;
4048b2fc195eSAndrew Gallatin
4049b2fc195eSAndrew Gallatin case SIOCSIFCAP:
4050a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx);
405193037a67SJustin Hibbits mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
4052b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) {
405393037a67SJustin Hibbits if (IFCAP_TXCSUM & if_getcapenable(ifp)) {
4054cbb9ccf7SRyan Moeller mask &= ~IFCAP_TSO4;
405593037a67SJustin Hibbits if_setcapenablebit(ifp, 0, (IFCAP_TXCSUM|IFCAP_TSO4));
405693037a67SJustin Hibbits if_sethwassistbits(ifp, 0, (CSUM_TCP | CSUM_UDP));
4057b2fc195eSAndrew Gallatin } else {
405893037a67SJustin Hibbits if_setcapenablebit(ifp, IFCAP_TXCSUM, 0);
405993037a67SJustin Hibbits if_sethwassistbits(ifp, (CSUM_TCP | CSUM_UDP), 0);
4060b2fc195eSAndrew Gallatin }
4061cbb9ccf7SRyan Moeller }
4062cbb9ccf7SRyan Moeller if (mask & IFCAP_RXCSUM) {
406393037a67SJustin Hibbits if (IFCAP_RXCSUM & if_getcapenable(ifp)) {
406493037a67SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_RXCSUM);
4065b2fc195eSAndrew Gallatin } else {
406693037a67SJustin Hibbits if_setcapenablebit(ifp, IFCAP_RXCSUM, 0);
4067b2fc195eSAndrew Gallatin }
4068b2fc195eSAndrew Gallatin }
4069aed8e389SAndrew Gallatin if (mask & IFCAP_TSO4) {
407093037a67SJustin Hibbits if (IFCAP_TSO4 & if_getcapenable(ifp)) {
407193037a67SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_TSO4);
407293037a67SJustin Hibbits } else if (IFCAP_TXCSUM & if_getcapenable(ifp)) {
407393037a67SJustin Hibbits if_setcapenablebit(ifp, IFCAP_TSO4, 0);
407493037a67SJustin Hibbits if_sethwassistbits(ifp, CSUM_TSO, 0);
4075aed8e389SAndrew Gallatin } else {
4076aed8e389SAndrew Gallatin printf("mxge requires tx checksum offload"
4077aed8e389SAndrew Gallatin " be enabled to use TSO\n");
4078aed8e389SAndrew Gallatin err = EINVAL;
4079aed8e389SAndrew Gallatin }
4080aed8e389SAndrew Gallatin }
40810a7a780eSAndrew Gallatin #if IFCAP_TSO6
40820a7a780eSAndrew Gallatin if (mask & IFCAP_TXCSUM_IPV6) {
408393037a67SJustin Hibbits if (IFCAP_TXCSUM_IPV6 & if_getcapenable(ifp)) {
4084cbb9ccf7SRyan Moeller mask &= ~IFCAP_TSO6;
408593037a67SJustin Hibbits if_setcapenablebit(ifp, 0,
408693037a67SJustin Hibbits IFCAP_TXCSUM_IPV6 | IFCAP_TSO6);
408793037a67SJustin Hibbits if_sethwassistbits(ifp, 0,
408893037a67SJustin Hibbits CSUM_TCP_IPV6 | CSUM_UDP);
40890a7a780eSAndrew Gallatin } else {
409093037a67SJustin Hibbits if_setcapenablebit(ifp, IFCAP_TXCSUM_IPV6, 0);
409193037a67SJustin Hibbits if_sethwassistbits(ifp,
409293037a67SJustin Hibbits CSUM_TCP_IPV6 | CSUM_UDP_IPV6, 0);
40930a7a780eSAndrew Gallatin }
4094cbb9ccf7SRyan Moeller }
4095cbb9ccf7SRyan Moeller if (mask & IFCAP_RXCSUM_IPV6) {
409693037a67SJustin Hibbits if (IFCAP_RXCSUM_IPV6 & if_getcapenable(ifp)) {
409793037a67SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_RXCSUM_IPV6);
40980a7a780eSAndrew Gallatin } else {
409993037a67SJustin Hibbits if_setcapenablebit(ifp, IFCAP_RXCSUM_IPV6, 0);
41000a7a780eSAndrew Gallatin }
41010a7a780eSAndrew Gallatin }
41020a7a780eSAndrew Gallatin if (mask & IFCAP_TSO6) {
410393037a67SJustin Hibbits if (IFCAP_TSO6 & if_getcapenable(ifp)) {
410493037a67SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_TSO6);
410593037a67SJustin Hibbits } else if (IFCAP_TXCSUM_IPV6 & if_getcapenable(ifp)) {
410693037a67SJustin Hibbits if_setcapenablebit(ifp, IFCAP_TSO6, 0);
410793037a67SJustin Hibbits if_sethwassistbits(ifp, CSUM_TSO, 0);
41080a7a780eSAndrew Gallatin } else {
41090a7a780eSAndrew Gallatin printf("mxge requires tx checksum offload"
41100a7a780eSAndrew Gallatin " be enabled to use TSO\n");
41110a7a780eSAndrew Gallatin err = EINVAL;
41120a7a780eSAndrew Gallatin }
41130a7a780eSAndrew Gallatin }
41140a7a780eSAndrew Gallatin #endif /*IFCAP_TSO6 */
41150a7a780eSAndrew Gallatin
411626dd49c6SAndrew Gallatin if (mask & IFCAP_LRO)
411793037a67SJustin Hibbits if_togglecapenable(ifp, IFCAP_LRO);
4118c792928fSAndrew Gallatin if (mask & IFCAP_VLAN_HWTAGGING)
411993037a67SJustin Hibbits if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
41200dce6781SAndrew Gallatin if (mask & IFCAP_VLAN_HWTSO)
412193037a67SJustin Hibbits if_togglecapenable(ifp, IFCAP_VLAN_HWTSO);
41220dce6781SAndrew Gallatin
412393037a67SJustin Hibbits if (!(if_getcapabilities(ifp) & IFCAP_VLAN_HWTSO) ||
412493037a67SJustin Hibbits !(if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING))
412593037a67SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_VLAN_HWTSO);
41260dce6781SAndrew Gallatin
4127a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4128c792928fSAndrew Gallatin VLAN_CAPABILITIES(ifp);
4129c792928fSAndrew Gallatin
4130b2fc195eSAndrew Gallatin break;
4131b2fc195eSAndrew Gallatin
4132b2fc195eSAndrew Gallatin case SIOCGIFMEDIA:
4133c406ad2eSAndrew Gallatin mtx_lock(&sc->driver_mtx);
4134c0a1f0afSAndrew Gallatin if (sc->dying) {
4135c0a1f0afSAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4136c0a1f0afSAndrew Gallatin return (EINVAL);
4137c0a1f0afSAndrew Gallatin }
4138c406ad2eSAndrew Gallatin mxge_media_probe(sc);
4139c406ad2eSAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4140b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4141b2fc195eSAndrew Gallatin &sc->media, command);
4142b2fc195eSAndrew Gallatin break;
4143b2fc195eSAndrew Gallatin
4144df131e84SAndrew Gallatin case SIOCGI2C:
4145df131e84SAndrew Gallatin if (sc->connector != MXGE_XFP &&
4146df131e84SAndrew Gallatin sc->connector != MXGE_SFP) {
4147df131e84SAndrew Gallatin err = ENXIO;
4148df131e84SAndrew Gallatin break;
4149df131e84SAndrew Gallatin }
4150df131e84SAndrew Gallatin err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c));
4151df131e84SAndrew Gallatin if (err != 0)
4152df131e84SAndrew Gallatin break;
4153df131e84SAndrew Gallatin mtx_lock(&sc->driver_mtx);
4154df131e84SAndrew Gallatin if (sc->dying) {
4155df131e84SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4156df131e84SAndrew Gallatin return (EINVAL);
4157df131e84SAndrew Gallatin }
4158df131e84SAndrew Gallatin err = mxge_fetch_i2c(sc, &i2c);
4159df131e84SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4160df131e84SAndrew Gallatin if (err == 0)
41611b8b041cSBrooks Davis err = copyout(&i2c, ifr_data_get_ptr(ifr),
4162df131e84SAndrew Gallatin sizeof(i2c));
4163df131e84SAndrew Gallatin break;
4164b2fc195eSAndrew Gallatin default:
4165c756fb6eSRavi Pokala err = ether_ioctl(ifp, command, data);
4166c756fb6eSRavi Pokala break;
4167b2fc195eSAndrew Gallatin }
4168b2fc195eSAndrew Gallatin return err;
4169b2fc195eSAndrew Gallatin }
4170b2fc195eSAndrew Gallatin
4171b2fc195eSAndrew Gallatin static void
mxge_fetch_tunables(mxge_softc_t * sc)41726d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4173b2fc195eSAndrew Gallatin {
4174b2fc195eSAndrew Gallatin
41751e413cf9SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
41766d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
41776d87a65dSAndrew Gallatin &mxge_flow_control);
41786d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
41796d87a65dSAndrew Gallatin &mxge_intr_coal_delay);
41806d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
41816d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable);
4182d91b1b49SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4183d91b1b49SAndrew Gallatin &mxge_force_firmware);
41845e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
41855e7d8541SAndrew Gallatin &mxge_deassert_wait);
41865e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose",
41875e7d8541SAndrew Gallatin &mxge_verbose);
4188dce01b9bSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
41891e413cf9SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
41901e413cf9SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
419194c7d993SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4192f9453025SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
419365c69066SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4194b2fc195eSAndrew Gallatin
41955e7d8541SAndrew Gallatin if (bootverbose)
41965e7d8541SAndrew Gallatin mxge_verbose = 1;
41976d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
41986d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30;
4199dce01b9bSAndrew Gallatin if (mxge_ticks == 0)
42001e413cf9SAndrew Gallatin mxge_ticks = hz / 2;
42016d87a65dSAndrew Gallatin sc->pause = mxge_flow_control;
42021e413cf9SAndrew Gallatin if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4203bb8ddc66SAndrew Gallatin || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
42045769c5efSAndrew Gallatin mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4205b2fc195eSAndrew Gallatin }
4206f9453025SAndrew Gallatin if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4207f9453025SAndrew Gallatin mxge_initial_mtu < ETHER_MIN_LEN)
4208f9453025SAndrew Gallatin mxge_initial_mtu = ETHERMTU_JUMBO;
420965c69066SAndrew Gallatin
421065c69066SAndrew Gallatin if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
421165c69066SAndrew Gallatin mxge_throttle = MXGE_MAX_THROTTLE;
421265c69066SAndrew Gallatin if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
421365c69066SAndrew Gallatin mxge_throttle = MXGE_MIN_THROTTLE;
421465c69066SAndrew Gallatin sc->throttle = mxge_throttle;
42151e413cf9SAndrew Gallatin }
42161e413cf9SAndrew Gallatin
42171e413cf9SAndrew Gallatin static void
mxge_free_slices(mxge_softc_t * sc)42181e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
42191e413cf9SAndrew Gallatin {
42201e413cf9SAndrew Gallatin struct mxge_slice_state *ss;
42211e413cf9SAndrew Gallatin int i;
42221e413cf9SAndrew Gallatin
42231e413cf9SAndrew Gallatin if (sc->ss == NULL)
42241e413cf9SAndrew Gallatin return;
42251e413cf9SAndrew Gallatin
42261e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) {
42271e413cf9SAndrew Gallatin ss = &sc->ss[i];
42281e413cf9SAndrew Gallatin if (ss->fw_stats != NULL) {
42291e413cf9SAndrew Gallatin mxge_dma_free(&ss->fw_stats_dma);
42301e413cf9SAndrew Gallatin ss->fw_stats = NULL;
4231c6cb3e3fSAndrew Gallatin if (ss->tx.br != NULL) {
4232c6cb3e3fSAndrew Gallatin drbr_free(ss->tx.br, M_DEVBUF);
4233c6cb3e3fSAndrew Gallatin ss->tx.br = NULL;
4234c6cb3e3fSAndrew Gallatin }
42351e413cf9SAndrew Gallatin mtx_destroy(&ss->tx.mtx);
42361e413cf9SAndrew Gallatin }
42371e413cf9SAndrew Gallatin if (ss->rx_done.entry != NULL) {
42381e413cf9SAndrew Gallatin mxge_dma_free(&ss->rx_done.dma);
42391e413cf9SAndrew Gallatin ss->rx_done.entry = NULL;
42401e413cf9SAndrew Gallatin }
42411e413cf9SAndrew Gallatin }
42421e413cf9SAndrew Gallatin free(sc->ss, M_DEVBUF);
42431e413cf9SAndrew Gallatin sc->ss = NULL;
42441e413cf9SAndrew Gallatin }
42451e413cf9SAndrew Gallatin
42461e413cf9SAndrew Gallatin static int
mxge_alloc_slices(mxge_softc_t * sc)42471e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
42481e413cf9SAndrew Gallatin {
42491e413cf9SAndrew Gallatin mxge_cmd_t cmd;
42501e413cf9SAndrew Gallatin struct mxge_slice_state *ss;
42511e413cf9SAndrew Gallatin size_t bytes;
42521e413cf9SAndrew Gallatin int err, i, max_intr_slots;
42531e413cf9SAndrew Gallatin
42541e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
42551e413cf9SAndrew Gallatin if (err != 0) {
42561e413cf9SAndrew Gallatin device_printf(sc->dev, "Cannot determine rx ring size\n");
42571e413cf9SAndrew Gallatin return err;
42581e413cf9SAndrew Gallatin }
42591e413cf9SAndrew Gallatin sc->rx_ring_size = cmd.data0;
42601e413cf9SAndrew Gallatin max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
42611e413cf9SAndrew Gallatin
4262ac2fffa4SPedro F. Giffuni bytes = sizeof (*sc->ss) * sc->num_slices;
4263ac2fffa4SPedro F. Giffuni sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
42641e413cf9SAndrew Gallatin if (sc->ss == NULL)
42651e413cf9SAndrew Gallatin return (ENOMEM);
42661e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) {
42671e413cf9SAndrew Gallatin ss = &sc->ss[i];
42681e413cf9SAndrew Gallatin
42691e413cf9SAndrew Gallatin ss->sc = sc;
42701e413cf9SAndrew Gallatin
42711e413cf9SAndrew Gallatin /* allocate per-slice rx interrupt queues */
42721e413cf9SAndrew Gallatin
42731e413cf9SAndrew Gallatin bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
42741e413cf9SAndrew Gallatin err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
42751e413cf9SAndrew Gallatin if (err != 0)
42761e413cf9SAndrew Gallatin goto abort;
42771e413cf9SAndrew Gallatin ss->rx_done.entry = ss->rx_done.dma.addr;
42781e413cf9SAndrew Gallatin bzero(ss->rx_done.entry, bytes);
42791e413cf9SAndrew Gallatin
42801e413cf9SAndrew Gallatin /*
42811e413cf9SAndrew Gallatin * allocate the per-slice firmware stats; stats
42821e413cf9SAndrew Gallatin * (including tx) are used used only on the first
42831e413cf9SAndrew Gallatin * slice for now
42841e413cf9SAndrew Gallatin */
42851e413cf9SAndrew Gallatin
42861e413cf9SAndrew Gallatin bytes = sizeof (*ss->fw_stats);
42871e413cf9SAndrew Gallatin err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
42881e413cf9SAndrew Gallatin sizeof (*ss->fw_stats), 64);
42891e413cf9SAndrew Gallatin if (err != 0)
42901e413cf9SAndrew Gallatin goto abort;
42911e413cf9SAndrew Gallatin ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
42921e413cf9SAndrew Gallatin snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
42931e413cf9SAndrew Gallatin "%s:tx(%d)", device_get_nameunit(sc->dev), i);
42941e413cf9SAndrew Gallatin mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4295c6cb3e3fSAndrew Gallatin ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4296c6cb3e3fSAndrew Gallatin &ss->tx.mtx);
42971e413cf9SAndrew Gallatin }
42981e413cf9SAndrew Gallatin
42991e413cf9SAndrew Gallatin return (0);
43001e413cf9SAndrew Gallatin
43011e413cf9SAndrew Gallatin abort:
43021e413cf9SAndrew Gallatin mxge_free_slices(sc);
43031e413cf9SAndrew Gallatin return (ENOMEM);
43041e413cf9SAndrew Gallatin }
43051e413cf9SAndrew Gallatin
43061e413cf9SAndrew Gallatin static void
mxge_slice_probe(mxge_softc_t * sc)43071e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
43081e413cf9SAndrew Gallatin {
43091e413cf9SAndrew Gallatin mxge_cmd_t cmd;
43101e413cf9SAndrew Gallatin char *old_fw;
43111e413cf9SAndrew Gallatin int msix_cnt, status, max_intr_slots;
43121e413cf9SAndrew Gallatin
43131e413cf9SAndrew Gallatin sc->num_slices = 1;
43141e413cf9SAndrew Gallatin /*
43151e413cf9SAndrew Gallatin * don't enable multiple slices if they are not enabled,
43161e413cf9SAndrew Gallatin * or if this is not an SMP system
43171e413cf9SAndrew Gallatin */
43181e413cf9SAndrew Gallatin
43191e413cf9SAndrew Gallatin if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
43201e413cf9SAndrew Gallatin return;
43211e413cf9SAndrew Gallatin
43221e413cf9SAndrew Gallatin /* see how many MSI-X interrupts are available */
43231e413cf9SAndrew Gallatin msix_cnt = pci_msix_count(sc->dev);
43241e413cf9SAndrew Gallatin if (msix_cnt < 2)
43251e413cf9SAndrew Gallatin return;
43261e413cf9SAndrew Gallatin
43271e413cf9SAndrew Gallatin /* now load the slice aware firmware see what it supports */
43281e413cf9SAndrew Gallatin old_fw = sc->fw_name;
43291e413cf9SAndrew Gallatin if (old_fw == mxge_fw_aligned)
43301e413cf9SAndrew Gallatin sc->fw_name = mxge_fw_rss_aligned;
43311e413cf9SAndrew Gallatin else
43321e413cf9SAndrew Gallatin sc->fw_name = mxge_fw_rss_unaligned;
43331e413cf9SAndrew Gallatin status = mxge_load_firmware(sc, 0);
43341e413cf9SAndrew Gallatin if (status != 0) {
43351e413cf9SAndrew Gallatin device_printf(sc->dev, "Falling back to a single slice\n");
43361e413cf9SAndrew Gallatin return;
43371e413cf9SAndrew Gallatin }
43381e413cf9SAndrew Gallatin
43391e413cf9SAndrew Gallatin /* try to send a reset command to the card to see if it
43401e413cf9SAndrew Gallatin is alive */
43411e413cf9SAndrew Gallatin memset(&cmd, 0, sizeof (cmd));
43421e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
43431e413cf9SAndrew Gallatin if (status != 0) {
43441e413cf9SAndrew Gallatin device_printf(sc->dev, "failed reset\n");
43451e413cf9SAndrew Gallatin goto abort_with_fw;
43461e413cf9SAndrew Gallatin }
43471e413cf9SAndrew Gallatin
43481e413cf9SAndrew Gallatin /* get rx ring size */
43491e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
43501e413cf9SAndrew Gallatin if (status != 0) {
43511e413cf9SAndrew Gallatin device_printf(sc->dev, "Cannot determine rx ring size\n");
43521e413cf9SAndrew Gallatin goto abort_with_fw;
43531e413cf9SAndrew Gallatin }
43541e413cf9SAndrew Gallatin max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
43551e413cf9SAndrew Gallatin
43561e413cf9SAndrew Gallatin /* tell it the size of the interrupt queues */
43571e413cf9SAndrew Gallatin cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
43581e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
43591e413cf9SAndrew Gallatin if (status != 0) {
43601e413cf9SAndrew Gallatin device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
43611e413cf9SAndrew Gallatin goto abort_with_fw;
43621e413cf9SAndrew Gallatin }
43631e413cf9SAndrew Gallatin
43641e413cf9SAndrew Gallatin /* ask the maximum number of slices it supports */
43651e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
43661e413cf9SAndrew Gallatin if (status != 0) {
43671e413cf9SAndrew Gallatin device_printf(sc->dev,
43681e413cf9SAndrew Gallatin "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
43691e413cf9SAndrew Gallatin goto abort_with_fw;
43701e413cf9SAndrew Gallatin }
43711e413cf9SAndrew Gallatin sc->num_slices = cmd.data0;
43721e413cf9SAndrew Gallatin if (sc->num_slices > msix_cnt)
43731e413cf9SAndrew Gallatin sc->num_slices = msix_cnt;
43741e413cf9SAndrew Gallatin
43751e413cf9SAndrew Gallatin if (mxge_max_slices == -1) {
43761e413cf9SAndrew Gallatin /* cap to number of CPUs in system */
43771e413cf9SAndrew Gallatin if (sc->num_slices > mp_ncpus)
43781e413cf9SAndrew Gallatin sc->num_slices = mp_ncpus;
43791e413cf9SAndrew Gallatin } else {
43801e413cf9SAndrew Gallatin if (sc->num_slices > mxge_max_slices)
43811e413cf9SAndrew Gallatin sc->num_slices = mxge_max_slices;
43821e413cf9SAndrew Gallatin }
43831e413cf9SAndrew Gallatin /* make sure it is a power of two */
43841e413cf9SAndrew Gallatin while (sc->num_slices & (sc->num_slices - 1))
43851e413cf9SAndrew Gallatin sc->num_slices--;
43861e413cf9SAndrew Gallatin
43871e413cf9SAndrew Gallatin if (mxge_verbose)
43881e413cf9SAndrew Gallatin device_printf(sc->dev, "using %d slices\n",
43891e413cf9SAndrew Gallatin sc->num_slices);
43901e413cf9SAndrew Gallatin
43911e413cf9SAndrew Gallatin return;
43921e413cf9SAndrew Gallatin
43931e413cf9SAndrew Gallatin abort_with_fw:
43941e413cf9SAndrew Gallatin sc->fw_name = old_fw;
43951e413cf9SAndrew Gallatin (void) mxge_load_firmware(sc, 0);
43961e413cf9SAndrew Gallatin }
43971e413cf9SAndrew Gallatin
43981e413cf9SAndrew Gallatin static int
mxge_add_msix_irqs(mxge_softc_t * sc)43991e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
44001e413cf9SAndrew Gallatin {
4401ac2fffa4SPedro F. Giffuni size_t bytes;
44021e413cf9SAndrew Gallatin int count, err, i, rid;
44031e413cf9SAndrew Gallatin
44041e413cf9SAndrew Gallatin rid = PCIR_BAR(2);
44051e413cf9SAndrew Gallatin sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
44061e413cf9SAndrew Gallatin &rid, RF_ACTIVE);
44071e413cf9SAndrew Gallatin
44081e413cf9SAndrew Gallatin if (sc->msix_table_res == NULL) {
44091e413cf9SAndrew Gallatin device_printf(sc->dev, "couldn't alloc MSIX table res\n");
44101e413cf9SAndrew Gallatin return ENXIO;
44111e413cf9SAndrew Gallatin }
44121e413cf9SAndrew Gallatin
44131e413cf9SAndrew Gallatin count = sc->num_slices;
44141e413cf9SAndrew Gallatin err = pci_alloc_msix(sc->dev, &count);
44151e413cf9SAndrew Gallatin if (err != 0) {
44161e413cf9SAndrew Gallatin device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
44171e413cf9SAndrew Gallatin "err = %d \n", sc->num_slices, err);
44181e413cf9SAndrew Gallatin goto abort_with_msix_table;
44191e413cf9SAndrew Gallatin }
44201e413cf9SAndrew Gallatin if (count < sc->num_slices) {
44211e413cf9SAndrew Gallatin device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
44221e413cf9SAndrew Gallatin count, sc->num_slices);
44231e413cf9SAndrew Gallatin device_printf(sc->dev,
44241e413cf9SAndrew Gallatin "Try setting hw.mxge.max_slices to %d\n",
44251e413cf9SAndrew Gallatin count);
44261e413cf9SAndrew Gallatin err = ENOSPC;
44271e413cf9SAndrew Gallatin goto abort_with_msix;
44281e413cf9SAndrew Gallatin }
4429ac2fffa4SPedro F. Giffuni bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
4430ac2fffa4SPedro F. Giffuni sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44311e413cf9SAndrew Gallatin if (sc->msix_irq_res == NULL) {
44321e413cf9SAndrew Gallatin err = ENOMEM;
44331e413cf9SAndrew Gallatin goto abort_with_msix;
44341e413cf9SAndrew Gallatin }
44351e413cf9SAndrew Gallatin
44361e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) {
44371e413cf9SAndrew Gallatin rid = i + 1;
44381e413cf9SAndrew Gallatin sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
44391e413cf9SAndrew Gallatin SYS_RES_IRQ,
44401e413cf9SAndrew Gallatin &rid, RF_ACTIVE);
44411e413cf9SAndrew Gallatin if (sc->msix_irq_res[i] == NULL) {
44421e413cf9SAndrew Gallatin device_printf(sc->dev, "couldn't allocate IRQ res"
44431e413cf9SAndrew Gallatin " for message %d\n", i);
44441e413cf9SAndrew Gallatin err = ENXIO;
44451e413cf9SAndrew Gallatin goto abort_with_res;
44461e413cf9SAndrew Gallatin }
44471e413cf9SAndrew Gallatin }
44481e413cf9SAndrew Gallatin
4449ac2fffa4SPedro F. Giffuni bytes = sizeof (*sc->msix_ih) * sc->num_slices;
4450ac2fffa4SPedro F. Giffuni sc->msix_ih = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44511e413cf9SAndrew Gallatin
44521e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) {
44531e413cf9SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
4454c98b837aSElliott Mitchell INTR_TYPE_NET | INTR_MPSAFE, NULL,
445537d89b0cSAndrew Gallatin mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
44561e413cf9SAndrew Gallatin if (err != 0) {
44571e413cf9SAndrew Gallatin device_printf(sc->dev, "couldn't setup intr for "
44581e413cf9SAndrew Gallatin "message %d\n", i);
44591e413cf9SAndrew Gallatin goto abort_with_intr;
44601e413cf9SAndrew Gallatin }
446121089137SAndrew Gallatin bus_describe_intr(sc->dev, sc->msix_irq_res[i],
446221089137SAndrew Gallatin sc->msix_ih[i], "s%d", i);
44631e413cf9SAndrew Gallatin }
44641e413cf9SAndrew Gallatin
44651e413cf9SAndrew Gallatin if (mxge_verbose) {
44661e413cf9SAndrew Gallatin device_printf(sc->dev, "using %d msix IRQs:",
44671e413cf9SAndrew Gallatin sc->num_slices);
44681e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++)
4469da1b038aSJustin Hibbits printf(" %jd", rman_get_start(sc->msix_irq_res[i]));
44701e413cf9SAndrew Gallatin printf("\n");
44711e413cf9SAndrew Gallatin }
44721e413cf9SAndrew Gallatin return (0);
44731e413cf9SAndrew Gallatin
44741e413cf9SAndrew Gallatin abort_with_intr:
44751e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) {
44761e413cf9SAndrew Gallatin if (sc->msix_ih[i] != NULL) {
44771e413cf9SAndrew Gallatin bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
44781e413cf9SAndrew Gallatin sc->msix_ih[i]);
44791e413cf9SAndrew Gallatin sc->msix_ih[i] = NULL;
44801e413cf9SAndrew Gallatin }
44811e413cf9SAndrew Gallatin }
44821e413cf9SAndrew Gallatin free(sc->msix_ih, M_DEVBUF);
44831e413cf9SAndrew Gallatin
44841e413cf9SAndrew Gallatin abort_with_res:
44851e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) {
44861e413cf9SAndrew Gallatin rid = i + 1;
44871e413cf9SAndrew Gallatin if (sc->msix_irq_res[i] != NULL)
44881e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
44891e413cf9SAndrew Gallatin sc->msix_irq_res[i]);
44901e413cf9SAndrew Gallatin sc->msix_irq_res[i] = NULL;
44911e413cf9SAndrew Gallatin }
44921e413cf9SAndrew Gallatin free(sc->msix_irq_res, M_DEVBUF);
44931e413cf9SAndrew Gallatin
44941e413cf9SAndrew Gallatin abort_with_msix:
44951e413cf9SAndrew Gallatin pci_release_msi(sc->dev);
44961e413cf9SAndrew Gallatin
44971e413cf9SAndrew Gallatin abort_with_msix_table:
44981e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
44991e413cf9SAndrew Gallatin sc->msix_table_res);
45001e413cf9SAndrew Gallatin
45011e413cf9SAndrew Gallatin return err;
45021e413cf9SAndrew Gallatin }
45031e413cf9SAndrew Gallatin
45041e413cf9SAndrew Gallatin static int
mxge_add_single_irq(mxge_softc_t * sc)45051e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
45061e413cf9SAndrew Gallatin {
45071e413cf9SAndrew Gallatin int count, err, rid;
45081e413cf9SAndrew Gallatin
45091e413cf9SAndrew Gallatin count = pci_msi_count(sc->dev);
45101e413cf9SAndrew Gallatin if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
45111e413cf9SAndrew Gallatin rid = 1;
45121e413cf9SAndrew Gallatin } else {
45131e413cf9SAndrew Gallatin rid = 0;
451491ed8913SAndrew Gallatin sc->legacy_irq = 1;
45151e413cf9SAndrew Gallatin }
451643cd6160SJustin Hibbits sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
451743cd6160SJustin Hibbits RF_SHAREABLE | RF_ACTIVE);
45181e413cf9SAndrew Gallatin if (sc->irq_res == NULL) {
45191e413cf9SAndrew Gallatin device_printf(sc->dev, "could not alloc interrupt\n");
45201e413cf9SAndrew Gallatin return ENXIO;
45211e413cf9SAndrew Gallatin }
45221e413cf9SAndrew Gallatin if (mxge_verbose)
4523da1b038aSJustin Hibbits device_printf(sc->dev, "using %s irq %jd\n",
452491ed8913SAndrew Gallatin sc->legacy_irq ? "INTx" : "MSI",
45251e413cf9SAndrew Gallatin rman_get_start(sc->irq_res));
45261e413cf9SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res,
4527c98b837aSElliott Mitchell INTR_TYPE_NET | INTR_MPSAFE, NULL,
452837d89b0cSAndrew Gallatin mxge_intr, &sc->ss[0], &sc->ih);
45291e413cf9SAndrew Gallatin if (err != 0) {
45301e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_IRQ,
453191ed8913SAndrew Gallatin sc->legacy_irq ? 0 : 1, sc->irq_res);
453291ed8913SAndrew Gallatin if (!sc->legacy_irq)
45331e413cf9SAndrew Gallatin pci_release_msi(sc->dev);
45341e413cf9SAndrew Gallatin }
45351e413cf9SAndrew Gallatin return err;
45361e413cf9SAndrew Gallatin }
45371e413cf9SAndrew Gallatin
45381e413cf9SAndrew Gallatin static void
mxge_rem_msix_irqs(mxge_softc_t * sc)45391e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
45401e413cf9SAndrew Gallatin {
45411e413cf9SAndrew Gallatin int i, rid;
45421e413cf9SAndrew Gallatin
45431e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) {
45441e413cf9SAndrew Gallatin if (sc->msix_ih[i] != NULL) {
45451e413cf9SAndrew Gallatin bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
45461e413cf9SAndrew Gallatin sc->msix_ih[i]);
45471e413cf9SAndrew Gallatin sc->msix_ih[i] = NULL;
45481e413cf9SAndrew Gallatin }
45491e413cf9SAndrew Gallatin }
45501e413cf9SAndrew Gallatin free(sc->msix_ih, M_DEVBUF);
45511e413cf9SAndrew Gallatin
45521e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) {
45531e413cf9SAndrew Gallatin rid = i + 1;
45541e413cf9SAndrew Gallatin if (sc->msix_irq_res[i] != NULL)
45551e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
45561e413cf9SAndrew Gallatin sc->msix_irq_res[i]);
45571e413cf9SAndrew Gallatin sc->msix_irq_res[i] = NULL;
45581e413cf9SAndrew Gallatin }
45591e413cf9SAndrew Gallatin free(sc->msix_irq_res, M_DEVBUF);
45601e413cf9SAndrew Gallatin
45611e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
45621e413cf9SAndrew Gallatin sc->msix_table_res);
45631e413cf9SAndrew Gallatin
45641e413cf9SAndrew Gallatin pci_release_msi(sc->dev);
45651e413cf9SAndrew Gallatin return;
45661e413cf9SAndrew Gallatin }
45671e413cf9SAndrew Gallatin
45681e413cf9SAndrew Gallatin static void
mxge_rem_single_irq(mxge_softc_t * sc)45691e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
45701e413cf9SAndrew Gallatin {
45711e413cf9SAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
45721e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_IRQ,
457391ed8913SAndrew Gallatin sc->legacy_irq ? 0 : 1, sc->irq_res);
457491ed8913SAndrew Gallatin if (!sc->legacy_irq)
45751e413cf9SAndrew Gallatin pci_release_msi(sc->dev);
45761e413cf9SAndrew Gallatin }
45771e413cf9SAndrew Gallatin
45781e413cf9SAndrew Gallatin static void
mxge_rem_irq(mxge_softc_t * sc)45791e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
45801e413cf9SAndrew Gallatin {
45811e413cf9SAndrew Gallatin if (sc->num_slices > 1)
45821e413cf9SAndrew Gallatin mxge_rem_msix_irqs(sc);
45831e413cf9SAndrew Gallatin else
45841e413cf9SAndrew Gallatin mxge_rem_single_irq(sc);
45851e413cf9SAndrew Gallatin }
45861e413cf9SAndrew Gallatin
45871e413cf9SAndrew Gallatin static int
mxge_add_irq(mxge_softc_t * sc)45881e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
45891e413cf9SAndrew Gallatin {
45901e413cf9SAndrew Gallatin int err;
45911e413cf9SAndrew Gallatin
45921e413cf9SAndrew Gallatin if (sc->num_slices > 1)
45931e413cf9SAndrew Gallatin err = mxge_add_msix_irqs(sc);
45941e413cf9SAndrew Gallatin else
45951e413cf9SAndrew Gallatin err = mxge_add_single_irq(sc);
45961e413cf9SAndrew Gallatin
45971e413cf9SAndrew Gallatin if (0 && err == 0 && sc->num_slices > 1) {
45981e413cf9SAndrew Gallatin mxge_rem_msix_irqs(sc);
45991e413cf9SAndrew Gallatin err = mxge_add_msix_irqs(sc);
46001e413cf9SAndrew Gallatin }
46011e413cf9SAndrew Gallatin return err;
46021e413cf9SAndrew Gallatin }
46031e413cf9SAndrew Gallatin
4604b2fc195eSAndrew Gallatin static int
mxge_attach(device_t dev)46056d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4606b2fc195eSAndrew Gallatin {
46070a7a780eSAndrew Gallatin mxge_cmd_t cmd;
46086d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev);
460993037a67SJustin Hibbits if_t ifp;
46101e413cf9SAndrew Gallatin int err, rid;
4611b2fc195eSAndrew Gallatin
4612b2fc195eSAndrew Gallatin sc->dev = dev;
46136d87a65dSAndrew Gallatin mxge_fetch_tunables(sc);
4614b2fc195eSAndrew Gallatin
461572c042dfSAndrew Gallatin TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
4616a4a75d67SJohn Baldwin sc->tq = taskqueue_create("mxge_taskq", M_WAITOK,
4617a4a75d67SJohn Baldwin taskqueue_thread_enqueue, &sc->tq);
461872c042dfSAndrew Gallatin
461962ce43ccSScott Long err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
4620b2fc195eSAndrew Gallatin 1, /* alignment */
46211e413cf9SAndrew Gallatin 0, /* boundary */
4622b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */
4623b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */
4624b2fc195eSAndrew Gallatin NULL, NULL, /* filter */
4625aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */
46265e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */
46271e413cf9SAndrew Gallatin 65536, /* maxsegsize */
4628b2fc195eSAndrew Gallatin 0, /* flags */
4629b2fc195eSAndrew Gallatin NULL, NULL, /* lock */
4630b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */
4631b2fc195eSAndrew Gallatin
4632b2fc195eSAndrew Gallatin if (err != 0) {
4633b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n",
4634b2fc195eSAndrew Gallatin err);
463572c042dfSAndrew Gallatin goto abort_with_tq;
4636b2fc195eSAndrew Gallatin }
4637b2fc195eSAndrew Gallatin
4638b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER);
46391e413cf9SAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev));
46401e413cf9SAndrew Gallatin
4641a98d6cd7SAndrew Gallatin snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4642a98d6cd7SAndrew Gallatin device_get_nameunit(dev));
4643a98d6cd7SAndrew Gallatin mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4644a98d6cd7SAndrew Gallatin snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4645a98d6cd7SAndrew Gallatin "%s:drv", device_get_nameunit(dev));
4646a98d6cd7SAndrew Gallatin mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4647b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF);
4648b2fc195eSAndrew Gallatin
4649dce01b9bSAndrew Gallatin callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4650d91b1b49SAndrew Gallatin
4651dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc);
4652b2fc195eSAndrew Gallatin
4653b2fc195eSAndrew Gallatin /* Map the board into the kernel */
4654b2fc195eSAndrew Gallatin rid = PCIR_BARS;
465543cd6160SJustin Hibbits sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
465643cd6160SJustin Hibbits RF_ACTIVE);
4657b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) {
4658b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n");
4659b2fc195eSAndrew Gallatin err = ENXIO;
4660b2fc195eSAndrew Gallatin goto abort_with_lock;
4661b2fc195eSAndrew Gallatin }
4662b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res);
4663b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4664b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) {
4665da1b038aSJustin Hibbits device_printf(dev, "impossible memory region size %jd\n",
4666b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res));
4667b2fc195eSAndrew Gallatin err = ENXIO;
4668b2fc195eSAndrew Gallatin goto abort_with_mem_res;
4669b2fc195eSAndrew Gallatin }
4670b2fc195eSAndrew Gallatin
4671b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of
4672b2fc195eSAndrew Gallatin lanai SRAM */
46736d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4674b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4675b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res),
46766d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4677b2fc195eSAndrew Gallatin sc->eeprom_strings,
46786d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2);
46796d87a65dSAndrew Gallatin err = mxge_parse_strings(sc);
4680b2fc195eSAndrew Gallatin if (err != 0)
4681b2fc195eSAndrew Gallatin goto abort_with_mem_res;
4682b2fc195eSAndrew Gallatin
4683b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */
46846d87a65dSAndrew Gallatin mxge_enable_wc(sc);
4685b2fc195eSAndrew Gallatin
4686b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */
46876d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma,
46886d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64);
4689b2fc195eSAndrew Gallatin if (err != 0)
4690b2fc195eSAndrew Gallatin goto abort_with_mem_res;
4691b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
46926d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4693b2fc195eSAndrew Gallatin if (err != 0)
4694b2fc195eSAndrew Gallatin goto abort_with_cmd_dma;
4695b2fc195eSAndrew Gallatin
4696a98d6cd7SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4697a98d6cd7SAndrew Gallatin if (err != 0)
46981e413cf9SAndrew Gallatin goto abort_with_zeropad_dma;
4699b2fc195eSAndrew Gallatin
47008fe615baSAndrew Gallatin /* select & load the firmware */
47018fe615baSAndrew Gallatin err = mxge_select_firmware(sc);
4702b2fc195eSAndrew Gallatin if (err != 0)
47031e413cf9SAndrew Gallatin goto abort_with_dmabench;
47045e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay;
47051e413cf9SAndrew Gallatin
47061e413cf9SAndrew Gallatin mxge_slice_probe(sc);
47071e413cf9SAndrew Gallatin err = mxge_alloc_slices(sc);
47081e413cf9SAndrew Gallatin if (err != 0)
47091e413cf9SAndrew Gallatin goto abort_with_dmabench;
47101e413cf9SAndrew Gallatin
4711adae7080SAndrew Gallatin err = mxge_reset(sc, 0);
4712b2fc195eSAndrew Gallatin if (err != 0)
47131e413cf9SAndrew Gallatin goto abort_with_slices;
4714b2fc195eSAndrew Gallatin
4715a98d6cd7SAndrew Gallatin err = mxge_alloc_rings(sc);
4716a98d6cd7SAndrew Gallatin if (err != 0) {
4717a98d6cd7SAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n");
47182e084798SAndrew Gallatin goto abort_with_slices;
4719a98d6cd7SAndrew Gallatin }
4720a98d6cd7SAndrew Gallatin
47211e413cf9SAndrew Gallatin err = mxge_add_irq(sc);
4722a98d6cd7SAndrew Gallatin if (err != 0) {
47231e413cf9SAndrew Gallatin device_printf(sc->dev, "failed to add irq\n");
4724a98d6cd7SAndrew Gallatin goto abort_with_rings;
4725a98d6cd7SAndrew Gallatin }
47261e413cf9SAndrew Gallatin
472793037a67SJustin Hibbits if_setbaudrate(ifp, IF_Gbps(10));
472893037a67SJustin Hibbits if_setcapabilities(ifp, IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
472926dd49c6SAndrew Gallatin IFCAP_VLAN_MTU | IFCAP_LINKSTATE | IFCAP_TXCSUM_IPV6 |
473093037a67SJustin Hibbits IFCAP_RXCSUM_IPV6);
473126dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
473293037a67SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_LRO, 0);
4733eb6219e3SAndrew Gallatin #endif
473437d89b0cSAndrew Gallatin
473537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
473693037a67SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM, 0);
47370dce6781SAndrew Gallatin
47380dce6781SAndrew Gallatin /* Only FW 1.4.32 and newer can do TSO over vlans */
47390dce6781SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
47400dce6781SAndrew Gallatin sc->fw_ver_tiny >= 32)
474193037a67SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTSO, 0);
474237d89b0cSAndrew Gallatin #endif
4743053e637fSAndrew Gallatin sc->max_mtu = mxge_max_mtu(sc);
4744053e637fSAndrew Gallatin if (sc->max_mtu >= 9000)
474593037a67SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_JUMBO_MTU, 0);
4746053e637fSAndrew Gallatin else
4747053e637fSAndrew Gallatin device_printf(dev, "MTU limited to %d. Install "
4748adae7080SAndrew Gallatin "latest firmware for 9000 byte jumbo support\n",
4749053e637fSAndrew Gallatin sc->max_mtu - ETHER_HDR_LEN);
475093037a67SJustin Hibbits if_sethwassist(ifp, CSUM_TCP | CSUM_UDP | CSUM_TSO);
475193037a67SJustin Hibbits if_sethwassistbits(ifp, CSUM_TCP_IPV6 | CSUM_UDP_IPV6, 0);
47520a7a780eSAndrew Gallatin /* check to see if f/w supports TSO for IPv6 */
47530a7a780eSAndrew Gallatin if (!mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd)) {
47540a7a780eSAndrew Gallatin if (CSUM_TCP_IPV6)
475593037a67SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_TSO6, 0);
47560a7a780eSAndrew Gallatin sc->max_tso6_hlen = min(cmd.data0,
47570a7a780eSAndrew Gallatin sizeof (sc->ss[0].scratch));
47580a7a780eSAndrew Gallatin }
475993037a67SJustin Hibbits if_setcapenable(ifp, if_getcapabilities(ifp));
4760f04b33f8SAndrew Gallatin if (sc->lro_cnt == 0)
476193037a67SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_LRO);
476293037a67SJustin Hibbits if_setinitfn(ifp, mxge_init);
476393037a67SJustin Hibbits if_setsoftc(ifp, sc);
476493037a67SJustin Hibbits if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
476593037a67SJustin Hibbits if_setioctlfn(ifp, mxge_ioctl);
476693037a67SJustin Hibbits if_setstartfn(ifp, mxge_start);
476793037a67SJustin Hibbits if_setgetcounterfn(ifp, mxge_get_counter);
476893037a67SJustin Hibbits if_sethwtsomax(ifp, IP_MAXPACKET - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN));
476993037a67SJustin Hibbits if_sethwtsomaxsegcount(ifp, sc->ss[0].tx.max_desc);
477093037a67SJustin Hibbits if_sethwtsomaxsegsize(ifp, IP_MAXPACKET);
4771c587e59fSAndrew Gallatin /* Initialise the ifmedia structure */
4772c587e59fSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change,
4773c587e59fSAndrew Gallatin mxge_media_status);
4774c406ad2eSAndrew Gallatin mxge_media_init(sc);
4775c587e59fSAndrew Gallatin mxge_media_probe(sc);
47768c5d766cSAndrew Gallatin sc->dying = 0;
4777b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr);
4778f9453025SAndrew Gallatin /* ether_ifattach sets mtu to ETHERMTU */
4779f9453025SAndrew Gallatin if (mxge_initial_mtu != ETHERMTU)
4780f9453025SAndrew Gallatin mxge_change_mtu(sc, mxge_initial_mtu);
4781b2fc195eSAndrew Gallatin
47826d87a65dSAndrew Gallatin mxge_add_sysctls(sc);
478393037a67SJustin Hibbits if_settransmitfn(ifp, mxge_transmit);
478493037a67SJustin Hibbits if_setqflushfn(ifp, mxge_qflush);
47852e084798SAndrew Gallatin taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
47862e084798SAndrew Gallatin device_get_nameunit(sc->dev));
47876b484a49SAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4788b2fc195eSAndrew Gallatin return 0;
4789b2fc195eSAndrew Gallatin
4790a98d6cd7SAndrew Gallatin abort_with_rings:
4791a98d6cd7SAndrew Gallatin mxge_free_rings(sc);
47921e413cf9SAndrew Gallatin abort_with_slices:
47931e413cf9SAndrew Gallatin mxge_free_slices(sc);
4794a98d6cd7SAndrew Gallatin abort_with_dmabench:
4795a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma);
4796b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
47976d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma);
4798b2fc195eSAndrew Gallatin abort_with_cmd_dma:
47996d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma);
4800b2fc195eSAndrew Gallatin abort_with_mem_res:
4801b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4802b2fc195eSAndrew Gallatin abort_with_lock:
4803b2fc195eSAndrew Gallatin pci_disable_busmaster(dev);
4804a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx);
4805a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx);
4806b2fc195eSAndrew Gallatin if_free(ifp);
4807b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat);
480872c042dfSAndrew Gallatin abort_with_tq:
480972c042dfSAndrew Gallatin if (sc->tq != NULL) {
481072c042dfSAndrew Gallatin taskqueue_drain(sc->tq, &sc->watchdog_task);
481172c042dfSAndrew Gallatin taskqueue_free(sc->tq);
481272c042dfSAndrew Gallatin sc->tq = NULL;
481372c042dfSAndrew Gallatin }
4814b2fc195eSAndrew Gallatin return err;
4815b2fc195eSAndrew Gallatin }
4816b2fc195eSAndrew Gallatin
4817b2fc195eSAndrew Gallatin static int
mxge_detach(device_t dev)48186d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4819b2fc195eSAndrew Gallatin {
48206d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev);
4821b2fc195eSAndrew Gallatin
482237d89b0cSAndrew Gallatin if (mxge_vlans_active(sc)) {
4823c792928fSAndrew Gallatin device_printf(sc->dev,
4824c792928fSAndrew Gallatin "Detach vlans before removing module\n");
4825c792928fSAndrew Gallatin return EBUSY;
4826c792928fSAndrew Gallatin }
4827a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx);
48288c5d766cSAndrew Gallatin sc->dying = 1;
482993037a67SJustin Hibbits if (if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING)
4830a393336bSAndrew Gallatin mxge_close(sc, 0);
4831a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx);
4832b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp);
483372c042dfSAndrew Gallatin if (sc->tq != NULL) {
483472c042dfSAndrew Gallatin taskqueue_drain(sc->tq, &sc->watchdog_task);
483572c042dfSAndrew Gallatin taskqueue_free(sc->tq);
483672c042dfSAndrew Gallatin sc->tq = NULL;
483772c042dfSAndrew Gallatin }
4838e749ef6bSAndrew Gallatin callout_drain(&sc->co_hdl);
4839dce01b9bSAndrew Gallatin ifmedia_removeall(&sc->media);
4840091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0);
48411e413cf9SAndrew Gallatin mxge_rem_sysctls(sc);
48421e413cf9SAndrew Gallatin mxge_rem_irq(sc);
4843a98d6cd7SAndrew Gallatin mxge_free_rings(sc);
48441e413cf9SAndrew Gallatin mxge_free_slices(sc);
4845a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma);
48466d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma);
48476d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma);
4848b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4849b2fc195eSAndrew Gallatin pci_disable_busmaster(dev);
4850a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx);
4851a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx);
4852b2fc195eSAndrew Gallatin if_free(sc->ifp);
4853b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat);
4854b2fc195eSAndrew Gallatin return 0;
4855b2fc195eSAndrew Gallatin }
4856b2fc195eSAndrew Gallatin
4857b2fc195eSAndrew Gallatin static int
mxge_shutdown(device_t dev)48586d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4859b2fc195eSAndrew Gallatin {
4860b2fc195eSAndrew Gallatin return 0;
4861b2fc195eSAndrew Gallatin }
4862b2fc195eSAndrew Gallatin
4863b2fc195eSAndrew Gallatin /*
4864b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation.
4865b2fc195eSAndrew Gallatin
4866b2fc195eSAndrew Gallatin Local Variables:
4867b2fc195eSAndrew Gallatin c-file-style:"linux"
4868b2fc195eSAndrew Gallatin tab-width:8
4869b2fc195eSAndrew Gallatin End:
4870b2fc195eSAndrew Gallatin */
4871