xref: /freebsd/sys/dev/mxge/if_mxge.c (revision e936121d3140af047a498559493b9375a6ba6ba3)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
3cabc512fSAndrew Gallatin Copyright (c) 2006-2013, Myricom Inc.
4b2fc195eSAndrew Gallatin All rights reserved.
5b2fc195eSAndrew Gallatin 
6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
8b2fc195eSAndrew Gallatin 
9b2fc195eSAndrew Gallatin  1. Redistributions of source code must retain the above copyright notice,
10b2fc195eSAndrew Gallatin     this list of conditions and the following disclaimer.
11b2fc195eSAndrew Gallatin 
12eb8e82f5SAndrew Gallatin  2. Neither the name of the Myricom Inc, nor the names of its
13b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
14b2fc195eSAndrew Gallatin     this software without specific prior written permission.
15b2fc195eSAndrew Gallatin 
16b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
27b2fc195eSAndrew Gallatin 
28b2fc195eSAndrew Gallatin ***************************************************************************/
29b2fc195eSAndrew Gallatin 
30b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
31b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
32b2fc195eSAndrew Gallatin 
33b2fc195eSAndrew Gallatin #include <sys/param.h>
34b2fc195eSAndrew Gallatin #include <sys/systm.h>
35b2fc195eSAndrew Gallatin #include <sys/linker.h>
36b2fc195eSAndrew Gallatin #include <sys/firmware.h>
37b2fc195eSAndrew Gallatin #include <sys/endian.h>
38b2fc195eSAndrew Gallatin #include <sys/sockio.h>
39b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
40b2fc195eSAndrew Gallatin #include <sys/malloc.h>
41b2fc195eSAndrew Gallatin #include <sys/kdb.h>
42b2fc195eSAndrew Gallatin #include <sys/kernel.h>
434e7f640dSJohn Baldwin #include <sys/lock.h>
44b2fc195eSAndrew Gallatin #include <sys/module.h>
45b2fc195eSAndrew Gallatin #include <sys/socket.h>
46b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
47b2fc195eSAndrew Gallatin #include <sys/sx.h>
4872c042dfSAndrew Gallatin #include <sys/taskqueue.h>
49d9db5225SCraig Rodrigues #include <sys/zlib.h>
50b2fc195eSAndrew Gallatin 
51b2fc195eSAndrew Gallatin #include <net/if.h>
5276039bc8SGleb Smirnoff #include <net/if_var.h>
53b2fc195eSAndrew Gallatin #include <net/if_arp.h>
54b2fc195eSAndrew Gallatin #include <net/ethernet.h>
55b2fc195eSAndrew Gallatin #include <net/if_dl.h>
56b2fc195eSAndrew Gallatin #include <net/if_media.h>
57b2fc195eSAndrew Gallatin 
58b2fc195eSAndrew Gallatin #include <net/bpf.h>
59b2fc195eSAndrew Gallatin 
60b2fc195eSAndrew Gallatin #include <net/if_types.h>
61b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
62b2fc195eSAndrew Gallatin 
63b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
64b2fc195eSAndrew Gallatin #include <netinet/in.h>
65b2fc195eSAndrew Gallatin #include <netinet/ip.h>
660a7a780eSAndrew Gallatin #include <netinet/ip6.h>
67aed8e389SAndrew Gallatin #include <netinet/tcp.h>
6826dd49c6SAndrew Gallatin #include <netinet/tcp_lro.h>
690a7a780eSAndrew Gallatin #include <netinet6/ip6_var.h>
70b2fc195eSAndrew Gallatin 
71b2fc195eSAndrew Gallatin #include <machine/bus.h>
72053e637fSAndrew Gallatin #include <machine/in_cksum.h>
73b2fc195eSAndrew Gallatin #include <machine/resource.h>
74b2fc195eSAndrew Gallatin #include <sys/bus.h>
75b2fc195eSAndrew Gallatin #include <sys/rman.h>
761e413cf9SAndrew Gallatin #include <sys/smp.h>
77b2fc195eSAndrew Gallatin 
78b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
79b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
80e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
81b2fc195eSAndrew Gallatin 
82b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
83b2fc195eSAndrew Gallatin #include <vm/pmap.h>
84b2fc195eSAndrew Gallatin 
85c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
86c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
87c2c14a69SAndrew Gallatin #endif
88c2c14a69SAndrew Gallatin 
896d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
906d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
911e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
926d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
93869c7348SAndrew Gallatin #ifdef IFNET_BUF_RING
94869c7348SAndrew Gallatin #include <sys/buf_ring.h>
95869c7348SAndrew Gallatin #endif
96b2fc195eSAndrew Gallatin 
97eb6219e3SAndrew Gallatin #include "opt_inet.h"
980a7a780eSAndrew Gallatin #include "opt_inet6.h"
99eb6219e3SAndrew Gallatin 
100b2fc195eSAndrew Gallatin /* tunable params */
1016d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
102d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
1036d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1045e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1056d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1065e7d8541SAndrew Gallatin static int mxge_verbose = 0;
107dce01b9bSAndrew Gallatin static int mxge_ticks;
1081e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1095769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
1101e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
111f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
11265c69066SAndrew Gallatin static int mxge_throttle = 0;
1136d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1146d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1151e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1161e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
117b2fc195eSAndrew Gallatin 
1186d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1196d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1206d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1216d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1226d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
123b2fc195eSAndrew Gallatin 
1246d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
125b2fc195eSAndrew Gallatin {
126b2fc195eSAndrew Gallatin   /* Device interface */
1276d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1286d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1296d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1306d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
13161bfd867SSofian Brabez 
13261bfd867SSofian Brabez   DEVMETHOD_END
133b2fc195eSAndrew Gallatin };
134b2fc195eSAndrew Gallatin 
1356d87a65dSAndrew Gallatin static driver_t mxge_driver =
136b2fc195eSAndrew Gallatin {
1376d87a65dSAndrew Gallatin   "mxge",
1386d87a65dSAndrew Gallatin   mxge_methods,
1396d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
140b2fc195eSAndrew Gallatin };
141b2fc195eSAndrew Gallatin 
1426d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
143b2fc195eSAndrew Gallatin 
144b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1456d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1466d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
147f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
148b2fc195eSAndrew Gallatin 
1491e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1508fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
151a393336bSAndrew Gallatin static int mxge_close(mxge_softc_t *sc, int down);
152276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
153276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1548fe615baSAndrew Gallatin 
155b2fc195eSAndrew Gallatin static int
1566d87a65dSAndrew Gallatin mxge_probe(device_t dev)
157b2fc195eSAndrew Gallatin {
15801638550SAndrew Gallatin 	int rev;
15901638550SAndrew Gallatin 
16001638550SAndrew Gallatin 
1616d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
162f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
163f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
16401638550SAndrew Gallatin 		rev = pci_get_revid(dev);
16501638550SAndrew Gallatin 		switch (rev) {
16601638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
167b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
16801638550SAndrew Gallatin 			break;
16901638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
17001638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
17101638550SAndrew Gallatin 			break;
17201638550SAndrew Gallatin 		default:
17301638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
17401638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
17501638550SAndrew Gallatin 				      rev);
17601638550SAndrew Gallatin 			break;
17701638550SAndrew Gallatin 		}
178b2fc195eSAndrew Gallatin 		return 0;
179b2fc195eSAndrew Gallatin 	}
180b2fc195eSAndrew Gallatin 	return ENXIO;
181b2fc195eSAndrew Gallatin }
182b2fc195eSAndrew Gallatin 
183b2fc195eSAndrew Gallatin static void
1846d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
185b2fc195eSAndrew Gallatin {
186f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
187b2fc195eSAndrew Gallatin 	vm_offset_t len;
18847c2e987SAndrew Gallatin 	int err;
189b2fc195eSAndrew Gallatin 
1904d69a9d0SAndrew Gallatin 	sc->wc = 1;
191b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
192c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
193c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
19447c2e987SAndrew Gallatin 	if (err != 0) {
195c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
196c2c14a69SAndrew Gallatin 			      err);
1974d69a9d0SAndrew Gallatin 		sc->wc = 0;
198b2fc195eSAndrew Gallatin 	}
199f9ae0280SAndrew Gallatin #endif
200b2fc195eSAndrew Gallatin }
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 
274b2fc195eSAndrew Gallatin static void
2756d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
276b2fc195eSAndrew Gallatin {
277b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
278b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
279b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
280b2fc195eSAndrew Gallatin }
281b2fc195eSAndrew Gallatin 
282b2fc195eSAndrew Gallatin /*
283b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
284b2fc195eSAndrew Gallatin  * SN=x\0
285b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
286b2fc195eSAndrew Gallatin  * PC=text\0
287b2fc195eSAndrew Gallatin  */
288b2fc195eSAndrew Gallatin 
289b2fc195eSAndrew Gallatin static int
2906d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
291b2fc195eSAndrew Gallatin {
292dedbe836SAndrew Gallatin 	char *ptr;
293a4b233ddSAndrew Gallatin 	int i, found_mac, found_sn2;
294dedbe836SAndrew Gallatin 	char *endptr;
295b2fc195eSAndrew Gallatin 
296b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
297b2fc195eSAndrew Gallatin 	found_mac = 0;
298a4b233ddSAndrew Gallatin 	found_sn2 = 0;
299dedbe836SAndrew Gallatin 	while (*ptr != '\0') {
300dedbe836SAndrew Gallatin 		if (strncmp(ptr, "MAC=", 4) == 0) {
301dedbe836SAndrew Gallatin 			ptr += 4;
302dedbe836SAndrew Gallatin 			for (i = 0;;) {
303dedbe836SAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
304dedbe836SAndrew Gallatin 				if (endptr - ptr != 2)
305b2fc195eSAndrew Gallatin 					goto abort;
306dedbe836SAndrew Gallatin 				ptr = endptr;
307dedbe836SAndrew Gallatin 				if (++i == 6)
308dedbe836SAndrew Gallatin 					break;
309dedbe836SAndrew Gallatin 				if (*ptr++ != ':')
310dedbe836SAndrew Gallatin 					goto abort;
311b2fc195eSAndrew Gallatin 			}
312dedbe836SAndrew Gallatin 			found_mac = 1;
313dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "PC=", 3) == 0) {
3145e7d8541SAndrew Gallatin 			ptr += 3;
315dedbe836SAndrew Gallatin 			strlcpy(sc->product_code_string, ptr,
316dedbe836SAndrew Gallatin 			    sizeof(sc->product_code_string));
317dedbe836SAndrew Gallatin 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
3185e7d8541SAndrew Gallatin 			ptr += 3;
319dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
320dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
321dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
322a4b233ddSAndrew Gallatin 			/* SN2 takes precedence over SN */
323a4b233ddSAndrew Gallatin 			ptr += 4;
324a4b233ddSAndrew Gallatin 			found_sn2 = 1;
325dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
326dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
327b2fc195eSAndrew Gallatin 		}
328dedbe836SAndrew Gallatin 		while (*ptr++ != '\0') {}
329b2fc195eSAndrew Gallatin 	}
330b2fc195eSAndrew Gallatin 
331b2fc195eSAndrew Gallatin 	if (found_mac)
332b2fc195eSAndrew Gallatin 		return 0;
333b2fc195eSAndrew Gallatin 
334b2fc195eSAndrew Gallatin  abort:
335b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
336b2fc195eSAndrew Gallatin 
337b2fc195eSAndrew Gallatin 	return ENXIO;
338b2fc195eSAndrew Gallatin }
339b2fc195eSAndrew Gallatin 
3400d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3418fe615baSAndrew Gallatin static void
3428fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
343b2fc195eSAndrew Gallatin {
344b2fc195eSAndrew Gallatin 	uint32_t val;
3458fe615baSAndrew Gallatin 	unsigned long base, off;
346b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3478fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3488fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
349b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
350b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
351b2fc195eSAndrew Gallatin 
3528fe615baSAndrew Gallatin 
3538fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3548fe615baSAndrew Gallatin 		return;
3558fe615baSAndrew Gallatin 
3568fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3578fe615baSAndrew Gallatin 	if (pdev == NULL) {
3588fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3598fe615baSAndrew Gallatin 		return;
3608fe615baSAndrew Gallatin 	}
3618fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3628fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3638fe615baSAndrew Gallatin 
3648fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3658fe615baSAndrew Gallatin 		return;
3668fe615baSAndrew Gallatin 
3678fe615baSAndrew Gallatin 	base = 0;
3688fe615baSAndrew Gallatin 
3698fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3708fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3718fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3728fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3738fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3748fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3758fe615baSAndrew Gallatin 		if (mcp55 &&
3768fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3778fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3788fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3798fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3808fe615baSAndrew Gallatin 		}
3818fe615baSAndrew Gallatin 	}
3828fe615baSAndrew Gallatin 	if (!base)
3838fe615baSAndrew Gallatin 		return;
3848fe615baSAndrew Gallatin 
385b2fc195eSAndrew Gallatin 	/* XXXX
386b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
387b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
388b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
389b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
390b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
391b2fc195eSAndrew Gallatin 	*/
392b2fc195eSAndrew Gallatin #if 0
393b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
394b2fc195eSAndrew Gallatin 	   config space */
395b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
396b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
397b2fc195eSAndrew Gallatin 		val |= 0x40;
398b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3998fe615baSAndrew Gallatin 		return;
400b2fc195eSAndrew Gallatin 	}
401b2fc195eSAndrew Gallatin #endif
402b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
403b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
404b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
405b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
406b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
407b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
408b2fc195eSAndrew Gallatin 	 */
409b2fc195eSAndrew Gallatin 
410b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
411b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
412b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
413b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
414b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
415b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
416b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
417b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
418b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
419b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
420b2fc195eSAndrew Gallatin 
4218fe615baSAndrew Gallatin 	off =  base
422b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
423b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
424b2fc195eSAndrew Gallatin 						 + 8 * slot);
425b2fc195eSAndrew Gallatin 
426b2fc195eSAndrew Gallatin 	/* map it into the kernel */
427b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
428b2fc195eSAndrew Gallatin 
429b2fc195eSAndrew Gallatin 
430b2fc195eSAndrew Gallatin 	if (va == NULL) {
431b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4328fe615baSAndrew Gallatin 		return;
433b2fc195eSAndrew Gallatin 	}
434b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
435b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
436b2fc195eSAndrew Gallatin 
437b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
438b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
439b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
440b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
441b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
442b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
443b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4448fe615baSAndrew Gallatin 		return;
445b2fc195eSAndrew Gallatin 	}
446b2fc195eSAndrew Gallatin 
447b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
448b2fc195eSAndrew Gallatin 	val = *ptr32;
449b2fc195eSAndrew Gallatin 
450b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
451b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
452b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4538fe615baSAndrew Gallatin 		return;
454b2fc195eSAndrew Gallatin 	}
455b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
456b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4575e7d8541SAndrew Gallatin 	if (mxge_verbose)
458b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4595e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4605e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
461b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4628fe615baSAndrew Gallatin 	return;
463b2fc195eSAndrew Gallatin }
464b2fc195eSAndrew Gallatin #else
4658fe615baSAndrew Gallatin static void
466f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
467b2fc195eSAndrew Gallatin {
468b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
469b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4708fe615baSAndrew Gallatin 	return;
471b2fc195eSAndrew Gallatin }
472b2fc195eSAndrew Gallatin #endif
4738fe615baSAndrew Gallatin 
4748fe615baSAndrew Gallatin 
4758fe615baSAndrew Gallatin static int
4768fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4778fe615baSAndrew Gallatin {
4788fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4798fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4808fe615baSAndrew Gallatin 	int status;
4818fe615baSAndrew Gallatin 	uint32_t len;
4828fe615baSAndrew Gallatin 	char *test = " ";
4838fe615baSAndrew Gallatin 
4848fe615baSAndrew Gallatin 
4858fe615baSAndrew Gallatin 	/* Run a small DMA test.
4868fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4878fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4888fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4898fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4908fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4918fe615baSAndrew Gallatin 	 * transfers took to complete.
4928fe615baSAndrew Gallatin 	 */
4938fe615baSAndrew Gallatin 
4941e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4958fe615baSAndrew Gallatin 
4968fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4978fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4988fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4998fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5008fe615baSAndrew Gallatin 	if (status != 0) {
5018fe615baSAndrew Gallatin 		test = "read";
5028fe615baSAndrew Gallatin 		goto abort;
5038fe615baSAndrew Gallatin 	}
5048fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
5058fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5068fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5078fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5088fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
5098fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5108fe615baSAndrew Gallatin 	if (status != 0) {
5118fe615baSAndrew Gallatin 		test = "write";
5128fe615baSAndrew Gallatin 		goto abort;
5138fe615baSAndrew Gallatin 	}
5148fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5158fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5168fe615baSAndrew Gallatin 
5178fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5188fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5198fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5208fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5218fe615baSAndrew Gallatin 	if (status != 0) {
5228fe615baSAndrew Gallatin 		test = "read/write";
5238fe615baSAndrew Gallatin 		goto abort;
5248fe615baSAndrew Gallatin 	}
5258fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5268fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5278fe615baSAndrew Gallatin 
5288fe615baSAndrew Gallatin abort:
5298fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5308fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5318fe615baSAndrew Gallatin 			      test, status);
5328fe615baSAndrew Gallatin 
5338fe615baSAndrew Gallatin 	return status;
5348fe615baSAndrew Gallatin }
5358fe615baSAndrew Gallatin 
536b2fc195eSAndrew Gallatin /*
537b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
538b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
539b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
540b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
541b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
542b2fc195eSAndrew Gallatin  *
543b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
544b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
545b2fc195eSAndrew Gallatin  *
546b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
547b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
548b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
549b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5501e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
551b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5521e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
553b2fc195eSAndrew Gallatin  */
554b2fc195eSAndrew Gallatin 
5558fe615baSAndrew Gallatin static int
5568fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5578fe615baSAndrew Gallatin {
5588fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5598fe615baSAndrew Gallatin 	int reg, status;
5608fe615baSAndrew Gallatin 	uint16_t pectl;
5618fe615baSAndrew Gallatin 
5621e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5638fe615baSAndrew Gallatin 	/*
5648fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5658fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5668fe615baSAndrew Gallatin 	 */
5673b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
5688fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5698fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5708fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5718fe615baSAndrew Gallatin 				      pectl);
5721e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5738fe615baSAndrew Gallatin 		}
5748fe615baSAndrew Gallatin 	}
5758fe615baSAndrew Gallatin 
5768fe615baSAndrew Gallatin 	/*
5778fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5788fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5798fe615baSAndrew Gallatin 	 */
5808fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5811e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5828fe615baSAndrew Gallatin 	if (status != 0) {
5838fe615baSAndrew Gallatin 		return status;
5848fe615baSAndrew Gallatin 	}
5858fe615baSAndrew Gallatin 
5868fe615baSAndrew Gallatin 	/*
5878fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5888fe615baSAndrew Gallatin 	 */
5898fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5908fe615baSAndrew Gallatin 
5918fe615baSAndrew Gallatin 	/*
5928fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
593a4b233ddSAndrew Gallatin 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5948fe615baSAndrew Gallatin 	 */
595a4b233ddSAndrew Gallatin 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
596a4b233ddSAndrew Gallatin 		return 0;
5978fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5988fe615baSAndrew Gallatin 	if (status == 0)
5998fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
6008fe615baSAndrew Gallatin 
6018fe615baSAndrew Gallatin 	if (status != E2BIG)
6028fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
6038fe615baSAndrew Gallatin 	if (status == ENOSYS)
6048fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
6058fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
6068fe615baSAndrew Gallatin 	return status;
6078fe615baSAndrew Gallatin }
6088fe615baSAndrew Gallatin 
6098fe615baSAndrew Gallatin static int
6106d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
611b2fc195eSAndrew Gallatin {
6128fe615baSAndrew Gallatin 	int aligned = 0;
61365c69066SAndrew Gallatin 	int force_firmware = mxge_force_firmware;
614b2fc195eSAndrew Gallatin 
61565c69066SAndrew Gallatin 	if (sc->throttle)
61665c69066SAndrew Gallatin 		force_firmware = sc->throttle;
617d91b1b49SAndrew Gallatin 
61865c69066SAndrew Gallatin 	if (force_firmware != 0) {
61965c69066SAndrew Gallatin 		if (force_firmware == 1)
620d91b1b49SAndrew Gallatin 			aligned = 1;
621d91b1b49SAndrew Gallatin 		else
622d91b1b49SAndrew Gallatin 			aligned = 0;
623d91b1b49SAndrew Gallatin 		if (mxge_verbose)
624d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
625d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
626d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
627d91b1b49SAndrew Gallatin 		goto abort;
628d91b1b49SAndrew Gallatin 	}
629d91b1b49SAndrew Gallatin 
630d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
631d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
632d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
633d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
634d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
635d91b1b49SAndrew Gallatin 			      sc->link_width);
636d91b1b49SAndrew Gallatin 		aligned = 1;
637d91b1b49SAndrew Gallatin 		goto abort;
638d91b1b49SAndrew Gallatin 	}
639d91b1b49SAndrew Gallatin 
6408fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6418fe615baSAndrew Gallatin 		return 0;
642b2fc195eSAndrew Gallatin 
643b2fc195eSAndrew Gallatin abort:
644b2fc195eSAndrew Gallatin 	if (aligned) {
6456d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6461e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
647b2fc195eSAndrew Gallatin 	} else {
6486d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6491e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
650b2fc195eSAndrew Gallatin 	}
6511e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
652b2fc195eSAndrew Gallatin }
653b2fc195eSAndrew Gallatin 
6544da0d523SAndrew Gallatin static int
6554da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6564da0d523SAndrew Gallatin {
657b824b7d8SAndrew Gallatin 
6584da0d523SAndrew Gallatin 
6594da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6604da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6614da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6624da0d523SAndrew Gallatin 		return EIO;
6634da0d523SAndrew Gallatin 	}
6644da0d523SAndrew Gallatin 
6654da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
666dedbe836SAndrew Gallatin 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6674da0d523SAndrew Gallatin 	if (mxge_verbose)
6684da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6694da0d523SAndrew Gallatin 
670b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
671b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6724da0d523SAndrew Gallatin 
673b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
674b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6754da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6764da0d523SAndrew Gallatin 			      sc->fw_version);
6774da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6784da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6794da0d523SAndrew Gallatin 		return EINVAL;
6804da0d523SAndrew Gallatin 	}
6814da0d523SAndrew Gallatin 	return 0;
6824da0d523SAndrew Gallatin 
6834da0d523SAndrew Gallatin }
684b2fc195eSAndrew Gallatin 
685f9ae0280SAndrew Gallatin static void *
686f9ae0280SAndrew Gallatin z_alloc(void *nil, u_int items, u_int size)
687f9ae0280SAndrew Gallatin {
688f9ae0280SAndrew Gallatin 	void *ptr;
689f9ae0280SAndrew Gallatin 
690f9ae0280SAndrew Gallatin 	ptr = malloc(items * size, M_TEMP, M_NOWAIT);
691f9ae0280SAndrew Gallatin 	return ptr;
692f9ae0280SAndrew Gallatin }
693f9ae0280SAndrew Gallatin 
694f9ae0280SAndrew Gallatin static void
695f9ae0280SAndrew Gallatin z_free(void *nil, void *ptr)
696f9ae0280SAndrew Gallatin {
697f9ae0280SAndrew Gallatin 	free(ptr, M_TEMP);
698f9ae0280SAndrew Gallatin }
699f9ae0280SAndrew Gallatin 
700f9ae0280SAndrew Gallatin 
701b2fc195eSAndrew Gallatin static int
7026d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
703b2fc195eSAndrew Gallatin {
704f9ae0280SAndrew Gallatin 	z_stream zs;
705f9ae0280SAndrew Gallatin 	char *inflate_buffer;
70633d54970SLuigi Rizzo 	const struct firmware *fw;
707b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
708b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
709b2fc195eSAndrew Gallatin 	int status;
7104da0d523SAndrew Gallatin 	unsigned int i;
7114da0d523SAndrew Gallatin 	char dummy;
712f9ae0280SAndrew Gallatin 	size_t fw_len;
713b2fc195eSAndrew Gallatin 
714b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
715b2fc195eSAndrew Gallatin 	if (fw == NULL) {
716b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
717b2fc195eSAndrew Gallatin 			      sc->fw_name);
718b2fc195eSAndrew Gallatin 		return ENOENT;
719b2fc195eSAndrew Gallatin 	}
720b2fc195eSAndrew Gallatin 
721f9ae0280SAndrew Gallatin 
722f9ae0280SAndrew Gallatin 
723f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
724f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
725f9ae0280SAndrew Gallatin 	zs.zalloc = z_alloc;
726f9ae0280SAndrew Gallatin 	zs.zfree = z_free;
727f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
728f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
729b2fc195eSAndrew Gallatin 		status = EIO;
730b2fc195eSAndrew Gallatin 		goto abort_with_fw;
731b2fc195eSAndrew Gallatin 	}
732f9ae0280SAndrew Gallatin 
733f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
734f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
735f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
736f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
737f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
738f9ae0280SAndrew Gallatin 		goto abort_with_zs;
739f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
740f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
741f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
742f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
743f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
744f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
745f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
746f9ae0280SAndrew Gallatin 		status = EIO;
747f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
748f9ae0280SAndrew Gallatin 	}
749f9ae0280SAndrew Gallatin 
750f9ae0280SAndrew Gallatin 	/* check id */
751f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
752f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
753f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
754f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
755f9ae0280SAndrew Gallatin 		status = EIO;
756f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
757f9ae0280SAndrew Gallatin 	}
758f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
759b2fc195eSAndrew Gallatin 
7604da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7614da0d523SAndrew Gallatin 	if (status != 0)
762f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
763b2fc195eSAndrew Gallatin 
764b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
765f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7664da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
767f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
768f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
76973c7c83fSAndrew Gallatin 		wmb();
7704da0d523SAndrew Gallatin 		dummy = *sc->sram;
77173c7c83fSAndrew Gallatin 		wmb();
7724da0d523SAndrew Gallatin 	}
773b2fc195eSAndrew Gallatin 
774f9ae0280SAndrew Gallatin 	*limit = fw_len;
775b2fc195eSAndrew Gallatin 	status = 0;
776f9ae0280SAndrew Gallatin abort_with_buffer:
777f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
778f9ae0280SAndrew Gallatin abort_with_zs:
779f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
780b2fc195eSAndrew Gallatin abort_with_fw:
781b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
782b2fc195eSAndrew Gallatin 	return status;
783b2fc195eSAndrew Gallatin }
784b2fc195eSAndrew Gallatin 
785b2fc195eSAndrew Gallatin /*
786b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
787b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
788b2fc195eSAndrew Gallatin  */
789b2fc195eSAndrew Gallatin 
790b2fc195eSAndrew Gallatin static void
7916d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
792b2fc195eSAndrew Gallatin {
793b2fc195eSAndrew Gallatin 	char buf_bytes[72];
794b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
795b2fc195eSAndrew Gallatin 	volatile char *submit;
796b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
797b2fc195eSAndrew Gallatin 	int i;
798b2fc195eSAndrew Gallatin 
799b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
800b2fc195eSAndrew Gallatin 
801b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
802b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
803b2fc195eSAndrew Gallatin 	*confirm = 0;
80473c7c83fSAndrew Gallatin 	wmb();
805b2fc195eSAndrew Gallatin 
806b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
807b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
808b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
809b2fc195eSAndrew Gallatin 	*/
810b2fc195eSAndrew Gallatin 
8116d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8126d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
813b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
814b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
815b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
8166d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
8176d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
818b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
819b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
820b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
821b2fc195eSAndrew Gallatin 
822b2fc195eSAndrew Gallatin 
8230fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
824b2fc195eSAndrew Gallatin 
8256d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
82673c7c83fSAndrew Gallatin 	wmb();
827b2fc195eSAndrew Gallatin 	DELAY(1000);
82873c7c83fSAndrew Gallatin 	wmb();
829b2fc195eSAndrew Gallatin 	i = 0;
830b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
831b2fc195eSAndrew Gallatin 		DELAY(1000);
832b2fc195eSAndrew Gallatin 		i++;
833b2fc195eSAndrew Gallatin 	}
834b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
835b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
836b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
837b2fc195eSAndrew Gallatin 			      *confirm);
838b2fc195eSAndrew Gallatin 	}
839b2fc195eSAndrew Gallatin 	return;
840b2fc195eSAndrew Gallatin }
841b2fc195eSAndrew Gallatin 
842b2fc195eSAndrew Gallatin static int
8436d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
844b2fc195eSAndrew Gallatin {
845b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
846b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
847b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8480fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
849b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
850e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
851b2fc195eSAndrew Gallatin 
852b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
853b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
854b2fc195eSAndrew Gallatin 
855b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
856b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
857b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
858b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8596d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8606d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
861b2fc195eSAndrew Gallatin 
862b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
863b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
864a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
865b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
86673c7c83fSAndrew Gallatin 	wmb();
8676d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
868b2fc195eSAndrew Gallatin 
8695e7d8541SAndrew Gallatin 	/* wait up to 20ms */
870e0501fd0SAndrew Gallatin 	err = EAGAIN;
8715e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
872b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
873b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
87473c7c83fSAndrew Gallatin 		wmb();
875e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
876e0501fd0SAndrew Gallatin 		case 0:
877b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
878e0501fd0SAndrew Gallatin 			err = 0;
879e0501fd0SAndrew Gallatin 			break;
880e0501fd0SAndrew Gallatin 		case 0xffffffff:
881e0501fd0SAndrew Gallatin 			DELAY(1000);
882e0501fd0SAndrew Gallatin 			break;
883e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
884e0501fd0SAndrew Gallatin 			err = ENOSYS;
885e0501fd0SAndrew Gallatin 			break;
886e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
887e0501fd0SAndrew Gallatin 			err = E2BIG;
888e0501fd0SAndrew Gallatin 			break;
889c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
890c587e59fSAndrew Gallatin 			err = EBUSY;
891c587e59fSAndrew Gallatin 			break;
892c406ad2eSAndrew Gallatin 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
893c406ad2eSAndrew Gallatin 			err = ENXIO;
894c406ad2eSAndrew Gallatin 			break;
895e0501fd0SAndrew Gallatin 		default:
896b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8976d87a65dSAndrew Gallatin 				      "mxge: command %d "
898b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
899b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
900e0501fd0SAndrew Gallatin 			err = ENXIO;
901e0501fd0SAndrew Gallatin 			break;
902b2fc195eSAndrew Gallatin 		}
903e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
904e0501fd0SAndrew Gallatin 			break;
905b2fc195eSAndrew Gallatin 	}
906e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
9076d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
908b2fc195eSAndrew Gallatin 			      "result = %d\n",
909b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
910e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
911e0501fd0SAndrew Gallatin 	return err;
912b2fc195eSAndrew Gallatin }
913b2fc195eSAndrew Gallatin 
9144da0d523SAndrew Gallatin static int
9154da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
9164da0d523SAndrew Gallatin {
9174da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
9184da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
9194da0d523SAndrew Gallatin 	size_t hdr_offset;
9204da0d523SAndrew Gallatin 	int status;
9214da0d523SAndrew Gallatin 
9224da0d523SAndrew Gallatin 	/* find running firmware header */
9234da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9244da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9254da0d523SAndrew Gallatin 
9264da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9274da0d523SAndrew Gallatin 		device_printf(sc->dev,
9284da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9294da0d523SAndrew Gallatin 			      (int)hdr_offset);
9304da0d523SAndrew Gallatin 		return EIO;
9314da0d523SAndrew Gallatin 	}
9324da0d523SAndrew Gallatin 
9334da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9344da0d523SAndrew Gallatin 	 * validate firmware */
9354da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9364da0d523SAndrew Gallatin 	if (hdr == NULL) {
9374da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9384da0d523SAndrew Gallatin 		return ENOMEM;
9394da0d523SAndrew Gallatin 	}
9404da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9414da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9424da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9434da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9444da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
945b824b7d8SAndrew Gallatin 
946b824b7d8SAndrew Gallatin 	/*
947b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
948b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
949b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
950b824b7d8SAndrew Gallatin 	 */
951b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
952b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
953b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
954b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
955b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
956b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
957b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
958b824b7d8SAndrew Gallatin 	}
959b824b7d8SAndrew Gallatin 
9604da0d523SAndrew Gallatin 	return status;
9614da0d523SAndrew Gallatin }
9624da0d523SAndrew Gallatin 
963b2fc195eSAndrew Gallatin 
964b2fc195eSAndrew Gallatin static int
9651e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
966b2fc195eSAndrew Gallatin {
967b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
968b2fc195eSAndrew Gallatin 	volatile char *submit;
969b2fc195eSAndrew Gallatin 	char buf_bytes[72];
970b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
971b2fc195eSAndrew Gallatin 	int status, i;
972b2fc195eSAndrew Gallatin 
973b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
974b2fc195eSAndrew Gallatin 
975b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9766d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
977b2fc195eSAndrew Gallatin 	if (status) {
9781e413cf9SAndrew Gallatin 		if (!adopt)
9791e413cf9SAndrew Gallatin 			return status;
9804da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9814da0d523SAndrew Gallatin 		   it is new enough */
9824da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9834da0d523SAndrew Gallatin 		if (status) {
9844da0d523SAndrew Gallatin 			device_printf(sc->dev,
9854da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
986b2fc195eSAndrew Gallatin 			return status;
987b2fc195eSAndrew Gallatin 		}
9884da0d523SAndrew Gallatin 		device_printf(sc->dev,
9894da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9901e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9914da0d523SAndrew Gallatin 			device_printf(sc->dev,
9924da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9934da0d523SAndrew Gallatin 				 ".  For optimal\n");
9944da0d523SAndrew Gallatin 			device_printf(sc->dev,
9954da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9964da0d523SAndrew Gallatin 				 "firmware\n");
9974da0d523SAndrew Gallatin 		}
998d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9991e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
1000d91b1b49SAndrew Gallatin 		return 0;
10014da0d523SAndrew Gallatin 	}
1002b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
1003b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
1004b2fc195eSAndrew Gallatin 	*confirm = 0;
100573c7c83fSAndrew Gallatin 	wmb();
1006b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
1007b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
1008b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
1009b2fc195eSAndrew Gallatin 	*/
1010b2fc195eSAndrew Gallatin 
10116d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
10126d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
1013b2fc195eSAndrew Gallatin 
1014b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
1015b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
1016b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
1017b2fc195eSAndrew Gallatin 
1018b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
1019b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
1020b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
1021b2fc195eSAndrew Gallatin 	*/
1022b2fc195eSAndrew Gallatin 					/* where the code starts*/
10236d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1024b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1025b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1026b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1027b2fc195eSAndrew Gallatin 
10280fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10296d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
103073c7c83fSAndrew Gallatin 	wmb();
1031b2fc195eSAndrew Gallatin 	DELAY(1000);
103273c7c83fSAndrew Gallatin 	wmb();
1033b2fc195eSAndrew Gallatin 	i = 0;
1034b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1035b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1036b2fc195eSAndrew Gallatin 		i++;
1037b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1038b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1039b2fc195eSAndrew Gallatin 	}
1040b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1041b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1042b2fc195eSAndrew Gallatin 			confirm, *confirm);
1043b2fc195eSAndrew Gallatin 
1044b2fc195eSAndrew Gallatin 		return ENXIO;
1045b2fc195eSAndrew Gallatin 	}
1046b2fc195eSAndrew Gallatin 	return 0;
1047b2fc195eSAndrew Gallatin }
1048b2fc195eSAndrew Gallatin 
1049b2fc195eSAndrew Gallatin static int
10506d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1051b2fc195eSAndrew Gallatin {
10526d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1053b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1054b2fc195eSAndrew Gallatin 	int status;
1055b2fc195eSAndrew Gallatin 
1056b2fc195eSAndrew Gallatin 
1057b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1058b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1059b2fc195eSAndrew Gallatin 
1060b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1061b2fc195eSAndrew Gallatin 
10625e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1063b2fc195eSAndrew Gallatin 	return status;
1064b2fc195eSAndrew Gallatin }
1065b2fc195eSAndrew Gallatin 
1066b2fc195eSAndrew Gallatin static int
10676d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1068b2fc195eSAndrew Gallatin {
10696d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1070b2fc195eSAndrew Gallatin 	int status;
1071b2fc195eSAndrew Gallatin 
1072b2fc195eSAndrew Gallatin 	if (pause)
10735e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1074b2fc195eSAndrew Gallatin 				       &cmd);
1075b2fc195eSAndrew Gallatin 	else
10765e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1077b2fc195eSAndrew Gallatin 				       &cmd);
1078b2fc195eSAndrew Gallatin 
1079b2fc195eSAndrew Gallatin 	if (status) {
1080b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1081b2fc195eSAndrew Gallatin 		return ENXIO;
1082b2fc195eSAndrew Gallatin 	}
1083b2fc195eSAndrew Gallatin 	sc->pause = pause;
1084b2fc195eSAndrew Gallatin 	return 0;
1085b2fc195eSAndrew Gallatin }
1086b2fc195eSAndrew Gallatin 
1087b2fc195eSAndrew Gallatin static void
10886d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1089b2fc195eSAndrew Gallatin {
10906d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1091b2fc195eSAndrew Gallatin 	int status;
1092b2fc195eSAndrew Gallatin 
10931e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10941e413cf9SAndrew Gallatin 		promisc = 1;
10951e413cf9SAndrew Gallatin 
1096b2fc195eSAndrew Gallatin 	if (promisc)
10975e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1098b2fc195eSAndrew Gallatin 				       &cmd);
1099b2fc195eSAndrew Gallatin 	else
11005e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1101b2fc195eSAndrew Gallatin 				       &cmd);
1102b2fc195eSAndrew Gallatin 
1103b2fc195eSAndrew Gallatin 	if (status) {
1104b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1105b2fc195eSAndrew Gallatin 	}
1106b2fc195eSAndrew Gallatin }
1107b2fc195eSAndrew Gallatin 
11080fa7f681SAndrew Gallatin static void
11090fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
11100fa7f681SAndrew Gallatin {
11110fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
11120fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
11130fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
11140fa7f681SAndrew Gallatin 	int err;
11150fa7f681SAndrew Gallatin 
11160fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11170fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11180fa7f681SAndrew Gallatin 		return;
11190fa7f681SAndrew Gallatin 
11200fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11210fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11220fa7f681SAndrew Gallatin 	if (err != 0) {
11230fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11240fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11250fa7f681SAndrew Gallatin 		return;
11260fa7f681SAndrew Gallatin 	}
11270fa7f681SAndrew Gallatin 
1128b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1129b824b7d8SAndrew Gallatin 		return;
11300fa7f681SAndrew Gallatin 
11310fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11320fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11330fa7f681SAndrew Gallatin 		return;
11340fa7f681SAndrew Gallatin 
11350fa7f681SAndrew Gallatin 	/* Flush all the filters */
11360fa7f681SAndrew Gallatin 
11370fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11380fa7f681SAndrew Gallatin 	if (err != 0) {
11390fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11400fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11410fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11420fa7f681SAndrew Gallatin 		return;
11430fa7f681SAndrew Gallatin 	}
11440fa7f681SAndrew Gallatin 
11450fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11460fa7f681SAndrew Gallatin 
1147eb956cd0SRobert Watson 	if_maddr_rlock(ifp);
11480fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11490fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11500fa7f681SAndrew Gallatin 			continue;
11510fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11520fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11530fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11540fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11550fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11560fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11570fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11580fa7f681SAndrew Gallatin 		if (err != 0) {
11590fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11600fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11610fa7f681SAndrew Gallatin 			       "%d\t", err);
11620fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
1163eb956cd0SRobert Watson 			if_maddr_runlock(ifp);
11640fa7f681SAndrew Gallatin 			return;
11650fa7f681SAndrew Gallatin 		}
11660fa7f681SAndrew Gallatin 	}
1167eb956cd0SRobert Watson 	if_maddr_runlock(ifp);
11680fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11690fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11700fa7f681SAndrew Gallatin 	if (err != 0) {
11710fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11720fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11730fa7f681SAndrew Gallatin 	}
11740fa7f681SAndrew Gallatin }
11750fa7f681SAndrew Gallatin 
1176b2fc195eSAndrew Gallatin static int
1177053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1178053e637fSAndrew Gallatin {
1179053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1180053e637fSAndrew Gallatin 	int status;
1181053e637fSAndrew Gallatin 
1182c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1183c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1184053e637fSAndrew Gallatin 
1185053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1186053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1187053e637fSAndrew Gallatin 	cmd.data0 = 0;
1188053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1189053e637fSAndrew Gallatin 			       &cmd);
1190053e637fSAndrew Gallatin 	if (status == 0)
1191c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1192053e637fSAndrew Gallatin 
1193053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1194053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1195053e637fSAndrew Gallatin }
1196053e637fSAndrew Gallatin 
1197053e637fSAndrew Gallatin static int
1198adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1199b2fc195eSAndrew Gallatin {
12001e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
12011e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
12021e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
12036d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
12041e413cf9SAndrew Gallatin 	int slice, status;
1205b2fc195eSAndrew Gallatin 
1206b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1207b2fc195eSAndrew Gallatin 	   is alive */
1208b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
12095e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1210b2fc195eSAndrew Gallatin 	if (status != 0) {
1211b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1212b2fc195eSAndrew Gallatin 		return ENXIO;
1213b2fc195eSAndrew Gallatin 	}
1214b2fc195eSAndrew Gallatin 
1215091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1216091feecdSAndrew Gallatin 
12171e413cf9SAndrew Gallatin 
12181e413cf9SAndrew Gallatin 	/* set the intrq size */
12191e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12201e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12211e413cf9SAndrew Gallatin 
12221e413cf9SAndrew Gallatin 	/*
12231e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12241e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12251e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12261e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12271e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12281e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12291e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12301e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12311e413cf9SAndrew Gallatin 	 */
12321e413cf9SAndrew Gallatin 
12331e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12341e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12351e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12361e413cf9SAndrew Gallatin 					   &cmd);
12371e413cf9SAndrew Gallatin 		if (status != 0) {
12381e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12391e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12401e413cf9SAndrew Gallatin 			return status;
12411e413cf9SAndrew Gallatin 		}
12421e413cf9SAndrew Gallatin 		/*
12431e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12441e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12451e413cf9SAndrew Gallatin 		 */
12461e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12471e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1248c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1249c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1250c6cb3e3fSAndrew Gallatin #endif
12511e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12521e413cf9SAndrew Gallatin 					   &cmd);
12531e413cf9SAndrew Gallatin 		if (status != 0) {
12541e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12551e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12561e413cf9SAndrew Gallatin 			return status;
12571e413cf9SAndrew Gallatin 		}
12581e413cf9SAndrew Gallatin 	}
12591e413cf9SAndrew Gallatin 
12601e413cf9SAndrew Gallatin 
1261adae7080SAndrew Gallatin 	if (interrupts_setup) {
1262b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12631e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12641e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12651e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12661e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12671e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12681e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12691e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12701e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12711e413cf9SAndrew Gallatin 						&cmd);
12721e413cf9SAndrew Gallatin 		}
1273adae7080SAndrew Gallatin 	}
1274b2fc195eSAndrew Gallatin 
12756d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12765e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12775e7d8541SAndrew Gallatin 
12785e7d8541SAndrew Gallatin 
12795e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12805e7d8541SAndrew Gallatin 
12815e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12821e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12835e7d8541SAndrew Gallatin 
12845e7d8541SAndrew Gallatin 
12855e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12866d87a65dSAndrew Gallatin 				&cmd);
12875e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1288b2fc195eSAndrew Gallatin 	if (status != 0) {
1289b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1290b2fc195eSAndrew Gallatin 		return status;
1291b2fc195eSAndrew Gallatin 	}
1292b2fc195eSAndrew Gallatin 
12935e7d8541SAndrew Gallatin 
12945e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12955e7d8541SAndrew Gallatin 
12965e7d8541SAndrew Gallatin 
12975e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12988fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12995e7d8541SAndrew Gallatin 
13001e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
13011e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
13021e413cf9SAndrew Gallatin 
13031e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1304b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
13051e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
13061e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
13071e413cf9SAndrew Gallatin 		ss->tx.req = 0;
13081e413cf9SAndrew Gallatin 		ss->tx.done = 0;
13091e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1310c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1311c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1312c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
13131e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
13141e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
13151e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
13161e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
13171e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
131826dd49c6SAndrew Gallatin 		ss->lc.lro_bad_csum = 0;
131926dd49c6SAndrew Gallatin 		ss->lc.lro_queued = 0;
132026dd49c6SAndrew Gallatin 		ss->lc.lro_flushed = 0;
13211e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
1322a393336bSAndrew Gallatin 			bzero(ss->fw_stats, sizeof *ss->fw_stats);
13231e413cf9SAndrew Gallatin 		}
13241e413cf9SAndrew Gallatin 	}
1325b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13266d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1327bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13286d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13290fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
133065c69066SAndrew Gallatin 	if (sc->throttle) {
133165c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
133265c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
133365c69066SAndrew Gallatin 				  &cmd)) {
133465c69066SAndrew Gallatin 			device_printf(sc->dev,
133565c69066SAndrew Gallatin 				      "can't enable throttle\n");
133665c69066SAndrew Gallatin 		}
133765c69066SAndrew Gallatin 	}
1338b2fc195eSAndrew Gallatin 	return status;
1339b2fc195eSAndrew Gallatin }
1340b2fc195eSAndrew Gallatin 
1341b2fc195eSAndrew Gallatin static int
134265c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
134365c69066SAndrew Gallatin {
134465c69066SAndrew Gallatin 	mxge_cmd_t cmd;
134565c69066SAndrew Gallatin 	mxge_softc_t *sc;
134665c69066SAndrew Gallatin 	int err;
134765c69066SAndrew Gallatin 	unsigned int throttle;
134865c69066SAndrew Gallatin 
134965c69066SAndrew Gallatin 	sc = arg1;
135065c69066SAndrew Gallatin 	throttle = sc->throttle;
135165c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
135265c69066SAndrew Gallatin 	if (err != 0) {
135365c69066SAndrew Gallatin 		return err;
135465c69066SAndrew Gallatin 	}
135565c69066SAndrew Gallatin 
135665c69066SAndrew Gallatin 	if (throttle == sc->throttle)
135765c69066SAndrew Gallatin 		return 0;
135865c69066SAndrew Gallatin 
135965c69066SAndrew Gallatin 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
136065c69066SAndrew Gallatin 		return EINVAL;
136165c69066SAndrew Gallatin 
136265c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
136365c69066SAndrew Gallatin 	cmd.data0 = throttle;
136465c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
136565c69066SAndrew Gallatin 	if (err == 0)
136665c69066SAndrew Gallatin 		sc->throttle = throttle;
136765c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
136865c69066SAndrew Gallatin 	return err;
136965c69066SAndrew Gallatin }
137065c69066SAndrew Gallatin 
137165c69066SAndrew Gallatin static int
13726d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1373b2fc195eSAndrew Gallatin {
13746d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1375b2fc195eSAndrew Gallatin 	unsigned int intr_coal_delay;
1376b2fc195eSAndrew Gallatin 	int err;
1377b2fc195eSAndrew Gallatin 
1378b2fc195eSAndrew Gallatin 	sc = arg1;
1379b2fc195eSAndrew Gallatin 	intr_coal_delay = sc->intr_coal_delay;
1380b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1381b2fc195eSAndrew Gallatin 	if (err != 0) {
1382b2fc195eSAndrew Gallatin 		return err;
1383b2fc195eSAndrew Gallatin 	}
1384b2fc195eSAndrew Gallatin 	if (intr_coal_delay == sc->intr_coal_delay)
1385b2fc195eSAndrew Gallatin 		return 0;
1386b2fc195eSAndrew Gallatin 
1387b2fc195eSAndrew Gallatin 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1388b2fc195eSAndrew Gallatin 		return EINVAL;
1389b2fc195eSAndrew Gallatin 
1390a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13915e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1392b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13935e7d8541SAndrew Gallatin 
1394a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1395b2fc195eSAndrew Gallatin 	return err;
1396b2fc195eSAndrew Gallatin }
1397b2fc195eSAndrew Gallatin 
1398b2fc195eSAndrew Gallatin static int
13996d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1400b2fc195eSAndrew Gallatin {
14016d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1402b2fc195eSAndrew Gallatin 	unsigned int enabled;
1403b2fc195eSAndrew Gallatin 	int err;
1404b2fc195eSAndrew Gallatin 
1405b2fc195eSAndrew Gallatin 	sc = arg1;
1406b2fc195eSAndrew Gallatin 	enabled = sc->pause;
1407b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
1408b2fc195eSAndrew Gallatin 	if (err != 0) {
1409b2fc195eSAndrew Gallatin 		return err;
1410b2fc195eSAndrew Gallatin 	}
1411b2fc195eSAndrew Gallatin 	if (enabled == sc->pause)
1412b2fc195eSAndrew Gallatin 		return 0;
1413b2fc195eSAndrew Gallatin 
1414a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
14156d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1416a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1417b2fc195eSAndrew Gallatin 	return err;
1418b2fc195eSAndrew Gallatin }
1419b2fc195eSAndrew Gallatin 
1420b2fc195eSAndrew Gallatin static int
14216d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1422b2fc195eSAndrew Gallatin {
1423b2fc195eSAndrew Gallatin 	int err;
1424b2fc195eSAndrew Gallatin 
1425b2fc195eSAndrew Gallatin 	if (arg1 == NULL)
1426b2fc195eSAndrew Gallatin 		return EFAULT;
1427b2fc195eSAndrew Gallatin 	arg2 = be32toh(*(int *)arg1);
1428b2fc195eSAndrew Gallatin 	arg1 = NULL;
1429b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, arg1, arg2, req);
1430b2fc195eSAndrew Gallatin 
1431b2fc195eSAndrew Gallatin 	return err;
1432b2fc195eSAndrew Gallatin }
1433b2fc195eSAndrew Gallatin 
1434b2fc195eSAndrew Gallatin static void
14351e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14361e413cf9SAndrew Gallatin {
14371e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14381e413cf9SAndrew Gallatin 	int slice;
14391e413cf9SAndrew Gallatin 
14401e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14411e413cf9SAndrew Gallatin 		return;
14421e413cf9SAndrew Gallatin 
14431e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14441e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14451e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14461e413cf9SAndrew Gallatin 			continue;
14471e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14481e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14491e413cf9SAndrew Gallatin 	}
14501e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14511e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14521e413cf9SAndrew Gallatin }
14531e413cf9SAndrew Gallatin 
14541e413cf9SAndrew Gallatin static void
14556d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1456b2fc195eSAndrew Gallatin {
1457b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1458b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14595e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14601e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14611e413cf9SAndrew Gallatin 	int slice;
14621e413cf9SAndrew Gallatin 	char slice_num[8];
1463b2fc195eSAndrew Gallatin 
1464b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1465b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14661e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1467b2fc195eSAndrew Gallatin 
14685e7d8541SAndrew Gallatin 	/* random information */
14695e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14705e7d8541SAndrew Gallatin 		       "firmware_version",
1471f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->fw_version,
14725e7d8541SAndrew Gallatin 		       0, "firmware version");
14735e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14745e7d8541SAndrew Gallatin 		       "serial_number",
1475f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->serial_number_string,
14765e7d8541SAndrew Gallatin 		       0, "serial number");
14775e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14785e7d8541SAndrew Gallatin 		       "product_code",
1479f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->product_code_string,
14805e7d8541SAndrew Gallatin 		       0, "product_code");
14815e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1482d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1483d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1484d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1485d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14865e7d8541SAndrew Gallatin 		       "tx_boundary",
14871e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14885e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14895e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1490091feecdSAndrew Gallatin 		       "write_combine",
1491091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1492091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1493091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14945e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14955e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14965e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14975e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14985e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14995e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
15005e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
15015e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15025e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
15035e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
15045e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
1505a393336bSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1506a393336bSAndrew Gallatin 		       "watchdog_resets",
1507a393336bSAndrew Gallatin 		       CTLFLAG_RD, &sc->watchdog_resets,
1508a393336bSAndrew Gallatin 		       0, "Number of times NIC was reset");
15095e7d8541SAndrew Gallatin 
15105e7d8541SAndrew Gallatin 
15115e7d8541SAndrew Gallatin 	/* performance related tunables */
1512b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1513b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1514b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15156d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1516b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1517b2fc195eSAndrew Gallatin 
1518b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
151965c69066SAndrew Gallatin 			"throttle",
152065c69066SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
152165c69066SAndrew Gallatin 			0, mxge_change_throttle,
152265c69066SAndrew Gallatin 			"I", "transmit throttling");
152365c69066SAndrew Gallatin 
152465c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1525b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1526b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15276d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1528b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1529b2fc195eSAndrew Gallatin 
1530b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15315e7d8541SAndrew Gallatin 		       "deassert_wait",
15325e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15335e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1534b2fc195eSAndrew Gallatin 
1535b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1536b2fc195eSAndrew Gallatin 	   Need to swap it */
1537b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1538b2fc195eSAndrew Gallatin 			"link_up",
1539b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15406d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1541b2fc195eSAndrew Gallatin 			"I", "link up");
1542b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1543b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1544b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15456d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1546b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1547b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1548adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1549adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1550adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15516d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1552adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1553adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1554adae7080SAndrew Gallatin 			"dropped_bad_phy",
1555adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1556adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1557adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1558adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1559b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1560b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1561b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1562b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
15636d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1564b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1565b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1566adae7080SAndrew Gallatin 			"dropped_link_overflow",
1567adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1568adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1569adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1570adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15710fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
15720fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
15730fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
15740fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
15750fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
15760fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1577adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1578adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
15796d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1580adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1581b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1582b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1583b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1584b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
15856d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1586b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1587b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1588adae7080SAndrew Gallatin 			"dropped_overrun",
1589adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
15906d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1591adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1592adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1593adae7080SAndrew Gallatin 			"dropped_pause",
1594adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1595adae7080SAndrew Gallatin 			&fw->dropped_pause,
1596adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1597adae7080SAndrew Gallatin 			"I", "dropped_pause");
1598adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1599adae7080SAndrew Gallatin 			"dropped_runt",
1600adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1601adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1602adae7080SAndrew Gallatin 			"I", "dropped_runt");
1603b2fc195eSAndrew Gallatin 
1604a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1605a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1606a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1607a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1608a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1609a0394e33SAndrew Gallatin 
16105e7d8541SAndrew Gallatin 	/* verbose printing? */
1611b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16125e7d8541SAndrew Gallatin 		       "verbose",
16135e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
16145e7d8541SAndrew Gallatin 		       0, "verbose printing");
1615b2fc195eSAndrew Gallatin 
16161e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
16171e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
16181e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
16191e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
16201e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
16211e413cf9SAndrew Gallatin 
16221e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16231e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16241e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16251e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16261e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16271e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16281e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16291e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16301e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
16311e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1632053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16331e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16341e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16351e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16361e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16371e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16381e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16391e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
1640*e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
164126dd49c6SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lc.lro_flushed,
1642053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1643053e637fSAndrew Gallatin 
1644*e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
164526dd49c6SAndrew Gallatin 			       "lro_bad_csum", CTLFLAG_RD, &ss->lc.lro_bad_csum,
164626dd49c6SAndrew Gallatin 			       0, "number of bad csums preventing LRO");
164726dd49c6SAndrew Gallatin 
1648*e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
164926dd49c6SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lc.lro_queued,
16501e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16511e413cf9SAndrew Gallatin 			       "queues");
1652053e637fSAndrew Gallatin 
1653c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16541e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16551e413cf9SAndrew Gallatin 		if (slice > 0)
16561e413cf9SAndrew Gallatin 			continue;
1657c6cb3e3fSAndrew Gallatin #endif
1658c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1659c6cb3e3fSAndrew Gallatin 			       "tx_req",
1660c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1661c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16621e413cf9SAndrew Gallatin 
16631e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16641e413cf9SAndrew Gallatin 			       "tx_done",
16651e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16661e413cf9SAndrew Gallatin 			       0, "tx_done");
16671e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16681e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16691e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16701e413cf9SAndrew Gallatin 			       0, "tx_done");
16711e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16721e413cf9SAndrew Gallatin 			       "tx_stall",
16731e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16741e413cf9SAndrew Gallatin 			       0, "tx_stall");
16751e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16761e413cf9SAndrew Gallatin 			       "tx_wake",
16771e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16781e413cf9SAndrew Gallatin 			       0, "tx_wake");
16791e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16801e413cf9SAndrew Gallatin 			       "tx_defrag",
16811e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16821e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1683c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1684c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1685c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1686c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1687c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1688c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1689c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1690c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1691c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1692c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1693c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1694c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
16951e413cf9SAndrew Gallatin 	}
1696b2fc195eSAndrew Gallatin }
1697b2fc195eSAndrew Gallatin 
1698b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1699b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1700b2fc195eSAndrew Gallatin 
1701b2fc195eSAndrew Gallatin static inline void
17021e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1703b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1704b2fc195eSAndrew Gallatin {
1705b2fc195eSAndrew Gallatin 	int idx, starting_slot;
1706b2fc195eSAndrew Gallatin 	starting_slot = tx->req;
1707b2fc195eSAndrew Gallatin 	while (cnt > 1) {
1708b2fc195eSAndrew Gallatin 		cnt--;
1709b2fc195eSAndrew Gallatin 		idx = (starting_slot + cnt) & tx->mask;
17106d87a65dSAndrew Gallatin 		mxge_pio_copy(&tx->lanai[idx],
1711b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
171273c7c83fSAndrew Gallatin 		wmb();
1713b2fc195eSAndrew Gallatin 	}
1714b2fc195eSAndrew Gallatin }
1715b2fc195eSAndrew Gallatin 
1716b2fc195eSAndrew Gallatin /*
1717b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1718b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1719b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1720b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1721b2fc195eSAndrew Gallatin  */
1722b2fc195eSAndrew Gallatin 
1723b2fc195eSAndrew Gallatin static inline void
17241e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1725b2fc195eSAndrew Gallatin 		  int cnt)
1726b2fc195eSAndrew Gallatin {
1727b2fc195eSAndrew Gallatin 	int idx, i;
1728b2fc195eSAndrew Gallatin 	uint32_t *src_ints;
1729b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1730b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *srcp;
1731b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
17325e7d8541SAndrew Gallatin 	uint8_t last_flags;
1733b2fc195eSAndrew Gallatin 
1734b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1735b2fc195eSAndrew Gallatin 
17365e7d8541SAndrew Gallatin 	last_flags = src->flags;
17375e7d8541SAndrew Gallatin 	src->flags = 0;
173873c7c83fSAndrew Gallatin 	wmb();
1739b2fc195eSAndrew Gallatin 	dst = dstp = &tx->lanai[idx];
1740b2fc195eSAndrew Gallatin 	srcp = src;
1741b2fc195eSAndrew Gallatin 
1742b2fc195eSAndrew Gallatin 	if ((idx + cnt) < tx->mask) {
1743b2fc195eSAndrew Gallatin 		for (i = 0; i < (cnt - 1); i += 2) {
17446d87a65dSAndrew Gallatin 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
174573c7c83fSAndrew Gallatin 			wmb(); /* force write every 32 bytes */
1746b2fc195eSAndrew Gallatin 			srcp += 2;
1747b2fc195eSAndrew Gallatin 			dstp += 2;
1748b2fc195eSAndrew Gallatin 		}
1749b2fc195eSAndrew Gallatin 	} else {
1750b2fc195eSAndrew Gallatin 		/* submit all but the first request, and ensure
1751b2fc195eSAndrew Gallatin 		   that it is submitted below */
17526d87a65dSAndrew Gallatin 		mxge_submit_req_backwards(tx, src, cnt);
1753b2fc195eSAndrew Gallatin 		i = 0;
1754b2fc195eSAndrew Gallatin 	}
1755b2fc195eSAndrew Gallatin 	if (i < cnt) {
1756b2fc195eSAndrew Gallatin 		/* submit the first request */
17576d87a65dSAndrew Gallatin 		mxge_pio_copy(dstp, srcp, sizeof(*src));
175873c7c83fSAndrew Gallatin 		wmb(); /* barrier before setting valid flag */
1759b2fc195eSAndrew Gallatin 	}
1760b2fc195eSAndrew Gallatin 
1761b2fc195eSAndrew Gallatin 	/* re-write the last 32-bits with the valid flags */
17625e7d8541SAndrew Gallatin 	src->flags = last_flags;
1763b2fc195eSAndrew Gallatin 	src_ints = (uint32_t *)src;
1764b2fc195eSAndrew Gallatin 	src_ints+=3;
1765b2fc195eSAndrew Gallatin 	dst_ints = (volatile uint32_t *)dst;
1766b2fc195eSAndrew Gallatin 	dst_ints+=3;
1767b2fc195eSAndrew Gallatin 	*dst_ints =  *src_ints;
1768b2fc195eSAndrew Gallatin 	tx->req += cnt;
176973c7c83fSAndrew Gallatin 	wmb();
1770b2fc195eSAndrew Gallatin }
1771b2fc195eSAndrew Gallatin 
17720a7a780eSAndrew Gallatin static int
17730a7a780eSAndrew Gallatin mxge_parse_tx(struct mxge_slice_state *ss, struct mbuf *m,
17740a7a780eSAndrew Gallatin     struct mxge_pkt_info *pi)
17750a7a780eSAndrew Gallatin {
17760a7a780eSAndrew Gallatin 	struct ether_vlan_header *eh;
17770a7a780eSAndrew Gallatin 	uint16_t etype;
17780a7a780eSAndrew Gallatin 	int tso = m->m_pkthdr.csum_flags & (CSUM_TSO);
17790a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17800a7a780eSAndrew Gallatin 	int nxt;
17810a7a780eSAndrew Gallatin #endif
17820a7a780eSAndrew Gallatin 
17830a7a780eSAndrew Gallatin 	eh = mtod(m, struct ether_vlan_header *);
17840a7a780eSAndrew Gallatin 	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
17850a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_proto);
17860a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
17870a7a780eSAndrew Gallatin 	} else {
17880a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_encap_proto);
17890a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN;
17900a7a780eSAndrew Gallatin 	}
17910a7a780eSAndrew Gallatin 
17920a7a780eSAndrew Gallatin 	switch (etype) {
17930a7a780eSAndrew Gallatin 	case ETHERTYPE_IP:
17940a7a780eSAndrew Gallatin 		/*
17950a7a780eSAndrew Gallatin 		 * ensure ip header is in first mbuf, copy it to a
17960a7a780eSAndrew Gallatin 		 * scratch buffer if not
17970a7a780eSAndrew Gallatin 		 */
17980a7a780eSAndrew Gallatin 		pi->ip = (struct ip *)(m->m_data + pi->ip_off);
17990a7a780eSAndrew Gallatin 		pi->ip6 = NULL;
18000a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip))) {
18010a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip),
18020a7a780eSAndrew Gallatin 			    ss->scratch);
18030a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
18040a7a780eSAndrew Gallatin 		}
18050a7a780eSAndrew Gallatin 		pi->ip_hlen = pi->ip->ip_hl << 2;
18060a7a780eSAndrew Gallatin 		if (!tso)
18070a7a780eSAndrew Gallatin 			return 0;
18080a7a780eSAndrew Gallatin 
18090a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
18100a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
18110a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
18120a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
18130a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
18140a7a780eSAndrew Gallatin 		}
18150a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip + pi->ip_hlen);
18160a7a780eSAndrew Gallatin 		break;
18170a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
18180a7a780eSAndrew Gallatin 	case ETHERTYPE_IPV6:
18190a7a780eSAndrew Gallatin 		pi->ip6 = (struct ip6_hdr *)(m->m_data + pi->ip_off);
18200a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip6))) {
18210a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip6),
18220a7a780eSAndrew Gallatin 			    ss->scratch);
18230a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
18240a7a780eSAndrew Gallatin 		}
18250a7a780eSAndrew Gallatin 		nxt = 0;
18260a7a780eSAndrew Gallatin 		pi->ip_hlen = ip6_lasthdr(m, pi->ip_off, IPPROTO_IPV6, &nxt);
18270a7a780eSAndrew Gallatin 		pi->ip_hlen -= pi->ip_off;
18280a7a780eSAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
18290a7a780eSAndrew Gallatin 			return EINVAL;
18300a7a780eSAndrew Gallatin 
18310a7a780eSAndrew Gallatin 		if (!tso)
18320a7a780eSAndrew Gallatin 			return 0;
18330a7a780eSAndrew Gallatin 
18340a7a780eSAndrew Gallatin 		if (pi->ip_off + pi->ip_hlen > ss->sc->max_tso6_hlen)
18350a7a780eSAndrew Gallatin 			return EINVAL;
18360a7a780eSAndrew Gallatin 
18370a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
18380a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
18390a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
18400a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
18410a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
18420a7a780eSAndrew Gallatin 		}
18430a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip6 + pi->ip_hlen);
18440a7a780eSAndrew Gallatin 		break;
18450a7a780eSAndrew Gallatin #endif
18460a7a780eSAndrew Gallatin 	default:
18470a7a780eSAndrew Gallatin 		return EINVAL;
18480a7a780eSAndrew Gallatin 	}
18490a7a780eSAndrew Gallatin 	return 0;
18500a7a780eSAndrew Gallatin }
18510a7a780eSAndrew Gallatin 
185237d89b0cSAndrew Gallatin #if IFCAP_TSO4
185337d89b0cSAndrew Gallatin 
1854b2fc195eSAndrew Gallatin static void
18551e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
18560a7a780eSAndrew Gallatin 	       int busdma_seg_cnt, struct mxge_pkt_info *pi)
1857aed8e389SAndrew Gallatin {
18581e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1859aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1860aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1861aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1862aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1863aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
18640a7a780eSAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss, sum;
1865aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1866aed8e389SAndrew Gallatin 	static int once;
1867aed8e389SAndrew Gallatin 
1868aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1869aed8e389SAndrew Gallatin 
1870aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1871aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1872aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1873aed8e389SAndrew Gallatin 	 */
1874aed8e389SAndrew Gallatin 
18750a7a780eSAndrew Gallatin 	cksum_offset = pi->ip_off + pi->ip_hlen;
18760a7a780eSAndrew Gallatin 	cum_len = -(cksum_offset + (pi->tcp->th_off << 2));
1877aed8e389SAndrew Gallatin 
1878aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
18790a7a780eSAndrew Gallatin 	if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) == 0)) {
18804ed8ca8fSAndrew Gallatin 		/*
18814ed8ca8fSAndrew Gallatin 		 * If packet has full TCP csum, replace it with pseudo hdr
18824ed8ca8fSAndrew Gallatin 		 * sum that the NIC expects, otherwise the NIC will emit
18834ed8ca8fSAndrew Gallatin 		 * packets with bad TCP checksums.
18844ed8ca8fSAndrew Gallatin 		 */
18854ed8ca8fSAndrew Gallatin 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
18860a7a780eSAndrew Gallatin 		if (pi->ip6) {
18870a7a780eSAndrew Gallatin #if (CSUM_TCP_IPV6 != 0) && defined(INET6)
18880a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP_IPV6;
18890a7a780eSAndrew Gallatin 			sum = in6_cksum_pseudo(pi->ip6,
18900a7a780eSAndrew Gallatin 			    m->m_pkthdr.len - cksum_offset,
18910a7a780eSAndrew Gallatin 			    IPPROTO_TCP, 0);
18920a7a780eSAndrew Gallatin #endif
18930a7a780eSAndrew Gallatin 		} else {
1894abc5b96bSAndrew Gallatin #ifdef INET
18950a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP;
18960a7a780eSAndrew Gallatin 			sum = in_pseudo(pi->ip->ip_src.s_addr,
18970a7a780eSAndrew Gallatin 			    pi->ip->ip_dst.s_addr,
18980a7a780eSAndrew Gallatin 			    htons(IPPROTO_TCP + (m->m_pkthdr.len -
18990a7a780eSAndrew Gallatin 				    cksum_offset)));
1900abc5b96bSAndrew Gallatin #endif
19010a7a780eSAndrew Gallatin 		}
19020a7a780eSAndrew Gallatin 		m_copyback(m, offsetof(struct tcphdr, th_sum) +
19030a7a780eSAndrew Gallatin 		    cksum_offset, sizeof(sum), (caddr_t)&sum);
19044ed8ca8fSAndrew Gallatin 	}
1905aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1906aed8e389SAndrew Gallatin 
1907aed8e389SAndrew Gallatin 
1908aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1909aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1910aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1911aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1912aed8e389SAndrew Gallatin 
19130a7a780eSAndrew Gallatin 	if (pi->ip6) {
19140a7a780eSAndrew Gallatin 		/*
19150a7a780eSAndrew Gallatin 		 * for IPv6 TSO, the "checksum offset" is re-purposed
19160a7a780eSAndrew Gallatin 		 * to store the TCP header len
19170a7a780eSAndrew Gallatin 		 */
19180a7a780eSAndrew Gallatin 		cksum_offset = (pi->tcp->th_off << 2);
19190a7a780eSAndrew Gallatin 	}
19200a7a780eSAndrew Gallatin 
19211e413cf9SAndrew Gallatin 	tx = &ss->tx;
1922aed8e389SAndrew Gallatin 	req = tx->req_list;
1923aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1924aed8e389SAndrew Gallatin 	cnt = 0;
1925aed8e389SAndrew Gallatin 	rdma_count = 0;
1926aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1927aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1928aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1929aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1930aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1931aed8e389SAndrew Gallatin 	 *
1932aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1933aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1934aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1935aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1936aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1937aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1938aed8e389SAndrew Gallatin 	 *
1939aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1940aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1941aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1942aed8e389SAndrew Gallatin 	 */
1943aed8e389SAndrew Gallatin 
1944aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1945aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1946aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1947aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1948e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1949aed8e389SAndrew Gallatin 
1950aed8e389SAndrew Gallatin 		while (len) {
1951aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1952e39a0a37SAndrew Gallatin 			seglen = len;
1953aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1954aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1955aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1956aed8e389SAndrew Gallatin 				/* payload */
1957aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1958aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1959aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1960aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1961aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1962aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1963aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1964aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1965aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1966aed8e389SAndrew Gallatin 				/* header ends */
1967aed8e389SAndrew Gallatin 				rdma_count = -1;
1968aed8e389SAndrew Gallatin 				cum_len_next = 0;
1969aed8e389SAndrew Gallatin 				seglen = -cum_len;
1970aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1971aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1972aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1973aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1974aed8e389SAndrew Gallatin 			    }
1975aed8e389SAndrew Gallatin 
1976aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1977aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1978aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1979aed8e389SAndrew Gallatin 			req->pad = 0;
1980aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1981aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1982aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1983aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1984aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1985aed8e389SAndrew Gallatin 			low += seglen;
1986aed8e389SAndrew Gallatin 			len -= seglen;
1987aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1988aed8e389SAndrew Gallatin 			flags = flags_next;
1989aed8e389SAndrew Gallatin 			req++;
1990aed8e389SAndrew Gallatin 			cnt++;
1991aed8e389SAndrew Gallatin 			rdma_count++;
19920a7a780eSAndrew Gallatin 			if (cksum_offset != 0 && !pi->ip6) {
1993aed8e389SAndrew Gallatin 				if (__predict_false(cksum_offset > seglen))
1994aed8e389SAndrew Gallatin 					cksum_offset -= seglen;
1995aed8e389SAndrew Gallatin 				else
1996aed8e389SAndrew Gallatin 					cksum_offset = 0;
19970a7a780eSAndrew Gallatin 			}
1998adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1999aed8e389SAndrew Gallatin 				goto drop;
2000aed8e389SAndrew Gallatin 		}
2001aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
2002aed8e389SAndrew Gallatin 		seg++;
2003aed8e389SAndrew Gallatin 	}
2004aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
2005aed8e389SAndrew Gallatin 
2006aed8e389SAndrew Gallatin 	do {
2007aed8e389SAndrew Gallatin 		req--;
2008aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
2009aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
2010aed8e389SAndrew Gallatin 
2011aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
2012aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2013c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2014c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2015c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2016c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2017c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2018c6cb3e3fSAndrew Gallatin 		tx->activate++;
2019c6cb3e3fSAndrew Gallatin 		wmb();
2020c6cb3e3fSAndrew Gallatin 	}
2021c6cb3e3fSAndrew Gallatin #endif
2022aed8e389SAndrew Gallatin 	return;
2023aed8e389SAndrew Gallatin 
2024aed8e389SAndrew Gallatin drop:
2025e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
2026aed8e389SAndrew Gallatin 	m_freem(m);
2027c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2028aed8e389SAndrew Gallatin 	if (!once) {
2029adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
2030adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
2031adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
2032aed8e389SAndrew Gallatin 		once = 1;
2033aed8e389SAndrew Gallatin 	}
2034aed8e389SAndrew Gallatin 	return;
2035aed8e389SAndrew Gallatin 
2036aed8e389SAndrew Gallatin }
2037aed8e389SAndrew Gallatin 
203837d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
203937d89b0cSAndrew Gallatin 
204037d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2041c792928fSAndrew Gallatin /*
2042c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
2043c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
2044c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
2045c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
2046c792928fSAndrew Gallatin  */
2047c792928fSAndrew Gallatin static struct mbuf *
2048c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
2049c792928fSAndrew Gallatin {
2050c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2051c792928fSAndrew Gallatin 
2052c6499eccSGleb Smirnoff 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
2053c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
2054c792928fSAndrew Gallatin 		return NULL;
2055c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
2056c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
2057c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2058c792928fSAndrew Gallatin 			return NULL;
2059c792928fSAndrew Gallatin 	}
2060c792928fSAndrew Gallatin 	/*
2061c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2062c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2063c792928fSAndrew Gallatin 	 */
2064c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2065c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2066c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2067c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2068c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2069c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2070c792928fSAndrew Gallatin 	return m;
2071c792928fSAndrew Gallatin }
207237d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2073c792928fSAndrew Gallatin 
2074aed8e389SAndrew Gallatin static void
20751e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2076b2fc195eSAndrew Gallatin {
20770a7a780eSAndrew Gallatin 	struct mxge_pkt_info pi = {0,0,0,0};
20781e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2079b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2080b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2081b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
2082b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20831e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
20840a7a780eSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
2085aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2086aed8e389SAndrew Gallatin 	uint8_t flags, cksum_offset;
2087b2fc195eSAndrew Gallatin 
2088b2fc195eSAndrew Gallatin 
20891e413cf9SAndrew Gallatin 	sc = ss->sc;
2090b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20911e413cf9SAndrew Gallatin 	tx = &ss->tx;
2092b2fc195eSAndrew Gallatin 
209337d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2094c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2095c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2096c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
20970a7a780eSAndrew Gallatin 			goto drop_without_m;
2098c792928fSAndrew Gallatin 	}
209937d89b0cSAndrew Gallatin #endif
21000a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
21010a7a780eSAndrew Gallatin 	    (CSUM_TSO | CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
21020a7a780eSAndrew Gallatin 		if (mxge_parse_tx(ss, m, &pi))
21030a7a780eSAndrew Gallatin 			goto drop;
21040a7a780eSAndrew Gallatin 	}
21050a7a780eSAndrew Gallatin 
2106b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2107b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2108b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2109aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2110b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2111adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2112b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2113b2fc195eSAndrew Gallatin 		   to defrag */
2114b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2115b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2116b2fc195eSAndrew Gallatin 			goto drop;
2117b2fc195eSAndrew Gallatin 		}
21181e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2119b2fc195eSAndrew Gallatin 		m = m_tmp;
2120b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2121b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2122aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2123b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2124b2fc195eSAndrew Gallatin 	}
2125adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2126aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2127aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2128b2fc195eSAndrew Gallatin 		goto drop;
2129b2fc195eSAndrew Gallatin 	}
2130b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2131b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
21325e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2133b2fc195eSAndrew Gallatin 
213437d89b0cSAndrew Gallatin #if IFCAP_TSO4
2135aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2136aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
21370a7a780eSAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, &pi);
2138aed8e389SAndrew Gallatin 		return;
2139aed8e389SAndrew Gallatin 	}
214037d89b0cSAndrew Gallatin #endif
2141aed8e389SAndrew Gallatin 
2142b2fc195eSAndrew Gallatin 	req = tx->req_list;
2143b2fc195eSAndrew Gallatin 	cksum_offset = 0;
21445e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
21455e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2146b2fc195eSAndrew Gallatin 
2147b2fc195eSAndrew Gallatin 	/* checksum offloading? */
21480a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
21490a7a780eSAndrew Gallatin 	    (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
2150aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2151aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
21520a7a780eSAndrew Gallatin 		cksum_offset = pi.ip_off + pi.ip_hlen;
2153b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
21545e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2155b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
21565e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2157aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2158aed8e389SAndrew Gallatin 	} else {
2159aed8e389SAndrew Gallatin 		odd_flag = 0;
2160b2fc195eSAndrew Gallatin 	}
21615e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21625e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2163b2fc195eSAndrew Gallatin 
2164b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2165b2fc195eSAndrew Gallatin 	cum_len = 0;
2166aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21675e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2168b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2169b2fc195eSAndrew Gallatin 		req->addr_low =
21706d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2171b2fc195eSAndrew Gallatin 		req->addr_high =
21726d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2173b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2174b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2175b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2176b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2177b2fc195eSAndrew Gallatin 		else
2178b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21795e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21805e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21815e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2182aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2183b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2184b2fc195eSAndrew Gallatin 		seg++;
2185b2fc195eSAndrew Gallatin 		req++;
2186b2fc195eSAndrew Gallatin 		req->flags = 0;
2187b2fc195eSAndrew Gallatin 	}
2188b2fc195eSAndrew Gallatin 	req--;
2189b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2190b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2191b2fc195eSAndrew Gallatin 		req++;
2192b2fc195eSAndrew Gallatin 		req->addr_low =
21936d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2194b2fc195eSAndrew Gallatin 		req->addr_high =
21956d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2196b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21975e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21985e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21995e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
22005e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2201aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2202b2fc195eSAndrew Gallatin 		cnt++;
2203b2fc195eSAndrew Gallatin 	}
22045e7d8541SAndrew Gallatin 
22055e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
22065e7d8541SAndrew Gallatin #if 0
22075e7d8541SAndrew Gallatin 	/* print what the firmware will see */
22085e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
22095e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
22105e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
22115e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
22125e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
22135e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
22145e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
22155e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
22165e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
22175e7d8541SAndrew Gallatin 	}
22185e7d8541SAndrew Gallatin 	printf("--------------\n");
22195e7d8541SAndrew Gallatin #endif
22205e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
22216d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2222c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2223c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2224c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2225c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2226c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2227c6cb3e3fSAndrew Gallatin 		tx->activate++;
2228c6cb3e3fSAndrew Gallatin 		wmb();
2229c6cb3e3fSAndrew Gallatin 	}
2230c6cb3e3fSAndrew Gallatin #endif
2231b2fc195eSAndrew Gallatin 	return;
2232b2fc195eSAndrew Gallatin 
2233b2fc195eSAndrew Gallatin drop:
2234b2fc195eSAndrew Gallatin 	m_freem(m);
22350a7a780eSAndrew Gallatin drop_without_m:
2236c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2237b2fc195eSAndrew Gallatin 	return;
2238b2fc195eSAndrew Gallatin }
2239b2fc195eSAndrew Gallatin 
2240c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2241c6cb3e3fSAndrew Gallatin static void
2242c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2243c6cb3e3fSAndrew Gallatin {
2244c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2245c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2246c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2247c6cb3e3fSAndrew Gallatin 	int slice;
2248b2fc195eSAndrew Gallatin 
2249c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2250c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2251c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2252c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2253c6cb3e3fSAndrew Gallatin 			m_freem(m);
2254c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2255c6cb3e3fSAndrew Gallatin 	}
2256c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2257c6cb3e3fSAndrew Gallatin }
22586d914a32SAndrew Gallatin 
2259c6cb3e3fSAndrew Gallatin static inline void
2260c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2261c6cb3e3fSAndrew Gallatin {
2262c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2263c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2264c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2265c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2266c6cb3e3fSAndrew Gallatin 
2267c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2268c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2269c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2270c6cb3e3fSAndrew Gallatin 
2271c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2272c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2273c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2274c6cb3e3fSAndrew Gallatin 			return;
2275c6cb3e3fSAndrew Gallatin 		}
2276c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2277c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2278c6cb3e3fSAndrew Gallatin 
2279c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2280c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2281c6cb3e3fSAndrew Gallatin 	}
2282c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2283c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2284c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2285c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2286c6cb3e3fSAndrew Gallatin 		tx->stall++;
2287c6cb3e3fSAndrew Gallatin 	}
2288c6cb3e3fSAndrew Gallatin }
2289c6cb3e3fSAndrew Gallatin 
2290c6cb3e3fSAndrew Gallatin static int
2291c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2292c6cb3e3fSAndrew Gallatin {
2293c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2294c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2295c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2296c6cb3e3fSAndrew Gallatin 	int err;
2297c6cb3e3fSAndrew Gallatin 
2298c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2299c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2300c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2301c6cb3e3fSAndrew Gallatin 
2302c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2303c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2304c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2305c6cb3e3fSAndrew Gallatin 		return (err);
2306c6cb3e3fSAndrew Gallatin 	}
2307c6cb3e3fSAndrew Gallatin 
2308193cbc4dSMax Laier 	if (!drbr_needs_enqueue(ifp, tx->br) &&
2309c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2310c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2311c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2312c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2313c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2314c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2315c6cb3e3fSAndrew Gallatin 		return (err);
2316c6cb3e3fSAndrew Gallatin 	}
2317c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2318c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2319c6cb3e3fSAndrew Gallatin 	return (0);
2320c6cb3e3fSAndrew Gallatin }
2321c6cb3e3fSAndrew Gallatin 
2322c6cb3e3fSAndrew Gallatin static int
2323c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2324c6cb3e3fSAndrew Gallatin {
2325c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2326c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2327c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2328c6cb3e3fSAndrew Gallatin 	int err = 0;
2329c6cb3e3fSAndrew Gallatin 	int slice;
2330c6cb3e3fSAndrew Gallatin 
2331c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2332c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2333c6cb3e3fSAndrew Gallatin 
2334c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2335c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2336c6cb3e3fSAndrew Gallatin 
2337c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2338c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2339c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2340c6cb3e3fSAndrew Gallatin 	} else {
2341c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2342c6cb3e3fSAndrew Gallatin 	}
2343c6cb3e3fSAndrew Gallatin 
2344c6cb3e3fSAndrew Gallatin 	return (err);
2345c6cb3e3fSAndrew Gallatin }
2346c6cb3e3fSAndrew Gallatin 
2347c6cb3e3fSAndrew Gallatin #else
23486d914a32SAndrew Gallatin 
23496d914a32SAndrew Gallatin static inline void
23501e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2351b2fc195eSAndrew Gallatin {
23521e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2353b2fc195eSAndrew Gallatin 	struct mbuf *m;
2354b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
23551e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2356b2fc195eSAndrew Gallatin 
23571e413cf9SAndrew Gallatin 	sc = ss->sc;
2358b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23591e413cf9SAndrew Gallatin 	tx = &ss->tx;
2360adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
23616d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
23626d914a32SAndrew Gallatin 		if (m == NULL) {
23636d914a32SAndrew Gallatin 			return;
23646d914a32SAndrew Gallatin 		}
2365b2fc195eSAndrew Gallatin 		/* let BPF see it */
2366b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2367b2fc195eSAndrew Gallatin 
2368b2fc195eSAndrew Gallatin 		/* give it to the nic */
23691e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
23706d914a32SAndrew Gallatin 	}
23716d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2372a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2373b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2374adae7080SAndrew Gallatin 		tx->stall++;
2375a82c2581SAndrew Gallatin 	}
2376b2fc195eSAndrew Gallatin }
2377c6cb3e3fSAndrew Gallatin #endif
2378b2fc195eSAndrew Gallatin static void
23796d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2380b2fc195eSAndrew Gallatin {
23816d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
23821e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2383b2fc195eSAndrew Gallatin 
23841e413cf9SAndrew Gallatin 	/* only use the first slice for now */
23851e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
23861e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
23871e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
23881e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2389b2fc195eSAndrew Gallatin }
2390b2fc195eSAndrew Gallatin 
23915e7d8541SAndrew Gallatin /*
23925e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23935e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23945e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23955e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23965e7d8541SAndrew Gallatin  * in a burst
23975e7d8541SAndrew Gallatin  */
23985e7d8541SAndrew Gallatin static inline void
23995e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
24005e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
24015e7d8541SAndrew Gallatin {
24025e7d8541SAndrew Gallatin 	uint32_t low;
24035e7d8541SAndrew Gallatin 
24045e7d8541SAndrew Gallatin 	low = src->addr_low;
24055e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2406a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
240773c7c83fSAndrew Gallatin 	wmb();
2408a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
240973c7c83fSAndrew Gallatin 	wmb();
241040385a5fSAndrew Gallatin 	src->addr_low = low;
24115e7d8541SAndrew Gallatin 	dst->addr_low = low;
241273c7c83fSAndrew Gallatin 	wmb();
24135e7d8541SAndrew Gallatin }
24145e7d8541SAndrew Gallatin 
2415b2fc195eSAndrew Gallatin static int
24161e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2417b2fc195eSAndrew Gallatin {
2418b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2419b2fc195eSAndrew Gallatin 	struct mbuf *m;
24201e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2421b2fc195eSAndrew Gallatin 	int cnt, err;
2422b2fc195eSAndrew Gallatin 
2423c6499eccSGleb Smirnoff 	m = m_gethdr(M_NOWAIT, MT_DATA);
2424b2fc195eSAndrew Gallatin 	if (m == NULL) {
2425b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2426b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2427b2fc195eSAndrew Gallatin 		goto done;
2428b2fc195eSAndrew Gallatin 	}
2429b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2430b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2431b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2432b2fc195eSAndrew Gallatin 	if (err != 0) {
2433b2fc195eSAndrew Gallatin 		m_free(m);
2434b2fc195eSAndrew Gallatin 		goto done;
2435b2fc195eSAndrew Gallatin 	}
2436b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2437b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
24386d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2439b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
24406d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2441b2fc195eSAndrew Gallatin 
2442b2fc195eSAndrew Gallatin done:
2443adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2444adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2445b2fc195eSAndrew Gallatin 	return err;
2446b2fc195eSAndrew Gallatin }
2447b2fc195eSAndrew Gallatin 
2448b2fc195eSAndrew Gallatin static int
24491e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2450b2fc195eSAndrew Gallatin {
2451053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2452b2fc195eSAndrew Gallatin 	struct mbuf *m;
24531e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2454053e637fSAndrew Gallatin 	int cnt, err, i;
2455b2fc195eSAndrew Gallatin 
2456c6499eccSGleb Smirnoff 	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2457b2fc195eSAndrew Gallatin 	if (m == NULL) {
2458b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2459b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2460b2fc195eSAndrew Gallatin 		goto done;
2461b2fc195eSAndrew Gallatin 	}
24624d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2463b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2464053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2465b2fc195eSAndrew Gallatin 	if (err != 0) {
2466b2fc195eSAndrew Gallatin 		m_free(m);
2467b2fc195eSAndrew Gallatin 		goto done;
2468b2fc195eSAndrew Gallatin 	}
2469b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2470b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2471b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2472b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2473b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2474053e637fSAndrew Gallatin 
2475b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2476b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2477053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2478053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2479053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2480053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2481053e637fSAndrew Gallatin        }
2482b0f7b922SAndrew Gallatin #endif
2483b2fc195eSAndrew Gallatin 
2484b2fc195eSAndrew Gallatin done:
2485053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2486b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
24875e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
24885e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2489b2fc195eSAndrew Gallatin 		}
2490053e637fSAndrew Gallatin 		idx++;
2491053e637fSAndrew Gallatin 	}
2492b2fc195eSAndrew Gallatin 	return err;
2493b2fc195eSAndrew Gallatin }
2494b2fc195eSAndrew Gallatin 
249526dd49c6SAndrew Gallatin #ifdef INET6
249626dd49c6SAndrew Gallatin 
249726dd49c6SAndrew Gallatin static uint16_t
249826dd49c6SAndrew Gallatin mxge_csum_generic(uint16_t *raw, int len)
249926dd49c6SAndrew Gallatin {
250026dd49c6SAndrew Gallatin 	uint32_t csum;
250126dd49c6SAndrew Gallatin 
250226dd49c6SAndrew Gallatin 
250326dd49c6SAndrew Gallatin 	csum = 0;
250426dd49c6SAndrew Gallatin 	while (len > 0) {
250526dd49c6SAndrew Gallatin 		csum += *raw;
250626dd49c6SAndrew Gallatin 		raw++;
250726dd49c6SAndrew Gallatin 		len -= 2;
250826dd49c6SAndrew Gallatin 	}
250926dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
251026dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
251126dd49c6SAndrew Gallatin 	return (uint16_t)csum;
251226dd49c6SAndrew Gallatin }
251326dd49c6SAndrew Gallatin 
251426dd49c6SAndrew Gallatin static inline uint16_t
251526dd49c6SAndrew Gallatin mxge_rx_csum6(void *p, struct mbuf *m, uint32_t csum)
251626dd49c6SAndrew Gallatin {
251726dd49c6SAndrew Gallatin 	uint32_t partial;
251826dd49c6SAndrew Gallatin 	int nxt, cksum_offset;
251926dd49c6SAndrew Gallatin 	struct ip6_hdr *ip6 = p;
252026dd49c6SAndrew Gallatin 	uint16_t c;
252126dd49c6SAndrew Gallatin 
252226dd49c6SAndrew Gallatin 	nxt = ip6->ip6_nxt;
252326dd49c6SAndrew Gallatin 	cksum_offset = sizeof (*ip6) + ETHER_HDR_LEN;
252426dd49c6SAndrew Gallatin 	if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) {
252526dd49c6SAndrew Gallatin 		cksum_offset = ip6_lasthdr(m, ETHER_HDR_LEN,
252626dd49c6SAndrew Gallatin 					   IPPROTO_IPV6, &nxt);
252726dd49c6SAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
252826dd49c6SAndrew Gallatin 			return (1);
252926dd49c6SAndrew Gallatin 	}
253026dd49c6SAndrew Gallatin 
253126dd49c6SAndrew Gallatin 	/*
253226dd49c6SAndrew Gallatin 	 * IPv6 headers do not contain a checksum, and hence
253326dd49c6SAndrew Gallatin 	 * do not checksum to zero, so they don't "fall out"
253426dd49c6SAndrew Gallatin 	 * of the partial checksum calculation like IPv4
253526dd49c6SAndrew Gallatin 	 * headers do.  We need to fix the partial checksum by
253626dd49c6SAndrew Gallatin 	 * subtracting the checksum of the IPv6 header.
253726dd49c6SAndrew Gallatin 	 */
253826dd49c6SAndrew Gallatin 
253926dd49c6SAndrew Gallatin 	partial = mxge_csum_generic((uint16_t *)ip6, cksum_offset -
254026dd49c6SAndrew Gallatin 				    ETHER_HDR_LEN);
254126dd49c6SAndrew Gallatin 	csum += ~partial;
254226dd49c6SAndrew Gallatin 	csum +=	 (csum < ~partial);
254326dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
254426dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
254526dd49c6SAndrew Gallatin 	c = in6_cksum_pseudo(ip6, m->m_pkthdr.len - cksum_offset, nxt,
254626dd49c6SAndrew Gallatin 			     csum);
254726dd49c6SAndrew Gallatin 	c ^= 0xffff;
254826dd49c6SAndrew Gallatin 	return (c);
254926dd49c6SAndrew Gallatin }
255026dd49c6SAndrew Gallatin #endif /* INET6 */
25519b03b0f3SAndrew Gallatin /*
25529b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
25539b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
25549b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
25559b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2556053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2557053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
25589b03b0f3SAndrew Gallatin  */
25599b03b0f3SAndrew Gallatin 
2560053e637fSAndrew Gallatin static inline uint16_t
2561053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2562053e637fSAndrew Gallatin {
2563053e637fSAndrew Gallatin 	struct ether_header *eh;
256426dd49c6SAndrew Gallatin #ifdef INET
2565053e637fSAndrew Gallatin 	struct ip *ip;
256626dd49c6SAndrew Gallatin #endif
2567abc5b96bSAndrew Gallatin #if defined(INET) || defined(INET6)
2568abc5b96bSAndrew Gallatin 	int cap = m->m_pkthdr.rcvif->if_capenable;
2569abc5b96bSAndrew Gallatin #endif
257026dd49c6SAndrew Gallatin 	uint16_t c, etype;
257126dd49c6SAndrew Gallatin 
2572053e637fSAndrew Gallatin 
2573053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
257426dd49c6SAndrew Gallatin 	etype = ntohs(eh->ether_type);
257526dd49c6SAndrew Gallatin 	switch (etype) {
2576eb6219e3SAndrew Gallatin #ifdef INET
257726dd49c6SAndrew Gallatin 	case ETHERTYPE_IP:
257826dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM) == 0)
257926dd49c6SAndrew Gallatin 			return (1);
258026dd49c6SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
258126dd49c6SAndrew Gallatin 		if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP)
258226dd49c6SAndrew Gallatin 			return (1);
2583053e637fSAndrew Gallatin 		c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
258426dd49c6SAndrew Gallatin 			      htonl(ntohs(csum) + ntohs(ip->ip_len) -
258526dd49c6SAndrew Gallatin 				    (ip->ip_hl << 2) + ip->ip_p));
2586053e637fSAndrew Gallatin 		c ^= 0xffff;
258726dd49c6SAndrew Gallatin 		break;
258826dd49c6SAndrew Gallatin #endif
258926dd49c6SAndrew Gallatin #ifdef INET6
259026dd49c6SAndrew Gallatin 	case ETHERTYPE_IPV6:
259126dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM_IPV6) == 0)
259226dd49c6SAndrew Gallatin 			return (1);
259326dd49c6SAndrew Gallatin 		c = mxge_rx_csum6((eh + 1), m, csum);
259426dd49c6SAndrew Gallatin 		break;
259526dd49c6SAndrew Gallatin #endif
259626dd49c6SAndrew Gallatin 	default:
259726dd49c6SAndrew Gallatin 		c = 1;
259826dd49c6SAndrew Gallatin 	}
2599053e637fSAndrew Gallatin 	return (c);
26005e7d8541SAndrew Gallatin }
2601053e637fSAndrew Gallatin 
2602c792928fSAndrew Gallatin static void
2603c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2604c792928fSAndrew Gallatin {
2605c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2606c792928fSAndrew Gallatin 	struct ether_header *eh;
2607c792928fSAndrew Gallatin 	uint32_t partial;
2608c792928fSAndrew Gallatin 
2609c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2610c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2611c792928fSAndrew Gallatin 
2612c792928fSAndrew Gallatin 	/*
2613c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2614c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2615c792928fSAndrew Gallatin 	 * header.
2616c792928fSAndrew Gallatin 	 */
2617c792928fSAndrew Gallatin 
2618c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2619c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2620c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2621c792928fSAndrew Gallatin 	(*csum) += ~partial;
2622c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2623c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2624c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2625c792928fSAndrew Gallatin 
2626c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2627c792928fSAndrew Gallatin 	   later consumers expect this */
2628c792928fSAndrew Gallatin 	*csum = htons(*csum);
2629c792928fSAndrew Gallatin 
2630c792928fSAndrew Gallatin 	/* save the tag */
263137d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2632c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
263337d89b0cSAndrew Gallatin #else
263437d89b0cSAndrew Gallatin 	{
263537d89b0cSAndrew Gallatin 		struct m_tag *mtag;
263637d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
263737d89b0cSAndrew Gallatin 				   M_NOWAIT);
263837d89b0cSAndrew Gallatin 		if (mtag == NULL)
263937d89b0cSAndrew Gallatin 			return;
264037d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
264137d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
264237d89b0cSAndrew Gallatin 	}
264337d89b0cSAndrew Gallatin 
264437d89b0cSAndrew Gallatin #endif
264537d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2646c792928fSAndrew Gallatin 
2647c792928fSAndrew Gallatin 	/*
2648c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2649c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2650c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2651c792928fSAndrew Gallatin 	 * type field is already in place.
2652c792928fSAndrew Gallatin 	 */
2653c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2654c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2655c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2656c792928fSAndrew Gallatin }
2657c792928fSAndrew Gallatin 
26585e7d8541SAndrew Gallatin 
26595e7d8541SAndrew Gallatin static inline void
266026dd49c6SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len,
266126dd49c6SAndrew Gallatin 		 uint32_t csum, int lro)
2662b2fc195eSAndrew Gallatin {
26631e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2664b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2665053e637fSAndrew Gallatin 	struct mbuf *m;
2666c792928fSAndrew Gallatin 	struct ether_header *eh;
26671e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2668053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2669b2fc195eSAndrew Gallatin 	int idx;
2670b2fc195eSAndrew Gallatin 
26711e413cf9SAndrew Gallatin 	sc = ss->sc;
2672b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26731e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2674b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2675053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2676b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2677b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2678b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26791e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2680053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2681f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2682053e637fSAndrew Gallatin 		return;
2683b2fc195eSAndrew Gallatin 	}
2684053e637fSAndrew Gallatin 
2685b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2686b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2687b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2688b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2689b2fc195eSAndrew Gallatin 
2690b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2691b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2692b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2693b2fc195eSAndrew Gallatin 
2694053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2695053e637fSAndrew Gallatin 	 * aligned */
26965e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2697b2fc195eSAndrew Gallatin 
2698053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2699053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
27001e413cf9SAndrew Gallatin 	ss->ipackets++;
2701c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2702c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2703c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2704c792928fSAndrew Gallatin 	}
2705b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
270626dd49c6SAndrew Gallatin 
270726dd49c6SAndrew Gallatin 	if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
270826dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
270926dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2710053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
271126dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
271226dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
271326dd49c6SAndrew Gallatin 
271426dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
271526dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, 0)))
271626dd49c6SAndrew Gallatin 			return;
271726dd49c6SAndrew Gallatin #endif
2718b2fc195eSAndrew Gallatin 	}
2719c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2720c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2721c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2722c2529042SHans Petter Selasky 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2723c6cb3e3fSAndrew Gallatin 	}
2724053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2725053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2726b2fc195eSAndrew Gallatin }
2727b2fc195eSAndrew Gallatin 
2728b2fc195eSAndrew Gallatin static inline void
272926dd49c6SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len,
273026dd49c6SAndrew Gallatin 		   uint32_t csum, int lro)
2731b2fc195eSAndrew Gallatin {
27321e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2733b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2734c792928fSAndrew Gallatin 	struct ether_header *eh;
2735b2fc195eSAndrew Gallatin 	struct mbuf *m;
27361e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2737b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2738b2fc195eSAndrew Gallatin 	int idx;
2739b2fc195eSAndrew Gallatin 
27401e413cf9SAndrew Gallatin 	sc = ss->sc;
2741b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
27421e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2743b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2744b2fc195eSAndrew Gallatin 	rx->cnt++;
2745b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2746b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2747b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
27481e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2749b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2750f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2751b2fc195eSAndrew Gallatin 		return;
2752b2fc195eSAndrew Gallatin 	}
2753b2fc195eSAndrew Gallatin 
2754b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2755b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2756b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2757b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2758b2fc195eSAndrew Gallatin 
2759b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2760b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2761b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2762b2fc195eSAndrew Gallatin 
2763b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2764b2fc195eSAndrew Gallatin 	 * aligned */
27655e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2766b2fc195eSAndrew Gallatin 
27679b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
27689b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
27691e413cf9SAndrew Gallatin 	ss->ipackets++;
2770c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2771c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2772c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2773c792928fSAndrew Gallatin 	}
2774b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
277526dd49c6SAndrew Gallatin 	if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
277626dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
277726dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2778053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
277926dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
278026dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
278126dd49c6SAndrew Gallatin 
278226dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
278326dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, csum)))
278426dd49c6SAndrew Gallatin 			return;
278526dd49c6SAndrew Gallatin #endif
2786053e637fSAndrew Gallatin 	}
2787c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2788c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2789c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2790c2529042SHans Petter Selasky 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2791c6cb3e3fSAndrew Gallatin 	}
2792b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2793b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2794b2fc195eSAndrew Gallatin }
2795b2fc195eSAndrew Gallatin 
2796b2fc195eSAndrew Gallatin static inline void
27971e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
27985e7d8541SAndrew Gallatin {
27991e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
28005e7d8541SAndrew Gallatin 	int limit = 0;
28015e7d8541SAndrew Gallatin 	uint16_t length;
28025e7d8541SAndrew Gallatin 	uint16_t checksum;
280326dd49c6SAndrew Gallatin 	int lro;
28045e7d8541SAndrew Gallatin 
280526dd49c6SAndrew Gallatin 	lro = ss->sc->ifp->if_capenable & IFCAP_LRO;
28065e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
28075e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
28085e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2809053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2810b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
281126dd49c6SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum, lro);
28125e7d8541SAndrew Gallatin 		else
281326dd49c6SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum, lro);
28145e7d8541SAndrew Gallatin 		rx_done->cnt++;
2815adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
28165e7d8541SAndrew Gallatin 
28175e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2818f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
28195e7d8541SAndrew Gallatin 			break;
2820053e637fSAndrew Gallatin 	}
282126dd49c6SAndrew Gallatin #if defined(INET)  || defined (INET6)
282226dd49c6SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lc.lro_active)) {
282326dd49c6SAndrew Gallatin 		struct lro_entry *lro = SLIST_FIRST(&ss->lc.lro_active);
282426dd49c6SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lc.lro_active, next);
282526dd49c6SAndrew Gallatin 		tcp_lro_flush(&ss->lc, lro);
28265e7d8541SAndrew Gallatin 	}
2827eb6219e3SAndrew Gallatin #endif
28285e7d8541SAndrew Gallatin }
28295e7d8541SAndrew Gallatin 
28305e7d8541SAndrew Gallatin 
28315e7d8541SAndrew Gallatin static inline void
28321e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2833b2fc195eSAndrew Gallatin {
2834b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
28351e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2836b2fc195eSAndrew Gallatin 	struct mbuf *m;
2837b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2838f616ebc7SAndrew Gallatin 	int idx;
2839c6cb3e3fSAndrew Gallatin 	int *flags;
2840b2fc195eSAndrew Gallatin 
28411e413cf9SAndrew Gallatin 	tx = &ss->tx;
28421e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
28435e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2844b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2845b2fc195eSAndrew Gallatin 		tx->done++;
2846b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2847b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2848b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2849b2fc195eSAndrew Gallatin 		if (m != NULL) {
285071032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
285171032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
285271032832SAndrew Gallatin 				ss->omcasts++;
2853c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2854b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2855b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2856b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2857b2fc195eSAndrew Gallatin 			m_freem(m);
2858b2fc195eSAndrew Gallatin 		}
28595e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
28605e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
28615e7d8541SAndrew Gallatin 			tx->pkt_done++;
28625e7d8541SAndrew Gallatin 		}
2863b2fc195eSAndrew Gallatin 	}
2864b2fc195eSAndrew Gallatin 
2865b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2866b2fc195eSAndrew Gallatin 	   its OK to send packets */
2867c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2868c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2869c6cb3e3fSAndrew Gallatin #else
2870c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2871c6cb3e3fSAndrew Gallatin #endif
28721e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2873c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2874c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2875c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
28761e413cf9SAndrew Gallatin 		ss->tx.wake++;
28771e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2878b2fc195eSAndrew Gallatin 	}
2879c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2880c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2881c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2882c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2883c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2884c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2885c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2886c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2887c6cb3e3fSAndrew Gallatin 			wmb();
2888c6cb3e3fSAndrew Gallatin 		}
2889c6cb3e3fSAndrew Gallatin 	}
2890c6cb3e3fSAndrew Gallatin #endif
2891c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2892c6cb3e3fSAndrew Gallatin 
2893b2fc195eSAndrew Gallatin }
2894b2fc195eSAndrew Gallatin 
289501638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2896c587e59fSAndrew Gallatin {
2897c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2898c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2899c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2900c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
290101638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2902c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2903c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2904c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2905c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2906c587e59fSAndrew Gallatin };
290701638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
290801638550SAndrew Gallatin {
290951bc2092SAndrew Gallatin 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
29104ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
291101638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
291201638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
291356b67858SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
291456b67858SAndrew Gallatin 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
291501638550SAndrew Gallatin };
2916c587e59fSAndrew Gallatin 
2917c587e59fSAndrew Gallatin static void
2918c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type)
2919c587e59fSAndrew Gallatin {
2920c406ad2eSAndrew Gallatin 
2921c406ad2eSAndrew Gallatin 
2922c406ad2eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type,
2923c406ad2eSAndrew Gallatin 		    0, NULL);
2924c406ad2eSAndrew Gallatin 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
2925c406ad2eSAndrew Gallatin 	sc->current_media = media_type;
2926c406ad2eSAndrew Gallatin 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
2927c587e59fSAndrew Gallatin }
2928c587e59fSAndrew Gallatin 
2929c587e59fSAndrew Gallatin static void
2930c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc)
2931c587e59fSAndrew Gallatin {
2932c587e59fSAndrew Gallatin 	char *ptr;
2933c406ad2eSAndrew Gallatin 	int i;
2934c587e59fSAndrew Gallatin 
2935c406ad2eSAndrew Gallatin 	ifmedia_removeall(&sc->media);
2936c406ad2eSAndrew Gallatin 	mxge_media_set(sc, IFM_AUTO);
2937c587e59fSAndrew Gallatin 
2938c587e59fSAndrew Gallatin 	/*
2939c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2940c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2941c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2942c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2943c587e59fSAndrew Gallatin 	 */
2944c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2945c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2946c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2947c406ad2eSAndrew Gallatin 		return;
2948c587e59fSAndrew Gallatin 	}
2949c587e59fSAndrew Gallatin 
2950c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
2951dc15eac0SEd Schouten 		ptr = strchr(ptr, '-');
2952c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2953c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2954c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2955c587e59fSAndrew Gallatin 			return;
2956c587e59fSAndrew Gallatin 		}
2957c587e59fSAndrew Gallatin 	}
295830882b10SAndrew Gallatin 	if (*ptr == 'C' || *(ptr +1) == 'C') {
295901638550SAndrew Gallatin 		/* -C is CX4 */
2960c406ad2eSAndrew Gallatin 		sc->connector = MXGE_CX4;
2961c406ad2eSAndrew Gallatin 		mxge_media_set(sc, IFM_10G_CX4);
2962c406ad2eSAndrew Gallatin 	} else if (*ptr == 'Q') {
296301638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2964c406ad2eSAndrew Gallatin 		sc->connector = MXGE_QRF;
2965c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2966c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2967c406ad2eSAndrew Gallatin 	} else if (*ptr == 'R') {
2968c406ad2eSAndrew Gallatin 		/* -R is XFP */
2969c406ad2eSAndrew Gallatin 		sc->connector = MXGE_XFP;
2970c406ad2eSAndrew Gallatin 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
2971c406ad2eSAndrew Gallatin 		/* -S or -2S is SFP+ */
2972c406ad2eSAndrew Gallatin 		sc->connector = MXGE_SFP;
2973c406ad2eSAndrew Gallatin 	} else {
2974c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2975c406ad2eSAndrew Gallatin 	}
2976c587e59fSAndrew Gallatin }
2977c587e59fSAndrew Gallatin 
2978c406ad2eSAndrew Gallatin /*
2979c406ad2eSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2980c406ad2eSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2981c406ad2eSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2982c406ad2eSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2983c406ad2eSAndrew Gallatin  * than in the interrupt handler itself.
2984c406ad2eSAndrew Gallatin  */
2985c406ad2eSAndrew Gallatin static void
2986c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2987c406ad2eSAndrew Gallatin {
2988c406ad2eSAndrew Gallatin 	mxge_cmd_t cmd;
2989c406ad2eSAndrew Gallatin 	char *cage_type;
2990c406ad2eSAndrew Gallatin 
2991c406ad2eSAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
2992c406ad2eSAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
2993c406ad2eSAndrew Gallatin 	uint32_t byte;
2994c406ad2eSAndrew Gallatin 
2995c406ad2eSAndrew Gallatin 	sc->need_media_probe = 0;
2996c406ad2eSAndrew Gallatin 
2997c406ad2eSAndrew Gallatin 	if (sc->connector == MXGE_XFP) {
299801638550SAndrew Gallatin 		/* -R is XFP */
299901638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
300001638550SAndrew Gallatin 		mxge_media_type_entries =
300101638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types) /
300201638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types[0]);
300301638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
300401638550SAndrew Gallatin 		cage_type = "XFP";
3005c406ad2eSAndrew Gallatin 	} else 	if (sc->connector == MXGE_SFP) {
300601638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
300701638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
300801638550SAndrew Gallatin 		mxge_media_type_entries =
300901638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types) /
301001638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types[0]);
301101638550SAndrew Gallatin 		cage_type = "SFP+";
301201638550SAndrew Gallatin 		byte = 3;
3013c406ad2eSAndrew Gallatin 	} else {
3014c406ad2eSAndrew Gallatin 		/* nothing to do; media type cannot change */
3015c587e59fSAndrew Gallatin 		return;
3016c587e59fSAndrew Gallatin 	}
3017c587e59fSAndrew Gallatin 
3018c587e59fSAndrew Gallatin 	/*
3019c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
3020c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
3021c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
3022c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
3023c587e59fSAndrew Gallatin 	 * a millisecond
3024c587e59fSAndrew Gallatin 	 */
3025c587e59fSAndrew Gallatin 
3026c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
302701638550SAndrew Gallatin 	cmd.data1 = byte;
302801638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
302901638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
3030c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
3031c587e59fSAndrew Gallatin 	}
303201638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
303301638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
3034c587e59fSAndrew Gallatin 	}
3035c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
3036c587e59fSAndrew Gallatin 		return;
3037c587e59fSAndrew Gallatin 	}
3038c587e59fSAndrew Gallatin 
3039c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
304001638550SAndrew Gallatin 	cmd.data0 = byte;
304101638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3042c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
3043c587e59fSAndrew Gallatin 		DELAY(1000);
304401638550SAndrew Gallatin 		cmd.data0 = byte;
304501638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3046c587e59fSAndrew Gallatin 	}
3047c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
304801638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
304901638550SAndrew Gallatin 			      cage_type, err, ms);
3050c587e59fSAndrew Gallatin 		return;
3051c587e59fSAndrew Gallatin 	}
3052c587e59fSAndrew Gallatin 
3053c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
3054c587e59fSAndrew Gallatin 		if (mxge_verbose)
305501638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
3056c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
3057c406ad2eSAndrew Gallatin 		if (sc->current_media != mxge_media_types[0].flag) {
3058c406ad2eSAndrew Gallatin 			mxge_media_init(sc);
3059c406ad2eSAndrew Gallatin 			mxge_media_set(sc, mxge_media_types[0].flag);
3060c406ad2eSAndrew Gallatin 		}
3061c587e59fSAndrew Gallatin 		return;
3062c587e59fSAndrew Gallatin 	}
306301638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
3064c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
3065c587e59fSAndrew Gallatin 			if (mxge_verbose)
306601638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
306701638550SAndrew Gallatin 					      cage_type,
3068c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
3069c587e59fSAndrew Gallatin 
3070c406ad2eSAndrew Gallatin 			if (sc->current_media != mxge_media_types[i].flag) {
3071c406ad2eSAndrew Gallatin 				mxge_media_init(sc);
3072c406ad2eSAndrew Gallatin 				mxge_media_set(sc, mxge_media_types[i].flag);
3073c406ad2eSAndrew Gallatin 			}
3074c587e59fSAndrew Gallatin 			return;
3075c587e59fSAndrew Gallatin 		}
3076c587e59fSAndrew Gallatin 	}
3077c406ad2eSAndrew Gallatin 	if (mxge_verbose)
3078c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "%s media 0x%x unknown\n",
3079c406ad2eSAndrew Gallatin 			      cage_type, cmd.data0);
3080c587e59fSAndrew Gallatin 
3081c587e59fSAndrew Gallatin 	return;
3082c587e59fSAndrew Gallatin }
3083c587e59fSAndrew Gallatin 
3084b2fc195eSAndrew Gallatin static void
30856d87a65dSAndrew Gallatin mxge_intr(void *arg)
3086b2fc195eSAndrew Gallatin {
30871e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
30881e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
30891e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
30901e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
30911e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
30925e7d8541SAndrew Gallatin 	uint32_t send_done_count;
30935e7d8541SAndrew Gallatin 	uint8_t valid;
3094b2fc195eSAndrew Gallatin 
3095b2fc195eSAndrew Gallatin 
3096c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
30971e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
30981e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
30991e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
31001e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
31011e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
31021e413cf9SAndrew Gallatin 		return;
31031e413cf9SAndrew Gallatin 	}
3104c6cb3e3fSAndrew Gallatin #endif
31051e413cf9SAndrew Gallatin 
31065e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
31075e7d8541SAndrew Gallatin 	if (!stats->valid) {
31085e7d8541SAndrew Gallatin 		return;
3109b2fc195eSAndrew Gallatin 	}
31105e7d8541SAndrew Gallatin 	valid = stats->valid;
3111b2fc195eSAndrew Gallatin 
311291ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
31135e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
31145e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
31155e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
31165e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
31175e7d8541SAndrew Gallatin 			stats->valid = 0;
3118dc8731d4SAndrew Gallatin 	} else {
3119dc8731d4SAndrew Gallatin 		stats->valid = 0;
3120dc8731d4SAndrew Gallatin 	}
3121dc8731d4SAndrew Gallatin 
3122dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
31235e7d8541SAndrew Gallatin 	do {
31245e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
31255e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
31265e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
31275e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
3128c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
31291e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
31301e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
31315e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
3132b2fc195eSAndrew Gallatin 		}
313391ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
313473c7c83fSAndrew Gallatin 			wmb();
31355e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
3136b2fc195eSAndrew Gallatin 
3137c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
3138c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
31395e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
31405e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
3141b2fc195eSAndrew Gallatin 			if (sc->link_state) {
31425e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
31435e7d8541SAndrew Gallatin 				if (mxge_verbose)
31445e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
3145b2fc195eSAndrew Gallatin 			} else {
31465e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
31475e7d8541SAndrew Gallatin 				if (mxge_verbose)
31485e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
3149b2fc195eSAndrew Gallatin 			}
3150c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
3151b2fc195eSAndrew Gallatin 		}
3152b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
31531e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
3154b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
31551e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
31565e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
31575e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
31585e7d8541SAndrew Gallatin 		}
3159c587e59fSAndrew Gallatin 
3160c587e59fSAndrew Gallatin 		if (stats->link_down) {
31615e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3162c587e59fSAndrew Gallatin 			sc->link_state = 0;
3163c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3164c587e59fSAndrew Gallatin 		}
3165b2fc195eSAndrew Gallatin 	}
3166b2fc195eSAndrew Gallatin 
31675e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
31685e7d8541SAndrew Gallatin 	if (valid & 0x1)
31691e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
31701e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3171b2fc195eSAndrew Gallatin }
3172b2fc195eSAndrew Gallatin 
3173b2fc195eSAndrew Gallatin static void
31746d87a65dSAndrew Gallatin mxge_init(void *arg)
3175b2fc195eSAndrew Gallatin {
31763cae7311SAndrew Gallatin 	mxge_softc_t *sc = arg;
31773cae7311SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
31783cae7311SAndrew Gallatin 
31793cae7311SAndrew Gallatin 
31803cae7311SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
31813cae7311SAndrew Gallatin 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
31823cae7311SAndrew Gallatin 		(void) mxge_open(sc);
31833cae7311SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3184b2fc195eSAndrew Gallatin }
3185b2fc195eSAndrew Gallatin 
3186b2fc195eSAndrew Gallatin 
3187b2fc195eSAndrew Gallatin 
3188b2fc195eSAndrew Gallatin static void
31891e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
31901e413cf9SAndrew Gallatin {
31911e413cf9SAndrew Gallatin 	int i;
31921e413cf9SAndrew Gallatin 
319326dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
319426dd49c6SAndrew Gallatin 	tcp_lro_free(&ss->lc);
319526dd49c6SAndrew Gallatin #endif
31961e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
31971e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
31981e413cf9SAndrew Gallatin 			continue;
31991e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
32001e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
32011e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
32021e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
32031e413cf9SAndrew Gallatin 	}
32041e413cf9SAndrew Gallatin 
32051e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
32061e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
32071e413cf9SAndrew Gallatin 			continue;
32081e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
32091e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
32101e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
32111e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
32121e413cf9SAndrew Gallatin 	}
32131e413cf9SAndrew Gallatin 
32141e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
32151e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
32161e413cf9SAndrew Gallatin 		return;
32171e413cf9SAndrew Gallatin 
32181e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
32191e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
32201e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
32211e413cf9SAndrew Gallatin 			continue;
32221e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
32231e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
32241e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
32251e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
32261e413cf9SAndrew Gallatin 	}
32271e413cf9SAndrew Gallatin }
32281e413cf9SAndrew Gallatin 
32291e413cf9SAndrew Gallatin static void
32306d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3231b2fc195eSAndrew Gallatin {
32321e413cf9SAndrew Gallatin 	int slice;
32331e413cf9SAndrew Gallatin 
32341e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
32351e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
32361e413cf9SAndrew Gallatin }
32371e413cf9SAndrew Gallatin 
32381e413cf9SAndrew Gallatin static void
32391e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
32401e413cf9SAndrew Gallatin {
3241b2fc195eSAndrew Gallatin 	int i;
3242b2fc195eSAndrew Gallatin 
3243b2fc195eSAndrew Gallatin 
32441e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
32451e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
32461e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3247b2fc195eSAndrew Gallatin 
32481e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
32491e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
32501e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
32511e413cf9SAndrew Gallatin 
32521e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
32531e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
32541e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
32551e413cf9SAndrew Gallatin 
32561e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
32571e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
32581e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
32591e413cf9SAndrew Gallatin 
32601e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
32611e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
32621e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
32631e413cf9SAndrew Gallatin 
32641e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
32651e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
32661e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
32671e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
32681e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3269b2fc195eSAndrew Gallatin 			}
32701e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
32711e413cf9SAndrew Gallatin 		}
32721e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
32731e413cf9SAndrew Gallatin 	}
32741e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
32751e413cf9SAndrew Gallatin 
32761e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
32771e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
32781e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
32791e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
32801e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
32811e413cf9SAndrew Gallatin 			}
32821e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
32831e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
32841e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
32851e413cf9SAndrew Gallatin 		}
32861e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
32871e413cf9SAndrew Gallatin 	}
32881e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
32891e413cf9SAndrew Gallatin 
32901e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
32911e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
32921e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
32931e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
32941e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
32951e413cf9SAndrew Gallatin 			}
32961e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
32971e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
32981e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
32991e413cf9SAndrew Gallatin 		}
33001e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
33011e413cf9SAndrew Gallatin 	}
33021e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3303b2fc195eSAndrew Gallatin }
3304b2fc195eSAndrew Gallatin 
3305b2fc195eSAndrew Gallatin static void
33066d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3307b2fc195eSAndrew Gallatin {
33081e413cf9SAndrew Gallatin 	int slice;
3309b2fc195eSAndrew Gallatin 
33101e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
33111e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3312c2657176SAndrew Gallatin }
3313b2fc195eSAndrew Gallatin 
3314b2fc195eSAndrew Gallatin static int
33151e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
33161e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3317b2fc195eSAndrew Gallatin {
33181e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
33191e413cf9SAndrew Gallatin 	size_t bytes;
33201e413cf9SAndrew Gallatin 	int err, i;
3321b2fc195eSAndrew Gallatin 
33221e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3323adae7080SAndrew Gallatin 
33241e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
33251e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3326aed8e389SAndrew Gallatin 
3327b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
33281e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
33291e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3330b2fc195eSAndrew Gallatin 
33311e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
33321e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3333b2fc195eSAndrew Gallatin 
33341e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
33351e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
33361e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3337b2fc195eSAndrew Gallatin 
33381e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
33391e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3340b2fc195eSAndrew Gallatin 
33411e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3342b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3343b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3344b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3345b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3346b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3347b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3348b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3349b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3350b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3351b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3352b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
33531e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3354b2fc195eSAndrew Gallatin 	if (err != 0) {
3355b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3356b2fc195eSAndrew Gallatin 			      err);
3357c2ede4b3SMartin Blapp 		return err;
3358b2fc195eSAndrew Gallatin 	}
3359b2fc195eSAndrew Gallatin 
3360b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3361b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3362b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3363b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3364b0f7b922SAndrew Gallatin #else
3365b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3366b0f7b922SAndrew Gallatin #endif
3367b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3368b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3369b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3370053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3371b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3372053e637fSAndrew Gallatin 				 3,			/* num segs */
3373b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3374b0f7b922SAndrew Gallatin #else
3375b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3376b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3377b0f7b922SAndrew Gallatin #endif
3378b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3379b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
33801e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3381b2fc195eSAndrew Gallatin 	if (err != 0) {
3382b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3383b2fc195eSAndrew Gallatin 			      err);
3384c2ede4b3SMartin Blapp 		return err;
3385b2fc195eSAndrew Gallatin 	}
33861e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
33871e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
33881e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3389b2fc195eSAndrew Gallatin 		if (err != 0) {
3390b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3391b2fc195eSAndrew Gallatin 				      err);
3392c2ede4b3SMartin Blapp 			return err;
3393b2fc195eSAndrew Gallatin 		}
3394b2fc195eSAndrew Gallatin 	}
33951e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
33961e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3397b2fc195eSAndrew Gallatin 	if (err != 0) {
3398b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3399b2fc195eSAndrew Gallatin 			      err);
3400c2ede4b3SMartin Blapp 		return err;
3401b2fc195eSAndrew Gallatin 	}
3402b2fc195eSAndrew Gallatin 
34031e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
34041e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
34051e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3406b2fc195eSAndrew Gallatin 		if (err != 0) {
3407b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3408b2fc195eSAndrew Gallatin 				      err);
3409c2ede4b3SMartin Blapp 			return err;
3410b2fc195eSAndrew Gallatin 		}
3411b2fc195eSAndrew Gallatin 	}
34121e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
34131e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3414b2fc195eSAndrew Gallatin 	if (err != 0) {
3415b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3416b2fc195eSAndrew Gallatin 			      err);
3417c2ede4b3SMartin Blapp 		return err;
34181e413cf9SAndrew Gallatin 	}
34191e413cf9SAndrew Gallatin 
3420b78540b1SGabor Kovesdan 	/* now allocate TX resources */
34211e413cf9SAndrew Gallatin 
3422c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
34231e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
34241e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
34251e413cf9SAndrew Gallatin 		return 0;
3426c6cb3e3fSAndrew Gallatin #endif
34271e413cf9SAndrew Gallatin 
34281e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
34291e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
34301e413cf9SAndrew Gallatin 
34311e413cf9SAndrew Gallatin 
34321e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
34331e413cf9SAndrew Gallatin 	bytes = 8 +
34341e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
34351e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
34361e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
34371e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
34381e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
34391e413cf9SAndrew Gallatin 
34401e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
34411e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
34421e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
34431e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
34441e413cf9SAndrew Gallatin 
34451e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
34461e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
34471e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
34481e413cf9SAndrew Gallatin 
34491e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
34501e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
34511e413cf9SAndrew Gallatin 				 1,			/* alignment */
34521e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
34531e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
34541e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
34551e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
34561e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
34571e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
34581e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
34591e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
34601e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
34611e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
34621e413cf9SAndrew Gallatin 
34631e413cf9SAndrew Gallatin 	if (err != 0) {
34641e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
34651e413cf9SAndrew Gallatin 			      err);
3466c2ede4b3SMartin Blapp 		return err;
34671e413cf9SAndrew Gallatin 	}
34681e413cf9SAndrew Gallatin 
34691e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
34701e413cf9SAndrew Gallatin 	   in the ring */
34711e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
34721e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
34731e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
34741e413cf9SAndrew Gallatin 		if (err != 0) {
34751e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
34761e413cf9SAndrew Gallatin 				      err);
3477c2ede4b3SMartin Blapp 			return err;
34781e413cf9SAndrew Gallatin 		}
3479b2fc195eSAndrew Gallatin 	}
3480b2fc195eSAndrew Gallatin 	return 0;
3481b2fc195eSAndrew Gallatin 
3482b2fc195eSAndrew Gallatin }
3483b2fc195eSAndrew Gallatin 
34841e413cf9SAndrew Gallatin static int
34851e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
34861e413cf9SAndrew Gallatin {
34871e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
34881e413cf9SAndrew Gallatin 	int tx_ring_size;
34891e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
34901e413cf9SAndrew Gallatin 	int err, slice;
34911e413cf9SAndrew Gallatin 
34921e413cf9SAndrew Gallatin 	/* get ring sizes */
34931e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
34941e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
34951e413cf9SAndrew Gallatin 	if (err != 0) {
34961e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
34971e413cf9SAndrew Gallatin 		goto abort;
34981e413cf9SAndrew Gallatin 	}
34991e413cf9SAndrew Gallatin 
35001e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
35011e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
35021e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
35031e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
35041e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
35051e413cf9SAndrew Gallatin 
35061e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
35071e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
35081e413cf9SAndrew Gallatin 					     rx_ring_entries,
35091e413cf9SAndrew Gallatin 					     tx_ring_entries);
35101e413cf9SAndrew Gallatin 		if (err != 0)
35111e413cf9SAndrew Gallatin 			goto abort;
35121e413cf9SAndrew Gallatin 	}
35131e413cf9SAndrew Gallatin 	return 0;
35141e413cf9SAndrew Gallatin 
35151e413cf9SAndrew Gallatin abort:
35161e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
35171e413cf9SAndrew Gallatin 	return err;
35181e413cf9SAndrew Gallatin 
35191e413cf9SAndrew Gallatin }
35201e413cf9SAndrew Gallatin 
35211e413cf9SAndrew Gallatin 
3522053e637fSAndrew Gallatin static void
3523053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3524053e637fSAndrew Gallatin {
3525c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3526053e637fSAndrew Gallatin 
3527053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3528053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3529053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3530053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3531053e637fSAndrew Gallatin 		*nbufs = 1;
3532053e637fSAndrew Gallatin 		return;
3533053e637fSAndrew Gallatin 	}
3534053e637fSAndrew Gallatin 
3535053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3536053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3537053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3538053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3539053e637fSAndrew Gallatin 		*nbufs = 1;
3540053e637fSAndrew Gallatin 		return;
3541053e637fSAndrew Gallatin 	}
3542b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3543053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3544053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3545053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3546053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3547053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3548053e637fSAndrew Gallatin 	if (*nbufs == 3)
3549053e637fSAndrew Gallatin 		*nbufs = 4;
3550b0f7b922SAndrew Gallatin #else
3551b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3552b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3553b0f7b922SAndrew Gallatin 	*nbufs = 1;
3554b0f7b922SAndrew Gallatin #endif
3555053e637fSAndrew Gallatin }
3556053e637fSAndrew Gallatin 
3557b2fc195eSAndrew Gallatin static int
35581e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3559b2fc195eSAndrew Gallatin {
35601e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
35616d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3562b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
35631e413cf9SAndrew Gallatin 	int err, i, slice;
3564b2fc195eSAndrew Gallatin 
35651e413cf9SAndrew Gallatin 
35661e413cf9SAndrew Gallatin 	sc = ss->sc;
35671e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
35681e413cf9SAndrew Gallatin 
356926dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
357026dd49c6SAndrew Gallatin 	(void)tcp_lro_init(&ss->lc);
357126dd49c6SAndrew Gallatin #endif
357226dd49c6SAndrew Gallatin 	ss->lc.ifp = sc->ifp;
3573053e637fSAndrew Gallatin 
35741e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
35751e413cf9SAndrew Gallatin 
35761e413cf9SAndrew Gallatin 	err = 0;
3577c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35781e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
35791e413cf9SAndrew Gallatin 	if (slice == 0) {
3580c6cb3e3fSAndrew Gallatin #endif
35811e413cf9SAndrew Gallatin 		cmd.data0 = slice;
35821e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
35831e413cf9SAndrew Gallatin 		ss->tx.lanai =
35841e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3585c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3586c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3587c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3588c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3589c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35901e413cf9SAndrew Gallatin 	}
3591c6cb3e3fSAndrew Gallatin #endif
35921e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35931e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
35941e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
35951e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
35961e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35971e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35981e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
35991e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
36001e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
36011e413cf9SAndrew Gallatin 
36021e413cf9SAndrew Gallatin 	if (err != 0) {
36031e413cf9SAndrew Gallatin 		device_printf(sc->dev,
36041e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
36051e413cf9SAndrew Gallatin 		return EIO;
36061e413cf9SAndrew Gallatin 	}
36071e413cf9SAndrew Gallatin 
36081e413cf9SAndrew Gallatin 	/* stock receive rings */
36091e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
36101e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
36111e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
36121e413cf9SAndrew Gallatin 		if (err) {
36131e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
36141e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
36151e413cf9SAndrew Gallatin 			return ENOMEM;
36161e413cf9SAndrew Gallatin 		}
36171e413cf9SAndrew Gallatin 	}
36181e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
36191e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
36201e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
36211e413cf9SAndrew Gallatin 	}
36221e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
36231e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
36244d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
36254d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
36261e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
36271e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
36281e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
36291e413cf9SAndrew Gallatin 		if (err) {
36301e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
36311e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
36321e413cf9SAndrew Gallatin 			return ENOMEM;
36331e413cf9SAndrew Gallatin 		}
36341e413cf9SAndrew Gallatin 	}
36351e413cf9SAndrew Gallatin 	return 0;
36361e413cf9SAndrew Gallatin }
36371e413cf9SAndrew Gallatin 
36381e413cf9SAndrew Gallatin static int
36391e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
36401e413cf9SAndrew Gallatin {
36411e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
36421e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
36431e413cf9SAndrew Gallatin 	bus_addr_t bus;
36441e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3645c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3646b2fc195eSAndrew Gallatin 
36477d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
36487d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
36497d542e2dSAndrew Gallatin 
3650adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3651b2fc195eSAndrew Gallatin 	if (err != 0) {
3652b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3653b2fc195eSAndrew Gallatin 		return EIO;
3654b2fc195eSAndrew Gallatin 	}
3655b2fc195eSAndrew Gallatin 
36561e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
36571e413cf9SAndrew Gallatin 		/* setup the indirection table */
36581e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
36591e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
36601e413cf9SAndrew Gallatin 				    &cmd);
3661b2fc195eSAndrew Gallatin 
36621e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
36631e413cf9SAndrew Gallatin 				     &cmd);
36641e413cf9SAndrew Gallatin 		if (err != 0) {
36651e413cf9SAndrew Gallatin 			device_printf(sc->dev,
36661e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
36671e413cf9SAndrew Gallatin 			return err;
36681e413cf9SAndrew Gallatin 		}
36691e413cf9SAndrew Gallatin 
36701e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
36711e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
36721e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
36731e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
36741e413cf9SAndrew Gallatin 
36751e413cf9SAndrew Gallatin 		cmd.data0 = 1;
36761e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
36771e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
36781e413cf9SAndrew Gallatin 		if (err != 0) {
36791e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
36801e413cf9SAndrew Gallatin 			return err;
36811e413cf9SAndrew Gallatin 		}
36821e413cf9SAndrew Gallatin 	}
36831e413cf9SAndrew Gallatin 
36841e413cf9SAndrew Gallatin 
36851e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
36861e413cf9SAndrew Gallatin 
36871e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3688053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3689053e637fSAndrew Gallatin 			    &cmd);
3690053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3691053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
36921e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3693053e637fSAndrew Gallatin 		device_printf(sc->dev,
3694053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
36951e413cf9SAndrew Gallatin 			      nbufs);
3696053e637fSAndrew Gallatin 		return EIO;
3697053e637fSAndrew Gallatin 	}
3698b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3699b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3700b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3701c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
37025e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3703b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
37045e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3705b2fc195eSAndrew Gallatin 			     &cmd);
3706053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
37075e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
37080fa7f681SAndrew Gallatin 
37090fa7f681SAndrew Gallatin 	if (err != 0) {
37100fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
37110fa7f681SAndrew Gallatin 		goto abort;
37120fa7f681SAndrew Gallatin 	}
37130fa7f681SAndrew Gallatin 
3714b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3715c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3716c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3717c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3718c6cb3e3fSAndrew Gallatin #else
3719c6cb3e3fSAndrew Gallatin 	     slice < 1;
3720c6cb3e3fSAndrew Gallatin #endif
3721c6cb3e3fSAndrew Gallatin 	     slice++) {
3722c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3723c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3724c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3725c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3726c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
37270fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3728c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3729c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3730c6cb3e3fSAndrew Gallatin 	}
37310fa7f681SAndrew Gallatin 
37320fa7f681SAndrew Gallatin 	if (err != 0) {
37331e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
37340fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
37350fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
37360fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
37370fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
37380fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
37390fa7f681SAndrew Gallatin 				    &cmd);
37400fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
37410fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
37420fa7f681SAndrew Gallatin 	} else {
37430fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
37440fa7f681SAndrew Gallatin 	}
3745b2fc195eSAndrew Gallatin 
3746b2fc195eSAndrew Gallatin 	if (err != 0) {
3747b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3748b2fc195eSAndrew Gallatin 		goto abort;
3749b2fc195eSAndrew Gallatin 	}
3750b2fc195eSAndrew Gallatin 
37511e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
37521e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
37531e413cf9SAndrew Gallatin 		if (err != 0) {
37541e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
37551e413cf9SAndrew Gallatin 				      slice);
37561e413cf9SAndrew Gallatin 			goto abort;
37571e413cf9SAndrew Gallatin 		}
37581e413cf9SAndrew Gallatin 	}
37591e413cf9SAndrew Gallatin 
3760b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
37615e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3762b2fc195eSAndrew Gallatin 	if (err) {
3763b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3764b2fc195eSAndrew Gallatin 		goto abort;
3765b2fc195eSAndrew Gallatin 	}
3766c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3767c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3768c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3769c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3770c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3771c6cb3e3fSAndrew Gallatin 	}
3772c6cb3e3fSAndrew Gallatin #endif
3773b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3774b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3775b2fc195eSAndrew Gallatin 
3776b2fc195eSAndrew Gallatin 	return 0;
3777b2fc195eSAndrew Gallatin 
3778b2fc195eSAndrew Gallatin 
3779b2fc195eSAndrew Gallatin abort:
37806d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3781a98d6cd7SAndrew Gallatin 
3782b2fc195eSAndrew Gallatin 	return err;
3783b2fc195eSAndrew Gallatin }
3784b2fc195eSAndrew Gallatin 
3785b2fc195eSAndrew Gallatin static int
3786a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3787b2fc195eSAndrew Gallatin {
37886d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3789b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3790c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3791c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3792c6cb3e3fSAndrew Gallatin 	int slice;
3793c6cb3e3fSAndrew Gallatin #endif
3794b2fc195eSAndrew Gallatin 
3795c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3796c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3797c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3798c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3799c6cb3e3fSAndrew Gallatin 	}
3800c6cb3e3fSAndrew Gallatin #endif
3801b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3802a393336bSAndrew Gallatin 	if (!down) {
3803b2fc195eSAndrew Gallatin 		old_down_cnt = sc->down_cnt;
380473c7c83fSAndrew Gallatin 		wmb();
38055e7d8541SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3806b2fc195eSAndrew Gallatin 		if (err) {
3807a393336bSAndrew Gallatin 			device_printf(sc->dev,
3808a393336bSAndrew Gallatin 				      "Couldn't bring down link\n");
3809b2fc195eSAndrew Gallatin 		}
3810b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3811b2fc195eSAndrew Gallatin 			/* wait for down irq */
3812dce01b9bSAndrew Gallatin 			DELAY(10 * sc->intr_coal_delay);
3813b2fc195eSAndrew Gallatin 		}
381473c7c83fSAndrew Gallatin 		wmb();
3815b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3816b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "never got down irq\n");
3817b2fc195eSAndrew Gallatin 		}
3818a393336bSAndrew Gallatin 	}
38196d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3820a98d6cd7SAndrew Gallatin 
3821b2fc195eSAndrew Gallatin 	return 0;
3822b2fc195eSAndrew Gallatin }
3823b2fc195eSAndrew Gallatin 
3824dce01b9bSAndrew Gallatin static void
3825dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3826dce01b9bSAndrew Gallatin {
3827dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3828dce01b9bSAndrew Gallatin 	int reg;
3829c68534f1SScott Long 	uint16_t lnk, pectl;
3830dce01b9bSAndrew Gallatin 
3831dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
38323b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
3833dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3834dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3835dce01b9bSAndrew Gallatin 
383683d54b59SAndrew Gallatin 		if (sc->pectl == 0) {
3837dce01b9bSAndrew Gallatin 			pectl = pci_read_config(dev, reg + 0x8, 2);
3838dce01b9bSAndrew Gallatin 			pectl = (pectl & ~0x7000) | (5 << 12);
3839dce01b9bSAndrew Gallatin 			pci_write_config(dev, reg + 0x8, pectl, 2);
384083d54b59SAndrew Gallatin 			sc->pectl = pectl;
384183d54b59SAndrew Gallatin 		} else {
384283d54b59SAndrew Gallatin 			/* restore saved pectl after watchdog reset */
384383d54b59SAndrew Gallatin 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
384483d54b59SAndrew Gallatin 		}
3845dce01b9bSAndrew Gallatin 	}
3846dce01b9bSAndrew Gallatin 
3847dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3848dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3849dce01b9bSAndrew Gallatin }
3850dce01b9bSAndrew Gallatin 
3851dce01b9bSAndrew Gallatin static uint32_t
3852dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3853dce01b9bSAndrew Gallatin {
3854dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3855dce01b9bSAndrew Gallatin 	uint32_t vs;
3856dce01b9bSAndrew Gallatin 
3857dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
38583b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) {
3859dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3860dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3861dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3862dce01b9bSAndrew Gallatin 	}
3863dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3864dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3865dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3866dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3867dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3868dce01b9bSAndrew Gallatin }
3869dce01b9bSAndrew Gallatin 
387072c042dfSAndrew Gallatin static void
387172c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3872dce01b9bSAndrew Gallatin {
3873e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3874a393336bSAndrew Gallatin 	struct mxge_slice_state *ss;
3875a393336bSAndrew Gallatin 	int err, running, s, num_tx_slices = 1;
3876dce01b9bSAndrew Gallatin 	uint32_t reboot;
3877dce01b9bSAndrew Gallatin 	uint16_t cmd;
3878dce01b9bSAndrew Gallatin 
3879dce01b9bSAndrew Gallatin 	err = ENXIO;
3880dce01b9bSAndrew Gallatin 
3881dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3882dce01b9bSAndrew Gallatin 
3883dce01b9bSAndrew Gallatin 	/*
3884dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3885dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3886dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3887dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3888dce01b9bSAndrew Gallatin 	 * again
3889dce01b9bSAndrew Gallatin 	 */
3890dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3891dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3892dce01b9bSAndrew Gallatin 		/*
3893dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3894dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3895dce01b9bSAndrew Gallatin 		 * back, then give up
3896dce01b9bSAndrew Gallatin 		 */
3897dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3898dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3899dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3900dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3901dce01b9bSAndrew Gallatin 		}
3902dce01b9bSAndrew Gallatin 	}
3903dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3904dce01b9bSAndrew Gallatin 		/* print the reboot status */
3905dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3906dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3907dce01b9bSAndrew Gallatin 			      reboot);
3908a393336bSAndrew Gallatin 		running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
3909a393336bSAndrew Gallatin 		if (running) {
3910a393336bSAndrew Gallatin 
3911a393336bSAndrew Gallatin 			/*
3912a393336bSAndrew Gallatin 			 * quiesce NIC so that TX routines will not try to
3913a393336bSAndrew Gallatin 			 * xmit after restoration of BAR
3914a393336bSAndrew Gallatin 			 */
3915a393336bSAndrew Gallatin 
3916a393336bSAndrew Gallatin 			/* Mark the link as down */
3917a393336bSAndrew Gallatin 			if (sc->link_state) {
3918a393336bSAndrew Gallatin 				sc->link_state = 0;
3919a393336bSAndrew Gallatin 				if_link_state_change(sc->ifp,
3920a393336bSAndrew Gallatin 						     LINK_STATE_DOWN);
3921a393336bSAndrew Gallatin 			}
3922a393336bSAndrew Gallatin #ifdef IFNET_BUF_RING
3923a393336bSAndrew Gallatin 			num_tx_slices = sc->num_slices;
3924a393336bSAndrew Gallatin #endif
3925a393336bSAndrew Gallatin 			/* grab all TX locks to ensure no tx  */
3926a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3927a393336bSAndrew Gallatin 				ss = &sc->ss[s];
3928a393336bSAndrew Gallatin 				mtx_lock(&ss->tx.mtx);
3929a393336bSAndrew Gallatin 			}
3930a393336bSAndrew Gallatin 			mxge_close(sc, 1);
3931a393336bSAndrew Gallatin 		}
3932dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3933e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3934e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3935dce01b9bSAndrew Gallatin 
3936dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3937dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
393810882804SAndrew Gallatin 
3939a393336bSAndrew Gallatin 		/* reload f/w */
3940a393336bSAndrew Gallatin 		err = mxge_load_firmware(sc, 0);
3941a393336bSAndrew Gallatin 		if (err) {
3942a393336bSAndrew Gallatin 			device_printf(sc->dev,
3943a393336bSAndrew Gallatin 				      "Unable to re-load f/w\n");
394410882804SAndrew Gallatin 		}
3945a393336bSAndrew Gallatin 		if (running) {
3946a393336bSAndrew Gallatin 			if (!err)
3947a393336bSAndrew Gallatin 				err = mxge_open(sc);
3948a393336bSAndrew Gallatin 			/* release all TX locks */
3949a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3950a393336bSAndrew Gallatin 				ss = &sc->ss[s];
395183d54b59SAndrew Gallatin #ifdef IFNET_BUF_RING
395283d54b59SAndrew Gallatin 				mxge_start_locked(ss);
395383d54b59SAndrew Gallatin #endif
3954a393336bSAndrew Gallatin 				mtx_unlock(&ss->tx.mtx);
3955a393336bSAndrew Gallatin 			}
3956a393336bSAndrew Gallatin 		}
3957a393336bSAndrew Gallatin 		sc->watchdog_resets++;
3958dce01b9bSAndrew Gallatin 	} else {
3959c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
396072c042dfSAndrew Gallatin 			      "NIC did not reboot, not resetting\n");
396172c042dfSAndrew Gallatin 		err = 0;
396272c042dfSAndrew Gallatin 	}
396372c042dfSAndrew Gallatin 	if (err) {
396472c042dfSAndrew Gallatin 		device_printf(sc->dev, "watchdog reset failed\n");
396572c042dfSAndrew Gallatin 	} else {
39666b484a49SAndrew Gallatin 		if (sc->dying == 2)
39676b484a49SAndrew Gallatin 			sc->dying = 0;
39686b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
396972c042dfSAndrew Gallatin 	}
397072c042dfSAndrew Gallatin }
397172c042dfSAndrew Gallatin 
397272c042dfSAndrew Gallatin static void
397372c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
397472c042dfSAndrew Gallatin {
397572c042dfSAndrew Gallatin 	mxge_softc_t *sc = arg;
397672c042dfSAndrew Gallatin 
397772c042dfSAndrew Gallatin 
397872c042dfSAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
397972c042dfSAndrew Gallatin 	mxge_watchdog_reset(sc);
398072c042dfSAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
398172c042dfSAndrew Gallatin }
398272c042dfSAndrew Gallatin 
398372c042dfSAndrew Gallatin static void
398472c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
398572c042dfSAndrew Gallatin {
398672c042dfSAndrew Gallatin 	tx = &sc->ss[slice].tx;
398772c042dfSAndrew Gallatin 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3988c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev,
3989c6cb3e3fSAndrew Gallatin 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3990c6cb3e3fSAndrew Gallatin 		      tx->req, tx->done, tx->queue_active);
3991c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3992c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3993dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3994c6cb3e3fSAndrew Gallatin 		      tx->pkt_done,
39951e413cf9SAndrew Gallatin 		      be32toh(sc->ss->fw_stats->send_done_count));
3996dce01b9bSAndrew Gallatin }
3997dce01b9bSAndrew Gallatin 
3998e749ef6bSAndrew Gallatin static int
3999dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
4000dce01b9bSAndrew Gallatin {
4001c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
40021e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
4003c6cb3e3fSAndrew Gallatin 	int i, err = 0;
4004dce01b9bSAndrew Gallatin 
4005dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
4006dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
4007c6cb3e3fSAndrew Gallatin 	for (i = 0;
4008c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4009c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
4010c6cb3e3fSAndrew Gallatin #else
4011c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
4012c6cb3e3fSAndrew Gallatin #endif
4013c6cb3e3fSAndrew Gallatin 	     i++) {
4014c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
4015dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
4016dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
4017c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
4018c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
401972c042dfSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause) {
402072c042dfSAndrew Gallatin 				mxge_warn_stuck(sc, tx, i);
402172c042dfSAndrew Gallatin 				taskqueue_enqueue(sc->tq, &sc->watchdog_task);
402272c042dfSAndrew Gallatin 				return (ENXIO);
402372c042dfSAndrew Gallatin 			}
4024c587e59fSAndrew Gallatin 			else
4025c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
4026c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
4027c587e59fSAndrew Gallatin 		}
4028dce01b9bSAndrew Gallatin 
4029dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
4030dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
4031c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
4032c6cb3e3fSAndrew Gallatin 	}
4033c587e59fSAndrew Gallatin 
4034c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
4035c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
4036e749ef6bSAndrew Gallatin 	return (err);
4037dce01b9bSAndrew Gallatin }
4038dce01b9bSAndrew Gallatin 
4039f3f040d9SGleb Smirnoff static uint64_t
4040f3f040d9SGleb Smirnoff mxge_get_counter(struct ifnet *ifp, ift_counter cnt)
40411e413cf9SAndrew Gallatin {
4042f3f040d9SGleb Smirnoff 	struct mxge_softc *sc;
4043f3f040d9SGleb Smirnoff 	uint64_t rv;
40441e413cf9SAndrew Gallatin 
4045f3f040d9SGleb Smirnoff 	sc = if_getsoftc(ifp);
4046f3f040d9SGleb Smirnoff 	rv = 0;
4047f3f040d9SGleb Smirnoff 
4048f3f040d9SGleb Smirnoff 	switch (cnt) {
4049f3f040d9SGleb Smirnoff 	case IFCOUNTER_IPACKETS:
4050f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4051f3f040d9SGleb Smirnoff 			rv += sc->ss[s].ipackets;
4052f3f040d9SGleb Smirnoff 		return (rv);
4053f3f040d9SGleb Smirnoff 	case IFCOUNTER_OPACKETS:
4054f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4055f3f040d9SGleb Smirnoff 			rv += sc->ss[s].opackets;
4056f3f040d9SGleb Smirnoff 		return (rv);
4057f3f040d9SGleb Smirnoff 	case IFCOUNTER_OERRORS:
4058f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4059f3f040d9SGleb Smirnoff 			rv += sc->ss[s].oerrors;
4060f3f040d9SGleb Smirnoff 		return (rv);
406171032832SAndrew Gallatin #ifdef IFNET_BUF_RING
4062f3f040d9SGleb Smirnoff 	case IFCOUNTER_OBYTES:
4063f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4064f3f040d9SGleb Smirnoff 			rv += sc->ss[s].obytes;
4065f3f040d9SGleb Smirnoff 		return (rv);
4066f3f040d9SGleb Smirnoff 	case IFCOUNTER_OMCASTS:
4067f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4068f3f040d9SGleb Smirnoff 			rv += sc->ss[s].omcasts;
4069f3f040d9SGleb Smirnoff 		return (rv);
4070f3f040d9SGleb Smirnoff 	case IFCOUNTER_OQDROPS:
4071f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4072f3f040d9SGleb Smirnoff 			rv += sc->ss[s].tx.br->br_drops;
4073f3f040d9SGleb Smirnoff 		return (rv);
407471032832SAndrew Gallatin #endif
4075f3f040d9SGleb Smirnoff 	default:
4076f3f040d9SGleb Smirnoff 		return (if_get_counter_default(ifp, cnt));
40771e413cf9SAndrew Gallatin 	}
40781e413cf9SAndrew Gallatin }
4079c6cb3e3fSAndrew Gallatin 
40801e413cf9SAndrew Gallatin static void
4081dce01b9bSAndrew Gallatin mxge_tick(void *arg)
4082dce01b9bSAndrew Gallatin {
4083dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
40846b484a49SAndrew Gallatin 	u_long pkts = 0;
4085e749ef6bSAndrew Gallatin 	int err = 0;
40866b484a49SAndrew Gallatin 	int running, ticks;
40876b484a49SAndrew Gallatin 	uint16_t cmd;
4088dce01b9bSAndrew Gallatin 
40896b484a49SAndrew Gallatin 	ticks = mxge_ticks;
40906b484a49SAndrew Gallatin 	running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
40916b484a49SAndrew Gallatin 	if (running) {
40921e413cf9SAndrew Gallatin 		if (!sc->watchdog_countdown) {
4093e749ef6bSAndrew Gallatin 			err = mxge_watchdog(sc);
40941e413cf9SAndrew Gallatin 			sc->watchdog_countdown = 4;
40951e413cf9SAndrew Gallatin 		}
40961e413cf9SAndrew Gallatin 		sc->watchdog_countdown--;
40976b484a49SAndrew Gallatin 	}
40986b484a49SAndrew Gallatin 	if (pkts == 0) {
40996b484a49SAndrew Gallatin 		/* ensure NIC did not suffer h/w fault while idle */
41006b484a49SAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
41016b484a49SAndrew Gallatin 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
41026b484a49SAndrew Gallatin 			sc->dying = 2;
41036b484a49SAndrew Gallatin 			taskqueue_enqueue(sc->tq, &sc->watchdog_task);
41046b484a49SAndrew Gallatin 			err = ENXIO;
41056b484a49SAndrew Gallatin 		}
41066b484a49SAndrew Gallatin 		/* look less often if NIC is idle */
41076b484a49SAndrew Gallatin 		ticks *= 4;
41086b484a49SAndrew Gallatin 	}
41096b484a49SAndrew Gallatin 
4110e749ef6bSAndrew Gallatin 	if (err == 0)
41116b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
4112e749ef6bSAndrew Gallatin 
4113dce01b9bSAndrew Gallatin }
4114b2fc195eSAndrew Gallatin 
4115b2fc195eSAndrew Gallatin static int
41166d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
4117b2fc195eSAndrew Gallatin {
4118b2fc195eSAndrew Gallatin 	return EINVAL;
4119b2fc195eSAndrew Gallatin }
4120b2fc195eSAndrew Gallatin 
4121b2fc195eSAndrew Gallatin static int
41226d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
4123b2fc195eSAndrew Gallatin {
4124b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
4125b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
4126b2fc195eSAndrew Gallatin 	int err = 0;
4127b2fc195eSAndrew Gallatin 
4128b2fc195eSAndrew Gallatin 
4129c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
4130053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
4131b2fc195eSAndrew Gallatin 		return EINVAL;
4132a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4133b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
4134b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
4135b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4136a393336bSAndrew Gallatin 		mxge_close(sc, 0);
41376d87a65dSAndrew Gallatin 		err = mxge_open(sc);
4138b2fc195eSAndrew Gallatin 		if (err != 0) {
4139b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
4140a393336bSAndrew Gallatin 			mxge_close(sc, 0);
41416d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
4142b2fc195eSAndrew Gallatin 		}
4143b2fc195eSAndrew Gallatin 	}
4144a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4145b2fc195eSAndrew Gallatin 	return err;
4146b2fc195eSAndrew Gallatin }
4147b2fc195eSAndrew Gallatin 
4148b2fc195eSAndrew Gallatin static void
41496d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
4150b2fc195eSAndrew Gallatin {
41516d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4152b2fc195eSAndrew Gallatin 
4153b2fc195eSAndrew Gallatin 
4154b2fc195eSAndrew Gallatin 	if (sc == NULL)
4155b2fc195eSAndrew Gallatin 		return;
4156b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
4157c406ad2eSAndrew Gallatin 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
4158c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
4159c406ad2eSAndrew Gallatin 	ifmr->ifm_active |= sc->current_media;
4160b2fc195eSAndrew Gallatin }
4161b2fc195eSAndrew Gallatin 
4162b2fc195eSAndrew Gallatin static int
41636d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
4164b2fc195eSAndrew Gallatin {
41656d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4166b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
4167b2fc195eSAndrew Gallatin 	int err, mask;
4168b2fc195eSAndrew Gallatin 
4169b2fc195eSAndrew Gallatin 	err = 0;
4170b2fc195eSAndrew Gallatin 	switch (command) {
4171b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
4172b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
4173b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
4174b2fc195eSAndrew Gallatin 		break;
4175b2fc195eSAndrew Gallatin 
4176b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
41776d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
4178b2fc195eSAndrew Gallatin 		break;
4179b2fc195eSAndrew Gallatin 
4180b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
4181a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
41828c5d766cSAndrew Gallatin 		if (sc->dying) {
41838c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
41848c5d766cSAndrew Gallatin 			return EINVAL;
41858c5d766cSAndrew Gallatin 		}
4186b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
4187dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
41886d87a65dSAndrew Gallatin 				err = mxge_open(sc);
4189dce01b9bSAndrew Gallatin 			} else {
41900fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
41910fa7f681SAndrew Gallatin 				   flag chages */
41920fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
41930fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
41940fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
41950fa7f681SAndrew Gallatin 			}
4196b2fc195eSAndrew Gallatin 		} else {
4197dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4198a393336bSAndrew Gallatin 				mxge_close(sc, 0);
4199dce01b9bSAndrew Gallatin 			}
4200b2fc195eSAndrew Gallatin 		}
4201a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4202b2fc195eSAndrew Gallatin 		break;
4203b2fc195eSAndrew Gallatin 
4204b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
4205b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
4206a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
42070fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
4208a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4209b2fc195eSAndrew Gallatin 		break;
4210b2fc195eSAndrew Gallatin 
4211b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
4212a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4213b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
4214b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
4215b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
4216aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
42170a7a780eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
4218b2fc195eSAndrew Gallatin 			} else {
4219b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
4220b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
4221b2fc195eSAndrew Gallatin 			}
4222b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
4223b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
4224b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
4225b2fc195eSAndrew Gallatin 			} else {
4226b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
4227b2fc195eSAndrew Gallatin 			}
4228b2fc195eSAndrew Gallatin 		}
4229aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
4230aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
4231aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
4232aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
4233aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
4234aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
4235aed8e389SAndrew Gallatin 			} else {
4236aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4237aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4238aed8e389SAndrew Gallatin 				err = EINVAL;
4239aed8e389SAndrew Gallatin 			}
4240aed8e389SAndrew Gallatin 		}
42410a7a780eSAndrew Gallatin #if IFCAP_TSO6
42420a7a780eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM_IPV6) {
42430a7a780eSAndrew Gallatin 			if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) {
42440a7a780eSAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6
42450a7a780eSAndrew Gallatin 						       | IFCAP_TSO6);
42460a7a780eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP_IPV6
42470a7a780eSAndrew Gallatin 						      | CSUM_UDP);
42480a7a780eSAndrew Gallatin 			} else {
42490a7a780eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM_IPV6;
42500a7a780eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP_IPV6
42510a7a780eSAndrew Gallatin 						     | CSUM_UDP_IPV6);
42520a7a780eSAndrew Gallatin 			}
425326dd49c6SAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM_IPV6) {
425426dd49c6SAndrew Gallatin 			if (IFCAP_RXCSUM_IPV6 & ifp->if_capenable) {
425526dd49c6SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM_IPV6;
42560a7a780eSAndrew Gallatin 			} else {
425726dd49c6SAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM_IPV6;
42580a7a780eSAndrew Gallatin 			}
42590a7a780eSAndrew Gallatin 		}
42600a7a780eSAndrew Gallatin 		if (mask & IFCAP_TSO6) {
42610a7a780eSAndrew Gallatin 			if (IFCAP_TSO6 & ifp->if_capenable) {
42620a7a780eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO6;
42630a7a780eSAndrew Gallatin 			} else if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) {
42640a7a780eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO6;
42650a7a780eSAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
42660a7a780eSAndrew Gallatin 			} else {
42670a7a780eSAndrew Gallatin 				printf("mxge requires tx checksum offload"
42680a7a780eSAndrew Gallatin 				       " be enabled to use TSO\n");
42690a7a780eSAndrew Gallatin 				err = EINVAL;
42700a7a780eSAndrew Gallatin 			}
42710a7a780eSAndrew Gallatin 		}
42720a7a780eSAndrew Gallatin #endif /*IFCAP_TSO6 */
42730a7a780eSAndrew Gallatin 
427426dd49c6SAndrew Gallatin 		if (mask & IFCAP_LRO)
427526dd49c6SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_LRO;
4276c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
4277c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
42780dce6781SAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTSO)
42790dce6781SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
42800dce6781SAndrew Gallatin 
42810dce6781SAndrew Gallatin 		if (!(ifp->if_capabilities & IFCAP_VLAN_HWTSO) ||
42820dce6781SAndrew Gallatin 		    !(ifp->if_capenable & IFCAP_VLAN_HWTAGGING))
42830dce6781SAndrew Gallatin 			ifp->if_capenable &= ~IFCAP_VLAN_HWTSO;
42840dce6781SAndrew Gallatin 
4285a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4286c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4287c792928fSAndrew Gallatin 
4288b2fc195eSAndrew Gallatin 		break;
4289b2fc195eSAndrew Gallatin 
4290b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4291c406ad2eSAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4292c406ad2eSAndrew Gallatin 		mxge_media_probe(sc);
4293c406ad2eSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4294b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4295b2fc195eSAndrew Gallatin 				    &sc->media, command);
4296b2fc195eSAndrew Gallatin 		break;
4297b2fc195eSAndrew Gallatin 
4298b2fc195eSAndrew Gallatin 	default:
4299b2fc195eSAndrew Gallatin 		err = ENOTTY;
4300b2fc195eSAndrew Gallatin 	}
4301b2fc195eSAndrew Gallatin 	return err;
4302b2fc195eSAndrew Gallatin }
4303b2fc195eSAndrew Gallatin 
4304b2fc195eSAndrew Gallatin static void
43056d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4306b2fc195eSAndrew Gallatin {
4307b2fc195eSAndrew Gallatin 
43081e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
43096d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
43106d87a65dSAndrew Gallatin 			  &mxge_flow_control);
43116d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
43126d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
43136d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
43146d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4315d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4316d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
43175e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
43185e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
43195e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
43205e7d8541SAndrew Gallatin 			  &mxge_verbose);
4321dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
43221e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
43231e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
432494c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4325f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
432665c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4327b2fc195eSAndrew Gallatin 
43285e7d8541SAndrew Gallatin 	if (bootverbose)
43295e7d8541SAndrew Gallatin 		mxge_verbose = 1;
43306d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
43316d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4332dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
43331e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
43346d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
43351e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4336bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
43375769c5efSAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4338b2fc195eSAndrew Gallatin 	}
4339f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4340f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4341f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
434265c69066SAndrew Gallatin 
434365c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
434465c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
434565c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
434665c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
434765c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
43481e413cf9SAndrew Gallatin }
43491e413cf9SAndrew Gallatin 
43501e413cf9SAndrew Gallatin 
43511e413cf9SAndrew Gallatin static void
43521e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
43531e413cf9SAndrew Gallatin {
43541e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
43551e413cf9SAndrew Gallatin 	int i;
43561e413cf9SAndrew Gallatin 
43571e413cf9SAndrew Gallatin 
43581e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
43591e413cf9SAndrew Gallatin 		return;
43601e413cf9SAndrew Gallatin 
43611e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43621e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
43631e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
43641e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
43651e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4366c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4367c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4368c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4369c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4370c6cb3e3fSAndrew Gallatin 			}
4371c6cb3e3fSAndrew Gallatin #endif
43721e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
43731e413cf9SAndrew Gallatin 		}
43741e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
43751e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
43761e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
43771e413cf9SAndrew Gallatin 		}
43781e413cf9SAndrew Gallatin 	}
43791e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
43801e413cf9SAndrew Gallatin 	sc->ss = NULL;
43811e413cf9SAndrew Gallatin }
43821e413cf9SAndrew Gallatin 
43831e413cf9SAndrew Gallatin static int
43841e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
43851e413cf9SAndrew Gallatin {
43861e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
43871e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
43881e413cf9SAndrew Gallatin 	size_t bytes;
43891e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
43901e413cf9SAndrew Gallatin 
43911e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
43921e413cf9SAndrew Gallatin 	if (err != 0) {
43931e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
43941e413cf9SAndrew Gallatin 		return err;
43951e413cf9SAndrew Gallatin 	}
43961e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
43971e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
43981e413cf9SAndrew Gallatin 
43991e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
44001e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
44011e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
44021e413cf9SAndrew Gallatin 		return (ENOMEM);
44031e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44041e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
44051e413cf9SAndrew Gallatin 
44061e413cf9SAndrew Gallatin 		ss->sc = sc;
44071e413cf9SAndrew Gallatin 
44081e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
44091e413cf9SAndrew Gallatin 
44101e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
44111e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
44121e413cf9SAndrew Gallatin 		if (err != 0)
44131e413cf9SAndrew Gallatin 			goto abort;
44141e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
44151e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
44161e413cf9SAndrew Gallatin 
44171e413cf9SAndrew Gallatin 		/*
44181e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
44191e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
44201e413cf9SAndrew Gallatin 		 * slice for now
44211e413cf9SAndrew Gallatin 		 */
4422c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
44231e413cf9SAndrew Gallatin 		if (i > 0)
44241e413cf9SAndrew Gallatin 			continue;
4425c6cb3e3fSAndrew Gallatin #endif
44261e413cf9SAndrew Gallatin 
44271e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
44281e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
44291e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
44301e413cf9SAndrew Gallatin 		if (err != 0)
44311e413cf9SAndrew Gallatin 			goto abort;
44321e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
44331e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
44341e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
44351e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4436c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4437c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4438c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4439c6cb3e3fSAndrew Gallatin #endif
44401e413cf9SAndrew Gallatin 	}
44411e413cf9SAndrew Gallatin 
44421e413cf9SAndrew Gallatin 	return (0);
44431e413cf9SAndrew Gallatin 
44441e413cf9SAndrew Gallatin abort:
44451e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
44461e413cf9SAndrew Gallatin 	return (ENOMEM);
44471e413cf9SAndrew Gallatin }
44481e413cf9SAndrew Gallatin 
44491e413cf9SAndrew Gallatin static void
44501e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
44511e413cf9SAndrew Gallatin {
44521e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
44531e413cf9SAndrew Gallatin 	char *old_fw;
44541e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
44551e413cf9SAndrew Gallatin 
44561e413cf9SAndrew Gallatin 	sc->num_slices = 1;
44571e413cf9SAndrew Gallatin 	/*
44581e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
44591e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
44601e413cf9SAndrew Gallatin 	 */
44611e413cf9SAndrew Gallatin 
44621e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
44631e413cf9SAndrew Gallatin 		return;
44641e413cf9SAndrew Gallatin 
44651e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
44661e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
44671e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
44681e413cf9SAndrew Gallatin 		return;
44691e413cf9SAndrew Gallatin 
44701e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
44711e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
44721e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
44731e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
44741e413cf9SAndrew Gallatin 	else
44751e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
44761e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
44771e413cf9SAndrew Gallatin 	if (status != 0) {
44781e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
44791e413cf9SAndrew Gallatin 		return;
44801e413cf9SAndrew Gallatin 	}
44811e413cf9SAndrew Gallatin 
44821e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
44831e413cf9SAndrew Gallatin 	   is alive */
44841e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
44851e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
44861e413cf9SAndrew Gallatin 	if (status != 0) {
44871e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
44881e413cf9SAndrew Gallatin 		goto abort_with_fw;
44891e413cf9SAndrew Gallatin 	}
44901e413cf9SAndrew Gallatin 
44911e413cf9SAndrew Gallatin 	/* get rx ring size */
44921e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
44931e413cf9SAndrew Gallatin 	if (status != 0) {
44941e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
44951e413cf9SAndrew Gallatin 		goto abort_with_fw;
44961e413cf9SAndrew Gallatin 	}
44971e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
44981e413cf9SAndrew Gallatin 
44991e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
45001e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
45011e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
45021e413cf9SAndrew Gallatin 	if (status != 0) {
45031e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
45041e413cf9SAndrew Gallatin 		goto abort_with_fw;
45051e413cf9SAndrew Gallatin 	}
45061e413cf9SAndrew Gallatin 
45071e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
45081e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
45091e413cf9SAndrew Gallatin 	if (status != 0) {
45101e413cf9SAndrew Gallatin 		device_printf(sc->dev,
45111e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
45121e413cf9SAndrew Gallatin 		goto abort_with_fw;
45131e413cf9SAndrew Gallatin 	}
45141e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
45151e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
45161e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
45171e413cf9SAndrew Gallatin 
45181e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
45191e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
45201e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
45211e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
45221e413cf9SAndrew Gallatin 	} else {
45231e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
45241e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
45251e413cf9SAndrew Gallatin 	}
45261e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
45271e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
45281e413cf9SAndrew Gallatin 		sc->num_slices--;
45291e413cf9SAndrew Gallatin 
45301e413cf9SAndrew Gallatin 	if (mxge_verbose)
45311e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
45321e413cf9SAndrew Gallatin 			      sc->num_slices);
45331e413cf9SAndrew Gallatin 
45341e413cf9SAndrew Gallatin 	return;
45351e413cf9SAndrew Gallatin 
45361e413cf9SAndrew Gallatin abort_with_fw:
45371e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
45381e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
45391e413cf9SAndrew Gallatin }
45401e413cf9SAndrew Gallatin 
45411e413cf9SAndrew Gallatin static int
45421e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
45431e413cf9SAndrew Gallatin {
45441e413cf9SAndrew Gallatin 	size_t bytes;
45451e413cf9SAndrew Gallatin 	int count, err, i, rid;
45461e413cf9SAndrew Gallatin 
45471e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
45481e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
45491e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
45501e413cf9SAndrew Gallatin 
45511e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
45521e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
45531e413cf9SAndrew Gallatin 		return ENXIO;
45541e413cf9SAndrew Gallatin 	}
45551e413cf9SAndrew Gallatin 
45561e413cf9SAndrew Gallatin 	count = sc->num_slices;
45571e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
45581e413cf9SAndrew Gallatin 	if (err != 0) {
45591e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
45601e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
45611e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
45621e413cf9SAndrew Gallatin 	}
45631e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
45641e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
45651e413cf9SAndrew Gallatin 			      count, sc->num_slices);
45661e413cf9SAndrew Gallatin 		device_printf(sc->dev,
45671e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
45681e413cf9SAndrew Gallatin 			      count);
45691e413cf9SAndrew Gallatin 		err = ENOSPC;
45701e413cf9SAndrew Gallatin 		goto abort_with_msix;
45711e413cf9SAndrew Gallatin 	}
45721e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
45731e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
45741e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
45751e413cf9SAndrew Gallatin 		err = ENOMEM;
45761e413cf9SAndrew Gallatin 		goto abort_with_msix;
45771e413cf9SAndrew Gallatin 	}
45781e413cf9SAndrew Gallatin 
45791e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45801e413cf9SAndrew Gallatin 		rid = i + 1;
45811e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
45821e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
45831e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
45841e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
45851e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
45861e413cf9SAndrew Gallatin 				      " for message %d\n", i);
45871e413cf9SAndrew Gallatin 			err = ENXIO;
45881e413cf9SAndrew Gallatin 			goto abort_with_res;
45891e413cf9SAndrew Gallatin 		}
45901e413cf9SAndrew Gallatin 	}
45911e413cf9SAndrew Gallatin 
45921e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
45931e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
45941e413cf9SAndrew Gallatin 
45951e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45961e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
45971e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
459837d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
459937d89b0cSAndrew Gallatin 				     NULL,
460037d89b0cSAndrew Gallatin #endif
460137d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
46021e413cf9SAndrew Gallatin 		if (err != 0) {
46031e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
46041e413cf9SAndrew Gallatin 				      "message %d\n", i);
46051e413cf9SAndrew Gallatin 			goto abort_with_intr;
46061e413cf9SAndrew Gallatin 		}
460721089137SAndrew Gallatin 		bus_describe_intr(sc->dev, sc->msix_irq_res[i],
460821089137SAndrew Gallatin 				  sc->msix_ih[i], "s%d", i);
46091e413cf9SAndrew Gallatin 	}
46101e413cf9SAndrew Gallatin 
46111e413cf9SAndrew Gallatin 	if (mxge_verbose) {
46121e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
46131e413cf9SAndrew Gallatin 			      sc->num_slices);
46141e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
46151e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
46161e413cf9SAndrew Gallatin 		printf("\n");
46171e413cf9SAndrew Gallatin 	}
46181e413cf9SAndrew Gallatin 	return (0);
46191e413cf9SAndrew Gallatin 
46201e413cf9SAndrew Gallatin abort_with_intr:
46211e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46221e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
46231e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
46241e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
46251e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
46261e413cf9SAndrew Gallatin 		}
46271e413cf9SAndrew Gallatin 	}
46281e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
46291e413cf9SAndrew Gallatin 
46301e413cf9SAndrew Gallatin 
46311e413cf9SAndrew Gallatin abort_with_res:
46321e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46331e413cf9SAndrew Gallatin 		rid = i + 1;
46341e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
46351e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
46361e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
46371e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
46381e413cf9SAndrew Gallatin 	}
46391e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
46401e413cf9SAndrew Gallatin 
46411e413cf9SAndrew Gallatin 
46421e413cf9SAndrew Gallatin abort_with_msix:
46431e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
46441e413cf9SAndrew Gallatin 
46451e413cf9SAndrew Gallatin abort_with_msix_table:
46461e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
46471e413cf9SAndrew Gallatin 			     sc->msix_table_res);
46481e413cf9SAndrew Gallatin 
46491e413cf9SAndrew Gallatin 	return err;
46501e413cf9SAndrew Gallatin }
46511e413cf9SAndrew Gallatin 
46521e413cf9SAndrew Gallatin static int
46531e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
46541e413cf9SAndrew Gallatin {
46551e413cf9SAndrew Gallatin 	int count, err, rid;
46561e413cf9SAndrew Gallatin 
46571e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
46581e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
46591e413cf9SAndrew Gallatin 		rid = 1;
46601e413cf9SAndrew Gallatin 	} else {
46611e413cf9SAndrew Gallatin 		rid = 0;
466291ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
46631e413cf9SAndrew Gallatin 	}
46641e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
46651e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
46661e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
46671e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
46681e413cf9SAndrew Gallatin 		return ENXIO;
46691e413cf9SAndrew Gallatin 	}
46701e413cf9SAndrew Gallatin 	if (mxge_verbose)
46711e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
467291ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
46731e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
46741e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
46751e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
467637d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
467737d89b0cSAndrew Gallatin 			     NULL,
467837d89b0cSAndrew Gallatin #endif
467937d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
46801e413cf9SAndrew Gallatin 	if (err != 0) {
46811e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
468291ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
468391ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
46841e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
46851e413cf9SAndrew Gallatin 	}
46861e413cf9SAndrew Gallatin 	return err;
46871e413cf9SAndrew Gallatin }
46881e413cf9SAndrew Gallatin 
46891e413cf9SAndrew Gallatin static void
46901e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
46911e413cf9SAndrew Gallatin {
46921e413cf9SAndrew Gallatin 	int i, rid;
46931e413cf9SAndrew Gallatin 
46941e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46951e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
46961e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
46971e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
46981e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
46991e413cf9SAndrew Gallatin 		}
47001e413cf9SAndrew Gallatin 	}
47011e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
47021e413cf9SAndrew Gallatin 
47031e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
47041e413cf9SAndrew Gallatin 		rid = i + 1;
47051e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
47061e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
47071e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
47081e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
47091e413cf9SAndrew Gallatin 	}
47101e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
47111e413cf9SAndrew Gallatin 
47121e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
47131e413cf9SAndrew Gallatin 			     sc->msix_table_res);
47141e413cf9SAndrew Gallatin 
47151e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
47161e413cf9SAndrew Gallatin 	return;
47171e413cf9SAndrew Gallatin }
47181e413cf9SAndrew Gallatin 
47191e413cf9SAndrew Gallatin static void
47201e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
47211e413cf9SAndrew Gallatin {
47221e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
47231e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
472491ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
472591ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
47261e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
47271e413cf9SAndrew Gallatin }
47281e413cf9SAndrew Gallatin 
47291e413cf9SAndrew Gallatin static void
47301e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
47311e413cf9SAndrew Gallatin {
47321e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
47331e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
47341e413cf9SAndrew Gallatin 	else
47351e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
47361e413cf9SAndrew Gallatin }
47371e413cf9SAndrew Gallatin 
47381e413cf9SAndrew Gallatin static int
47391e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
47401e413cf9SAndrew Gallatin {
47411e413cf9SAndrew Gallatin 	int err;
47421e413cf9SAndrew Gallatin 
47431e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
47441e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
47451e413cf9SAndrew Gallatin 	else
47461e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
47471e413cf9SAndrew Gallatin 
47481e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
47491e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
47501e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
47511e413cf9SAndrew Gallatin 	}
47521e413cf9SAndrew Gallatin 	return err;
47531e413cf9SAndrew Gallatin }
47541e413cf9SAndrew Gallatin 
4755b2fc195eSAndrew Gallatin 
4756b2fc195eSAndrew Gallatin static int
47576d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4758b2fc195eSAndrew Gallatin {
47590a7a780eSAndrew Gallatin 	mxge_cmd_t cmd;
47606d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4761b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
47621e413cf9SAndrew Gallatin 	int err, rid;
4763b2fc195eSAndrew Gallatin 
4764b2fc195eSAndrew Gallatin 	sc->dev = dev;
47656d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4766b2fc195eSAndrew Gallatin 
476772c042dfSAndrew Gallatin 	TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
4768a4a75d67SJohn Baldwin 	sc->tq = taskqueue_create("mxge_taskq", M_WAITOK,
4769a4a75d67SJohn Baldwin 				  taskqueue_thread_enqueue, &sc->tq);
477072c042dfSAndrew Gallatin 	if (sc->tq == NULL) {
477172c042dfSAndrew Gallatin 		err = ENOMEM;
477272c042dfSAndrew Gallatin 		goto abort_with_nothing;
477372c042dfSAndrew Gallatin 	}
477472c042dfSAndrew Gallatin 
477562ce43ccSScott Long 	err = bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
4776b2fc195eSAndrew Gallatin 				 1,			/* alignment */
47771e413cf9SAndrew Gallatin 				 0,			/* boundary */
4778b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4779b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4780b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4781aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
47825e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
47831e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4784b2fc195eSAndrew Gallatin 				 0,			/* flags */
4785b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4786b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4787b2fc195eSAndrew Gallatin 
4788b2fc195eSAndrew Gallatin 	if (err != 0) {
4789b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4790b2fc195eSAndrew Gallatin 			      err);
479172c042dfSAndrew Gallatin 		goto abort_with_tq;
4792b2fc195eSAndrew Gallatin 	}
4793b2fc195eSAndrew Gallatin 
4794b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4795b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4796b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4797b2fc195eSAndrew Gallatin 		err = ENOSPC;
4798b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4799b2fc195eSAndrew Gallatin 	}
48001e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
48011e413cf9SAndrew Gallatin 
4802a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4803a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4804a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4805a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4806a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4807a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4808b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4809b2fc195eSAndrew Gallatin 
4810dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4811d91b1b49SAndrew Gallatin 
4812dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4813b2fc195eSAndrew Gallatin 
4814b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4815b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4816b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4817b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4818b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4819b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4820b2fc195eSAndrew Gallatin 		err = ENXIO;
4821b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4822b2fc195eSAndrew Gallatin 	}
4823b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4824b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4825b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4826b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4827b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4828b2fc195eSAndrew Gallatin 		err = ENXIO;
4829b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4830b2fc195eSAndrew Gallatin 	}
4831b2fc195eSAndrew Gallatin 
4832b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4833b2fc195eSAndrew Gallatin 	   lanai SRAM */
48346d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4835b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4836b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
48376d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4838b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
48396d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
48406d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4841b2fc195eSAndrew Gallatin 	if (err != 0)
4842b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4843b2fc195eSAndrew Gallatin 
4844b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
48456d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4846b2fc195eSAndrew Gallatin 
4847b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
48486d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
48496d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4850b2fc195eSAndrew Gallatin 	if (err != 0)
4851b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4852b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
48536d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4854b2fc195eSAndrew Gallatin 	if (err != 0)
4855b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4856b2fc195eSAndrew Gallatin 
4857a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4858a98d6cd7SAndrew Gallatin 	if (err != 0)
48591e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4860b2fc195eSAndrew Gallatin 
48618fe615baSAndrew Gallatin 	/* select & load the firmware */
48628fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4863b2fc195eSAndrew Gallatin 	if (err != 0)
48641e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
48655e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
48661e413cf9SAndrew Gallatin 
48671e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
48681e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
48691e413cf9SAndrew Gallatin 	if (err != 0)
48701e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
48711e413cf9SAndrew Gallatin 
4872adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4873b2fc195eSAndrew Gallatin 	if (err != 0)
48741e413cf9SAndrew Gallatin 		goto abort_with_slices;
4875b2fc195eSAndrew Gallatin 
4876a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4877a98d6cd7SAndrew Gallatin 	if (err != 0) {
4878a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
48792e084798SAndrew Gallatin 		goto abort_with_slices;
4880a98d6cd7SAndrew Gallatin 	}
4881a98d6cd7SAndrew Gallatin 
48821e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4883a98d6cd7SAndrew Gallatin 	if (err != 0) {
48841e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4885a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4886a98d6cd7SAndrew Gallatin 	}
48871e413cf9SAndrew Gallatin 
4888b245f96cSGleb Smirnoff 	ifp->if_baudrate = IF_Gbps(10);
4889c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
489026dd49c6SAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LINKSTATE | IFCAP_TXCSUM_IPV6 |
489126dd49c6SAndrew Gallatin 		IFCAP_RXCSUM_IPV6;
489226dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
4893eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4894eb6219e3SAndrew Gallatin #endif
489537d89b0cSAndrew Gallatin 
489637d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
489737d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
48980dce6781SAndrew Gallatin 
48990dce6781SAndrew Gallatin 	/* Only FW 1.4.32 and newer can do TSO over vlans */
49000dce6781SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
49010dce6781SAndrew Gallatin 	    sc->fw_ver_tiny >= 32)
49020dce6781SAndrew Gallatin 		ifp->if_capabilities |= IFCAP_VLAN_HWTSO;
490337d89b0cSAndrew Gallatin #endif
4904053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4905053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4906053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4907053e637fSAndrew Gallatin 	else
4908053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4909adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4910053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4911aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
49120a7a780eSAndrew Gallatin 	ifp->if_hwassist |= CSUM_TCP_IPV6 | CSUM_UDP_IPV6;
49130a7a780eSAndrew Gallatin 	/* check to see if f/w supports TSO for IPv6 */
49140a7a780eSAndrew Gallatin 	if (!mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd)) {
49150a7a780eSAndrew Gallatin 		if (CSUM_TCP_IPV6)
49160a7a780eSAndrew Gallatin 			ifp->if_capabilities |= IFCAP_TSO6;
49170a7a780eSAndrew Gallatin 		sc->max_tso6_hlen = min(cmd.data0,
49180a7a780eSAndrew Gallatin 					sizeof (sc->ss[0].scratch));
49190a7a780eSAndrew Gallatin 	}
4920b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4921f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4922f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
49236d87a65dSAndrew Gallatin 	ifp->if_init = mxge_init;
4924b2fc195eSAndrew Gallatin 	ifp->if_softc = sc;
4925b2fc195eSAndrew Gallatin 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
49266d87a65dSAndrew Gallatin 	ifp->if_ioctl = mxge_ioctl;
49276d87a65dSAndrew Gallatin 	ifp->if_start = mxge_start;
4928f3f040d9SGleb Smirnoff 	ifp->if_get_counter = mxge_get_counter;
4929c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4930c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4931c587e59fSAndrew Gallatin 		     mxge_media_status);
4932c406ad2eSAndrew Gallatin 	mxge_media_init(sc);
4933c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
49348c5d766cSAndrew Gallatin 	sc->dying = 0;
4935b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4936f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4937f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4938f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4939b2fc195eSAndrew Gallatin 
49406d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4941c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4942c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4943c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4944c6cb3e3fSAndrew Gallatin #endif
49452e084798SAndrew Gallatin 	taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
49462e084798SAndrew Gallatin 				device_get_nameunit(sc->dev));
49476b484a49SAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4948b2fc195eSAndrew Gallatin 	return 0;
4949b2fc195eSAndrew Gallatin 
4950a98d6cd7SAndrew Gallatin abort_with_rings:
4951a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
49521e413cf9SAndrew Gallatin abort_with_slices:
49531e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4954a98d6cd7SAndrew Gallatin abort_with_dmabench:
4955a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4956b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
49576d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4958b2fc195eSAndrew Gallatin abort_with_cmd_dma:
49596d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4960b2fc195eSAndrew Gallatin abort_with_mem_res:
4961b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4962b2fc195eSAndrew Gallatin abort_with_lock:
4963b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4964a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4965a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4966b2fc195eSAndrew Gallatin 	if_free(ifp);
4967b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4968b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
496972c042dfSAndrew Gallatin abort_with_tq:
497072c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
497172c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
497272c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
497372c042dfSAndrew Gallatin 		sc->tq = NULL;
497472c042dfSAndrew Gallatin 	}
4975b2fc195eSAndrew Gallatin abort_with_nothing:
4976b2fc195eSAndrew Gallatin 	return err;
4977b2fc195eSAndrew Gallatin }
4978b2fc195eSAndrew Gallatin 
4979b2fc195eSAndrew Gallatin static int
49806d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4981b2fc195eSAndrew Gallatin {
49826d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4983b2fc195eSAndrew Gallatin 
498437d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4985c792928fSAndrew Gallatin 		device_printf(sc->dev,
4986c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4987c792928fSAndrew Gallatin 		return EBUSY;
4988c792928fSAndrew Gallatin 	}
4989a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
49908c5d766cSAndrew Gallatin 	sc->dying = 1;
4991b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
4992a393336bSAndrew Gallatin 		mxge_close(sc, 0);
4993a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4994b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
499572c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
499672c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
499772c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
499872c042dfSAndrew Gallatin 		sc->tq = NULL;
499972c042dfSAndrew Gallatin 	}
5000e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
5001dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
5002091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
50031e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
50041e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
5005a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
50061e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
5007a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
50086d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
50096d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
5010b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
5011b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
5012a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
5013a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
5014b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
5015b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
5016b2fc195eSAndrew Gallatin 	return 0;
5017b2fc195eSAndrew Gallatin }
5018b2fc195eSAndrew Gallatin 
5019b2fc195eSAndrew Gallatin static int
50206d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
5021b2fc195eSAndrew Gallatin {
5022b2fc195eSAndrew Gallatin 	return 0;
5023b2fc195eSAndrew Gallatin }
5024b2fc195eSAndrew Gallatin 
5025b2fc195eSAndrew Gallatin /*
5026b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
5027b2fc195eSAndrew Gallatin 
5028b2fc195eSAndrew Gallatin   Local Variables:
5029b2fc195eSAndrew Gallatin   c-file-style:"linux"
5030b2fc195eSAndrew Gallatin   tab-width:8
5031b2fc195eSAndrew Gallatin   End:
5032b2fc195eSAndrew Gallatin */
5033