xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 99b29e34087108004ee60c0ef424ee33d31bf903)
16d87a65dSAndrew Gallatin /******************************************************************************
2718cf2ccSPedro F. Giffuni SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3b2fc195eSAndrew Gallatin 
4cabc512fSAndrew Gallatin Copyright (c) 2006-2013, Myricom Inc.
5b2fc195eSAndrew Gallatin All rights reserved.
6b2fc195eSAndrew Gallatin 
7b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
8b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
9b2fc195eSAndrew Gallatin 
10b2fc195eSAndrew Gallatin  1. Redistributions of source code must retain the above copyright notice,
11b2fc195eSAndrew Gallatin     this list of conditions and the following disclaimer.
12b2fc195eSAndrew Gallatin 
13eb8e82f5SAndrew Gallatin  2. Neither the name of the Myricom Inc, nor the names of its
14b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
15b2fc195eSAndrew Gallatin     this software without specific prior written permission.
16b2fc195eSAndrew Gallatin 
17b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
28b2fc195eSAndrew Gallatin 
29b2fc195eSAndrew Gallatin ***************************************************************************/
30b2fc195eSAndrew Gallatin 
31b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
32b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
33b2fc195eSAndrew Gallatin 
34b2fc195eSAndrew Gallatin #include <sys/param.h>
35b2fc195eSAndrew Gallatin #include <sys/systm.h>
36b2fc195eSAndrew Gallatin #include <sys/linker.h>
37b2fc195eSAndrew Gallatin #include <sys/firmware.h>
38b2fc195eSAndrew Gallatin #include <sys/endian.h>
39b2fc195eSAndrew Gallatin #include <sys/sockio.h>
40b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
41b2fc195eSAndrew Gallatin #include <sys/malloc.h>
42b2fc195eSAndrew Gallatin #include <sys/kdb.h>
43b2fc195eSAndrew Gallatin #include <sys/kernel.h>
444e7f640dSJohn Baldwin #include <sys/lock.h>
45b2fc195eSAndrew Gallatin #include <sys/module.h>
46b2fc195eSAndrew Gallatin #include <sys/socket.h>
47b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
48b2fc195eSAndrew Gallatin #include <sys/sx.h>
4972c042dfSAndrew Gallatin #include <sys/taskqueue.h>
501dbf944aSXin LI #include <contrib/zlib/zlib.h>
511dbf944aSXin LI #include <dev/zlib/zcalloc.h>
52b2fc195eSAndrew Gallatin 
53b2fc195eSAndrew Gallatin #include <net/if.h>
5476039bc8SGleb Smirnoff #include <net/if_var.h>
55b2fc195eSAndrew Gallatin #include <net/if_arp.h>
56b2fc195eSAndrew Gallatin #include <net/ethernet.h>
57b2fc195eSAndrew Gallatin #include <net/if_dl.h>
58b2fc195eSAndrew Gallatin #include <net/if_media.h>
59b2fc195eSAndrew Gallatin 
60b2fc195eSAndrew Gallatin #include <net/bpf.h>
61b2fc195eSAndrew Gallatin 
62b2fc195eSAndrew Gallatin #include <net/if_types.h>
63b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
64b2fc195eSAndrew Gallatin 
65b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
66b2fc195eSAndrew Gallatin #include <netinet/in.h>
67b2fc195eSAndrew Gallatin #include <netinet/ip.h>
680a7a780eSAndrew Gallatin #include <netinet/ip6.h>
69aed8e389SAndrew Gallatin #include <netinet/tcp.h>
7026dd49c6SAndrew Gallatin #include <netinet/tcp_lro.h>
710a7a780eSAndrew Gallatin #include <netinet6/ip6_var.h>
72b2fc195eSAndrew Gallatin 
73b2fc195eSAndrew Gallatin #include <machine/bus.h>
74053e637fSAndrew Gallatin #include <machine/in_cksum.h>
75b2fc195eSAndrew Gallatin #include <machine/resource.h>
76b2fc195eSAndrew Gallatin #include <sys/bus.h>
77b2fc195eSAndrew Gallatin #include <sys/rman.h>
781e413cf9SAndrew Gallatin #include <sys/smp.h>
79b2fc195eSAndrew Gallatin 
80b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
81b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
82e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
83b2fc195eSAndrew Gallatin 
84b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
85b2fc195eSAndrew Gallatin #include <vm/pmap.h>
86b2fc195eSAndrew Gallatin 
87c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
88c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
89c2c14a69SAndrew Gallatin #endif
90c2c14a69SAndrew Gallatin 
916d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
926d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
931e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
946d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
95869c7348SAndrew Gallatin #ifdef IFNET_BUF_RING
96869c7348SAndrew Gallatin #include <sys/buf_ring.h>
97869c7348SAndrew Gallatin #endif
98b2fc195eSAndrew Gallatin 
99eb6219e3SAndrew Gallatin #include "opt_inet.h"
1000a7a780eSAndrew Gallatin #include "opt_inet6.h"
101eb6219e3SAndrew Gallatin 
102b2fc195eSAndrew Gallatin /* tunable params */
1036d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
104d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
1056d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1065e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1076d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1085e7d8541SAndrew Gallatin static int mxge_verbose = 0;
109dce01b9bSAndrew Gallatin static int mxge_ticks;
1101e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1115769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
1121e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
113f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
11465c69066SAndrew Gallatin static int mxge_throttle = 0;
1156d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1166d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1171e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1181e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
119b2fc195eSAndrew Gallatin 
1206d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1216d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1226d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1236d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1246d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
125b2fc195eSAndrew Gallatin 
1266d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
127b2fc195eSAndrew Gallatin {
128b2fc195eSAndrew Gallatin   /* Device interface */
1296d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1306d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1316d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1326d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
13361bfd867SSofian Brabez 
13461bfd867SSofian Brabez   DEVMETHOD_END
135b2fc195eSAndrew Gallatin };
136b2fc195eSAndrew Gallatin 
1376d87a65dSAndrew Gallatin static driver_t mxge_driver =
138b2fc195eSAndrew Gallatin {
1396d87a65dSAndrew Gallatin   "mxge",
1406d87a65dSAndrew Gallatin   mxge_methods,
1416d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
142b2fc195eSAndrew Gallatin };
143b2fc195eSAndrew Gallatin 
144b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
145*99b29e34SJohn Baldwin DRIVER_MODULE(mxge, pci, mxge_driver, 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 
1606d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
161f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
162f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
16301638550SAndrew Gallatin 		rev = pci_get_revid(dev);
16401638550SAndrew Gallatin 		switch (rev) {
16501638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
166b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
16701638550SAndrew Gallatin 			break;
16801638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
16901638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
17001638550SAndrew Gallatin 			break;
17101638550SAndrew Gallatin 		default:
17201638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
17301638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
17401638550SAndrew Gallatin 				      rev);
17501638550SAndrew Gallatin 			break;
17601638550SAndrew Gallatin 		}
177b2fc195eSAndrew Gallatin 		return 0;
178b2fc195eSAndrew Gallatin 	}
179b2fc195eSAndrew Gallatin 	return ENXIO;
180b2fc195eSAndrew Gallatin }
181b2fc195eSAndrew Gallatin 
182b2fc195eSAndrew Gallatin static void
1836d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
184b2fc195eSAndrew Gallatin {
185f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
186b2fc195eSAndrew Gallatin 	vm_offset_t len;
18747c2e987SAndrew Gallatin 	int err;
188b2fc195eSAndrew Gallatin 
1894d69a9d0SAndrew Gallatin 	sc->wc = 1;
190b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
191c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
192c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
19347c2e987SAndrew Gallatin 	if (err != 0) {
194c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
195c2c14a69SAndrew Gallatin 			      err);
1964d69a9d0SAndrew Gallatin 		sc->wc = 0;
197b2fc195eSAndrew Gallatin 	}
198f9ae0280SAndrew Gallatin #endif
199b2fc195eSAndrew Gallatin }
200b2fc195eSAndrew Gallatin 
201b2fc195eSAndrew Gallatin /* callback to get our DMA address */
202b2fc195eSAndrew Gallatin static void
2036d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
204b2fc195eSAndrew Gallatin 			 int error)
205b2fc195eSAndrew Gallatin {
206b2fc195eSAndrew Gallatin 	if (error == 0) {
207b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
208b2fc195eSAndrew Gallatin 	}
209b2fc195eSAndrew Gallatin }
210b2fc195eSAndrew Gallatin 
211b2fc195eSAndrew Gallatin static int
2126d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
213b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
214b2fc195eSAndrew Gallatin {
215b2fc195eSAndrew Gallatin 	int err;
216b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2171e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2181e413cf9SAndrew Gallatin 
2191e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2201e413cf9SAndrew Gallatin 		boundary = 0;
2211e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2221e413cf9SAndrew Gallatin 	} else {
2231e413cf9SAndrew Gallatin 		boundary = 4096;
2241e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2251e413cf9SAndrew Gallatin 	}
226b2fc195eSAndrew Gallatin 
227b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
228b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
229b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2301e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
231b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
232b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
233b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
234b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
235b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2361e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
237b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
238b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
239b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
240b2fc195eSAndrew Gallatin 	if (err != 0) {
241b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
242b2fc195eSAndrew Gallatin 		return err;
243b2fc195eSAndrew Gallatin 	}
244b2fc195eSAndrew Gallatin 
245b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
246b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
247b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
248b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
249b2fc195eSAndrew Gallatin 	if (err != 0) {
250b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
251b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
252b2fc195eSAndrew Gallatin 	}
253b2fc195eSAndrew Gallatin 
254b2fc195eSAndrew Gallatin 	/* load the memory */
255b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2566d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
257b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
258b2fc195eSAndrew Gallatin 	if (err != 0) {
259b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
260b2fc195eSAndrew Gallatin 		goto abort_with_mem;
261b2fc195eSAndrew Gallatin 	}
262b2fc195eSAndrew Gallatin 	return 0;
263b2fc195eSAndrew Gallatin 
264b2fc195eSAndrew Gallatin abort_with_mem:
265b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
266b2fc195eSAndrew Gallatin abort_with_dmat:
267b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
268b2fc195eSAndrew Gallatin 	return err;
269b2fc195eSAndrew Gallatin }
270b2fc195eSAndrew Gallatin 
271b2fc195eSAndrew Gallatin static void
2726d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
273b2fc195eSAndrew Gallatin {
274b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
275b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
276b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
277b2fc195eSAndrew Gallatin }
278b2fc195eSAndrew Gallatin 
279b2fc195eSAndrew Gallatin /*
280b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
281b2fc195eSAndrew Gallatin  * SN=x\0
282b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
283b2fc195eSAndrew Gallatin  * PC=text\0
284b2fc195eSAndrew Gallatin  */
285b2fc195eSAndrew Gallatin 
286b2fc195eSAndrew Gallatin static int
2876d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
288b2fc195eSAndrew Gallatin {
289dedbe836SAndrew Gallatin 	char *ptr;
290a4b233ddSAndrew Gallatin 	int i, found_mac, found_sn2;
291dedbe836SAndrew Gallatin 	char *endptr;
292b2fc195eSAndrew Gallatin 
293b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
294b2fc195eSAndrew Gallatin 	found_mac = 0;
295a4b233ddSAndrew Gallatin 	found_sn2 = 0;
296dedbe836SAndrew Gallatin 	while (*ptr != '\0') {
297dedbe836SAndrew Gallatin 		if (strncmp(ptr, "MAC=", 4) == 0) {
298dedbe836SAndrew Gallatin 			ptr += 4;
299dedbe836SAndrew Gallatin 			for (i = 0;;) {
300dedbe836SAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
301dedbe836SAndrew Gallatin 				if (endptr - ptr != 2)
302b2fc195eSAndrew Gallatin 					goto abort;
303dedbe836SAndrew Gallatin 				ptr = endptr;
304dedbe836SAndrew Gallatin 				if (++i == 6)
305dedbe836SAndrew Gallatin 					break;
306dedbe836SAndrew Gallatin 				if (*ptr++ != ':')
307dedbe836SAndrew Gallatin 					goto abort;
308b2fc195eSAndrew Gallatin 			}
309dedbe836SAndrew Gallatin 			found_mac = 1;
310dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "PC=", 3) == 0) {
3115e7d8541SAndrew Gallatin 			ptr += 3;
312dedbe836SAndrew Gallatin 			strlcpy(sc->product_code_string, ptr,
313dedbe836SAndrew Gallatin 			    sizeof(sc->product_code_string));
314dedbe836SAndrew Gallatin 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
3155e7d8541SAndrew Gallatin 			ptr += 3;
316dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
317dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
318dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
319a4b233ddSAndrew Gallatin 			/* SN2 takes precedence over SN */
320a4b233ddSAndrew Gallatin 			ptr += 4;
321a4b233ddSAndrew Gallatin 			found_sn2 = 1;
322dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
323dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
324b2fc195eSAndrew Gallatin 		}
325dedbe836SAndrew Gallatin 		while (*ptr++ != '\0') {}
326b2fc195eSAndrew Gallatin 	}
327b2fc195eSAndrew Gallatin 
328b2fc195eSAndrew Gallatin 	if (found_mac)
329b2fc195eSAndrew Gallatin 		return 0;
330b2fc195eSAndrew Gallatin 
331b2fc195eSAndrew Gallatin  abort:
332b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
333b2fc195eSAndrew Gallatin 
334b2fc195eSAndrew Gallatin 	return ENXIO;
335b2fc195eSAndrew Gallatin }
336b2fc195eSAndrew Gallatin 
3370d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3388fe615baSAndrew Gallatin static void
3398fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
340b2fc195eSAndrew Gallatin {
341b2fc195eSAndrew Gallatin 	uint32_t val;
3428fe615baSAndrew Gallatin 	unsigned long base, off;
343b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3448fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3458fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
346b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
347b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
348b2fc195eSAndrew Gallatin 
3498fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3508fe615baSAndrew Gallatin 		return;
3518fe615baSAndrew Gallatin 
3528fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3538fe615baSAndrew Gallatin 	if (pdev == NULL) {
3548fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3558fe615baSAndrew Gallatin 		return;
3568fe615baSAndrew Gallatin 	}
3578fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3588fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3598fe615baSAndrew Gallatin 
3608fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3618fe615baSAndrew Gallatin 		return;
3628fe615baSAndrew Gallatin 
3638fe615baSAndrew Gallatin 	base = 0;
3648fe615baSAndrew Gallatin 
3658fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3668fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3678fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3688fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3698fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3708fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3718fe615baSAndrew Gallatin 		if (mcp55 &&
3728fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3738fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3748fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3758fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3768fe615baSAndrew Gallatin 		}
3778fe615baSAndrew Gallatin 	}
3788fe615baSAndrew Gallatin 	if (!base)
3798fe615baSAndrew Gallatin 		return;
3808fe615baSAndrew Gallatin 
381b2fc195eSAndrew Gallatin 	/* XXXX
382b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
383b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
384b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
385b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
386b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
387b2fc195eSAndrew Gallatin 	*/
388b2fc195eSAndrew Gallatin #if 0
389b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
390b2fc195eSAndrew Gallatin 	   config space */
391b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
392b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
393b2fc195eSAndrew Gallatin 		val |= 0x40;
394b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3958fe615baSAndrew Gallatin 		return;
396b2fc195eSAndrew Gallatin 	}
397b2fc195eSAndrew Gallatin #endif
398b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
399b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
400b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
401b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
402b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
403b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
404b2fc195eSAndrew Gallatin 	 */
405b2fc195eSAndrew Gallatin 
406b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
407b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
408b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
409b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
410b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
411b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
412b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
413b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
414b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
415b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
416b2fc195eSAndrew Gallatin 
4178fe615baSAndrew Gallatin 	off =  base
418b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
419b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
420b2fc195eSAndrew Gallatin 						 + 8 * slot);
421b2fc195eSAndrew Gallatin 
422b2fc195eSAndrew Gallatin 	/* map it into the kernel */
423b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
424b2fc195eSAndrew Gallatin 
425b2fc195eSAndrew Gallatin 	if (va == NULL) {
426b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4278fe615baSAndrew Gallatin 		return;
428b2fc195eSAndrew Gallatin 	}
429b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
430b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
431b2fc195eSAndrew Gallatin 
432b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
433b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
434b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
435b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
436b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
437b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
438b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4398fe615baSAndrew Gallatin 		return;
440b2fc195eSAndrew Gallatin 	}
441b2fc195eSAndrew Gallatin 
442b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
443b2fc195eSAndrew Gallatin 	val = *ptr32;
444b2fc195eSAndrew Gallatin 
445b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
446b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
447b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4488fe615baSAndrew Gallatin 		return;
449b2fc195eSAndrew Gallatin 	}
450b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
451b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4525e7d8541SAndrew Gallatin 	if (mxge_verbose)
453b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4545e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4555e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
456b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4578fe615baSAndrew Gallatin 	return;
458b2fc195eSAndrew Gallatin }
459b2fc195eSAndrew Gallatin #else
4608fe615baSAndrew Gallatin static void
461f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
462b2fc195eSAndrew Gallatin {
463b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
464b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4658fe615baSAndrew Gallatin 	return;
466b2fc195eSAndrew Gallatin }
467b2fc195eSAndrew Gallatin #endif
4688fe615baSAndrew Gallatin 
4698fe615baSAndrew Gallatin static int
4708fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4718fe615baSAndrew Gallatin {
4728fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4738fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4748fe615baSAndrew Gallatin 	int status;
4758fe615baSAndrew Gallatin 	uint32_t len;
4768fe615baSAndrew Gallatin 	char *test = " ";
4778fe615baSAndrew Gallatin 
4788fe615baSAndrew Gallatin 	/* Run a small DMA test.
4798fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4808fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4818fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4828fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4838fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4848fe615baSAndrew Gallatin 	 * transfers took to complete.
4858fe615baSAndrew Gallatin 	 */
4868fe615baSAndrew Gallatin 
4871e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4888fe615baSAndrew Gallatin 
4898fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4908fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4918fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4928fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4938fe615baSAndrew Gallatin 	if (status != 0) {
4948fe615baSAndrew Gallatin 		test = "read";
4958fe615baSAndrew Gallatin 		goto abort;
4968fe615baSAndrew Gallatin 	}
4978fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4988fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4998fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5008fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5018fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
5028fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5038fe615baSAndrew Gallatin 	if (status != 0) {
5048fe615baSAndrew Gallatin 		test = "write";
5058fe615baSAndrew Gallatin 		goto abort;
5068fe615baSAndrew Gallatin 	}
5078fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5088fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5098fe615baSAndrew Gallatin 
5108fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5118fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5128fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5138fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5148fe615baSAndrew Gallatin 	if (status != 0) {
5158fe615baSAndrew Gallatin 		test = "read/write";
5168fe615baSAndrew Gallatin 		goto abort;
5178fe615baSAndrew Gallatin 	}
5188fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5198fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5208fe615baSAndrew Gallatin 
5218fe615baSAndrew Gallatin abort:
5228fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5238fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5248fe615baSAndrew Gallatin 			      test, status);
5258fe615baSAndrew Gallatin 
5268fe615baSAndrew Gallatin 	return status;
5278fe615baSAndrew Gallatin }
5288fe615baSAndrew Gallatin 
529b2fc195eSAndrew Gallatin /*
530b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
531b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
532b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
533b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
534b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
535b2fc195eSAndrew Gallatin  *
536b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
537b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
538b2fc195eSAndrew Gallatin  *
539b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
540b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
541b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
542b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5431e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
544b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5451e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
546b2fc195eSAndrew Gallatin  */
547b2fc195eSAndrew Gallatin 
5488fe615baSAndrew Gallatin static int
5498fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5508fe615baSAndrew Gallatin {
5518fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5528fe615baSAndrew Gallatin 	int reg, status;
5538fe615baSAndrew Gallatin 	uint16_t pectl;
5548fe615baSAndrew Gallatin 
5551e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5568fe615baSAndrew Gallatin 	/*
5578fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5588fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5598fe615baSAndrew Gallatin 	 */
5603b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
5618fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5628fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5638fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5648fe615baSAndrew Gallatin 				      pectl);
5651e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5668fe615baSAndrew Gallatin 		}
5678fe615baSAndrew Gallatin 	}
5688fe615baSAndrew Gallatin 
5698fe615baSAndrew Gallatin 	/*
5708fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5718fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5728fe615baSAndrew Gallatin 	 */
5738fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5741e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5758fe615baSAndrew Gallatin 	if (status != 0) {
5768fe615baSAndrew Gallatin 		return status;
5778fe615baSAndrew Gallatin 	}
5788fe615baSAndrew Gallatin 
5798fe615baSAndrew Gallatin 	/*
5808fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5818fe615baSAndrew Gallatin 	 */
5828fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5838fe615baSAndrew Gallatin 
5848fe615baSAndrew Gallatin 	/*
5858fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
586a4b233ddSAndrew Gallatin 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5878fe615baSAndrew Gallatin 	 */
588a4b233ddSAndrew Gallatin 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
589a4b233ddSAndrew Gallatin 		return 0;
5908fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5918fe615baSAndrew Gallatin 	if (status == 0)
5928fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5938fe615baSAndrew Gallatin 
5948fe615baSAndrew Gallatin 	if (status != E2BIG)
5958fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5968fe615baSAndrew Gallatin 	if (status == ENOSYS)
5978fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5988fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5998fe615baSAndrew Gallatin 	return status;
6008fe615baSAndrew Gallatin }
6018fe615baSAndrew Gallatin 
6028fe615baSAndrew Gallatin static int
6036d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
604b2fc195eSAndrew Gallatin {
6058fe615baSAndrew Gallatin 	int aligned = 0;
60665c69066SAndrew Gallatin 	int force_firmware = mxge_force_firmware;
607b2fc195eSAndrew Gallatin 
60865c69066SAndrew Gallatin 	if (sc->throttle)
60965c69066SAndrew Gallatin 		force_firmware = sc->throttle;
610d91b1b49SAndrew Gallatin 
61165c69066SAndrew Gallatin 	if (force_firmware != 0) {
61265c69066SAndrew Gallatin 		if (force_firmware == 1)
613d91b1b49SAndrew Gallatin 			aligned = 1;
614d91b1b49SAndrew Gallatin 		else
615d91b1b49SAndrew Gallatin 			aligned = 0;
616d91b1b49SAndrew Gallatin 		if (mxge_verbose)
617d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
618d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
619d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
620d91b1b49SAndrew Gallatin 		goto abort;
621d91b1b49SAndrew Gallatin 	}
622d91b1b49SAndrew Gallatin 
623d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
624d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
625d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
626d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
627d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
628d91b1b49SAndrew Gallatin 			      sc->link_width);
629d91b1b49SAndrew Gallatin 		aligned = 1;
630d91b1b49SAndrew Gallatin 		goto abort;
631d91b1b49SAndrew Gallatin 	}
632d91b1b49SAndrew Gallatin 
6338fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6348fe615baSAndrew Gallatin 		return 0;
635b2fc195eSAndrew Gallatin 
636b2fc195eSAndrew Gallatin abort:
637b2fc195eSAndrew Gallatin 	if (aligned) {
6386d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6391e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
640b2fc195eSAndrew Gallatin 	} else {
6416d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6421e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
643b2fc195eSAndrew Gallatin 	}
6441e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
645b2fc195eSAndrew Gallatin }
646b2fc195eSAndrew Gallatin 
6474da0d523SAndrew Gallatin static int
6484da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6494da0d523SAndrew Gallatin {
650b824b7d8SAndrew Gallatin 
6514da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6524da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6534da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6544da0d523SAndrew Gallatin 		return EIO;
6554da0d523SAndrew Gallatin 	}
6564da0d523SAndrew Gallatin 
6574da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
658dedbe836SAndrew Gallatin 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6594da0d523SAndrew Gallatin 	if (mxge_verbose)
6604da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6614da0d523SAndrew Gallatin 
662b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
663b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6644da0d523SAndrew Gallatin 
665b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
666b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6674da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6684da0d523SAndrew Gallatin 			      sc->fw_version);
6694da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6704da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6714da0d523SAndrew Gallatin 		return EINVAL;
6724da0d523SAndrew Gallatin 	}
6734da0d523SAndrew Gallatin 	return 0;
6744da0d523SAndrew Gallatin 
6754da0d523SAndrew Gallatin }
676b2fc195eSAndrew Gallatin 
677b2fc195eSAndrew Gallatin static int
6786d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
679b2fc195eSAndrew Gallatin {
680f9ae0280SAndrew Gallatin 	z_stream zs;
681f9ae0280SAndrew Gallatin 	char *inflate_buffer;
68233d54970SLuigi Rizzo 	const struct firmware *fw;
683b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
684b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
685b2fc195eSAndrew Gallatin 	int status;
6864da0d523SAndrew Gallatin 	unsigned int i;
687f9ae0280SAndrew Gallatin 	size_t fw_len;
688b2fc195eSAndrew Gallatin 
689b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
690b2fc195eSAndrew Gallatin 	if (fw == NULL) {
691b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
692b2fc195eSAndrew Gallatin 			      sc->fw_name);
693b2fc195eSAndrew Gallatin 		return ENOENT;
694b2fc195eSAndrew Gallatin 	}
695b2fc195eSAndrew Gallatin 
696f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
697f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
6981dbf944aSXin LI 	zs.zalloc = zcalloc_nowait;
6991dbf944aSXin LI 	zs.zfree = zcfree;
700f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
701f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
702b2fc195eSAndrew Gallatin 		status = EIO;
703b2fc195eSAndrew Gallatin 		goto abort_with_fw;
704b2fc195eSAndrew Gallatin 	}
705f9ae0280SAndrew Gallatin 
706f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
707f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
708f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
709f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
710f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
711f9ae0280SAndrew Gallatin 		goto abort_with_zs;
712f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
713f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
714f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
715f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
716f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
717f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
718f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
719f9ae0280SAndrew Gallatin 		status = EIO;
720f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
721f9ae0280SAndrew Gallatin 	}
722f9ae0280SAndrew Gallatin 
723f9ae0280SAndrew Gallatin 	/* check id */
724f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
725f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
726f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
727f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
728f9ae0280SAndrew Gallatin 		status = EIO;
729f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
730f9ae0280SAndrew Gallatin 	}
731f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
732b2fc195eSAndrew Gallatin 
7334da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7344da0d523SAndrew Gallatin 	if (status != 0)
735f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
736b2fc195eSAndrew Gallatin 
737b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
738f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7394da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
740f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
741f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
74273c7c83fSAndrew Gallatin 		wmb();
743b08a56adSJohn Baldwin 		(void)*sc->sram;
74473c7c83fSAndrew Gallatin 		wmb();
7454da0d523SAndrew Gallatin 	}
746b2fc195eSAndrew Gallatin 
747f9ae0280SAndrew Gallatin 	*limit = fw_len;
748b2fc195eSAndrew Gallatin 	status = 0;
749f9ae0280SAndrew Gallatin abort_with_buffer:
750f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
751f9ae0280SAndrew Gallatin abort_with_zs:
752f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
753b2fc195eSAndrew Gallatin abort_with_fw:
754b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
755b2fc195eSAndrew Gallatin 	return status;
756b2fc195eSAndrew Gallatin }
757b2fc195eSAndrew Gallatin 
758b2fc195eSAndrew Gallatin /*
759b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
760b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
761b2fc195eSAndrew Gallatin  */
762b2fc195eSAndrew Gallatin 
763b2fc195eSAndrew Gallatin static void
7646d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
765b2fc195eSAndrew Gallatin {
766b2fc195eSAndrew Gallatin 	char buf_bytes[72];
767b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
768b2fc195eSAndrew Gallatin 	volatile char *submit;
769b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
770b2fc195eSAndrew Gallatin 	int i;
771b2fc195eSAndrew Gallatin 
772aa54c242SJohn Baldwin 	buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
773b2fc195eSAndrew Gallatin 
774b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
775b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
776b2fc195eSAndrew Gallatin 	*confirm = 0;
77773c7c83fSAndrew Gallatin 	wmb();
778b2fc195eSAndrew Gallatin 
779b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
780b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
781b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
782b2fc195eSAndrew Gallatin 	*/
783b2fc195eSAndrew Gallatin 
7846d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7856d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
786b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
787b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
788b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7896d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7906d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
791b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
792b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
793b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
794b2fc195eSAndrew Gallatin 
7950fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
796b2fc195eSAndrew Gallatin 
7976d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
79873c7c83fSAndrew Gallatin 	wmb();
799b2fc195eSAndrew Gallatin 	DELAY(1000);
80073c7c83fSAndrew Gallatin 	wmb();
801b2fc195eSAndrew Gallatin 	i = 0;
802b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
803b2fc195eSAndrew Gallatin 		DELAY(1000);
804b2fc195eSAndrew Gallatin 		i++;
805b2fc195eSAndrew Gallatin 	}
806b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
807b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
808b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
809b2fc195eSAndrew Gallatin 			      *confirm);
810b2fc195eSAndrew Gallatin 	}
811b2fc195eSAndrew Gallatin 	return;
812b2fc195eSAndrew Gallatin }
813b2fc195eSAndrew Gallatin 
814b2fc195eSAndrew Gallatin static int
8156d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
816b2fc195eSAndrew Gallatin {
817b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
818b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
819b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8200fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
821b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
822e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
823b2fc195eSAndrew Gallatin 
824b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
825aa54c242SJohn Baldwin 	buf = (mcp_cmd_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
826b2fc195eSAndrew Gallatin 
827b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
828b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
829b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
830b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8316d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8326d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
833b2fc195eSAndrew Gallatin 
834b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
835b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
836a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
837b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
83873c7c83fSAndrew Gallatin 	wmb();
8396d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
840b2fc195eSAndrew Gallatin 
8415e7d8541SAndrew Gallatin 	/* wait up to 20ms */
842e0501fd0SAndrew Gallatin 	err = EAGAIN;
8435e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
844b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
845b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
84673c7c83fSAndrew Gallatin 		wmb();
847e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
848e0501fd0SAndrew Gallatin 		case 0:
849b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
850e0501fd0SAndrew Gallatin 			err = 0;
851e0501fd0SAndrew Gallatin 			break;
852e0501fd0SAndrew Gallatin 		case 0xffffffff:
853e0501fd0SAndrew Gallatin 			DELAY(1000);
854e0501fd0SAndrew Gallatin 			break;
855e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
856e0501fd0SAndrew Gallatin 			err = ENOSYS;
857e0501fd0SAndrew Gallatin 			break;
858e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
859e0501fd0SAndrew Gallatin 			err = E2BIG;
860e0501fd0SAndrew Gallatin 			break;
861c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
862c587e59fSAndrew Gallatin 			err = EBUSY;
863c587e59fSAndrew Gallatin 			break;
864c406ad2eSAndrew Gallatin 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
865c406ad2eSAndrew Gallatin 			err = ENXIO;
866c406ad2eSAndrew Gallatin 			break;
867e0501fd0SAndrew Gallatin 		default:
868b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8696d87a65dSAndrew Gallatin 				      "mxge: command %d "
870b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
871b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
872e0501fd0SAndrew Gallatin 			err = ENXIO;
873e0501fd0SAndrew Gallatin 			break;
874b2fc195eSAndrew Gallatin 		}
875e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
876e0501fd0SAndrew Gallatin 			break;
877b2fc195eSAndrew Gallatin 	}
878e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8796d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
880b2fc195eSAndrew Gallatin 			      "result = %d\n",
881b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
882e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
883e0501fd0SAndrew Gallatin 	return err;
884b2fc195eSAndrew Gallatin }
885b2fc195eSAndrew Gallatin 
8864da0d523SAndrew Gallatin static int
8874da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8884da0d523SAndrew Gallatin {
8894da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8904da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8914da0d523SAndrew Gallatin 	size_t hdr_offset;
8924da0d523SAndrew Gallatin 	int status;
8934da0d523SAndrew Gallatin 
8944da0d523SAndrew Gallatin 	/* find running firmware header */
8954da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
8964da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
8974da0d523SAndrew Gallatin 
8984da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8994da0d523SAndrew Gallatin 		device_printf(sc->dev,
9004da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9014da0d523SAndrew Gallatin 			      (int)hdr_offset);
9024da0d523SAndrew Gallatin 		return EIO;
9034da0d523SAndrew Gallatin 	}
9044da0d523SAndrew Gallatin 
9054da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9064da0d523SAndrew Gallatin 	 * validate firmware */
9074da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9084da0d523SAndrew Gallatin 	if (hdr == NULL) {
9094da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9104da0d523SAndrew Gallatin 		return ENOMEM;
9114da0d523SAndrew Gallatin 	}
9124da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9134da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9144da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9154da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9164da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
917b824b7d8SAndrew Gallatin 
918b824b7d8SAndrew Gallatin 	/*
919b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
920b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
921b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
922b824b7d8SAndrew Gallatin 	 */
923b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
924b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
925b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
926b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
927b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
928b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
929b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
930b824b7d8SAndrew Gallatin 	}
931b824b7d8SAndrew Gallatin 
9324da0d523SAndrew Gallatin 	return status;
9334da0d523SAndrew Gallatin }
9344da0d523SAndrew Gallatin 
935b2fc195eSAndrew Gallatin static int
9361e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
937b2fc195eSAndrew Gallatin {
938b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
939b2fc195eSAndrew Gallatin 	volatile char *submit;
940b2fc195eSAndrew Gallatin 	char buf_bytes[72];
941b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
942b2fc195eSAndrew Gallatin 	int status, i;
943b2fc195eSAndrew Gallatin 
944aa54c242SJohn Baldwin 	buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
945b2fc195eSAndrew Gallatin 
946b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9476d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
948b2fc195eSAndrew Gallatin 	if (status) {
9491e413cf9SAndrew Gallatin 		if (!adopt)
9501e413cf9SAndrew Gallatin 			return status;
9514da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9524da0d523SAndrew Gallatin 		   it is new enough */
9534da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9544da0d523SAndrew Gallatin 		if (status) {
9554da0d523SAndrew Gallatin 			device_printf(sc->dev,
9564da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
957b2fc195eSAndrew Gallatin 			return status;
958b2fc195eSAndrew Gallatin 		}
9594da0d523SAndrew Gallatin 		device_printf(sc->dev,
9604da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9611e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9624da0d523SAndrew Gallatin 			device_printf(sc->dev,
9634da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9644da0d523SAndrew Gallatin 				 ".  For optimal\n");
9654da0d523SAndrew Gallatin 			device_printf(sc->dev,
9664da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9674da0d523SAndrew Gallatin 				 "firmware\n");
9684da0d523SAndrew Gallatin 		}
969d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9701e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
971d91b1b49SAndrew Gallatin 		return 0;
9724da0d523SAndrew Gallatin 	}
973b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
974b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
975b2fc195eSAndrew Gallatin 	*confirm = 0;
97673c7c83fSAndrew Gallatin 	wmb();
977b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
978b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
979b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
980b2fc195eSAndrew Gallatin 	*/
981b2fc195eSAndrew Gallatin 
9826d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9836d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
984b2fc195eSAndrew Gallatin 
985b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
986b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
987b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
988b2fc195eSAndrew Gallatin 
989b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
990b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
991b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
992b2fc195eSAndrew Gallatin 	*/
993b2fc195eSAndrew Gallatin 					/* where the code starts*/
9946d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
995b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
996b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
997b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
998b2fc195eSAndrew Gallatin 
9990fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10006d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
100173c7c83fSAndrew Gallatin 	wmb();
1002b2fc195eSAndrew Gallatin 	DELAY(1000);
100373c7c83fSAndrew Gallatin 	wmb();
1004b2fc195eSAndrew Gallatin 	i = 0;
1005b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1006b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1007b2fc195eSAndrew Gallatin 		i++;
1008b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1009b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1010b2fc195eSAndrew Gallatin 	}
1011b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1012b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1013b2fc195eSAndrew Gallatin 			confirm, *confirm);
1014b2fc195eSAndrew Gallatin 
1015b2fc195eSAndrew Gallatin 		return ENXIO;
1016b2fc195eSAndrew Gallatin 	}
1017b2fc195eSAndrew Gallatin 	return 0;
1018b2fc195eSAndrew Gallatin }
1019b2fc195eSAndrew Gallatin 
1020b2fc195eSAndrew Gallatin static int
10216d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1022b2fc195eSAndrew Gallatin {
10236d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1024b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1025b2fc195eSAndrew Gallatin 	int status;
1026b2fc195eSAndrew Gallatin 
1027b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1028b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1029b2fc195eSAndrew Gallatin 
1030b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1031b2fc195eSAndrew Gallatin 
10325e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1033b2fc195eSAndrew Gallatin 	return status;
1034b2fc195eSAndrew Gallatin }
1035b2fc195eSAndrew Gallatin 
1036b2fc195eSAndrew Gallatin static int
10376d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1038b2fc195eSAndrew Gallatin {
10396d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1040b2fc195eSAndrew Gallatin 	int status;
1041b2fc195eSAndrew Gallatin 
1042b2fc195eSAndrew Gallatin 	if (pause)
10435e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1044b2fc195eSAndrew Gallatin 				       &cmd);
1045b2fc195eSAndrew Gallatin 	else
10465e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1047b2fc195eSAndrew Gallatin 				       &cmd);
1048b2fc195eSAndrew Gallatin 
1049b2fc195eSAndrew Gallatin 	if (status) {
1050b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1051b2fc195eSAndrew Gallatin 		return ENXIO;
1052b2fc195eSAndrew Gallatin 	}
1053b2fc195eSAndrew Gallatin 	sc->pause = pause;
1054b2fc195eSAndrew Gallatin 	return 0;
1055b2fc195eSAndrew Gallatin }
1056b2fc195eSAndrew Gallatin 
1057b2fc195eSAndrew Gallatin static void
10586d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1059b2fc195eSAndrew Gallatin {
10606d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1061b2fc195eSAndrew Gallatin 	int status;
1062b2fc195eSAndrew Gallatin 
10631e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10641e413cf9SAndrew Gallatin 		promisc = 1;
10651e413cf9SAndrew Gallatin 
1066b2fc195eSAndrew Gallatin 	if (promisc)
10675e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1068b2fc195eSAndrew Gallatin 				       &cmd);
1069b2fc195eSAndrew Gallatin 	else
10705e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1071b2fc195eSAndrew Gallatin 				       &cmd);
1072b2fc195eSAndrew Gallatin 
1073b2fc195eSAndrew Gallatin 	if (status) {
1074b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1075b2fc195eSAndrew Gallatin 	}
1076b2fc195eSAndrew Gallatin }
1077b2fc195eSAndrew Gallatin 
10782a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx {
10792a0f8518SGleb Smirnoff 	mxge_softc_t *sc;
10802a0f8518SGleb Smirnoff 	int error;
10812a0f8518SGleb Smirnoff };
10822a0f8518SGleb Smirnoff 
10832a0f8518SGleb Smirnoff static u_int
10842a0f8518SGleb Smirnoff mxge_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
10852a0f8518SGleb Smirnoff {
10862a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx *ctx = arg;
10872a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
10882a0f8518SGleb Smirnoff 
10892a0f8518SGleb Smirnoff 	if (ctx->error != 0)
10902a0f8518SGleb Smirnoff 		return (0);
10912a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl), &cmd.data0, 4);
10922a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl) + 4, &cmd.data1, 2);
10932a0f8518SGleb Smirnoff 	cmd.data0 = htonl(cmd.data0);
10942a0f8518SGleb Smirnoff 	cmd.data1 = htonl(cmd.data1);
10952a0f8518SGleb Smirnoff 
10962a0f8518SGleb Smirnoff 	ctx->error = mxge_send_cmd(ctx->sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10972a0f8518SGleb Smirnoff 
10982a0f8518SGleb Smirnoff 	return (1);
10992a0f8518SGleb Smirnoff }
11002a0f8518SGleb Smirnoff 
11010fa7f681SAndrew Gallatin static void
11020fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
11030fa7f681SAndrew Gallatin {
11042a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx ctx;
11050fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
11062a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
11070fa7f681SAndrew Gallatin 	int err;
11080fa7f681SAndrew Gallatin 
11090fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11100fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11110fa7f681SAndrew Gallatin 		return;
11120fa7f681SAndrew Gallatin 
11130fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11140fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11150fa7f681SAndrew Gallatin 	if (err != 0) {
11160fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11170fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11180fa7f681SAndrew Gallatin 		return;
11190fa7f681SAndrew Gallatin 	}
11200fa7f681SAndrew Gallatin 
1121b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1122b824b7d8SAndrew Gallatin 		return;
11230fa7f681SAndrew Gallatin 
11240fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11250fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11260fa7f681SAndrew Gallatin 		return;
11270fa7f681SAndrew Gallatin 
11280fa7f681SAndrew Gallatin 	/* Flush all the filters */
11290fa7f681SAndrew Gallatin 
11300fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11310fa7f681SAndrew Gallatin 	if (err != 0) {
11320fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11330fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11340fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11350fa7f681SAndrew Gallatin 		return;
11360fa7f681SAndrew Gallatin 	}
11370fa7f681SAndrew Gallatin 
11380fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11392a0f8518SGleb Smirnoff 	ctx.sc = sc;
11402a0f8518SGleb Smirnoff 	ctx.error = 0;
11412a0f8518SGleb Smirnoff 	if_foreach_llmaddr(ifp, mxge_add_maddr, &ctx);
11422a0f8518SGleb Smirnoff 	if (ctx.error != 0) {
11432a0f8518SGleb Smirnoff 		device_printf(sc->dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
11442a0f8518SGleb Smirnoff 		    "error status:" "%d\t", ctx.error);
11450fa7f681SAndrew Gallatin 		/* abort, leaving multicast filtering off */
11460fa7f681SAndrew Gallatin 		return;
11470fa7f681SAndrew Gallatin 	}
11482a0f8518SGleb Smirnoff 
11490fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11500fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11510fa7f681SAndrew Gallatin 	if (err != 0) {
11520fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11530fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11540fa7f681SAndrew Gallatin 	}
11550fa7f681SAndrew Gallatin }
11560fa7f681SAndrew Gallatin 
1157b2fc195eSAndrew Gallatin static int
1158053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1159053e637fSAndrew Gallatin {
1160053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1161053e637fSAndrew Gallatin 	int status;
1162053e637fSAndrew Gallatin 
1163c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1164c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1165053e637fSAndrew Gallatin 
1166053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1167053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1168053e637fSAndrew Gallatin 	cmd.data0 = 0;
1169053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1170053e637fSAndrew Gallatin 			       &cmd);
1171053e637fSAndrew Gallatin 	if (status == 0)
1172c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1173053e637fSAndrew Gallatin 
1174053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1175053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1176053e637fSAndrew Gallatin }
1177053e637fSAndrew Gallatin 
1178053e637fSAndrew Gallatin static int
1179adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1180b2fc195eSAndrew Gallatin {
11811e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11821e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11831e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11846d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11851e413cf9SAndrew Gallatin 	int slice, status;
1186b2fc195eSAndrew Gallatin 
1187b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1188b2fc195eSAndrew Gallatin 	   is alive */
1189b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11905e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1191b2fc195eSAndrew Gallatin 	if (status != 0) {
1192b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1193b2fc195eSAndrew Gallatin 		return ENXIO;
1194b2fc195eSAndrew Gallatin 	}
1195b2fc195eSAndrew Gallatin 
1196091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1197091feecdSAndrew Gallatin 
11981e413cf9SAndrew Gallatin 	/* set the intrq size */
11991e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12001e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12011e413cf9SAndrew Gallatin 
12021e413cf9SAndrew Gallatin 	/*
12031e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12041e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12051e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12061e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12071e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12081e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12091e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12101e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12111e413cf9SAndrew Gallatin 	 */
12121e413cf9SAndrew Gallatin 
12131e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12141e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12151e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12161e413cf9SAndrew Gallatin 					   &cmd);
12171e413cf9SAndrew Gallatin 		if (status != 0) {
12181e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12191e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12201e413cf9SAndrew Gallatin 			return status;
12211e413cf9SAndrew Gallatin 		}
12221e413cf9SAndrew Gallatin 		/*
12231e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12241e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12251e413cf9SAndrew Gallatin 		 */
12261e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12271e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1228c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1229c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1230c6cb3e3fSAndrew Gallatin #endif
12311e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12321e413cf9SAndrew Gallatin 					   &cmd);
12331e413cf9SAndrew Gallatin 		if (status != 0) {
12341e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12351e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12361e413cf9SAndrew Gallatin 			return status;
12371e413cf9SAndrew Gallatin 		}
12381e413cf9SAndrew Gallatin 	}
12391e413cf9SAndrew Gallatin 
1240adae7080SAndrew Gallatin 	if (interrupts_setup) {
1241b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12421e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12431e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12441e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12451e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12461e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12471e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12481e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12491e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12501e413cf9SAndrew Gallatin 						&cmd);
12511e413cf9SAndrew Gallatin 		}
1252adae7080SAndrew Gallatin 	}
1253b2fc195eSAndrew Gallatin 
12546d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12555e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12565e7d8541SAndrew Gallatin 
12575e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12585e7d8541SAndrew Gallatin 
12595e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12601e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12615e7d8541SAndrew Gallatin 
12625e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12636d87a65dSAndrew Gallatin 				&cmd);
12645e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1265b2fc195eSAndrew Gallatin 	if (status != 0) {
1266b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1267b2fc195eSAndrew Gallatin 		return status;
1268b2fc195eSAndrew Gallatin 	}
1269b2fc195eSAndrew Gallatin 
12705e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12715e7d8541SAndrew Gallatin 
12725e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12738fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12745e7d8541SAndrew Gallatin 
12751e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12761e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12771e413cf9SAndrew Gallatin 
12781e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1279b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12801e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12811e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12821e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12831e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12841e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1285c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1286c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1287c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
12881e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
12891e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
12901e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
12911e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
12921e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
129326dd49c6SAndrew Gallatin 		ss->lc.lro_bad_csum = 0;
129426dd49c6SAndrew Gallatin 		ss->lc.lro_queued = 0;
129526dd49c6SAndrew Gallatin 		ss->lc.lro_flushed = 0;
12961e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
1297a393336bSAndrew Gallatin 			bzero(ss->fw_stats, sizeof *ss->fw_stats);
12981e413cf9SAndrew Gallatin 		}
12991e413cf9SAndrew Gallatin 	}
1300b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13016d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1302bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13036d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13040fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
130565c69066SAndrew Gallatin 	if (sc->throttle) {
130665c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
130765c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
130865c69066SAndrew Gallatin 				  &cmd)) {
130965c69066SAndrew Gallatin 			device_printf(sc->dev,
131065c69066SAndrew Gallatin 				      "can't enable throttle\n");
131165c69066SAndrew Gallatin 		}
131265c69066SAndrew Gallatin 	}
1313b2fc195eSAndrew Gallatin 	return status;
1314b2fc195eSAndrew Gallatin }
1315b2fc195eSAndrew Gallatin 
1316b2fc195eSAndrew Gallatin static int
131765c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
131865c69066SAndrew Gallatin {
131965c69066SAndrew Gallatin 	mxge_cmd_t cmd;
132065c69066SAndrew Gallatin 	mxge_softc_t *sc;
132165c69066SAndrew Gallatin 	int err;
132265c69066SAndrew Gallatin 	unsigned int throttle;
132365c69066SAndrew Gallatin 
132465c69066SAndrew Gallatin 	sc = arg1;
132565c69066SAndrew Gallatin 	throttle = sc->throttle;
132665c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
132765c69066SAndrew Gallatin 	if (err != 0) {
132865c69066SAndrew Gallatin 		return err;
132965c69066SAndrew Gallatin 	}
133065c69066SAndrew Gallatin 
133165c69066SAndrew Gallatin 	if (throttle == sc->throttle)
133265c69066SAndrew Gallatin 		return 0;
133365c69066SAndrew Gallatin 
133465c69066SAndrew Gallatin 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
133565c69066SAndrew Gallatin 		return EINVAL;
133665c69066SAndrew Gallatin 
133765c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
133865c69066SAndrew Gallatin 	cmd.data0 = throttle;
133965c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
134065c69066SAndrew Gallatin 	if (err == 0)
134165c69066SAndrew Gallatin 		sc->throttle = throttle;
134265c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
134365c69066SAndrew Gallatin 	return err;
134465c69066SAndrew Gallatin }
134565c69066SAndrew Gallatin 
134665c69066SAndrew Gallatin static int
13476d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1348b2fc195eSAndrew Gallatin {
13496d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1350b2fc195eSAndrew Gallatin 	unsigned int intr_coal_delay;
1351b2fc195eSAndrew Gallatin 	int err;
1352b2fc195eSAndrew Gallatin 
1353b2fc195eSAndrew Gallatin 	sc = arg1;
1354b2fc195eSAndrew Gallatin 	intr_coal_delay = sc->intr_coal_delay;
1355b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1356b2fc195eSAndrew Gallatin 	if (err != 0) {
1357b2fc195eSAndrew Gallatin 		return err;
1358b2fc195eSAndrew Gallatin 	}
1359b2fc195eSAndrew Gallatin 	if (intr_coal_delay == sc->intr_coal_delay)
1360b2fc195eSAndrew Gallatin 		return 0;
1361b2fc195eSAndrew Gallatin 
1362b2fc195eSAndrew Gallatin 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1363b2fc195eSAndrew Gallatin 		return EINVAL;
1364b2fc195eSAndrew Gallatin 
1365a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13665e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1367b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13685e7d8541SAndrew Gallatin 
1369a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1370b2fc195eSAndrew Gallatin 	return err;
1371b2fc195eSAndrew Gallatin }
1372b2fc195eSAndrew Gallatin 
1373b2fc195eSAndrew Gallatin static int
13746d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1375b2fc195eSAndrew Gallatin {
13766d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1377b2fc195eSAndrew Gallatin 	unsigned int enabled;
1378b2fc195eSAndrew Gallatin 	int err;
1379b2fc195eSAndrew Gallatin 
1380b2fc195eSAndrew Gallatin 	sc = arg1;
1381b2fc195eSAndrew Gallatin 	enabled = sc->pause;
1382b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
1383b2fc195eSAndrew Gallatin 	if (err != 0) {
1384b2fc195eSAndrew Gallatin 		return err;
1385b2fc195eSAndrew Gallatin 	}
1386b2fc195eSAndrew Gallatin 	if (enabled == sc->pause)
1387b2fc195eSAndrew Gallatin 		return 0;
1388b2fc195eSAndrew Gallatin 
1389a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13906d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1391a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1392b2fc195eSAndrew Gallatin 	return err;
1393b2fc195eSAndrew Gallatin }
1394b2fc195eSAndrew Gallatin 
1395b2fc195eSAndrew Gallatin static int
13966d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1397b2fc195eSAndrew Gallatin {
1398b2fc195eSAndrew Gallatin 	int err;
1399b2fc195eSAndrew Gallatin 
1400b2fc195eSAndrew Gallatin 	if (arg1 == NULL)
1401b2fc195eSAndrew Gallatin 		return EFAULT;
1402b2fc195eSAndrew Gallatin 	arg2 = be32toh(*(int *)arg1);
1403b2fc195eSAndrew Gallatin 	arg1 = NULL;
1404b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, arg1, arg2, req);
1405b2fc195eSAndrew Gallatin 
1406b2fc195eSAndrew Gallatin 	return err;
1407b2fc195eSAndrew Gallatin }
1408b2fc195eSAndrew Gallatin 
1409b2fc195eSAndrew Gallatin static void
14101e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14111e413cf9SAndrew Gallatin {
14121e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14131e413cf9SAndrew Gallatin 	int slice;
14141e413cf9SAndrew Gallatin 
14151e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14161e413cf9SAndrew Gallatin 		return;
14171e413cf9SAndrew Gallatin 
14181e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14191e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14201e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14211e413cf9SAndrew Gallatin 			continue;
14221e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14231e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14241e413cf9SAndrew Gallatin 	}
14251e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14261e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14271e413cf9SAndrew Gallatin }
14281e413cf9SAndrew Gallatin 
14291e413cf9SAndrew Gallatin static void
14306d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1431b2fc195eSAndrew Gallatin {
1432b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1433b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14345e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14351e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14361e413cf9SAndrew Gallatin 	int slice;
14371e413cf9SAndrew Gallatin 	char slice_num[8];
1438b2fc195eSAndrew Gallatin 
1439b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1440b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14411e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1442b2fc195eSAndrew Gallatin 
14435e7d8541SAndrew Gallatin 	/* random information */
14445e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14455e7d8541SAndrew Gallatin 		       "firmware_version",
1446f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->fw_version,
14475e7d8541SAndrew Gallatin 		       0, "firmware version");
14485e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14495e7d8541SAndrew Gallatin 		       "serial_number",
1450f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->serial_number_string,
14515e7d8541SAndrew Gallatin 		       0, "serial number");
14525e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14535e7d8541SAndrew Gallatin 		       "product_code",
1454f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->product_code_string,
14555e7d8541SAndrew Gallatin 		       0, "product_code");
14565e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1457d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1458d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1459d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1460d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14615e7d8541SAndrew Gallatin 		       "tx_boundary",
14621e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14635e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14645e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1465091feecdSAndrew Gallatin 		       "write_combine",
1466091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1467091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1468091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14695e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14705e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14715e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14725e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14735e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14745e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14755e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14765e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14775e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14785e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14795e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
1480a393336bSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1481a393336bSAndrew Gallatin 		       "watchdog_resets",
1482a393336bSAndrew Gallatin 		       CTLFLAG_RD, &sc->watchdog_resets,
1483a393336bSAndrew Gallatin 		       0, "Number of times NIC was reset");
14845e7d8541SAndrew Gallatin 
14855e7d8541SAndrew Gallatin 	/* performance related tunables */
1486b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14877029da5cSPawel Biernacki 	    "intr_coal_delay", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
14887029da5cSPawel Biernacki 	    sc, 0, mxge_change_intr_coal, "I",
14897029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1490b2fc195eSAndrew Gallatin 
1491b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14927029da5cSPawel Biernacki 	    "throttle", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14937029da5cSPawel Biernacki 	    mxge_change_throttle, "I", "transmit throttling");
149465c69066SAndrew Gallatin 
149565c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1496b2fc195eSAndrew Gallatin 	    "flow_control_enabled",
14977029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14987029da5cSPawel Biernacki 	    mxge_change_flow_control, "I",
14997029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1500b2fc195eSAndrew Gallatin 
1501b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15025e7d8541SAndrew Gallatin 		       "deassert_wait",
15035e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15045e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1505b2fc195eSAndrew Gallatin 
1506b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1507b2fc195eSAndrew Gallatin 	   Need to swap it */
1508b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15097029da5cSPawel Biernacki 	    "link_up", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15107029da5cSPawel Biernacki 	    &fw->link_up, 0, mxge_handle_be32, "I", "link up");
1511b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15127029da5cSPawel Biernacki 	    "rdma_tags_available", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15137029da5cSPawel Biernacki 	    &fw->rdma_tags_available, 0, mxge_handle_be32, "I",
15147029da5cSPawel Biernacki 	    "rdma_tags_available");
1515b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15167029da5cSPawel Biernacki 	    "dropped_bad_crc32", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15177029da5cSPawel Biernacki 	    &fw->dropped_bad_crc32, 0, mxge_handle_be32, "I",
15187029da5cSPawel Biernacki 	    "dropped_bad_crc32");
1519adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15207029da5cSPawel Biernacki 	    "dropped_bad_phy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15217029da5cSPawel Biernacki 	    &fw->dropped_bad_phy, 0, mxge_handle_be32, "I", "dropped_bad_phy");
1522b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1523b2fc195eSAndrew Gallatin 	    "dropped_link_error_or_filtered",
15247029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15257029da5cSPawel Biernacki 	    &fw->dropped_link_error_or_filtered, 0, mxge_handle_be32, "I",
15267029da5cSPawel Biernacki 	    "dropped_link_error_or_filtered");
1527b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1528adae7080SAndrew Gallatin 	    "dropped_link_overflow",
15297029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15307029da5cSPawel Biernacki 	    &fw->dropped_link_overflow, 0, mxge_handle_be32, "I",
15317029da5cSPawel Biernacki 	    "dropped_link_overflow");
1532adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15330fa7f681SAndrew Gallatin 	    "dropped_multicast_filtered",
15347029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15357029da5cSPawel Biernacki 	    &fw->dropped_multicast_filtered, 0, mxge_handle_be32, "I",
15367029da5cSPawel Biernacki 	    "dropped_multicast_filtered");
15370fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1538adae7080SAndrew Gallatin 	    "dropped_no_big_buffer",
15397029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15407029da5cSPawel Biernacki 	    &fw->dropped_no_big_buffer, 0, mxge_handle_be32, "I",
15417029da5cSPawel Biernacki 	    "dropped_no_big_buffer");
1542b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1543b2fc195eSAndrew Gallatin 	    "dropped_no_small_buffer",
15447029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15457029da5cSPawel Biernacki 	    &fw->dropped_no_small_buffer, 0, mxge_handle_be32, "I",
15467029da5cSPawel Biernacki 	    "dropped_no_small_buffer");
1547b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1548adae7080SAndrew Gallatin 	    "dropped_overrun",
15497029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15507029da5cSPawel Biernacki 	    &fw->dropped_overrun, 0, mxge_handle_be32, "I",
15517029da5cSPawel Biernacki 	    "dropped_overrun");
1552adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15537029da5cSPawel Biernacki 	    "dropped_pause", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15547029da5cSPawel Biernacki 	    &fw->dropped_pause, 0, mxge_handle_be32, "I", "dropped_pause");
1555adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15567029da5cSPawel Biernacki 	    "dropped_runt", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15577029da5cSPawel Biernacki 	    &fw->dropped_runt, 0, mxge_handle_be32, "I", "dropped_runt");
1558b2fc195eSAndrew Gallatin 
1559a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1560a0394e33SAndrew Gallatin 	    "dropped_unicast_filtered",
15617029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15627029da5cSPawel Biernacki 	    &fw->dropped_unicast_filtered, 0, mxge_handle_be32, "I",
15637029da5cSPawel Biernacki 	    "dropped_unicast_filtered");
1564a0394e33SAndrew Gallatin 
15655e7d8541SAndrew Gallatin 	/* verbose printing? */
1566b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15675e7d8541SAndrew Gallatin 		       "verbose",
15685e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15695e7d8541SAndrew Gallatin 		       0, "verbose printing");
1570b2fc195eSAndrew Gallatin 
15711e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
15721e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15731e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
15741e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15757029da5cSPawel Biernacki 		    "slice", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15761e413cf9SAndrew Gallatin 
15771e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
15781e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
15791e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
15801e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
15811e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
15821e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
15831e413cf9SAndrew Gallatin 		ss->sysctl_tree =
15841e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
15857029da5cSPawel Biernacki 			    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15861e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1587053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15881e413cf9SAndrew Gallatin 			       "rx_small_cnt",
15891e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
15901e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
15911e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15921e413cf9SAndrew Gallatin 			       "rx_big_cnt",
15931e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
15941e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
1595e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
159626dd49c6SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lc.lro_flushed,
1597053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1598053e637fSAndrew Gallatin 
1599e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
160026dd49c6SAndrew Gallatin 			       "lro_bad_csum", CTLFLAG_RD, &ss->lc.lro_bad_csum,
160126dd49c6SAndrew Gallatin 			       0, "number of bad csums preventing LRO");
160226dd49c6SAndrew Gallatin 
1603e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
160426dd49c6SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lc.lro_queued,
16051e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16061e413cf9SAndrew Gallatin 			       "queues");
1607053e637fSAndrew Gallatin 
1608c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16091e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16101e413cf9SAndrew Gallatin 		if (slice > 0)
16111e413cf9SAndrew Gallatin 			continue;
1612c6cb3e3fSAndrew Gallatin #endif
1613c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1614c6cb3e3fSAndrew Gallatin 			       "tx_req",
1615c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1616c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16171e413cf9SAndrew Gallatin 
16181e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16191e413cf9SAndrew Gallatin 			       "tx_done",
16201e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16211e413cf9SAndrew Gallatin 			       0, "tx_done");
16221e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16231e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16241e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16251e413cf9SAndrew Gallatin 			       0, "tx_done");
16261e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16271e413cf9SAndrew Gallatin 			       "tx_stall",
16281e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16291e413cf9SAndrew Gallatin 			       0, "tx_stall");
16301e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16311e413cf9SAndrew Gallatin 			       "tx_wake",
16321e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16331e413cf9SAndrew Gallatin 			       0, "tx_wake");
16341e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16351e413cf9SAndrew Gallatin 			       "tx_defrag",
16361e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16371e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1638c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1639c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1640c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1641c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1642c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1643c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1644c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1645c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1646c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1647c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1648c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1649c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
16501e413cf9SAndrew Gallatin 	}
1651b2fc195eSAndrew Gallatin }
1652b2fc195eSAndrew Gallatin 
1653b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1654b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1655b2fc195eSAndrew Gallatin 
1656b2fc195eSAndrew Gallatin static inline void
16571e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1658b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1659b2fc195eSAndrew Gallatin {
1660b2fc195eSAndrew Gallatin 	int idx, starting_slot;
1661b2fc195eSAndrew Gallatin 	starting_slot = tx->req;
1662b2fc195eSAndrew Gallatin 	while (cnt > 1) {
1663b2fc195eSAndrew Gallatin 		cnt--;
1664b2fc195eSAndrew Gallatin 		idx = (starting_slot + cnt) & tx->mask;
16656d87a65dSAndrew Gallatin 		mxge_pio_copy(&tx->lanai[idx],
1666b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
166773c7c83fSAndrew Gallatin 		wmb();
1668b2fc195eSAndrew Gallatin 	}
1669b2fc195eSAndrew Gallatin }
1670b2fc195eSAndrew Gallatin 
1671b2fc195eSAndrew Gallatin /*
1672b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1673b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1674b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1675b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1676b2fc195eSAndrew Gallatin  */
1677b2fc195eSAndrew Gallatin 
1678b2fc195eSAndrew Gallatin static inline void
16791e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1680b2fc195eSAndrew Gallatin 		  int cnt)
1681b2fc195eSAndrew Gallatin {
1682b2fc195eSAndrew Gallatin 	int idx, i;
1683b2fc195eSAndrew Gallatin 	uint32_t *src_ints;
1684b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1685b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *srcp;
1686b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
16875e7d8541SAndrew Gallatin 	uint8_t last_flags;
1688b2fc195eSAndrew Gallatin 
1689b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1690b2fc195eSAndrew Gallatin 
16915e7d8541SAndrew Gallatin 	last_flags = src->flags;
16925e7d8541SAndrew Gallatin 	src->flags = 0;
169373c7c83fSAndrew Gallatin 	wmb();
1694b2fc195eSAndrew Gallatin 	dst = dstp = &tx->lanai[idx];
1695b2fc195eSAndrew Gallatin 	srcp = src;
1696b2fc195eSAndrew Gallatin 
1697b2fc195eSAndrew Gallatin 	if ((idx + cnt) < tx->mask) {
1698b2fc195eSAndrew Gallatin 		for (i = 0; i < (cnt - 1); i += 2) {
16996d87a65dSAndrew Gallatin 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
170073c7c83fSAndrew Gallatin 			wmb(); /* force write every 32 bytes */
1701b2fc195eSAndrew Gallatin 			srcp += 2;
1702b2fc195eSAndrew Gallatin 			dstp += 2;
1703b2fc195eSAndrew Gallatin 		}
1704b2fc195eSAndrew Gallatin 	} else {
1705b2fc195eSAndrew Gallatin 		/* submit all but the first request, and ensure
1706b2fc195eSAndrew Gallatin 		   that it is submitted below */
17076d87a65dSAndrew Gallatin 		mxge_submit_req_backwards(tx, src, cnt);
1708b2fc195eSAndrew Gallatin 		i = 0;
1709b2fc195eSAndrew Gallatin 	}
1710b2fc195eSAndrew Gallatin 	if (i < cnt) {
1711b2fc195eSAndrew Gallatin 		/* submit the first request */
17126d87a65dSAndrew Gallatin 		mxge_pio_copy(dstp, srcp, sizeof(*src));
171373c7c83fSAndrew Gallatin 		wmb(); /* barrier before setting valid flag */
1714b2fc195eSAndrew Gallatin 	}
1715b2fc195eSAndrew Gallatin 
1716b2fc195eSAndrew Gallatin 	/* re-write the last 32-bits with the valid flags */
17175e7d8541SAndrew Gallatin 	src->flags = last_flags;
1718b2fc195eSAndrew Gallatin 	src_ints = (uint32_t *)src;
1719b2fc195eSAndrew Gallatin 	src_ints+=3;
1720b2fc195eSAndrew Gallatin 	dst_ints = (volatile uint32_t *)dst;
1721b2fc195eSAndrew Gallatin 	dst_ints+=3;
1722b2fc195eSAndrew Gallatin 	*dst_ints =  *src_ints;
1723b2fc195eSAndrew Gallatin 	tx->req += cnt;
172473c7c83fSAndrew Gallatin 	wmb();
1725b2fc195eSAndrew Gallatin }
1726b2fc195eSAndrew Gallatin 
17270a7a780eSAndrew Gallatin static int
17280a7a780eSAndrew Gallatin mxge_parse_tx(struct mxge_slice_state *ss, struct mbuf *m,
17290a7a780eSAndrew Gallatin     struct mxge_pkt_info *pi)
17300a7a780eSAndrew Gallatin {
17310a7a780eSAndrew Gallatin 	struct ether_vlan_header *eh;
17320a7a780eSAndrew Gallatin 	uint16_t etype;
17330a7a780eSAndrew Gallatin 	int tso = m->m_pkthdr.csum_flags & (CSUM_TSO);
17340a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17350a7a780eSAndrew Gallatin 	int nxt;
17360a7a780eSAndrew Gallatin #endif
17370a7a780eSAndrew Gallatin 
17380a7a780eSAndrew Gallatin 	eh = mtod(m, struct ether_vlan_header *);
17390a7a780eSAndrew Gallatin 	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
17400a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_proto);
17410a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
17420a7a780eSAndrew Gallatin 	} else {
17430a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_encap_proto);
17440a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN;
17450a7a780eSAndrew Gallatin 	}
17460a7a780eSAndrew Gallatin 
17470a7a780eSAndrew Gallatin 	switch (etype) {
17480a7a780eSAndrew Gallatin 	case ETHERTYPE_IP:
17490a7a780eSAndrew Gallatin 		/*
17500a7a780eSAndrew Gallatin 		 * ensure ip header is in first mbuf, copy it to a
17510a7a780eSAndrew Gallatin 		 * scratch buffer if not
17520a7a780eSAndrew Gallatin 		 */
17530a7a780eSAndrew Gallatin 		pi->ip = (struct ip *)(m->m_data + pi->ip_off);
17540a7a780eSAndrew Gallatin 		pi->ip6 = NULL;
17550a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip))) {
17560a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip),
17570a7a780eSAndrew Gallatin 			    ss->scratch);
17580a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17590a7a780eSAndrew Gallatin 		}
17600a7a780eSAndrew Gallatin 		pi->ip_hlen = pi->ip->ip_hl << 2;
17610a7a780eSAndrew Gallatin 		if (!tso)
17620a7a780eSAndrew Gallatin 			return 0;
17630a7a780eSAndrew Gallatin 
17640a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17650a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17660a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17670a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17680a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17690a7a780eSAndrew Gallatin 		}
17700a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip + pi->ip_hlen);
17710a7a780eSAndrew Gallatin 		break;
17720a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17730a7a780eSAndrew Gallatin 	case ETHERTYPE_IPV6:
17740a7a780eSAndrew Gallatin 		pi->ip6 = (struct ip6_hdr *)(m->m_data + pi->ip_off);
17750a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip6))) {
17760a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip6),
17770a7a780eSAndrew Gallatin 			    ss->scratch);
17780a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17790a7a780eSAndrew Gallatin 		}
17800a7a780eSAndrew Gallatin 		nxt = 0;
17810a7a780eSAndrew Gallatin 		pi->ip_hlen = ip6_lasthdr(m, pi->ip_off, IPPROTO_IPV6, &nxt);
17820a7a780eSAndrew Gallatin 		pi->ip_hlen -= pi->ip_off;
17830a7a780eSAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
17840a7a780eSAndrew Gallatin 			return EINVAL;
17850a7a780eSAndrew Gallatin 
17860a7a780eSAndrew Gallatin 		if (!tso)
17870a7a780eSAndrew Gallatin 			return 0;
17880a7a780eSAndrew Gallatin 
17890a7a780eSAndrew Gallatin 		if (pi->ip_off + pi->ip_hlen > ss->sc->max_tso6_hlen)
17900a7a780eSAndrew Gallatin 			return EINVAL;
17910a7a780eSAndrew Gallatin 
17920a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17930a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17940a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17950a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17960a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17970a7a780eSAndrew Gallatin 		}
17980a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip6 + pi->ip_hlen);
17990a7a780eSAndrew Gallatin 		break;
18000a7a780eSAndrew Gallatin #endif
18010a7a780eSAndrew Gallatin 	default:
18020a7a780eSAndrew Gallatin 		return EINVAL;
18030a7a780eSAndrew Gallatin 	}
18040a7a780eSAndrew Gallatin 	return 0;
18050a7a780eSAndrew Gallatin }
18060a7a780eSAndrew Gallatin 
180737d89b0cSAndrew Gallatin #if IFCAP_TSO4
180837d89b0cSAndrew Gallatin 
1809b2fc195eSAndrew Gallatin static void
18101e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
18110a7a780eSAndrew Gallatin 	       int busdma_seg_cnt, struct mxge_pkt_info *pi)
1812aed8e389SAndrew Gallatin {
18131e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1814aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1815aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1816aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1817aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1818aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
18190a7a780eSAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss, sum;
1820aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1821aed8e389SAndrew Gallatin 	static int once;
1822aed8e389SAndrew Gallatin 
1823aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1824aed8e389SAndrew Gallatin 
1825aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1826aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1827aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1828aed8e389SAndrew Gallatin 	 */
1829aed8e389SAndrew Gallatin 
18300a7a780eSAndrew Gallatin 	cksum_offset = pi->ip_off + pi->ip_hlen;
18310a7a780eSAndrew Gallatin 	cum_len = -(cksum_offset + (pi->tcp->th_off << 2));
1832aed8e389SAndrew Gallatin 
1833aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
18340a7a780eSAndrew Gallatin 	if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) == 0)) {
18354ed8ca8fSAndrew Gallatin 		/*
18364ed8ca8fSAndrew Gallatin 		 * If packet has full TCP csum, replace it with pseudo hdr
18374ed8ca8fSAndrew Gallatin 		 * sum that the NIC expects, otherwise the NIC will emit
18384ed8ca8fSAndrew Gallatin 		 * packets with bad TCP checksums.
18394ed8ca8fSAndrew Gallatin 		 */
18404ed8ca8fSAndrew Gallatin 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
18410a7a780eSAndrew Gallatin 		if (pi->ip6) {
18420a7a780eSAndrew Gallatin #if (CSUM_TCP_IPV6 != 0) && defined(INET6)
18430a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP_IPV6;
18440a7a780eSAndrew Gallatin 			sum = in6_cksum_pseudo(pi->ip6,
18450a7a780eSAndrew Gallatin 			    m->m_pkthdr.len - cksum_offset,
18460a7a780eSAndrew Gallatin 			    IPPROTO_TCP, 0);
18470a7a780eSAndrew Gallatin #endif
18480a7a780eSAndrew Gallatin 		} else {
1849abc5b96bSAndrew Gallatin #ifdef INET
18500a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP;
18510a7a780eSAndrew Gallatin 			sum = in_pseudo(pi->ip->ip_src.s_addr,
18520a7a780eSAndrew Gallatin 			    pi->ip->ip_dst.s_addr,
18530a7a780eSAndrew Gallatin 			    htons(IPPROTO_TCP + (m->m_pkthdr.len -
18540a7a780eSAndrew Gallatin 				    cksum_offset)));
1855abc5b96bSAndrew Gallatin #endif
18560a7a780eSAndrew Gallatin 		}
18570a7a780eSAndrew Gallatin 		m_copyback(m, offsetof(struct tcphdr, th_sum) +
18580a7a780eSAndrew Gallatin 		    cksum_offset, sizeof(sum), (caddr_t)&sum);
18594ed8ca8fSAndrew Gallatin 	}
1860aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1861aed8e389SAndrew Gallatin 
1862aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1863aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1864aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1865aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1866aed8e389SAndrew Gallatin 
18670a7a780eSAndrew Gallatin 	if (pi->ip6) {
18680a7a780eSAndrew Gallatin 		/*
18690a7a780eSAndrew Gallatin 		 * for IPv6 TSO, the "checksum offset" is re-purposed
18700a7a780eSAndrew Gallatin 		 * to store the TCP header len
18710a7a780eSAndrew Gallatin 		 */
18720a7a780eSAndrew Gallatin 		cksum_offset = (pi->tcp->th_off << 2);
18730a7a780eSAndrew Gallatin 	}
18740a7a780eSAndrew Gallatin 
18751e413cf9SAndrew Gallatin 	tx = &ss->tx;
1876aed8e389SAndrew Gallatin 	req = tx->req_list;
1877aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1878aed8e389SAndrew Gallatin 	cnt = 0;
1879aed8e389SAndrew Gallatin 	rdma_count = 0;
1880aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1881aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1882aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1883aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1884aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1885aed8e389SAndrew Gallatin 	 *
1886aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1887aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1888aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1889aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1890aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1891aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1892aed8e389SAndrew Gallatin 	 *
1893aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1894aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1895aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1896aed8e389SAndrew Gallatin 	 */
1897aed8e389SAndrew Gallatin 
1898aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1899aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1900aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1901aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1902e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1903aed8e389SAndrew Gallatin 
1904aed8e389SAndrew Gallatin 		while (len) {
1905aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1906e39a0a37SAndrew Gallatin 			seglen = len;
1907aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1908aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1909aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1910aed8e389SAndrew Gallatin 				/* payload */
1911aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1912aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1913aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1914aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1915aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1916aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1917aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1918aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1919aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1920aed8e389SAndrew Gallatin 				/* header ends */
1921aed8e389SAndrew Gallatin 				rdma_count = -1;
1922aed8e389SAndrew Gallatin 				cum_len_next = 0;
1923aed8e389SAndrew Gallatin 				seglen = -cum_len;
1924aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1925aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1926aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1927aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1928aed8e389SAndrew Gallatin 			    }
1929aed8e389SAndrew Gallatin 
1930aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1931aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1932aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1933aed8e389SAndrew Gallatin 			req->pad = 0;
1934aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1935aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1936aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1937aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1938aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1939aed8e389SAndrew Gallatin 			low += seglen;
1940aed8e389SAndrew Gallatin 			len -= seglen;
1941aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1942aed8e389SAndrew Gallatin 			flags = flags_next;
1943aed8e389SAndrew Gallatin 			req++;
1944aed8e389SAndrew Gallatin 			cnt++;
1945aed8e389SAndrew Gallatin 			rdma_count++;
19460a7a780eSAndrew Gallatin 			if (cksum_offset != 0 && !pi->ip6) {
1947aed8e389SAndrew Gallatin 				if (__predict_false(cksum_offset > seglen))
1948aed8e389SAndrew Gallatin 					cksum_offset -= seglen;
1949aed8e389SAndrew Gallatin 				else
1950aed8e389SAndrew Gallatin 					cksum_offset = 0;
19510a7a780eSAndrew Gallatin 			}
1952adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1953aed8e389SAndrew Gallatin 				goto drop;
1954aed8e389SAndrew Gallatin 		}
1955aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1956aed8e389SAndrew Gallatin 		seg++;
1957aed8e389SAndrew Gallatin 	}
1958aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1959aed8e389SAndrew Gallatin 
1960aed8e389SAndrew Gallatin 	do {
1961aed8e389SAndrew Gallatin 		req--;
1962aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1963aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1964aed8e389SAndrew Gallatin 
1965aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1966aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1967c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1968c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1969c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1970c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1971c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1972c6cb3e3fSAndrew Gallatin 		tx->activate++;
1973c6cb3e3fSAndrew Gallatin 		wmb();
1974c6cb3e3fSAndrew Gallatin 	}
1975c6cb3e3fSAndrew Gallatin #endif
1976aed8e389SAndrew Gallatin 	return;
1977aed8e389SAndrew Gallatin 
1978aed8e389SAndrew Gallatin drop:
1979e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1980aed8e389SAndrew Gallatin 	m_freem(m);
1981c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1982aed8e389SAndrew Gallatin 	if (!once) {
1983adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1984adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1985adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1986aed8e389SAndrew Gallatin 		once = 1;
1987aed8e389SAndrew Gallatin 	}
1988aed8e389SAndrew Gallatin 	return;
1989aed8e389SAndrew Gallatin 
1990aed8e389SAndrew Gallatin }
1991aed8e389SAndrew Gallatin 
199237d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
199337d89b0cSAndrew Gallatin 
199437d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1995c792928fSAndrew Gallatin /*
1996c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1997c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1998c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1999c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
2000c792928fSAndrew Gallatin  */
2001c792928fSAndrew Gallatin static struct mbuf *
2002c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
2003c792928fSAndrew Gallatin {
2004c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2005c792928fSAndrew Gallatin 
2006c6499eccSGleb Smirnoff 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
2007c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
2008c792928fSAndrew Gallatin 		return NULL;
2009c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
2010c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
2011c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2012c792928fSAndrew Gallatin 			return NULL;
2013c792928fSAndrew Gallatin 	}
2014c792928fSAndrew Gallatin 	/*
2015c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2016c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2017c792928fSAndrew Gallatin 	 */
2018c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2019c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2020c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2021c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2022c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2023c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2024c792928fSAndrew Gallatin 	return m;
2025c792928fSAndrew Gallatin }
202637d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2027c792928fSAndrew Gallatin 
2028aed8e389SAndrew Gallatin static void
20291e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2030b2fc195eSAndrew Gallatin {
20310a7a780eSAndrew Gallatin 	struct mxge_pkt_info pi = {0,0,0,0};
20321e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2033b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2034b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2035b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
20361e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
20370a7a780eSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
2038aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2039aed8e389SAndrew Gallatin 	uint8_t flags, cksum_offset;
2040b2fc195eSAndrew Gallatin 
20411e413cf9SAndrew Gallatin 	sc = ss->sc;
20421e413cf9SAndrew Gallatin 	tx = &ss->tx;
2043b2fc195eSAndrew Gallatin 
204437d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2045c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2046c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2047c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
20480a7a780eSAndrew Gallatin 			goto drop_without_m;
2049c792928fSAndrew Gallatin 	}
205037d89b0cSAndrew Gallatin #endif
20510a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
20520a7a780eSAndrew Gallatin 	    (CSUM_TSO | CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
20530a7a780eSAndrew Gallatin 		if (mxge_parse_tx(ss, m, &pi))
20540a7a780eSAndrew Gallatin 			goto drop;
20550a7a780eSAndrew Gallatin 	}
20560a7a780eSAndrew Gallatin 
2057b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2058b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2059b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2060aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2061b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2062adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2063b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2064b2fc195eSAndrew Gallatin 		   to defrag */
2065b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2066b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2067b2fc195eSAndrew Gallatin 			goto drop;
2068b2fc195eSAndrew Gallatin 		}
20691e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2070b2fc195eSAndrew Gallatin 		m = m_tmp;
2071b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2072b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2073aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2074b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2075b2fc195eSAndrew Gallatin 	}
2076adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2077aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2078aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2079b2fc195eSAndrew Gallatin 		goto drop;
2080b2fc195eSAndrew Gallatin 	}
2081b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2082b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20835e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2084b2fc195eSAndrew Gallatin 
208537d89b0cSAndrew Gallatin #if IFCAP_TSO4
2086aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2087aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20880a7a780eSAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, &pi);
2089aed8e389SAndrew Gallatin 		return;
2090aed8e389SAndrew Gallatin 	}
209137d89b0cSAndrew Gallatin #endif
2092aed8e389SAndrew Gallatin 
2093b2fc195eSAndrew Gallatin 	req = tx->req_list;
2094b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20955e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20965e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2097b2fc195eSAndrew Gallatin 
2098b2fc195eSAndrew Gallatin 	/* checksum offloading? */
20990a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
21000a7a780eSAndrew Gallatin 	    (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
2101aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2102aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
21030a7a780eSAndrew Gallatin 		cksum_offset = pi.ip_off + pi.ip_hlen;
2104b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
21055e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2106b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
21075e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2108aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2109aed8e389SAndrew Gallatin 	} else {
2110aed8e389SAndrew Gallatin 		odd_flag = 0;
2111b2fc195eSAndrew Gallatin 	}
21125e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21135e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2114b2fc195eSAndrew Gallatin 
2115b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2116b2fc195eSAndrew Gallatin 	cum_len = 0;
2117aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21185e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2119b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2120b2fc195eSAndrew Gallatin 		req->addr_low =
21216d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2122b2fc195eSAndrew Gallatin 		req->addr_high =
21236d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2124b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2125b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2126b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2127b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2128b2fc195eSAndrew Gallatin 		else
2129b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21305e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21315e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21325e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2133aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2134b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2135b2fc195eSAndrew Gallatin 		seg++;
2136b2fc195eSAndrew Gallatin 		req++;
2137b2fc195eSAndrew Gallatin 		req->flags = 0;
2138b2fc195eSAndrew Gallatin 	}
2139b2fc195eSAndrew Gallatin 	req--;
2140b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2141b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2142b2fc195eSAndrew Gallatin 		req++;
2143b2fc195eSAndrew Gallatin 		req->addr_low =
21446d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2145b2fc195eSAndrew Gallatin 		req->addr_high =
21466d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2147b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21485e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21495e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21505e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21515e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2152aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2153b2fc195eSAndrew Gallatin 		cnt++;
2154b2fc195eSAndrew Gallatin 	}
21555e7d8541SAndrew Gallatin 
21565e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
21575e7d8541SAndrew Gallatin #if 0
21585e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21595e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21605e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21615e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21625e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21635e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21645e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21655e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21665e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21675e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21685e7d8541SAndrew Gallatin 	}
21695e7d8541SAndrew Gallatin 	printf("--------------\n");
21705e7d8541SAndrew Gallatin #endif
21715e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21726d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2173c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2174c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2175c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2176c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2177c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2178c6cb3e3fSAndrew Gallatin 		tx->activate++;
2179c6cb3e3fSAndrew Gallatin 		wmb();
2180c6cb3e3fSAndrew Gallatin 	}
2181c6cb3e3fSAndrew Gallatin #endif
2182b2fc195eSAndrew Gallatin 	return;
2183b2fc195eSAndrew Gallatin 
2184b2fc195eSAndrew Gallatin drop:
2185b2fc195eSAndrew Gallatin 	m_freem(m);
21860a7a780eSAndrew Gallatin drop_without_m:
2187c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2188b2fc195eSAndrew Gallatin 	return;
2189b2fc195eSAndrew Gallatin }
2190b2fc195eSAndrew Gallatin 
2191c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2192c6cb3e3fSAndrew Gallatin static void
2193c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2194c6cb3e3fSAndrew Gallatin {
2195c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2196c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2197c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2198c6cb3e3fSAndrew Gallatin 	int slice;
2199b2fc195eSAndrew Gallatin 
2200c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2201c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2202c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2203c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2204c6cb3e3fSAndrew Gallatin 			m_freem(m);
2205c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2206c6cb3e3fSAndrew Gallatin 	}
2207c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2208c6cb3e3fSAndrew Gallatin }
22096d914a32SAndrew Gallatin 
2210c6cb3e3fSAndrew Gallatin static inline void
2211c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2212c6cb3e3fSAndrew Gallatin {
2213c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2214c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2215c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2216c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2217c6cb3e3fSAndrew Gallatin 
2218c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2219c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2220c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2221c6cb3e3fSAndrew Gallatin 
2222c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2223c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2224c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2225c6cb3e3fSAndrew Gallatin 			return;
2226c6cb3e3fSAndrew Gallatin 		}
2227c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2228c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2229c6cb3e3fSAndrew Gallatin 
2230c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2231c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2232c6cb3e3fSAndrew Gallatin 	}
2233c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2234c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2235c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2236c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2237c6cb3e3fSAndrew Gallatin 		tx->stall++;
2238c6cb3e3fSAndrew Gallatin 	}
2239c6cb3e3fSAndrew Gallatin }
2240c6cb3e3fSAndrew Gallatin 
2241c6cb3e3fSAndrew Gallatin static int
2242c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2243c6cb3e3fSAndrew Gallatin {
2244c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2245c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2246c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2247c6cb3e3fSAndrew Gallatin 	int err;
2248c6cb3e3fSAndrew Gallatin 
2249c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2250c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2251c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2252c6cb3e3fSAndrew Gallatin 
2253c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2254c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2255c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2256c6cb3e3fSAndrew Gallatin 		return (err);
2257c6cb3e3fSAndrew Gallatin 	}
2258c6cb3e3fSAndrew Gallatin 
2259193cbc4dSMax Laier 	if (!drbr_needs_enqueue(ifp, tx->br) &&
2260c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2261c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2262c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2263c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2264c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2265c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2266c6cb3e3fSAndrew Gallatin 		return (err);
2267c6cb3e3fSAndrew Gallatin 	}
2268c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2269c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2270c6cb3e3fSAndrew Gallatin 	return (0);
2271c6cb3e3fSAndrew Gallatin }
2272c6cb3e3fSAndrew Gallatin 
2273c6cb3e3fSAndrew Gallatin static int
2274c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2275c6cb3e3fSAndrew Gallatin {
2276c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2277c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2278c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2279c6cb3e3fSAndrew Gallatin 	int err = 0;
2280c6cb3e3fSAndrew Gallatin 	int slice;
2281c6cb3e3fSAndrew Gallatin 
2282c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2283c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2284c6cb3e3fSAndrew Gallatin 
2285c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2286c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2287c6cb3e3fSAndrew Gallatin 
2288c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2289c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2290c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2291c6cb3e3fSAndrew Gallatin 	} else {
2292c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2293c6cb3e3fSAndrew Gallatin 	}
2294c6cb3e3fSAndrew Gallatin 
2295c6cb3e3fSAndrew Gallatin 	return (err);
2296c6cb3e3fSAndrew Gallatin }
2297c6cb3e3fSAndrew Gallatin 
2298c6cb3e3fSAndrew Gallatin #else
22996d914a32SAndrew Gallatin 
23006d914a32SAndrew Gallatin static inline void
23011e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2302b2fc195eSAndrew Gallatin {
23031e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2304b2fc195eSAndrew Gallatin 	struct mbuf *m;
2305b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
23061e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2307b2fc195eSAndrew Gallatin 
23081e413cf9SAndrew Gallatin 	sc = ss->sc;
2309b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23101e413cf9SAndrew Gallatin 	tx = &ss->tx;
2311adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
23126d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
23136d914a32SAndrew Gallatin 		if (m == NULL) {
23146d914a32SAndrew Gallatin 			return;
23156d914a32SAndrew Gallatin 		}
2316b2fc195eSAndrew Gallatin 		/* let BPF see it */
2317b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2318b2fc195eSAndrew Gallatin 
2319b2fc195eSAndrew Gallatin 		/* give it to the nic */
23201e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
23216d914a32SAndrew Gallatin 	}
23226d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2323a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2324b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2325adae7080SAndrew Gallatin 		tx->stall++;
2326a82c2581SAndrew Gallatin 	}
2327b2fc195eSAndrew Gallatin }
2328c6cb3e3fSAndrew Gallatin #endif
2329b2fc195eSAndrew Gallatin static void
23306d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2331b2fc195eSAndrew Gallatin {
23326d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
23331e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2334b2fc195eSAndrew Gallatin 
23351e413cf9SAndrew Gallatin 	/* only use the first slice for now */
23361e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
23371e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
23381e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
23391e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2340b2fc195eSAndrew Gallatin }
2341b2fc195eSAndrew Gallatin 
23425e7d8541SAndrew Gallatin /*
23435e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23445e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23455e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23465e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23475e7d8541SAndrew Gallatin  * in a burst
23485e7d8541SAndrew Gallatin  */
23495e7d8541SAndrew Gallatin static inline void
23505e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23515e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
23525e7d8541SAndrew Gallatin {
23535e7d8541SAndrew Gallatin 	uint32_t low;
23545e7d8541SAndrew Gallatin 
23555e7d8541SAndrew Gallatin 	low = src->addr_low;
23565e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2357a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
235873c7c83fSAndrew Gallatin 	wmb();
2359a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
236073c7c83fSAndrew Gallatin 	wmb();
236140385a5fSAndrew Gallatin 	src->addr_low = low;
23625e7d8541SAndrew Gallatin 	dst->addr_low = low;
236373c7c83fSAndrew Gallatin 	wmb();
23645e7d8541SAndrew Gallatin }
23655e7d8541SAndrew Gallatin 
2366b2fc195eSAndrew Gallatin static int
23671e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2368b2fc195eSAndrew Gallatin {
2369b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2370b2fc195eSAndrew Gallatin 	struct mbuf *m;
23711e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2372b2fc195eSAndrew Gallatin 	int cnt, err;
2373b2fc195eSAndrew Gallatin 
2374c6499eccSGleb Smirnoff 	m = m_gethdr(M_NOWAIT, MT_DATA);
2375b2fc195eSAndrew Gallatin 	if (m == NULL) {
2376b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2377b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2378b2fc195eSAndrew Gallatin 		goto done;
2379b2fc195eSAndrew Gallatin 	}
2380b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2381b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2382b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2383b2fc195eSAndrew Gallatin 	if (err != 0) {
2384b2fc195eSAndrew Gallatin 		m_free(m);
2385b2fc195eSAndrew Gallatin 		goto done;
2386b2fc195eSAndrew Gallatin 	}
2387b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2388b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23896d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2390b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23916d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2392b2fc195eSAndrew Gallatin 
2393b2fc195eSAndrew Gallatin done:
2394adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2395adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2396b2fc195eSAndrew Gallatin 	return err;
2397b2fc195eSAndrew Gallatin }
2398b2fc195eSAndrew Gallatin 
2399b2fc195eSAndrew Gallatin static int
24001e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2401b2fc195eSAndrew Gallatin {
2402053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2403b2fc195eSAndrew Gallatin 	struct mbuf *m;
24041e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2405053e637fSAndrew Gallatin 	int cnt, err, i;
2406b2fc195eSAndrew Gallatin 
2407c6499eccSGleb Smirnoff 	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2408b2fc195eSAndrew Gallatin 	if (m == NULL) {
2409b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2410b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2411b2fc195eSAndrew Gallatin 		goto done;
2412b2fc195eSAndrew Gallatin 	}
24134d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2414b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2415053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2416b2fc195eSAndrew Gallatin 	if (err != 0) {
2417b2fc195eSAndrew Gallatin 		m_free(m);
2418b2fc195eSAndrew Gallatin 		goto done;
2419b2fc195eSAndrew Gallatin 	}
2420b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2421b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2422b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2423b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2424b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2425053e637fSAndrew Gallatin 
2426b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2427b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2428053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2429053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2430053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2431053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2432053e637fSAndrew Gallatin        }
2433b0f7b922SAndrew Gallatin #endif
2434b2fc195eSAndrew Gallatin 
2435b2fc195eSAndrew Gallatin done:
2436053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2437b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
24385e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
24395e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2440b2fc195eSAndrew Gallatin 		}
2441053e637fSAndrew Gallatin 		idx++;
2442053e637fSAndrew Gallatin 	}
2443b2fc195eSAndrew Gallatin 	return err;
2444b2fc195eSAndrew Gallatin }
2445b2fc195eSAndrew Gallatin 
244626dd49c6SAndrew Gallatin #ifdef INET6
244726dd49c6SAndrew Gallatin 
244826dd49c6SAndrew Gallatin static uint16_t
244926dd49c6SAndrew Gallatin mxge_csum_generic(uint16_t *raw, int len)
245026dd49c6SAndrew Gallatin {
245126dd49c6SAndrew Gallatin 	uint32_t csum;
245226dd49c6SAndrew Gallatin 
245326dd49c6SAndrew Gallatin 	csum = 0;
245426dd49c6SAndrew Gallatin 	while (len > 0) {
245526dd49c6SAndrew Gallatin 		csum += *raw;
245626dd49c6SAndrew Gallatin 		raw++;
245726dd49c6SAndrew Gallatin 		len -= 2;
245826dd49c6SAndrew Gallatin 	}
245926dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
246026dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
246126dd49c6SAndrew Gallatin 	return (uint16_t)csum;
246226dd49c6SAndrew Gallatin }
246326dd49c6SAndrew Gallatin 
246426dd49c6SAndrew Gallatin static inline uint16_t
246526dd49c6SAndrew Gallatin mxge_rx_csum6(void *p, struct mbuf *m, uint32_t csum)
246626dd49c6SAndrew Gallatin {
246726dd49c6SAndrew Gallatin 	uint32_t partial;
246826dd49c6SAndrew Gallatin 	int nxt, cksum_offset;
246926dd49c6SAndrew Gallatin 	struct ip6_hdr *ip6 = p;
247026dd49c6SAndrew Gallatin 	uint16_t c;
247126dd49c6SAndrew Gallatin 
247226dd49c6SAndrew Gallatin 	nxt = ip6->ip6_nxt;
247326dd49c6SAndrew Gallatin 	cksum_offset = sizeof (*ip6) + ETHER_HDR_LEN;
247426dd49c6SAndrew Gallatin 	if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) {
247526dd49c6SAndrew Gallatin 		cksum_offset = ip6_lasthdr(m, ETHER_HDR_LEN,
247626dd49c6SAndrew Gallatin 					   IPPROTO_IPV6, &nxt);
247726dd49c6SAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
247826dd49c6SAndrew Gallatin 			return (1);
247926dd49c6SAndrew Gallatin 	}
248026dd49c6SAndrew Gallatin 
248126dd49c6SAndrew Gallatin 	/*
248226dd49c6SAndrew Gallatin 	 * IPv6 headers do not contain a checksum, and hence
248326dd49c6SAndrew Gallatin 	 * do not checksum to zero, so they don't "fall out"
248426dd49c6SAndrew Gallatin 	 * of the partial checksum calculation like IPv4
248526dd49c6SAndrew Gallatin 	 * headers do.  We need to fix the partial checksum by
248626dd49c6SAndrew Gallatin 	 * subtracting the checksum of the IPv6 header.
248726dd49c6SAndrew Gallatin 	 */
248826dd49c6SAndrew Gallatin 
248926dd49c6SAndrew Gallatin 	partial = mxge_csum_generic((uint16_t *)ip6, cksum_offset -
249026dd49c6SAndrew Gallatin 				    ETHER_HDR_LEN);
249126dd49c6SAndrew Gallatin 	csum += ~partial;
249226dd49c6SAndrew Gallatin 	csum +=	 (csum < ~partial);
249326dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
249426dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
249526dd49c6SAndrew Gallatin 	c = in6_cksum_pseudo(ip6, m->m_pkthdr.len - cksum_offset, nxt,
249626dd49c6SAndrew Gallatin 			     csum);
249726dd49c6SAndrew Gallatin 	c ^= 0xffff;
249826dd49c6SAndrew Gallatin 	return (c);
249926dd49c6SAndrew Gallatin }
250026dd49c6SAndrew Gallatin #endif /* INET6 */
25019b03b0f3SAndrew Gallatin /*
25029b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
25039b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
25049b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
25059b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2506053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2507053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
25089b03b0f3SAndrew Gallatin  */
25099b03b0f3SAndrew Gallatin 
2510053e637fSAndrew Gallatin static inline uint16_t
2511053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2512053e637fSAndrew Gallatin {
2513053e637fSAndrew Gallatin 	struct ether_header *eh;
251426dd49c6SAndrew Gallatin #ifdef INET
2515053e637fSAndrew Gallatin 	struct ip *ip;
251626dd49c6SAndrew Gallatin #endif
2517abc5b96bSAndrew Gallatin #if defined(INET) || defined(INET6)
2518abc5b96bSAndrew Gallatin 	int cap = m->m_pkthdr.rcvif->if_capenable;
2519abc5b96bSAndrew Gallatin #endif
252026dd49c6SAndrew Gallatin 	uint16_t c, etype;
252126dd49c6SAndrew Gallatin 
2522053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
252326dd49c6SAndrew Gallatin 	etype = ntohs(eh->ether_type);
252426dd49c6SAndrew Gallatin 	switch (etype) {
2525eb6219e3SAndrew Gallatin #ifdef INET
252626dd49c6SAndrew Gallatin 	case ETHERTYPE_IP:
252726dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM) == 0)
252826dd49c6SAndrew Gallatin 			return (1);
252926dd49c6SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
253026dd49c6SAndrew Gallatin 		if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP)
253126dd49c6SAndrew Gallatin 			return (1);
2532053e637fSAndrew Gallatin 		c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
253326dd49c6SAndrew Gallatin 			      htonl(ntohs(csum) + ntohs(ip->ip_len) -
253426dd49c6SAndrew Gallatin 				    (ip->ip_hl << 2) + ip->ip_p));
2535053e637fSAndrew Gallatin 		c ^= 0xffff;
253626dd49c6SAndrew Gallatin 		break;
253726dd49c6SAndrew Gallatin #endif
253826dd49c6SAndrew Gallatin #ifdef INET6
253926dd49c6SAndrew Gallatin 	case ETHERTYPE_IPV6:
254026dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM_IPV6) == 0)
254126dd49c6SAndrew Gallatin 			return (1);
254226dd49c6SAndrew Gallatin 		c = mxge_rx_csum6((eh + 1), m, csum);
254326dd49c6SAndrew Gallatin 		break;
254426dd49c6SAndrew Gallatin #endif
254526dd49c6SAndrew Gallatin 	default:
254626dd49c6SAndrew Gallatin 		c = 1;
254726dd49c6SAndrew Gallatin 	}
2548053e637fSAndrew Gallatin 	return (c);
25495e7d8541SAndrew Gallatin }
2550053e637fSAndrew Gallatin 
2551c792928fSAndrew Gallatin static void
2552c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2553c792928fSAndrew Gallatin {
2554c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2555c792928fSAndrew Gallatin 	uint32_t partial;
2556c792928fSAndrew Gallatin 
2557c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2558c792928fSAndrew Gallatin 
2559c792928fSAndrew Gallatin 	/*
2560c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2561c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2562c792928fSAndrew Gallatin 	 * header.
2563c792928fSAndrew Gallatin 	 */
2564c792928fSAndrew Gallatin 
2565c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2566c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2567c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2568c792928fSAndrew Gallatin 	(*csum) += ~partial;
2569c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2570c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2571c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2572c792928fSAndrew Gallatin 
2573c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2574c792928fSAndrew Gallatin 	   later consumers expect this */
2575c792928fSAndrew Gallatin 	*csum = htons(*csum);
2576c792928fSAndrew Gallatin 
2577c792928fSAndrew Gallatin 	/* save the tag */
257837d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2579c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
258037d89b0cSAndrew Gallatin #else
258137d89b0cSAndrew Gallatin 	{
258237d89b0cSAndrew Gallatin 		struct m_tag *mtag;
258337d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
258437d89b0cSAndrew Gallatin 				   M_NOWAIT);
258537d89b0cSAndrew Gallatin 		if (mtag == NULL)
258637d89b0cSAndrew Gallatin 			return;
258737d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
258837d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
258937d89b0cSAndrew Gallatin 	}
259037d89b0cSAndrew Gallatin 
259137d89b0cSAndrew Gallatin #endif
259237d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2593c792928fSAndrew Gallatin 
2594c792928fSAndrew Gallatin 	/*
2595c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2596c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2597c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2598c792928fSAndrew Gallatin 	 * type field is already in place.
2599c792928fSAndrew Gallatin 	 */
2600c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2601c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2602c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2603c792928fSAndrew Gallatin }
2604c792928fSAndrew Gallatin 
26055e7d8541SAndrew Gallatin static inline void
260626dd49c6SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len,
260726dd49c6SAndrew Gallatin 		 uint32_t csum, int lro)
2608b2fc195eSAndrew Gallatin {
26091e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2610b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2611053e637fSAndrew Gallatin 	struct mbuf *m;
2612c792928fSAndrew Gallatin 	struct ether_header *eh;
26131e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2614053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2615b2fc195eSAndrew Gallatin 	int idx;
2616b2fc195eSAndrew Gallatin 
26171e413cf9SAndrew Gallatin 	sc = ss->sc;
2618b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26191e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2620b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2621053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2622b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2623b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2624b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26251e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2626053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2627f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2628053e637fSAndrew Gallatin 		return;
2629b2fc195eSAndrew Gallatin 	}
2630053e637fSAndrew Gallatin 
2631b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2632b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2633b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2634b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2635b2fc195eSAndrew Gallatin 
2636b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2637b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2638b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2639b2fc195eSAndrew Gallatin 
2640053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2641053e637fSAndrew Gallatin 	 * aligned */
26425e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2643b2fc195eSAndrew Gallatin 
2644053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2645053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
26461e413cf9SAndrew Gallatin 	ss->ipackets++;
2647c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2648c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2649c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2650c792928fSAndrew Gallatin 	}
2651eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2652eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2653eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2654eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2655eacb70baSSepherosa Ziehau 	}
2656b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
265726dd49c6SAndrew Gallatin 	if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
265826dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
265926dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2660053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
266126dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
266226dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
266326dd49c6SAndrew Gallatin 
266426dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
266526dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, 0)))
266626dd49c6SAndrew Gallatin 			return;
266726dd49c6SAndrew Gallatin #endif
2668b2fc195eSAndrew Gallatin 	}
2669053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2670053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2671b2fc195eSAndrew Gallatin }
2672b2fc195eSAndrew Gallatin 
2673b2fc195eSAndrew Gallatin static inline void
267426dd49c6SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len,
267526dd49c6SAndrew Gallatin 		   uint32_t csum, int lro)
2676b2fc195eSAndrew Gallatin {
26771e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2678b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2679c792928fSAndrew Gallatin 	struct ether_header *eh;
2680b2fc195eSAndrew Gallatin 	struct mbuf *m;
26811e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2682b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2683b2fc195eSAndrew Gallatin 	int idx;
2684b2fc195eSAndrew Gallatin 
26851e413cf9SAndrew Gallatin 	sc = ss->sc;
2686b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26871e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2688b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2689b2fc195eSAndrew Gallatin 	rx->cnt++;
2690b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2691b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2692b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26931e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2694b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2695f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2696b2fc195eSAndrew Gallatin 		return;
2697b2fc195eSAndrew Gallatin 	}
2698b2fc195eSAndrew Gallatin 
2699b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2700b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2701b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2702b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2703b2fc195eSAndrew Gallatin 
2704b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2705b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2706b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2707b2fc195eSAndrew Gallatin 
2708b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2709b2fc195eSAndrew Gallatin 	 * aligned */
27105e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2711b2fc195eSAndrew Gallatin 
27129b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
27139b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
27141e413cf9SAndrew Gallatin 	ss->ipackets++;
2715c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2716c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2717c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2718c792928fSAndrew Gallatin 	}
2719eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2720eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2721eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2722eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2723eacb70baSSepherosa Ziehau 	}
2724b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
272526dd49c6SAndrew Gallatin 	if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
272626dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
272726dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2728053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
272926dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
273026dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
273126dd49c6SAndrew Gallatin 
273226dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
273326dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, csum)))
273426dd49c6SAndrew Gallatin 			return;
273526dd49c6SAndrew Gallatin #endif
2736053e637fSAndrew Gallatin 	}
2737b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2738b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2739b2fc195eSAndrew Gallatin }
2740b2fc195eSAndrew Gallatin 
2741b2fc195eSAndrew Gallatin static inline void
27421e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
27435e7d8541SAndrew Gallatin {
27441e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
27455e7d8541SAndrew Gallatin 	int limit = 0;
27465e7d8541SAndrew Gallatin 	uint16_t length;
27475e7d8541SAndrew Gallatin 	uint16_t checksum;
274826dd49c6SAndrew Gallatin 	int lro;
27495e7d8541SAndrew Gallatin 
275026dd49c6SAndrew Gallatin 	lro = ss->sc->ifp->if_capenable & IFCAP_LRO;
27515e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
27525e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
27535e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2754053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2755b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
275626dd49c6SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum, lro);
27575e7d8541SAndrew Gallatin 		else
275826dd49c6SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum, lro);
27595e7d8541SAndrew Gallatin 		rx_done->cnt++;
2760adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
27615e7d8541SAndrew Gallatin 
27625e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2763f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
27645e7d8541SAndrew Gallatin 			break;
2765053e637fSAndrew Gallatin 	}
276626dd49c6SAndrew Gallatin #if defined(INET)  || defined (INET6)
27676dd38b87SSepherosa Ziehau 	tcp_lro_flush_all(&ss->lc);
2768eb6219e3SAndrew Gallatin #endif
27695e7d8541SAndrew Gallatin }
27705e7d8541SAndrew Gallatin 
27715e7d8541SAndrew Gallatin static inline void
27721e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2773b2fc195eSAndrew Gallatin {
277437466424SWarner Losh 	struct ifnet *ifp __unused;
27751e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2776b2fc195eSAndrew Gallatin 	struct mbuf *m;
2777b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2778f616ebc7SAndrew Gallatin 	int idx;
2779c6cb3e3fSAndrew Gallatin 	int *flags;
2780b2fc195eSAndrew Gallatin 
27811e413cf9SAndrew Gallatin 	tx = &ss->tx;
27821e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
27835e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2784b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2785b2fc195eSAndrew Gallatin 		tx->done++;
2786b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2787b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2788b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2789b2fc195eSAndrew Gallatin 		if (m != NULL) {
279071032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
279171032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
279271032832SAndrew Gallatin 				ss->omcasts++;
2793c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2794b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2795b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2796b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2797b2fc195eSAndrew Gallatin 			m_freem(m);
2798b2fc195eSAndrew Gallatin 		}
27995e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
28005e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
28015e7d8541SAndrew Gallatin 			tx->pkt_done++;
28025e7d8541SAndrew Gallatin 		}
2803b2fc195eSAndrew Gallatin 	}
2804b2fc195eSAndrew Gallatin 
2805b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2806b2fc195eSAndrew Gallatin 	   its OK to send packets */
2807c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2808c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2809c6cb3e3fSAndrew Gallatin #else
2810c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2811c6cb3e3fSAndrew Gallatin #endif
28121e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2813c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2814c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2815c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
28161e413cf9SAndrew Gallatin 		ss->tx.wake++;
28171e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2818b2fc195eSAndrew Gallatin 	}
2819c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2820c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2821c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2822c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2823c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2824c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2825c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2826c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2827c6cb3e3fSAndrew Gallatin 			wmb();
2828c6cb3e3fSAndrew Gallatin 		}
2829c6cb3e3fSAndrew Gallatin 	}
2830c6cb3e3fSAndrew Gallatin #endif
2831c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2832c6cb3e3fSAndrew Gallatin 
2833b2fc195eSAndrew Gallatin }
2834b2fc195eSAndrew Gallatin 
283501638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2836c587e59fSAndrew Gallatin {
2837c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2838c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2839c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2840c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
284101638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2842c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2843c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2844c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2845c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2846c587e59fSAndrew Gallatin };
284701638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
284801638550SAndrew Gallatin {
284951bc2092SAndrew Gallatin 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
28504ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
285101638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
285201638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
285356b67858SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
285456b67858SAndrew Gallatin 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
285501638550SAndrew Gallatin };
2856c587e59fSAndrew Gallatin 
2857c587e59fSAndrew Gallatin static void
2858c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type)
2859c587e59fSAndrew Gallatin {
2860c406ad2eSAndrew Gallatin 
2861c406ad2eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type,
2862c406ad2eSAndrew Gallatin 		    0, NULL);
2863c406ad2eSAndrew Gallatin 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
2864c406ad2eSAndrew Gallatin 	sc->current_media = media_type;
2865c406ad2eSAndrew Gallatin 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
2866c587e59fSAndrew Gallatin }
2867c587e59fSAndrew Gallatin 
2868c587e59fSAndrew Gallatin static void
2869c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc)
2870c587e59fSAndrew Gallatin {
2871c587e59fSAndrew Gallatin 	char *ptr;
2872c406ad2eSAndrew Gallatin 	int i;
2873c587e59fSAndrew Gallatin 
2874c406ad2eSAndrew Gallatin 	ifmedia_removeall(&sc->media);
2875c406ad2eSAndrew Gallatin 	mxge_media_set(sc, IFM_AUTO);
2876c587e59fSAndrew Gallatin 
2877c587e59fSAndrew Gallatin 	/*
2878c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2879c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2880c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2881c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2882c587e59fSAndrew Gallatin 	 */
2883c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2884c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2885c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2886c406ad2eSAndrew Gallatin 		return;
2887c587e59fSAndrew Gallatin 	}
2888c587e59fSAndrew Gallatin 
2889c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
2890dc15eac0SEd Schouten 		ptr = strchr(ptr, '-');
2891c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2892c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2893c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2894c587e59fSAndrew Gallatin 			return;
2895c587e59fSAndrew Gallatin 		}
2896c587e59fSAndrew Gallatin 	}
289730882b10SAndrew Gallatin 	if (*ptr == 'C' || *(ptr +1) == 'C') {
289801638550SAndrew Gallatin 		/* -C is CX4 */
2899c406ad2eSAndrew Gallatin 		sc->connector = MXGE_CX4;
2900c406ad2eSAndrew Gallatin 		mxge_media_set(sc, IFM_10G_CX4);
2901c406ad2eSAndrew Gallatin 	} else if (*ptr == 'Q') {
290201638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2903c406ad2eSAndrew Gallatin 		sc->connector = MXGE_QRF;
2904c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2905c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2906c406ad2eSAndrew Gallatin 	} else if (*ptr == 'R') {
2907c406ad2eSAndrew Gallatin 		/* -R is XFP */
2908c406ad2eSAndrew Gallatin 		sc->connector = MXGE_XFP;
2909c406ad2eSAndrew Gallatin 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
2910c406ad2eSAndrew Gallatin 		/* -S or -2S is SFP+ */
2911c406ad2eSAndrew Gallatin 		sc->connector = MXGE_SFP;
2912c406ad2eSAndrew Gallatin 	} else {
2913c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2914c406ad2eSAndrew Gallatin 	}
2915c587e59fSAndrew Gallatin }
2916c587e59fSAndrew Gallatin 
2917c406ad2eSAndrew Gallatin /*
2918c406ad2eSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2919c406ad2eSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2920c406ad2eSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2921c406ad2eSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2922c406ad2eSAndrew Gallatin  * than in the interrupt handler itself.
2923c406ad2eSAndrew Gallatin  */
2924c406ad2eSAndrew Gallatin static void
2925c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2926c406ad2eSAndrew Gallatin {
2927c406ad2eSAndrew Gallatin 	mxge_cmd_t cmd;
2928c406ad2eSAndrew Gallatin 	char *cage_type;
2929c406ad2eSAndrew Gallatin 
2930c406ad2eSAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
2931c406ad2eSAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
2932c406ad2eSAndrew Gallatin 	uint32_t byte;
2933c406ad2eSAndrew Gallatin 
2934c406ad2eSAndrew Gallatin 	sc->need_media_probe = 0;
2935c406ad2eSAndrew Gallatin 
2936c406ad2eSAndrew Gallatin 	if (sc->connector == MXGE_XFP) {
293701638550SAndrew Gallatin 		/* -R is XFP */
293801638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
293901638550SAndrew Gallatin 		mxge_media_type_entries =
294073a1170aSPedro F. Giffuni 			nitems(mxge_xfp_media_types);
294101638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
294201638550SAndrew Gallatin 		cage_type = "XFP";
2943c406ad2eSAndrew Gallatin 	} else 	if (sc->connector == MXGE_SFP) {
294401638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
294501638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
294601638550SAndrew Gallatin 		mxge_media_type_entries =
294773a1170aSPedro F. Giffuni 			nitems(mxge_sfp_media_types);
294801638550SAndrew Gallatin 		cage_type = "SFP+";
294901638550SAndrew Gallatin 		byte = 3;
2950c406ad2eSAndrew Gallatin 	} else {
2951c406ad2eSAndrew Gallatin 		/* nothing to do; media type cannot change */
2952c587e59fSAndrew Gallatin 		return;
2953c587e59fSAndrew Gallatin 	}
2954c587e59fSAndrew Gallatin 
2955c587e59fSAndrew Gallatin 	/*
2956c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2957c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2958c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2959c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2960c587e59fSAndrew Gallatin 	 * a millisecond
2961c587e59fSAndrew Gallatin 	 */
2962c587e59fSAndrew Gallatin 
2963c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
296401638550SAndrew Gallatin 	cmd.data1 = byte;
296501638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
296601638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2967c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2968c587e59fSAndrew Gallatin 	}
296901638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
297001638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2971c587e59fSAndrew Gallatin 	}
2972c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2973c587e59fSAndrew Gallatin 		return;
2974c587e59fSAndrew Gallatin 	}
2975c587e59fSAndrew Gallatin 
2976c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
297701638550SAndrew Gallatin 	cmd.data0 = byte;
297801638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2979c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2980c587e59fSAndrew Gallatin 		DELAY(1000);
298101638550SAndrew Gallatin 		cmd.data0 = byte;
298201638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2983c587e59fSAndrew Gallatin 	}
2984c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
298501638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
298601638550SAndrew Gallatin 			      cage_type, err, ms);
2987c587e59fSAndrew Gallatin 		return;
2988c587e59fSAndrew Gallatin 	}
2989c587e59fSAndrew Gallatin 
2990c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2991c587e59fSAndrew Gallatin 		if (mxge_verbose)
299201638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2993c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2994c406ad2eSAndrew Gallatin 		if (sc->current_media != mxge_media_types[0].flag) {
2995c406ad2eSAndrew Gallatin 			mxge_media_init(sc);
2996c406ad2eSAndrew Gallatin 			mxge_media_set(sc, mxge_media_types[0].flag);
2997c406ad2eSAndrew Gallatin 		}
2998c587e59fSAndrew Gallatin 		return;
2999c587e59fSAndrew Gallatin 	}
300001638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
3001c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
3002c587e59fSAndrew Gallatin 			if (mxge_verbose)
300301638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
300401638550SAndrew Gallatin 					      cage_type,
3005c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
3006c587e59fSAndrew Gallatin 
3007c406ad2eSAndrew Gallatin 			if (sc->current_media != mxge_media_types[i].flag) {
3008c406ad2eSAndrew Gallatin 				mxge_media_init(sc);
3009c406ad2eSAndrew Gallatin 				mxge_media_set(sc, mxge_media_types[i].flag);
3010c406ad2eSAndrew Gallatin 			}
3011c587e59fSAndrew Gallatin 			return;
3012c587e59fSAndrew Gallatin 		}
3013c587e59fSAndrew Gallatin 	}
3014c406ad2eSAndrew Gallatin 	if (mxge_verbose)
3015c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "%s media 0x%x unknown\n",
3016c406ad2eSAndrew Gallatin 			      cage_type, cmd.data0);
3017c587e59fSAndrew Gallatin 
3018c587e59fSAndrew Gallatin 	return;
3019c587e59fSAndrew Gallatin }
3020c587e59fSAndrew Gallatin 
3021b2fc195eSAndrew Gallatin static void
30226d87a65dSAndrew Gallatin mxge_intr(void *arg)
3023b2fc195eSAndrew Gallatin {
30241e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
30251e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
30261e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
30271e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
30281e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
30295e7d8541SAndrew Gallatin 	uint32_t send_done_count;
30305e7d8541SAndrew Gallatin 	uint8_t valid;
3031b2fc195eSAndrew Gallatin 
3032c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
30331e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
30341e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
30351e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
30361e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
30371e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
30381e413cf9SAndrew Gallatin 		return;
30391e413cf9SAndrew Gallatin 	}
3040c6cb3e3fSAndrew Gallatin #endif
30411e413cf9SAndrew Gallatin 
30425e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
30435e7d8541SAndrew Gallatin 	if (!stats->valid) {
30445e7d8541SAndrew Gallatin 		return;
3045b2fc195eSAndrew Gallatin 	}
30465e7d8541SAndrew Gallatin 	valid = stats->valid;
3047b2fc195eSAndrew Gallatin 
304891ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
30495e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
30505e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
30515e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
30525e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
30535e7d8541SAndrew Gallatin 			stats->valid = 0;
3054dc8731d4SAndrew Gallatin 	} else {
3055dc8731d4SAndrew Gallatin 		stats->valid = 0;
3056dc8731d4SAndrew Gallatin 	}
3057dc8731d4SAndrew Gallatin 
3058dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
30595e7d8541SAndrew Gallatin 	do {
30605e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
30615e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
30625e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
30635e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
3064c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
30651e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
30661e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
30675e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
3068b2fc195eSAndrew Gallatin 		}
306991ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
307073c7c83fSAndrew Gallatin 			wmb();
30715e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
3072b2fc195eSAndrew Gallatin 
3073c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
3074c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
30755e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
30765e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
3077b2fc195eSAndrew Gallatin 			if (sc->link_state) {
30785e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
30795e7d8541SAndrew Gallatin 				if (mxge_verbose)
30805e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
3081b2fc195eSAndrew Gallatin 			} else {
30825e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
30835e7d8541SAndrew Gallatin 				if (mxge_verbose)
30845e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
3085b2fc195eSAndrew Gallatin 			}
3086c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
3087b2fc195eSAndrew Gallatin 		}
3088b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
30891e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
3090b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
30911e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
30925e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
30935e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
30945e7d8541SAndrew Gallatin 		}
3095c587e59fSAndrew Gallatin 
3096c587e59fSAndrew Gallatin 		if (stats->link_down) {
30975e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3098c587e59fSAndrew Gallatin 			sc->link_state = 0;
3099c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3100c587e59fSAndrew Gallatin 		}
3101b2fc195eSAndrew Gallatin 	}
3102b2fc195eSAndrew Gallatin 
31035e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
31045e7d8541SAndrew Gallatin 	if (valid & 0x1)
31051e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
31061e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3107b2fc195eSAndrew Gallatin }
3108b2fc195eSAndrew Gallatin 
3109b2fc195eSAndrew Gallatin static void
31106d87a65dSAndrew Gallatin mxge_init(void *arg)
3111b2fc195eSAndrew Gallatin {
31123cae7311SAndrew Gallatin 	mxge_softc_t *sc = arg;
31133cae7311SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
31143cae7311SAndrew Gallatin 
31153cae7311SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
31163cae7311SAndrew Gallatin 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
31173cae7311SAndrew Gallatin 		(void) mxge_open(sc);
31183cae7311SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3119b2fc195eSAndrew Gallatin }
3120b2fc195eSAndrew Gallatin 
3121b2fc195eSAndrew Gallatin static void
31221e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
31231e413cf9SAndrew Gallatin {
31241e413cf9SAndrew Gallatin 	int i;
31251e413cf9SAndrew Gallatin 
312626dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
312726dd49c6SAndrew Gallatin 	tcp_lro_free(&ss->lc);
312826dd49c6SAndrew Gallatin #endif
31291e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
31301e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
31311e413cf9SAndrew Gallatin 			continue;
31321e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
31331e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
31341e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
31351e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
31361e413cf9SAndrew Gallatin 	}
31371e413cf9SAndrew Gallatin 
31381e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
31391e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
31401e413cf9SAndrew Gallatin 			continue;
31411e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
31421e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
31431e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
31441e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
31451e413cf9SAndrew Gallatin 	}
31461e413cf9SAndrew Gallatin 
31471e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
31481e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
31491e413cf9SAndrew Gallatin 		return;
31501e413cf9SAndrew Gallatin 
31511e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
31521e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
31531e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
31541e413cf9SAndrew Gallatin 			continue;
31551e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
31561e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
31571e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
31581e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
31591e413cf9SAndrew Gallatin 	}
31601e413cf9SAndrew Gallatin }
31611e413cf9SAndrew Gallatin 
31621e413cf9SAndrew Gallatin static void
31636d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3164b2fc195eSAndrew Gallatin {
31651e413cf9SAndrew Gallatin 	int slice;
31661e413cf9SAndrew Gallatin 
31671e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31681e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
31691e413cf9SAndrew Gallatin }
31701e413cf9SAndrew Gallatin 
31711e413cf9SAndrew Gallatin static void
31721e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
31731e413cf9SAndrew Gallatin {
3174b2fc195eSAndrew Gallatin 	int i;
3175b2fc195eSAndrew Gallatin 
31761e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
31771e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
31781e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3179b2fc195eSAndrew Gallatin 
31801e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
31811e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
31821e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
31831e413cf9SAndrew Gallatin 
31841e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
31851e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
31861e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
31871e413cf9SAndrew Gallatin 
31881e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
31891e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
31901e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
31911e413cf9SAndrew Gallatin 
31921e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
31931e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
31941e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
31951e413cf9SAndrew Gallatin 
31961e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
31971e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
31981e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
31991e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
32001e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3201b2fc195eSAndrew Gallatin 			}
32021e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
32031e413cf9SAndrew Gallatin 		}
32041e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
32051e413cf9SAndrew Gallatin 	}
32061e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
32071e413cf9SAndrew Gallatin 
32081e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
32091e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
32101e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
32111e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
32121e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
32131e413cf9SAndrew Gallatin 			}
32141e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
32151e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
32161e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
32171e413cf9SAndrew Gallatin 		}
32181e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
32191e413cf9SAndrew Gallatin 	}
32201e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
32211e413cf9SAndrew Gallatin 
32221e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
32231e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
32241e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
32251e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
32261e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
32271e413cf9SAndrew Gallatin 			}
32281e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
32291e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
32301e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
32311e413cf9SAndrew Gallatin 		}
32321e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
32331e413cf9SAndrew Gallatin 	}
32341e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3235b2fc195eSAndrew Gallatin }
3236b2fc195eSAndrew Gallatin 
3237b2fc195eSAndrew Gallatin static void
32386d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3239b2fc195eSAndrew Gallatin {
32401e413cf9SAndrew Gallatin 	int slice;
3241b2fc195eSAndrew Gallatin 
32421e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
32431e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3244c2657176SAndrew Gallatin }
3245b2fc195eSAndrew Gallatin 
3246b2fc195eSAndrew Gallatin static int
32471e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
32481e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3249b2fc195eSAndrew Gallatin {
32501e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
32511e413cf9SAndrew Gallatin 	size_t bytes;
32521e413cf9SAndrew Gallatin 	int err, i;
3253b2fc195eSAndrew Gallatin 
32541e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3255adae7080SAndrew Gallatin 
32561e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
32571e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3258aed8e389SAndrew Gallatin 
3259b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
32601e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
32611e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3262b2fc195eSAndrew Gallatin 
32631e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
32641e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3265b2fc195eSAndrew Gallatin 
32661e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
32671e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
32681e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3269b2fc195eSAndrew Gallatin 
32701e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
32711e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3272b2fc195eSAndrew Gallatin 
32731e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3274b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3275b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3276b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3277b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3278b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3279b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3280b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3281b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3282b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3283b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3284b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32851e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3286b2fc195eSAndrew Gallatin 	if (err != 0) {
3287b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3288b2fc195eSAndrew Gallatin 			      err);
3289c2ede4b3SMartin Blapp 		return err;
3290b2fc195eSAndrew Gallatin 	}
3291b2fc195eSAndrew Gallatin 
3292b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3293b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3294b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3295b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3296b0f7b922SAndrew Gallatin #else
3297b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3298b0f7b922SAndrew Gallatin #endif
3299b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3300b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3301b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3302053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3303b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3304053e637fSAndrew Gallatin 				 3,			/* num segs */
3305b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3306b0f7b922SAndrew Gallatin #else
3307b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3308b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3309b0f7b922SAndrew Gallatin #endif
3310b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3311b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
33121e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3313b2fc195eSAndrew Gallatin 	if (err != 0) {
3314b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3315b2fc195eSAndrew Gallatin 			      err);
3316c2ede4b3SMartin Blapp 		return err;
3317b2fc195eSAndrew Gallatin 	}
33181e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
33191e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
33201e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3321b2fc195eSAndrew Gallatin 		if (err != 0) {
3322b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3323b2fc195eSAndrew Gallatin 				      err);
3324c2ede4b3SMartin Blapp 			return err;
3325b2fc195eSAndrew Gallatin 		}
3326b2fc195eSAndrew Gallatin 	}
33271e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
33281e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3329b2fc195eSAndrew Gallatin 	if (err != 0) {
3330b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3331b2fc195eSAndrew Gallatin 			      err);
3332c2ede4b3SMartin Blapp 		return err;
3333b2fc195eSAndrew Gallatin 	}
3334b2fc195eSAndrew Gallatin 
33351e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
33361e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
33371e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3338b2fc195eSAndrew Gallatin 		if (err != 0) {
3339b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3340b2fc195eSAndrew Gallatin 				      err);
3341c2ede4b3SMartin Blapp 			return err;
3342b2fc195eSAndrew Gallatin 		}
3343b2fc195eSAndrew Gallatin 	}
33441e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
33451e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3346b2fc195eSAndrew Gallatin 	if (err != 0) {
3347b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3348b2fc195eSAndrew Gallatin 			      err);
3349c2ede4b3SMartin Blapp 		return err;
33501e413cf9SAndrew Gallatin 	}
33511e413cf9SAndrew Gallatin 
3352b78540b1SGabor Kovesdan 	/* now allocate TX resources */
33531e413cf9SAndrew Gallatin 
3354c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
33551e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
33561e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
33571e413cf9SAndrew Gallatin 		return 0;
3358c6cb3e3fSAndrew Gallatin #endif
33591e413cf9SAndrew Gallatin 
33601e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
33611e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
33621e413cf9SAndrew Gallatin 
33631e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
33641e413cf9SAndrew Gallatin 	bytes = 8 +
33651e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
33661e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
33671e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
33681e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
3369aa54c242SJohn Baldwin 		((uintptr_t)(ss->tx.req_bytes + 7) & ~7UL);
33701e413cf9SAndrew Gallatin 
33711e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
33721e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
33731e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
33741e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
33751e413cf9SAndrew Gallatin 
33761e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
33771e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
33781e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
33791e413cf9SAndrew Gallatin 
33801e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
33811e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
33821e413cf9SAndrew Gallatin 				 1,			/* alignment */
33831e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
33841e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
33851e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
33861e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
33871e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
33881e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
33891e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
33901e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
33911e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
33921e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
33931e413cf9SAndrew Gallatin 
33941e413cf9SAndrew Gallatin 	if (err != 0) {
33951e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
33961e413cf9SAndrew Gallatin 			      err);
3397c2ede4b3SMartin Blapp 		return err;
33981e413cf9SAndrew Gallatin 	}
33991e413cf9SAndrew Gallatin 
34001e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
34011e413cf9SAndrew Gallatin 	   in the ring */
34021e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
34031e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
34041e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
34051e413cf9SAndrew Gallatin 		if (err != 0) {
34061e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
34071e413cf9SAndrew Gallatin 				      err);
3408c2ede4b3SMartin Blapp 			return err;
34091e413cf9SAndrew Gallatin 		}
3410b2fc195eSAndrew Gallatin 	}
3411b2fc195eSAndrew Gallatin 	return 0;
3412b2fc195eSAndrew Gallatin 
3413b2fc195eSAndrew Gallatin }
3414b2fc195eSAndrew Gallatin 
34151e413cf9SAndrew Gallatin static int
34161e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
34171e413cf9SAndrew Gallatin {
34181e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
34191e413cf9SAndrew Gallatin 	int tx_ring_size;
34201e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
34211e413cf9SAndrew Gallatin 	int err, slice;
34221e413cf9SAndrew Gallatin 
34231e413cf9SAndrew Gallatin 	/* get ring sizes */
34241e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
34251e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
34261e413cf9SAndrew Gallatin 	if (err != 0) {
34271e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
34281e413cf9SAndrew Gallatin 		goto abort;
34291e413cf9SAndrew Gallatin 	}
34301e413cf9SAndrew Gallatin 
34311e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
34321e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
34331e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
34341e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
34351e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
34361e413cf9SAndrew Gallatin 
34371e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
34381e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
34391e413cf9SAndrew Gallatin 					     rx_ring_entries,
34401e413cf9SAndrew Gallatin 					     tx_ring_entries);
34411e413cf9SAndrew Gallatin 		if (err != 0)
34421e413cf9SAndrew Gallatin 			goto abort;
34431e413cf9SAndrew Gallatin 	}
34441e413cf9SAndrew Gallatin 	return 0;
34451e413cf9SAndrew Gallatin 
34461e413cf9SAndrew Gallatin abort:
34471e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
34481e413cf9SAndrew Gallatin 	return err;
34491e413cf9SAndrew Gallatin 
34501e413cf9SAndrew Gallatin }
34511e413cf9SAndrew Gallatin 
3452053e637fSAndrew Gallatin static void
3453053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3454053e637fSAndrew Gallatin {
3455c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3456053e637fSAndrew Gallatin 
3457053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3458053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3459053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3460053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3461053e637fSAndrew Gallatin 		*nbufs = 1;
3462053e637fSAndrew Gallatin 		return;
3463053e637fSAndrew Gallatin 	}
3464053e637fSAndrew Gallatin 
3465053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3466053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3467053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3468053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3469053e637fSAndrew Gallatin 		*nbufs = 1;
3470053e637fSAndrew Gallatin 		return;
3471053e637fSAndrew Gallatin 	}
3472b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3473053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3474053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3475053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3476053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3477053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3478053e637fSAndrew Gallatin 	if (*nbufs == 3)
3479053e637fSAndrew Gallatin 		*nbufs = 4;
3480b0f7b922SAndrew Gallatin #else
3481b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3482b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3483b0f7b922SAndrew Gallatin 	*nbufs = 1;
3484b0f7b922SAndrew Gallatin #endif
3485053e637fSAndrew Gallatin }
3486053e637fSAndrew Gallatin 
3487b2fc195eSAndrew Gallatin static int
34881e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3489b2fc195eSAndrew Gallatin {
34901e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
34916d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3492b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
34931e413cf9SAndrew Gallatin 	int err, i, slice;
3494b2fc195eSAndrew Gallatin 
34951e413cf9SAndrew Gallatin 	sc = ss->sc;
34961e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
34971e413cf9SAndrew Gallatin 
349826dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
349926dd49c6SAndrew Gallatin 	(void)tcp_lro_init(&ss->lc);
350026dd49c6SAndrew Gallatin #endif
350126dd49c6SAndrew Gallatin 	ss->lc.ifp = sc->ifp;
3502053e637fSAndrew Gallatin 
35031e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
35041e413cf9SAndrew Gallatin 
35051e413cf9SAndrew Gallatin 	err = 0;
3506c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35071e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
35081e413cf9SAndrew Gallatin 	if (slice == 0) {
3509c6cb3e3fSAndrew Gallatin #endif
35101e413cf9SAndrew Gallatin 		cmd.data0 = slice;
35111e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
35121e413cf9SAndrew Gallatin 		ss->tx.lanai =
35131e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3514c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3515c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3516c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3517c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3518c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35191e413cf9SAndrew Gallatin 	}
3520c6cb3e3fSAndrew Gallatin #endif
35211e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35221e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
35231e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
35241e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
35251e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35261e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35271e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
35281e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
35291e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35301e413cf9SAndrew Gallatin 
35311e413cf9SAndrew Gallatin 	if (err != 0) {
35321e413cf9SAndrew Gallatin 		device_printf(sc->dev,
35331e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
35341e413cf9SAndrew Gallatin 		return EIO;
35351e413cf9SAndrew Gallatin 	}
35361e413cf9SAndrew Gallatin 
35371e413cf9SAndrew Gallatin 	/* stock receive rings */
35381e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
35391e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
35401e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
35411e413cf9SAndrew Gallatin 		if (err) {
35421e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
35431e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
35441e413cf9SAndrew Gallatin 			return ENOMEM;
35451e413cf9SAndrew Gallatin 		}
35461e413cf9SAndrew Gallatin 	}
35471e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
35481e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
35491e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
35501e413cf9SAndrew Gallatin 	}
35511e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
35521e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
35534d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
35544d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
35551e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
35561e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
35571e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
35581e413cf9SAndrew Gallatin 		if (err) {
35591e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
35601e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
35611e413cf9SAndrew Gallatin 			return ENOMEM;
35621e413cf9SAndrew Gallatin 		}
35631e413cf9SAndrew Gallatin 	}
35641e413cf9SAndrew Gallatin 	return 0;
35651e413cf9SAndrew Gallatin }
35661e413cf9SAndrew Gallatin 
35671e413cf9SAndrew Gallatin static int
35681e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
35691e413cf9SAndrew Gallatin {
35701e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
35711e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
35721e413cf9SAndrew Gallatin 	bus_addr_t bus;
35731e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3574c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3575b2fc195eSAndrew Gallatin 
35767d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
35777d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
35787d542e2dSAndrew Gallatin 
3579adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3580b2fc195eSAndrew Gallatin 	if (err != 0) {
3581b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3582b2fc195eSAndrew Gallatin 		return EIO;
3583b2fc195eSAndrew Gallatin 	}
3584b2fc195eSAndrew Gallatin 
35851e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
35861e413cf9SAndrew Gallatin 		/* setup the indirection table */
35871e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
35881e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
35891e413cf9SAndrew Gallatin 				    &cmd);
3590b2fc195eSAndrew Gallatin 
35911e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
35921e413cf9SAndrew Gallatin 				     &cmd);
35931e413cf9SAndrew Gallatin 		if (err != 0) {
35941e413cf9SAndrew Gallatin 			device_printf(sc->dev,
35951e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
35961e413cf9SAndrew Gallatin 			return err;
35971e413cf9SAndrew Gallatin 		}
35981e413cf9SAndrew Gallatin 
35991e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
36001e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
36011e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
36021e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
36031e413cf9SAndrew Gallatin 
36041e413cf9SAndrew Gallatin 		cmd.data0 = 1;
36051e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
36061e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
36071e413cf9SAndrew Gallatin 		if (err != 0) {
36081e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
36091e413cf9SAndrew Gallatin 			return err;
36101e413cf9SAndrew Gallatin 		}
36111e413cf9SAndrew Gallatin 	}
36121e413cf9SAndrew Gallatin 
36131e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
36141e413cf9SAndrew Gallatin 
36151e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3616053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3617053e637fSAndrew Gallatin 			    &cmd);
3618053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3619053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
36201e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3621053e637fSAndrew Gallatin 		device_printf(sc->dev,
3622053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
36231e413cf9SAndrew Gallatin 			      nbufs);
3624053e637fSAndrew Gallatin 		return EIO;
3625053e637fSAndrew Gallatin 	}
3626b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3627b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3628b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3629c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
36305e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3631b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
36325e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3633b2fc195eSAndrew Gallatin 			     &cmd);
3634053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
36355e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
36360fa7f681SAndrew Gallatin 
36370fa7f681SAndrew Gallatin 	if (err != 0) {
36380fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
36390fa7f681SAndrew Gallatin 		goto abort;
36400fa7f681SAndrew Gallatin 	}
36410fa7f681SAndrew Gallatin 
3642b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3643c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3644c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3645c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3646c6cb3e3fSAndrew Gallatin #else
3647c6cb3e3fSAndrew Gallatin 	     slice < 1;
3648c6cb3e3fSAndrew Gallatin #endif
3649c6cb3e3fSAndrew Gallatin 	     slice++) {
3650c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3651c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3652c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3653c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3654c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
36550fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3656c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3657c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3658c6cb3e3fSAndrew Gallatin 	}
36590fa7f681SAndrew Gallatin 
36600fa7f681SAndrew Gallatin 	if (err != 0) {
36611e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
36620fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
36630fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
36640fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
36650fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
36660fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
36670fa7f681SAndrew Gallatin 				    &cmd);
36680fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
36690fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
36700fa7f681SAndrew Gallatin 	} else {
36710fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
36720fa7f681SAndrew Gallatin 	}
3673b2fc195eSAndrew Gallatin 
3674b2fc195eSAndrew Gallatin 	if (err != 0) {
3675b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3676b2fc195eSAndrew Gallatin 		goto abort;
3677b2fc195eSAndrew Gallatin 	}
3678b2fc195eSAndrew Gallatin 
36791e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
36801e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
36811e413cf9SAndrew Gallatin 		if (err != 0) {
36821e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
36831e413cf9SAndrew Gallatin 				      slice);
36841e413cf9SAndrew Gallatin 			goto abort;
36851e413cf9SAndrew Gallatin 		}
36861e413cf9SAndrew Gallatin 	}
36871e413cf9SAndrew Gallatin 
3688b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
36895e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3690b2fc195eSAndrew Gallatin 	if (err) {
3691b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3692b2fc195eSAndrew Gallatin 		goto abort;
3693b2fc195eSAndrew Gallatin 	}
3694c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3695c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3696c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3697c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3698c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3699c6cb3e3fSAndrew Gallatin 	}
3700c6cb3e3fSAndrew Gallatin #endif
3701b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3702b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3703b2fc195eSAndrew Gallatin 
3704b2fc195eSAndrew Gallatin 	return 0;
3705b2fc195eSAndrew Gallatin 
3706b2fc195eSAndrew Gallatin abort:
37076d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3708a98d6cd7SAndrew Gallatin 
3709b2fc195eSAndrew Gallatin 	return err;
3710b2fc195eSAndrew Gallatin }
3711b2fc195eSAndrew Gallatin 
3712b2fc195eSAndrew Gallatin static int
3713a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3714b2fc195eSAndrew Gallatin {
37156d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3716b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3717c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3718c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3719c6cb3e3fSAndrew Gallatin 	int slice;
3720c6cb3e3fSAndrew Gallatin #endif
3721b2fc195eSAndrew Gallatin 
3722c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3723c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3724c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3725c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3726c6cb3e3fSAndrew Gallatin 	}
3727c6cb3e3fSAndrew Gallatin #endif
3728b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3729a393336bSAndrew Gallatin 	if (!down) {
3730b2fc195eSAndrew Gallatin 		old_down_cnt = sc->down_cnt;
373173c7c83fSAndrew Gallatin 		wmb();
37325e7d8541SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3733b2fc195eSAndrew Gallatin 		if (err) {
3734a393336bSAndrew Gallatin 			device_printf(sc->dev,
3735a393336bSAndrew Gallatin 				      "Couldn't bring down link\n");
3736b2fc195eSAndrew Gallatin 		}
3737b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3738b2fc195eSAndrew Gallatin 			/* wait for down irq */
3739dce01b9bSAndrew Gallatin 			DELAY(10 * sc->intr_coal_delay);
3740b2fc195eSAndrew Gallatin 		}
374173c7c83fSAndrew Gallatin 		wmb();
3742b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3743b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "never got down irq\n");
3744b2fc195eSAndrew Gallatin 		}
3745a393336bSAndrew Gallatin 	}
37466d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3747a98d6cd7SAndrew Gallatin 
3748b2fc195eSAndrew Gallatin 	return 0;
3749b2fc195eSAndrew Gallatin }
3750b2fc195eSAndrew Gallatin 
3751dce01b9bSAndrew Gallatin static void
3752dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3753dce01b9bSAndrew Gallatin {
3754dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3755dce01b9bSAndrew Gallatin 	int reg;
3756c68534f1SScott Long 	uint16_t lnk, pectl;
3757dce01b9bSAndrew Gallatin 
3758dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
37593b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
3760dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3761dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3762dce01b9bSAndrew Gallatin 
376383d54b59SAndrew Gallatin 		if (sc->pectl == 0) {
3764dce01b9bSAndrew Gallatin 			pectl = pci_read_config(dev, reg + 0x8, 2);
3765dce01b9bSAndrew Gallatin 			pectl = (pectl & ~0x7000) | (5 << 12);
3766dce01b9bSAndrew Gallatin 			pci_write_config(dev, reg + 0x8, pectl, 2);
376783d54b59SAndrew Gallatin 			sc->pectl = pectl;
376883d54b59SAndrew Gallatin 		} else {
376983d54b59SAndrew Gallatin 			/* restore saved pectl after watchdog reset */
377083d54b59SAndrew Gallatin 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
377183d54b59SAndrew Gallatin 		}
3772dce01b9bSAndrew Gallatin 	}
3773dce01b9bSAndrew Gallatin 
3774dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3775dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3776dce01b9bSAndrew Gallatin }
3777dce01b9bSAndrew Gallatin 
3778dce01b9bSAndrew Gallatin static uint32_t
3779dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3780dce01b9bSAndrew Gallatin {
3781dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3782dce01b9bSAndrew Gallatin 	uint32_t vs;
3783dce01b9bSAndrew Gallatin 
3784dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
37853b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) {
3786dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3787dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3788dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3789dce01b9bSAndrew Gallatin 	}
3790dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3791dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3792dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3793dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3794dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3795dce01b9bSAndrew Gallatin }
3796dce01b9bSAndrew Gallatin 
379772c042dfSAndrew Gallatin static void
379872c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3799dce01b9bSAndrew Gallatin {
3800e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3801a393336bSAndrew Gallatin 	struct mxge_slice_state *ss;
3802a393336bSAndrew Gallatin 	int err, running, s, num_tx_slices = 1;
3803dce01b9bSAndrew Gallatin 	uint32_t reboot;
3804dce01b9bSAndrew Gallatin 	uint16_t cmd;
3805dce01b9bSAndrew Gallatin 
3806dce01b9bSAndrew Gallatin 	err = ENXIO;
3807dce01b9bSAndrew Gallatin 
3808dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3809dce01b9bSAndrew Gallatin 
3810dce01b9bSAndrew Gallatin 	/*
3811dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3812dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3813dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3814dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3815dce01b9bSAndrew Gallatin 	 * again
3816dce01b9bSAndrew Gallatin 	 */
3817dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3818dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3819dce01b9bSAndrew Gallatin 		/*
3820dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3821dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3822dce01b9bSAndrew Gallatin 		 * back, then give up
3823dce01b9bSAndrew Gallatin 		 */
3824dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3825dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3826dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3827dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3828dce01b9bSAndrew Gallatin 		}
3829dce01b9bSAndrew Gallatin 	}
3830dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3831dce01b9bSAndrew Gallatin 		/* print the reboot status */
3832dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3833dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3834dce01b9bSAndrew Gallatin 			      reboot);
3835a393336bSAndrew Gallatin 		running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
3836a393336bSAndrew Gallatin 		if (running) {
3837a393336bSAndrew Gallatin 			/*
3838a393336bSAndrew Gallatin 			 * quiesce NIC so that TX routines will not try to
3839a393336bSAndrew Gallatin 			 * xmit after restoration of BAR
3840a393336bSAndrew Gallatin 			 */
3841a393336bSAndrew Gallatin 
3842a393336bSAndrew Gallatin 			/* Mark the link as down */
3843a393336bSAndrew Gallatin 			if (sc->link_state) {
3844a393336bSAndrew Gallatin 				sc->link_state = 0;
3845a393336bSAndrew Gallatin 				if_link_state_change(sc->ifp,
3846a393336bSAndrew Gallatin 						     LINK_STATE_DOWN);
3847a393336bSAndrew Gallatin 			}
3848a393336bSAndrew Gallatin #ifdef IFNET_BUF_RING
3849a393336bSAndrew Gallatin 			num_tx_slices = sc->num_slices;
3850a393336bSAndrew Gallatin #endif
3851a393336bSAndrew Gallatin 			/* grab all TX locks to ensure no tx  */
3852a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3853a393336bSAndrew Gallatin 				ss = &sc->ss[s];
3854a393336bSAndrew Gallatin 				mtx_lock(&ss->tx.mtx);
3855a393336bSAndrew Gallatin 			}
3856a393336bSAndrew Gallatin 			mxge_close(sc, 1);
3857a393336bSAndrew Gallatin 		}
3858dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3859e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3860e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3861dce01b9bSAndrew Gallatin 
3862dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3863dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
386410882804SAndrew Gallatin 
3865a393336bSAndrew Gallatin 		/* reload f/w */
3866a393336bSAndrew Gallatin 		err = mxge_load_firmware(sc, 0);
3867a393336bSAndrew Gallatin 		if (err) {
3868a393336bSAndrew Gallatin 			device_printf(sc->dev,
3869a393336bSAndrew Gallatin 				      "Unable to re-load f/w\n");
387010882804SAndrew Gallatin 		}
3871a393336bSAndrew Gallatin 		if (running) {
3872a393336bSAndrew Gallatin 			if (!err)
3873a393336bSAndrew Gallatin 				err = mxge_open(sc);
3874a393336bSAndrew Gallatin 			/* release all TX locks */
3875a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3876a393336bSAndrew Gallatin 				ss = &sc->ss[s];
387783d54b59SAndrew Gallatin #ifdef IFNET_BUF_RING
387883d54b59SAndrew Gallatin 				mxge_start_locked(ss);
387983d54b59SAndrew Gallatin #endif
3880a393336bSAndrew Gallatin 				mtx_unlock(&ss->tx.mtx);
3881a393336bSAndrew Gallatin 			}
3882a393336bSAndrew Gallatin 		}
3883a393336bSAndrew Gallatin 		sc->watchdog_resets++;
3884dce01b9bSAndrew Gallatin 	} else {
3885c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
388672c042dfSAndrew Gallatin 			      "NIC did not reboot, not resetting\n");
388772c042dfSAndrew Gallatin 		err = 0;
388872c042dfSAndrew Gallatin 	}
388972c042dfSAndrew Gallatin 	if (err) {
389072c042dfSAndrew Gallatin 		device_printf(sc->dev, "watchdog reset failed\n");
389172c042dfSAndrew Gallatin 	} else {
38926b484a49SAndrew Gallatin 		if (sc->dying == 2)
38936b484a49SAndrew Gallatin 			sc->dying = 0;
38946b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
389572c042dfSAndrew Gallatin 	}
389672c042dfSAndrew Gallatin }
389772c042dfSAndrew Gallatin 
389872c042dfSAndrew Gallatin static void
389972c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
390072c042dfSAndrew Gallatin {
390172c042dfSAndrew Gallatin 	mxge_softc_t *sc = arg;
390272c042dfSAndrew Gallatin 
390372c042dfSAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
390472c042dfSAndrew Gallatin 	mxge_watchdog_reset(sc);
390572c042dfSAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
390672c042dfSAndrew Gallatin }
390772c042dfSAndrew Gallatin 
390872c042dfSAndrew Gallatin static void
390972c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
391072c042dfSAndrew Gallatin {
391172c042dfSAndrew Gallatin 	tx = &sc->ss[slice].tx;
391272c042dfSAndrew Gallatin 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3913c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev,
3914c6cb3e3fSAndrew Gallatin 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3915c6cb3e3fSAndrew Gallatin 		      tx->req, tx->done, tx->queue_active);
3916c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3917c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3918dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3919c6cb3e3fSAndrew Gallatin 		      tx->pkt_done,
39201e413cf9SAndrew Gallatin 		      be32toh(sc->ss->fw_stats->send_done_count));
3921dce01b9bSAndrew Gallatin }
3922dce01b9bSAndrew Gallatin 
3923e749ef6bSAndrew Gallatin static int
3924dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3925dce01b9bSAndrew Gallatin {
3926c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
39271e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3928c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3929dce01b9bSAndrew Gallatin 
3930dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3931dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3932c6cb3e3fSAndrew Gallatin 	for (i = 0;
3933c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3934c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
3935c6cb3e3fSAndrew Gallatin #else
3936c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
3937c6cb3e3fSAndrew Gallatin #endif
3938c6cb3e3fSAndrew Gallatin 	     i++) {
3939c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3940dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3941dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3942c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3943c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
394472c042dfSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause) {
394572c042dfSAndrew Gallatin 				mxge_warn_stuck(sc, tx, i);
394672c042dfSAndrew Gallatin 				taskqueue_enqueue(sc->tq, &sc->watchdog_task);
394772c042dfSAndrew Gallatin 				return (ENXIO);
394872c042dfSAndrew Gallatin 			}
3949c587e59fSAndrew Gallatin 			else
3950c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3951c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3952c587e59fSAndrew Gallatin 		}
3953dce01b9bSAndrew Gallatin 
3954dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3955dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3956c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3957c6cb3e3fSAndrew Gallatin 	}
3958c587e59fSAndrew Gallatin 
3959c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3960c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3961e749ef6bSAndrew Gallatin 	return (err);
3962dce01b9bSAndrew Gallatin }
3963dce01b9bSAndrew Gallatin 
3964f3f040d9SGleb Smirnoff static uint64_t
3965f3f040d9SGleb Smirnoff mxge_get_counter(struct ifnet *ifp, ift_counter cnt)
39661e413cf9SAndrew Gallatin {
3967f3f040d9SGleb Smirnoff 	struct mxge_softc *sc;
3968f3f040d9SGleb Smirnoff 	uint64_t rv;
39691e413cf9SAndrew Gallatin 
3970f3f040d9SGleb Smirnoff 	sc = if_getsoftc(ifp);
3971f3f040d9SGleb Smirnoff 	rv = 0;
3972f3f040d9SGleb Smirnoff 
3973f3f040d9SGleb Smirnoff 	switch (cnt) {
3974f3f040d9SGleb Smirnoff 	case IFCOUNTER_IPACKETS:
3975f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3976f3f040d9SGleb Smirnoff 			rv += sc->ss[s].ipackets;
3977f3f040d9SGleb Smirnoff 		return (rv);
3978f3f040d9SGleb Smirnoff 	case IFCOUNTER_OPACKETS:
3979f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3980f3f040d9SGleb Smirnoff 			rv += sc->ss[s].opackets;
3981f3f040d9SGleb Smirnoff 		return (rv);
3982f3f040d9SGleb Smirnoff 	case IFCOUNTER_OERRORS:
3983f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3984f3f040d9SGleb Smirnoff 			rv += sc->ss[s].oerrors;
3985f3f040d9SGleb Smirnoff 		return (rv);
398671032832SAndrew Gallatin #ifdef IFNET_BUF_RING
3987f3f040d9SGleb Smirnoff 	case IFCOUNTER_OBYTES:
3988f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3989f3f040d9SGleb Smirnoff 			rv += sc->ss[s].obytes;
3990f3f040d9SGleb Smirnoff 		return (rv);
3991f3f040d9SGleb Smirnoff 	case IFCOUNTER_OMCASTS:
3992f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3993f3f040d9SGleb Smirnoff 			rv += sc->ss[s].omcasts;
3994f3f040d9SGleb Smirnoff 		return (rv);
3995f3f040d9SGleb Smirnoff 	case IFCOUNTER_OQDROPS:
3996f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3997f3f040d9SGleb Smirnoff 			rv += sc->ss[s].tx.br->br_drops;
3998f3f040d9SGleb Smirnoff 		return (rv);
399971032832SAndrew Gallatin #endif
4000f3f040d9SGleb Smirnoff 	default:
4001f3f040d9SGleb Smirnoff 		return (if_get_counter_default(ifp, cnt));
40021e413cf9SAndrew Gallatin 	}
40031e413cf9SAndrew Gallatin }
4004c6cb3e3fSAndrew Gallatin 
40051e413cf9SAndrew Gallatin static void
4006dce01b9bSAndrew Gallatin mxge_tick(void *arg)
4007dce01b9bSAndrew Gallatin {
4008dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
40096b484a49SAndrew Gallatin 	u_long pkts = 0;
4010e749ef6bSAndrew Gallatin 	int err = 0;
40116b484a49SAndrew Gallatin 	int running, ticks;
40126b484a49SAndrew Gallatin 	uint16_t cmd;
4013dce01b9bSAndrew Gallatin 
40146b484a49SAndrew Gallatin 	ticks = mxge_ticks;
40156b484a49SAndrew Gallatin 	running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
40166b484a49SAndrew Gallatin 	if (running) {
40171e413cf9SAndrew Gallatin 		if (!sc->watchdog_countdown) {
4018e749ef6bSAndrew Gallatin 			err = mxge_watchdog(sc);
40191e413cf9SAndrew Gallatin 			sc->watchdog_countdown = 4;
40201e413cf9SAndrew Gallatin 		}
40211e413cf9SAndrew Gallatin 		sc->watchdog_countdown--;
40226b484a49SAndrew Gallatin 	}
40236b484a49SAndrew Gallatin 	if (pkts == 0) {
40246b484a49SAndrew Gallatin 		/* ensure NIC did not suffer h/w fault while idle */
40256b484a49SAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
40266b484a49SAndrew Gallatin 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
40276b484a49SAndrew Gallatin 			sc->dying = 2;
40286b484a49SAndrew Gallatin 			taskqueue_enqueue(sc->tq, &sc->watchdog_task);
40296b484a49SAndrew Gallatin 			err = ENXIO;
40306b484a49SAndrew Gallatin 		}
40316b484a49SAndrew Gallatin 		/* look less often if NIC is idle */
40326b484a49SAndrew Gallatin 		ticks *= 4;
40336b484a49SAndrew Gallatin 	}
40346b484a49SAndrew Gallatin 
4035e749ef6bSAndrew Gallatin 	if (err == 0)
40366b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
4037e749ef6bSAndrew Gallatin 
4038dce01b9bSAndrew Gallatin }
4039b2fc195eSAndrew Gallatin 
4040b2fc195eSAndrew Gallatin static int
40416d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
4042b2fc195eSAndrew Gallatin {
4043b2fc195eSAndrew Gallatin 	return EINVAL;
4044b2fc195eSAndrew Gallatin }
4045b2fc195eSAndrew Gallatin 
4046b2fc195eSAndrew Gallatin static int
40476d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
4048b2fc195eSAndrew Gallatin {
4049b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
4050b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
4051b2fc195eSAndrew Gallatin 	int err = 0;
4052b2fc195eSAndrew Gallatin 
4053c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
4054053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
4055b2fc195eSAndrew Gallatin 		return EINVAL;
4056a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4057b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
4058b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
4059b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4060a393336bSAndrew Gallatin 		mxge_close(sc, 0);
40616d87a65dSAndrew Gallatin 		err = mxge_open(sc);
4062b2fc195eSAndrew Gallatin 		if (err != 0) {
4063b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
4064a393336bSAndrew Gallatin 			mxge_close(sc, 0);
40656d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
4066b2fc195eSAndrew Gallatin 		}
4067b2fc195eSAndrew Gallatin 	}
4068a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4069b2fc195eSAndrew Gallatin 	return err;
4070b2fc195eSAndrew Gallatin }
4071b2fc195eSAndrew Gallatin 
4072b2fc195eSAndrew Gallatin static void
40736d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
4074b2fc195eSAndrew Gallatin {
40756d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4076b2fc195eSAndrew Gallatin 
4077b2fc195eSAndrew Gallatin 	if (sc == NULL)
4078b2fc195eSAndrew Gallatin 		return;
4079b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
4080c406ad2eSAndrew Gallatin 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
4081c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
4082c406ad2eSAndrew Gallatin 	ifmr->ifm_active |= sc->current_media;
4083b2fc195eSAndrew Gallatin }
4084b2fc195eSAndrew Gallatin 
4085b2fc195eSAndrew Gallatin static int
4086df131e84SAndrew Gallatin mxge_fetch_i2c(mxge_softc_t *sc, struct ifi2creq *i2c)
4087df131e84SAndrew Gallatin {
4088df131e84SAndrew Gallatin 	mxge_cmd_t cmd;
4089df131e84SAndrew Gallatin 	uint32_t i2c_args;
4090df131e84SAndrew Gallatin 	int i, ms, err;
4091df131e84SAndrew Gallatin 
4092df131e84SAndrew Gallatin 	if (i2c->dev_addr != 0xA0 &&
4093df131e84SAndrew Gallatin 	    i2c->dev_addr != 0xA2)
4094df131e84SAndrew Gallatin 		return (EINVAL);
4095df131e84SAndrew Gallatin 	if (i2c->len > sizeof(i2c->data))
4096df131e84SAndrew Gallatin 		return (EINVAL);
4097df131e84SAndrew Gallatin 
4098df131e84SAndrew Gallatin 	for (i = 0; i < i2c->len; i++) {
4099df131e84SAndrew Gallatin 		i2c_args = i2c->dev_addr << 0x8;
4100df131e84SAndrew Gallatin 		i2c_args |= i2c->offset + i;
4101df131e84SAndrew Gallatin 		cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
4102df131e84SAndrew Gallatin 		cmd.data1 = i2c_args;
4103df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
4104df131e84SAndrew Gallatin 
4105df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
4106df131e84SAndrew Gallatin 			return (EIO);
4107df131e84SAndrew Gallatin 		/* now we wait for the data to be cached */
4108df131e84SAndrew Gallatin 		cmd.data0 = i2c_args & 0xff;
4109df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
4110df131e84SAndrew Gallatin 		for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
4111df131e84SAndrew Gallatin 			cmd.data0 = i2c_args & 0xff;
4112df131e84SAndrew Gallatin 			err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
4113df131e84SAndrew Gallatin 			if (err == EBUSY)
4114df131e84SAndrew Gallatin 				DELAY(1000);
4115df131e84SAndrew Gallatin 		}
4116df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
4117df131e84SAndrew Gallatin 			return (EIO);
4118df131e84SAndrew Gallatin 		i2c->data[i] = cmd.data0;
4119df131e84SAndrew Gallatin 	}
4120df131e84SAndrew Gallatin 	return (0);
4121df131e84SAndrew Gallatin }
4122df131e84SAndrew Gallatin 
4123df131e84SAndrew Gallatin static int
41246d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
4125b2fc195eSAndrew Gallatin {
41266d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4127b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
4128df131e84SAndrew Gallatin 	struct ifi2creq i2c;
4129b2fc195eSAndrew Gallatin 	int err, mask;
4130b2fc195eSAndrew Gallatin 
4131b2fc195eSAndrew Gallatin 	err = 0;
4132b2fc195eSAndrew Gallatin 	switch (command) {
4133b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
41346d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
4135b2fc195eSAndrew Gallatin 		break;
4136b2fc195eSAndrew Gallatin 
4137b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
4138a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
41398c5d766cSAndrew Gallatin 		if (sc->dying) {
41408c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
41418c5d766cSAndrew Gallatin 			return EINVAL;
41428c5d766cSAndrew Gallatin 		}
4143b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
4144dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
41456d87a65dSAndrew Gallatin 				err = mxge_open(sc);
4146dce01b9bSAndrew Gallatin 			} else {
41470fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
41480fa7f681SAndrew Gallatin 				   flag chages */
41490fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
41500fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
41510fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
41520fa7f681SAndrew Gallatin 			}
4153b2fc195eSAndrew Gallatin 		} else {
4154dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4155a393336bSAndrew Gallatin 				mxge_close(sc, 0);
4156dce01b9bSAndrew Gallatin 			}
4157b2fc195eSAndrew Gallatin 		}
4158a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4159b2fc195eSAndrew Gallatin 		break;
4160b2fc195eSAndrew Gallatin 
4161b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
4162b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
4163a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4164c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4165c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4166c0a1f0afSAndrew Gallatin 			return (EINVAL);
4167c0a1f0afSAndrew Gallatin 		}
41680fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
4169a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4170b2fc195eSAndrew Gallatin 		break;
4171b2fc195eSAndrew Gallatin 
4172b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
4173a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4174b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
4175b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
4176b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
4177cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO4;
4178aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
41790a7a780eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
4180b2fc195eSAndrew Gallatin 			} else {
4181b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
4182b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
4183b2fc195eSAndrew Gallatin 			}
4184cbb9ccf7SRyan Moeller 		}
4185cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM) {
4186b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
4187b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
4188b2fc195eSAndrew Gallatin 			} else {
4189b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
4190b2fc195eSAndrew Gallatin 			}
4191b2fc195eSAndrew Gallatin 		}
4192aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
4193aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
4194aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
4195aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
4196aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
4197aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
4198aed8e389SAndrew Gallatin 			} else {
4199aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4200aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4201aed8e389SAndrew Gallatin 				err = EINVAL;
4202aed8e389SAndrew Gallatin 			}
4203aed8e389SAndrew Gallatin 		}
42040a7a780eSAndrew Gallatin #if IFCAP_TSO6
42050a7a780eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM_IPV6) {
42060a7a780eSAndrew Gallatin 			if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) {
4207cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO6;
42080a7a780eSAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6
42090a7a780eSAndrew Gallatin 						       | IFCAP_TSO6);
42100a7a780eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP_IPV6
42110a7a780eSAndrew Gallatin 						      | CSUM_UDP);
42120a7a780eSAndrew Gallatin 			} else {
42130a7a780eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM_IPV6;
42140a7a780eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP_IPV6
42150a7a780eSAndrew Gallatin 						     | CSUM_UDP_IPV6);
42160a7a780eSAndrew Gallatin 			}
4217cbb9ccf7SRyan Moeller 		}
4218cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM_IPV6) {
421926dd49c6SAndrew Gallatin 			if (IFCAP_RXCSUM_IPV6 & ifp->if_capenable) {
422026dd49c6SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM_IPV6;
42210a7a780eSAndrew Gallatin 			} else {
422226dd49c6SAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM_IPV6;
42230a7a780eSAndrew Gallatin 			}
42240a7a780eSAndrew Gallatin 		}
42250a7a780eSAndrew Gallatin 		if (mask & IFCAP_TSO6) {
42260a7a780eSAndrew Gallatin 			if (IFCAP_TSO6 & ifp->if_capenable) {
42270a7a780eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO6;
42280a7a780eSAndrew Gallatin 			} else if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) {
42290a7a780eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO6;
42300a7a780eSAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
42310a7a780eSAndrew Gallatin 			} else {
42320a7a780eSAndrew Gallatin 				printf("mxge requires tx checksum offload"
42330a7a780eSAndrew Gallatin 				       " be enabled to use TSO\n");
42340a7a780eSAndrew Gallatin 				err = EINVAL;
42350a7a780eSAndrew Gallatin 			}
42360a7a780eSAndrew Gallatin 		}
42370a7a780eSAndrew Gallatin #endif /*IFCAP_TSO6 */
42380a7a780eSAndrew Gallatin 
423926dd49c6SAndrew Gallatin 		if (mask & IFCAP_LRO)
424026dd49c6SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_LRO;
4241c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
4242c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
42430dce6781SAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTSO)
42440dce6781SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
42450dce6781SAndrew Gallatin 
42460dce6781SAndrew Gallatin 		if (!(ifp->if_capabilities & IFCAP_VLAN_HWTSO) ||
42470dce6781SAndrew Gallatin 		    !(ifp->if_capenable & IFCAP_VLAN_HWTAGGING))
42480dce6781SAndrew Gallatin 			ifp->if_capenable &= ~IFCAP_VLAN_HWTSO;
42490dce6781SAndrew Gallatin 
4250a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4251c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4252c792928fSAndrew Gallatin 
4253b2fc195eSAndrew Gallatin 		break;
4254b2fc195eSAndrew Gallatin 
4255b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4256c406ad2eSAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4257c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4258c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4259c0a1f0afSAndrew Gallatin 			return (EINVAL);
4260c0a1f0afSAndrew Gallatin 		}
4261c406ad2eSAndrew Gallatin 		mxge_media_probe(sc);
4262c406ad2eSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4263b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4264b2fc195eSAndrew Gallatin 				    &sc->media, command);
4265b2fc195eSAndrew Gallatin 		break;
4266b2fc195eSAndrew Gallatin 
4267df131e84SAndrew Gallatin 	case SIOCGI2C:
4268df131e84SAndrew Gallatin 		if (sc->connector != MXGE_XFP &&
4269df131e84SAndrew Gallatin 		    sc->connector != MXGE_SFP) {
4270df131e84SAndrew Gallatin 			err = ENXIO;
4271df131e84SAndrew Gallatin 			break;
4272df131e84SAndrew Gallatin 		}
4273df131e84SAndrew Gallatin 		err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c));
4274df131e84SAndrew Gallatin 		if (err != 0)
4275df131e84SAndrew Gallatin 			break;
4276df131e84SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4277df131e84SAndrew Gallatin 		if (sc->dying) {
4278df131e84SAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4279df131e84SAndrew Gallatin 			return (EINVAL);
4280df131e84SAndrew Gallatin 		}
4281df131e84SAndrew Gallatin 		err = mxge_fetch_i2c(sc, &i2c);
4282df131e84SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4283df131e84SAndrew Gallatin 		if (err == 0)
42841b8b041cSBrooks Davis 			err = copyout(&i2c, ifr_data_get_ptr(ifr),
4285df131e84SAndrew Gallatin 			    sizeof(i2c));
4286df131e84SAndrew Gallatin 		break;
4287b2fc195eSAndrew Gallatin 	default:
4288c756fb6eSRavi Pokala 		err = ether_ioctl(ifp, command, data);
4289c756fb6eSRavi Pokala 		break;
4290b2fc195eSAndrew Gallatin 	}
4291b2fc195eSAndrew Gallatin 	return err;
4292b2fc195eSAndrew Gallatin }
4293b2fc195eSAndrew Gallatin 
4294b2fc195eSAndrew Gallatin static void
42956d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4296b2fc195eSAndrew Gallatin {
4297b2fc195eSAndrew Gallatin 
42981e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
42996d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
43006d87a65dSAndrew Gallatin 			  &mxge_flow_control);
43016d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
43026d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
43036d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
43046d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4305d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4306d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
43075e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
43085e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
43095e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
43105e7d8541SAndrew Gallatin 			  &mxge_verbose);
4311dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
43121e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
43131e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
431494c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4315f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
431665c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4317b2fc195eSAndrew Gallatin 
43185e7d8541SAndrew Gallatin 	if (bootverbose)
43195e7d8541SAndrew Gallatin 		mxge_verbose = 1;
43206d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
43216d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4322dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
43231e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
43246d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
43251e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4326bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
43275769c5efSAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4328b2fc195eSAndrew Gallatin 	}
4329f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4330f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4331f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
433265c69066SAndrew Gallatin 
433365c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
433465c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
433565c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
433665c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
433765c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
43381e413cf9SAndrew Gallatin }
43391e413cf9SAndrew Gallatin 
43401e413cf9SAndrew Gallatin static void
43411e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
43421e413cf9SAndrew Gallatin {
43431e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
43441e413cf9SAndrew Gallatin 	int i;
43451e413cf9SAndrew Gallatin 
43461e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
43471e413cf9SAndrew Gallatin 		return;
43481e413cf9SAndrew Gallatin 
43491e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43501e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
43511e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
43521e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
43531e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4354c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4355c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4356c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4357c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4358c6cb3e3fSAndrew Gallatin 			}
4359c6cb3e3fSAndrew Gallatin #endif
43601e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
43611e413cf9SAndrew Gallatin 		}
43621e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
43631e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
43641e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
43651e413cf9SAndrew Gallatin 		}
43661e413cf9SAndrew Gallatin 	}
43671e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
43681e413cf9SAndrew Gallatin 	sc->ss = NULL;
43691e413cf9SAndrew Gallatin }
43701e413cf9SAndrew Gallatin 
43711e413cf9SAndrew Gallatin static int
43721e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
43731e413cf9SAndrew Gallatin {
43741e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
43751e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
43761e413cf9SAndrew Gallatin 	size_t bytes;
43771e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
43781e413cf9SAndrew Gallatin 
43791e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
43801e413cf9SAndrew Gallatin 	if (err != 0) {
43811e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
43821e413cf9SAndrew Gallatin 		return err;
43831e413cf9SAndrew Gallatin 	}
43841e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
43851e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
43861e413cf9SAndrew Gallatin 
4387ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->ss) * sc->num_slices;
4388ac2fffa4SPedro F. Giffuni 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
43891e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
43901e413cf9SAndrew Gallatin 		return (ENOMEM);
43911e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43921e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
43931e413cf9SAndrew Gallatin 
43941e413cf9SAndrew Gallatin 		ss->sc = sc;
43951e413cf9SAndrew Gallatin 
43961e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
43971e413cf9SAndrew Gallatin 
43981e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
43991e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
44001e413cf9SAndrew Gallatin 		if (err != 0)
44011e413cf9SAndrew Gallatin 			goto abort;
44021e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
44031e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
44041e413cf9SAndrew Gallatin 
44051e413cf9SAndrew Gallatin 		/*
44061e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
44071e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
44081e413cf9SAndrew Gallatin 		 * slice for now
44091e413cf9SAndrew Gallatin 		 */
4410c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
44111e413cf9SAndrew Gallatin 		if (i > 0)
44121e413cf9SAndrew Gallatin 			continue;
4413c6cb3e3fSAndrew Gallatin #endif
44141e413cf9SAndrew Gallatin 
44151e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
44161e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
44171e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
44181e413cf9SAndrew Gallatin 		if (err != 0)
44191e413cf9SAndrew Gallatin 			goto abort;
44201e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
44211e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
44221e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
44231e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4424c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4425c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4426c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4427c6cb3e3fSAndrew Gallatin #endif
44281e413cf9SAndrew Gallatin 	}
44291e413cf9SAndrew Gallatin 
44301e413cf9SAndrew Gallatin 	return (0);
44311e413cf9SAndrew Gallatin 
44321e413cf9SAndrew Gallatin abort:
44331e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
44341e413cf9SAndrew Gallatin 	return (ENOMEM);
44351e413cf9SAndrew Gallatin }
44361e413cf9SAndrew Gallatin 
44371e413cf9SAndrew Gallatin static void
44381e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
44391e413cf9SAndrew Gallatin {
44401e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
44411e413cf9SAndrew Gallatin 	char *old_fw;
44421e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
44431e413cf9SAndrew Gallatin 
44441e413cf9SAndrew Gallatin 	sc->num_slices = 1;
44451e413cf9SAndrew Gallatin 	/*
44461e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
44471e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
44481e413cf9SAndrew Gallatin 	 */
44491e413cf9SAndrew Gallatin 
44501e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
44511e413cf9SAndrew Gallatin 		return;
44521e413cf9SAndrew Gallatin 
44531e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
44541e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
44551e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
44561e413cf9SAndrew Gallatin 		return;
44571e413cf9SAndrew Gallatin 
44581e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
44591e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
44601e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
44611e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
44621e413cf9SAndrew Gallatin 	else
44631e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
44641e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
44651e413cf9SAndrew Gallatin 	if (status != 0) {
44661e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
44671e413cf9SAndrew Gallatin 		return;
44681e413cf9SAndrew Gallatin 	}
44691e413cf9SAndrew Gallatin 
44701e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
44711e413cf9SAndrew Gallatin 	   is alive */
44721e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
44731e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
44741e413cf9SAndrew Gallatin 	if (status != 0) {
44751e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
44761e413cf9SAndrew Gallatin 		goto abort_with_fw;
44771e413cf9SAndrew Gallatin 	}
44781e413cf9SAndrew Gallatin 
44791e413cf9SAndrew Gallatin 	/* get rx ring size */
44801e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
44811e413cf9SAndrew Gallatin 	if (status != 0) {
44821e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
44831e413cf9SAndrew Gallatin 		goto abort_with_fw;
44841e413cf9SAndrew Gallatin 	}
44851e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
44861e413cf9SAndrew Gallatin 
44871e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
44881e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
44891e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
44901e413cf9SAndrew Gallatin 	if (status != 0) {
44911e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
44921e413cf9SAndrew Gallatin 		goto abort_with_fw;
44931e413cf9SAndrew Gallatin 	}
44941e413cf9SAndrew Gallatin 
44951e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
44961e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
44971e413cf9SAndrew Gallatin 	if (status != 0) {
44981e413cf9SAndrew Gallatin 		device_printf(sc->dev,
44991e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
45001e413cf9SAndrew Gallatin 		goto abort_with_fw;
45011e413cf9SAndrew Gallatin 	}
45021e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
45031e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
45041e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
45051e413cf9SAndrew Gallatin 
45061e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
45071e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
45081e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
45091e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
45101e413cf9SAndrew Gallatin 	} else {
45111e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
45121e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
45131e413cf9SAndrew Gallatin 	}
45141e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
45151e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
45161e413cf9SAndrew Gallatin 		sc->num_slices--;
45171e413cf9SAndrew Gallatin 
45181e413cf9SAndrew Gallatin 	if (mxge_verbose)
45191e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
45201e413cf9SAndrew Gallatin 			      sc->num_slices);
45211e413cf9SAndrew Gallatin 
45221e413cf9SAndrew Gallatin 	return;
45231e413cf9SAndrew Gallatin 
45241e413cf9SAndrew Gallatin abort_with_fw:
45251e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
45261e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
45271e413cf9SAndrew Gallatin }
45281e413cf9SAndrew Gallatin 
45291e413cf9SAndrew Gallatin static int
45301e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
45311e413cf9SAndrew Gallatin {
4532ac2fffa4SPedro F. Giffuni 	size_t bytes;
45331e413cf9SAndrew Gallatin 	int count, err, i, rid;
45341e413cf9SAndrew Gallatin 
45351e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
45361e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
45371e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
45381e413cf9SAndrew Gallatin 
45391e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
45401e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
45411e413cf9SAndrew Gallatin 		return ENXIO;
45421e413cf9SAndrew Gallatin 	}
45431e413cf9SAndrew Gallatin 
45441e413cf9SAndrew Gallatin 	count = sc->num_slices;
45451e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
45461e413cf9SAndrew Gallatin 	if (err != 0) {
45471e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
45481e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
45491e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
45501e413cf9SAndrew Gallatin 	}
45511e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
45521e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
45531e413cf9SAndrew Gallatin 			      count, sc->num_slices);
45541e413cf9SAndrew Gallatin 		device_printf(sc->dev,
45551e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
45561e413cf9SAndrew Gallatin 			      count);
45571e413cf9SAndrew Gallatin 		err = ENOSPC;
45581e413cf9SAndrew Gallatin 		goto abort_with_msix;
45591e413cf9SAndrew Gallatin 	}
4560ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
4561ac2fffa4SPedro F. Giffuni 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
45621e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
45631e413cf9SAndrew Gallatin 		err = ENOMEM;
45641e413cf9SAndrew Gallatin 		goto abort_with_msix;
45651e413cf9SAndrew Gallatin 	}
45661e413cf9SAndrew Gallatin 
45671e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45681e413cf9SAndrew Gallatin 		rid = i + 1;
45691e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
45701e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
45711e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
45721e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
45731e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
45741e413cf9SAndrew Gallatin 				      " for message %d\n", i);
45751e413cf9SAndrew Gallatin 			err = ENXIO;
45761e413cf9SAndrew Gallatin 			goto abort_with_res;
45771e413cf9SAndrew Gallatin 		}
45781e413cf9SAndrew Gallatin 	}
45791e413cf9SAndrew Gallatin 
4580ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
4581ac2fffa4SPedro F. Giffuni 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
45821e413cf9SAndrew Gallatin 
45831e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45841e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
45851e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
458637d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
458737d89b0cSAndrew Gallatin 				     NULL,
458837d89b0cSAndrew Gallatin #endif
458937d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
45901e413cf9SAndrew Gallatin 		if (err != 0) {
45911e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
45921e413cf9SAndrew Gallatin 				      "message %d\n", i);
45931e413cf9SAndrew Gallatin 			goto abort_with_intr;
45941e413cf9SAndrew Gallatin 		}
459521089137SAndrew Gallatin 		bus_describe_intr(sc->dev, sc->msix_irq_res[i],
459621089137SAndrew Gallatin 				  sc->msix_ih[i], "s%d", i);
45971e413cf9SAndrew Gallatin 	}
45981e413cf9SAndrew Gallatin 
45991e413cf9SAndrew Gallatin 	if (mxge_verbose) {
46001e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
46011e413cf9SAndrew Gallatin 			      sc->num_slices);
46021e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
4603da1b038aSJustin Hibbits 			printf(" %jd", rman_get_start(sc->msix_irq_res[i]));
46041e413cf9SAndrew Gallatin 		printf("\n");
46051e413cf9SAndrew Gallatin 	}
46061e413cf9SAndrew Gallatin 	return (0);
46071e413cf9SAndrew Gallatin 
46081e413cf9SAndrew Gallatin abort_with_intr:
46091e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46101e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
46111e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
46121e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
46131e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
46141e413cf9SAndrew Gallatin 		}
46151e413cf9SAndrew Gallatin 	}
46161e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
46171e413cf9SAndrew Gallatin 
46181e413cf9SAndrew Gallatin abort_with_res:
46191e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46201e413cf9SAndrew Gallatin 		rid = i + 1;
46211e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
46221e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
46231e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
46241e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
46251e413cf9SAndrew Gallatin 	}
46261e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
46271e413cf9SAndrew Gallatin 
46281e413cf9SAndrew Gallatin abort_with_msix:
46291e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
46301e413cf9SAndrew Gallatin 
46311e413cf9SAndrew Gallatin abort_with_msix_table:
46321e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
46331e413cf9SAndrew Gallatin 			     sc->msix_table_res);
46341e413cf9SAndrew Gallatin 
46351e413cf9SAndrew Gallatin 	return err;
46361e413cf9SAndrew Gallatin }
46371e413cf9SAndrew Gallatin 
46381e413cf9SAndrew Gallatin static int
46391e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
46401e413cf9SAndrew Gallatin {
46411e413cf9SAndrew Gallatin 	int count, err, rid;
46421e413cf9SAndrew Gallatin 
46431e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
46441e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
46451e413cf9SAndrew Gallatin 		rid = 1;
46461e413cf9SAndrew Gallatin 	} else {
46471e413cf9SAndrew Gallatin 		rid = 0;
464891ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
46491e413cf9SAndrew Gallatin 	}
465043cd6160SJustin Hibbits 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
465143cd6160SJustin Hibbits 					     RF_SHAREABLE | RF_ACTIVE);
46521e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
46531e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
46541e413cf9SAndrew Gallatin 		return ENXIO;
46551e413cf9SAndrew Gallatin 	}
46561e413cf9SAndrew Gallatin 	if (mxge_verbose)
4657da1b038aSJustin Hibbits 		device_printf(sc->dev, "using %s irq %jd\n",
465891ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
46591e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
46601e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
46611e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
466237d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
466337d89b0cSAndrew Gallatin 			     NULL,
466437d89b0cSAndrew Gallatin #endif
466537d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
46661e413cf9SAndrew Gallatin 	if (err != 0) {
46671e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
466891ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
466991ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
46701e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
46711e413cf9SAndrew Gallatin 	}
46721e413cf9SAndrew Gallatin 	return err;
46731e413cf9SAndrew Gallatin }
46741e413cf9SAndrew Gallatin 
46751e413cf9SAndrew Gallatin static void
46761e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
46771e413cf9SAndrew Gallatin {
46781e413cf9SAndrew Gallatin 	int i, rid;
46791e413cf9SAndrew Gallatin 
46801e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46811e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
46821e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
46831e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
46841e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
46851e413cf9SAndrew Gallatin 		}
46861e413cf9SAndrew Gallatin 	}
46871e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
46881e413cf9SAndrew Gallatin 
46891e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46901e413cf9SAndrew Gallatin 		rid = i + 1;
46911e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
46921e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
46931e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
46941e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
46951e413cf9SAndrew Gallatin 	}
46961e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
46971e413cf9SAndrew Gallatin 
46981e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
46991e413cf9SAndrew Gallatin 			     sc->msix_table_res);
47001e413cf9SAndrew Gallatin 
47011e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
47021e413cf9SAndrew Gallatin 	return;
47031e413cf9SAndrew Gallatin }
47041e413cf9SAndrew Gallatin 
47051e413cf9SAndrew Gallatin static void
47061e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
47071e413cf9SAndrew Gallatin {
47081e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
47091e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
471091ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
471191ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
47121e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
47131e413cf9SAndrew Gallatin }
47141e413cf9SAndrew Gallatin 
47151e413cf9SAndrew Gallatin static void
47161e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
47171e413cf9SAndrew Gallatin {
47181e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
47191e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
47201e413cf9SAndrew Gallatin 	else
47211e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
47221e413cf9SAndrew Gallatin }
47231e413cf9SAndrew Gallatin 
47241e413cf9SAndrew Gallatin static int
47251e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
47261e413cf9SAndrew Gallatin {
47271e413cf9SAndrew Gallatin 	int err;
47281e413cf9SAndrew Gallatin 
47291e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
47301e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
47311e413cf9SAndrew Gallatin 	else
47321e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
47331e413cf9SAndrew Gallatin 
47341e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
47351e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
47361e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
47371e413cf9SAndrew Gallatin 	}
47381e413cf9SAndrew Gallatin 	return err;
47391e413cf9SAndrew Gallatin }
47401e413cf9SAndrew Gallatin 
4741b2fc195eSAndrew Gallatin static int
47426d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4743b2fc195eSAndrew Gallatin {
47440a7a780eSAndrew Gallatin 	mxge_cmd_t cmd;
47456d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4746b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
47471e413cf9SAndrew Gallatin 	int err, rid;
4748b2fc195eSAndrew Gallatin 
4749b2fc195eSAndrew Gallatin 	sc->dev = dev;
47506d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4751b2fc195eSAndrew Gallatin 
475272c042dfSAndrew Gallatin 	TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
4753a4a75d67SJohn Baldwin 	sc->tq = taskqueue_create("mxge_taskq", M_WAITOK,
4754a4a75d67SJohn Baldwin 				  taskqueue_thread_enqueue, &sc->tq);
475572c042dfSAndrew Gallatin 	if (sc->tq == NULL) {
475672c042dfSAndrew Gallatin 		err = ENOMEM;
475772c042dfSAndrew Gallatin 		goto abort_with_nothing;
475872c042dfSAndrew Gallatin 	}
475972c042dfSAndrew Gallatin 
476062ce43ccSScott Long 	err = bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
4761b2fc195eSAndrew Gallatin 				 1,			/* alignment */
47621e413cf9SAndrew Gallatin 				 0,			/* boundary */
4763b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4764b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4765b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4766aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
47675e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
47681e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4769b2fc195eSAndrew Gallatin 				 0,			/* flags */
4770b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4771b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4772b2fc195eSAndrew Gallatin 
4773b2fc195eSAndrew Gallatin 	if (err != 0) {
4774b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4775b2fc195eSAndrew Gallatin 			      err);
477672c042dfSAndrew Gallatin 		goto abort_with_tq;
4777b2fc195eSAndrew Gallatin 	}
4778b2fc195eSAndrew Gallatin 
4779b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4780b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4781b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4782b2fc195eSAndrew Gallatin 		err = ENOSPC;
4783b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4784b2fc195eSAndrew Gallatin 	}
47851e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
47861e413cf9SAndrew Gallatin 
4787a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4788a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4789a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4790a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4791a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4792a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4793b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4794b2fc195eSAndrew Gallatin 
4795dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4796d91b1b49SAndrew Gallatin 
4797dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4798b2fc195eSAndrew Gallatin 
4799b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4800b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
480143cd6160SJustin Hibbits 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
480243cd6160SJustin Hibbits 					     RF_ACTIVE);
4803b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4804b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4805b2fc195eSAndrew Gallatin 		err = ENXIO;
4806b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4807b2fc195eSAndrew Gallatin 	}
4808b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4809b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4810b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4811da1b038aSJustin Hibbits 		device_printf(dev, "impossible memory region size %jd\n",
4812b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4813b2fc195eSAndrew Gallatin 		err = ENXIO;
4814b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4815b2fc195eSAndrew Gallatin 	}
4816b2fc195eSAndrew Gallatin 
4817b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4818b2fc195eSAndrew Gallatin 	   lanai SRAM */
48196d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4820b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4821b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
48226d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4823b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
48246d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
48256d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4826b2fc195eSAndrew Gallatin 	if (err != 0)
4827b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4828b2fc195eSAndrew Gallatin 
4829b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
48306d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4831b2fc195eSAndrew Gallatin 
4832b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
48336d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
48346d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4835b2fc195eSAndrew Gallatin 	if (err != 0)
4836b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4837b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
48386d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4839b2fc195eSAndrew Gallatin 	if (err != 0)
4840b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4841b2fc195eSAndrew Gallatin 
4842a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4843a98d6cd7SAndrew Gallatin 	if (err != 0)
48441e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4845b2fc195eSAndrew Gallatin 
48468fe615baSAndrew Gallatin 	/* select & load the firmware */
48478fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4848b2fc195eSAndrew Gallatin 	if (err != 0)
48491e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
48505e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
48511e413cf9SAndrew Gallatin 
48521e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
48531e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
48541e413cf9SAndrew Gallatin 	if (err != 0)
48551e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
48561e413cf9SAndrew Gallatin 
4857adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4858b2fc195eSAndrew Gallatin 	if (err != 0)
48591e413cf9SAndrew Gallatin 		goto abort_with_slices;
4860b2fc195eSAndrew Gallatin 
4861a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4862a98d6cd7SAndrew Gallatin 	if (err != 0) {
4863a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
48642e084798SAndrew Gallatin 		goto abort_with_slices;
4865a98d6cd7SAndrew Gallatin 	}
4866a98d6cd7SAndrew Gallatin 
48671e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4868a98d6cd7SAndrew Gallatin 	if (err != 0) {
48691e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4870a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4871a98d6cd7SAndrew Gallatin 	}
48721e413cf9SAndrew Gallatin 
4873b245f96cSGleb Smirnoff 	ifp->if_baudrate = IF_Gbps(10);
4874c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
487526dd49c6SAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LINKSTATE | IFCAP_TXCSUM_IPV6 |
487626dd49c6SAndrew Gallatin 		IFCAP_RXCSUM_IPV6;
487726dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
4878eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4879eb6219e3SAndrew Gallatin #endif
488037d89b0cSAndrew Gallatin 
488137d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
488237d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
48830dce6781SAndrew Gallatin 
48840dce6781SAndrew Gallatin 	/* Only FW 1.4.32 and newer can do TSO over vlans */
48850dce6781SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
48860dce6781SAndrew Gallatin 	    sc->fw_ver_tiny >= 32)
48870dce6781SAndrew Gallatin 		ifp->if_capabilities |= IFCAP_VLAN_HWTSO;
488837d89b0cSAndrew Gallatin #endif
4889053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4890053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4891053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4892053e637fSAndrew Gallatin 	else
4893053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4894adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4895053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4896aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
48970a7a780eSAndrew Gallatin 	ifp->if_hwassist |= CSUM_TCP_IPV6 | CSUM_UDP_IPV6;
48980a7a780eSAndrew Gallatin 	/* check to see if f/w supports TSO for IPv6 */
48990a7a780eSAndrew Gallatin 	if (!mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd)) {
49000a7a780eSAndrew Gallatin 		if (CSUM_TCP_IPV6)
49010a7a780eSAndrew Gallatin 			ifp->if_capabilities |= IFCAP_TSO6;
49020a7a780eSAndrew Gallatin 		sc->max_tso6_hlen = min(cmd.data0,
49030a7a780eSAndrew Gallatin 					sizeof (sc->ss[0].scratch));
49040a7a780eSAndrew Gallatin 	}
4905b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4906f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4907f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
49086d87a65dSAndrew Gallatin 	ifp->if_init = mxge_init;
4909b2fc195eSAndrew Gallatin 	ifp->if_softc = sc;
4910b2fc195eSAndrew Gallatin 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
49116d87a65dSAndrew Gallatin 	ifp->if_ioctl = mxge_ioctl;
49126d87a65dSAndrew Gallatin 	ifp->if_start = mxge_start;
4913f3f040d9SGleb Smirnoff 	ifp->if_get_counter = mxge_get_counter;
49143d07f894SAndrew Gallatin 	ifp->if_hw_tsomax = IP_MAXPACKET - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
49157eafc4d5SAndrew Gallatin 	ifp->if_hw_tsomaxsegcount = sc->ss[0].tx.max_desc;
49163d07f894SAndrew Gallatin 	ifp->if_hw_tsomaxsegsize = IP_MAXPACKET;
4917c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4918c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4919c587e59fSAndrew Gallatin 		     mxge_media_status);
4920c406ad2eSAndrew Gallatin 	mxge_media_init(sc);
4921c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
49228c5d766cSAndrew Gallatin 	sc->dying = 0;
4923b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4924f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4925f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4926f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4927b2fc195eSAndrew Gallatin 
49286d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4929c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4930c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4931c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4932c6cb3e3fSAndrew Gallatin #endif
49332e084798SAndrew Gallatin 	taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
49342e084798SAndrew Gallatin 				device_get_nameunit(sc->dev));
49356b484a49SAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4936b2fc195eSAndrew Gallatin 	return 0;
4937b2fc195eSAndrew Gallatin 
4938a98d6cd7SAndrew Gallatin abort_with_rings:
4939a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
49401e413cf9SAndrew Gallatin abort_with_slices:
49411e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4942a98d6cd7SAndrew Gallatin abort_with_dmabench:
4943a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4944b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
49456d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4946b2fc195eSAndrew Gallatin abort_with_cmd_dma:
49476d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4948b2fc195eSAndrew Gallatin abort_with_mem_res:
4949b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4950b2fc195eSAndrew Gallatin abort_with_lock:
4951b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4952a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4953a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4954b2fc195eSAndrew Gallatin 	if_free(ifp);
4955b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4956b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
495772c042dfSAndrew Gallatin abort_with_tq:
495872c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
495972c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
496072c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
496172c042dfSAndrew Gallatin 		sc->tq = NULL;
496272c042dfSAndrew Gallatin 	}
4963b2fc195eSAndrew Gallatin abort_with_nothing:
4964b2fc195eSAndrew Gallatin 	return err;
4965b2fc195eSAndrew Gallatin }
4966b2fc195eSAndrew Gallatin 
4967b2fc195eSAndrew Gallatin static int
49686d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4969b2fc195eSAndrew Gallatin {
49706d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4971b2fc195eSAndrew Gallatin 
497237d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4973c792928fSAndrew Gallatin 		device_printf(sc->dev,
4974c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4975c792928fSAndrew Gallatin 		return EBUSY;
4976c792928fSAndrew Gallatin 	}
4977a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
49788c5d766cSAndrew Gallatin 	sc->dying = 1;
4979b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
4980a393336bSAndrew Gallatin 		mxge_close(sc, 0);
4981a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4982b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
498372c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
498472c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
498572c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
498672c042dfSAndrew Gallatin 		sc->tq = NULL;
498772c042dfSAndrew Gallatin 	}
4988e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4989dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4990091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
49911e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
49921e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4993a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
49941e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4995a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
49966d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
49976d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4998b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4999b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
5000a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
5001a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
5002b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
5003b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
5004b2fc195eSAndrew Gallatin 	return 0;
5005b2fc195eSAndrew Gallatin }
5006b2fc195eSAndrew Gallatin 
5007b2fc195eSAndrew Gallatin static int
50086d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
5009b2fc195eSAndrew Gallatin {
5010b2fc195eSAndrew Gallatin 	return 0;
5011b2fc195eSAndrew Gallatin }
5012b2fc195eSAndrew Gallatin 
5013b2fc195eSAndrew Gallatin /*
5014b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
5015b2fc195eSAndrew Gallatin 
5016b2fc195eSAndrew Gallatin   Local Variables:
5017b2fc195eSAndrew Gallatin   c-file-style:"linux"
5018b2fc195eSAndrew Gallatin   tab-width:8
5019b2fc195eSAndrew Gallatin   End:
5020b2fc195eSAndrew Gallatin */
5021