xref: /freebsd/sys/dev/mxge/if_mxge.c (revision b08a56ad4608586ce457838655db7e59a126e08d)
16d87a65dSAndrew Gallatin /******************************************************************************
2718cf2ccSPedro F. Giffuni SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
32b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
33b2fc195eSAndrew Gallatin 
34b2fc195eSAndrew Gallatin #include <sys/param.h>
35b2fc195eSAndrew Gallatin #include <sys/systm.h>
36b2fc195eSAndrew Gallatin #include <sys/linker.h>
37b2fc195eSAndrew Gallatin #include <sys/firmware.h>
38b2fc195eSAndrew Gallatin #include <sys/endian.h>
39b2fc195eSAndrew Gallatin #include <sys/sockio.h>
40b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
41b2fc195eSAndrew Gallatin #include <sys/malloc.h>
42b2fc195eSAndrew Gallatin #include <sys/kdb.h>
43b2fc195eSAndrew Gallatin #include <sys/kernel.h>
444e7f640dSJohn Baldwin #include <sys/lock.h>
45b2fc195eSAndrew Gallatin #include <sys/module.h>
46b2fc195eSAndrew Gallatin #include <sys/socket.h>
47b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
48b2fc195eSAndrew Gallatin #include <sys/sx.h>
4972c042dfSAndrew Gallatin #include <sys/taskqueue.h>
501dbf944aSXin LI #include <contrib/zlib/zlib.h>
511dbf944aSXin LI #include <dev/zlib/zcalloc.h>
52b2fc195eSAndrew Gallatin 
53b2fc195eSAndrew Gallatin #include <net/if.h>
5476039bc8SGleb Smirnoff #include <net/if_var.h>
55b2fc195eSAndrew Gallatin #include <net/if_arp.h>
56b2fc195eSAndrew Gallatin #include <net/ethernet.h>
57b2fc195eSAndrew Gallatin #include <net/if_dl.h>
58b2fc195eSAndrew Gallatin #include <net/if_media.h>
59b2fc195eSAndrew Gallatin 
60b2fc195eSAndrew Gallatin #include <net/bpf.h>
61b2fc195eSAndrew Gallatin 
62b2fc195eSAndrew Gallatin #include <net/if_types.h>
63b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
64b2fc195eSAndrew Gallatin 
65b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
66b2fc195eSAndrew Gallatin #include <netinet/in.h>
67b2fc195eSAndrew Gallatin #include <netinet/ip.h>
680a7a780eSAndrew Gallatin #include <netinet/ip6.h>
69aed8e389SAndrew Gallatin #include <netinet/tcp.h>
7026dd49c6SAndrew Gallatin #include <netinet/tcp_lro.h>
710a7a780eSAndrew Gallatin #include <netinet6/ip6_var.h>
72b2fc195eSAndrew Gallatin 
73b2fc195eSAndrew Gallatin #include <machine/bus.h>
74053e637fSAndrew Gallatin #include <machine/in_cksum.h>
75b2fc195eSAndrew Gallatin #include <machine/resource.h>
76b2fc195eSAndrew Gallatin #include <sys/bus.h>
77b2fc195eSAndrew Gallatin #include <sys/rman.h>
781e413cf9SAndrew Gallatin #include <sys/smp.h>
79b2fc195eSAndrew Gallatin 
80b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
81b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
82e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
83b2fc195eSAndrew Gallatin 
84b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
85b2fc195eSAndrew Gallatin #include <vm/pmap.h>
86b2fc195eSAndrew Gallatin 
87c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
88c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
89c2c14a69SAndrew Gallatin #endif
90c2c14a69SAndrew Gallatin 
916d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
926d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
931e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
946d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
95869c7348SAndrew Gallatin #ifdef IFNET_BUF_RING
96869c7348SAndrew Gallatin #include <sys/buf_ring.h>
97869c7348SAndrew Gallatin #endif
98b2fc195eSAndrew Gallatin 
99eb6219e3SAndrew Gallatin #include "opt_inet.h"
1000a7a780eSAndrew Gallatin #include "opt_inet6.h"
101eb6219e3SAndrew Gallatin 
102b2fc195eSAndrew Gallatin /* tunable params */
1036d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
104d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
1056d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1065e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1076d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1085e7d8541SAndrew Gallatin static int mxge_verbose = 0;
109dce01b9bSAndrew Gallatin static int mxge_ticks;
1101e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1115769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
1121e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
113f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
11465c69066SAndrew Gallatin static int mxge_throttle = 0;
1156d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1166d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1171e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1181e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
119b2fc195eSAndrew Gallatin 
1206d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1216d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1226d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1236d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1246d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
125b2fc195eSAndrew Gallatin 
1266d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
127b2fc195eSAndrew Gallatin {
128b2fc195eSAndrew Gallatin   /* Device interface */
1296d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1306d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1316d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1326d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
13361bfd867SSofian Brabez 
13461bfd867SSofian Brabez   DEVMETHOD_END
135b2fc195eSAndrew Gallatin };
136b2fc195eSAndrew Gallatin 
1376d87a65dSAndrew Gallatin static driver_t mxge_driver =
138b2fc195eSAndrew Gallatin {
1396d87a65dSAndrew Gallatin   "mxge",
1406d87a65dSAndrew Gallatin   mxge_methods,
1416d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
142b2fc195eSAndrew Gallatin };
143b2fc195eSAndrew Gallatin 
1446d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
145b2fc195eSAndrew Gallatin 
146b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1476d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1486d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
149f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
150b2fc195eSAndrew Gallatin 
1511e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1528fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
153a393336bSAndrew Gallatin static int mxge_close(mxge_softc_t *sc, int down);
154276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
155276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1568fe615baSAndrew Gallatin 
157b2fc195eSAndrew Gallatin static int
1586d87a65dSAndrew Gallatin mxge_probe(device_t dev)
159b2fc195eSAndrew Gallatin {
16001638550SAndrew Gallatin 	int rev;
16101638550SAndrew Gallatin 
1626d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
163f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
164f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
16501638550SAndrew Gallatin 		rev = pci_get_revid(dev);
16601638550SAndrew Gallatin 		switch (rev) {
16701638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
168b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
16901638550SAndrew Gallatin 			break;
17001638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
17101638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
17201638550SAndrew Gallatin 			break;
17301638550SAndrew Gallatin 		default:
17401638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
17501638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
17601638550SAndrew Gallatin 				      rev);
17701638550SAndrew Gallatin 			break;
17801638550SAndrew Gallatin 		}
179b2fc195eSAndrew Gallatin 		return 0;
180b2fc195eSAndrew Gallatin 	}
181b2fc195eSAndrew Gallatin 	return ENXIO;
182b2fc195eSAndrew Gallatin }
183b2fc195eSAndrew Gallatin 
184b2fc195eSAndrew Gallatin static void
1856d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
186b2fc195eSAndrew Gallatin {
187f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
188b2fc195eSAndrew Gallatin 	vm_offset_t len;
18947c2e987SAndrew Gallatin 	int err;
190b2fc195eSAndrew Gallatin 
1914d69a9d0SAndrew Gallatin 	sc->wc = 1;
192b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
193c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
194c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
19547c2e987SAndrew Gallatin 	if (err != 0) {
196c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
197c2c14a69SAndrew Gallatin 			      err);
1984d69a9d0SAndrew Gallatin 		sc->wc = 0;
199b2fc195eSAndrew Gallatin 	}
200f9ae0280SAndrew Gallatin #endif
201b2fc195eSAndrew Gallatin }
202b2fc195eSAndrew Gallatin 
203b2fc195eSAndrew Gallatin /* callback to get our DMA address */
204b2fc195eSAndrew Gallatin static void
2056d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
206b2fc195eSAndrew Gallatin 			 int error)
207b2fc195eSAndrew Gallatin {
208b2fc195eSAndrew Gallatin 	if (error == 0) {
209b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
210b2fc195eSAndrew Gallatin 	}
211b2fc195eSAndrew Gallatin }
212b2fc195eSAndrew Gallatin 
213b2fc195eSAndrew Gallatin static int
2146d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
215b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
216b2fc195eSAndrew Gallatin {
217b2fc195eSAndrew Gallatin 	int err;
218b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2191e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2201e413cf9SAndrew Gallatin 
2211e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2221e413cf9SAndrew Gallatin 		boundary = 0;
2231e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2241e413cf9SAndrew Gallatin 	} else {
2251e413cf9SAndrew Gallatin 		boundary = 4096;
2261e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2271e413cf9SAndrew Gallatin 	}
228b2fc195eSAndrew Gallatin 
229b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
230b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
231b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2321e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
233b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
234b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
235b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
236b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
237b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2381e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
239b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
240b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
241b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
242b2fc195eSAndrew Gallatin 	if (err != 0) {
243b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
244b2fc195eSAndrew Gallatin 		return err;
245b2fc195eSAndrew Gallatin 	}
246b2fc195eSAndrew Gallatin 
247b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
248b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
249b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
250b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
251b2fc195eSAndrew Gallatin 	if (err != 0) {
252b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
253b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
254b2fc195eSAndrew Gallatin 	}
255b2fc195eSAndrew Gallatin 
256b2fc195eSAndrew Gallatin 	/* load the memory */
257b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2586d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
259b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
260b2fc195eSAndrew Gallatin 	if (err != 0) {
261b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
262b2fc195eSAndrew Gallatin 		goto abort_with_mem;
263b2fc195eSAndrew Gallatin 	}
264b2fc195eSAndrew Gallatin 	return 0;
265b2fc195eSAndrew Gallatin 
266b2fc195eSAndrew Gallatin abort_with_mem:
267b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
268b2fc195eSAndrew Gallatin abort_with_dmat:
269b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
270b2fc195eSAndrew Gallatin 	return err;
271b2fc195eSAndrew Gallatin }
272b2fc195eSAndrew Gallatin 
273b2fc195eSAndrew Gallatin static void
2746d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
275b2fc195eSAndrew Gallatin {
276b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
277b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
278b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
279b2fc195eSAndrew Gallatin }
280b2fc195eSAndrew Gallatin 
281b2fc195eSAndrew Gallatin /*
282b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
283b2fc195eSAndrew Gallatin  * SN=x\0
284b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
285b2fc195eSAndrew Gallatin  * PC=text\0
286b2fc195eSAndrew Gallatin  */
287b2fc195eSAndrew Gallatin 
288b2fc195eSAndrew Gallatin static int
2896d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
290b2fc195eSAndrew Gallatin {
291dedbe836SAndrew Gallatin 	char *ptr;
292a4b233ddSAndrew Gallatin 	int i, found_mac, found_sn2;
293dedbe836SAndrew Gallatin 	char *endptr;
294b2fc195eSAndrew Gallatin 
295b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
296b2fc195eSAndrew Gallatin 	found_mac = 0;
297a4b233ddSAndrew Gallatin 	found_sn2 = 0;
298dedbe836SAndrew Gallatin 	while (*ptr != '\0') {
299dedbe836SAndrew Gallatin 		if (strncmp(ptr, "MAC=", 4) == 0) {
300dedbe836SAndrew Gallatin 			ptr += 4;
301dedbe836SAndrew Gallatin 			for (i = 0;;) {
302dedbe836SAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
303dedbe836SAndrew Gallatin 				if (endptr - ptr != 2)
304b2fc195eSAndrew Gallatin 					goto abort;
305dedbe836SAndrew Gallatin 				ptr = endptr;
306dedbe836SAndrew Gallatin 				if (++i == 6)
307dedbe836SAndrew Gallatin 					break;
308dedbe836SAndrew Gallatin 				if (*ptr++ != ':')
309dedbe836SAndrew Gallatin 					goto abort;
310b2fc195eSAndrew Gallatin 			}
311dedbe836SAndrew Gallatin 			found_mac = 1;
312dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "PC=", 3) == 0) {
3135e7d8541SAndrew Gallatin 			ptr += 3;
314dedbe836SAndrew Gallatin 			strlcpy(sc->product_code_string, ptr,
315dedbe836SAndrew Gallatin 			    sizeof(sc->product_code_string));
316dedbe836SAndrew Gallatin 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
3175e7d8541SAndrew Gallatin 			ptr += 3;
318dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
319dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
320dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
321a4b233ddSAndrew Gallatin 			/* SN2 takes precedence over SN */
322a4b233ddSAndrew Gallatin 			ptr += 4;
323a4b233ddSAndrew Gallatin 			found_sn2 = 1;
324dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
325dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
326b2fc195eSAndrew Gallatin 		}
327dedbe836SAndrew Gallatin 		while (*ptr++ != '\0') {}
328b2fc195eSAndrew Gallatin 	}
329b2fc195eSAndrew Gallatin 
330b2fc195eSAndrew Gallatin 	if (found_mac)
331b2fc195eSAndrew Gallatin 		return 0;
332b2fc195eSAndrew Gallatin 
333b2fc195eSAndrew Gallatin  abort:
334b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
335b2fc195eSAndrew Gallatin 
336b2fc195eSAndrew Gallatin 	return ENXIO;
337b2fc195eSAndrew Gallatin }
338b2fc195eSAndrew Gallatin 
3390d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3408fe615baSAndrew Gallatin static void
3418fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
342b2fc195eSAndrew Gallatin {
343b2fc195eSAndrew Gallatin 	uint32_t val;
3448fe615baSAndrew Gallatin 	unsigned long base, off;
345b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3468fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3478fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
348b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
349b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
350b2fc195eSAndrew Gallatin 
3518fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3528fe615baSAndrew Gallatin 		return;
3538fe615baSAndrew Gallatin 
3548fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3558fe615baSAndrew Gallatin 	if (pdev == NULL) {
3568fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3578fe615baSAndrew Gallatin 		return;
3588fe615baSAndrew Gallatin 	}
3598fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3608fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3618fe615baSAndrew Gallatin 
3628fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3638fe615baSAndrew Gallatin 		return;
3648fe615baSAndrew Gallatin 
3658fe615baSAndrew Gallatin 	base = 0;
3668fe615baSAndrew Gallatin 
3678fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3688fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3698fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3708fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3718fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3728fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3738fe615baSAndrew Gallatin 		if (mcp55 &&
3748fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3758fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3768fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3778fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3788fe615baSAndrew Gallatin 		}
3798fe615baSAndrew Gallatin 	}
3808fe615baSAndrew Gallatin 	if (!base)
3818fe615baSAndrew Gallatin 		return;
3828fe615baSAndrew Gallatin 
383b2fc195eSAndrew Gallatin 	/* XXXX
384b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
385b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
386b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
387b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
388b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
389b2fc195eSAndrew Gallatin 	*/
390b2fc195eSAndrew Gallatin #if 0
391b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
392b2fc195eSAndrew Gallatin 	   config space */
393b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
394b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
395b2fc195eSAndrew Gallatin 		val |= 0x40;
396b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3978fe615baSAndrew Gallatin 		return;
398b2fc195eSAndrew Gallatin 	}
399b2fc195eSAndrew Gallatin #endif
400b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
401b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
402b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
403b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
404b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
405b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
406b2fc195eSAndrew Gallatin 	 */
407b2fc195eSAndrew Gallatin 
408b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
409b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
410b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
411b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
412b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
413b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
414b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
415b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
416b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
417b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
418b2fc195eSAndrew Gallatin 
4198fe615baSAndrew Gallatin 	off =  base
420b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
421b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
422b2fc195eSAndrew Gallatin 						 + 8 * slot);
423b2fc195eSAndrew Gallatin 
424b2fc195eSAndrew Gallatin 	/* map it into the kernel */
425b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
426b2fc195eSAndrew Gallatin 
427b2fc195eSAndrew Gallatin 	if (va == NULL) {
428b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4298fe615baSAndrew Gallatin 		return;
430b2fc195eSAndrew Gallatin 	}
431b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
432b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
433b2fc195eSAndrew Gallatin 
434b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
435b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
436b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
437b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
438b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
439b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
440b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4418fe615baSAndrew Gallatin 		return;
442b2fc195eSAndrew Gallatin 	}
443b2fc195eSAndrew Gallatin 
444b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
445b2fc195eSAndrew Gallatin 	val = *ptr32;
446b2fc195eSAndrew Gallatin 
447b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
448b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
449b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4508fe615baSAndrew Gallatin 		return;
451b2fc195eSAndrew Gallatin 	}
452b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
453b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4545e7d8541SAndrew Gallatin 	if (mxge_verbose)
455b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4565e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4575e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
458b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4598fe615baSAndrew Gallatin 	return;
460b2fc195eSAndrew Gallatin }
461b2fc195eSAndrew Gallatin #else
4628fe615baSAndrew Gallatin static void
463f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
464b2fc195eSAndrew Gallatin {
465b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
466b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4678fe615baSAndrew Gallatin 	return;
468b2fc195eSAndrew Gallatin }
469b2fc195eSAndrew Gallatin #endif
4708fe615baSAndrew Gallatin 
4718fe615baSAndrew Gallatin static int
4728fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4738fe615baSAndrew Gallatin {
4748fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4758fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4768fe615baSAndrew Gallatin 	int status;
4778fe615baSAndrew Gallatin 	uint32_t len;
4788fe615baSAndrew Gallatin 	char *test = " ";
4798fe615baSAndrew Gallatin 
4808fe615baSAndrew Gallatin 	/* Run a small DMA test.
4818fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4828fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4838fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4848fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4858fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4868fe615baSAndrew Gallatin 	 * transfers took to complete.
4878fe615baSAndrew Gallatin 	 */
4888fe615baSAndrew Gallatin 
4891e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4908fe615baSAndrew Gallatin 
4918fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4928fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4938fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4948fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4958fe615baSAndrew Gallatin 	if (status != 0) {
4968fe615baSAndrew Gallatin 		test = "read";
4978fe615baSAndrew Gallatin 		goto abort;
4988fe615baSAndrew Gallatin 	}
4998fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
5008fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5018fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5028fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5038fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
5048fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5058fe615baSAndrew Gallatin 	if (status != 0) {
5068fe615baSAndrew Gallatin 		test = "write";
5078fe615baSAndrew Gallatin 		goto abort;
5088fe615baSAndrew Gallatin 	}
5098fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5108fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5118fe615baSAndrew Gallatin 
5128fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5138fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5148fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5158fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5168fe615baSAndrew Gallatin 	if (status != 0) {
5178fe615baSAndrew Gallatin 		test = "read/write";
5188fe615baSAndrew Gallatin 		goto abort;
5198fe615baSAndrew Gallatin 	}
5208fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5218fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5228fe615baSAndrew Gallatin 
5238fe615baSAndrew Gallatin abort:
5248fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5258fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5268fe615baSAndrew Gallatin 			      test, status);
5278fe615baSAndrew Gallatin 
5288fe615baSAndrew Gallatin 	return status;
5298fe615baSAndrew Gallatin }
5308fe615baSAndrew Gallatin 
531b2fc195eSAndrew Gallatin /*
532b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
533b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
534b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
535b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
536b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
537b2fc195eSAndrew Gallatin  *
538b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
539b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
540b2fc195eSAndrew Gallatin  *
541b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
542b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
543b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
544b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5451e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
546b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5471e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
548b2fc195eSAndrew Gallatin  */
549b2fc195eSAndrew Gallatin 
5508fe615baSAndrew Gallatin static int
5518fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5528fe615baSAndrew Gallatin {
5538fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5548fe615baSAndrew Gallatin 	int reg, status;
5558fe615baSAndrew Gallatin 	uint16_t pectl;
5568fe615baSAndrew Gallatin 
5571e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5588fe615baSAndrew Gallatin 	/*
5598fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5608fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5618fe615baSAndrew Gallatin 	 */
5623b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
5638fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5648fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5658fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5668fe615baSAndrew Gallatin 				      pectl);
5671e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5688fe615baSAndrew Gallatin 		}
5698fe615baSAndrew Gallatin 	}
5708fe615baSAndrew Gallatin 
5718fe615baSAndrew Gallatin 	/*
5728fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5738fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5748fe615baSAndrew Gallatin 	 */
5758fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5761e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5778fe615baSAndrew Gallatin 	if (status != 0) {
5788fe615baSAndrew Gallatin 		return status;
5798fe615baSAndrew Gallatin 	}
5808fe615baSAndrew Gallatin 
5818fe615baSAndrew Gallatin 	/*
5828fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5838fe615baSAndrew Gallatin 	 */
5848fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5858fe615baSAndrew Gallatin 
5868fe615baSAndrew Gallatin 	/*
5878fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
588a4b233ddSAndrew Gallatin 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5898fe615baSAndrew Gallatin 	 */
590a4b233ddSAndrew Gallatin 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
591a4b233ddSAndrew Gallatin 		return 0;
5928fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5938fe615baSAndrew Gallatin 	if (status == 0)
5948fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5958fe615baSAndrew Gallatin 
5968fe615baSAndrew Gallatin 	if (status != E2BIG)
5978fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5988fe615baSAndrew Gallatin 	if (status == ENOSYS)
5998fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
6008fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
6018fe615baSAndrew Gallatin 	return status;
6028fe615baSAndrew Gallatin }
6038fe615baSAndrew Gallatin 
6048fe615baSAndrew Gallatin static int
6056d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
606b2fc195eSAndrew Gallatin {
6078fe615baSAndrew Gallatin 	int aligned = 0;
60865c69066SAndrew Gallatin 	int force_firmware = mxge_force_firmware;
609b2fc195eSAndrew Gallatin 
61065c69066SAndrew Gallatin 	if (sc->throttle)
61165c69066SAndrew Gallatin 		force_firmware = sc->throttle;
612d91b1b49SAndrew Gallatin 
61365c69066SAndrew Gallatin 	if (force_firmware != 0) {
61465c69066SAndrew Gallatin 		if (force_firmware == 1)
615d91b1b49SAndrew Gallatin 			aligned = 1;
616d91b1b49SAndrew Gallatin 		else
617d91b1b49SAndrew Gallatin 			aligned = 0;
618d91b1b49SAndrew Gallatin 		if (mxge_verbose)
619d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
620d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
621d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
622d91b1b49SAndrew Gallatin 		goto abort;
623d91b1b49SAndrew Gallatin 	}
624d91b1b49SAndrew Gallatin 
625d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
626d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
627d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
628d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
629d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
630d91b1b49SAndrew Gallatin 			      sc->link_width);
631d91b1b49SAndrew Gallatin 		aligned = 1;
632d91b1b49SAndrew Gallatin 		goto abort;
633d91b1b49SAndrew Gallatin 	}
634d91b1b49SAndrew Gallatin 
6358fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6368fe615baSAndrew Gallatin 		return 0;
637b2fc195eSAndrew Gallatin 
638b2fc195eSAndrew Gallatin abort:
639b2fc195eSAndrew Gallatin 	if (aligned) {
6406d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6411e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
642b2fc195eSAndrew Gallatin 	} else {
6436d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6441e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
645b2fc195eSAndrew Gallatin 	}
6461e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
647b2fc195eSAndrew Gallatin }
648b2fc195eSAndrew Gallatin 
6494da0d523SAndrew Gallatin static int
6504da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6514da0d523SAndrew Gallatin {
652b824b7d8SAndrew Gallatin 
6534da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6544da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6554da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6564da0d523SAndrew Gallatin 		return EIO;
6574da0d523SAndrew Gallatin 	}
6584da0d523SAndrew Gallatin 
6594da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
660dedbe836SAndrew Gallatin 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6614da0d523SAndrew Gallatin 	if (mxge_verbose)
6624da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6634da0d523SAndrew Gallatin 
664b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
665b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6664da0d523SAndrew Gallatin 
667b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
668b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6694da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6704da0d523SAndrew Gallatin 			      sc->fw_version);
6714da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6724da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6734da0d523SAndrew Gallatin 		return EINVAL;
6744da0d523SAndrew Gallatin 	}
6754da0d523SAndrew Gallatin 	return 0;
6764da0d523SAndrew Gallatin 
6774da0d523SAndrew Gallatin }
678b2fc195eSAndrew Gallatin 
679b2fc195eSAndrew Gallatin static int
6806d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
681b2fc195eSAndrew Gallatin {
682f9ae0280SAndrew Gallatin 	z_stream zs;
683f9ae0280SAndrew Gallatin 	char *inflate_buffer;
68433d54970SLuigi Rizzo 	const struct firmware *fw;
685b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
686b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
687b2fc195eSAndrew Gallatin 	int status;
6884da0d523SAndrew Gallatin 	unsigned int i;
689f9ae0280SAndrew Gallatin 	size_t fw_len;
690b2fc195eSAndrew Gallatin 
691b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
692b2fc195eSAndrew Gallatin 	if (fw == NULL) {
693b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
694b2fc195eSAndrew Gallatin 			      sc->fw_name);
695b2fc195eSAndrew Gallatin 		return ENOENT;
696b2fc195eSAndrew Gallatin 	}
697b2fc195eSAndrew Gallatin 
698f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
699f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
7001dbf944aSXin LI 	zs.zalloc = zcalloc_nowait;
7011dbf944aSXin LI 	zs.zfree = zcfree;
702f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
703f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
704b2fc195eSAndrew Gallatin 		status = EIO;
705b2fc195eSAndrew Gallatin 		goto abort_with_fw;
706b2fc195eSAndrew Gallatin 	}
707f9ae0280SAndrew Gallatin 
708f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
709f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
710f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
711f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
712f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
713f9ae0280SAndrew Gallatin 		goto abort_with_zs;
714f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
715f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
716f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
717f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
718f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
719f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
720f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
721f9ae0280SAndrew Gallatin 		status = EIO;
722f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
723f9ae0280SAndrew Gallatin 	}
724f9ae0280SAndrew Gallatin 
725f9ae0280SAndrew Gallatin 	/* check id */
726f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
727f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
728f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
729f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
730f9ae0280SAndrew Gallatin 		status = EIO;
731f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
732f9ae0280SAndrew Gallatin 	}
733f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
734b2fc195eSAndrew Gallatin 
7354da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7364da0d523SAndrew Gallatin 	if (status != 0)
737f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
738b2fc195eSAndrew Gallatin 
739b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
740f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7414da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
742f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
743f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
74473c7c83fSAndrew Gallatin 		wmb();
745*b08a56adSJohn Baldwin 		(void)*sc->sram;
74673c7c83fSAndrew Gallatin 		wmb();
7474da0d523SAndrew Gallatin 	}
748b2fc195eSAndrew Gallatin 
749f9ae0280SAndrew Gallatin 	*limit = fw_len;
750b2fc195eSAndrew Gallatin 	status = 0;
751f9ae0280SAndrew Gallatin abort_with_buffer:
752f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
753f9ae0280SAndrew Gallatin abort_with_zs:
754f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
755b2fc195eSAndrew Gallatin abort_with_fw:
756b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
757b2fc195eSAndrew Gallatin 	return status;
758b2fc195eSAndrew Gallatin }
759b2fc195eSAndrew Gallatin 
760b2fc195eSAndrew Gallatin /*
761b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
762b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
763b2fc195eSAndrew Gallatin  */
764b2fc195eSAndrew Gallatin 
765b2fc195eSAndrew Gallatin static void
7666d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
767b2fc195eSAndrew Gallatin {
768b2fc195eSAndrew Gallatin 	char buf_bytes[72];
769b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
770b2fc195eSAndrew Gallatin 	volatile char *submit;
771b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
772b2fc195eSAndrew Gallatin 	int i;
773b2fc195eSAndrew Gallatin 
774aa54c242SJohn Baldwin 	buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
775b2fc195eSAndrew Gallatin 
776b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
777b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
778b2fc195eSAndrew Gallatin 	*confirm = 0;
77973c7c83fSAndrew Gallatin 	wmb();
780b2fc195eSAndrew Gallatin 
781b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
782b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
783b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
784b2fc195eSAndrew Gallatin 	*/
785b2fc195eSAndrew Gallatin 
7866d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7876d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
788b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
789b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
790b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7916d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7926d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
793b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
794b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
795b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
796b2fc195eSAndrew Gallatin 
7970fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
798b2fc195eSAndrew Gallatin 
7996d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
80073c7c83fSAndrew Gallatin 	wmb();
801b2fc195eSAndrew Gallatin 	DELAY(1000);
80273c7c83fSAndrew Gallatin 	wmb();
803b2fc195eSAndrew Gallatin 	i = 0;
804b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
805b2fc195eSAndrew Gallatin 		DELAY(1000);
806b2fc195eSAndrew Gallatin 		i++;
807b2fc195eSAndrew Gallatin 	}
808b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
809b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
810b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
811b2fc195eSAndrew Gallatin 			      *confirm);
812b2fc195eSAndrew Gallatin 	}
813b2fc195eSAndrew Gallatin 	return;
814b2fc195eSAndrew Gallatin }
815b2fc195eSAndrew Gallatin 
816b2fc195eSAndrew Gallatin static int
8176d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
818b2fc195eSAndrew Gallatin {
819b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
820b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
821b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8220fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
823b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
824e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
825b2fc195eSAndrew Gallatin 
826b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
827aa54c242SJohn Baldwin 	buf = (mcp_cmd_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
828b2fc195eSAndrew Gallatin 
829b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
830b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
831b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
832b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8336d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8346d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
835b2fc195eSAndrew Gallatin 
836b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
837b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
838a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
839b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
84073c7c83fSAndrew Gallatin 	wmb();
8416d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
842b2fc195eSAndrew Gallatin 
8435e7d8541SAndrew Gallatin 	/* wait up to 20ms */
844e0501fd0SAndrew Gallatin 	err = EAGAIN;
8455e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
846b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
847b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
84873c7c83fSAndrew Gallatin 		wmb();
849e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
850e0501fd0SAndrew Gallatin 		case 0:
851b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
852e0501fd0SAndrew Gallatin 			err = 0;
853e0501fd0SAndrew Gallatin 			break;
854e0501fd0SAndrew Gallatin 		case 0xffffffff:
855e0501fd0SAndrew Gallatin 			DELAY(1000);
856e0501fd0SAndrew Gallatin 			break;
857e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
858e0501fd0SAndrew Gallatin 			err = ENOSYS;
859e0501fd0SAndrew Gallatin 			break;
860e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
861e0501fd0SAndrew Gallatin 			err = E2BIG;
862e0501fd0SAndrew Gallatin 			break;
863c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
864c587e59fSAndrew Gallatin 			err = EBUSY;
865c587e59fSAndrew Gallatin 			break;
866c406ad2eSAndrew Gallatin 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
867c406ad2eSAndrew Gallatin 			err = ENXIO;
868c406ad2eSAndrew Gallatin 			break;
869e0501fd0SAndrew Gallatin 		default:
870b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8716d87a65dSAndrew Gallatin 				      "mxge: command %d "
872b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
873b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
874e0501fd0SAndrew Gallatin 			err = ENXIO;
875e0501fd0SAndrew Gallatin 			break;
876b2fc195eSAndrew Gallatin 		}
877e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
878e0501fd0SAndrew Gallatin 			break;
879b2fc195eSAndrew Gallatin 	}
880e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8816d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
882b2fc195eSAndrew Gallatin 			      "result = %d\n",
883b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
884e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
885e0501fd0SAndrew Gallatin 	return err;
886b2fc195eSAndrew Gallatin }
887b2fc195eSAndrew Gallatin 
8884da0d523SAndrew Gallatin static int
8894da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8904da0d523SAndrew Gallatin {
8914da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8924da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8934da0d523SAndrew Gallatin 	size_t hdr_offset;
8944da0d523SAndrew Gallatin 	int status;
8954da0d523SAndrew Gallatin 
8964da0d523SAndrew Gallatin 	/* find running firmware header */
8974da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
8984da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
8994da0d523SAndrew Gallatin 
9004da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9014da0d523SAndrew Gallatin 		device_printf(sc->dev,
9024da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9034da0d523SAndrew Gallatin 			      (int)hdr_offset);
9044da0d523SAndrew Gallatin 		return EIO;
9054da0d523SAndrew Gallatin 	}
9064da0d523SAndrew Gallatin 
9074da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9084da0d523SAndrew Gallatin 	 * validate firmware */
9094da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9104da0d523SAndrew Gallatin 	if (hdr == NULL) {
9114da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9124da0d523SAndrew Gallatin 		return ENOMEM;
9134da0d523SAndrew Gallatin 	}
9144da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9154da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9164da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9174da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9184da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
919b824b7d8SAndrew Gallatin 
920b824b7d8SAndrew Gallatin 	/*
921b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
922b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
923b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
924b824b7d8SAndrew Gallatin 	 */
925b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
926b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
927b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
928b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
929b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
930b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
931b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
932b824b7d8SAndrew Gallatin 	}
933b824b7d8SAndrew Gallatin 
9344da0d523SAndrew Gallatin 	return status;
9354da0d523SAndrew Gallatin }
9364da0d523SAndrew Gallatin 
937b2fc195eSAndrew Gallatin static int
9381e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
939b2fc195eSAndrew Gallatin {
940b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
941b2fc195eSAndrew Gallatin 	volatile char *submit;
942b2fc195eSAndrew Gallatin 	char buf_bytes[72];
943b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
944b2fc195eSAndrew Gallatin 	int status, i;
945b2fc195eSAndrew Gallatin 
946aa54c242SJohn Baldwin 	buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
947b2fc195eSAndrew Gallatin 
948b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9496d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
950b2fc195eSAndrew Gallatin 	if (status) {
9511e413cf9SAndrew Gallatin 		if (!adopt)
9521e413cf9SAndrew Gallatin 			return status;
9534da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9544da0d523SAndrew Gallatin 		   it is new enough */
9554da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9564da0d523SAndrew Gallatin 		if (status) {
9574da0d523SAndrew Gallatin 			device_printf(sc->dev,
9584da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
959b2fc195eSAndrew Gallatin 			return status;
960b2fc195eSAndrew Gallatin 		}
9614da0d523SAndrew Gallatin 		device_printf(sc->dev,
9624da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9631e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9644da0d523SAndrew Gallatin 			device_printf(sc->dev,
9654da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9664da0d523SAndrew Gallatin 				 ".  For optimal\n");
9674da0d523SAndrew Gallatin 			device_printf(sc->dev,
9684da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9694da0d523SAndrew Gallatin 				 "firmware\n");
9704da0d523SAndrew Gallatin 		}
971d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9721e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
973d91b1b49SAndrew Gallatin 		return 0;
9744da0d523SAndrew Gallatin 	}
975b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
976b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
977b2fc195eSAndrew Gallatin 	*confirm = 0;
97873c7c83fSAndrew Gallatin 	wmb();
979b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
980b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
981b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
982b2fc195eSAndrew Gallatin 	*/
983b2fc195eSAndrew Gallatin 
9846d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9856d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
986b2fc195eSAndrew Gallatin 
987b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
988b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
989b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
990b2fc195eSAndrew Gallatin 
991b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
992b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
993b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
994b2fc195eSAndrew Gallatin 	*/
995b2fc195eSAndrew Gallatin 					/* where the code starts*/
9966d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
997b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
998b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
999b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1000b2fc195eSAndrew Gallatin 
10010fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10026d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
100373c7c83fSAndrew Gallatin 	wmb();
1004b2fc195eSAndrew Gallatin 	DELAY(1000);
100573c7c83fSAndrew Gallatin 	wmb();
1006b2fc195eSAndrew Gallatin 	i = 0;
1007b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1008b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1009b2fc195eSAndrew Gallatin 		i++;
1010b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1011b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1012b2fc195eSAndrew Gallatin 	}
1013b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1014b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1015b2fc195eSAndrew Gallatin 			confirm, *confirm);
1016b2fc195eSAndrew Gallatin 
1017b2fc195eSAndrew Gallatin 		return ENXIO;
1018b2fc195eSAndrew Gallatin 	}
1019b2fc195eSAndrew Gallatin 	return 0;
1020b2fc195eSAndrew Gallatin }
1021b2fc195eSAndrew Gallatin 
1022b2fc195eSAndrew Gallatin static int
10236d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1024b2fc195eSAndrew Gallatin {
10256d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1026b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1027b2fc195eSAndrew Gallatin 	int status;
1028b2fc195eSAndrew Gallatin 
1029b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1030b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1031b2fc195eSAndrew Gallatin 
1032b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1033b2fc195eSAndrew Gallatin 
10345e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1035b2fc195eSAndrew Gallatin 	return status;
1036b2fc195eSAndrew Gallatin }
1037b2fc195eSAndrew Gallatin 
1038b2fc195eSAndrew Gallatin static int
10396d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1040b2fc195eSAndrew Gallatin {
10416d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1042b2fc195eSAndrew Gallatin 	int status;
1043b2fc195eSAndrew Gallatin 
1044b2fc195eSAndrew Gallatin 	if (pause)
10455e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1046b2fc195eSAndrew Gallatin 				       &cmd);
1047b2fc195eSAndrew Gallatin 	else
10485e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1049b2fc195eSAndrew Gallatin 				       &cmd);
1050b2fc195eSAndrew Gallatin 
1051b2fc195eSAndrew Gallatin 	if (status) {
1052b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1053b2fc195eSAndrew Gallatin 		return ENXIO;
1054b2fc195eSAndrew Gallatin 	}
1055b2fc195eSAndrew Gallatin 	sc->pause = pause;
1056b2fc195eSAndrew Gallatin 	return 0;
1057b2fc195eSAndrew Gallatin }
1058b2fc195eSAndrew Gallatin 
1059b2fc195eSAndrew Gallatin static void
10606d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1061b2fc195eSAndrew Gallatin {
10626d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1063b2fc195eSAndrew Gallatin 	int status;
1064b2fc195eSAndrew Gallatin 
10651e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10661e413cf9SAndrew Gallatin 		promisc = 1;
10671e413cf9SAndrew Gallatin 
1068b2fc195eSAndrew Gallatin 	if (promisc)
10695e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1070b2fc195eSAndrew Gallatin 				       &cmd);
1071b2fc195eSAndrew Gallatin 	else
10725e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1073b2fc195eSAndrew Gallatin 				       &cmd);
1074b2fc195eSAndrew Gallatin 
1075b2fc195eSAndrew Gallatin 	if (status) {
1076b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1077b2fc195eSAndrew Gallatin 	}
1078b2fc195eSAndrew Gallatin }
1079b2fc195eSAndrew Gallatin 
10802a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx {
10812a0f8518SGleb Smirnoff 	mxge_softc_t *sc;
10822a0f8518SGleb Smirnoff 	int error;
10832a0f8518SGleb Smirnoff };
10842a0f8518SGleb Smirnoff 
10852a0f8518SGleb Smirnoff static u_int
10862a0f8518SGleb Smirnoff mxge_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
10872a0f8518SGleb Smirnoff {
10882a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx *ctx = arg;
10892a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
10902a0f8518SGleb Smirnoff 
10912a0f8518SGleb Smirnoff 	if (ctx->error != 0)
10922a0f8518SGleb Smirnoff 		return (0);
10932a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl), &cmd.data0, 4);
10942a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl) + 4, &cmd.data1, 2);
10952a0f8518SGleb Smirnoff 	cmd.data0 = htonl(cmd.data0);
10962a0f8518SGleb Smirnoff 	cmd.data1 = htonl(cmd.data1);
10972a0f8518SGleb Smirnoff 
10982a0f8518SGleb Smirnoff 	ctx->error = mxge_send_cmd(ctx->sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10992a0f8518SGleb Smirnoff 
11002a0f8518SGleb Smirnoff 	return (1);
11012a0f8518SGleb Smirnoff }
11022a0f8518SGleb Smirnoff 
11030fa7f681SAndrew Gallatin static void
11040fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
11050fa7f681SAndrew Gallatin {
11062a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx ctx;
11070fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
11082a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
11090fa7f681SAndrew Gallatin 	int err;
11100fa7f681SAndrew Gallatin 
11110fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11120fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11130fa7f681SAndrew Gallatin 		return;
11140fa7f681SAndrew Gallatin 
11150fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11160fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11170fa7f681SAndrew Gallatin 	if (err != 0) {
11180fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11190fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11200fa7f681SAndrew Gallatin 		return;
11210fa7f681SAndrew Gallatin 	}
11220fa7f681SAndrew Gallatin 
1123b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1124b824b7d8SAndrew Gallatin 		return;
11250fa7f681SAndrew Gallatin 
11260fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11270fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11280fa7f681SAndrew Gallatin 		return;
11290fa7f681SAndrew Gallatin 
11300fa7f681SAndrew Gallatin 	/* Flush all the filters */
11310fa7f681SAndrew Gallatin 
11320fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11330fa7f681SAndrew Gallatin 	if (err != 0) {
11340fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11350fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11360fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11370fa7f681SAndrew Gallatin 		return;
11380fa7f681SAndrew Gallatin 	}
11390fa7f681SAndrew Gallatin 
11400fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11412a0f8518SGleb Smirnoff 	ctx.sc = sc;
11422a0f8518SGleb Smirnoff 	ctx.error = 0;
11432a0f8518SGleb Smirnoff 	if_foreach_llmaddr(ifp, mxge_add_maddr, &ctx);
11442a0f8518SGleb Smirnoff 	if (ctx.error != 0) {
11452a0f8518SGleb Smirnoff 		device_printf(sc->dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
11462a0f8518SGleb Smirnoff 		    "error status:" "%d\t", ctx.error);
11470fa7f681SAndrew Gallatin 		/* abort, leaving multicast filtering off */
11480fa7f681SAndrew Gallatin 		return;
11490fa7f681SAndrew Gallatin 	}
11502a0f8518SGleb Smirnoff 
11510fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11520fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11530fa7f681SAndrew Gallatin 	if (err != 0) {
11540fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11550fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11560fa7f681SAndrew Gallatin 	}
11570fa7f681SAndrew Gallatin }
11580fa7f681SAndrew Gallatin 
1159b2fc195eSAndrew Gallatin static int
1160053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1161053e637fSAndrew Gallatin {
1162053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1163053e637fSAndrew Gallatin 	int status;
1164053e637fSAndrew Gallatin 
1165c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1166c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1167053e637fSAndrew Gallatin 
1168053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1169053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1170053e637fSAndrew Gallatin 	cmd.data0 = 0;
1171053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1172053e637fSAndrew Gallatin 			       &cmd);
1173053e637fSAndrew Gallatin 	if (status == 0)
1174c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1175053e637fSAndrew Gallatin 
1176053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1177053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1178053e637fSAndrew Gallatin }
1179053e637fSAndrew Gallatin 
1180053e637fSAndrew Gallatin static int
1181adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1182b2fc195eSAndrew Gallatin {
11831e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11841e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11851e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11866d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11871e413cf9SAndrew Gallatin 	int slice, status;
1188b2fc195eSAndrew Gallatin 
1189b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1190b2fc195eSAndrew Gallatin 	   is alive */
1191b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11925e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1193b2fc195eSAndrew Gallatin 	if (status != 0) {
1194b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1195b2fc195eSAndrew Gallatin 		return ENXIO;
1196b2fc195eSAndrew Gallatin 	}
1197b2fc195eSAndrew Gallatin 
1198091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1199091feecdSAndrew Gallatin 
12001e413cf9SAndrew Gallatin 	/* set the intrq size */
12011e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12021e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12031e413cf9SAndrew Gallatin 
12041e413cf9SAndrew Gallatin 	/*
12051e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12061e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12071e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12081e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12091e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12101e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12111e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12121e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12131e413cf9SAndrew Gallatin 	 */
12141e413cf9SAndrew Gallatin 
12151e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12161e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12171e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12181e413cf9SAndrew Gallatin 					   &cmd);
12191e413cf9SAndrew Gallatin 		if (status != 0) {
12201e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12211e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12221e413cf9SAndrew Gallatin 			return status;
12231e413cf9SAndrew Gallatin 		}
12241e413cf9SAndrew Gallatin 		/*
12251e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12261e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12271e413cf9SAndrew Gallatin 		 */
12281e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12291e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1230c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1231c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1232c6cb3e3fSAndrew Gallatin #endif
12331e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12341e413cf9SAndrew Gallatin 					   &cmd);
12351e413cf9SAndrew Gallatin 		if (status != 0) {
12361e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12371e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12381e413cf9SAndrew Gallatin 			return status;
12391e413cf9SAndrew Gallatin 		}
12401e413cf9SAndrew Gallatin 	}
12411e413cf9SAndrew Gallatin 
1242adae7080SAndrew Gallatin 	if (interrupts_setup) {
1243b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12441e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12451e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12461e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12471e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12481e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12491e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12501e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12511e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12521e413cf9SAndrew Gallatin 						&cmd);
12531e413cf9SAndrew Gallatin 		}
1254adae7080SAndrew Gallatin 	}
1255b2fc195eSAndrew Gallatin 
12566d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12575e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12585e7d8541SAndrew Gallatin 
12595e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12605e7d8541SAndrew Gallatin 
12615e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12621e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12635e7d8541SAndrew Gallatin 
12645e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12656d87a65dSAndrew Gallatin 				&cmd);
12665e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1267b2fc195eSAndrew Gallatin 	if (status != 0) {
1268b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1269b2fc195eSAndrew Gallatin 		return status;
1270b2fc195eSAndrew Gallatin 	}
1271b2fc195eSAndrew Gallatin 
12725e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12735e7d8541SAndrew Gallatin 
12745e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12758fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12765e7d8541SAndrew Gallatin 
12771e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12781e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12791e413cf9SAndrew Gallatin 
12801e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1281b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12821e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12831e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12841e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12851e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12861e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1287c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1288c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1289c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
12901e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
12911e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
12921e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
12931e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
12941e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
129526dd49c6SAndrew Gallatin 		ss->lc.lro_bad_csum = 0;
129626dd49c6SAndrew Gallatin 		ss->lc.lro_queued = 0;
129726dd49c6SAndrew Gallatin 		ss->lc.lro_flushed = 0;
12981e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
1299a393336bSAndrew Gallatin 			bzero(ss->fw_stats, sizeof *ss->fw_stats);
13001e413cf9SAndrew Gallatin 		}
13011e413cf9SAndrew Gallatin 	}
1302b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13036d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1304bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13056d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13060fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
130765c69066SAndrew Gallatin 	if (sc->throttle) {
130865c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
130965c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
131065c69066SAndrew Gallatin 				  &cmd)) {
131165c69066SAndrew Gallatin 			device_printf(sc->dev,
131265c69066SAndrew Gallatin 				      "can't enable throttle\n");
131365c69066SAndrew Gallatin 		}
131465c69066SAndrew Gallatin 	}
1315b2fc195eSAndrew Gallatin 	return status;
1316b2fc195eSAndrew Gallatin }
1317b2fc195eSAndrew Gallatin 
1318b2fc195eSAndrew Gallatin static int
131965c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
132065c69066SAndrew Gallatin {
132165c69066SAndrew Gallatin 	mxge_cmd_t cmd;
132265c69066SAndrew Gallatin 	mxge_softc_t *sc;
132365c69066SAndrew Gallatin 	int err;
132465c69066SAndrew Gallatin 	unsigned int throttle;
132565c69066SAndrew Gallatin 
132665c69066SAndrew Gallatin 	sc = arg1;
132765c69066SAndrew Gallatin 	throttle = sc->throttle;
132865c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
132965c69066SAndrew Gallatin 	if (err != 0) {
133065c69066SAndrew Gallatin 		return err;
133165c69066SAndrew Gallatin 	}
133265c69066SAndrew Gallatin 
133365c69066SAndrew Gallatin 	if (throttle == sc->throttle)
133465c69066SAndrew Gallatin 		return 0;
133565c69066SAndrew Gallatin 
133665c69066SAndrew Gallatin 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
133765c69066SAndrew Gallatin 		return EINVAL;
133865c69066SAndrew Gallatin 
133965c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
134065c69066SAndrew Gallatin 	cmd.data0 = throttle;
134165c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
134265c69066SAndrew Gallatin 	if (err == 0)
134365c69066SAndrew Gallatin 		sc->throttle = throttle;
134465c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
134565c69066SAndrew Gallatin 	return err;
134665c69066SAndrew Gallatin }
134765c69066SAndrew Gallatin 
134865c69066SAndrew Gallatin static int
13496d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1350b2fc195eSAndrew Gallatin {
13516d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1352b2fc195eSAndrew Gallatin 	unsigned int intr_coal_delay;
1353b2fc195eSAndrew Gallatin 	int err;
1354b2fc195eSAndrew Gallatin 
1355b2fc195eSAndrew Gallatin 	sc = arg1;
1356b2fc195eSAndrew Gallatin 	intr_coal_delay = sc->intr_coal_delay;
1357b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1358b2fc195eSAndrew Gallatin 	if (err != 0) {
1359b2fc195eSAndrew Gallatin 		return err;
1360b2fc195eSAndrew Gallatin 	}
1361b2fc195eSAndrew Gallatin 	if (intr_coal_delay == sc->intr_coal_delay)
1362b2fc195eSAndrew Gallatin 		return 0;
1363b2fc195eSAndrew Gallatin 
1364b2fc195eSAndrew Gallatin 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1365b2fc195eSAndrew Gallatin 		return EINVAL;
1366b2fc195eSAndrew Gallatin 
1367a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13685e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1369b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13705e7d8541SAndrew Gallatin 
1371a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1372b2fc195eSAndrew Gallatin 	return err;
1373b2fc195eSAndrew Gallatin }
1374b2fc195eSAndrew Gallatin 
1375b2fc195eSAndrew Gallatin static int
13766d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1377b2fc195eSAndrew Gallatin {
13786d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1379b2fc195eSAndrew Gallatin 	unsigned int enabled;
1380b2fc195eSAndrew Gallatin 	int err;
1381b2fc195eSAndrew Gallatin 
1382b2fc195eSAndrew Gallatin 	sc = arg1;
1383b2fc195eSAndrew Gallatin 	enabled = sc->pause;
1384b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
1385b2fc195eSAndrew Gallatin 	if (err != 0) {
1386b2fc195eSAndrew Gallatin 		return err;
1387b2fc195eSAndrew Gallatin 	}
1388b2fc195eSAndrew Gallatin 	if (enabled == sc->pause)
1389b2fc195eSAndrew Gallatin 		return 0;
1390b2fc195eSAndrew Gallatin 
1391a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13926d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1393a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1394b2fc195eSAndrew Gallatin 	return err;
1395b2fc195eSAndrew Gallatin }
1396b2fc195eSAndrew Gallatin 
1397b2fc195eSAndrew Gallatin static int
13986d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1399b2fc195eSAndrew Gallatin {
1400b2fc195eSAndrew Gallatin 	int err;
1401b2fc195eSAndrew Gallatin 
1402b2fc195eSAndrew Gallatin 	if (arg1 == NULL)
1403b2fc195eSAndrew Gallatin 		return EFAULT;
1404b2fc195eSAndrew Gallatin 	arg2 = be32toh(*(int *)arg1);
1405b2fc195eSAndrew Gallatin 	arg1 = NULL;
1406b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, arg1, arg2, req);
1407b2fc195eSAndrew Gallatin 
1408b2fc195eSAndrew Gallatin 	return err;
1409b2fc195eSAndrew Gallatin }
1410b2fc195eSAndrew Gallatin 
1411b2fc195eSAndrew Gallatin static void
14121e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14131e413cf9SAndrew Gallatin {
14141e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14151e413cf9SAndrew Gallatin 	int slice;
14161e413cf9SAndrew Gallatin 
14171e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14181e413cf9SAndrew Gallatin 		return;
14191e413cf9SAndrew Gallatin 
14201e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14211e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14221e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14231e413cf9SAndrew Gallatin 			continue;
14241e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14251e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14261e413cf9SAndrew Gallatin 	}
14271e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14281e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14291e413cf9SAndrew Gallatin }
14301e413cf9SAndrew Gallatin 
14311e413cf9SAndrew Gallatin static void
14326d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1433b2fc195eSAndrew Gallatin {
1434b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1435b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14365e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14371e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14381e413cf9SAndrew Gallatin 	int slice;
14391e413cf9SAndrew Gallatin 	char slice_num[8];
1440b2fc195eSAndrew Gallatin 
1441b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1442b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14431e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1444b2fc195eSAndrew Gallatin 
14455e7d8541SAndrew Gallatin 	/* random information */
14465e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14475e7d8541SAndrew Gallatin 		       "firmware_version",
1448f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->fw_version,
14495e7d8541SAndrew Gallatin 		       0, "firmware version");
14505e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14515e7d8541SAndrew Gallatin 		       "serial_number",
1452f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->serial_number_string,
14535e7d8541SAndrew Gallatin 		       0, "serial number");
14545e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14555e7d8541SAndrew Gallatin 		       "product_code",
1456f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->product_code_string,
14575e7d8541SAndrew Gallatin 		       0, "product_code");
14585e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1459d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1460d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1461d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1462d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14635e7d8541SAndrew Gallatin 		       "tx_boundary",
14641e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14655e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14665e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1467091feecdSAndrew Gallatin 		       "write_combine",
1468091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1469091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1470091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14715e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14725e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14735e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14745e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14755e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14765e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14775e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14785e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14795e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14805e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14815e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
1482a393336bSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1483a393336bSAndrew Gallatin 		       "watchdog_resets",
1484a393336bSAndrew Gallatin 		       CTLFLAG_RD, &sc->watchdog_resets,
1485a393336bSAndrew Gallatin 		       0, "Number of times NIC was reset");
14865e7d8541SAndrew Gallatin 
14875e7d8541SAndrew Gallatin 	/* performance related tunables */
1488b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14897029da5cSPawel Biernacki 	    "intr_coal_delay", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
14907029da5cSPawel Biernacki 	    sc, 0, mxge_change_intr_coal, "I",
14917029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1492b2fc195eSAndrew Gallatin 
1493b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14947029da5cSPawel Biernacki 	    "throttle", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14957029da5cSPawel Biernacki 	    mxge_change_throttle, "I", "transmit throttling");
149665c69066SAndrew Gallatin 
149765c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1498b2fc195eSAndrew Gallatin 	    "flow_control_enabled",
14997029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
15007029da5cSPawel Biernacki 	    mxge_change_flow_control, "I",
15017029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1502b2fc195eSAndrew Gallatin 
1503b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15045e7d8541SAndrew Gallatin 		       "deassert_wait",
15055e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15065e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1507b2fc195eSAndrew Gallatin 
1508b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1509b2fc195eSAndrew Gallatin 	   Need to swap it */
1510b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15117029da5cSPawel Biernacki 	    "link_up", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15127029da5cSPawel Biernacki 	    &fw->link_up, 0, mxge_handle_be32, "I", "link up");
1513b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15147029da5cSPawel Biernacki 	    "rdma_tags_available", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15157029da5cSPawel Biernacki 	    &fw->rdma_tags_available, 0, mxge_handle_be32, "I",
15167029da5cSPawel Biernacki 	    "rdma_tags_available");
1517b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15187029da5cSPawel Biernacki 	    "dropped_bad_crc32", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15197029da5cSPawel Biernacki 	    &fw->dropped_bad_crc32, 0, mxge_handle_be32, "I",
15207029da5cSPawel Biernacki 	    "dropped_bad_crc32");
1521adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15227029da5cSPawel Biernacki 	    "dropped_bad_phy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15237029da5cSPawel Biernacki 	    &fw->dropped_bad_phy, 0, mxge_handle_be32, "I", "dropped_bad_phy");
1524b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1525b2fc195eSAndrew Gallatin 	    "dropped_link_error_or_filtered",
15267029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15277029da5cSPawel Biernacki 	    &fw->dropped_link_error_or_filtered, 0, mxge_handle_be32, "I",
15287029da5cSPawel Biernacki 	    "dropped_link_error_or_filtered");
1529b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1530adae7080SAndrew Gallatin 	    "dropped_link_overflow",
15317029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15327029da5cSPawel Biernacki 	    &fw->dropped_link_overflow, 0, mxge_handle_be32, "I",
15337029da5cSPawel Biernacki 	    "dropped_link_overflow");
1534adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15350fa7f681SAndrew Gallatin 	    "dropped_multicast_filtered",
15367029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15377029da5cSPawel Biernacki 	    &fw->dropped_multicast_filtered, 0, mxge_handle_be32, "I",
15387029da5cSPawel Biernacki 	    "dropped_multicast_filtered");
15390fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1540adae7080SAndrew Gallatin 	    "dropped_no_big_buffer",
15417029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15427029da5cSPawel Biernacki 	    &fw->dropped_no_big_buffer, 0, mxge_handle_be32, "I",
15437029da5cSPawel Biernacki 	    "dropped_no_big_buffer");
1544b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1545b2fc195eSAndrew Gallatin 	    "dropped_no_small_buffer",
15467029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15477029da5cSPawel Biernacki 	    &fw->dropped_no_small_buffer, 0, mxge_handle_be32, "I",
15487029da5cSPawel Biernacki 	    "dropped_no_small_buffer");
1549b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1550adae7080SAndrew Gallatin 	    "dropped_overrun",
15517029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15527029da5cSPawel Biernacki 	    &fw->dropped_overrun, 0, mxge_handle_be32, "I",
15537029da5cSPawel Biernacki 	    "dropped_overrun");
1554adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15557029da5cSPawel Biernacki 	    "dropped_pause", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15567029da5cSPawel Biernacki 	    &fw->dropped_pause, 0, mxge_handle_be32, "I", "dropped_pause");
1557adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15587029da5cSPawel Biernacki 	    "dropped_runt", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15597029da5cSPawel Biernacki 	    &fw->dropped_runt, 0, mxge_handle_be32, "I", "dropped_runt");
1560b2fc195eSAndrew Gallatin 
1561a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1562a0394e33SAndrew Gallatin 	    "dropped_unicast_filtered",
15637029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15647029da5cSPawel Biernacki 	    &fw->dropped_unicast_filtered, 0, mxge_handle_be32, "I",
15657029da5cSPawel Biernacki 	    "dropped_unicast_filtered");
1566a0394e33SAndrew Gallatin 
15675e7d8541SAndrew Gallatin 	/* verbose printing? */
1568b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15695e7d8541SAndrew Gallatin 		       "verbose",
15705e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15715e7d8541SAndrew Gallatin 		       0, "verbose printing");
1572b2fc195eSAndrew Gallatin 
15731e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
15741e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15751e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
15761e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15777029da5cSPawel Biernacki 		    "slice", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15781e413cf9SAndrew Gallatin 
15791e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
15801e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
15811e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
15821e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
15831e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
15841e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
15851e413cf9SAndrew Gallatin 		ss->sysctl_tree =
15861e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
15877029da5cSPawel Biernacki 			    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15881e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1589053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15901e413cf9SAndrew Gallatin 			       "rx_small_cnt",
15911e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
15921e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
15931e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15941e413cf9SAndrew Gallatin 			       "rx_big_cnt",
15951e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
15961e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
1597e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
159826dd49c6SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lc.lro_flushed,
1599053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1600053e637fSAndrew Gallatin 
1601e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
160226dd49c6SAndrew Gallatin 			       "lro_bad_csum", CTLFLAG_RD, &ss->lc.lro_bad_csum,
160326dd49c6SAndrew Gallatin 			       0, "number of bad csums preventing LRO");
160426dd49c6SAndrew Gallatin 
1605e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
160626dd49c6SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lc.lro_queued,
16071e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16081e413cf9SAndrew Gallatin 			       "queues");
1609053e637fSAndrew Gallatin 
1610c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16111e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16121e413cf9SAndrew Gallatin 		if (slice > 0)
16131e413cf9SAndrew Gallatin 			continue;
1614c6cb3e3fSAndrew Gallatin #endif
1615c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1616c6cb3e3fSAndrew Gallatin 			       "tx_req",
1617c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1618c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16191e413cf9SAndrew Gallatin 
16201e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16211e413cf9SAndrew Gallatin 			       "tx_done",
16221e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16231e413cf9SAndrew Gallatin 			       0, "tx_done");
16241e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16251e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16261e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16271e413cf9SAndrew Gallatin 			       0, "tx_done");
16281e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16291e413cf9SAndrew Gallatin 			       "tx_stall",
16301e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16311e413cf9SAndrew Gallatin 			       0, "tx_stall");
16321e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16331e413cf9SAndrew Gallatin 			       "tx_wake",
16341e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16351e413cf9SAndrew Gallatin 			       0, "tx_wake");
16361e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16371e413cf9SAndrew Gallatin 			       "tx_defrag",
16381e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16391e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1640c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1641c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1642c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1643c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1644c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1645c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1646c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1647c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1648c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1649c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1650c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1651c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
16521e413cf9SAndrew Gallatin 	}
1653b2fc195eSAndrew Gallatin }
1654b2fc195eSAndrew Gallatin 
1655b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1656b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1657b2fc195eSAndrew Gallatin 
1658b2fc195eSAndrew Gallatin static inline void
16591e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1660b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1661b2fc195eSAndrew Gallatin {
1662b2fc195eSAndrew Gallatin 	int idx, starting_slot;
1663b2fc195eSAndrew Gallatin 	starting_slot = tx->req;
1664b2fc195eSAndrew Gallatin 	while (cnt > 1) {
1665b2fc195eSAndrew Gallatin 		cnt--;
1666b2fc195eSAndrew Gallatin 		idx = (starting_slot + cnt) & tx->mask;
16676d87a65dSAndrew Gallatin 		mxge_pio_copy(&tx->lanai[idx],
1668b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
166973c7c83fSAndrew Gallatin 		wmb();
1670b2fc195eSAndrew Gallatin 	}
1671b2fc195eSAndrew Gallatin }
1672b2fc195eSAndrew Gallatin 
1673b2fc195eSAndrew Gallatin /*
1674b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1675b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1676b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1677b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1678b2fc195eSAndrew Gallatin  */
1679b2fc195eSAndrew Gallatin 
1680b2fc195eSAndrew Gallatin static inline void
16811e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1682b2fc195eSAndrew Gallatin 		  int cnt)
1683b2fc195eSAndrew Gallatin {
1684b2fc195eSAndrew Gallatin 	int idx, i;
1685b2fc195eSAndrew Gallatin 	uint32_t *src_ints;
1686b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1687b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *srcp;
1688b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
16895e7d8541SAndrew Gallatin 	uint8_t last_flags;
1690b2fc195eSAndrew Gallatin 
1691b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1692b2fc195eSAndrew Gallatin 
16935e7d8541SAndrew Gallatin 	last_flags = src->flags;
16945e7d8541SAndrew Gallatin 	src->flags = 0;
169573c7c83fSAndrew Gallatin 	wmb();
1696b2fc195eSAndrew Gallatin 	dst = dstp = &tx->lanai[idx];
1697b2fc195eSAndrew Gallatin 	srcp = src;
1698b2fc195eSAndrew Gallatin 
1699b2fc195eSAndrew Gallatin 	if ((idx + cnt) < tx->mask) {
1700b2fc195eSAndrew Gallatin 		for (i = 0; i < (cnt - 1); i += 2) {
17016d87a65dSAndrew Gallatin 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
170273c7c83fSAndrew Gallatin 			wmb(); /* force write every 32 bytes */
1703b2fc195eSAndrew Gallatin 			srcp += 2;
1704b2fc195eSAndrew Gallatin 			dstp += 2;
1705b2fc195eSAndrew Gallatin 		}
1706b2fc195eSAndrew Gallatin 	} else {
1707b2fc195eSAndrew Gallatin 		/* submit all but the first request, and ensure
1708b2fc195eSAndrew Gallatin 		   that it is submitted below */
17096d87a65dSAndrew Gallatin 		mxge_submit_req_backwards(tx, src, cnt);
1710b2fc195eSAndrew Gallatin 		i = 0;
1711b2fc195eSAndrew Gallatin 	}
1712b2fc195eSAndrew Gallatin 	if (i < cnt) {
1713b2fc195eSAndrew Gallatin 		/* submit the first request */
17146d87a65dSAndrew Gallatin 		mxge_pio_copy(dstp, srcp, sizeof(*src));
171573c7c83fSAndrew Gallatin 		wmb(); /* barrier before setting valid flag */
1716b2fc195eSAndrew Gallatin 	}
1717b2fc195eSAndrew Gallatin 
1718b2fc195eSAndrew Gallatin 	/* re-write the last 32-bits with the valid flags */
17195e7d8541SAndrew Gallatin 	src->flags = last_flags;
1720b2fc195eSAndrew Gallatin 	src_ints = (uint32_t *)src;
1721b2fc195eSAndrew Gallatin 	src_ints+=3;
1722b2fc195eSAndrew Gallatin 	dst_ints = (volatile uint32_t *)dst;
1723b2fc195eSAndrew Gallatin 	dst_ints+=3;
1724b2fc195eSAndrew Gallatin 	*dst_ints =  *src_ints;
1725b2fc195eSAndrew Gallatin 	tx->req += cnt;
172673c7c83fSAndrew Gallatin 	wmb();
1727b2fc195eSAndrew Gallatin }
1728b2fc195eSAndrew Gallatin 
17290a7a780eSAndrew Gallatin static int
17300a7a780eSAndrew Gallatin mxge_parse_tx(struct mxge_slice_state *ss, struct mbuf *m,
17310a7a780eSAndrew Gallatin     struct mxge_pkt_info *pi)
17320a7a780eSAndrew Gallatin {
17330a7a780eSAndrew Gallatin 	struct ether_vlan_header *eh;
17340a7a780eSAndrew Gallatin 	uint16_t etype;
17350a7a780eSAndrew Gallatin 	int tso = m->m_pkthdr.csum_flags & (CSUM_TSO);
17360a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17370a7a780eSAndrew Gallatin 	int nxt;
17380a7a780eSAndrew Gallatin #endif
17390a7a780eSAndrew Gallatin 
17400a7a780eSAndrew Gallatin 	eh = mtod(m, struct ether_vlan_header *);
17410a7a780eSAndrew Gallatin 	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
17420a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_proto);
17430a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
17440a7a780eSAndrew Gallatin 	} else {
17450a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_encap_proto);
17460a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN;
17470a7a780eSAndrew Gallatin 	}
17480a7a780eSAndrew Gallatin 
17490a7a780eSAndrew Gallatin 	switch (etype) {
17500a7a780eSAndrew Gallatin 	case ETHERTYPE_IP:
17510a7a780eSAndrew Gallatin 		/*
17520a7a780eSAndrew Gallatin 		 * ensure ip header is in first mbuf, copy it to a
17530a7a780eSAndrew Gallatin 		 * scratch buffer if not
17540a7a780eSAndrew Gallatin 		 */
17550a7a780eSAndrew Gallatin 		pi->ip = (struct ip *)(m->m_data + pi->ip_off);
17560a7a780eSAndrew Gallatin 		pi->ip6 = NULL;
17570a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip))) {
17580a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip),
17590a7a780eSAndrew Gallatin 			    ss->scratch);
17600a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17610a7a780eSAndrew Gallatin 		}
17620a7a780eSAndrew Gallatin 		pi->ip_hlen = pi->ip->ip_hl << 2;
17630a7a780eSAndrew Gallatin 		if (!tso)
17640a7a780eSAndrew Gallatin 			return 0;
17650a7a780eSAndrew Gallatin 
17660a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17670a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17680a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17690a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17700a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17710a7a780eSAndrew Gallatin 		}
17720a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip + pi->ip_hlen);
17730a7a780eSAndrew Gallatin 		break;
17740a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17750a7a780eSAndrew Gallatin 	case ETHERTYPE_IPV6:
17760a7a780eSAndrew Gallatin 		pi->ip6 = (struct ip6_hdr *)(m->m_data + pi->ip_off);
17770a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip6))) {
17780a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip6),
17790a7a780eSAndrew Gallatin 			    ss->scratch);
17800a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17810a7a780eSAndrew Gallatin 		}
17820a7a780eSAndrew Gallatin 		nxt = 0;
17830a7a780eSAndrew Gallatin 		pi->ip_hlen = ip6_lasthdr(m, pi->ip_off, IPPROTO_IPV6, &nxt);
17840a7a780eSAndrew Gallatin 		pi->ip_hlen -= pi->ip_off;
17850a7a780eSAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
17860a7a780eSAndrew Gallatin 			return EINVAL;
17870a7a780eSAndrew Gallatin 
17880a7a780eSAndrew Gallatin 		if (!tso)
17890a7a780eSAndrew Gallatin 			return 0;
17900a7a780eSAndrew Gallatin 
17910a7a780eSAndrew Gallatin 		if (pi->ip_off + pi->ip_hlen > ss->sc->max_tso6_hlen)
17920a7a780eSAndrew Gallatin 			return EINVAL;
17930a7a780eSAndrew Gallatin 
17940a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17950a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17960a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17970a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17980a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17990a7a780eSAndrew Gallatin 		}
18000a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip6 + pi->ip_hlen);
18010a7a780eSAndrew Gallatin 		break;
18020a7a780eSAndrew Gallatin #endif
18030a7a780eSAndrew Gallatin 	default:
18040a7a780eSAndrew Gallatin 		return EINVAL;
18050a7a780eSAndrew Gallatin 	}
18060a7a780eSAndrew Gallatin 	return 0;
18070a7a780eSAndrew Gallatin }
18080a7a780eSAndrew Gallatin 
180937d89b0cSAndrew Gallatin #if IFCAP_TSO4
181037d89b0cSAndrew Gallatin 
1811b2fc195eSAndrew Gallatin static void
18121e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
18130a7a780eSAndrew Gallatin 	       int busdma_seg_cnt, struct mxge_pkt_info *pi)
1814aed8e389SAndrew Gallatin {
18151e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1816aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1817aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1818aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1819aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1820aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
18210a7a780eSAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss, sum;
1822aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1823aed8e389SAndrew Gallatin 	static int once;
1824aed8e389SAndrew Gallatin 
1825aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1826aed8e389SAndrew Gallatin 
1827aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1828aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1829aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1830aed8e389SAndrew Gallatin 	 */
1831aed8e389SAndrew Gallatin 
18320a7a780eSAndrew Gallatin 	cksum_offset = pi->ip_off + pi->ip_hlen;
18330a7a780eSAndrew Gallatin 	cum_len = -(cksum_offset + (pi->tcp->th_off << 2));
1834aed8e389SAndrew Gallatin 
1835aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
18360a7a780eSAndrew Gallatin 	if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) == 0)) {
18374ed8ca8fSAndrew Gallatin 		/*
18384ed8ca8fSAndrew Gallatin 		 * If packet has full TCP csum, replace it with pseudo hdr
18394ed8ca8fSAndrew Gallatin 		 * sum that the NIC expects, otherwise the NIC will emit
18404ed8ca8fSAndrew Gallatin 		 * packets with bad TCP checksums.
18414ed8ca8fSAndrew Gallatin 		 */
18424ed8ca8fSAndrew Gallatin 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
18430a7a780eSAndrew Gallatin 		if (pi->ip6) {
18440a7a780eSAndrew Gallatin #if (CSUM_TCP_IPV6 != 0) && defined(INET6)
18450a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP_IPV6;
18460a7a780eSAndrew Gallatin 			sum = in6_cksum_pseudo(pi->ip6,
18470a7a780eSAndrew Gallatin 			    m->m_pkthdr.len - cksum_offset,
18480a7a780eSAndrew Gallatin 			    IPPROTO_TCP, 0);
18490a7a780eSAndrew Gallatin #endif
18500a7a780eSAndrew Gallatin 		} else {
1851abc5b96bSAndrew Gallatin #ifdef INET
18520a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP;
18530a7a780eSAndrew Gallatin 			sum = in_pseudo(pi->ip->ip_src.s_addr,
18540a7a780eSAndrew Gallatin 			    pi->ip->ip_dst.s_addr,
18550a7a780eSAndrew Gallatin 			    htons(IPPROTO_TCP + (m->m_pkthdr.len -
18560a7a780eSAndrew Gallatin 				    cksum_offset)));
1857abc5b96bSAndrew Gallatin #endif
18580a7a780eSAndrew Gallatin 		}
18590a7a780eSAndrew Gallatin 		m_copyback(m, offsetof(struct tcphdr, th_sum) +
18600a7a780eSAndrew Gallatin 		    cksum_offset, sizeof(sum), (caddr_t)&sum);
18614ed8ca8fSAndrew Gallatin 	}
1862aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1863aed8e389SAndrew Gallatin 
1864aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1865aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1866aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1867aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1868aed8e389SAndrew Gallatin 
18690a7a780eSAndrew Gallatin 	if (pi->ip6) {
18700a7a780eSAndrew Gallatin 		/*
18710a7a780eSAndrew Gallatin 		 * for IPv6 TSO, the "checksum offset" is re-purposed
18720a7a780eSAndrew Gallatin 		 * to store the TCP header len
18730a7a780eSAndrew Gallatin 		 */
18740a7a780eSAndrew Gallatin 		cksum_offset = (pi->tcp->th_off << 2);
18750a7a780eSAndrew Gallatin 	}
18760a7a780eSAndrew Gallatin 
18771e413cf9SAndrew Gallatin 	tx = &ss->tx;
1878aed8e389SAndrew Gallatin 	req = tx->req_list;
1879aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1880aed8e389SAndrew Gallatin 	cnt = 0;
1881aed8e389SAndrew Gallatin 	rdma_count = 0;
1882aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1883aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1884aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1885aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1886aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1887aed8e389SAndrew Gallatin 	 *
1888aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1889aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1890aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1891aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1892aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1893aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1894aed8e389SAndrew Gallatin 	 *
1895aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1896aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1897aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1898aed8e389SAndrew Gallatin 	 */
1899aed8e389SAndrew Gallatin 
1900aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1901aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1902aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1903aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1904e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1905aed8e389SAndrew Gallatin 
1906aed8e389SAndrew Gallatin 		while (len) {
1907aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1908e39a0a37SAndrew Gallatin 			seglen = len;
1909aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1910aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1911aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1912aed8e389SAndrew Gallatin 				/* payload */
1913aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1914aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1915aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1916aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1917aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1918aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1919aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1920aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1921aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1922aed8e389SAndrew Gallatin 				/* header ends */
1923aed8e389SAndrew Gallatin 				rdma_count = -1;
1924aed8e389SAndrew Gallatin 				cum_len_next = 0;
1925aed8e389SAndrew Gallatin 				seglen = -cum_len;
1926aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1927aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1928aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1929aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1930aed8e389SAndrew Gallatin 			    }
1931aed8e389SAndrew Gallatin 
1932aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1933aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1934aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1935aed8e389SAndrew Gallatin 			req->pad = 0;
1936aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1937aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1938aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1939aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1940aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1941aed8e389SAndrew Gallatin 			low += seglen;
1942aed8e389SAndrew Gallatin 			len -= seglen;
1943aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1944aed8e389SAndrew Gallatin 			flags = flags_next;
1945aed8e389SAndrew Gallatin 			req++;
1946aed8e389SAndrew Gallatin 			cnt++;
1947aed8e389SAndrew Gallatin 			rdma_count++;
19480a7a780eSAndrew Gallatin 			if (cksum_offset != 0 && !pi->ip6) {
1949aed8e389SAndrew Gallatin 				if (__predict_false(cksum_offset > seglen))
1950aed8e389SAndrew Gallatin 					cksum_offset -= seglen;
1951aed8e389SAndrew Gallatin 				else
1952aed8e389SAndrew Gallatin 					cksum_offset = 0;
19530a7a780eSAndrew Gallatin 			}
1954adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1955aed8e389SAndrew Gallatin 				goto drop;
1956aed8e389SAndrew Gallatin 		}
1957aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1958aed8e389SAndrew Gallatin 		seg++;
1959aed8e389SAndrew Gallatin 	}
1960aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1961aed8e389SAndrew Gallatin 
1962aed8e389SAndrew Gallatin 	do {
1963aed8e389SAndrew Gallatin 		req--;
1964aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1965aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1966aed8e389SAndrew Gallatin 
1967aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1968aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1969c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1970c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1971c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1972c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1973c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1974c6cb3e3fSAndrew Gallatin 		tx->activate++;
1975c6cb3e3fSAndrew Gallatin 		wmb();
1976c6cb3e3fSAndrew Gallatin 	}
1977c6cb3e3fSAndrew Gallatin #endif
1978aed8e389SAndrew Gallatin 	return;
1979aed8e389SAndrew Gallatin 
1980aed8e389SAndrew Gallatin drop:
1981e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1982aed8e389SAndrew Gallatin 	m_freem(m);
1983c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1984aed8e389SAndrew Gallatin 	if (!once) {
1985adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1986adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1987adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1988aed8e389SAndrew Gallatin 		once = 1;
1989aed8e389SAndrew Gallatin 	}
1990aed8e389SAndrew Gallatin 	return;
1991aed8e389SAndrew Gallatin 
1992aed8e389SAndrew Gallatin }
1993aed8e389SAndrew Gallatin 
199437d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
199537d89b0cSAndrew Gallatin 
199637d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1997c792928fSAndrew Gallatin /*
1998c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1999c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
2000c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
2001c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
2002c792928fSAndrew Gallatin  */
2003c792928fSAndrew Gallatin static struct mbuf *
2004c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
2005c792928fSAndrew Gallatin {
2006c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2007c792928fSAndrew Gallatin 
2008c6499eccSGleb Smirnoff 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
2009c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
2010c792928fSAndrew Gallatin 		return NULL;
2011c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
2012c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
2013c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2014c792928fSAndrew Gallatin 			return NULL;
2015c792928fSAndrew Gallatin 	}
2016c792928fSAndrew Gallatin 	/*
2017c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2018c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2019c792928fSAndrew Gallatin 	 */
2020c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2021c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2022c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2023c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2024c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2025c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2026c792928fSAndrew Gallatin 	return m;
2027c792928fSAndrew Gallatin }
202837d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2029c792928fSAndrew Gallatin 
2030aed8e389SAndrew Gallatin static void
20311e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2032b2fc195eSAndrew Gallatin {
20330a7a780eSAndrew Gallatin 	struct mxge_pkt_info pi = {0,0,0,0};
20341e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2035b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2036b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2037b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
20381e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
20390a7a780eSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
2040aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2041aed8e389SAndrew Gallatin 	uint8_t flags, cksum_offset;
2042b2fc195eSAndrew Gallatin 
20431e413cf9SAndrew Gallatin 	sc = ss->sc;
20441e413cf9SAndrew Gallatin 	tx = &ss->tx;
2045b2fc195eSAndrew Gallatin 
204637d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2047c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2048c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2049c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
20500a7a780eSAndrew Gallatin 			goto drop_without_m;
2051c792928fSAndrew Gallatin 	}
205237d89b0cSAndrew Gallatin #endif
20530a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
20540a7a780eSAndrew Gallatin 	    (CSUM_TSO | CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
20550a7a780eSAndrew Gallatin 		if (mxge_parse_tx(ss, m, &pi))
20560a7a780eSAndrew Gallatin 			goto drop;
20570a7a780eSAndrew Gallatin 	}
20580a7a780eSAndrew Gallatin 
2059b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2060b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2061b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2062aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2063b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2064adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2065b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2066b2fc195eSAndrew Gallatin 		   to defrag */
2067b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2068b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2069b2fc195eSAndrew Gallatin 			goto drop;
2070b2fc195eSAndrew Gallatin 		}
20711e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2072b2fc195eSAndrew Gallatin 		m = m_tmp;
2073b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2074b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2075aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2076b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2077b2fc195eSAndrew Gallatin 	}
2078adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2079aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2080aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2081b2fc195eSAndrew Gallatin 		goto drop;
2082b2fc195eSAndrew Gallatin 	}
2083b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2084b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20855e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2086b2fc195eSAndrew Gallatin 
208737d89b0cSAndrew Gallatin #if IFCAP_TSO4
2088aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2089aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20900a7a780eSAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, &pi);
2091aed8e389SAndrew Gallatin 		return;
2092aed8e389SAndrew Gallatin 	}
209337d89b0cSAndrew Gallatin #endif
2094aed8e389SAndrew Gallatin 
2095b2fc195eSAndrew Gallatin 	req = tx->req_list;
2096b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20975e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20985e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2099b2fc195eSAndrew Gallatin 
2100b2fc195eSAndrew Gallatin 	/* checksum offloading? */
21010a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
21020a7a780eSAndrew Gallatin 	    (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
2103aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2104aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
21050a7a780eSAndrew Gallatin 		cksum_offset = pi.ip_off + pi.ip_hlen;
2106b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
21075e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2108b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
21095e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2110aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2111aed8e389SAndrew Gallatin 	} else {
2112aed8e389SAndrew Gallatin 		odd_flag = 0;
2113b2fc195eSAndrew Gallatin 	}
21145e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21155e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2116b2fc195eSAndrew Gallatin 
2117b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2118b2fc195eSAndrew Gallatin 	cum_len = 0;
2119aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21205e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2121b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2122b2fc195eSAndrew Gallatin 		req->addr_low =
21236d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2124b2fc195eSAndrew Gallatin 		req->addr_high =
21256d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2126b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2127b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2128b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2129b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2130b2fc195eSAndrew Gallatin 		else
2131b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21325e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21335e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21345e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2135aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2136b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2137b2fc195eSAndrew Gallatin 		seg++;
2138b2fc195eSAndrew Gallatin 		req++;
2139b2fc195eSAndrew Gallatin 		req->flags = 0;
2140b2fc195eSAndrew Gallatin 	}
2141b2fc195eSAndrew Gallatin 	req--;
2142b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2143b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2144b2fc195eSAndrew Gallatin 		req++;
2145b2fc195eSAndrew Gallatin 		req->addr_low =
21466d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2147b2fc195eSAndrew Gallatin 		req->addr_high =
21486d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2149b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21505e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21515e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21525e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21535e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2154aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2155b2fc195eSAndrew Gallatin 		cnt++;
2156b2fc195eSAndrew Gallatin 	}
21575e7d8541SAndrew Gallatin 
21585e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
21595e7d8541SAndrew Gallatin #if 0
21605e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21615e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21625e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21635e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21645e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21655e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21665e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21675e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21685e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21695e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21705e7d8541SAndrew Gallatin 	}
21715e7d8541SAndrew Gallatin 	printf("--------------\n");
21725e7d8541SAndrew Gallatin #endif
21735e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21746d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2175c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2176c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2177c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2178c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2179c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2180c6cb3e3fSAndrew Gallatin 		tx->activate++;
2181c6cb3e3fSAndrew Gallatin 		wmb();
2182c6cb3e3fSAndrew Gallatin 	}
2183c6cb3e3fSAndrew Gallatin #endif
2184b2fc195eSAndrew Gallatin 	return;
2185b2fc195eSAndrew Gallatin 
2186b2fc195eSAndrew Gallatin drop:
2187b2fc195eSAndrew Gallatin 	m_freem(m);
21880a7a780eSAndrew Gallatin drop_without_m:
2189c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2190b2fc195eSAndrew Gallatin 	return;
2191b2fc195eSAndrew Gallatin }
2192b2fc195eSAndrew Gallatin 
2193c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2194c6cb3e3fSAndrew Gallatin static void
2195c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2196c6cb3e3fSAndrew Gallatin {
2197c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2198c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2199c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2200c6cb3e3fSAndrew Gallatin 	int slice;
2201b2fc195eSAndrew Gallatin 
2202c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2203c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2204c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2205c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2206c6cb3e3fSAndrew Gallatin 			m_freem(m);
2207c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2208c6cb3e3fSAndrew Gallatin 	}
2209c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2210c6cb3e3fSAndrew Gallatin }
22116d914a32SAndrew Gallatin 
2212c6cb3e3fSAndrew Gallatin static inline void
2213c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2214c6cb3e3fSAndrew Gallatin {
2215c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2216c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2217c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2218c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2219c6cb3e3fSAndrew Gallatin 
2220c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2221c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2222c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2223c6cb3e3fSAndrew Gallatin 
2224c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2225c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2226c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2227c6cb3e3fSAndrew Gallatin 			return;
2228c6cb3e3fSAndrew Gallatin 		}
2229c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2230c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2231c6cb3e3fSAndrew Gallatin 
2232c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2233c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2234c6cb3e3fSAndrew Gallatin 	}
2235c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2236c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2237c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2238c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2239c6cb3e3fSAndrew Gallatin 		tx->stall++;
2240c6cb3e3fSAndrew Gallatin 	}
2241c6cb3e3fSAndrew Gallatin }
2242c6cb3e3fSAndrew Gallatin 
2243c6cb3e3fSAndrew Gallatin static int
2244c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2245c6cb3e3fSAndrew Gallatin {
2246c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2247c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2248c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2249c6cb3e3fSAndrew Gallatin 	int err;
2250c6cb3e3fSAndrew Gallatin 
2251c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2252c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2253c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2254c6cb3e3fSAndrew Gallatin 
2255c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2256c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2257c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2258c6cb3e3fSAndrew Gallatin 		return (err);
2259c6cb3e3fSAndrew Gallatin 	}
2260c6cb3e3fSAndrew Gallatin 
2261193cbc4dSMax Laier 	if (!drbr_needs_enqueue(ifp, tx->br) &&
2262c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2263c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2264c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2265c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2266c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2267c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2268c6cb3e3fSAndrew Gallatin 		return (err);
2269c6cb3e3fSAndrew Gallatin 	}
2270c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2271c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2272c6cb3e3fSAndrew Gallatin 	return (0);
2273c6cb3e3fSAndrew Gallatin }
2274c6cb3e3fSAndrew Gallatin 
2275c6cb3e3fSAndrew Gallatin static int
2276c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2277c6cb3e3fSAndrew Gallatin {
2278c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2279c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2280c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2281c6cb3e3fSAndrew Gallatin 	int err = 0;
2282c6cb3e3fSAndrew Gallatin 	int slice;
2283c6cb3e3fSAndrew Gallatin 
2284c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2285c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2286c6cb3e3fSAndrew Gallatin 
2287c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2288c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2289c6cb3e3fSAndrew Gallatin 
2290c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2291c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2292c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2293c6cb3e3fSAndrew Gallatin 	} else {
2294c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2295c6cb3e3fSAndrew Gallatin 	}
2296c6cb3e3fSAndrew Gallatin 
2297c6cb3e3fSAndrew Gallatin 	return (err);
2298c6cb3e3fSAndrew Gallatin }
2299c6cb3e3fSAndrew Gallatin 
2300c6cb3e3fSAndrew Gallatin #else
23016d914a32SAndrew Gallatin 
23026d914a32SAndrew Gallatin static inline void
23031e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2304b2fc195eSAndrew Gallatin {
23051e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2306b2fc195eSAndrew Gallatin 	struct mbuf *m;
2307b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
23081e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2309b2fc195eSAndrew Gallatin 
23101e413cf9SAndrew Gallatin 	sc = ss->sc;
2311b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23121e413cf9SAndrew Gallatin 	tx = &ss->tx;
2313adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
23146d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
23156d914a32SAndrew Gallatin 		if (m == NULL) {
23166d914a32SAndrew Gallatin 			return;
23176d914a32SAndrew Gallatin 		}
2318b2fc195eSAndrew Gallatin 		/* let BPF see it */
2319b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2320b2fc195eSAndrew Gallatin 
2321b2fc195eSAndrew Gallatin 		/* give it to the nic */
23221e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
23236d914a32SAndrew Gallatin 	}
23246d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2325a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2326b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2327adae7080SAndrew Gallatin 		tx->stall++;
2328a82c2581SAndrew Gallatin 	}
2329b2fc195eSAndrew Gallatin }
2330c6cb3e3fSAndrew Gallatin #endif
2331b2fc195eSAndrew Gallatin static void
23326d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2333b2fc195eSAndrew Gallatin {
23346d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
23351e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2336b2fc195eSAndrew Gallatin 
23371e413cf9SAndrew Gallatin 	/* only use the first slice for now */
23381e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
23391e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
23401e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
23411e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2342b2fc195eSAndrew Gallatin }
2343b2fc195eSAndrew Gallatin 
23445e7d8541SAndrew Gallatin /*
23455e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23465e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23475e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23485e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23495e7d8541SAndrew Gallatin  * in a burst
23505e7d8541SAndrew Gallatin  */
23515e7d8541SAndrew Gallatin static inline void
23525e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23535e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
23545e7d8541SAndrew Gallatin {
23555e7d8541SAndrew Gallatin 	uint32_t low;
23565e7d8541SAndrew Gallatin 
23575e7d8541SAndrew Gallatin 	low = src->addr_low;
23585e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2359a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
236073c7c83fSAndrew Gallatin 	wmb();
2361a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
236273c7c83fSAndrew Gallatin 	wmb();
236340385a5fSAndrew Gallatin 	src->addr_low = low;
23645e7d8541SAndrew Gallatin 	dst->addr_low = low;
236573c7c83fSAndrew Gallatin 	wmb();
23665e7d8541SAndrew Gallatin }
23675e7d8541SAndrew Gallatin 
2368b2fc195eSAndrew Gallatin static int
23691e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2370b2fc195eSAndrew Gallatin {
2371b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2372b2fc195eSAndrew Gallatin 	struct mbuf *m;
23731e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2374b2fc195eSAndrew Gallatin 	int cnt, err;
2375b2fc195eSAndrew Gallatin 
2376c6499eccSGleb Smirnoff 	m = m_gethdr(M_NOWAIT, MT_DATA);
2377b2fc195eSAndrew Gallatin 	if (m == NULL) {
2378b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2379b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2380b2fc195eSAndrew Gallatin 		goto done;
2381b2fc195eSAndrew Gallatin 	}
2382b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2383b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2384b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2385b2fc195eSAndrew Gallatin 	if (err != 0) {
2386b2fc195eSAndrew Gallatin 		m_free(m);
2387b2fc195eSAndrew Gallatin 		goto done;
2388b2fc195eSAndrew Gallatin 	}
2389b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2390b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23916d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2392b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23936d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2394b2fc195eSAndrew Gallatin 
2395b2fc195eSAndrew Gallatin done:
2396adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2397adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2398b2fc195eSAndrew Gallatin 	return err;
2399b2fc195eSAndrew Gallatin }
2400b2fc195eSAndrew Gallatin 
2401b2fc195eSAndrew Gallatin static int
24021e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2403b2fc195eSAndrew Gallatin {
2404053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2405b2fc195eSAndrew Gallatin 	struct mbuf *m;
24061e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2407053e637fSAndrew Gallatin 	int cnt, err, i;
2408b2fc195eSAndrew Gallatin 
2409c6499eccSGleb Smirnoff 	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2410b2fc195eSAndrew Gallatin 	if (m == NULL) {
2411b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2412b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2413b2fc195eSAndrew Gallatin 		goto done;
2414b2fc195eSAndrew Gallatin 	}
24154d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2416b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2417053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2418b2fc195eSAndrew Gallatin 	if (err != 0) {
2419b2fc195eSAndrew Gallatin 		m_free(m);
2420b2fc195eSAndrew Gallatin 		goto done;
2421b2fc195eSAndrew Gallatin 	}
2422b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2423b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2424b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2425b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2426b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2427053e637fSAndrew Gallatin 
2428b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2429b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2430053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2431053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2432053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2433053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2434053e637fSAndrew Gallatin        }
2435b0f7b922SAndrew Gallatin #endif
2436b2fc195eSAndrew Gallatin 
2437b2fc195eSAndrew Gallatin done:
2438053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2439b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
24405e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
24415e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2442b2fc195eSAndrew Gallatin 		}
2443053e637fSAndrew Gallatin 		idx++;
2444053e637fSAndrew Gallatin 	}
2445b2fc195eSAndrew Gallatin 	return err;
2446b2fc195eSAndrew Gallatin }
2447b2fc195eSAndrew Gallatin 
244826dd49c6SAndrew Gallatin #ifdef INET6
244926dd49c6SAndrew Gallatin 
245026dd49c6SAndrew Gallatin static uint16_t
245126dd49c6SAndrew Gallatin mxge_csum_generic(uint16_t *raw, int len)
245226dd49c6SAndrew Gallatin {
245326dd49c6SAndrew Gallatin 	uint32_t csum;
245426dd49c6SAndrew Gallatin 
245526dd49c6SAndrew Gallatin 	csum = 0;
245626dd49c6SAndrew Gallatin 	while (len > 0) {
245726dd49c6SAndrew Gallatin 		csum += *raw;
245826dd49c6SAndrew Gallatin 		raw++;
245926dd49c6SAndrew Gallatin 		len -= 2;
246026dd49c6SAndrew Gallatin 	}
246126dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
246226dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
246326dd49c6SAndrew Gallatin 	return (uint16_t)csum;
246426dd49c6SAndrew Gallatin }
246526dd49c6SAndrew Gallatin 
246626dd49c6SAndrew Gallatin static inline uint16_t
246726dd49c6SAndrew Gallatin mxge_rx_csum6(void *p, struct mbuf *m, uint32_t csum)
246826dd49c6SAndrew Gallatin {
246926dd49c6SAndrew Gallatin 	uint32_t partial;
247026dd49c6SAndrew Gallatin 	int nxt, cksum_offset;
247126dd49c6SAndrew Gallatin 	struct ip6_hdr *ip6 = p;
247226dd49c6SAndrew Gallatin 	uint16_t c;
247326dd49c6SAndrew Gallatin 
247426dd49c6SAndrew Gallatin 	nxt = ip6->ip6_nxt;
247526dd49c6SAndrew Gallatin 	cksum_offset = sizeof (*ip6) + ETHER_HDR_LEN;
247626dd49c6SAndrew Gallatin 	if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) {
247726dd49c6SAndrew Gallatin 		cksum_offset = ip6_lasthdr(m, ETHER_HDR_LEN,
247826dd49c6SAndrew Gallatin 					   IPPROTO_IPV6, &nxt);
247926dd49c6SAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
248026dd49c6SAndrew Gallatin 			return (1);
248126dd49c6SAndrew Gallatin 	}
248226dd49c6SAndrew Gallatin 
248326dd49c6SAndrew Gallatin 	/*
248426dd49c6SAndrew Gallatin 	 * IPv6 headers do not contain a checksum, and hence
248526dd49c6SAndrew Gallatin 	 * do not checksum to zero, so they don't "fall out"
248626dd49c6SAndrew Gallatin 	 * of the partial checksum calculation like IPv4
248726dd49c6SAndrew Gallatin 	 * headers do.  We need to fix the partial checksum by
248826dd49c6SAndrew Gallatin 	 * subtracting the checksum of the IPv6 header.
248926dd49c6SAndrew Gallatin 	 */
249026dd49c6SAndrew Gallatin 
249126dd49c6SAndrew Gallatin 	partial = mxge_csum_generic((uint16_t *)ip6, cksum_offset -
249226dd49c6SAndrew Gallatin 				    ETHER_HDR_LEN);
249326dd49c6SAndrew Gallatin 	csum += ~partial;
249426dd49c6SAndrew Gallatin 	csum +=	 (csum < ~partial);
249526dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
249626dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
249726dd49c6SAndrew Gallatin 	c = in6_cksum_pseudo(ip6, m->m_pkthdr.len - cksum_offset, nxt,
249826dd49c6SAndrew Gallatin 			     csum);
249926dd49c6SAndrew Gallatin 	c ^= 0xffff;
250026dd49c6SAndrew Gallatin 	return (c);
250126dd49c6SAndrew Gallatin }
250226dd49c6SAndrew Gallatin #endif /* INET6 */
25039b03b0f3SAndrew Gallatin /*
25049b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
25059b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
25069b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
25079b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2508053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2509053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
25109b03b0f3SAndrew Gallatin  */
25119b03b0f3SAndrew Gallatin 
2512053e637fSAndrew Gallatin static inline uint16_t
2513053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2514053e637fSAndrew Gallatin {
2515053e637fSAndrew Gallatin 	struct ether_header *eh;
251626dd49c6SAndrew Gallatin #ifdef INET
2517053e637fSAndrew Gallatin 	struct ip *ip;
251826dd49c6SAndrew Gallatin #endif
2519abc5b96bSAndrew Gallatin #if defined(INET) || defined(INET6)
2520abc5b96bSAndrew Gallatin 	int cap = m->m_pkthdr.rcvif->if_capenable;
2521abc5b96bSAndrew Gallatin #endif
252226dd49c6SAndrew Gallatin 	uint16_t c, etype;
252326dd49c6SAndrew Gallatin 
2524053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
252526dd49c6SAndrew Gallatin 	etype = ntohs(eh->ether_type);
252626dd49c6SAndrew Gallatin 	switch (etype) {
2527eb6219e3SAndrew Gallatin #ifdef INET
252826dd49c6SAndrew Gallatin 	case ETHERTYPE_IP:
252926dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM) == 0)
253026dd49c6SAndrew Gallatin 			return (1);
253126dd49c6SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
253226dd49c6SAndrew Gallatin 		if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP)
253326dd49c6SAndrew Gallatin 			return (1);
2534053e637fSAndrew Gallatin 		c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
253526dd49c6SAndrew Gallatin 			      htonl(ntohs(csum) + ntohs(ip->ip_len) -
253626dd49c6SAndrew Gallatin 				    (ip->ip_hl << 2) + ip->ip_p));
2537053e637fSAndrew Gallatin 		c ^= 0xffff;
253826dd49c6SAndrew Gallatin 		break;
253926dd49c6SAndrew Gallatin #endif
254026dd49c6SAndrew Gallatin #ifdef INET6
254126dd49c6SAndrew Gallatin 	case ETHERTYPE_IPV6:
254226dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM_IPV6) == 0)
254326dd49c6SAndrew Gallatin 			return (1);
254426dd49c6SAndrew Gallatin 		c = mxge_rx_csum6((eh + 1), m, csum);
254526dd49c6SAndrew Gallatin 		break;
254626dd49c6SAndrew Gallatin #endif
254726dd49c6SAndrew Gallatin 	default:
254826dd49c6SAndrew Gallatin 		c = 1;
254926dd49c6SAndrew Gallatin 	}
2550053e637fSAndrew Gallatin 	return (c);
25515e7d8541SAndrew Gallatin }
2552053e637fSAndrew Gallatin 
2553c792928fSAndrew Gallatin static void
2554c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2555c792928fSAndrew Gallatin {
2556c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2557c792928fSAndrew Gallatin 	uint32_t partial;
2558c792928fSAndrew Gallatin 
2559c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2560c792928fSAndrew Gallatin 
2561c792928fSAndrew Gallatin 	/*
2562c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2563c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2564c792928fSAndrew Gallatin 	 * header.
2565c792928fSAndrew Gallatin 	 */
2566c792928fSAndrew Gallatin 
2567c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2568c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2569c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2570c792928fSAndrew Gallatin 	(*csum) += ~partial;
2571c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2572c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2573c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2574c792928fSAndrew Gallatin 
2575c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2576c792928fSAndrew Gallatin 	   later consumers expect this */
2577c792928fSAndrew Gallatin 	*csum = htons(*csum);
2578c792928fSAndrew Gallatin 
2579c792928fSAndrew Gallatin 	/* save the tag */
258037d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2581c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
258237d89b0cSAndrew Gallatin #else
258337d89b0cSAndrew Gallatin 	{
258437d89b0cSAndrew Gallatin 		struct m_tag *mtag;
258537d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
258637d89b0cSAndrew Gallatin 				   M_NOWAIT);
258737d89b0cSAndrew Gallatin 		if (mtag == NULL)
258837d89b0cSAndrew Gallatin 			return;
258937d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
259037d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
259137d89b0cSAndrew Gallatin 	}
259237d89b0cSAndrew Gallatin 
259337d89b0cSAndrew Gallatin #endif
259437d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2595c792928fSAndrew Gallatin 
2596c792928fSAndrew Gallatin 	/*
2597c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2598c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2599c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2600c792928fSAndrew Gallatin 	 * type field is already in place.
2601c792928fSAndrew Gallatin 	 */
2602c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2603c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2604c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2605c792928fSAndrew Gallatin }
2606c792928fSAndrew Gallatin 
26075e7d8541SAndrew Gallatin static inline void
260826dd49c6SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len,
260926dd49c6SAndrew Gallatin 		 uint32_t csum, int lro)
2610b2fc195eSAndrew Gallatin {
26111e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2612b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2613053e637fSAndrew Gallatin 	struct mbuf *m;
2614c792928fSAndrew Gallatin 	struct ether_header *eh;
26151e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2616053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2617b2fc195eSAndrew Gallatin 	int idx;
2618b2fc195eSAndrew Gallatin 
26191e413cf9SAndrew Gallatin 	sc = ss->sc;
2620b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26211e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2622b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2623053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2624b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2625b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2626b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26271e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2628053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2629f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2630053e637fSAndrew Gallatin 		return;
2631b2fc195eSAndrew Gallatin 	}
2632053e637fSAndrew Gallatin 
2633b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2634b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2635b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2636b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2637b2fc195eSAndrew Gallatin 
2638b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2639b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2640b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2641b2fc195eSAndrew Gallatin 
2642053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2643053e637fSAndrew Gallatin 	 * aligned */
26445e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2645b2fc195eSAndrew Gallatin 
2646053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2647053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
26481e413cf9SAndrew Gallatin 	ss->ipackets++;
2649c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2650c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2651c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2652c792928fSAndrew Gallatin 	}
2653eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2654eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2655eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2656eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2657eacb70baSSepherosa Ziehau 	}
2658b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
265926dd49c6SAndrew Gallatin 	if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
266026dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
266126dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2662053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
266326dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
266426dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
266526dd49c6SAndrew Gallatin 
266626dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
266726dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, 0)))
266826dd49c6SAndrew Gallatin 			return;
266926dd49c6SAndrew Gallatin #endif
2670b2fc195eSAndrew Gallatin 	}
2671053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2672053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2673b2fc195eSAndrew Gallatin }
2674b2fc195eSAndrew Gallatin 
2675b2fc195eSAndrew Gallatin static inline void
267626dd49c6SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len,
267726dd49c6SAndrew Gallatin 		   uint32_t csum, int lro)
2678b2fc195eSAndrew Gallatin {
26791e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2680b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2681c792928fSAndrew Gallatin 	struct ether_header *eh;
2682b2fc195eSAndrew Gallatin 	struct mbuf *m;
26831e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2684b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2685b2fc195eSAndrew Gallatin 	int idx;
2686b2fc195eSAndrew Gallatin 
26871e413cf9SAndrew Gallatin 	sc = ss->sc;
2688b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26891e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2690b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2691b2fc195eSAndrew Gallatin 	rx->cnt++;
2692b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2693b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2694b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26951e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2696b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2697f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2698b2fc195eSAndrew Gallatin 		return;
2699b2fc195eSAndrew Gallatin 	}
2700b2fc195eSAndrew Gallatin 
2701b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2702b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2703b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2704b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2705b2fc195eSAndrew Gallatin 
2706b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2707b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2708b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2709b2fc195eSAndrew Gallatin 
2710b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2711b2fc195eSAndrew Gallatin 	 * aligned */
27125e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2713b2fc195eSAndrew Gallatin 
27149b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
27159b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
27161e413cf9SAndrew Gallatin 	ss->ipackets++;
2717c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2718c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2719c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2720c792928fSAndrew Gallatin 	}
2721eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2722eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2723eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2724eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2725eacb70baSSepherosa Ziehau 	}
2726b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
272726dd49c6SAndrew Gallatin 	if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
272826dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
272926dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2730053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
273126dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
273226dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
273326dd49c6SAndrew Gallatin 
273426dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
273526dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, csum)))
273626dd49c6SAndrew Gallatin 			return;
273726dd49c6SAndrew Gallatin #endif
2738053e637fSAndrew Gallatin 	}
2739b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2740b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2741b2fc195eSAndrew Gallatin }
2742b2fc195eSAndrew Gallatin 
2743b2fc195eSAndrew Gallatin static inline void
27441e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
27455e7d8541SAndrew Gallatin {
27461e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
27475e7d8541SAndrew Gallatin 	int limit = 0;
27485e7d8541SAndrew Gallatin 	uint16_t length;
27495e7d8541SAndrew Gallatin 	uint16_t checksum;
275026dd49c6SAndrew Gallatin 	int lro;
27515e7d8541SAndrew Gallatin 
275226dd49c6SAndrew Gallatin 	lro = ss->sc->ifp->if_capenable & IFCAP_LRO;
27535e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
27545e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
27555e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2756053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2757b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
275826dd49c6SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum, lro);
27595e7d8541SAndrew Gallatin 		else
276026dd49c6SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum, lro);
27615e7d8541SAndrew Gallatin 		rx_done->cnt++;
2762adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
27635e7d8541SAndrew Gallatin 
27645e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2765f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
27665e7d8541SAndrew Gallatin 			break;
2767053e637fSAndrew Gallatin 	}
276826dd49c6SAndrew Gallatin #if defined(INET)  || defined (INET6)
27696dd38b87SSepherosa Ziehau 	tcp_lro_flush_all(&ss->lc);
2770eb6219e3SAndrew Gallatin #endif
27715e7d8541SAndrew Gallatin }
27725e7d8541SAndrew Gallatin 
27735e7d8541SAndrew Gallatin static inline void
27741e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2775b2fc195eSAndrew Gallatin {
277637466424SWarner Losh 	struct ifnet *ifp __unused;
27771e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2778b2fc195eSAndrew Gallatin 	struct mbuf *m;
2779b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2780f616ebc7SAndrew Gallatin 	int idx;
2781c6cb3e3fSAndrew Gallatin 	int *flags;
2782b2fc195eSAndrew Gallatin 
27831e413cf9SAndrew Gallatin 	tx = &ss->tx;
27841e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
27855e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2786b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2787b2fc195eSAndrew Gallatin 		tx->done++;
2788b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2789b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2790b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2791b2fc195eSAndrew Gallatin 		if (m != NULL) {
279271032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
279371032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
279471032832SAndrew Gallatin 				ss->omcasts++;
2795c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2796b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2797b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2798b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2799b2fc195eSAndrew Gallatin 			m_freem(m);
2800b2fc195eSAndrew Gallatin 		}
28015e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
28025e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
28035e7d8541SAndrew Gallatin 			tx->pkt_done++;
28045e7d8541SAndrew Gallatin 		}
2805b2fc195eSAndrew Gallatin 	}
2806b2fc195eSAndrew Gallatin 
2807b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2808b2fc195eSAndrew Gallatin 	   its OK to send packets */
2809c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2810c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2811c6cb3e3fSAndrew Gallatin #else
2812c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2813c6cb3e3fSAndrew Gallatin #endif
28141e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2815c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2816c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2817c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
28181e413cf9SAndrew Gallatin 		ss->tx.wake++;
28191e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2820b2fc195eSAndrew Gallatin 	}
2821c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2822c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2823c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2824c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2825c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2826c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2827c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2828c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2829c6cb3e3fSAndrew Gallatin 			wmb();
2830c6cb3e3fSAndrew Gallatin 		}
2831c6cb3e3fSAndrew Gallatin 	}
2832c6cb3e3fSAndrew Gallatin #endif
2833c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2834c6cb3e3fSAndrew Gallatin 
2835b2fc195eSAndrew Gallatin }
2836b2fc195eSAndrew Gallatin 
283701638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2838c587e59fSAndrew Gallatin {
2839c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2840c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2841c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2842c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
284301638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2844c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2845c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2846c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2847c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2848c587e59fSAndrew Gallatin };
284901638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
285001638550SAndrew Gallatin {
285151bc2092SAndrew Gallatin 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
28524ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
285301638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
285401638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
285556b67858SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
285656b67858SAndrew Gallatin 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
285701638550SAndrew Gallatin };
2858c587e59fSAndrew Gallatin 
2859c587e59fSAndrew Gallatin static void
2860c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type)
2861c587e59fSAndrew Gallatin {
2862c406ad2eSAndrew Gallatin 
2863c406ad2eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type,
2864c406ad2eSAndrew Gallatin 		    0, NULL);
2865c406ad2eSAndrew Gallatin 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
2866c406ad2eSAndrew Gallatin 	sc->current_media = media_type;
2867c406ad2eSAndrew Gallatin 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
2868c587e59fSAndrew Gallatin }
2869c587e59fSAndrew Gallatin 
2870c587e59fSAndrew Gallatin static void
2871c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc)
2872c587e59fSAndrew Gallatin {
2873c587e59fSAndrew Gallatin 	char *ptr;
2874c406ad2eSAndrew Gallatin 	int i;
2875c587e59fSAndrew Gallatin 
2876c406ad2eSAndrew Gallatin 	ifmedia_removeall(&sc->media);
2877c406ad2eSAndrew Gallatin 	mxge_media_set(sc, IFM_AUTO);
2878c587e59fSAndrew Gallatin 
2879c587e59fSAndrew Gallatin 	/*
2880c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2881c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2882c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2883c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2884c587e59fSAndrew Gallatin 	 */
2885c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2886c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2887c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2888c406ad2eSAndrew Gallatin 		return;
2889c587e59fSAndrew Gallatin 	}
2890c587e59fSAndrew Gallatin 
2891c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
2892dc15eac0SEd Schouten 		ptr = strchr(ptr, '-');
2893c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2894c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2895c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2896c587e59fSAndrew Gallatin 			return;
2897c587e59fSAndrew Gallatin 		}
2898c587e59fSAndrew Gallatin 	}
289930882b10SAndrew Gallatin 	if (*ptr == 'C' || *(ptr +1) == 'C') {
290001638550SAndrew Gallatin 		/* -C is CX4 */
2901c406ad2eSAndrew Gallatin 		sc->connector = MXGE_CX4;
2902c406ad2eSAndrew Gallatin 		mxge_media_set(sc, IFM_10G_CX4);
2903c406ad2eSAndrew Gallatin 	} else if (*ptr == 'Q') {
290401638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2905c406ad2eSAndrew Gallatin 		sc->connector = MXGE_QRF;
2906c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2907c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2908c406ad2eSAndrew Gallatin 	} else if (*ptr == 'R') {
2909c406ad2eSAndrew Gallatin 		/* -R is XFP */
2910c406ad2eSAndrew Gallatin 		sc->connector = MXGE_XFP;
2911c406ad2eSAndrew Gallatin 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
2912c406ad2eSAndrew Gallatin 		/* -S or -2S is SFP+ */
2913c406ad2eSAndrew Gallatin 		sc->connector = MXGE_SFP;
2914c406ad2eSAndrew Gallatin 	} else {
2915c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2916c406ad2eSAndrew Gallatin 	}
2917c587e59fSAndrew Gallatin }
2918c587e59fSAndrew Gallatin 
2919c406ad2eSAndrew Gallatin /*
2920c406ad2eSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2921c406ad2eSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2922c406ad2eSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2923c406ad2eSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2924c406ad2eSAndrew Gallatin  * than in the interrupt handler itself.
2925c406ad2eSAndrew Gallatin  */
2926c406ad2eSAndrew Gallatin static void
2927c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2928c406ad2eSAndrew Gallatin {
2929c406ad2eSAndrew Gallatin 	mxge_cmd_t cmd;
2930c406ad2eSAndrew Gallatin 	char *cage_type;
2931c406ad2eSAndrew Gallatin 
2932c406ad2eSAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
2933c406ad2eSAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
2934c406ad2eSAndrew Gallatin 	uint32_t byte;
2935c406ad2eSAndrew Gallatin 
2936c406ad2eSAndrew Gallatin 	sc->need_media_probe = 0;
2937c406ad2eSAndrew Gallatin 
2938c406ad2eSAndrew Gallatin 	if (sc->connector == MXGE_XFP) {
293901638550SAndrew Gallatin 		/* -R is XFP */
294001638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
294101638550SAndrew Gallatin 		mxge_media_type_entries =
294273a1170aSPedro F. Giffuni 			nitems(mxge_xfp_media_types);
294301638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
294401638550SAndrew Gallatin 		cage_type = "XFP";
2945c406ad2eSAndrew Gallatin 	} else 	if (sc->connector == MXGE_SFP) {
294601638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
294701638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
294801638550SAndrew Gallatin 		mxge_media_type_entries =
294973a1170aSPedro F. Giffuni 			nitems(mxge_sfp_media_types);
295001638550SAndrew Gallatin 		cage_type = "SFP+";
295101638550SAndrew Gallatin 		byte = 3;
2952c406ad2eSAndrew Gallatin 	} else {
2953c406ad2eSAndrew Gallatin 		/* nothing to do; media type cannot change */
2954c587e59fSAndrew Gallatin 		return;
2955c587e59fSAndrew Gallatin 	}
2956c587e59fSAndrew Gallatin 
2957c587e59fSAndrew Gallatin 	/*
2958c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2959c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2960c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2961c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2962c587e59fSAndrew Gallatin 	 * a millisecond
2963c587e59fSAndrew Gallatin 	 */
2964c587e59fSAndrew Gallatin 
2965c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
296601638550SAndrew Gallatin 	cmd.data1 = byte;
296701638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
296801638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2969c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2970c587e59fSAndrew Gallatin 	}
297101638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
297201638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2973c587e59fSAndrew Gallatin 	}
2974c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2975c587e59fSAndrew Gallatin 		return;
2976c587e59fSAndrew Gallatin 	}
2977c587e59fSAndrew Gallatin 
2978c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
297901638550SAndrew Gallatin 	cmd.data0 = byte;
298001638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2981c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2982c587e59fSAndrew Gallatin 		DELAY(1000);
298301638550SAndrew Gallatin 		cmd.data0 = byte;
298401638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2985c587e59fSAndrew Gallatin 	}
2986c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
298701638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
298801638550SAndrew Gallatin 			      cage_type, err, ms);
2989c587e59fSAndrew Gallatin 		return;
2990c587e59fSAndrew Gallatin 	}
2991c587e59fSAndrew Gallatin 
2992c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2993c587e59fSAndrew Gallatin 		if (mxge_verbose)
299401638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2995c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2996c406ad2eSAndrew Gallatin 		if (sc->current_media != mxge_media_types[0].flag) {
2997c406ad2eSAndrew Gallatin 			mxge_media_init(sc);
2998c406ad2eSAndrew Gallatin 			mxge_media_set(sc, mxge_media_types[0].flag);
2999c406ad2eSAndrew Gallatin 		}
3000c587e59fSAndrew Gallatin 		return;
3001c587e59fSAndrew Gallatin 	}
300201638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
3003c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
3004c587e59fSAndrew Gallatin 			if (mxge_verbose)
300501638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
300601638550SAndrew Gallatin 					      cage_type,
3007c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
3008c587e59fSAndrew Gallatin 
3009c406ad2eSAndrew Gallatin 			if (sc->current_media != mxge_media_types[i].flag) {
3010c406ad2eSAndrew Gallatin 				mxge_media_init(sc);
3011c406ad2eSAndrew Gallatin 				mxge_media_set(sc, mxge_media_types[i].flag);
3012c406ad2eSAndrew Gallatin 			}
3013c587e59fSAndrew Gallatin 			return;
3014c587e59fSAndrew Gallatin 		}
3015c587e59fSAndrew Gallatin 	}
3016c406ad2eSAndrew Gallatin 	if (mxge_verbose)
3017c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "%s media 0x%x unknown\n",
3018c406ad2eSAndrew Gallatin 			      cage_type, cmd.data0);
3019c587e59fSAndrew Gallatin 
3020c587e59fSAndrew Gallatin 	return;
3021c587e59fSAndrew Gallatin }
3022c587e59fSAndrew Gallatin 
3023b2fc195eSAndrew Gallatin static void
30246d87a65dSAndrew Gallatin mxge_intr(void *arg)
3025b2fc195eSAndrew Gallatin {
30261e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
30271e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
30281e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
30291e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
30301e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
30315e7d8541SAndrew Gallatin 	uint32_t send_done_count;
30325e7d8541SAndrew Gallatin 	uint8_t valid;
3033b2fc195eSAndrew Gallatin 
3034c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
30351e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
30361e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
30371e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
30381e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
30391e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
30401e413cf9SAndrew Gallatin 		return;
30411e413cf9SAndrew Gallatin 	}
3042c6cb3e3fSAndrew Gallatin #endif
30431e413cf9SAndrew Gallatin 
30445e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
30455e7d8541SAndrew Gallatin 	if (!stats->valid) {
30465e7d8541SAndrew Gallatin 		return;
3047b2fc195eSAndrew Gallatin 	}
30485e7d8541SAndrew Gallatin 	valid = stats->valid;
3049b2fc195eSAndrew Gallatin 
305091ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
30515e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
30525e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
30535e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
30545e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
30555e7d8541SAndrew Gallatin 			stats->valid = 0;
3056dc8731d4SAndrew Gallatin 	} else {
3057dc8731d4SAndrew Gallatin 		stats->valid = 0;
3058dc8731d4SAndrew Gallatin 	}
3059dc8731d4SAndrew Gallatin 
3060dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
30615e7d8541SAndrew Gallatin 	do {
30625e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
30635e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
30645e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
30655e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
3066c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
30671e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
30681e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
30695e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
3070b2fc195eSAndrew Gallatin 		}
307191ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
307273c7c83fSAndrew Gallatin 			wmb();
30735e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
3074b2fc195eSAndrew Gallatin 
3075c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
3076c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
30775e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
30785e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
3079b2fc195eSAndrew Gallatin 			if (sc->link_state) {
30805e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
30815e7d8541SAndrew Gallatin 				if (mxge_verbose)
30825e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
3083b2fc195eSAndrew Gallatin 			} else {
30845e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
30855e7d8541SAndrew Gallatin 				if (mxge_verbose)
30865e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
3087b2fc195eSAndrew Gallatin 			}
3088c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
3089b2fc195eSAndrew Gallatin 		}
3090b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
30911e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
3092b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
30931e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
30945e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
30955e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
30965e7d8541SAndrew Gallatin 		}
3097c587e59fSAndrew Gallatin 
3098c587e59fSAndrew Gallatin 		if (stats->link_down) {
30995e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3100c587e59fSAndrew Gallatin 			sc->link_state = 0;
3101c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3102c587e59fSAndrew Gallatin 		}
3103b2fc195eSAndrew Gallatin 	}
3104b2fc195eSAndrew Gallatin 
31055e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
31065e7d8541SAndrew Gallatin 	if (valid & 0x1)
31071e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
31081e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3109b2fc195eSAndrew Gallatin }
3110b2fc195eSAndrew Gallatin 
3111b2fc195eSAndrew Gallatin static void
31126d87a65dSAndrew Gallatin mxge_init(void *arg)
3113b2fc195eSAndrew Gallatin {
31143cae7311SAndrew Gallatin 	mxge_softc_t *sc = arg;
31153cae7311SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
31163cae7311SAndrew Gallatin 
31173cae7311SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
31183cae7311SAndrew Gallatin 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
31193cae7311SAndrew Gallatin 		(void) mxge_open(sc);
31203cae7311SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3121b2fc195eSAndrew Gallatin }
3122b2fc195eSAndrew Gallatin 
3123b2fc195eSAndrew Gallatin static void
31241e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
31251e413cf9SAndrew Gallatin {
31261e413cf9SAndrew Gallatin 	int i;
31271e413cf9SAndrew Gallatin 
312826dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
312926dd49c6SAndrew Gallatin 	tcp_lro_free(&ss->lc);
313026dd49c6SAndrew Gallatin #endif
31311e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
31321e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
31331e413cf9SAndrew Gallatin 			continue;
31341e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
31351e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
31361e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
31371e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
31381e413cf9SAndrew Gallatin 	}
31391e413cf9SAndrew Gallatin 
31401e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
31411e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
31421e413cf9SAndrew Gallatin 			continue;
31431e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
31441e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
31451e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
31461e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
31471e413cf9SAndrew Gallatin 	}
31481e413cf9SAndrew Gallatin 
31491e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
31501e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
31511e413cf9SAndrew Gallatin 		return;
31521e413cf9SAndrew Gallatin 
31531e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
31541e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
31551e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
31561e413cf9SAndrew Gallatin 			continue;
31571e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
31581e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
31591e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
31601e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
31611e413cf9SAndrew Gallatin 	}
31621e413cf9SAndrew Gallatin }
31631e413cf9SAndrew Gallatin 
31641e413cf9SAndrew Gallatin static void
31656d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3166b2fc195eSAndrew Gallatin {
31671e413cf9SAndrew Gallatin 	int slice;
31681e413cf9SAndrew Gallatin 
31691e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31701e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
31711e413cf9SAndrew Gallatin }
31721e413cf9SAndrew Gallatin 
31731e413cf9SAndrew Gallatin static void
31741e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
31751e413cf9SAndrew Gallatin {
3176b2fc195eSAndrew Gallatin 	int i;
3177b2fc195eSAndrew Gallatin 
31781e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
31791e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
31801e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3181b2fc195eSAndrew Gallatin 
31821e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
31831e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
31841e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
31851e413cf9SAndrew Gallatin 
31861e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
31871e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
31881e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
31891e413cf9SAndrew Gallatin 
31901e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
31911e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
31921e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
31931e413cf9SAndrew Gallatin 
31941e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
31951e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
31961e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
31971e413cf9SAndrew Gallatin 
31981e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
31991e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
32001e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
32011e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
32021e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3203b2fc195eSAndrew Gallatin 			}
32041e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
32051e413cf9SAndrew Gallatin 		}
32061e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
32071e413cf9SAndrew Gallatin 	}
32081e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
32091e413cf9SAndrew Gallatin 
32101e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
32111e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
32121e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
32131e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
32141e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
32151e413cf9SAndrew Gallatin 			}
32161e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
32171e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
32181e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
32191e413cf9SAndrew Gallatin 		}
32201e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
32211e413cf9SAndrew Gallatin 	}
32221e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
32231e413cf9SAndrew Gallatin 
32241e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
32251e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
32261e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
32271e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
32281e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
32291e413cf9SAndrew Gallatin 			}
32301e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
32311e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
32321e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
32331e413cf9SAndrew Gallatin 		}
32341e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
32351e413cf9SAndrew Gallatin 	}
32361e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3237b2fc195eSAndrew Gallatin }
3238b2fc195eSAndrew Gallatin 
3239b2fc195eSAndrew Gallatin static void
32406d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3241b2fc195eSAndrew Gallatin {
32421e413cf9SAndrew Gallatin 	int slice;
3243b2fc195eSAndrew Gallatin 
32441e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
32451e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3246c2657176SAndrew Gallatin }
3247b2fc195eSAndrew Gallatin 
3248b2fc195eSAndrew Gallatin static int
32491e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
32501e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3251b2fc195eSAndrew Gallatin {
32521e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
32531e413cf9SAndrew Gallatin 	size_t bytes;
32541e413cf9SAndrew Gallatin 	int err, i;
3255b2fc195eSAndrew Gallatin 
32561e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3257adae7080SAndrew Gallatin 
32581e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
32591e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3260aed8e389SAndrew Gallatin 
3261b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
32621e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
32631e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3264b2fc195eSAndrew Gallatin 
32651e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
32661e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3267b2fc195eSAndrew Gallatin 
32681e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
32691e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
32701e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3271b2fc195eSAndrew Gallatin 
32721e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
32731e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3274b2fc195eSAndrew Gallatin 
32751e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3276b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3277b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3278b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3279b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3280b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3281b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3282b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3283b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3284b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3285b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3286b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32871e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3288b2fc195eSAndrew Gallatin 	if (err != 0) {
3289b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3290b2fc195eSAndrew Gallatin 			      err);
3291c2ede4b3SMartin Blapp 		return err;
3292b2fc195eSAndrew Gallatin 	}
3293b2fc195eSAndrew Gallatin 
3294b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3295b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3296b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3297b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3298b0f7b922SAndrew Gallatin #else
3299b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3300b0f7b922SAndrew Gallatin #endif
3301b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3302b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3303b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3304053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3305b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3306053e637fSAndrew Gallatin 				 3,			/* num segs */
3307b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3308b0f7b922SAndrew Gallatin #else
3309b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3310b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3311b0f7b922SAndrew Gallatin #endif
3312b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3313b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
33141e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3315b2fc195eSAndrew Gallatin 	if (err != 0) {
3316b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3317b2fc195eSAndrew Gallatin 			      err);
3318c2ede4b3SMartin Blapp 		return err;
3319b2fc195eSAndrew Gallatin 	}
33201e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
33211e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
33221e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3323b2fc195eSAndrew Gallatin 		if (err != 0) {
3324b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3325b2fc195eSAndrew Gallatin 				      err);
3326c2ede4b3SMartin Blapp 			return err;
3327b2fc195eSAndrew Gallatin 		}
3328b2fc195eSAndrew Gallatin 	}
33291e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
33301e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3331b2fc195eSAndrew Gallatin 	if (err != 0) {
3332b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3333b2fc195eSAndrew Gallatin 			      err);
3334c2ede4b3SMartin Blapp 		return err;
3335b2fc195eSAndrew Gallatin 	}
3336b2fc195eSAndrew Gallatin 
33371e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
33381e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
33391e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3340b2fc195eSAndrew Gallatin 		if (err != 0) {
3341b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3342b2fc195eSAndrew Gallatin 				      err);
3343c2ede4b3SMartin Blapp 			return err;
3344b2fc195eSAndrew Gallatin 		}
3345b2fc195eSAndrew Gallatin 	}
33461e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
33471e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3348b2fc195eSAndrew Gallatin 	if (err != 0) {
3349b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3350b2fc195eSAndrew Gallatin 			      err);
3351c2ede4b3SMartin Blapp 		return err;
33521e413cf9SAndrew Gallatin 	}
33531e413cf9SAndrew Gallatin 
3354b78540b1SGabor Kovesdan 	/* now allocate TX resources */
33551e413cf9SAndrew Gallatin 
3356c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
33571e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
33581e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
33591e413cf9SAndrew Gallatin 		return 0;
3360c6cb3e3fSAndrew Gallatin #endif
33611e413cf9SAndrew Gallatin 
33621e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
33631e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
33641e413cf9SAndrew Gallatin 
33651e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
33661e413cf9SAndrew Gallatin 	bytes = 8 +
33671e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
33681e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
33691e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
33701e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
3371aa54c242SJohn Baldwin 		((uintptr_t)(ss->tx.req_bytes + 7) & ~7UL);
33721e413cf9SAndrew Gallatin 
33731e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
33741e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
33751e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
33761e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
33771e413cf9SAndrew Gallatin 
33781e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
33791e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
33801e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
33811e413cf9SAndrew Gallatin 
33821e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
33831e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
33841e413cf9SAndrew Gallatin 				 1,			/* alignment */
33851e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
33861e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
33871e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
33881e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
33891e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
33901e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
33911e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
33921e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
33931e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
33941e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
33951e413cf9SAndrew Gallatin 
33961e413cf9SAndrew Gallatin 	if (err != 0) {
33971e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
33981e413cf9SAndrew Gallatin 			      err);
3399c2ede4b3SMartin Blapp 		return err;
34001e413cf9SAndrew Gallatin 	}
34011e413cf9SAndrew Gallatin 
34021e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
34031e413cf9SAndrew Gallatin 	   in the ring */
34041e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
34051e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
34061e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
34071e413cf9SAndrew Gallatin 		if (err != 0) {
34081e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
34091e413cf9SAndrew Gallatin 				      err);
3410c2ede4b3SMartin Blapp 			return err;
34111e413cf9SAndrew Gallatin 		}
3412b2fc195eSAndrew Gallatin 	}
3413b2fc195eSAndrew Gallatin 	return 0;
3414b2fc195eSAndrew Gallatin 
3415b2fc195eSAndrew Gallatin }
3416b2fc195eSAndrew Gallatin 
34171e413cf9SAndrew Gallatin static int
34181e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
34191e413cf9SAndrew Gallatin {
34201e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
34211e413cf9SAndrew Gallatin 	int tx_ring_size;
34221e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
34231e413cf9SAndrew Gallatin 	int err, slice;
34241e413cf9SAndrew Gallatin 
34251e413cf9SAndrew Gallatin 	/* get ring sizes */
34261e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
34271e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
34281e413cf9SAndrew Gallatin 	if (err != 0) {
34291e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
34301e413cf9SAndrew Gallatin 		goto abort;
34311e413cf9SAndrew Gallatin 	}
34321e413cf9SAndrew Gallatin 
34331e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
34341e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
34351e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
34361e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
34371e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
34381e413cf9SAndrew Gallatin 
34391e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
34401e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
34411e413cf9SAndrew Gallatin 					     rx_ring_entries,
34421e413cf9SAndrew Gallatin 					     tx_ring_entries);
34431e413cf9SAndrew Gallatin 		if (err != 0)
34441e413cf9SAndrew Gallatin 			goto abort;
34451e413cf9SAndrew Gallatin 	}
34461e413cf9SAndrew Gallatin 	return 0;
34471e413cf9SAndrew Gallatin 
34481e413cf9SAndrew Gallatin abort:
34491e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
34501e413cf9SAndrew Gallatin 	return err;
34511e413cf9SAndrew Gallatin 
34521e413cf9SAndrew Gallatin }
34531e413cf9SAndrew Gallatin 
3454053e637fSAndrew Gallatin static void
3455053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3456053e637fSAndrew Gallatin {
3457c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3458053e637fSAndrew Gallatin 
3459053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3460053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3461053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3462053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3463053e637fSAndrew Gallatin 		*nbufs = 1;
3464053e637fSAndrew Gallatin 		return;
3465053e637fSAndrew Gallatin 	}
3466053e637fSAndrew Gallatin 
3467053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3468053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3469053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3470053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3471053e637fSAndrew Gallatin 		*nbufs = 1;
3472053e637fSAndrew Gallatin 		return;
3473053e637fSAndrew Gallatin 	}
3474b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3475053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3476053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3477053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3478053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3479053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3480053e637fSAndrew Gallatin 	if (*nbufs == 3)
3481053e637fSAndrew Gallatin 		*nbufs = 4;
3482b0f7b922SAndrew Gallatin #else
3483b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3484b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3485b0f7b922SAndrew Gallatin 	*nbufs = 1;
3486b0f7b922SAndrew Gallatin #endif
3487053e637fSAndrew Gallatin }
3488053e637fSAndrew Gallatin 
3489b2fc195eSAndrew Gallatin static int
34901e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3491b2fc195eSAndrew Gallatin {
34921e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
34936d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3494b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
34951e413cf9SAndrew Gallatin 	int err, i, slice;
3496b2fc195eSAndrew Gallatin 
34971e413cf9SAndrew Gallatin 	sc = ss->sc;
34981e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
34991e413cf9SAndrew Gallatin 
350026dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
350126dd49c6SAndrew Gallatin 	(void)tcp_lro_init(&ss->lc);
350226dd49c6SAndrew Gallatin #endif
350326dd49c6SAndrew Gallatin 	ss->lc.ifp = sc->ifp;
3504053e637fSAndrew Gallatin 
35051e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
35061e413cf9SAndrew Gallatin 
35071e413cf9SAndrew Gallatin 	err = 0;
3508c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35091e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
35101e413cf9SAndrew Gallatin 	if (slice == 0) {
3511c6cb3e3fSAndrew Gallatin #endif
35121e413cf9SAndrew Gallatin 		cmd.data0 = slice;
35131e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
35141e413cf9SAndrew Gallatin 		ss->tx.lanai =
35151e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3516c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3517c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3518c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3519c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3520c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35211e413cf9SAndrew Gallatin 	}
3522c6cb3e3fSAndrew Gallatin #endif
35231e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35241e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
35251e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
35261e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
35271e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35281e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35291e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
35301e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
35311e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35321e413cf9SAndrew Gallatin 
35331e413cf9SAndrew Gallatin 	if (err != 0) {
35341e413cf9SAndrew Gallatin 		device_printf(sc->dev,
35351e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
35361e413cf9SAndrew Gallatin 		return EIO;
35371e413cf9SAndrew Gallatin 	}
35381e413cf9SAndrew Gallatin 
35391e413cf9SAndrew Gallatin 	/* stock receive rings */
35401e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
35411e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
35421e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
35431e413cf9SAndrew Gallatin 		if (err) {
35441e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
35451e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
35461e413cf9SAndrew Gallatin 			return ENOMEM;
35471e413cf9SAndrew Gallatin 		}
35481e413cf9SAndrew Gallatin 	}
35491e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
35501e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
35511e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
35521e413cf9SAndrew Gallatin 	}
35531e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
35541e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
35554d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
35564d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
35571e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
35581e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
35591e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
35601e413cf9SAndrew Gallatin 		if (err) {
35611e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
35621e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
35631e413cf9SAndrew Gallatin 			return ENOMEM;
35641e413cf9SAndrew Gallatin 		}
35651e413cf9SAndrew Gallatin 	}
35661e413cf9SAndrew Gallatin 	return 0;
35671e413cf9SAndrew Gallatin }
35681e413cf9SAndrew Gallatin 
35691e413cf9SAndrew Gallatin static int
35701e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
35711e413cf9SAndrew Gallatin {
35721e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
35731e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
35741e413cf9SAndrew Gallatin 	bus_addr_t bus;
35751e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3576c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3577b2fc195eSAndrew Gallatin 
35787d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
35797d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
35807d542e2dSAndrew Gallatin 
3581adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3582b2fc195eSAndrew Gallatin 	if (err != 0) {
3583b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3584b2fc195eSAndrew Gallatin 		return EIO;
3585b2fc195eSAndrew Gallatin 	}
3586b2fc195eSAndrew Gallatin 
35871e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
35881e413cf9SAndrew Gallatin 		/* setup the indirection table */
35891e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
35901e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
35911e413cf9SAndrew Gallatin 				    &cmd);
3592b2fc195eSAndrew Gallatin 
35931e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
35941e413cf9SAndrew Gallatin 				     &cmd);
35951e413cf9SAndrew Gallatin 		if (err != 0) {
35961e413cf9SAndrew Gallatin 			device_printf(sc->dev,
35971e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
35981e413cf9SAndrew Gallatin 			return err;
35991e413cf9SAndrew Gallatin 		}
36001e413cf9SAndrew Gallatin 
36011e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
36021e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
36031e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
36041e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
36051e413cf9SAndrew Gallatin 
36061e413cf9SAndrew Gallatin 		cmd.data0 = 1;
36071e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
36081e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
36091e413cf9SAndrew Gallatin 		if (err != 0) {
36101e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
36111e413cf9SAndrew Gallatin 			return err;
36121e413cf9SAndrew Gallatin 		}
36131e413cf9SAndrew Gallatin 	}
36141e413cf9SAndrew Gallatin 
36151e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
36161e413cf9SAndrew Gallatin 
36171e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3618053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3619053e637fSAndrew Gallatin 			    &cmd);
3620053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3621053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
36221e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3623053e637fSAndrew Gallatin 		device_printf(sc->dev,
3624053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
36251e413cf9SAndrew Gallatin 			      nbufs);
3626053e637fSAndrew Gallatin 		return EIO;
3627053e637fSAndrew Gallatin 	}
3628b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3629b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3630b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3631c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
36325e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3633b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
36345e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3635b2fc195eSAndrew Gallatin 			     &cmd);
3636053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
36375e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
36380fa7f681SAndrew Gallatin 
36390fa7f681SAndrew Gallatin 	if (err != 0) {
36400fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
36410fa7f681SAndrew Gallatin 		goto abort;
36420fa7f681SAndrew Gallatin 	}
36430fa7f681SAndrew Gallatin 
3644b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3645c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3646c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3647c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3648c6cb3e3fSAndrew Gallatin #else
3649c6cb3e3fSAndrew Gallatin 	     slice < 1;
3650c6cb3e3fSAndrew Gallatin #endif
3651c6cb3e3fSAndrew Gallatin 	     slice++) {
3652c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3653c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3654c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3655c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3656c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
36570fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3658c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3659c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3660c6cb3e3fSAndrew Gallatin 	}
36610fa7f681SAndrew Gallatin 
36620fa7f681SAndrew Gallatin 	if (err != 0) {
36631e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
36640fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
36650fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
36660fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
36670fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
36680fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
36690fa7f681SAndrew Gallatin 				    &cmd);
36700fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
36710fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
36720fa7f681SAndrew Gallatin 	} else {
36730fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
36740fa7f681SAndrew Gallatin 	}
3675b2fc195eSAndrew Gallatin 
3676b2fc195eSAndrew Gallatin 	if (err != 0) {
3677b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3678b2fc195eSAndrew Gallatin 		goto abort;
3679b2fc195eSAndrew Gallatin 	}
3680b2fc195eSAndrew Gallatin 
36811e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
36821e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
36831e413cf9SAndrew Gallatin 		if (err != 0) {
36841e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
36851e413cf9SAndrew Gallatin 				      slice);
36861e413cf9SAndrew Gallatin 			goto abort;
36871e413cf9SAndrew Gallatin 		}
36881e413cf9SAndrew Gallatin 	}
36891e413cf9SAndrew Gallatin 
3690b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
36915e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3692b2fc195eSAndrew Gallatin 	if (err) {
3693b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3694b2fc195eSAndrew Gallatin 		goto abort;
3695b2fc195eSAndrew Gallatin 	}
3696c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3697c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3698c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3699c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3700c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3701c6cb3e3fSAndrew Gallatin 	}
3702c6cb3e3fSAndrew Gallatin #endif
3703b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3704b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3705b2fc195eSAndrew Gallatin 
3706b2fc195eSAndrew Gallatin 	return 0;
3707b2fc195eSAndrew Gallatin 
3708b2fc195eSAndrew Gallatin abort:
37096d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3710a98d6cd7SAndrew Gallatin 
3711b2fc195eSAndrew Gallatin 	return err;
3712b2fc195eSAndrew Gallatin }
3713b2fc195eSAndrew Gallatin 
3714b2fc195eSAndrew Gallatin static int
3715a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3716b2fc195eSAndrew Gallatin {
37176d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3718b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3719c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3720c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3721c6cb3e3fSAndrew Gallatin 	int slice;
3722c6cb3e3fSAndrew Gallatin #endif
3723b2fc195eSAndrew Gallatin 
3724c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3725c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3726c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3727c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3728c6cb3e3fSAndrew Gallatin 	}
3729c6cb3e3fSAndrew Gallatin #endif
3730b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3731a393336bSAndrew Gallatin 	if (!down) {
3732b2fc195eSAndrew Gallatin 		old_down_cnt = sc->down_cnt;
373373c7c83fSAndrew Gallatin 		wmb();
37345e7d8541SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3735b2fc195eSAndrew Gallatin 		if (err) {
3736a393336bSAndrew Gallatin 			device_printf(sc->dev,
3737a393336bSAndrew Gallatin 				      "Couldn't bring down link\n");
3738b2fc195eSAndrew Gallatin 		}
3739b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3740b2fc195eSAndrew Gallatin 			/* wait for down irq */
3741dce01b9bSAndrew Gallatin 			DELAY(10 * sc->intr_coal_delay);
3742b2fc195eSAndrew Gallatin 		}
374373c7c83fSAndrew Gallatin 		wmb();
3744b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3745b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "never got down irq\n");
3746b2fc195eSAndrew Gallatin 		}
3747a393336bSAndrew Gallatin 	}
37486d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3749a98d6cd7SAndrew Gallatin 
3750b2fc195eSAndrew Gallatin 	return 0;
3751b2fc195eSAndrew Gallatin }
3752b2fc195eSAndrew Gallatin 
3753dce01b9bSAndrew Gallatin static void
3754dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3755dce01b9bSAndrew Gallatin {
3756dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3757dce01b9bSAndrew Gallatin 	int reg;
3758c68534f1SScott Long 	uint16_t lnk, pectl;
3759dce01b9bSAndrew Gallatin 
3760dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
37613b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
3762dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3763dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3764dce01b9bSAndrew Gallatin 
376583d54b59SAndrew Gallatin 		if (sc->pectl == 0) {
3766dce01b9bSAndrew Gallatin 			pectl = pci_read_config(dev, reg + 0x8, 2);
3767dce01b9bSAndrew Gallatin 			pectl = (pectl & ~0x7000) | (5 << 12);
3768dce01b9bSAndrew Gallatin 			pci_write_config(dev, reg + 0x8, pectl, 2);
376983d54b59SAndrew Gallatin 			sc->pectl = pectl;
377083d54b59SAndrew Gallatin 		} else {
377183d54b59SAndrew Gallatin 			/* restore saved pectl after watchdog reset */
377283d54b59SAndrew Gallatin 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
377383d54b59SAndrew Gallatin 		}
3774dce01b9bSAndrew Gallatin 	}
3775dce01b9bSAndrew Gallatin 
3776dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3777dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3778dce01b9bSAndrew Gallatin }
3779dce01b9bSAndrew Gallatin 
3780dce01b9bSAndrew Gallatin static uint32_t
3781dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3782dce01b9bSAndrew Gallatin {
3783dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3784dce01b9bSAndrew Gallatin 	uint32_t vs;
3785dce01b9bSAndrew Gallatin 
3786dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
37873b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) {
3788dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3789dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3790dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3791dce01b9bSAndrew Gallatin 	}
3792dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3793dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3794dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3795dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3796dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3797dce01b9bSAndrew Gallatin }
3798dce01b9bSAndrew Gallatin 
379972c042dfSAndrew Gallatin static void
380072c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3801dce01b9bSAndrew Gallatin {
3802e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3803a393336bSAndrew Gallatin 	struct mxge_slice_state *ss;
3804a393336bSAndrew Gallatin 	int err, running, s, num_tx_slices = 1;
3805dce01b9bSAndrew Gallatin 	uint32_t reboot;
3806dce01b9bSAndrew Gallatin 	uint16_t cmd;
3807dce01b9bSAndrew Gallatin 
3808dce01b9bSAndrew Gallatin 	err = ENXIO;
3809dce01b9bSAndrew Gallatin 
3810dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3811dce01b9bSAndrew Gallatin 
3812dce01b9bSAndrew Gallatin 	/*
3813dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3814dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3815dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3816dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3817dce01b9bSAndrew Gallatin 	 * again
3818dce01b9bSAndrew Gallatin 	 */
3819dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3820dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3821dce01b9bSAndrew Gallatin 		/*
3822dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3823dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3824dce01b9bSAndrew Gallatin 		 * back, then give up
3825dce01b9bSAndrew Gallatin 		 */
3826dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3827dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3828dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3829dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3830dce01b9bSAndrew Gallatin 		}
3831dce01b9bSAndrew Gallatin 	}
3832dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3833dce01b9bSAndrew Gallatin 		/* print the reboot status */
3834dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3835dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3836dce01b9bSAndrew Gallatin 			      reboot);
3837a393336bSAndrew Gallatin 		running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
3838a393336bSAndrew Gallatin 		if (running) {
3839a393336bSAndrew Gallatin 			/*
3840a393336bSAndrew Gallatin 			 * quiesce NIC so that TX routines will not try to
3841a393336bSAndrew Gallatin 			 * xmit after restoration of BAR
3842a393336bSAndrew Gallatin 			 */
3843a393336bSAndrew Gallatin 
3844a393336bSAndrew Gallatin 			/* Mark the link as down */
3845a393336bSAndrew Gallatin 			if (sc->link_state) {
3846a393336bSAndrew Gallatin 				sc->link_state = 0;
3847a393336bSAndrew Gallatin 				if_link_state_change(sc->ifp,
3848a393336bSAndrew Gallatin 						     LINK_STATE_DOWN);
3849a393336bSAndrew Gallatin 			}
3850a393336bSAndrew Gallatin #ifdef IFNET_BUF_RING
3851a393336bSAndrew Gallatin 			num_tx_slices = sc->num_slices;
3852a393336bSAndrew Gallatin #endif
3853a393336bSAndrew Gallatin 			/* grab all TX locks to ensure no tx  */
3854a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3855a393336bSAndrew Gallatin 				ss = &sc->ss[s];
3856a393336bSAndrew Gallatin 				mtx_lock(&ss->tx.mtx);
3857a393336bSAndrew Gallatin 			}
3858a393336bSAndrew Gallatin 			mxge_close(sc, 1);
3859a393336bSAndrew Gallatin 		}
3860dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3861e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3862e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3863dce01b9bSAndrew Gallatin 
3864dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3865dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
386610882804SAndrew Gallatin 
3867a393336bSAndrew Gallatin 		/* reload f/w */
3868a393336bSAndrew Gallatin 		err = mxge_load_firmware(sc, 0);
3869a393336bSAndrew Gallatin 		if (err) {
3870a393336bSAndrew Gallatin 			device_printf(sc->dev,
3871a393336bSAndrew Gallatin 				      "Unable to re-load f/w\n");
387210882804SAndrew Gallatin 		}
3873a393336bSAndrew Gallatin 		if (running) {
3874a393336bSAndrew Gallatin 			if (!err)
3875a393336bSAndrew Gallatin 				err = mxge_open(sc);
3876a393336bSAndrew Gallatin 			/* release all TX locks */
3877a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3878a393336bSAndrew Gallatin 				ss = &sc->ss[s];
387983d54b59SAndrew Gallatin #ifdef IFNET_BUF_RING
388083d54b59SAndrew Gallatin 				mxge_start_locked(ss);
388183d54b59SAndrew Gallatin #endif
3882a393336bSAndrew Gallatin 				mtx_unlock(&ss->tx.mtx);
3883a393336bSAndrew Gallatin 			}
3884a393336bSAndrew Gallatin 		}
3885a393336bSAndrew Gallatin 		sc->watchdog_resets++;
3886dce01b9bSAndrew Gallatin 	} else {
3887c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
388872c042dfSAndrew Gallatin 			      "NIC did not reboot, not resetting\n");
388972c042dfSAndrew Gallatin 		err = 0;
389072c042dfSAndrew Gallatin 	}
389172c042dfSAndrew Gallatin 	if (err) {
389272c042dfSAndrew Gallatin 		device_printf(sc->dev, "watchdog reset failed\n");
389372c042dfSAndrew Gallatin 	} else {
38946b484a49SAndrew Gallatin 		if (sc->dying == 2)
38956b484a49SAndrew Gallatin 			sc->dying = 0;
38966b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
389772c042dfSAndrew Gallatin 	}
389872c042dfSAndrew Gallatin }
389972c042dfSAndrew Gallatin 
390072c042dfSAndrew Gallatin static void
390172c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
390272c042dfSAndrew Gallatin {
390372c042dfSAndrew Gallatin 	mxge_softc_t *sc = arg;
390472c042dfSAndrew Gallatin 
390572c042dfSAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
390672c042dfSAndrew Gallatin 	mxge_watchdog_reset(sc);
390772c042dfSAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
390872c042dfSAndrew Gallatin }
390972c042dfSAndrew Gallatin 
391072c042dfSAndrew Gallatin static void
391172c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
391272c042dfSAndrew Gallatin {
391372c042dfSAndrew Gallatin 	tx = &sc->ss[slice].tx;
391472c042dfSAndrew Gallatin 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3915c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev,
3916c6cb3e3fSAndrew Gallatin 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3917c6cb3e3fSAndrew Gallatin 		      tx->req, tx->done, tx->queue_active);
3918c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3919c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3920dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3921c6cb3e3fSAndrew Gallatin 		      tx->pkt_done,
39221e413cf9SAndrew Gallatin 		      be32toh(sc->ss->fw_stats->send_done_count));
3923dce01b9bSAndrew Gallatin }
3924dce01b9bSAndrew Gallatin 
3925e749ef6bSAndrew Gallatin static int
3926dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3927dce01b9bSAndrew Gallatin {
3928c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
39291e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3930c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3931dce01b9bSAndrew Gallatin 
3932dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3933dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3934c6cb3e3fSAndrew Gallatin 	for (i = 0;
3935c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3936c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
3937c6cb3e3fSAndrew Gallatin #else
3938c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
3939c6cb3e3fSAndrew Gallatin #endif
3940c6cb3e3fSAndrew Gallatin 	     i++) {
3941c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3942dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3943dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3944c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3945c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
394672c042dfSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause) {
394772c042dfSAndrew Gallatin 				mxge_warn_stuck(sc, tx, i);
394872c042dfSAndrew Gallatin 				taskqueue_enqueue(sc->tq, &sc->watchdog_task);
394972c042dfSAndrew Gallatin 				return (ENXIO);
395072c042dfSAndrew Gallatin 			}
3951c587e59fSAndrew Gallatin 			else
3952c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3953c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3954c587e59fSAndrew Gallatin 		}
3955dce01b9bSAndrew Gallatin 
3956dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3957dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3958c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3959c6cb3e3fSAndrew Gallatin 	}
3960c587e59fSAndrew Gallatin 
3961c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3962c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3963e749ef6bSAndrew Gallatin 	return (err);
3964dce01b9bSAndrew Gallatin }
3965dce01b9bSAndrew Gallatin 
3966f3f040d9SGleb Smirnoff static uint64_t
3967f3f040d9SGleb Smirnoff mxge_get_counter(struct ifnet *ifp, ift_counter cnt)
39681e413cf9SAndrew Gallatin {
3969f3f040d9SGleb Smirnoff 	struct mxge_softc *sc;
3970f3f040d9SGleb Smirnoff 	uint64_t rv;
39711e413cf9SAndrew Gallatin 
3972f3f040d9SGleb Smirnoff 	sc = if_getsoftc(ifp);
3973f3f040d9SGleb Smirnoff 	rv = 0;
3974f3f040d9SGleb Smirnoff 
3975f3f040d9SGleb Smirnoff 	switch (cnt) {
3976f3f040d9SGleb Smirnoff 	case IFCOUNTER_IPACKETS:
3977f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3978f3f040d9SGleb Smirnoff 			rv += sc->ss[s].ipackets;
3979f3f040d9SGleb Smirnoff 		return (rv);
3980f3f040d9SGleb Smirnoff 	case IFCOUNTER_OPACKETS:
3981f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3982f3f040d9SGleb Smirnoff 			rv += sc->ss[s].opackets;
3983f3f040d9SGleb Smirnoff 		return (rv);
3984f3f040d9SGleb Smirnoff 	case IFCOUNTER_OERRORS:
3985f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3986f3f040d9SGleb Smirnoff 			rv += sc->ss[s].oerrors;
3987f3f040d9SGleb Smirnoff 		return (rv);
398871032832SAndrew Gallatin #ifdef IFNET_BUF_RING
3989f3f040d9SGleb Smirnoff 	case IFCOUNTER_OBYTES:
3990f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3991f3f040d9SGleb Smirnoff 			rv += sc->ss[s].obytes;
3992f3f040d9SGleb Smirnoff 		return (rv);
3993f3f040d9SGleb Smirnoff 	case IFCOUNTER_OMCASTS:
3994f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3995f3f040d9SGleb Smirnoff 			rv += sc->ss[s].omcasts;
3996f3f040d9SGleb Smirnoff 		return (rv);
3997f3f040d9SGleb Smirnoff 	case IFCOUNTER_OQDROPS:
3998f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3999f3f040d9SGleb Smirnoff 			rv += sc->ss[s].tx.br->br_drops;
4000f3f040d9SGleb Smirnoff 		return (rv);
400171032832SAndrew Gallatin #endif
4002f3f040d9SGleb Smirnoff 	default:
4003f3f040d9SGleb Smirnoff 		return (if_get_counter_default(ifp, cnt));
40041e413cf9SAndrew Gallatin 	}
40051e413cf9SAndrew Gallatin }
4006c6cb3e3fSAndrew Gallatin 
40071e413cf9SAndrew Gallatin static void
4008dce01b9bSAndrew Gallatin mxge_tick(void *arg)
4009dce01b9bSAndrew Gallatin {
4010dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
40116b484a49SAndrew Gallatin 	u_long pkts = 0;
4012e749ef6bSAndrew Gallatin 	int err = 0;
40136b484a49SAndrew Gallatin 	int running, ticks;
40146b484a49SAndrew Gallatin 	uint16_t cmd;
4015dce01b9bSAndrew Gallatin 
40166b484a49SAndrew Gallatin 	ticks = mxge_ticks;
40176b484a49SAndrew Gallatin 	running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
40186b484a49SAndrew Gallatin 	if (running) {
40191e413cf9SAndrew Gallatin 		if (!sc->watchdog_countdown) {
4020e749ef6bSAndrew Gallatin 			err = mxge_watchdog(sc);
40211e413cf9SAndrew Gallatin 			sc->watchdog_countdown = 4;
40221e413cf9SAndrew Gallatin 		}
40231e413cf9SAndrew Gallatin 		sc->watchdog_countdown--;
40246b484a49SAndrew Gallatin 	}
40256b484a49SAndrew Gallatin 	if (pkts == 0) {
40266b484a49SAndrew Gallatin 		/* ensure NIC did not suffer h/w fault while idle */
40276b484a49SAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
40286b484a49SAndrew Gallatin 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
40296b484a49SAndrew Gallatin 			sc->dying = 2;
40306b484a49SAndrew Gallatin 			taskqueue_enqueue(sc->tq, &sc->watchdog_task);
40316b484a49SAndrew Gallatin 			err = ENXIO;
40326b484a49SAndrew Gallatin 		}
40336b484a49SAndrew Gallatin 		/* look less often if NIC is idle */
40346b484a49SAndrew Gallatin 		ticks *= 4;
40356b484a49SAndrew Gallatin 	}
40366b484a49SAndrew Gallatin 
4037e749ef6bSAndrew Gallatin 	if (err == 0)
40386b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
4039e749ef6bSAndrew Gallatin 
4040dce01b9bSAndrew Gallatin }
4041b2fc195eSAndrew Gallatin 
4042b2fc195eSAndrew Gallatin static int
40436d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
4044b2fc195eSAndrew Gallatin {
4045b2fc195eSAndrew Gallatin 	return EINVAL;
4046b2fc195eSAndrew Gallatin }
4047b2fc195eSAndrew Gallatin 
4048b2fc195eSAndrew Gallatin static int
40496d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
4050b2fc195eSAndrew Gallatin {
4051b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
4052b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
4053b2fc195eSAndrew Gallatin 	int err = 0;
4054b2fc195eSAndrew Gallatin 
4055c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
4056053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
4057b2fc195eSAndrew Gallatin 		return EINVAL;
4058a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4059b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
4060b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
4061b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4062a393336bSAndrew Gallatin 		mxge_close(sc, 0);
40636d87a65dSAndrew Gallatin 		err = mxge_open(sc);
4064b2fc195eSAndrew Gallatin 		if (err != 0) {
4065b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
4066a393336bSAndrew Gallatin 			mxge_close(sc, 0);
40676d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
4068b2fc195eSAndrew Gallatin 		}
4069b2fc195eSAndrew Gallatin 	}
4070a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4071b2fc195eSAndrew Gallatin 	return err;
4072b2fc195eSAndrew Gallatin }
4073b2fc195eSAndrew Gallatin 
4074b2fc195eSAndrew Gallatin static void
40756d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
4076b2fc195eSAndrew Gallatin {
40776d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4078b2fc195eSAndrew Gallatin 
4079b2fc195eSAndrew Gallatin 	if (sc == NULL)
4080b2fc195eSAndrew Gallatin 		return;
4081b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
4082c406ad2eSAndrew Gallatin 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
4083c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
4084c406ad2eSAndrew Gallatin 	ifmr->ifm_active |= sc->current_media;
4085b2fc195eSAndrew Gallatin }
4086b2fc195eSAndrew Gallatin 
4087b2fc195eSAndrew Gallatin static int
4088df131e84SAndrew Gallatin mxge_fetch_i2c(mxge_softc_t *sc, struct ifi2creq *i2c)
4089df131e84SAndrew Gallatin {
4090df131e84SAndrew Gallatin 	mxge_cmd_t cmd;
4091df131e84SAndrew Gallatin 	uint32_t i2c_args;
4092df131e84SAndrew Gallatin 	int i, ms, err;
4093df131e84SAndrew Gallatin 
4094df131e84SAndrew Gallatin 	if (i2c->dev_addr != 0xA0 &&
4095df131e84SAndrew Gallatin 	    i2c->dev_addr != 0xA2)
4096df131e84SAndrew Gallatin 		return (EINVAL);
4097df131e84SAndrew Gallatin 	if (i2c->len > sizeof(i2c->data))
4098df131e84SAndrew Gallatin 		return (EINVAL);
4099df131e84SAndrew Gallatin 
4100df131e84SAndrew Gallatin 	for (i = 0; i < i2c->len; i++) {
4101df131e84SAndrew Gallatin 		i2c_args = i2c->dev_addr << 0x8;
4102df131e84SAndrew Gallatin 		i2c_args |= i2c->offset + i;
4103df131e84SAndrew Gallatin 		cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
4104df131e84SAndrew Gallatin 		cmd.data1 = i2c_args;
4105df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
4106df131e84SAndrew Gallatin 
4107df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
4108df131e84SAndrew Gallatin 			return (EIO);
4109df131e84SAndrew Gallatin 		/* now we wait for the data to be cached */
4110df131e84SAndrew Gallatin 		cmd.data0 = i2c_args & 0xff;
4111df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
4112df131e84SAndrew Gallatin 		for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
4113df131e84SAndrew Gallatin 			cmd.data0 = i2c_args & 0xff;
4114df131e84SAndrew Gallatin 			err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
4115df131e84SAndrew Gallatin 			if (err == EBUSY)
4116df131e84SAndrew Gallatin 				DELAY(1000);
4117df131e84SAndrew Gallatin 		}
4118df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
4119df131e84SAndrew Gallatin 			return (EIO);
4120df131e84SAndrew Gallatin 		i2c->data[i] = cmd.data0;
4121df131e84SAndrew Gallatin 	}
4122df131e84SAndrew Gallatin 	return (0);
4123df131e84SAndrew Gallatin }
4124df131e84SAndrew Gallatin 
4125df131e84SAndrew Gallatin static int
41266d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
4127b2fc195eSAndrew Gallatin {
41286d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4129b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
4130df131e84SAndrew Gallatin 	struct ifi2creq i2c;
4131b2fc195eSAndrew Gallatin 	int err, mask;
4132b2fc195eSAndrew Gallatin 
4133b2fc195eSAndrew Gallatin 	err = 0;
4134b2fc195eSAndrew Gallatin 	switch (command) {
4135b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
41366d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
4137b2fc195eSAndrew Gallatin 		break;
4138b2fc195eSAndrew Gallatin 
4139b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
4140a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
41418c5d766cSAndrew Gallatin 		if (sc->dying) {
41428c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
41438c5d766cSAndrew Gallatin 			return EINVAL;
41448c5d766cSAndrew Gallatin 		}
4145b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
4146dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
41476d87a65dSAndrew Gallatin 				err = mxge_open(sc);
4148dce01b9bSAndrew Gallatin 			} else {
41490fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
41500fa7f681SAndrew Gallatin 				   flag chages */
41510fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
41520fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
41530fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
41540fa7f681SAndrew Gallatin 			}
4155b2fc195eSAndrew Gallatin 		} else {
4156dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4157a393336bSAndrew Gallatin 				mxge_close(sc, 0);
4158dce01b9bSAndrew Gallatin 			}
4159b2fc195eSAndrew Gallatin 		}
4160a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4161b2fc195eSAndrew Gallatin 		break;
4162b2fc195eSAndrew Gallatin 
4163b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
4164b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
4165a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4166c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4167c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4168c0a1f0afSAndrew Gallatin 			return (EINVAL);
4169c0a1f0afSAndrew Gallatin 		}
41700fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
4171a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4172b2fc195eSAndrew Gallatin 		break;
4173b2fc195eSAndrew Gallatin 
4174b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
4175a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4176b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
4177b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
4178b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
4179cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO4;
4180aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
41810a7a780eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
4182b2fc195eSAndrew Gallatin 			} else {
4183b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
4184b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
4185b2fc195eSAndrew Gallatin 			}
4186cbb9ccf7SRyan Moeller 		}
4187cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM) {
4188b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
4189b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
4190b2fc195eSAndrew Gallatin 			} else {
4191b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
4192b2fc195eSAndrew Gallatin 			}
4193b2fc195eSAndrew Gallatin 		}
4194aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
4195aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
4196aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
4197aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
4198aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
4199aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
4200aed8e389SAndrew Gallatin 			} else {
4201aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4202aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4203aed8e389SAndrew Gallatin 				err = EINVAL;
4204aed8e389SAndrew Gallatin 			}
4205aed8e389SAndrew Gallatin 		}
42060a7a780eSAndrew Gallatin #if IFCAP_TSO6
42070a7a780eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM_IPV6) {
42080a7a780eSAndrew Gallatin 			if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) {
4209cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO6;
42100a7a780eSAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6
42110a7a780eSAndrew Gallatin 						       | IFCAP_TSO6);
42120a7a780eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP_IPV6
42130a7a780eSAndrew Gallatin 						      | CSUM_UDP);
42140a7a780eSAndrew Gallatin 			} else {
42150a7a780eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM_IPV6;
42160a7a780eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP_IPV6
42170a7a780eSAndrew Gallatin 						     | CSUM_UDP_IPV6);
42180a7a780eSAndrew Gallatin 			}
4219cbb9ccf7SRyan Moeller 		}
4220cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM_IPV6) {
422126dd49c6SAndrew Gallatin 			if (IFCAP_RXCSUM_IPV6 & ifp->if_capenable) {
422226dd49c6SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM_IPV6;
42230a7a780eSAndrew Gallatin 			} else {
422426dd49c6SAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM_IPV6;
42250a7a780eSAndrew Gallatin 			}
42260a7a780eSAndrew Gallatin 		}
42270a7a780eSAndrew Gallatin 		if (mask & IFCAP_TSO6) {
42280a7a780eSAndrew Gallatin 			if (IFCAP_TSO6 & ifp->if_capenable) {
42290a7a780eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO6;
42300a7a780eSAndrew Gallatin 			} else if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) {
42310a7a780eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO6;
42320a7a780eSAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
42330a7a780eSAndrew Gallatin 			} else {
42340a7a780eSAndrew Gallatin 				printf("mxge requires tx checksum offload"
42350a7a780eSAndrew Gallatin 				       " be enabled to use TSO\n");
42360a7a780eSAndrew Gallatin 				err = EINVAL;
42370a7a780eSAndrew Gallatin 			}
42380a7a780eSAndrew Gallatin 		}
42390a7a780eSAndrew Gallatin #endif /*IFCAP_TSO6 */
42400a7a780eSAndrew Gallatin 
424126dd49c6SAndrew Gallatin 		if (mask & IFCAP_LRO)
424226dd49c6SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_LRO;
4243c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
4244c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
42450dce6781SAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTSO)
42460dce6781SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
42470dce6781SAndrew Gallatin 
42480dce6781SAndrew Gallatin 		if (!(ifp->if_capabilities & IFCAP_VLAN_HWTSO) ||
42490dce6781SAndrew Gallatin 		    !(ifp->if_capenable & IFCAP_VLAN_HWTAGGING))
42500dce6781SAndrew Gallatin 			ifp->if_capenable &= ~IFCAP_VLAN_HWTSO;
42510dce6781SAndrew Gallatin 
4252a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4253c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4254c792928fSAndrew Gallatin 
4255b2fc195eSAndrew Gallatin 		break;
4256b2fc195eSAndrew Gallatin 
4257b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4258c406ad2eSAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4259c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4260c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4261c0a1f0afSAndrew Gallatin 			return (EINVAL);
4262c0a1f0afSAndrew Gallatin 		}
4263c406ad2eSAndrew Gallatin 		mxge_media_probe(sc);
4264c406ad2eSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4265b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4266b2fc195eSAndrew Gallatin 				    &sc->media, command);
4267b2fc195eSAndrew Gallatin 		break;
4268b2fc195eSAndrew Gallatin 
4269df131e84SAndrew Gallatin 	case SIOCGI2C:
4270df131e84SAndrew Gallatin 		if (sc->connector != MXGE_XFP &&
4271df131e84SAndrew Gallatin 		    sc->connector != MXGE_SFP) {
4272df131e84SAndrew Gallatin 			err = ENXIO;
4273df131e84SAndrew Gallatin 			break;
4274df131e84SAndrew Gallatin 		}
4275df131e84SAndrew Gallatin 		err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c));
4276df131e84SAndrew Gallatin 		if (err != 0)
4277df131e84SAndrew Gallatin 			break;
4278df131e84SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4279df131e84SAndrew Gallatin 		if (sc->dying) {
4280df131e84SAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4281df131e84SAndrew Gallatin 			return (EINVAL);
4282df131e84SAndrew Gallatin 		}
4283df131e84SAndrew Gallatin 		err = mxge_fetch_i2c(sc, &i2c);
4284df131e84SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4285df131e84SAndrew Gallatin 		if (err == 0)
42861b8b041cSBrooks Davis 			err = copyout(&i2c, ifr_data_get_ptr(ifr),
4287df131e84SAndrew Gallatin 			    sizeof(i2c));
4288df131e84SAndrew Gallatin 		break;
4289b2fc195eSAndrew Gallatin 	default:
4290c756fb6eSRavi Pokala 		err = ether_ioctl(ifp, command, data);
4291c756fb6eSRavi Pokala 		break;
4292b2fc195eSAndrew Gallatin 	}
4293b2fc195eSAndrew Gallatin 	return err;
4294b2fc195eSAndrew Gallatin }
4295b2fc195eSAndrew Gallatin 
4296b2fc195eSAndrew Gallatin static void
42976d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4298b2fc195eSAndrew Gallatin {
4299b2fc195eSAndrew Gallatin 
43001e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
43016d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
43026d87a65dSAndrew Gallatin 			  &mxge_flow_control);
43036d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
43046d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
43056d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
43066d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4307d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4308d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
43095e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
43105e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
43115e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
43125e7d8541SAndrew Gallatin 			  &mxge_verbose);
4313dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
43141e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
43151e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
431694c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4317f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
431865c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4319b2fc195eSAndrew Gallatin 
43205e7d8541SAndrew Gallatin 	if (bootverbose)
43215e7d8541SAndrew Gallatin 		mxge_verbose = 1;
43226d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
43236d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4324dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
43251e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
43266d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
43271e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4328bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
43295769c5efSAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4330b2fc195eSAndrew Gallatin 	}
4331f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4332f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4333f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
433465c69066SAndrew Gallatin 
433565c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
433665c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
433765c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
433865c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
433965c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
43401e413cf9SAndrew Gallatin }
43411e413cf9SAndrew Gallatin 
43421e413cf9SAndrew Gallatin static void
43431e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
43441e413cf9SAndrew Gallatin {
43451e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
43461e413cf9SAndrew Gallatin 	int i;
43471e413cf9SAndrew Gallatin 
43481e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
43491e413cf9SAndrew Gallatin 		return;
43501e413cf9SAndrew Gallatin 
43511e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43521e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
43531e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
43541e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
43551e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4356c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4357c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4358c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4359c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4360c6cb3e3fSAndrew Gallatin 			}
4361c6cb3e3fSAndrew Gallatin #endif
43621e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
43631e413cf9SAndrew Gallatin 		}
43641e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
43651e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
43661e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
43671e413cf9SAndrew Gallatin 		}
43681e413cf9SAndrew Gallatin 	}
43691e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
43701e413cf9SAndrew Gallatin 	sc->ss = NULL;
43711e413cf9SAndrew Gallatin }
43721e413cf9SAndrew Gallatin 
43731e413cf9SAndrew Gallatin static int
43741e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
43751e413cf9SAndrew Gallatin {
43761e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
43771e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
43781e413cf9SAndrew Gallatin 	size_t bytes;
43791e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
43801e413cf9SAndrew Gallatin 
43811e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
43821e413cf9SAndrew Gallatin 	if (err != 0) {
43831e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
43841e413cf9SAndrew Gallatin 		return err;
43851e413cf9SAndrew Gallatin 	}
43861e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
43871e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
43881e413cf9SAndrew Gallatin 
4389ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->ss) * sc->num_slices;
4390ac2fffa4SPedro F. Giffuni 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
43911e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
43921e413cf9SAndrew Gallatin 		return (ENOMEM);
43931e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43941e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
43951e413cf9SAndrew Gallatin 
43961e413cf9SAndrew Gallatin 		ss->sc = sc;
43971e413cf9SAndrew Gallatin 
43981e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
43991e413cf9SAndrew Gallatin 
44001e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
44011e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
44021e413cf9SAndrew Gallatin 		if (err != 0)
44031e413cf9SAndrew Gallatin 			goto abort;
44041e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
44051e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
44061e413cf9SAndrew Gallatin 
44071e413cf9SAndrew Gallatin 		/*
44081e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
44091e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
44101e413cf9SAndrew Gallatin 		 * slice for now
44111e413cf9SAndrew Gallatin 		 */
4412c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
44131e413cf9SAndrew Gallatin 		if (i > 0)
44141e413cf9SAndrew Gallatin 			continue;
4415c6cb3e3fSAndrew Gallatin #endif
44161e413cf9SAndrew Gallatin 
44171e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
44181e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
44191e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
44201e413cf9SAndrew Gallatin 		if (err != 0)
44211e413cf9SAndrew Gallatin 			goto abort;
44221e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
44231e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
44241e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
44251e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4426c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4427c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4428c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4429c6cb3e3fSAndrew Gallatin #endif
44301e413cf9SAndrew Gallatin 	}
44311e413cf9SAndrew Gallatin 
44321e413cf9SAndrew Gallatin 	return (0);
44331e413cf9SAndrew Gallatin 
44341e413cf9SAndrew Gallatin abort:
44351e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
44361e413cf9SAndrew Gallatin 	return (ENOMEM);
44371e413cf9SAndrew Gallatin }
44381e413cf9SAndrew Gallatin 
44391e413cf9SAndrew Gallatin static void
44401e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
44411e413cf9SAndrew Gallatin {
44421e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
44431e413cf9SAndrew Gallatin 	char *old_fw;
44441e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
44451e413cf9SAndrew Gallatin 
44461e413cf9SAndrew Gallatin 	sc->num_slices = 1;
44471e413cf9SAndrew Gallatin 	/*
44481e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
44491e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
44501e413cf9SAndrew Gallatin 	 */
44511e413cf9SAndrew Gallatin 
44521e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
44531e413cf9SAndrew Gallatin 		return;
44541e413cf9SAndrew Gallatin 
44551e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
44561e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
44571e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
44581e413cf9SAndrew Gallatin 		return;
44591e413cf9SAndrew Gallatin 
44601e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
44611e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
44621e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
44631e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
44641e413cf9SAndrew Gallatin 	else
44651e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
44661e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
44671e413cf9SAndrew Gallatin 	if (status != 0) {
44681e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
44691e413cf9SAndrew Gallatin 		return;
44701e413cf9SAndrew Gallatin 	}
44711e413cf9SAndrew Gallatin 
44721e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
44731e413cf9SAndrew Gallatin 	   is alive */
44741e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
44751e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
44761e413cf9SAndrew Gallatin 	if (status != 0) {
44771e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
44781e413cf9SAndrew Gallatin 		goto abort_with_fw;
44791e413cf9SAndrew Gallatin 	}
44801e413cf9SAndrew Gallatin 
44811e413cf9SAndrew Gallatin 	/* get rx ring size */
44821e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
44831e413cf9SAndrew Gallatin 	if (status != 0) {
44841e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
44851e413cf9SAndrew Gallatin 		goto abort_with_fw;
44861e413cf9SAndrew Gallatin 	}
44871e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
44881e413cf9SAndrew Gallatin 
44891e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
44901e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
44911e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
44921e413cf9SAndrew Gallatin 	if (status != 0) {
44931e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
44941e413cf9SAndrew Gallatin 		goto abort_with_fw;
44951e413cf9SAndrew Gallatin 	}
44961e413cf9SAndrew Gallatin 
44971e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
44981e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
44991e413cf9SAndrew Gallatin 	if (status != 0) {
45001e413cf9SAndrew Gallatin 		device_printf(sc->dev,
45011e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
45021e413cf9SAndrew Gallatin 		goto abort_with_fw;
45031e413cf9SAndrew Gallatin 	}
45041e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
45051e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
45061e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
45071e413cf9SAndrew Gallatin 
45081e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
45091e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
45101e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
45111e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
45121e413cf9SAndrew Gallatin 	} else {
45131e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
45141e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
45151e413cf9SAndrew Gallatin 	}
45161e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
45171e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
45181e413cf9SAndrew Gallatin 		sc->num_slices--;
45191e413cf9SAndrew Gallatin 
45201e413cf9SAndrew Gallatin 	if (mxge_verbose)
45211e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
45221e413cf9SAndrew Gallatin 			      sc->num_slices);
45231e413cf9SAndrew Gallatin 
45241e413cf9SAndrew Gallatin 	return;
45251e413cf9SAndrew Gallatin 
45261e413cf9SAndrew Gallatin abort_with_fw:
45271e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
45281e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
45291e413cf9SAndrew Gallatin }
45301e413cf9SAndrew Gallatin 
45311e413cf9SAndrew Gallatin static int
45321e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
45331e413cf9SAndrew Gallatin {
4534ac2fffa4SPedro F. Giffuni 	size_t bytes;
45351e413cf9SAndrew Gallatin 	int count, err, i, rid;
45361e413cf9SAndrew Gallatin 
45371e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
45381e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
45391e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
45401e413cf9SAndrew Gallatin 
45411e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
45421e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
45431e413cf9SAndrew Gallatin 		return ENXIO;
45441e413cf9SAndrew Gallatin 	}
45451e413cf9SAndrew Gallatin 
45461e413cf9SAndrew Gallatin 	count = sc->num_slices;
45471e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
45481e413cf9SAndrew Gallatin 	if (err != 0) {
45491e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
45501e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
45511e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
45521e413cf9SAndrew Gallatin 	}
45531e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
45541e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
45551e413cf9SAndrew Gallatin 			      count, sc->num_slices);
45561e413cf9SAndrew Gallatin 		device_printf(sc->dev,
45571e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
45581e413cf9SAndrew Gallatin 			      count);
45591e413cf9SAndrew Gallatin 		err = ENOSPC;
45601e413cf9SAndrew Gallatin 		goto abort_with_msix;
45611e413cf9SAndrew Gallatin 	}
4562ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
4563ac2fffa4SPedro F. Giffuni 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
45641e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
45651e413cf9SAndrew Gallatin 		err = ENOMEM;
45661e413cf9SAndrew Gallatin 		goto abort_with_msix;
45671e413cf9SAndrew Gallatin 	}
45681e413cf9SAndrew Gallatin 
45691e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45701e413cf9SAndrew Gallatin 		rid = i + 1;
45711e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
45721e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
45731e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
45741e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
45751e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
45761e413cf9SAndrew Gallatin 				      " for message %d\n", i);
45771e413cf9SAndrew Gallatin 			err = ENXIO;
45781e413cf9SAndrew Gallatin 			goto abort_with_res;
45791e413cf9SAndrew Gallatin 		}
45801e413cf9SAndrew Gallatin 	}
45811e413cf9SAndrew Gallatin 
4582ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
4583ac2fffa4SPedro F. Giffuni 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
45841e413cf9SAndrew Gallatin 
45851e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45861e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
45871e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
458837d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
458937d89b0cSAndrew Gallatin 				     NULL,
459037d89b0cSAndrew Gallatin #endif
459137d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
45921e413cf9SAndrew Gallatin 		if (err != 0) {
45931e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
45941e413cf9SAndrew Gallatin 				      "message %d\n", i);
45951e413cf9SAndrew Gallatin 			goto abort_with_intr;
45961e413cf9SAndrew Gallatin 		}
459721089137SAndrew Gallatin 		bus_describe_intr(sc->dev, sc->msix_irq_res[i],
459821089137SAndrew Gallatin 				  sc->msix_ih[i], "s%d", i);
45991e413cf9SAndrew Gallatin 	}
46001e413cf9SAndrew Gallatin 
46011e413cf9SAndrew Gallatin 	if (mxge_verbose) {
46021e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
46031e413cf9SAndrew Gallatin 			      sc->num_slices);
46041e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
4605da1b038aSJustin Hibbits 			printf(" %jd", rman_get_start(sc->msix_irq_res[i]));
46061e413cf9SAndrew Gallatin 		printf("\n");
46071e413cf9SAndrew Gallatin 	}
46081e413cf9SAndrew Gallatin 	return (0);
46091e413cf9SAndrew Gallatin 
46101e413cf9SAndrew Gallatin abort_with_intr:
46111e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46121e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
46131e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
46141e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
46151e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
46161e413cf9SAndrew Gallatin 		}
46171e413cf9SAndrew Gallatin 	}
46181e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
46191e413cf9SAndrew Gallatin 
46201e413cf9SAndrew Gallatin abort_with_res:
46211e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46221e413cf9SAndrew Gallatin 		rid = i + 1;
46231e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
46241e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
46251e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
46261e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
46271e413cf9SAndrew Gallatin 	}
46281e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
46291e413cf9SAndrew Gallatin 
46301e413cf9SAndrew Gallatin abort_with_msix:
46311e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
46321e413cf9SAndrew Gallatin 
46331e413cf9SAndrew Gallatin abort_with_msix_table:
46341e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
46351e413cf9SAndrew Gallatin 			     sc->msix_table_res);
46361e413cf9SAndrew Gallatin 
46371e413cf9SAndrew Gallatin 	return err;
46381e413cf9SAndrew Gallatin }
46391e413cf9SAndrew Gallatin 
46401e413cf9SAndrew Gallatin static int
46411e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
46421e413cf9SAndrew Gallatin {
46431e413cf9SAndrew Gallatin 	int count, err, rid;
46441e413cf9SAndrew Gallatin 
46451e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
46461e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
46471e413cf9SAndrew Gallatin 		rid = 1;
46481e413cf9SAndrew Gallatin 	} else {
46491e413cf9SAndrew Gallatin 		rid = 0;
465091ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
46511e413cf9SAndrew Gallatin 	}
465243cd6160SJustin Hibbits 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
465343cd6160SJustin Hibbits 					     RF_SHAREABLE | RF_ACTIVE);
46541e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
46551e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
46561e413cf9SAndrew Gallatin 		return ENXIO;
46571e413cf9SAndrew Gallatin 	}
46581e413cf9SAndrew Gallatin 	if (mxge_verbose)
4659da1b038aSJustin Hibbits 		device_printf(sc->dev, "using %s irq %jd\n",
466091ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
46611e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
46621e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
46631e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
466437d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
466537d89b0cSAndrew Gallatin 			     NULL,
466637d89b0cSAndrew Gallatin #endif
466737d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
46681e413cf9SAndrew Gallatin 	if (err != 0) {
46691e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
467091ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
467191ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
46721e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
46731e413cf9SAndrew Gallatin 	}
46741e413cf9SAndrew Gallatin 	return err;
46751e413cf9SAndrew Gallatin }
46761e413cf9SAndrew Gallatin 
46771e413cf9SAndrew Gallatin static void
46781e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
46791e413cf9SAndrew Gallatin {
46801e413cf9SAndrew Gallatin 	int i, rid;
46811e413cf9SAndrew Gallatin 
46821e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46831e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
46841e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
46851e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
46861e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
46871e413cf9SAndrew Gallatin 		}
46881e413cf9SAndrew Gallatin 	}
46891e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
46901e413cf9SAndrew Gallatin 
46911e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46921e413cf9SAndrew Gallatin 		rid = i + 1;
46931e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
46941e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
46951e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
46961e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
46971e413cf9SAndrew Gallatin 	}
46981e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
46991e413cf9SAndrew Gallatin 
47001e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
47011e413cf9SAndrew Gallatin 			     sc->msix_table_res);
47021e413cf9SAndrew Gallatin 
47031e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
47041e413cf9SAndrew Gallatin 	return;
47051e413cf9SAndrew Gallatin }
47061e413cf9SAndrew Gallatin 
47071e413cf9SAndrew Gallatin static void
47081e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
47091e413cf9SAndrew Gallatin {
47101e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
47111e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
471291ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
471391ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
47141e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
47151e413cf9SAndrew Gallatin }
47161e413cf9SAndrew Gallatin 
47171e413cf9SAndrew Gallatin static void
47181e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
47191e413cf9SAndrew Gallatin {
47201e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
47211e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
47221e413cf9SAndrew Gallatin 	else
47231e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
47241e413cf9SAndrew Gallatin }
47251e413cf9SAndrew Gallatin 
47261e413cf9SAndrew Gallatin static int
47271e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
47281e413cf9SAndrew Gallatin {
47291e413cf9SAndrew Gallatin 	int err;
47301e413cf9SAndrew Gallatin 
47311e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
47321e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
47331e413cf9SAndrew Gallatin 	else
47341e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
47351e413cf9SAndrew Gallatin 
47361e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
47371e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
47381e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
47391e413cf9SAndrew Gallatin 	}
47401e413cf9SAndrew Gallatin 	return err;
47411e413cf9SAndrew Gallatin }
47421e413cf9SAndrew Gallatin 
4743b2fc195eSAndrew Gallatin static int
47446d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4745b2fc195eSAndrew Gallatin {
47460a7a780eSAndrew Gallatin 	mxge_cmd_t cmd;
47476d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4748b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
47491e413cf9SAndrew Gallatin 	int err, rid;
4750b2fc195eSAndrew Gallatin 
4751b2fc195eSAndrew Gallatin 	sc->dev = dev;
47526d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4753b2fc195eSAndrew Gallatin 
475472c042dfSAndrew Gallatin 	TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
4755a4a75d67SJohn Baldwin 	sc->tq = taskqueue_create("mxge_taskq", M_WAITOK,
4756a4a75d67SJohn Baldwin 				  taskqueue_thread_enqueue, &sc->tq);
475772c042dfSAndrew Gallatin 	if (sc->tq == NULL) {
475872c042dfSAndrew Gallatin 		err = ENOMEM;
475972c042dfSAndrew Gallatin 		goto abort_with_nothing;
476072c042dfSAndrew Gallatin 	}
476172c042dfSAndrew Gallatin 
476262ce43ccSScott Long 	err = bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
4763b2fc195eSAndrew Gallatin 				 1,			/* alignment */
47641e413cf9SAndrew Gallatin 				 0,			/* boundary */
4765b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4766b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4767b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4768aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
47695e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
47701e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4771b2fc195eSAndrew Gallatin 				 0,			/* flags */
4772b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4773b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4774b2fc195eSAndrew Gallatin 
4775b2fc195eSAndrew Gallatin 	if (err != 0) {
4776b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4777b2fc195eSAndrew Gallatin 			      err);
477872c042dfSAndrew Gallatin 		goto abort_with_tq;
4779b2fc195eSAndrew Gallatin 	}
4780b2fc195eSAndrew Gallatin 
4781b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4782b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4783b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4784b2fc195eSAndrew Gallatin 		err = ENOSPC;
4785b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4786b2fc195eSAndrew Gallatin 	}
47871e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
47881e413cf9SAndrew Gallatin 
4789a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4790a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4791a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4792a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4793a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4794a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4795b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4796b2fc195eSAndrew Gallatin 
4797dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4798d91b1b49SAndrew Gallatin 
4799dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4800b2fc195eSAndrew Gallatin 
4801b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4802b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
480343cd6160SJustin Hibbits 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
480443cd6160SJustin Hibbits 					     RF_ACTIVE);
4805b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4806b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4807b2fc195eSAndrew Gallatin 		err = ENXIO;
4808b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4809b2fc195eSAndrew Gallatin 	}
4810b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4811b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4812b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4813da1b038aSJustin Hibbits 		device_printf(dev, "impossible memory region size %jd\n",
4814b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4815b2fc195eSAndrew Gallatin 		err = ENXIO;
4816b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4817b2fc195eSAndrew Gallatin 	}
4818b2fc195eSAndrew Gallatin 
4819b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4820b2fc195eSAndrew Gallatin 	   lanai SRAM */
48216d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4822b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4823b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
48246d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4825b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
48266d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
48276d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4828b2fc195eSAndrew Gallatin 	if (err != 0)
4829b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4830b2fc195eSAndrew Gallatin 
4831b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
48326d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4833b2fc195eSAndrew Gallatin 
4834b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
48356d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
48366d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4837b2fc195eSAndrew Gallatin 	if (err != 0)
4838b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4839b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
48406d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4841b2fc195eSAndrew Gallatin 	if (err != 0)
4842b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4843b2fc195eSAndrew Gallatin 
4844a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4845a98d6cd7SAndrew Gallatin 	if (err != 0)
48461e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4847b2fc195eSAndrew Gallatin 
48488fe615baSAndrew Gallatin 	/* select & load the firmware */
48498fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4850b2fc195eSAndrew Gallatin 	if (err != 0)
48511e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
48525e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
48531e413cf9SAndrew Gallatin 
48541e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
48551e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
48561e413cf9SAndrew Gallatin 	if (err != 0)
48571e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
48581e413cf9SAndrew Gallatin 
4859adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4860b2fc195eSAndrew Gallatin 	if (err != 0)
48611e413cf9SAndrew Gallatin 		goto abort_with_slices;
4862b2fc195eSAndrew Gallatin 
4863a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4864a98d6cd7SAndrew Gallatin 	if (err != 0) {
4865a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
48662e084798SAndrew Gallatin 		goto abort_with_slices;
4867a98d6cd7SAndrew Gallatin 	}
4868a98d6cd7SAndrew Gallatin 
48691e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4870a98d6cd7SAndrew Gallatin 	if (err != 0) {
48711e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4872a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4873a98d6cd7SAndrew Gallatin 	}
48741e413cf9SAndrew Gallatin 
4875b245f96cSGleb Smirnoff 	ifp->if_baudrate = IF_Gbps(10);
4876c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
487726dd49c6SAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LINKSTATE | IFCAP_TXCSUM_IPV6 |
487826dd49c6SAndrew Gallatin 		IFCAP_RXCSUM_IPV6;
487926dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
4880eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4881eb6219e3SAndrew Gallatin #endif
488237d89b0cSAndrew Gallatin 
488337d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
488437d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
48850dce6781SAndrew Gallatin 
48860dce6781SAndrew Gallatin 	/* Only FW 1.4.32 and newer can do TSO over vlans */
48870dce6781SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
48880dce6781SAndrew Gallatin 	    sc->fw_ver_tiny >= 32)
48890dce6781SAndrew Gallatin 		ifp->if_capabilities |= IFCAP_VLAN_HWTSO;
489037d89b0cSAndrew Gallatin #endif
4891053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4892053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4893053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4894053e637fSAndrew Gallatin 	else
4895053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4896adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4897053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4898aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
48990a7a780eSAndrew Gallatin 	ifp->if_hwassist |= CSUM_TCP_IPV6 | CSUM_UDP_IPV6;
49000a7a780eSAndrew Gallatin 	/* check to see if f/w supports TSO for IPv6 */
49010a7a780eSAndrew Gallatin 	if (!mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd)) {
49020a7a780eSAndrew Gallatin 		if (CSUM_TCP_IPV6)
49030a7a780eSAndrew Gallatin 			ifp->if_capabilities |= IFCAP_TSO6;
49040a7a780eSAndrew Gallatin 		sc->max_tso6_hlen = min(cmd.data0,
49050a7a780eSAndrew Gallatin 					sizeof (sc->ss[0].scratch));
49060a7a780eSAndrew Gallatin 	}
4907b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4908f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4909f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
49106d87a65dSAndrew Gallatin 	ifp->if_init = mxge_init;
4911b2fc195eSAndrew Gallatin 	ifp->if_softc = sc;
4912b2fc195eSAndrew Gallatin 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
49136d87a65dSAndrew Gallatin 	ifp->if_ioctl = mxge_ioctl;
49146d87a65dSAndrew Gallatin 	ifp->if_start = mxge_start;
4915f3f040d9SGleb Smirnoff 	ifp->if_get_counter = mxge_get_counter;
49163d07f894SAndrew Gallatin 	ifp->if_hw_tsomax = IP_MAXPACKET - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
49177eafc4d5SAndrew Gallatin 	ifp->if_hw_tsomaxsegcount = sc->ss[0].tx.max_desc;
49183d07f894SAndrew Gallatin 	ifp->if_hw_tsomaxsegsize = IP_MAXPACKET;
4919c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4920c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4921c587e59fSAndrew Gallatin 		     mxge_media_status);
4922c406ad2eSAndrew Gallatin 	mxge_media_init(sc);
4923c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
49248c5d766cSAndrew Gallatin 	sc->dying = 0;
4925b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4926f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4927f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4928f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4929b2fc195eSAndrew Gallatin 
49306d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4931c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4932c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4933c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4934c6cb3e3fSAndrew Gallatin #endif
49352e084798SAndrew Gallatin 	taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
49362e084798SAndrew Gallatin 				device_get_nameunit(sc->dev));
49376b484a49SAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4938b2fc195eSAndrew Gallatin 	return 0;
4939b2fc195eSAndrew Gallatin 
4940a98d6cd7SAndrew Gallatin abort_with_rings:
4941a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
49421e413cf9SAndrew Gallatin abort_with_slices:
49431e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4944a98d6cd7SAndrew Gallatin abort_with_dmabench:
4945a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4946b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
49476d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4948b2fc195eSAndrew Gallatin abort_with_cmd_dma:
49496d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4950b2fc195eSAndrew Gallatin abort_with_mem_res:
4951b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4952b2fc195eSAndrew Gallatin abort_with_lock:
4953b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4954a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4955a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4956b2fc195eSAndrew Gallatin 	if_free(ifp);
4957b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4958b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
495972c042dfSAndrew Gallatin abort_with_tq:
496072c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
496172c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
496272c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
496372c042dfSAndrew Gallatin 		sc->tq = NULL;
496472c042dfSAndrew Gallatin 	}
4965b2fc195eSAndrew Gallatin abort_with_nothing:
4966b2fc195eSAndrew Gallatin 	return err;
4967b2fc195eSAndrew Gallatin }
4968b2fc195eSAndrew Gallatin 
4969b2fc195eSAndrew Gallatin static int
49706d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4971b2fc195eSAndrew Gallatin {
49726d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4973b2fc195eSAndrew Gallatin 
497437d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4975c792928fSAndrew Gallatin 		device_printf(sc->dev,
4976c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4977c792928fSAndrew Gallatin 		return EBUSY;
4978c792928fSAndrew Gallatin 	}
4979a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
49808c5d766cSAndrew Gallatin 	sc->dying = 1;
4981b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
4982a393336bSAndrew Gallatin 		mxge_close(sc, 0);
4983a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4984b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
498572c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
498672c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
498772c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
498872c042dfSAndrew Gallatin 		sc->tq = NULL;
498972c042dfSAndrew Gallatin 	}
4990e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4991dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4992091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
49931e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
49941e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4995a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
49961e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4997a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
49986d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
49996d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
5000b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
5001b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
5002a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
5003a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
5004b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
5005b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
5006b2fc195eSAndrew Gallatin 	return 0;
5007b2fc195eSAndrew Gallatin }
5008b2fc195eSAndrew Gallatin 
5009b2fc195eSAndrew Gallatin static int
50106d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
5011b2fc195eSAndrew Gallatin {
5012b2fc195eSAndrew Gallatin 	return 0;
5013b2fc195eSAndrew Gallatin }
5014b2fc195eSAndrew Gallatin 
5015b2fc195eSAndrew Gallatin /*
5016b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
5017b2fc195eSAndrew Gallatin 
5018b2fc195eSAndrew Gallatin   Local Variables:
5019b2fc195eSAndrew Gallatin   c-file-style:"linux"
5020b2fc195eSAndrew Gallatin   tab-width:8
5021b2fc195eSAndrew Gallatin   End:
5022b2fc195eSAndrew Gallatin */
5023