xref: /freebsd/sys/dev/mxge/if_mxge.c (revision cbb9ccf735e9d58e2f986408b368fac5654bd729)
16d87a65dSAndrew Gallatin /******************************************************************************
2718cf2ccSPedro F. Giffuni SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3b2fc195eSAndrew Gallatin 
4cabc512fSAndrew Gallatin Copyright (c) 2006-2013, Myricom Inc.
5b2fc195eSAndrew Gallatin All rights reserved.
6b2fc195eSAndrew Gallatin 
7b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
8b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
9b2fc195eSAndrew Gallatin 
10b2fc195eSAndrew Gallatin  1. Redistributions of source code must retain the above copyright notice,
11b2fc195eSAndrew Gallatin     this list of conditions and the following disclaimer.
12b2fc195eSAndrew Gallatin 
13eb8e82f5SAndrew Gallatin  2. Neither the name of the Myricom Inc, nor the names of its
14b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
15b2fc195eSAndrew Gallatin     this software without specific prior written permission.
16b2fc195eSAndrew Gallatin 
17b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
28b2fc195eSAndrew Gallatin 
29b2fc195eSAndrew Gallatin ***************************************************************************/
30b2fc195eSAndrew Gallatin 
31b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
32b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
33b2fc195eSAndrew Gallatin 
34b2fc195eSAndrew Gallatin #include <sys/param.h>
35b2fc195eSAndrew Gallatin #include <sys/systm.h>
36b2fc195eSAndrew Gallatin #include <sys/linker.h>
37b2fc195eSAndrew Gallatin #include <sys/firmware.h>
38b2fc195eSAndrew Gallatin #include <sys/endian.h>
39b2fc195eSAndrew Gallatin #include <sys/sockio.h>
40b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
41b2fc195eSAndrew Gallatin #include <sys/malloc.h>
42b2fc195eSAndrew Gallatin #include <sys/kdb.h>
43b2fc195eSAndrew Gallatin #include <sys/kernel.h>
444e7f640dSJohn Baldwin #include <sys/lock.h>
45b2fc195eSAndrew Gallatin #include <sys/module.h>
46b2fc195eSAndrew Gallatin #include <sys/socket.h>
47b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
48b2fc195eSAndrew Gallatin #include <sys/sx.h>
4972c042dfSAndrew Gallatin #include <sys/taskqueue.h>
501dbf944aSXin LI #include <contrib/zlib/zlib.h>
511dbf944aSXin LI #include <dev/zlib/zcalloc.h>
52b2fc195eSAndrew Gallatin 
53b2fc195eSAndrew Gallatin #include <net/if.h>
5476039bc8SGleb Smirnoff #include <net/if_var.h>
55b2fc195eSAndrew Gallatin #include <net/if_arp.h>
56b2fc195eSAndrew Gallatin #include <net/ethernet.h>
57b2fc195eSAndrew Gallatin #include <net/if_dl.h>
58b2fc195eSAndrew Gallatin #include <net/if_media.h>
59b2fc195eSAndrew Gallatin 
60b2fc195eSAndrew Gallatin #include <net/bpf.h>
61b2fc195eSAndrew Gallatin 
62b2fc195eSAndrew Gallatin #include <net/if_types.h>
63b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
64b2fc195eSAndrew Gallatin 
65b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
66b2fc195eSAndrew Gallatin #include <netinet/in.h>
67b2fc195eSAndrew Gallatin #include <netinet/ip.h>
680a7a780eSAndrew Gallatin #include <netinet/ip6.h>
69aed8e389SAndrew Gallatin #include <netinet/tcp.h>
7026dd49c6SAndrew Gallatin #include <netinet/tcp_lro.h>
710a7a780eSAndrew Gallatin #include <netinet6/ip6_var.h>
72b2fc195eSAndrew Gallatin 
73b2fc195eSAndrew Gallatin #include <machine/bus.h>
74053e637fSAndrew Gallatin #include <machine/in_cksum.h>
75b2fc195eSAndrew Gallatin #include <machine/resource.h>
76b2fc195eSAndrew Gallatin #include <sys/bus.h>
77b2fc195eSAndrew Gallatin #include <sys/rman.h>
781e413cf9SAndrew Gallatin #include <sys/smp.h>
79b2fc195eSAndrew Gallatin 
80b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
81b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
82e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
83b2fc195eSAndrew Gallatin 
84b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
85b2fc195eSAndrew Gallatin #include <vm/pmap.h>
86b2fc195eSAndrew Gallatin 
87c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
88c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
89c2c14a69SAndrew Gallatin #endif
90c2c14a69SAndrew Gallatin 
916d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
926d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
931e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
946d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
95869c7348SAndrew Gallatin #ifdef IFNET_BUF_RING
96869c7348SAndrew Gallatin #include <sys/buf_ring.h>
97869c7348SAndrew Gallatin #endif
98b2fc195eSAndrew Gallatin 
99eb6219e3SAndrew Gallatin #include "opt_inet.h"
1000a7a780eSAndrew Gallatin #include "opt_inet6.h"
101eb6219e3SAndrew Gallatin 
102b2fc195eSAndrew Gallatin /* tunable params */
1036d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
104d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
1056d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1065e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1076d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1085e7d8541SAndrew Gallatin static int mxge_verbose = 0;
109dce01b9bSAndrew Gallatin static int mxge_ticks;
1101e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1115769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
1121e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
113f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
11465c69066SAndrew Gallatin static int mxge_throttle = 0;
1156d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1166d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1171e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1181e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
119b2fc195eSAndrew Gallatin 
1206d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1216d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1226d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1236d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1246d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
125b2fc195eSAndrew Gallatin 
1266d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
127b2fc195eSAndrew Gallatin {
128b2fc195eSAndrew Gallatin   /* Device interface */
1296d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1306d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1316d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1326d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
13361bfd867SSofian Brabez 
13461bfd867SSofian Brabez   DEVMETHOD_END
135b2fc195eSAndrew Gallatin };
136b2fc195eSAndrew Gallatin 
1376d87a65dSAndrew Gallatin static driver_t mxge_driver =
138b2fc195eSAndrew Gallatin {
1396d87a65dSAndrew Gallatin   "mxge",
1406d87a65dSAndrew Gallatin   mxge_methods,
1416d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
142b2fc195eSAndrew Gallatin };
143b2fc195eSAndrew Gallatin 
1446d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
145b2fc195eSAndrew Gallatin 
146b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1476d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1486d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
149f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
150b2fc195eSAndrew Gallatin 
1511e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1528fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
153a393336bSAndrew Gallatin static int mxge_close(mxge_softc_t *sc, int down);
154276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
155276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1568fe615baSAndrew Gallatin 
157b2fc195eSAndrew Gallatin static int
1586d87a65dSAndrew Gallatin mxge_probe(device_t dev)
159b2fc195eSAndrew Gallatin {
16001638550SAndrew Gallatin 	int rev;
16101638550SAndrew Gallatin 
16201638550SAndrew Gallatin 
1636d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
164f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
165f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
16601638550SAndrew Gallatin 		rev = pci_get_revid(dev);
16701638550SAndrew Gallatin 		switch (rev) {
16801638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
169b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
17001638550SAndrew Gallatin 			break;
17101638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
17201638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
17301638550SAndrew Gallatin 			break;
17401638550SAndrew Gallatin 		default:
17501638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
17601638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
17701638550SAndrew Gallatin 				      rev);
17801638550SAndrew Gallatin 			break;
17901638550SAndrew Gallatin 		}
180b2fc195eSAndrew Gallatin 		return 0;
181b2fc195eSAndrew Gallatin 	}
182b2fc195eSAndrew Gallatin 	return ENXIO;
183b2fc195eSAndrew Gallatin }
184b2fc195eSAndrew Gallatin 
185b2fc195eSAndrew Gallatin static void
1866d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
187b2fc195eSAndrew Gallatin {
188f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
189b2fc195eSAndrew Gallatin 	vm_offset_t len;
19047c2e987SAndrew Gallatin 	int err;
191b2fc195eSAndrew Gallatin 
1924d69a9d0SAndrew Gallatin 	sc->wc = 1;
193b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
194c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
195c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
19647c2e987SAndrew Gallatin 	if (err != 0) {
197c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
198c2c14a69SAndrew Gallatin 			      err);
1994d69a9d0SAndrew Gallatin 		sc->wc = 0;
200b2fc195eSAndrew Gallatin 	}
201f9ae0280SAndrew Gallatin #endif
202b2fc195eSAndrew Gallatin }
203b2fc195eSAndrew Gallatin 
204b2fc195eSAndrew Gallatin 
205b2fc195eSAndrew Gallatin /* callback to get our DMA address */
206b2fc195eSAndrew Gallatin static void
2076d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
208b2fc195eSAndrew Gallatin 			 int error)
209b2fc195eSAndrew Gallatin {
210b2fc195eSAndrew Gallatin 	if (error == 0) {
211b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
212b2fc195eSAndrew Gallatin 	}
213b2fc195eSAndrew Gallatin }
214b2fc195eSAndrew Gallatin 
215b2fc195eSAndrew Gallatin static int
2166d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
217b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
218b2fc195eSAndrew Gallatin {
219b2fc195eSAndrew Gallatin 	int err;
220b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2211e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2221e413cf9SAndrew Gallatin 
2231e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2241e413cf9SAndrew Gallatin 		boundary = 0;
2251e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2261e413cf9SAndrew Gallatin 	} else {
2271e413cf9SAndrew Gallatin 		boundary = 4096;
2281e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2291e413cf9SAndrew Gallatin 	}
230b2fc195eSAndrew Gallatin 
231b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
232b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
233b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2341e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
235b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
236b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
237b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
238b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
239b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2401e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
241b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
242b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
243b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
244b2fc195eSAndrew Gallatin 	if (err != 0) {
245b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
246b2fc195eSAndrew Gallatin 		return err;
247b2fc195eSAndrew Gallatin 	}
248b2fc195eSAndrew Gallatin 
249b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
250b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
251b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
252b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
253b2fc195eSAndrew Gallatin 	if (err != 0) {
254b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
255b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
256b2fc195eSAndrew Gallatin 	}
257b2fc195eSAndrew Gallatin 
258b2fc195eSAndrew Gallatin 	/* load the memory */
259b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2606d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
261b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
262b2fc195eSAndrew Gallatin 	if (err != 0) {
263b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
264b2fc195eSAndrew Gallatin 		goto abort_with_mem;
265b2fc195eSAndrew Gallatin 	}
266b2fc195eSAndrew Gallatin 	return 0;
267b2fc195eSAndrew Gallatin 
268b2fc195eSAndrew Gallatin abort_with_mem:
269b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
270b2fc195eSAndrew Gallatin abort_with_dmat:
271b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
272b2fc195eSAndrew Gallatin 	return err;
273b2fc195eSAndrew Gallatin }
274b2fc195eSAndrew Gallatin 
275b2fc195eSAndrew Gallatin 
276b2fc195eSAndrew Gallatin static void
2776d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
278b2fc195eSAndrew Gallatin {
279b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
280b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
281b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
282b2fc195eSAndrew Gallatin }
283b2fc195eSAndrew Gallatin 
284b2fc195eSAndrew Gallatin /*
285b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
286b2fc195eSAndrew Gallatin  * SN=x\0
287b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
288b2fc195eSAndrew Gallatin  * PC=text\0
289b2fc195eSAndrew Gallatin  */
290b2fc195eSAndrew Gallatin 
291b2fc195eSAndrew Gallatin static int
2926d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
293b2fc195eSAndrew Gallatin {
294dedbe836SAndrew Gallatin 	char *ptr;
295a4b233ddSAndrew Gallatin 	int i, found_mac, found_sn2;
296dedbe836SAndrew Gallatin 	char *endptr;
297b2fc195eSAndrew Gallatin 
298b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
299b2fc195eSAndrew Gallatin 	found_mac = 0;
300a4b233ddSAndrew Gallatin 	found_sn2 = 0;
301dedbe836SAndrew Gallatin 	while (*ptr != '\0') {
302dedbe836SAndrew Gallatin 		if (strncmp(ptr, "MAC=", 4) == 0) {
303dedbe836SAndrew Gallatin 			ptr += 4;
304dedbe836SAndrew Gallatin 			for (i = 0;;) {
305dedbe836SAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
306dedbe836SAndrew Gallatin 				if (endptr - ptr != 2)
307b2fc195eSAndrew Gallatin 					goto abort;
308dedbe836SAndrew Gallatin 				ptr = endptr;
309dedbe836SAndrew Gallatin 				if (++i == 6)
310dedbe836SAndrew Gallatin 					break;
311dedbe836SAndrew Gallatin 				if (*ptr++ != ':')
312dedbe836SAndrew Gallatin 					goto abort;
313b2fc195eSAndrew Gallatin 			}
314dedbe836SAndrew Gallatin 			found_mac = 1;
315dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "PC=", 3) == 0) {
3165e7d8541SAndrew Gallatin 			ptr += 3;
317dedbe836SAndrew Gallatin 			strlcpy(sc->product_code_string, ptr,
318dedbe836SAndrew Gallatin 			    sizeof(sc->product_code_string));
319dedbe836SAndrew Gallatin 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
3205e7d8541SAndrew Gallatin 			ptr += 3;
321dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
322dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
323dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
324a4b233ddSAndrew Gallatin 			/* SN2 takes precedence over SN */
325a4b233ddSAndrew Gallatin 			ptr += 4;
326a4b233ddSAndrew Gallatin 			found_sn2 = 1;
327dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
328dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
329b2fc195eSAndrew Gallatin 		}
330dedbe836SAndrew Gallatin 		while (*ptr++ != '\0') {}
331b2fc195eSAndrew Gallatin 	}
332b2fc195eSAndrew Gallatin 
333b2fc195eSAndrew Gallatin 	if (found_mac)
334b2fc195eSAndrew Gallatin 		return 0;
335b2fc195eSAndrew Gallatin 
336b2fc195eSAndrew Gallatin  abort:
337b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
338b2fc195eSAndrew Gallatin 
339b2fc195eSAndrew Gallatin 	return ENXIO;
340b2fc195eSAndrew Gallatin }
341b2fc195eSAndrew Gallatin 
3420d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3438fe615baSAndrew Gallatin static void
3448fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
345b2fc195eSAndrew Gallatin {
346b2fc195eSAndrew Gallatin 	uint32_t val;
3478fe615baSAndrew Gallatin 	unsigned long base, off;
348b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3498fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3508fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
351b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
352b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
353b2fc195eSAndrew Gallatin 
3548fe615baSAndrew Gallatin 
3558fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3568fe615baSAndrew Gallatin 		return;
3578fe615baSAndrew Gallatin 
3588fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3598fe615baSAndrew Gallatin 	if (pdev == NULL) {
3608fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3618fe615baSAndrew Gallatin 		return;
3628fe615baSAndrew Gallatin 	}
3638fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3648fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3658fe615baSAndrew Gallatin 
3668fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3678fe615baSAndrew Gallatin 		return;
3688fe615baSAndrew Gallatin 
3698fe615baSAndrew Gallatin 	base = 0;
3708fe615baSAndrew Gallatin 
3718fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3728fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3738fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3748fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3758fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3768fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3778fe615baSAndrew Gallatin 		if (mcp55 &&
3788fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3798fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3808fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3818fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3828fe615baSAndrew Gallatin 		}
3838fe615baSAndrew Gallatin 	}
3848fe615baSAndrew Gallatin 	if (!base)
3858fe615baSAndrew Gallatin 		return;
3868fe615baSAndrew Gallatin 
387b2fc195eSAndrew Gallatin 	/* XXXX
388b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
389b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
390b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
391b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
392b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
393b2fc195eSAndrew Gallatin 	*/
394b2fc195eSAndrew Gallatin #if 0
395b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
396b2fc195eSAndrew Gallatin 	   config space */
397b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
398b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
399b2fc195eSAndrew Gallatin 		val |= 0x40;
400b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
4018fe615baSAndrew Gallatin 		return;
402b2fc195eSAndrew Gallatin 	}
403b2fc195eSAndrew Gallatin #endif
404b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
405b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
406b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
407b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
408b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
409b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
410b2fc195eSAndrew Gallatin 	 */
411b2fc195eSAndrew Gallatin 
412b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
413b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
414b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
415b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
416b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
417b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
418b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
419b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
420b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
421b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
422b2fc195eSAndrew Gallatin 
4238fe615baSAndrew Gallatin 	off =  base
424b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
425b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
426b2fc195eSAndrew Gallatin 						 + 8 * slot);
427b2fc195eSAndrew Gallatin 
428b2fc195eSAndrew Gallatin 	/* map it into the kernel */
429b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
430b2fc195eSAndrew Gallatin 
431b2fc195eSAndrew Gallatin 
432b2fc195eSAndrew Gallatin 	if (va == NULL) {
433b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4348fe615baSAndrew Gallatin 		return;
435b2fc195eSAndrew Gallatin 	}
436b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
437b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
438b2fc195eSAndrew Gallatin 
439b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
440b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
441b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
442b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
443b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
444b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
445b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4468fe615baSAndrew Gallatin 		return;
447b2fc195eSAndrew Gallatin 	}
448b2fc195eSAndrew Gallatin 
449b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
450b2fc195eSAndrew Gallatin 	val = *ptr32;
451b2fc195eSAndrew Gallatin 
452b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
453b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
454b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4558fe615baSAndrew Gallatin 		return;
456b2fc195eSAndrew Gallatin 	}
457b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
458b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4595e7d8541SAndrew Gallatin 	if (mxge_verbose)
460b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4615e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4625e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
463b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4648fe615baSAndrew Gallatin 	return;
465b2fc195eSAndrew Gallatin }
466b2fc195eSAndrew Gallatin #else
4678fe615baSAndrew Gallatin static void
468f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
469b2fc195eSAndrew Gallatin {
470b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
471b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4728fe615baSAndrew Gallatin 	return;
473b2fc195eSAndrew Gallatin }
474b2fc195eSAndrew Gallatin #endif
4758fe615baSAndrew Gallatin 
4768fe615baSAndrew Gallatin 
4778fe615baSAndrew Gallatin static int
4788fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4798fe615baSAndrew Gallatin {
4808fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4818fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4828fe615baSAndrew Gallatin 	int status;
4838fe615baSAndrew Gallatin 	uint32_t len;
4848fe615baSAndrew Gallatin 	char *test = " ";
4858fe615baSAndrew Gallatin 
4868fe615baSAndrew Gallatin 
4878fe615baSAndrew Gallatin 	/* Run a small DMA test.
4888fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4898fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4908fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4918fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4928fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4938fe615baSAndrew Gallatin 	 * transfers took to complete.
4948fe615baSAndrew Gallatin 	 */
4958fe615baSAndrew Gallatin 
4961e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4978fe615baSAndrew Gallatin 
4988fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4998fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5008fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
5018fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5028fe615baSAndrew Gallatin 	if (status != 0) {
5038fe615baSAndrew Gallatin 		test = "read";
5048fe615baSAndrew Gallatin 		goto abort;
5058fe615baSAndrew Gallatin 	}
5068fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
5078fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5088fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5098fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5108fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
5118fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5128fe615baSAndrew Gallatin 	if (status != 0) {
5138fe615baSAndrew Gallatin 		test = "write";
5148fe615baSAndrew Gallatin 		goto abort;
5158fe615baSAndrew Gallatin 	}
5168fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5178fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5188fe615baSAndrew Gallatin 
5198fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5208fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5218fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5228fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5238fe615baSAndrew Gallatin 	if (status != 0) {
5248fe615baSAndrew Gallatin 		test = "read/write";
5258fe615baSAndrew Gallatin 		goto abort;
5268fe615baSAndrew Gallatin 	}
5278fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5288fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5298fe615baSAndrew Gallatin 
5308fe615baSAndrew Gallatin abort:
5318fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5328fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5338fe615baSAndrew Gallatin 			      test, status);
5348fe615baSAndrew Gallatin 
5358fe615baSAndrew Gallatin 	return status;
5368fe615baSAndrew Gallatin }
5378fe615baSAndrew Gallatin 
538b2fc195eSAndrew Gallatin /*
539b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
540b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
541b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
542b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
543b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
544b2fc195eSAndrew Gallatin  *
545b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
546b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
547b2fc195eSAndrew Gallatin  *
548b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
549b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
550b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
551b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5521e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
553b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5541e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
555b2fc195eSAndrew Gallatin  */
556b2fc195eSAndrew Gallatin 
5578fe615baSAndrew Gallatin static int
5588fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5598fe615baSAndrew Gallatin {
5608fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5618fe615baSAndrew Gallatin 	int reg, status;
5628fe615baSAndrew Gallatin 	uint16_t pectl;
5638fe615baSAndrew Gallatin 
5641e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5658fe615baSAndrew Gallatin 	/*
5668fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5678fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5688fe615baSAndrew Gallatin 	 */
5693b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
5708fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5718fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5728fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5738fe615baSAndrew Gallatin 				      pectl);
5741e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5758fe615baSAndrew Gallatin 		}
5768fe615baSAndrew Gallatin 	}
5778fe615baSAndrew Gallatin 
5788fe615baSAndrew Gallatin 	/*
5798fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5808fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5818fe615baSAndrew Gallatin 	 */
5828fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5831e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5848fe615baSAndrew Gallatin 	if (status != 0) {
5858fe615baSAndrew Gallatin 		return status;
5868fe615baSAndrew Gallatin 	}
5878fe615baSAndrew Gallatin 
5888fe615baSAndrew Gallatin 	/*
5898fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5908fe615baSAndrew Gallatin 	 */
5918fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5928fe615baSAndrew Gallatin 
5938fe615baSAndrew Gallatin 	/*
5948fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
595a4b233ddSAndrew Gallatin 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5968fe615baSAndrew Gallatin 	 */
597a4b233ddSAndrew Gallatin 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
598a4b233ddSAndrew Gallatin 		return 0;
5998fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
6008fe615baSAndrew Gallatin 	if (status == 0)
6018fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
6028fe615baSAndrew Gallatin 
6038fe615baSAndrew Gallatin 	if (status != E2BIG)
6048fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
6058fe615baSAndrew Gallatin 	if (status == ENOSYS)
6068fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
6078fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
6088fe615baSAndrew Gallatin 	return status;
6098fe615baSAndrew Gallatin }
6108fe615baSAndrew Gallatin 
6118fe615baSAndrew Gallatin static int
6126d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
613b2fc195eSAndrew Gallatin {
6148fe615baSAndrew Gallatin 	int aligned = 0;
61565c69066SAndrew Gallatin 	int force_firmware = mxge_force_firmware;
616b2fc195eSAndrew Gallatin 
61765c69066SAndrew Gallatin 	if (sc->throttle)
61865c69066SAndrew Gallatin 		force_firmware = sc->throttle;
619d91b1b49SAndrew Gallatin 
62065c69066SAndrew Gallatin 	if (force_firmware != 0) {
62165c69066SAndrew Gallatin 		if (force_firmware == 1)
622d91b1b49SAndrew Gallatin 			aligned = 1;
623d91b1b49SAndrew Gallatin 		else
624d91b1b49SAndrew Gallatin 			aligned = 0;
625d91b1b49SAndrew Gallatin 		if (mxge_verbose)
626d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
627d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
628d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
629d91b1b49SAndrew Gallatin 		goto abort;
630d91b1b49SAndrew Gallatin 	}
631d91b1b49SAndrew Gallatin 
632d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
633d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
634d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
635d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
636d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
637d91b1b49SAndrew Gallatin 			      sc->link_width);
638d91b1b49SAndrew Gallatin 		aligned = 1;
639d91b1b49SAndrew Gallatin 		goto abort;
640d91b1b49SAndrew Gallatin 	}
641d91b1b49SAndrew Gallatin 
6428fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6438fe615baSAndrew Gallatin 		return 0;
644b2fc195eSAndrew Gallatin 
645b2fc195eSAndrew Gallatin abort:
646b2fc195eSAndrew Gallatin 	if (aligned) {
6476d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6481e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
649b2fc195eSAndrew Gallatin 	} else {
6506d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6511e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
652b2fc195eSAndrew Gallatin 	}
6531e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
654b2fc195eSAndrew Gallatin }
655b2fc195eSAndrew Gallatin 
6564da0d523SAndrew Gallatin static int
6574da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6584da0d523SAndrew Gallatin {
659b824b7d8SAndrew Gallatin 
6604da0d523SAndrew Gallatin 
6614da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6624da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6634da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6644da0d523SAndrew Gallatin 		return EIO;
6654da0d523SAndrew Gallatin 	}
6664da0d523SAndrew Gallatin 
6674da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
668dedbe836SAndrew Gallatin 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6694da0d523SAndrew Gallatin 	if (mxge_verbose)
6704da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6714da0d523SAndrew Gallatin 
672b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
673b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6744da0d523SAndrew Gallatin 
675b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
676b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6774da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6784da0d523SAndrew Gallatin 			      sc->fw_version);
6794da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6804da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6814da0d523SAndrew Gallatin 		return EINVAL;
6824da0d523SAndrew Gallatin 	}
6834da0d523SAndrew Gallatin 	return 0;
6844da0d523SAndrew Gallatin 
6854da0d523SAndrew Gallatin }
686b2fc195eSAndrew Gallatin 
687b2fc195eSAndrew Gallatin static int
6886d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
689b2fc195eSAndrew Gallatin {
690f9ae0280SAndrew Gallatin 	z_stream zs;
691f9ae0280SAndrew Gallatin 	char *inflate_buffer;
69233d54970SLuigi Rizzo 	const struct firmware *fw;
693b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
694b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
695b2fc195eSAndrew Gallatin 	int status;
6964da0d523SAndrew Gallatin 	unsigned int i;
6974da0d523SAndrew Gallatin 	char dummy;
698f9ae0280SAndrew Gallatin 	size_t fw_len;
699b2fc195eSAndrew Gallatin 
700b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
701b2fc195eSAndrew Gallatin 	if (fw == NULL) {
702b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
703b2fc195eSAndrew Gallatin 			      sc->fw_name);
704b2fc195eSAndrew Gallatin 		return ENOENT;
705b2fc195eSAndrew Gallatin 	}
706b2fc195eSAndrew Gallatin 
707f9ae0280SAndrew Gallatin 
708f9ae0280SAndrew Gallatin 
709f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
710f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
7111dbf944aSXin LI 	zs.zalloc = zcalloc_nowait;
7121dbf944aSXin LI 	zs.zfree = zcfree;
713f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
714f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
715b2fc195eSAndrew Gallatin 		status = EIO;
716b2fc195eSAndrew Gallatin 		goto abort_with_fw;
717b2fc195eSAndrew Gallatin 	}
718f9ae0280SAndrew Gallatin 
719f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
720f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
721f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
722f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
723f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
724f9ae0280SAndrew Gallatin 		goto abort_with_zs;
725f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
726f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
727f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
728f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
729f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
730f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
731f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
732f9ae0280SAndrew Gallatin 		status = EIO;
733f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
734f9ae0280SAndrew Gallatin 	}
735f9ae0280SAndrew Gallatin 
736f9ae0280SAndrew Gallatin 	/* check id */
737f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
738f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
739f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
740f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
741f9ae0280SAndrew Gallatin 		status = EIO;
742f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
743f9ae0280SAndrew Gallatin 	}
744f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
745b2fc195eSAndrew Gallatin 
7464da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7474da0d523SAndrew Gallatin 	if (status != 0)
748f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
749b2fc195eSAndrew Gallatin 
750b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
751f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7524da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
753f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
754f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
75573c7c83fSAndrew Gallatin 		wmb();
7564da0d523SAndrew Gallatin 		dummy = *sc->sram;
75773c7c83fSAndrew Gallatin 		wmb();
7584da0d523SAndrew Gallatin 	}
759b2fc195eSAndrew Gallatin 
760f9ae0280SAndrew Gallatin 	*limit = fw_len;
761b2fc195eSAndrew Gallatin 	status = 0;
762f9ae0280SAndrew Gallatin abort_with_buffer:
763f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
764f9ae0280SAndrew Gallatin abort_with_zs:
765f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
766b2fc195eSAndrew Gallatin abort_with_fw:
767b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
768b2fc195eSAndrew Gallatin 	return status;
769b2fc195eSAndrew Gallatin }
770b2fc195eSAndrew Gallatin 
771b2fc195eSAndrew Gallatin /*
772b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
773b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
774b2fc195eSAndrew Gallatin  */
775b2fc195eSAndrew Gallatin 
776b2fc195eSAndrew Gallatin static void
7776d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
778b2fc195eSAndrew Gallatin {
779b2fc195eSAndrew Gallatin 	char buf_bytes[72];
780b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
781b2fc195eSAndrew Gallatin 	volatile char *submit;
782b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
783b2fc195eSAndrew Gallatin 	int i;
784b2fc195eSAndrew Gallatin 
785b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
786b2fc195eSAndrew Gallatin 
787b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
788b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
789b2fc195eSAndrew Gallatin 	*confirm = 0;
79073c7c83fSAndrew Gallatin 	wmb();
791b2fc195eSAndrew Gallatin 
792b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
793b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
794b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
795b2fc195eSAndrew Gallatin 	*/
796b2fc195eSAndrew Gallatin 
7976d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7986d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
799b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
800b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
801b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
8026d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
8036d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
804b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
805b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
806b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
807b2fc195eSAndrew Gallatin 
808b2fc195eSAndrew Gallatin 
8090fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
810b2fc195eSAndrew Gallatin 
8116d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
81273c7c83fSAndrew Gallatin 	wmb();
813b2fc195eSAndrew Gallatin 	DELAY(1000);
81473c7c83fSAndrew Gallatin 	wmb();
815b2fc195eSAndrew Gallatin 	i = 0;
816b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
817b2fc195eSAndrew Gallatin 		DELAY(1000);
818b2fc195eSAndrew Gallatin 		i++;
819b2fc195eSAndrew Gallatin 	}
820b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
821b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
822b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
823b2fc195eSAndrew Gallatin 			      *confirm);
824b2fc195eSAndrew Gallatin 	}
825b2fc195eSAndrew Gallatin 	return;
826b2fc195eSAndrew Gallatin }
827b2fc195eSAndrew Gallatin 
828b2fc195eSAndrew Gallatin static int
8296d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
830b2fc195eSAndrew Gallatin {
831b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
832b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
833b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8340fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
835b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
836e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
837b2fc195eSAndrew Gallatin 
838b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
839b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
840b2fc195eSAndrew Gallatin 
841b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
842b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
843b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
844b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8456d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8466d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
847b2fc195eSAndrew Gallatin 
848b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
849b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
850a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
851b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
85273c7c83fSAndrew Gallatin 	wmb();
8536d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
854b2fc195eSAndrew Gallatin 
8555e7d8541SAndrew Gallatin 	/* wait up to 20ms */
856e0501fd0SAndrew Gallatin 	err = EAGAIN;
8575e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
858b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
859b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
86073c7c83fSAndrew Gallatin 		wmb();
861e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
862e0501fd0SAndrew Gallatin 		case 0:
863b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
864e0501fd0SAndrew Gallatin 			err = 0;
865e0501fd0SAndrew Gallatin 			break;
866e0501fd0SAndrew Gallatin 		case 0xffffffff:
867e0501fd0SAndrew Gallatin 			DELAY(1000);
868e0501fd0SAndrew Gallatin 			break;
869e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
870e0501fd0SAndrew Gallatin 			err = ENOSYS;
871e0501fd0SAndrew Gallatin 			break;
872e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
873e0501fd0SAndrew Gallatin 			err = E2BIG;
874e0501fd0SAndrew Gallatin 			break;
875c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
876c587e59fSAndrew Gallatin 			err = EBUSY;
877c587e59fSAndrew Gallatin 			break;
878c406ad2eSAndrew Gallatin 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
879c406ad2eSAndrew Gallatin 			err = ENXIO;
880c406ad2eSAndrew Gallatin 			break;
881e0501fd0SAndrew Gallatin 		default:
882b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8836d87a65dSAndrew Gallatin 				      "mxge: command %d "
884b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
885b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
886e0501fd0SAndrew Gallatin 			err = ENXIO;
887e0501fd0SAndrew Gallatin 			break;
888b2fc195eSAndrew Gallatin 		}
889e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
890e0501fd0SAndrew Gallatin 			break;
891b2fc195eSAndrew Gallatin 	}
892e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8936d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
894b2fc195eSAndrew Gallatin 			      "result = %d\n",
895b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
896e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
897e0501fd0SAndrew Gallatin 	return err;
898b2fc195eSAndrew Gallatin }
899b2fc195eSAndrew Gallatin 
9004da0d523SAndrew Gallatin static int
9014da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
9024da0d523SAndrew Gallatin {
9034da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
9044da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
9054da0d523SAndrew Gallatin 	size_t hdr_offset;
9064da0d523SAndrew Gallatin 	int status;
9074da0d523SAndrew Gallatin 
9084da0d523SAndrew Gallatin 	/* find running firmware header */
9094da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9104da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9114da0d523SAndrew Gallatin 
9124da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9134da0d523SAndrew Gallatin 		device_printf(sc->dev,
9144da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9154da0d523SAndrew Gallatin 			      (int)hdr_offset);
9164da0d523SAndrew Gallatin 		return EIO;
9174da0d523SAndrew Gallatin 	}
9184da0d523SAndrew Gallatin 
9194da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9204da0d523SAndrew Gallatin 	 * validate firmware */
9214da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9224da0d523SAndrew Gallatin 	if (hdr == NULL) {
9234da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9244da0d523SAndrew Gallatin 		return ENOMEM;
9254da0d523SAndrew Gallatin 	}
9264da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9274da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9284da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9294da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9304da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
931b824b7d8SAndrew Gallatin 
932b824b7d8SAndrew Gallatin 	/*
933b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
934b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
935b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
936b824b7d8SAndrew Gallatin 	 */
937b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
938b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
939b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
940b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
941b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
942b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
943b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
944b824b7d8SAndrew Gallatin 	}
945b824b7d8SAndrew Gallatin 
9464da0d523SAndrew Gallatin 	return status;
9474da0d523SAndrew Gallatin }
9484da0d523SAndrew Gallatin 
949b2fc195eSAndrew Gallatin 
950b2fc195eSAndrew Gallatin static int
9511e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
952b2fc195eSAndrew Gallatin {
953b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
954b2fc195eSAndrew Gallatin 	volatile char *submit;
955b2fc195eSAndrew Gallatin 	char buf_bytes[72];
956b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
957b2fc195eSAndrew Gallatin 	int status, i;
958b2fc195eSAndrew Gallatin 
959b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
960b2fc195eSAndrew Gallatin 
961b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9626d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
963b2fc195eSAndrew Gallatin 	if (status) {
9641e413cf9SAndrew Gallatin 		if (!adopt)
9651e413cf9SAndrew Gallatin 			return status;
9664da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9674da0d523SAndrew Gallatin 		   it is new enough */
9684da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9694da0d523SAndrew Gallatin 		if (status) {
9704da0d523SAndrew Gallatin 			device_printf(sc->dev,
9714da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
972b2fc195eSAndrew Gallatin 			return status;
973b2fc195eSAndrew Gallatin 		}
9744da0d523SAndrew Gallatin 		device_printf(sc->dev,
9754da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9761e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9774da0d523SAndrew Gallatin 			device_printf(sc->dev,
9784da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9794da0d523SAndrew Gallatin 				 ".  For optimal\n");
9804da0d523SAndrew Gallatin 			device_printf(sc->dev,
9814da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9824da0d523SAndrew Gallatin 				 "firmware\n");
9834da0d523SAndrew Gallatin 		}
984d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9851e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
986d91b1b49SAndrew Gallatin 		return 0;
9874da0d523SAndrew Gallatin 	}
988b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
989b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
990b2fc195eSAndrew Gallatin 	*confirm = 0;
99173c7c83fSAndrew Gallatin 	wmb();
992b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
993b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
994b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
995b2fc195eSAndrew Gallatin 	*/
996b2fc195eSAndrew Gallatin 
9976d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9986d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
999b2fc195eSAndrew Gallatin 
1000b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
1001b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
1002b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
1003b2fc195eSAndrew Gallatin 
1004b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
1005b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
1006b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
1007b2fc195eSAndrew Gallatin 	*/
1008b2fc195eSAndrew Gallatin 					/* where the code starts*/
10096d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1010b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1011b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1012b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1013b2fc195eSAndrew Gallatin 
10140fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10156d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
101673c7c83fSAndrew Gallatin 	wmb();
1017b2fc195eSAndrew Gallatin 	DELAY(1000);
101873c7c83fSAndrew Gallatin 	wmb();
1019b2fc195eSAndrew Gallatin 	i = 0;
1020b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1021b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1022b2fc195eSAndrew Gallatin 		i++;
1023b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1024b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1025b2fc195eSAndrew Gallatin 	}
1026b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1027b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1028b2fc195eSAndrew Gallatin 			confirm, *confirm);
1029b2fc195eSAndrew Gallatin 
1030b2fc195eSAndrew Gallatin 		return ENXIO;
1031b2fc195eSAndrew Gallatin 	}
1032b2fc195eSAndrew Gallatin 	return 0;
1033b2fc195eSAndrew Gallatin }
1034b2fc195eSAndrew Gallatin 
1035b2fc195eSAndrew Gallatin static int
10366d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1037b2fc195eSAndrew Gallatin {
10386d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1039b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1040b2fc195eSAndrew Gallatin 	int status;
1041b2fc195eSAndrew Gallatin 
1042b2fc195eSAndrew Gallatin 
1043b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1044b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1045b2fc195eSAndrew Gallatin 
1046b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1047b2fc195eSAndrew Gallatin 
10485e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1049b2fc195eSAndrew Gallatin 	return status;
1050b2fc195eSAndrew Gallatin }
1051b2fc195eSAndrew Gallatin 
1052b2fc195eSAndrew Gallatin static int
10536d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1054b2fc195eSAndrew Gallatin {
10556d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1056b2fc195eSAndrew Gallatin 	int status;
1057b2fc195eSAndrew Gallatin 
1058b2fc195eSAndrew Gallatin 	if (pause)
10595e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1060b2fc195eSAndrew Gallatin 				       &cmd);
1061b2fc195eSAndrew Gallatin 	else
10625e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1063b2fc195eSAndrew Gallatin 				       &cmd);
1064b2fc195eSAndrew Gallatin 
1065b2fc195eSAndrew Gallatin 	if (status) {
1066b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1067b2fc195eSAndrew Gallatin 		return ENXIO;
1068b2fc195eSAndrew Gallatin 	}
1069b2fc195eSAndrew Gallatin 	sc->pause = pause;
1070b2fc195eSAndrew Gallatin 	return 0;
1071b2fc195eSAndrew Gallatin }
1072b2fc195eSAndrew Gallatin 
1073b2fc195eSAndrew Gallatin static void
10746d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1075b2fc195eSAndrew Gallatin {
10766d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1077b2fc195eSAndrew Gallatin 	int status;
1078b2fc195eSAndrew Gallatin 
10791e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10801e413cf9SAndrew Gallatin 		promisc = 1;
10811e413cf9SAndrew Gallatin 
1082b2fc195eSAndrew Gallatin 	if (promisc)
10835e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1084b2fc195eSAndrew Gallatin 				       &cmd);
1085b2fc195eSAndrew Gallatin 	else
10865e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1087b2fc195eSAndrew Gallatin 				       &cmd);
1088b2fc195eSAndrew Gallatin 
1089b2fc195eSAndrew Gallatin 	if (status) {
1090b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1091b2fc195eSAndrew Gallatin 	}
1092b2fc195eSAndrew Gallatin }
1093b2fc195eSAndrew Gallatin 
10942a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx {
10952a0f8518SGleb Smirnoff 	mxge_softc_t *sc;
10962a0f8518SGleb Smirnoff 	int error;
10972a0f8518SGleb Smirnoff };
10982a0f8518SGleb Smirnoff 
10992a0f8518SGleb Smirnoff static u_int
11002a0f8518SGleb Smirnoff mxge_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
11012a0f8518SGleb Smirnoff {
11022a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx *ctx = arg;
11032a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
11042a0f8518SGleb Smirnoff 
11052a0f8518SGleb Smirnoff 	if (ctx->error != 0)
11062a0f8518SGleb Smirnoff 		return (0);
11072a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl), &cmd.data0, 4);
11082a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl) + 4, &cmd.data1, 2);
11092a0f8518SGleb Smirnoff 	cmd.data0 = htonl(cmd.data0);
11102a0f8518SGleb Smirnoff 	cmd.data1 = htonl(cmd.data1);
11112a0f8518SGleb Smirnoff 
11122a0f8518SGleb Smirnoff 	ctx->error = mxge_send_cmd(ctx->sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11132a0f8518SGleb Smirnoff 
11142a0f8518SGleb Smirnoff 	return (1);
11152a0f8518SGleb Smirnoff }
11162a0f8518SGleb Smirnoff 
11170fa7f681SAndrew Gallatin static void
11180fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
11190fa7f681SAndrew Gallatin {
11202a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx ctx;
11210fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
11222a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
11230fa7f681SAndrew Gallatin 	int err;
11240fa7f681SAndrew Gallatin 
11250fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11260fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11270fa7f681SAndrew Gallatin 		return;
11280fa7f681SAndrew Gallatin 
11290fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11300fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11310fa7f681SAndrew Gallatin 	if (err != 0) {
11320fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11330fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11340fa7f681SAndrew Gallatin 		return;
11350fa7f681SAndrew Gallatin 	}
11360fa7f681SAndrew Gallatin 
1137b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1138b824b7d8SAndrew Gallatin 		return;
11390fa7f681SAndrew Gallatin 
11400fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11410fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11420fa7f681SAndrew Gallatin 		return;
11430fa7f681SAndrew Gallatin 
11440fa7f681SAndrew Gallatin 	/* Flush all the filters */
11450fa7f681SAndrew Gallatin 
11460fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11470fa7f681SAndrew Gallatin 	if (err != 0) {
11480fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11490fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11500fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11510fa7f681SAndrew Gallatin 		return;
11520fa7f681SAndrew Gallatin 	}
11530fa7f681SAndrew Gallatin 
11540fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11552a0f8518SGleb Smirnoff 	ctx.sc = sc;
11562a0f8518SGleb Smirnoff 	ctx.error = 0;
11572a0f8518SGleb Smirnoff 	if_foreach_llmaddr(ifp, mxge_add_maddr, &ctx);
11582a0f8518SGleb Smirnoff 	if (ctx.error != 0) {
11592a0f8518SGleb Smirnoff 		device_printf(sc->dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
11602a0f8518SGleb Smirnoff 		    "error status:" "%d\t", ctx.error);
11610fa7f681SAndrew Gallatin 		/* abort, leaving multicast filtering off */
11620fa7f681SAndrew Gallatin 		return;
11630fa7f681SAndrew Gallatin 	}
11642a0f8518SGleb Smirnoff 
11650fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11660fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11670fa7f681SAndrew Gallatin 	if (err != 0) {
11680fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11690fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11700fa7f681SAndrew Gallatin 	}
11710fa7f681SAndrew Gallatin }
11720fa7f681SAndrew Gallatin 
1173b2fc195eSAndrew Gallatin static int
1174053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1175053e637fSAndrew Gallatin {
1176053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1177053e637fSAndrew Gallatin 	int status;
1178053e637fSAndrew Gallatin 
1179c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1180c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1181053e637fSAndrew Gallatin 
1182053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1183053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1184053e637fSAndrew Gallatin 	cmd.data0 = 0;
1185053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1186053e637fSAndrew Gallatin 			       &cmd);
1187053e637fSAndrew Gallatin 	if (status == 0)
1188c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1189053e637fSAndrew Gallatin 
1190053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1191053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1192053e637fSAndrew Gallatin }
1193053e637fSAndrew Gallatin 
1194053e637fSAndrew Gallatin static int
1195adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1196b2fc195eSAndrew Gallatin {
11971e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11981e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11991e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
12006d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
12011e413cf9SAndrew Gallatin 	int slice, status;
1202b2fc195eSAndrew Gallatin 
1203b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1204b2fc195eSAndrew Gallatin 	   is alive */
1205b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
12065e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1207b2fc195eSAndrew Gallatin 	if (status != 0) {
1208b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1209b2fc195eSAndrew Gallatin 		return ENXIO;
1210b2fc195eSAndrew Gallatin 	}
1211b2fc195eSAndrew Gallatin 
1212091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1213091feecdSAndrew Gallatin 
12141e413cf9SAndrew Gallatin 
12151e413cf9SAndrew Gallatin 	/* set the intrq size */
12161e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12171e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12181e413cf9SAndrew Gallatin 
12191e413cf9SAndrew Gallatin 	/*
12201e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12211e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12221e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12231e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12241e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12251e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12261e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12271e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12281e413cf9SAndrew Gallatin 	 */
12291e413cf9SAndrew Gallatin 
12301e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12311e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12321e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12331e413cf9SAndrew Gallatin 					   &cmd);
12341e413cf9SAndrew Gallatin 		if (status != 0) {
12351e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12361e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12371e413cf9SAndrew Gallatin 			return status;
12381e413cf9SAndrew Gallatin 		}
12391e413cf9SAndrew Gallatin 		/*
12401e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12411e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12421e413cf9SAndrew Gallatin 		 */
12431e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12441e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1245c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1246c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1247c6cb3e3fSAndrew Gallatin #endif
12481e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12491e413cf9SAndrew Gallatin 					   &cmd);
12501e413cf9SAndrew Gallatin 		if (status != 0) {
12511e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12521e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12531e413cf9SAndrew Gallatin 			return status;
12541e413cf9SAndrew Gallatin 		}
12551e413cf9SAndrew Gallatin 	}
12561e413cf9SAndrew Gallatin 
12571e413cf9SAndrew Gallatin 
1258adae7080SAndrew Gallatin 	if (interrupts_setup) {
1259b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12601e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12611e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12621e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12631e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12641e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12651e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12661e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12671e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12681e413cf9SAndrew Gallatin 						&cmd);
12691e413cf9SAndrew Gallatin 		}
1270adae7080SAndrew Gallatin 	}
1271b2fc195eSAndrew Gallatin 
12726d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12735e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12745e7d8541SAndrew Gallatin 
12755e7d8541SAndrew Gallatin 
12765e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12775e7d8541SAndrew Gallatin 
12785e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12791e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12805e7d8541SAndrew Gallatin 
12815e7d8541SAndrew Gallatin 
12825e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12836d87a65dSAndrew Gallatin 				&cmd);
12845e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1285b2fc195eSAndrew Gallatin 	if (status != 0) {
1286b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1287b2fc195eSAndrew Gallatin 		return status;
1288b2fc195eSAndrew Gallatin 	}
1289b2fc195eSAndrew Gallatin 
12905e7d8541SAndrew Gallatin 
12915e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12925e7d8541SAndrew Gallatin 
12935e7d8541SAndrew Gallatin 
12945e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12958fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12965e7d8541SAndrew Gallatin 
12971e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12981e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12991e413cf9SAndrew Gallatin 
13001e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1301b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
13021e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
13031e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
13041e413cf9SAndrew Gallatin 		ss->tx.req = 0;
13051e413cf9SAndrew Gallatin 		ss->tx.done = 0;
13061e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1307c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1308c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1309c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
13101e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
13111e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
13121e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
13131e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
13141e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
131526dd49c6SAndrew Gallatin 		ss->lc.lro_bad_csum = 0;
131626dd49c6SAndrew Gallatin 		ss->lc.lro_queued = 0;
131726dd49c6SAndrew Gallatin 		ss->lc.lro_flushed = 0;
13181e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
1319a393336bSAndrew Gallatin 			bzero(ss->fw_stats, sizeof *ss->fw_stats);
13201e413cf9SAndrew Gallatin 		}
13211e413cf9SAndrew Gallatin 	}
1322b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13236d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1324bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13256d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13260fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
132765c69066SAndrew Gallatin 	if (sc->throttle) {
132865c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
132965c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
133065c69066SAndrew Gallatin 				  &cmd)) {
133165c69066SAndrew Gallatin 			device_printf(sc->dev,
133265c69066SAndrew Gallatin 				      "can't enable throttle\n");
133365c69066SAndrew Gallatin 		}
133465c69066SAndrew Gallatin 	}
1335b2fc195eSAndrew Gallatin 	return status;
1336b2fc195eSAndrew Gallatin }
1337b2fc195eSAndrew Gallatin 
1338b2fc195eSAndrew Gallatin static int
133965c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
134065c69066SAndrew Gallatin {
134165c69066SAndrew Gallatin 	mxge_cmd_t cmd;
134265c69066SAndrew Gallatin 	mxge_softc_t *sc;
134365c69066SAndrew Gallatin 	int err;
134465c69066SAndrew Gallatin 	unsigned int throttle;
134565c69066SAndrew Gallatin 
134665c69066SAndrew Gallatin 	sc = arg1;
134765c69066SAndrew Gallatin 	throttle = sc->throttle;
134865c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
134965c69066SAndrew Gallatin 	if (err != 0) {
135065c69066SAndrew Gallatin 		return err;
135165c69066SAndrew Gallatin 	}
135265c69066SAndrew Gallatin 
135365c69066SAndrew Gallatin 	if (throttle == sc->throttle)
135465c69066SAndrew Gallatin 		return 0;
135565c69066SAndrew Gallatin 
135665c69066SAndrew Gallatin 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
135765c69066SAndrew Gallatin 		return EINVAL;
135865c69066SAndrew Gallatin 
135965c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
136065c69066SAndrew Gallatin 	cmd.data0 = throttle;
136165c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
136265c69066SAndrew Gallatin 	if (err == 0)
136365c69066SAndrew Gallatin 		sc->throttle = throttle;
136465c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
136565c69066SAndrew Gallatin 	return err;
136665c69066SAndrew Gallatin }
136765c69066SAndrew Gallatin 
136865c69066SAndrew Gallatin static int
13696d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1370b2fc195eSAndrew Gallatin {
13716d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1372b2fc195eSAndrew Gallatin 	unsigned int intr_coal_delay;
1373b2fc195eSAndrew Gallatin 	int err;
1374b2fc195eSAndrew Gallatin 
1375b2fc195eSAndrew Gallatin 	sc = arg1;
1376b2fc195eSAndrew Gallatin 	intr_coal_delay = sc->intr_coal_delay;
1377b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1378b2fc195eSAndrew Gallatin 	if (err != 0) {
1379b2fc195eSAndrew Gallatin 		return err;
1380b2fc195eSAndrew Gallatin 	}
1381b2fc195eSAndrew Gallatin 	if (intr_coal_delay == sc->intr_coal_delay)
1382b2fc195eSAndrew Gallatin 		return 0;
1383b2fc195eSAndrew Gallatin 
1384b2fc195eSAndrew Gallatin 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1385b2fc195eSAndrew Gallatin 		return EINVAL;
1386b2fc195eSAndrew Gallatin 
1387a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13885e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1389b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13905e7d8541SAndrew Gallatin 
1391a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1392b2fc195eSAndrew Gallatin 	return err;
1393b2fc195eSAndrew Gallatin }
1394b2fc195eSAndrew Gallatin 
1395b2fc195eSAndrew Gallatin static int
13966d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1397b2fc195eSAndrew Gallatin {
13986d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1399b2fc195eSAndrew Gallatin 	unsigned int enabled;
1400b2fc195eSAndrew Gallatin 	int err;
1401b2fc195eSAndrew Gallatin 
1402b2fc195eSAndrew Gallatin 	sc = arg1;
1403b2fc195eSAndrew Gallatin 	enabled = sc->pause;
1404b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
1405b2fc195eSAndrew Gallatin 	if (err != 0) {
1406b2fc195eSAndrew Gallatin 		return err;
1407b2fc195eSAndrew Gallatin 	}
1408b2fc195eSAndrew Gallatin 	if (enabled == sc->pause)
1409b2fc195eSAndrew Gallatin 		return 0;
1410b2fc195eSAndrew Gallatin 
1411a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
14126d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1413a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1414b2fc195eSAndrew Gallatin 	return err;
1415b2fc195eSAndrew Gallatin }
1416b2fc195eSAndrew Gallatin 
1417b2fc195eSAndrew Gallatin static int
14186d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1419b2fc195eSAndrew Gallatin {
1420b2fc195eSAndrew Gallatin 	int err;
1421b2fc195eSAndrew Gallatin 
1422b2fc195eSAndrew Gallatin 	if (arg1 == NULL)
1423b2fc195eSAndrew Gallatin 		return EFAULT;
1424b2fc195eSAndrew Gallatin 	arg2 = be32toh(*(int *)arg1);
1425b2fc195eSAndrew Gallatin 	arg1 = NULL;
1426b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, arg1, arg2, req);
1427b2fc195eSAndrew Gallatin 
1428b2fc195eSAndrew Gallatin 	return err;
1429b2fc195eSAndrew Gallatin }
1430b2fc195eSAndrew Gallatin 
1431b2fc195eSAndrew Gallatin static void
14321e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14331e413cf9SAndrew Gallatin {
14341e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14351e413cf9SAndrew Gallatin 	int slice;
14361e413cf9SAndrew Gallatin 
14371e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14381e413cf9SAndrew Gallatin 		return;
14391e413cf9SAndrew Gallatin 
14401e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14411e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14421e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14431e413cf9SAndrew Gallatin 			continue;
14441e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14451e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14461e413cf9SAndrew Gallatin 	}
14471e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14481e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14491e413cf9SAndrew Gallatin }
14501e413cf9SAndrew Gallatin 
14511e413cf9SAndrew Gallatin static void
14526d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1453b2fc195eSAndrew Gallatin {
1454b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1455b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14565e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14571e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14581e413cf9SAndrew Gallatin 	int slice;
14591e413cf9SAndrew Gallatin 	char slice_num[8];
1460b2fc195eSAndrew Gallatin 
1461b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1462b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14631e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1464b2fc195eSAndrew Gallatin 
14655e7d8541SAndrew Gallatin 	/* random information */
14665e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14675e7d8541SAndrew Gallatin 		       "firmware_version",
1468f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->fw_version,
14695e7d8541SAndrew Gallatin 		       0, "firmware version");
14705e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14715e7d8541SAndrew Gallatin 		       "serial_number",
1472f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->serial_number_string,
14735e7d8541SAndrew Gallatin 		       0, "serial number");
14745e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14755e7d8541SAndrew Gallatin 		       "product_code",
1476f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->product_code_string,
14775e7d8541SAndrew Gallatin 		       0, "product_code");
14785e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1479d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1480d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1481d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1482d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14835e7d8541SAndrew Gallatin 		       "tx_boundary",
14841e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14855e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14865e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1487091feecdSAndrew Gallatin 		       "write_combine",
1488091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1489091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1490091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14915e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14925e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14935e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14945e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14955e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14965e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14975e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14985e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14995e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
15005e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
15015e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
1502a393336bSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1503a393336bSAndrew Gallatin 		       "watchdog_resets",
1504a393336bSAndrew Gallatin 		       CTLFLAG_RD, &sc->watchdog_resets,
1505a393336bSAndrew Gallatin 		       0, "Number of times NIC was reset");
15065e7d8541SAndrew Gallatin 
15075e7d8541SAndrew Gallatin 
15085e7d8541SAndrew Gallatin 	/* performance related tunables */
1509b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15107029da5cSPawel Biernacki 	    "intr_coal_delay", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
15117029da5cSPawel Biernacki 	    sc, 0, mxge_change_intr_coal, "I",
15127029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1513b2fc195eSAndrew Gallatin 
1514b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15157029da5cSPawel Biernacki 	    "throttle", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
15167029da5cSPawel Biernacki 	    mxge_change_throttle, "I", "transmit throttling");
151765c69066SAndrew Gallatin 
151865c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1519b2fc195eSAndrew Gallatin 	    "flow_control_enabled",
15207029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
15217029da5cSPawel Biernacki 	    mxge_change_flow_control, "I",
15227029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1523b2fc195eSAndrew Gallatin 
1524b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15255e7d8541SAndrew Gallatin 		       "deassert_wait",
15265e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15275e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1528b2fc195eSAndrew Gallatin 
1529b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1530b2fc195eSAndrew Gallatin 	   Need to swap it */
1531b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15327029da5cSPawel Biernacki 	    "link_up", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15337029da5cSPawel Biernacki 	    &fw->link_up, 0, mxge_handle_be32, "I", "link up");
1534b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15357029da5cSPawel Biernacki 	    "rdma_tags_available", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15367029da5cSPawel Biernacki 	    &fw->rdma_tags_available, 0, mxge_handle_be32, "I",
15377029da5cSPawel Biernacki 	    "rdma_tags_available");
1538b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15397029da5cSPawel Biernacki 	    "dropped_bad_crc32", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15407029da5cSPawel Biernacki 	    &fw->dropped_bad_crc32, 0, mxge_handle_be32, "I",
15417029da5cSPawel Biernacki 	    "dropped_bad_crc32");
1542adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15437029da5cSPawel Biernacki 	    "dropped_bad_phy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15447029da5cSPawel Biernacki 	    &fw->dropped_bad_phy, 0, mxge_handle_be32, "I", "dropped_bad_phy");
1545b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1546b2fc195eSAndrew Gallatin 	    "dropped_link_error_or_filtered",
15477029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15487029da5cSPawel Biernacki 	    &fw->dropped_link_error_or_filtered, 0, mxge_handle_be32, "I",
15497029da5cSPawel Biernacki 	    "dropped_link_error_or_filtered");
1550b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1551adae7080SAndrew Gallatin 	    "dropped_link_overflow",
15527029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15537029da5cSPawel Biernacki 	    &fw->dropped_link_overflow, 0, mxge_handle_be32, "I",
15547029da5cSPawel Biernacki 	    "dropped_link_overflow");
1555adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15560fa7f681SAndrew Gallatin 	    "dropped_multicast_filtered",
15577029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15587029da5cSPawel Biernacki 	    &fw->dropped_multicast_filtered, 0, mxge_handle_be32, "I",
15597029da5cSPawel Biernacki 	    "dropped_multicast_filtered");
15600fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1561adae7080SAndrew Gallatin 	    "dropped_no_big_buffer",
15627029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15637029da5cSPawel Biernacki 	    &fw->dropped_no_big_buffer, 0, mxge_handle_be32, "I",
15647029da5cSPawel Biernacki 	    "dropped_no_big_buffer");
1565b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1566b2fc195eSAndrew Gallatin 	    "dropped_no_small_buffer",
15677029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15687029da5cSPawel Biernacki 	    &fw->dropped_no_small_buffer, 0, mxge_handle_be32, "I",
15697029da5cSPawel Biernacki 	    "dropped_no_small_buffer");
1570b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1571adae7080SAndrew Gallatin 	    "dropped_overrun",
15727029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15737029da5cSPawel Biernacki 	    &fw->dropped_overrun, 0, mxge_handle_be32, "I",
15747029da5cSPawel Biernacki 	    "dropped_overrun");
1575adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15767029da5cSPawel Biernacki 	    "dropped_pause", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15777029da5cSPawel Biernacki 	    &fw->dropped_pause, 0, mxge_handle_be32, "I", "dropped_pause");
1578adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15797029da5cSPawel Biernacki 	    "dropped_runt", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15807029da5cSPawel Biernacki 	    &fw->dropped_runt, 0, mxge_handle_be32, "I", "dropped_runt");
1581b2fc195eSAndrew Gallatin 
1582a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1583a0394e33SAndrew Gallatin 	    "dropped_unicast_filtered",
15847029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15857029da5cSPawel Biernacki 	    &fw->dropped_unicast_filtered, 0, mxge_handle_be32, "I",
15867029da5cSPawel Biernacki 	    "dropped_unicast_filtered");
1587a0394e33SAndrew Gallatin 
15885e7d8541SAndrew Gallatin 	/* verbose printing? */
1589b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15905e7d8541SAndrew Gallatin 		       "verbose",
15915e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15925e7d8541SAndrew Gallatin 		       0, "verbose printing");
1593b2fc195eSAndrew Gallatin 
15941e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
15951e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15961e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
15971e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15987029da5cSPawel Biernacki 		    "slice", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15991e413cf9SAndrew Gallatin 
16001e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16011e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16021e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16031e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16041e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16051e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16061e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16071e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16087029da5cSPawel Biernacki 			    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
16091e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1610053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16111e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16121e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16131e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16141e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16151e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16161e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16171e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
1618e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
161926dd49c6SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lc.lro_flushed,
1620053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1621053e637fSAndrew Gallatin 
1622e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
162326dd49c6SAndrew Gallatin 			       "lro_bad_csum", CTLFLAG_RD, &ss->lc.lro_bad_csum,
162426dd49c6SAndrew Gallatin 			       0, "number of bad csums preventing LRO");
162526dd49c6SAndrew Gallatin 
1626e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
162726dd49c6SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lc.lro_queued,
16281e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16291e413cf9SAndrew Gallatin 			       "queues");
1630053e637fSAndrew Gallatin 
1631c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16321e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16331e413cf9SAndrew Gallatin 		if (slice > 0)
16341e413cf9SAndrew Gallatin 			continue;
1635c6cb3e3fSAndrew Gallatin #endif
1636c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1637c6cb3e3fSAndrew Gallatin 			       "tx_req",
1638c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1639c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16401e413cf9SAndrew Gallatin 
16411e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16421e413cf9SAndrew Gallatin 			       "tx_done",
16431e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16441e413cf9SAndrew Gallatin 			       0, "tx_done");
16451e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16461e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16471e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16481e413cf9SAndrew Gallatin 			       0, "tx_done");
16491e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16501e413cf9SAndrew Gallatin 			       "tx_stall",
16511e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16521e413cf9SAndrew Gallatin 			       0, "tx_stall");
16531e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16541e413cf9SAndrew Gallatin 			       "tx_wake",
16551e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16561e413cf9SAndrew Gallatin 			       0, "tx_wake");
16571e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16581e413cf9SAndrew Gallatin 			       "tx_defrag",
16591e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16601e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1661c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1662c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1663c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1664c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1665c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1666c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1667c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1668c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1669c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1670c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1671c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1672c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
16731e413cf9SAndrew Gallatin 	}
1674b2fc195eSAndrew Gallatin }
1675b2fc195eSAndrew Gallatin 
1676b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1677b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1678b2fc195eSAndrew Gallatin 
1679b2fc195eSAndrew Gallatin static inline void
16801e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1681b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1682b2fc195eSAndrew Gallatin {
1683b2fc195eSAndrew Gallatin 	int idx, starting_slot;
1684b2fc195eSAndrew Gallatin 	starting_slot = tx->req;
1685b2fc195eSAndrew Gallatin 	while (cnt > 1) {
1686b2fc195eSAndrew Gallatin 		cnt--;
1687b2fc195eSAndrew Gallatin 		idx = (starting_slot + cnt) & tx->mask;
16886d87a65dSAndrew Gallatin 		mxge_pio_copy(&tx->lanai[idx],
1689b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
169073c7c83fSAndrew Gallatin 		wmb();
1691b2fc195eSAndrew Gallatin 	}
1692b2fc195eSAndrew Gallatin }
1693b2fc195eSAndrew Gallatin 
1694b2fc195eSAndrew Gallatin /*
1695b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1696b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1697b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1698b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1699b2fc195eSAndrew Gallatin  */
1700b2fc195eSAndrew Gallatin 
1701b2fc195eSAndrew Gallatin static inline void
17021e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1703b2fc195eSAndrew Gallatin 		  int cnt)
1704b2fc195eSAndrew Gallatin {
1705b2fc195eSAndrew Gallatin 	int idx, i;
1706b2fc195eSAndrew Gallatin 	uint32_t *src_ints;
1707b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1708b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *srcp;
1709b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
17105e7d8541SAndrew Gallatin 	uint8_t last_flags;
1711b2fc195eSAndrew Gallatin 
1712b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1713b2fc195eSAndrew Gallatin 
17145e7d8541SAndrew Gallatin 	last_flags = src->flags;
17155e7d8541SAndrew Gallatin 	src->flags = 0;
171673c7c83fSAndrew Gallatin 	wmb();
1717b2fc195eSAndrew Gallatin 	dst = dstp = &tx->lanai[idx];
1718b2fc195eSAndrew Gallatin 	srcp = src;
1719b2fc195eSAndrew Gallatin 
1720b2fc195eSAndrew Gallatin 	if ((idx + cnt) < tx->mask) {
1721b2fc195eSAndrew Gallatin 		for (i = 0; i < (cnt - 1); i += 2) {
17226d87a65dSAndrew Gallatin 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
172373c7c83fSAndrew Gallatin 			wmb(); /* force write every 32 bytes */
1724b2fc195eSAndrew Gallatin 			srcp += 2;
1725b2fc195eSAndrew Gallatin 			dstp += 2;
1726b2fc195eSAndrew Gallatin 		}
1727b2fc195eSAndrew Gallatin 	} else {
1728b2fc195eSAndrew Gallatin 		/* submit all but the first request, and ensure
1729b2fc195eSAndrew Gallatin 		   that it is submitted below */
17306d87a65dSAndrew Gallatin 		mxge_submit_req_backwards(tx, src, cnt);
1731b2fc195eSAndrew Gallatin 		i = 0;
1732b2fc195eSAndrew Gallatin 	}
1733b2fc195eSAndrew Gallatin 	if (i < cnt) {
1734b2fc195eSAndrew Gallatin 		/* submit the first request */
17356d87a65dSAndrew Gallatin 		mxge_pio_copy(dstp, srcp, sizeof(*src));
173673c7c83fSAndrew Gallatin 		wmb(); /* barrier before setting valid flag */
1737b2fc195eSAndrew Gallatin 	}
1738b2fc195eSAndrew Gallatin 
1739b2fc195eSAndrew Gallatin 	/* re-write the last 32-bits with the valid flags */
17405e7d8541SAndrew Gallatin 	src->flags = last_flags;
1741b2fc195eSAndrew Gallatin 	src_ints = (uint32_t *)src;
1742b2fc195eSAndrew Gallatin 	src_ints+=3;
1743b2fc195eSAndrew Gallatin 	dst_ints = (volatile uint32_t *)dst;
1744b2fc195eSAndrew Gallatin 	dst_ints+=3;
1745b2fc195eSAndrew Gallatin 	*dst_ints =  *src_ints;
1746b2fc195eSAndrew Gallatin 	tx->req += cnt;
174773c7c83fSAndrew Gallatin 	wmb();
1748b2fc195eSAndrew Gallatin }
1749b2fc195eSAndrew Gallatin 
17500a7a780eSAndrew Gallatin static int
17510a7a780eSAndrew Gallatin mxge_parse_tx(struct mxge_slice_state *ss, struct mbuf *m,
17520a7a780eSAndrew Gallatin     struct mxge_pkt_info *pi)
17530a7a780eSAndrew Gallatin {
17540a7a780eSAndrew Gallatin 	struct ether_vlan_header *eh;
17550a7a780eSAndrew Gallatin 	uint16_t etype;
17560a7a780eSAndrew Gallatin 	int tso = m->m_pkthdr.csum_flags & (CSUM_TSO);
17570a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17580a7a780eSAndrew Gallatin 	int nxt;
17590a7a780eSAndrew Gallatin #endif
17600a7a780eSAndrew Gallatin 
17610a7a780eSAndrew Gallatin 	eh = mtod(m, struct ether_vlan_header *);
17620a7a780eSAndrew Gallatin 	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
17630a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_proto);
17640a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
17650a7a780eSAndrew Gallatin 	} else {
17660a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_encap_proto);
17670a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN;
17680a7a780eSAndrew Gallatin 	}
17690a7a780eSAndrew Gallatin 
17700a7a780eSAndrew Gallatin 	switch (etype) {
17710a7a780eSAndrew Gallatin 	case ETHERTYPE_IP:
17720a7a780eSAndrew Gallatin 		/*
17730a7a780eSAndrew Gallatin 		 * ensure ip header is in first mbuf, copy it to a
17740a7a780eSAndrew Gallatin 		 * scratch buffer if not
17750a7a780eSAndrew Gallatin 		 */
17760a7a780eSAndrew Gallatin 		pi->ip = (struct ip *)(m->m_data + pi->ip_off);
17770a7a780eSAndrew Gallatin 		pi->ip6 = NULL;
17780a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip))) {
17790a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip),
17800a7a780eSAndrew Gallatin 			    ss->scratch);
17810a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17820a7a780eSAndrew Gallatin 		}
17830a7a780eSAndrew Gallatin 		pi->ip_hlen = pi->ip->ip_hl << 2;
17840a7a780eSAndrew Gallatin 		if (!tso)
17850a7a780eSAndrew Gallatin 			return 0;
17860a7a780eSAndrew Gallatin 
17870a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17880a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17890a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17900a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17910a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17920a7a780eSAndrew Gallatin 		}
17930a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip + pi->ip_hlen);
17940a7a780eSAndrew Gallatin 		break;
17950a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17960a7a780eSAndrew Gallatin 	case ETHERTYPE_IPV6:
17970a7a780eSAndrew Gallatin 		pi->ip6 = (struct ip6_hdr *)(m->m_data + pi->ip_off);
17980a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip6))) {
17990a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip6),
18000a7a780eSAndrew Gallatin 			    ss->scratch);
18010a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
18020a7a780eSAndrew Gallatin 		}
18030a7a780eSAndrew Gallatin 		nxt = 0;
18040a7a780eSAndrew Gallatin 		pi->ip_hlen = ip6_lasthdr(m, pi->ip_off, IPPROTO_IPV6, &nxt);
18050a7a780eSAndrew Gallatin 		pi->ip_hlen -= pi->ip_off;
18060a7a780eSAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
18070a7a780eSAndrew Gallatin 			return EINVAL;
18080a7a780eSAndrew Gallatin 
18090a7a780eSAndrew Gallatin 		if (!tso)
18100a7a780eSAndrew Gallatin 			return 0;
18110a7a780eSAndrew Gallatin 
18120a7a780eSAndrew Gallatin 		if (pi->ip_off + pi->ip_hlen > ss->sc->max_tso6_hlen)
18130a7a780eSAndrew Gallatin 			return EINVAL;
18140a7a780eSAndrew Gallatin 
18150a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
18160a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
18170a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
18180a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
18190a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
18200a7a780eSAndrew Gallatin 		}
18210a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip6 + pi->ip_hlen);
18220a7a780eSAndrew Gallatin 		break;
18230a7a780eSAndrew Gallatin #endif
18240a7a780eSAndrew Gallatin 	default:
18250a7a780eSAndrew Gallatin 		return EINVAL;
18260a7a780eSAndrew Gallatin 	}
18270a7a780eSAndrew Gallatin 	return 0;
18280a7a780eSAndrew Gallatin }
18290a7a780eSAndrew Gallatin 
183037d89b0cSAndrew Gallatin #if IFCAP_TSO4
183137d89b0cSAndrew Gallatin 
1832b2fc195eSAndrew Gallatin static void
18331e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
18340a7a780eSAndrew Gallatin 	       int busdma_seg_cnt, struct mxge_pkt_info *pi)
1835aed8e389SAndrew Gallatin {
18361e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1837aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1838aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1839aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1840aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1841aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
18420a7a780eSAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss, sum;
1843aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1844aed8e389SAndrew Gallatin 	static int once;
1845aed8e389SAndrew Gallatin 
1846aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1847aed8e389SAndrew Gallatin 
1848aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1849aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1850aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1851aed8e389SAndrew Gallatin 	 */
1852aed8e389SAndrew Gallatin 
18530a7a780eSAndrew Gallatin 	cksum_offset = pi->ip_off + pi->ip_hlen;
18540a7a780eSAndrew Gallatin 	cum_len = -(cksum_offset + (pi->tcp->th_off << 2));
1855aed8e389SAndrew Gallatin 
1856aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
18570a7a780eSAndrew Gallatin 	if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) == 0)) {
18584ed8ca8fSAndrew Gallatin 		/*
18594ed8ca8fSAndrew Gallatin 		 * If packet has full TCP csum, replace it with pseudo hdr
18604ed8ca8fSAndrew Gallatin 		 * sum that the NIC expects, otherwise the NIC will emit
18614ed8ca8fSAndrew Gallatin 		 * packets with bad TCP checksums.
18624ed8ca8fSAndrew Gallatin 		 */
18634ed8ca8fSAndrew Gallatin 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
18640a7a780eSAndrew Gallatin 		if (pi->ip6) {
18650a7a780eSAndrew Gallatin #if (CSUM_TCP_IPV6 != 0) && defined(INET6)
18660a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP_IPV6;
18670a7a780eSAndrew Gallatin 			sum = in6_cksum_pseudo(pi->ip6,
18680a7a780eSAndrew Gallatin 			    m->m_pkthdr.len - cksum_offset,
18690a7a780eSAndrew Gallatin 			    IPPROTO_TCP, 0);
18700a7a780eSAndrew Gallatin #endif
18710a7a780eSAndrew Gallatin 		} else {
1872abc5b96bSAndrew Gallatin #ifdef INET
18730a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP;
18740a7a780eSAndrew Gallatin 			sum = in_pseudo(pi->ip->ip_src.s_addr,
18750a7a780eSAndrew Gallatin 			    pi->ip->ip_dst.s_addr,
18760a7a780eSAndrew Gallatin 			    htons(IPPROTO_TCP + (m->m_pkthdr.len -
18770a7a780eSAndrew Gallatin 				    cksum_offset)));
1878abc5b96bSAndrew Gallatin #endif
18790a7a780eSAndrew Gallatin 		}
18800a7a780eSAndrew Gallatin 		m_copyback(m, offsetof(struct tcphdr, th_sum) +
18810a7a780eSAndrew Gallatin 		    cksum_offset, sizeof(sum), (caddr_t)&sum);
18824ed8ca8fSAndrew Gallatin 	}
1883aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1884aed8e389SAndrew Gallatin 
1885aed8e389SAndrew Gallatin 
1886aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1887aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1888aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1889aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1890aed8e389SAndrew Gallatin 
18910a7a780eSAndrew Gallatin 	if (pi->ip6) {
18920a7a780eSAndrew Gallatin 		/*
18930a7a780eSAndrew Gallatin 		 * for IPv6 TSO, the "checksum offset" is re-purposed
18940a7a780eSAndrew Gallatin 		 * to store the TCP header len
18950a7a780eSAndrew Gallatin 		 */
18960a7a780eSAndrew Gallatin 		cksum_offset = (pi->tcp->th_off << 2);
18970a7a780eSAndrew Gallatin 	}
18980a7a780eSAndrew Gallatin 
18991e413cf9SAndrew Gallatin 	tx = &ss->tx;
1900aed8e389SAndrew Gallatin 	req = tx->req_list;
1901aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1902aed8e389SAndrew Gallatin 	cnt = 0;
1903aed8e389SAndrew Gallatin 	rdma_count = 0;
1904aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1905aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1906aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1907aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1908aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1909aed8e389SAndrew Gallatin 	 *
1910aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1911aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1912aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1913aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1914aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1915aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1916aed8e389SAndrew Gallatin 	 *
1917aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1918aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1919aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1920aed8e389SAndrew Gallatin 	 */
1921aed8e389SAndrew Gallatin 
1922aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1923aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1924aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1925aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1926e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1927aed8e389SAndrew Gallatin 
1928aed8e389SAndrew Gallatin 		while (len) {
1929aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1930e39a0a37SAndrew Gallatin 			seglen = len;
1931aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1932aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1933aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1934aed8e389SAndrew Gallatin 				/* payload */
1935aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1936aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1937aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1938aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1939aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1940aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1941aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1942aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1943aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1944aed8e389SAndrew Gallatin 				/* header ends */
1945aed8e389SAndrew Gallatin 				rdma_count = -1;
1946aed8e389SAndrew Gallatin 				cum_len_next = 0;
1947aed8e389SAndrew Gallatin 				seglen = -cum_len;
1948aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1949aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1950aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1951aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1952aed8e389SAndrew Gallatin 			    }
1953aed8e389SAndrew Gallatin 
1954aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1955aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1956aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1957aed8e389SAndrew Gallatin 			req->pad = 0;
1958aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1959aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1960aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1961aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1962aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1963aed8e389SAndrew Gallatin 			low += seglen;
1964aed8e389SAndrew Gallatin 			len -= seglen;
1965aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1966aed8e389SAndrew Gallatin 			flags = flags_next;
1967aed8e389SAndrew Gallatin 			req++;
1968aed8e389SAndrew Gallatin 			cnt++;
1969aed8e389SAndrew Gallatin 			rdma_count++;
19700a7a780eSAndrew Gallatin 			if (cksum_offset != 0 && !pi->ip6) {
1971aed8e389SAndrew Gallatin 				if (__predict_false(cksum_offset > seglen))
1972aed8e389SAndrew Gallatin 					cksum_offset -= seglen;
1973aed8e389SAndrew Gallatin 				else
1974aed8e389SAndrew Gallatin 					cksum_offset = 0;
19750a7a780eSAndrew Gallatin 			}
1976adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1977aed8e389SAndrew Gallatin 				goto drop;
1978aed8e389SAndrew Gallatin 		}
1979aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1980aed8e389SAndrew Gallatin 		seg++;
1981aed8e389SAndrew Gallatin 	}
1982aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1983aed8e389SAndrew Gallatin 
1984aed8e389SAndrew Gallatin 	do {
1985aed8e389SAndrew Gallatin 		req--;
1986aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1987aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1988aed8e389SAndrew Gallatin 
1989aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1990aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1991c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1992c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1993c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1994c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1995c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1996c6cb3e3fSAndrew Gallatin 		tx->activate++;
1997c6cb3e3fSAndrew Gallatin 		wmb();
1998c6cb3e3fSAndrew Gallatin 	}
1999c6cb3e3fSAndrew Gallatin #endif
2000aed8e389SAndrew Gallatin 	return;
2001aed8e389SAndrew Gallatin 
2002aed8e389SAndrew Gallatin drop:
2003e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
2004aed8e389SAndrew Gallatin 	m_freem(m);
2005c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2006aed8e389SAndrew Gallatin 	if (!once) {
2007adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
2008adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
2009adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
2010aed8e389SAndrew Gallatin 		once = 1;
2011aed8e389SAndrew Gallatin 	}
2012aed8e389SAndrew Gallatin 	return;
2013aed8e389SAndrew Gallatin 
2014aed8e389SAndrew Gallatin }
2015aed8e389SAndrew Gallatin 
201637d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
201737d89b0cSAndrew Gallatin 
201837d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2019c792928fSAndrew Gallatin /*
2020c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
2021c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
2022c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
2023c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
2024c792928fSAndrew Gallatin  */
2025c792928fSAndrew Gallatin static struct mbuf *
2026c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
2027c792928fSAndrew Gallatin {
2028c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2029c792928fSAndrew Gallatin 
2030c6499eccSGleb Smirnoff 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
2031c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
2032c792928fSAndrew Gallatin 		return NULL;
2033c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
2034c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
2035c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2036c792928fSAndrew Gallatin 			return NULL;
2037c792928fSAndrew Gallatin 	}
2038c792928fSAndrew Gallatin 	/*
2039c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2040c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2041c792928fSAndrew Gallatin 	 */
2042c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2043c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2044c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2045c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2046c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2047c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2048c792928fSAndrew Gallatin 	return m;
2049c792928fSAndrew Gallatin }
205037d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2051c792928fSAndrew Gallatin 
2052aed8e389SAndrew Gallatin static void
20531e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2054b2fc195eSAndrew Gallatin {
20550a7a780eSAndrew Gallatin 	struct mxge_pkt_info pi = {0,0,0,0};
20561e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2057b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2058b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2059b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
2060b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20611e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
20620a7a780eSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
2063aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2064aed8e389SAndrew Gallatin 	uint8_t flags, cksum_offset;
2065b2fc195eSAndrew Gallatin 
2066b2fc195eSAndrew Gallatin 
20671e413cf9SAndrew Gallatin 	sc = ss->sc;
2068b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20691e413cf9SAndrew Gallatin 	tx = &ss->tx;
2070b2fc195eSAndrew Gallatin 
207137d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2072c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2073c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2074c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
20750a7a780eSAndrew Gallatin 			goto drop_without_m;
2076c792928fSAndrew Gallatin 	}
207737d89b0cSAndrew Gallatin #endif
20780a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
20790a7a780eSAndrew Gallatin 	    (CSUM_TSO | CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
20800a7a780eSAndrew Gallatin 		if (mxge_parse_tx(ss, m, &pi))
20810a7a780eSAndrew Gallatin 			goto drop;
20820a7a780eSAndrew Gallatin 	}
20830a7a780eSAndrew Gallatin 
2084b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2085b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2086b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2087aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2088b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2089adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2090b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2091b2fc195eSAndrew Gallatin 		   to defrag */
2092b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2093b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2094b2fc195eSAndrew Gallatin 			goto drop;
2095b2fc195eSAndrew Gallatin 		}
20961e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2097b2fc195eSAndrew Gallatin 		m = m_tmp;
2098b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2099b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2100aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2101b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2102b2fc195eSAndrew Gallatin 	}
2103adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2104aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2105aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2106b2fc195eSAndrew Gallatin 		goto drop;
2107b2fc195eSAndrew Gallatin 	}
2108b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2109b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
21105e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2111b2fc195eSAndrew Gallatin 
211237d89b0cSAndrew Gallatin #if IFCAP_TSO4
2113aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2114aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
21150a7a780eSAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, &pi);
2116aed8e389SAndrew Gallatin 		return;
2117aed8e389SAndrew Gallatin 	}
211837d89b0cSAndrew Gallatin #endif
2119aed8e389SAndrew Gallatin 
2120b2fc195eSAndrew Gallatin 	req = tx->req_list;
2121b2fc195eSAndrew Gallatin 	cksum_offset = 0;
21225e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
21235e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2124b2fc195eSAndrew Gallatin 
2125b2fc195eSAndrew Gallatin 	/* checksum offloading? */
21260a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
21270a7a780eSAndrew Gallatin 	    (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
2128aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2129aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
21300a7a780eSAndrew Gallatin 		cksum_offset = pi.ip_off + pi.ip_hlen;
2131b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
21325e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2133b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
21345e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2135aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2136aed8e389SAndrew Gallatin 	} else {
2137aed8e389SAndrew Gallatin 		odd_flag = 0;
2138b2fc195eSAndrew Gallatin 	}
21395e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21405e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2141b2fc195eSAndrew Gallatin 
2142b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2143b2fc195eSAndrew Gallatin 	cum_len = 0;
2144aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21455e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2146b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2147b2fc195eSAndrew Gallatin 		req->addr_low =
21486d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2149b2fc195eSAndrew Gallatin 		req->addr_high =
21506d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2151b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2152b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2153b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2154b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2155b2fc195eSAndrew Gallatin 		else
2156b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21575e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21585e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21595e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2160aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2161b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2162b2fc195eSAndrew Gallatin 		seg++;
2163b2fc195eSAndrew Gallatin 		req++;
2164b2fc195eSAndrew Gallatin 		req->flags = 0;
2165b2fc195eSAndrew Gallatin 	}
2166b2fc195eSAndrew Gallatin 	req--;
2167b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2168b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2169b2fc195eSAndrew Gallatin 		req++;
2170b2fc195eSAndrew Gallatin 		req->addr_low =
21716d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2172b2fc195eSAndrew Gallatin 		req->addr_high =
21736d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2174b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21755e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21765e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21775e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21785e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2179aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2180b2fc195eSAndrew Gallatin 		cnt++;
2181b2fc195eSAndrew Gallatin 	}
21825e7d8541SAndrew Gallatin 
21835e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
21845e7d8541SAndrew Gallatin #if 0
21855e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21865e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21875e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21885e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21895e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21905e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21915e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21925e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21935e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21945e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21955e7d8541SAndrew Gallatin 	}
21965e7d8541SAndrew Gallatin 	printf("--------------\n");
21975e7d8541SAndrew Gallatin #endif
21985e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21996d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2200c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2201c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2202c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2203c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2204c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2205c6cb3e3fSAndrew Gallatin 		tx->activate++;
2206c6cb3e3fSAndrew Gallatin 		wmb();
2207c6cb3e3fSAndrew Gallatin 	}
2208c6cb3e3fSAndrew Gallatin #endif
2209b2fc195eSAndrew Gallatin 	return;
2210b2fc195eSAndrew Gallatin 
2211b2fc195eSAndrew Gallatin drop:
2212b2fc195eSAndrew Gallatin 	m_freem(m);
22130a7a780eSAndrew Gallatin drop_without_m:
2214c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2215b2fc195eSAndrew Gallatin 	return;
2216b2fc195eSAndrew Gallatin }
2217b2fc195eSAndrew Gallatin 
2218c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2219c6cb3e3fSAndrew Gallatin static void
2220c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2221c6cb3e3fSAndrew Gallatin {
2222c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2223c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2224c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2225c6cb3e3fSAndrew Gallatin 	int slice;
2226b2fc195eSAndrew Gallatin 
2227c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2228c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2229c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2230c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2231c6cb3e3fSAndrew Gallatin 			m_freem(m);
2232c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2233c6cb3e3fSAndrew Gallatin 	}
2234c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2235c6cb3e3fSAndrew Gallatin }
22366d914a32SAndrew Gallatin 
2237c6cb3e3fSAndrew Gallatin static inline void
2238c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2239c6cb3e3fSAndrew Gallatin {
2240c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2241c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2242c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2243c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2244c6cb3e3fSAndrew Gallatin 
2245c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2246c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2247c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2248c6cb3e3fSAndrew Gallatin 
2249c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2250c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2251c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2252c6cb3e3fSAndrew Gallatin 			return;
2253c6cb3e3fSAndrew Gallatin 		}
2254c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2255c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2256c6cb3e3fSAndrew Gallatin 
2257c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2258c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2259c6cb3e3fSAndrew Gallatin 	}
2260c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2261c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2262c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2263c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2264c6cb3e3fSAndrew Gallatin 		tx->stall++;
2265c6cb3e3fSAndrew Gallatin 	}
2266c6cb3e3fSAndrew Gallatin }
2267c6cb3e3fSAndrew Gallatin 
2268c6cb3e3fSAndrew Gallatin static int
2269c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2270c6cb3e3fSAndrew Gallatin {
2271c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2272c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2273c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2274c6cb3e3fSAndrew Gallatin 	int err;
2275c6cb3e3fSAndrew Gallatin 
2276c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2277c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2278c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2279c6cb3e3fSAndrew Gallatin 
2280c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2281c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2282c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2283c6cb3e3fSAndrew Gallatin 		return (err);
2284c6cb3e3fSAndrew Gallatin 	}
2285c6cb3e3fSAndrew Gallatin 
2286193cbc4dSMax Laier 	if (!drbr_needs_enqueue(ifp, tx->br) &&
2287c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2288c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2289c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2290c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2291c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2292c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2293c6cb3e3fSAndrew Gallatin 		return (err);
2294c6cb3e3fSAndrew Gallatin 	}
2295c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2296c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2297c6cb3e3fSAndrew Gallatin 	return (0);
2298c6cb3e3fSAndrew Gallatin }
2299c6cb3e3fSAndrew Gallatin 
2300c6cb3e3fSAndrew Gallatin static int
2301c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2302c6cb3e3fSAndrew Gallatin {
2303c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2304c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2305c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2306c6cb3e3fSAndrew Gallatin 	int err = 0;
2307c6cb3e3fSAndrew Gallatin 	int slice;
2308c6cb3e3fSAndrew Gallatin 
2309c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2310c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2311c6cb3e3fSAndrew Gallatin 
2312c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2313c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2314c6cb3e3fSAndrew Gallatin 
2315c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2316c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2317c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2318c6cb3e3fSAndrew Gallatin 	} else {
2319c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2320c6cb3e3fSAndrew Gallatin 	}
2321c6cb3e3fSAndrew Gallatin 
2322c6cb3e3fSAndrew Gallatin 	return (err);
2323c6cb3e3fSAndrew Gallatin }
2324c6cb3e3fSAndrew Gallatin 
2325c6cb3e3fSAndrew Gallatin #else
23266d914a32SAndrew Gallatin 
23276d914a32SAndrew Gallatin static inline void
23281e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2329b2fc195eSAndrew Gallatin {
23301e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2331b2fc195eSAndrew Gallatin 	struct mbuf *m;
2332b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
23331e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2334b2fc195eSAndrew Gallatin 
23351e413cf9SAndrew Gallatin 	sc = ss->sc;
2336b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23371e413cf9SAndrew Gallatin 	tx = &ss->tx;
2338adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
23396d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
23406d914a32SAndrew Gallatin 		if (m == NULL) {
23416d914a32SAndrew Gallatin 			return;
23426d914a32SAndrew Gallatin 		}
2343b2fc195eSAndrew Gallatin 		/* let BPF see it */
2344b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2345b2fc195eSAndrew Gallatin 
2346b2fc195eSAndrew Gallatin 		/* give it to the nic */
23471e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
23486d914a32SAndrew Gallatin 	}
23496d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2350a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2351b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2352adae7080SAndrew Gallatin 		tx->stall++;
2353a82c2581SAndrew Gallatin 	}
2354b2fc195eSAndrew Gallatin }
2355c6cb3e3fSAndrew Gallatin #endif
2356b2fc195eSAndrew Gallatin static void
23576d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2358b2fc195eSAndrew Gallatin {
23596d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
23601e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2361b2fc195eSAndrew Gallatin 
23621e413cf9SAndrew Gallatin 	/* only use the first slice for now */
23631e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
23641e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
23651e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
23661e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2367b2fc195eSAndrew Gallatin }
2368b2fc195eSAndrew Gallatin 
23695e7d8541SAndrew Gallatin /*
23705e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23715e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23725e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23735e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23745e7d8541SAndrew Gallatin  * in a burst
23755e7d8541SAndrew Gallatin  */
23765e7d8541SAndrew Gallatin static inline void
23775e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23785e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
23795e7d8541SAndrew Gallatin {
23805e7d8541SAndrew Gallatin 	uint32_t low;
23815e7d8541SAndrew Gallatin 
23825e7d8541SAndrew Gallatin 	low = src->addr_low;
23835e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2384a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
238573c7c83fSAndrew Gallatin 	wmb();
2386a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
238773c7c83fSAndrew Gallatin 	wmb();
238840385a5fSAndrew Gallatin 	src->addr_low = low;
23895e7d8541SAndrew Gallatin 	dst->addr_low = low;
239073c7c83fSAndrew Gallatin 	wmb();
23915e7d8541SAndrew Gallatin }
23925e7d8541SAndrew Gallatin 
2393b2fc195eSAndrew Gallatin static int
23941e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2395b2fc195eSAndrew Gallatin {
2396b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2397b2fc195eSAndrew Gallatin 	struct mbuf *m;
23981e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2399b2fc195eSAndrew Gallatin 	int cnt, err;
2400b2fc195eSAndrew Gallatin 
2401c6499eccSGleb Smirnoff 	m = m_gethdr(M_NOWAIT, MT_DATA);
2402b2fc195eSAndrew Gallatin 	if (m == NULL) {
2403b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2404b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2405b2fc195eSAndrew Gallatin 		goto done;
2406b2fc195eSAndrew Gallatin 	}
2407b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2408b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2409b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2410b2fc195eSAndrew Gallatin 	if (err != 0) {
2411b2fc195eSAndrew Gallatin 		m_free(m);
2412b2fc195eSAndrew Gallatin 		goto done;
2413b2fc195eSAndrew Gallatin 	}
2414b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2415b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
24166d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2417b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
24186d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2419b2fc195eSAndrew Gallatin 
2420b2fc195eSAndrew Gallatin done:
2421adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2422adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2423b2fc195eSAndrew Gallatin 	return err;
2424b2fc195eSAndrew Gallatin }
2425b2fc195eSAndrew Gallatin 
2426b2fc195eSAndrew Gallatin static int
24271e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2428b2fc195eSAndrew Gallatin {
2429053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2430b2fc195eSAndrew Gallatin 	struct mbuf *m;
24311e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2432053e637fSAndrew Gallatin 	int cnt, err, i;
2433b2fc195eSAndrew Gallatin 
2434c6499eccSGleb Smirnoff 	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2435b2fc195eSAndrew Gallatin 	if (m == NULL) {
2436b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2437b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2438b2fc195eSAndrew Gallatin 		goto done;
2439b2fc195eSAndrew Gallatin 	}
24404d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2441b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2442053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2443b2fc195eSAndrew Gallatin 	if (err != 0) {
2444b2fc195eSAndrew Gallatin 		m_free(m);
2445b2fc195eSAndrew Gallatin 		goto done;
2446b2fc195eSAndrew Gallatin 	}
2447b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2448b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2449b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2450b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2451b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2452053e637fSAndrew Gallatin 
2453b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2454b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2455053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2456053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2457053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2458053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2459053e637fSAndrew Gallatin        }
2460b0f7b922SAndrew Gallatin #endif
2461b2fc195eSAndrew Gallatin 
2462b2fc195eSAndrew Gallatin done:
2463053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2464b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
24655e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
24665e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2467b2fc195eSAndrew Gallatin 		}
2468053e637fSAndrew Gallatin 		idx++;
2469053e637fSAndrew Gallatin 	}
2470b2fc195eSAndrew Gallatin 	return err;
2471b2fc195eSAndrew Gallatin }
2472b2fc195eSAndrew Gallatin 
247326dd49c6SAndrew Gallatin #ifdef INET6
247426dd49c6SAndrew Gallatin 
247526dd49c6SAndrew Gallatin static uint16_t
247626dd49c6SAndrew Gallatin mxge_csum_generic(uint16_t *raw, int len)
247726dd49c6SAndrew Gallatin {
247826dd49c6SAndrew Gallatin 	uint32_t csum;
247926dd49c6SAndrew Gallatin 
248026dd49c6SAndrew Gallatin 
248126dd49c6SAndrew Gallatin 	csum = 0;
248226dd49c6SAndrew Gallatin 	while (len > 0) {
248326dd49c6SAndrew Gallatin 		csum += *raw;
248426dd49c6SAndrew Gallatin 		raw++;
248526dd49c6SAndrew Gallatin 		len -= 2;
248626dd49c6SAndrew Gallatin 	}
248726dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
248826dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
248926dd49c6SAndrew Gallatin 	return (uint16_t)csum;
249026dd49c6SAndrew Gallatin }
249126dd49c6SAndrew Gallatin 
249226dd49c6SAndrew Gallatin static inline uint16_t
249326dd49c6SAndrew Gallatin mxge_rx_csum6(void *p, struct mbuf *m, uint32_t csum)
249426dd49c6SAndrew Gallatin {
249526dd49c6SAndrew Gallatin 	uint32_t partial;
249626dd49c6SAndrew Gallatin 	int nxt, cksum_offset;
249726dd49c6SAndrew Gallatin 	struct ip6_hdr *ip6 = p;
249826dd49c6SAndrew Gallatin 	uint16_t c;
249926dd49c6SAndrew Gallatin 
250026dd49c6SAndrew Gallatin 	nxt = ip6->ip6_nxt;
250126dd49c6SAndrew Gallatin 	cksum_offset = sizeof (*ip6) + ETHER_HDR_LEN;
250226dd49c6SAndrew Gallatin 	if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) {
250326dd49c6SAndrew Gallatin 		cksum_offset = ip6_lasthdr(m, ETHER_HDR_LEN,
250426dd49c6SAndrew Gallatin 					   IPPROTO_IPV6, &nxt);
250526dd49c6SAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
250626dd49c6SAndrew Gallatin 			return (1);
250726dd49c6SAndrew Gallatin 	}
250826dd49c6SAndrew Gallatin 
250926dd49c6SAndrew Gallatin 	/*
251026dd49c6SAndrew Gallatin 	 * IPv6 headers do not contain a checksum, and hence
251126dd49c6SAndrew Gallatin 	 * do not checksum to zero, so they don't "fall out"
251226dd49c6SAndrew Gallatin 	 * of the partial checksum calculation like IPv4
251326dd49c6SAndrew Gallatin 	 * headers do.  We need to fix the partial checksum by
251426dd49c6SAndrew Gallatin 	 * subtracting the checksum of the IPv6 header.
251526dd49c6SAndrew Gallatin 	 */
251626dd49c6SAndrew Gallatin 
251726dd49c6SAndrew Gallatin 	partial = mxge_csum_generic((uint16_t *)ip6, cksum_offset -
251826dd49c6SAndrew Gallatin 				    ETHER_HDR_LEN);
251926dd49c6SAndrew Gallatin 	csum += ~partial;
252026dd49c6SAndrew Gallatin 	csum +=	 (csum < ~partial);
252126dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
252226dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
252326dd49c6SAndrew Gallatin 	c = in6_cksum_pseudo(ip6, m->m_pkthdr.len - cksum_offset, nxt,
252426dd49c6SAndrew Gallatin 			     csum);
252526dd49c6SAndrew Gallatin 	c ^= 0xffff;
252626dd49c6SAndrew Gallatin 	return (c);
252726dd49c6SAndrew Gallatin }
252826dd49c6SAndrew Gallatin #endif /* INET6 */
25299b03b0f3SAndrew Gallatin /*
25309b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
25319b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
25329b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
25339b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2534053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2535053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
25369b03b0f3SAndrew Gallatin  */
25379b03b0f3SAndrew Gallatin 
2538053e637fSAndrew Gallatin static inline uint16_t
2539053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2540053e637fSAndrew Gallatin {
2541053e637fSAndrew Gallatin 	struct ether_header *eh;
254226dd49c6SAndrew Gallatin #ifdef INET
2543053e637fSAndrew Gallatin 	struct ip *ip;
254426dd49c6SAndrew Gallatin #endif
2545abc5b96bSAndrew Gallatin #if defined(INET) || defined(INET6)
2546abc5b96bSAndrew Gallatin 	int cap = m->m_pkthdr.rcvif->if_capenable;
2547abc5b96bSAndrew Gallatin #endif
254826dd49c6SAndrew Gallatin 	uint16_t c, etype;
254926dd49c6SAndrew Gallatin 
2550053e637fSAndrew Gallatin 
2551053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
255226dd49c6SAndrew Gallatin 	etype = ntohs(eh->ether_type);
255326dd49c6SAndrew Gallatin 	switch (etype) {
2554eb6219e3SAndrew Gallatin #ifdef INET
255526dd49c6SAndrew Gallatin 	case ETHERTYPE_IP:
255626dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM) == 0)
255726dd49c6SAndrew Gallatin 			return (1);
255826dd49c6SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
255926dd49c6SAndrew Gallatin 		if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP)
256026dd49c6SAndrew Gallatin 			return (1);
2561053e637fSAndrew Gallatin 		c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
256226dd49c6SAndrew Gallatin 			      htonl(ntohs(csum) + ntohs(ip->ip_len) -
256326dd49c6SAndrew Gallatin 				    (ip->ip_hl << 2) + ip->ip_p));
2564053e637fSAndrew Gallatin 		c ^= 0xffff;
256526dd49c6SAndrew Gallatin 		break;
256626dd49c6SAndrew Gallatin #endif
256726dd49c6SAndrew Gallatin #ifdef INET6
256826dd49c6SAndrew Gallatin 	case ETHERTYPE_IPV6:
256926dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM_IPV6) == 0)
257026dd49c6SAndrew Gallatin 			return (1);
257126dd49c6SAndrew Gallatin 		c = mxge_rx_csum6((eh + 1), m, csum);
257226dd49c6SAndrew Gallatin 		break;
257326dd49c6SAndrew Gallatin #endif
257426dd49c6SAndrew Gallatin 	default:
257526dd49c6SAndrew Gallatin 		c = 1;
257626dd49c6SAndrew Gallatin 	}
2577053e637fSAndrew Gallatin 	return (c);
25785e7d8541SAndrew Gallatin }
2579053e637fSAndrew Gallatin 
2580c792928fSAndrew Gallatin static void
2581c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2582c792928fSAndrew Gallatin {
2583c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2584c792928fSAndrew Gallatin 	struct ether_header *eh;
2585c792928fSAndrew Gallatin 	uint32_t partial;
2586c792928fSAndrew Gallatin 
2587c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2588c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2589c792928fSAndrew Gallatin 
2590c792928fSAndrew Gallatin 	/*
2591c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2592c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2593c792928fSAndrew Gallatin 	 * header.
2594c792928fSAndrew Gallatin 	 */
2595c792928fSAndrew Gallatin 
2596c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2597c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2598c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2599c792928fSAndrew Gallatin 	(*csum) += ~partial;
2600c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2601c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2602c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2603c792928fSAndrew Gallatin 
2604c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2605c792928fSAndrew Gallatin 	   later consumers expect this */
2606c792928fSAndrew Gallatin 	*csum = htons(*csum);
2607c792928fSAndrew Gallatin 
2608c792928fSAndrew Gallatin 	/* save the tag */
260937d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2610c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
261137d89b0cSAndrew Gallatin #else
261237d89b0cSAndrew Gallatin 	{
261337d89b0cSAndrew Gallatin 		struct m_tag *mtag;
261437d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
261537d89b0cSAndrew Gallatin 				   M_NOWAIT);
261637d89b0cSAndrew Gallatin 		if (mtag == NULL)
261737d89b0cSAndrew Gallatin 			return;
261837d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
261937d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
262037d89b0cSAndrew Gallatin 	}
262137d89b0cSAndrew Gallatin 
262237d89b0cSAndrew Gallatin #endif
262337d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2624c792928fSAndrew Gallatin 
2625c792928fSAndrew Gallatin 	/*
2626c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2627c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2628c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2629c792928fSAndrew Gallatin 	 * type field is already in place.
2630c792928fSAndrew Gallatin 	 */
2631c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2632c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2633c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2634c792928fSAndrew Gallatin }
2635c792928fSAndrew Gallatin 
26365e7d8541SAndrew Gallatin 
26375e7d8541SAndrew Gallatin static inline void
263826dd49c6SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len,
263926dd49c6SAndrew Gallatin 		 uint32_t csum, int lro)
2640b2fc195eSAndrew Gallatin {
26411e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2642b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2643053e637fSAndrew Gallatin 	struct mbuf *m;
2644c792928fSAndrew Gallatin 	struct ether_header *eh;
26451e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2646053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2647b2fc195eSAndrew Gallatin 	int idx;
2648b2fc195eSAndrew Gallatin 
26491e413cf9SAndrew Gallatin 	sc = ss->sc;
2650b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26511e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2652b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2653053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2654b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2655b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2656b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26571e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2658053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2659f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2660053e637fSAndrew Gallatin 		return;
2661b2fc195eSAndrew Gallatin 	}
2662053e637fSAndrew Gallatin 
2663b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2664b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2665b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2666b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2667b2fc195eSAndrew Gallatin 
2668b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2669b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2670b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2671b2fc195eSAndrew Gallatin 
2672053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2673053e637fSAndrew Gallatin 	 * aligned */
26745e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2675b2fc195eSAndrew Gallatin 
2676053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2677053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
26781e413cf9SAndrew Gallatin 	ss->ipackets++;
2679c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2680c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2681c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2682c792928fSAndrew Gallatin 	}
2683eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2684eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2685eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2686eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2687eacb70baSSepherosa Ziehau 	}
2688b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
268926dd49c6SAndrew Gallatin 	if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
269026dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
269126dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2692053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
269326dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
269426dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
269526dd49c6SAndrew Gallatin 
269626dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
269726dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, 0)))
269826dd49c6SAndrew Gallatin 			return;
269926dd49c6SAndrew Gallatin #endif
2700b2fc195eSAndrew Gallatin 	}
2701053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2702053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2703b2fc195eSAndrew Gallatin }
2704b2fc195eSAndrew Gallatin 
2705b2fc195eSAndrew Gallatin static inline void
270626dd49c6SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len,
270726dd49c6SAndrew Gallatin 		   uint32_t csum, int lro)
2708b2fc195eSAndrew Gallatin {
27091e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2710b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2711c792928fSAndrew Gallatin 	struct ether_header *eh;
2712b2fc195eSAndrew Gallatin 	struct mbuf *m;
27131e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2714b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2715b2fc195eSAndrew Gallatin 	int idx;
2716b2fc195eSAndrew Gallatin 
27171e413cf9SAndrew Gallatin 	sc = ss->sc;
2718b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
27191e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2720b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2721b2fc195eSAndrew Gallatin 	rx->cnt++;
2722b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2723b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2724b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
27251e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2726b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2727f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2728b2fc195eSAndrew Gallatin 		return;
2729b2fc195eSAndrew Gallatin 	}
2730b2fc195eSAndrew Gallatin 
2731b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2732b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2733b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2734b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2735b2fc195eSAndrew Gallatin 
2736b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2737b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2738b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2739b2fc195eSAndrew Gallatin 
2740b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2741b2fc195eSAndrew Gallatin 	 * aligned */
27425e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2743b2fc195eSAndrew Gallatin 
27449b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
27459b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
27461e413cf9SAndrew Gallatin 	ss->ipackets++;
2747c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2748c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2749c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2750c792928fSAndrew Gallatin 	}
2751eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2752eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2753eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2754eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2755eacb70baSSepherosa Ziehau 	}
2756b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
275726dd49c6SAndrew Gallatin 	if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
275826dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
275926dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2760053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
276126dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
276226dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
276326dd49c6SAndrew Gallatin 
276426dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
276526dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, csum)))
276626dd49c6SAndrew Gallatin 			return;
276726dd49c6SAndrew Gallatin #endif
2768053e637fSAndrew Gallatin 	}
2769b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2770b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2771b2fc195eSAndrew Gallatin }
2772b2fc195eSAndrew Gallatin 
2773b2fc195eSAndrew Gallatin static inline void
27741e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
27755e7d8541SAndrew Gallatin {
27761e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
27775e7d8541SAndrew Gallatin 	int limit = 0;
27785e7d8541SAndrew Gallatin 	uint16_t length;
27795e7d8541SAndrew Gallatin 	uint16_t checksum;
278026dd49c6SAndrew Gallatin 	int lro;
27815e7d8541SAndrew Gallatin 
278226dd49c6SAndrew Gallatin 	lro = ss->sc->ifp->if_capenable & IFCAP_LRO;
27835e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
27845e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
27855e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2786053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2787b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
278826dd49c6SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum, lro);
27895e7d8541SAndrew Gallatin 		else
279026dd49c6SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum, lro);
27915e7d8541SAndrew Gallatin 		rx_done->cnt++;
2792adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
27935e7d8541SAndrew Gallatin 
27945e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2795f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
27965e7d8541SAndrew Gallatin 			break;
2797053e637fSAndrew Gallatin 	}
279826dd49c6SAndrew Gallatin #if defined(INET)  || defined (INET6)
27996dd38b87SSepherosa Ziehau 	tcp_lro_flush_all(&ss->lc);
2800eb6219e3SAndrew Gallatin #endif
28015e7d8541SAndrew Gallatin }
28025e7d8541SAndrew Gallatin 
28035e7d8541SAndrew Gallatin 
28045e7d8541SAndrew Gallatin static inline void
28051e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2806b2fc195eSAndrew Gallatin {
2807b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
28081e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2809b2fc195eSAndrew Gallatin 	struct mbuf *m;
2810b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2811f616ebc7SAndrew Gallatin 	int idx;
2812c6cb3e3fSAndrew Gallatin 	int *flags;
2813b2fc195eSAndrew Gallatin 
28141e413cf9SAndrew Gallatin 	tx = &ss->tx;
28151e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
28165e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2817b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2818b2fc195eSAndrew Gallatin 		tx->done++;
2819b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2820b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2821b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2822b2fc195eSAndrew Gallatin 		if (m != NULL) {
282371032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
282471032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
282571032832SAndrew Gallatin 				ss->omcasts++;
2826c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2827b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2828b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2829b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2830b2fc195eSAndrew Gallatin 			m_freem(m);
2831b2fc195eSAndrew Gallatin 		}
28325e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
28335e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
28345e7d8541SAndrew Gallatin 			tx->pkt_done++;
28355e7d8541SAndrew Gallatin 		}
2836b2fc195eSAndrew Gallatin 	}
2837b2fc195eSAndrew Gallatin 
2838b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2839b2fc195eSAndrew Gallatin 	   its OK to send packets */
2840c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2841c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2842c6cb3e3fSAndrew Gallatin #else
2843c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2844c6cb3e3fSAndrew Gallatin #endif
28451e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2846c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2847c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2848c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
28491e413cf9SAndrew Gallatin 		ss->tx.wake++;
28501e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2851b2fc195eSAndrew Gallatin 	}
2852c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2853c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2854c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2855c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2856c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2857c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2858c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2859c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2860c6cb3e3fSAndrew Gallatin 			wmb();
2861c6cb3e3fSAndrew Gallatin 		}
2862c6cb3e3fSAndrew Gallatin 	}
2863c6cb3e3fSAndrew Gallatin #endif
2864c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2865c6cb3e3fSAndrew Gallatin 
2866b2fc195eSAndrew Gallatin }
2867b2fc195eSAndrew Gallatin 
286801638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2869c587e59fSAndrew Gallatin {
2870c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2871c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2872c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2873c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
287401638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2875c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2876c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2877c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2878c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2879c587e59fSAndrew Gallatin };
288001638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
288101638550SAndrew Gallatin {
288251bc2092SAndrew Gallatin 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
28834ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
288401638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
288501638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
288656b67858SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
288756b67858SAndrew Gallatin 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
288801638550SAndrew Gallatin };
2889c587e59fSAndrew Gallatin 
2890c587e59fSAndrew Gallatin static void
2891c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type)
2892c587e59fSAndrew Gallatin {
2893c406ad2eSAndrew Gallatin 
2894c406ad2eSAndrew Gallatin 
2895c406ad2eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type,
2896c406ad2eSAndrew Gallatin 		    0, NULL);
2897c406ad2eSAndrew Gallatin 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
2898c406ad2eSAndrew Gallatin 	sc->current_media = media_type;
2899c406ad2eSAndrew Gallatin 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
2900c587e59fSAndrew Gallatin }
2901c587e59fSAndrew Gallatin 
2902c587e59fSAndrew Gallatin static void
2903c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc)
2904c587e59fSAndrew Gallatin {
2905c587e59fSAndrew Gallatin 	char *ptr;
2906c406ad2eSAndrew Gallatin 	int i;
2907c587e59fSAndrew Gallatin 
2908c406ad2eSAndrew Gallatin 	ifmedia_removeall(&sc->media);
2909c406ad2eSAndrew Gallatin 	mxge_media_set(sc, IFM_AUTO);
2910c587e59fSAndrew Gallatin 
2911c587e59fSAndrew Gallatin 	/*
2912c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2913c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2914c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2915c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2916c587e59fSAndrew Gallatin 	 */
2917c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2918c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2919c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2920c406ad2eSAndrew Gallatin 		return;
2921c587e59fSAndrew Gallatin 	}
2922c587e59fSAndrew Gallatin 
2923c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
2924dc15eac0SEd Schouten 		ptr = strchr(ptr, '-');
2925c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2926c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2927c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2928c587e59fSAndrew Gallatin 			return;
2929c587e59fSAndrew Gallatin 		}
2930c587e59fSAndrew Gallatin 	}
293130882b10SAndrew Gallatin 	if (*ptr == 'C' || *(ptr +1) == 'C') {
293201638550SAndrew Gallatin 		/* -C is CX4 */
2933c406ad2eSAndrew Gallatin 		sc->connector = MXGE_CX4;
2934c406ad2eSAndrew Gallatin 		mxge_media_set(sc, IFM_10G_CX4);
2935c406ad2eSAndrew Gallatin 	} else if (*ptr == 'Q') {
293601638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2937c406ad2eSAndrew Gallatin 		sc->connector = MXGE_QRF;
2938c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2939c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2940c406ad2eSAndrew Gallatin 	} else if (*ptr == 'R') {
2941c406ad2eSAndrew Gallatin 		/* -R is XFP */
2942c406ad2eSAndrew Gallatin 		sc->connector = MXGE_XFP;
2943c406ad2eSAndrew Gallatin 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
2944c406ad2eSAndrew Gallatin 		/* -S or -2S is SFP+ */
2945c406ad2eSAndrew Gallatin 		sc->connector = MXGE_SFP;
2946c406ad2eSAndrew Gallatin 	} else {
2947c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2948c406ad2eSAndrew Gallatin 	}
2949c587e59fSAndrew Gallatin }
2950c587e59fSAndrew Gallatin 
2951c406ad2eSAndrew Gallatin /*
2952c406ad2eSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2953c406ad2eSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2954c406ad2eSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2955c406ad2eSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2956c406ad2eSAndrew Gallatin  * than in the interrupt handler itself.
2957c406ad2eSAndrew Gallatin  */
2958c406ad2eSAndrew Gallatin static void
2959c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2960c406ad2eSAndrew Gallatin {
2961c406ad2eSAndrew Gallatin 	mxge_cmd_t cmd;
2962c406ad2eSAndrew Gallatin 	char *cage_type;
2963c406ad2eSAndrew Gallatin 
2964c406ad2eSAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
2965c406ad2eSAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
2966c406ad2eSAndrew Gallatin 	uint32_t byte;
2967c406ad2eSAndrew Gallatin 
2968c406ad2eSAndrew Gallatin 	sc->need_media_probe = 0;
2969c406ad2eSAndrew Gallatin 
2970c406ad2eSAndrew Gallatin 	if (sc->connector == MXGE_XFP) {
297101638550SAndrew Gallatin 		/* -R is XFP */
297201638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
297301638550SAndrew Gallatin 		mxge_media_type_entries =
297473a1170aSPedro F. Giffuni 			nitems(mxge_xfp_media_types);
297501638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
297601638550SAndrew Gallatin 		cage_type = "XFP";
2977c406ad2eSAndrew Gallatin 	} else 	if (sc->connector == MXGE_SFP) {
297801638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
297901638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
298001638550SAndrew Gallatin 		mxge_media_type_entries =
298173a1170aSPedro F. Giffuni 			nitems(mxge_sfp_media_types);
298201638550SAndrew Gallatin 		cage_type = "SFP+";
298301638550SAndrew Gallatin 		byte = 3;
2984c406ad2eSAndrew Gallatin 	} else {
2985c406ad2eSAndrew Gallatin 		/* nothing to do; media type cannot change */
2986c587e59fSAndrew Gallatin 		return;
2987c587e59fSAndrew Gallatin 	}
2988c587e59fSAndrew Gallatin 
2989c587e59fSAndrew Gallatin 	/*
2990c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2991c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2992c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2993c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2994c587e59fSAndrew Gallatin 	 * a millisecond
2995c587e59fSAndrew Gallatin 	 */
2996c587e59fSAndrew Gallatin 
2997c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
299801638550SAndrew Gallatin 	cmd.data1 = byte;
299901638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
300001638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
3001c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
3002c587e59fSAndrew Gallatin 	}
300301638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
300401638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
3005c587e59fSAndrew Gallatin 	}
3006c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
3007c587e59fSAndrew Gallatin 		return;
3008c587e59fSAndrew Gallatin 	}
3009c587e59fSAndrew Gallatin 
3010c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
301101638550SAndrew Gallatin 	cmd.data0 = byte;
301201638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3013c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
3014c587e59fSAndrew Gallatin 		DELAY(1000);
301501638550SAndrew Gallatin 		cmd.data0 = byte;
301601638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3017c587e59fSAndrew Gallatin 	}
3018c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
301901638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
302001638550SAndrew Gallatin 			      cage_type, err, ms);
3021c587e59fSAndrew Gallatin 		return;
3022c587e59fSAndrew Gallatin 	}
3023c587e59fSAndrew Gallatin 
3024c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
3025c587e59fSAndrew Gallatin 		if (mxge_verbose)
302601638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
3027c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
3028c406ad2eSAndrew Gallatin 		if (sc->current_media != mxge_media_types[0].flag) {
3029c406ad2eSAndrew Gallatin 			mxge_media_init(sc);
3030c406ad2eSAndrew Gallatin 			mxge_media_set(sc, mxge_media_types[0].flag);
3031c406ad2eSAndrew Gallatin 		}
3032c587e59fSAndrew Gallatin 		return;
3033c587e59fSAndrew Gallatin 	}
303401638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
3035c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
3036c587e59fSAndrew Gallatin 			if (mxge_verbose)
303701638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
303801638550SAndrew Gallatin 					      cage_type,
3039c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
3040c587e59fSAndrew Gallatin 
3041c406ad2eSAndrew Gallatin 			if (sc->current_media != mxge_media_types[i].flag) {
3042c406ad2eSAndrew Gallatin 				mxge_media_init(sc);
3043c406ad2eSAndrew Gallatin 				mxge_media_set(sc, mxge_media_types[i].flag);
3044c406ad2eSAndrew Gallatin 			}
3045c587e59fSAndrew Gallatin 			return;
3046c587e59fSAndrew Gallatin 		}
3047c587e59fSAndrew Gallatin 	}
3048c406ad2eSAndrew Gallatin 	if (mxge_verbose)
3049c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "%s media 0x%x unknown\n",
3050c406ad2eSAndrew Gallatin 			      cage_type, cmd.data0);
3051c587e59fSAndrew Gallatin 
3052c587e59fSAndrew Gallatin 	return;
3053c587e59fSAndrew Gallatin }
3054c587e59fSAndrew Gallatin 
3055b2fc195eSAndrew Gallatin static void
30566d87a65dSAndrew Gallatin mxge_intr(void *arg)
3057b2fc195eSAndrew Gallatin {
30581e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
30591e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
30601e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
30611e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
30621e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
30635e7d8541SAndrew Gallatin 	uint32_t send_done_count;
30645e7d8541SAndrew Gallatin 	uint8_t valid;
3065b2fc195eSAndrew Gallatin 
3066b2fc195eSAndrew Gallatin 
3067c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
30681e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
30691e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
30701e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
30711e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
30721e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
30731e413cf9SAndrew Gallatin 		return;
30741e413cf9SAndrew Gallatin 	}
3075c6cb3e3fSAndrew Gallatin #endif
30761e413cf9SAndrew Gallatin 
30775e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
30785e7d8541SAndrew Gallatin 	if (!stats->valid) {
30795e7d8541SAndrew Gallatin 		return;
3080b2fc195eSAndrew Gallatin 	}
30815e7d8541SAndrew Gallatin 	valid = stats->valid;
3082b2fc195eSAndrew Gallatin 
308391ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
30845e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
30855e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
30865e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
30875e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
30885e7d8541SAndrew Gallatin 			stats->valid = 0;
3089dc8731d4SAndrew Gallatin 	} else {
3090dc8731d4SAndrew Gallatin 		stats->valid = 0;
3091dc8731d4SAndrew Gallatin 	}
3092dc8731d4SAndrew Gallatin 
3093dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
30945e7d8541SAndrew Gallatin 	do {
30955e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
30965e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
30975e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
30985e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
3099c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
31001e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
31011e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
31025e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
3103b2fc195eSAndrew Gallatin 		}
310491ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
310573c7c83fSAndrew Gallatin 			wmb();
31065e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
3107b2fc195eSAndrew Gallatin 
3108c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
3109c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
31105e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
31115e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
3112b2fc195eSAndrew Gallatin 			if (sc->link_state) {
31135e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
31145e7d8541SAndrew Gallatin 				if (mxge_verbose)
31155e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
3116b2fc195eSAndrew Gallatin 			} else {
31175e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
31185e7d8541SAndrew Gallatin 				if (mxge_verbose)
31195e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
3120b2fc195eSAndrew Gallatin 			}
3121c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
3122b2fc195eSAndrew Gallatin 		}
3123b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
31241e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
3125b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
31261e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
31275e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
31285e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
31295e7d8541SAndrew Gallatin 		}
3130c587e59fSAndrew Gallatin 
3131c587e59fSAndrew Gallatin 		if (stats->link_down) {
31325e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3133c587e59fSAndrew Gallatin 			sc->link_state = 0;
3134c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3135c587e59fSAndrew Gallatin 		}
3136b2fc195eSAndrew Gallatin 	}
3137b2fc195eSAndrew Gallatin 
31385e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
31395e7d8541SAndrew Gallatin 	if (valid & 0x1)
31401e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
31411e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3142b2fc195eSAndrew Gallatin }
3143b2fc195eSAndrew Gallatin 
3144b2fc195eSAndrew Gallatin static void
31456d87a65dSAndrew Gallatin mxge_init(void *arg)
3146b2fc195eSAndrew Gallatin {
31473cae7311SAndrew Gallatin 	mxge_softc_t *sc = arg;
31483cae7311SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
31493cae7311SAndrew Gallatin 
31503cae7311SAndrew Gallatin 
31513cae7311SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
31523cae7311SAndrew Gallatin 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
31533cae7311SAndrew Gallatin 		(void) mxge_open(sc);
31543cae7311SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3155b2fc195eSAndrew Gallatin }
3156b2fc195eSAndrew Gallatin 
3157b2fc195eSAndrew Gallatin 
3158b2fc195eSAndrew Gallatin 
3159b2fc195eSAndrew Gallatin static void
31601e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
31611e413cf9SAndrew Gallatin {
31621e413cf9SAndrew Gallatin 	int i;
31631e413cf9SAndrew Gallatin 
316426dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
316526dd49c6SAndrew Gallatin 	tcp_lro_free(&ss->lc);
316626dd49c6SAndrew Gallatin #endif
31671e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
31681e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
31691e413cf9SAndrew Gallatin 			continue;
31701e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
31711e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
31721e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
31731e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
31741e413cf9SAndrew Gallatin 	}
31751e413cf9SAndrew Gallatin 
31761e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
31771e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
31781e413cf9SAndrew Gallatin 			continue;
31791e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
31801e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
31811e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
31821e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
31831e413cf9SAndrew Gallatin 	}
31841e413cf9SAndrew Gallatin 
31851e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
31861e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
31871e413cf9SAndrew Gallatin 		return;
31881e413cf9SAndrew Gallatin 
31891e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
31901e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
31911e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
31921e413cf9SAndrew Gallatin 			continue;
31931e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
31941e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
31951e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
31961e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
31971e413cf9SAndrew Gallatin 	}
31981e413cf9SAndrew Gallatin }
31991e413cf9SAndrew Gallatin 
32001e413cf9SAndrew Gallatin static void
32016d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3202b2fc195eSAndrew Gallatin {
32031e413cf9SAndrew Gallatin 	int slice;
32041e413cf9SAndrew Gallatin 
32051e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
32061e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
32071e413cf9SAndrew Gallatin }
32081e413cf9SAndrew Gallatin 
32091e413cf9SAndrew Gallatin static void
32101e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
32111e413cf9SAndrew Gallatin {
3212b2fc195eSAndrew Gallatin 	int i;
3213b2fc195eSAndrew Gallatin 
3214b2fc195eSAndrew Gallatin 
32151e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
32161e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
32171e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3218b2fc195eSAndrew Gallatin 
32191e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
32201e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
32211e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
32221e413cf9SAndrew Gallatin 
32231e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
32241e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
32251e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
32261e413cf9SAndrew Gallatin 
32271e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
32281e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
32291e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
32301e413cf9SAndrew Gallatin 
32311e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
32321e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
32331e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
32341e413cf9SAndrew Gallatin 
32351e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
32361e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
32371e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
32381e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
32391e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3240b2fc195eSAndrew Gallatin 			}
32411e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
32421e413cf9SAndrew Gallatin 		}
32431e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
32441e413cf9SAndrew Gallatin 	}
32451e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
32461e413cf9SAndrew Gallatin 
32471e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
32481e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
32491e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
32501e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
32511e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
32521e413cf9SAndrew Gallatin 			}
32531e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
32541e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
32551e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
32561e413cf9SAndrew Gallatin 		}
32571e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
32581e413cf9SAndrew Gallatin 	}
32591e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
32601e413cf9SAndrew Gallatin 
32611e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
32621e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
32631e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
32641e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
32651e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
32661e413cf9SAndrew Gallatin 			}
32671e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
32681e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
32691e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
32701e413cf9SAndrew Gallatin 		}
32711e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
32721e413cf9SAndrew Gallatin 	}
32731e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3274b2fc195eSAndrew Gallatin }
3275b2fc195eSAndrew Gallatin 
3276b2fc195eSAndrew Gallatin static void
32776d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3278b2fc195eSAndrew Gallatin {
32791e413cf9SAndrew Gallatin 	int slice;
3280b2fc195eSAndrew Gallatin 
32811e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
32821e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3283c2657176SAndrew Gallatin }
3284b2fc195eSAndrew Gallatin 
3285b2fc195eSAndrew Gallatin static int
32861e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
32871e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3288b2fc195eSAndrew Gallatin {
32891e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
32901e413cf9SAndrew Gallatin 	size_t bytes;
32911e413cf9SAndrew Gallatin 	int err, i;
3292b2fc195eSAndrew Gallatin 
32931e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3294adae7080SAndrew Gallatin 
32951e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
32961e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3297aed8e389SAndrew Gallatin 
3298b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
32991e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
33001e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3301b2fc195eSAndrew Gallatin 
33021e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
33031e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3304b2fc195eSAndrew Gallatin 
33051e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
33061e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
33071e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3308b2fc195eSAndrew Gallatin 
33091e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
33101e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3311b2fc195eSAndrew Gallatin 
33121e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3313b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3314b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3315b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3316b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3317b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3318b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3319b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3320b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3321b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3322b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3323b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
33241e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3325b2fc195eSAndrew Gallatin 	if (err != 0) {
3326b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3327b2fc195eSAndrew Gallatin 			      err);
3328c2ede4b3SMartin Blapp 		return err;
3329b2fc195eSAndrew Gallatin 	}
3330b2fc195eSAndrew Gallatin 
3331b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3332b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3333b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3334b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3335b0f7b922SAndrew Gallatin #else
3336b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3337b0f7b922SAndrew Gallatin #endif
3338b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3339b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3340b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3341053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3342b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3343053e637fSAndrew Gallatin 				 3,			/* num segs */
3344b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3345b0f7b922SAndrew Gallatin #else
3346b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3347b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3348b0f7b922SAndrew Gallatin #endif
3349b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3350b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
33511e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3352b2fc195eSAndrew Gallatin 	if (err != 0) {
3353b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3354b2fc195eSAndrew Gallatin 			      err);
3355c2ede4b3SMartin Blapp 		return err;
3356b2fc195eSAndrew Gallatin 	}
33571e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
33581e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
33591e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3360b2fc195eSAndrew Gallatin 		if (err != 0) {
3361b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3362b2fc195eSAndrew Gallatin 				      err);
3363c2ede4b3SMartin Blapp 			return err;
3364b2fc195eSAndrew Gallatin 		}
3365b2fc195eSAndrew Gallatin 	}
33661e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
33671e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3368b2fc195eSAndrew Gallatin 	if (err != 0) {
3369b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3370b2fc195eSAndrew Gallatin 			      err);
3371c2ede4b3SMartin Blapp 		return err;
3372b2fc195eSAndrew Gallatin 	}
3373b2fc195eSAndrew Gallatin 
33741e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
33751e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
33761e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3377b2fc195eSAndrew Gallatin 		if (err != 0) {
3378b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3379b2fc195eSAndrew Gallatin 				      err);
3380c2ede4b3SMartin Blapp 			return err;
3381b2fc195eSAndrew Gallatin 		}
3382b2fc195eSAndrew Gallatin 	}
33831e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
33841e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3385b2fc195eSAndrew Gallatin 	if (err != 0) {
3386b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3387b2fc195eSAndrew Gallatin 			      err);
3388c2ede4b3SMartin Blapp 		return err;
33891e413cf9SAndrew Gallatin 	}
33901e413cf9SAndrew Gallatin 
3391b78540b1SGabor Kovesdan 	/* now allocate TX resources */
33921e413cf9SAndrew Gallatin 
3393c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
33941e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
33951e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
33961e413cf9SAndrew Gallatin 		return 0;
3397c6cb3e3fSAndrew Gallatin #endif
33981e413cf9SAndrew Gallatin 
33991e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
34001e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
34011e413cf9SAndrew Gallatin 
34021e413cf9SAndrew Gallatin 
34031e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
34041e413cf9SAndrew Gallatin 	bytes = 8 +
34051e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
34061e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
34071e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
34081e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
34091e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
34101e413cf9SAndrew Gallatin 
34111e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
34121e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
34131e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
34141e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
34151e413cf9SAndrew Gallatin 
34161e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
34171e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
34181e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
34191e413cf9SAndrew Gallatin 
34201e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
34211e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
34221e413cf9SAndrew Gallatin 				 1,			/* alignment */
34231e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
34241e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
34251e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
34261e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
34271e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
34281e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
34291e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
34301e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
34311e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
34321e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
34331e413cf9SAndrew Gallatin 
34341e413cf9SAndrew Gallatin 	if (err != 0) {
34351e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
34361e413cf9SAndrew Gallatin 			      err);
3437c2ede4b3SMartin Blapp 		return err;
34381e413cf9SAndrew Gallatin 	}
34391e413cf9SAndrew Gallatin 
34401e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
34411e413cf9SAndrew Gallatin 	   in the ring */
34421e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
34431e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
34441e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
34451e413cf9SAndrew Gallatin 		if (err != 0) {
34461e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
34471e413cf9SAndrew Gallatin 				      err);
3448c2ede4b3SMartin Blapp 			return err;
34491e413cf9SAndrew Gallatin 		}
3450b2fc195eSAndrew Gallatin 	}
3451b2fc195eSAndrew Gallatin 	return 0;
3452b2fc195eSAndrew Gallatin 
3453b2fc195eSAndrew Gallatin }
3454b2fc195eSAndrew Gallatin 
34551e413cf9SAndrew Gallatin static int
34561e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
34571e413cf9SAndrew Gallatin {
34581e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
34591e413cf9SAndrew Gallatin 	int tx_ring_size;
34601e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
34611e413cf9SAndrew Gallatin 	int err, slice;
34621e413cf9SAndrew Gallatin 
34631e413cf9SAndrew Gallatin 	/* get ring sizes */
34641e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
34651e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
34661e413cf9SAndrew Gallatin 	if (err != 0) {
34671e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
34681e413cf9SAndrew Gallatin 		goto abort;
34691e413cf9SAndrew Gallatin 	}
34701e413cf9SAndrew Gallatin 
34711e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
34721e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
34731e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
34741e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
34751e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
34761e413cf9SAndrew Gallatin 
34771e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
34781e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
34791e413cf9SAndrew Gallatin 					     rx_ring_entries,
34801e413cf9SAndrew Gallatin 					     tx_ring_entries);
34811e413cf9SAndrew Gallatin 		if (err != 0)
34821e413cf9SAndrew Gallatin 			goto abort;
34831e413cf9SAndrew Gallatin 	}
34841e413cf9SAndrew Gallatin 	return 0;
34851e413cf9SAndrew Gallatin 
34861e413cf9SAndrew Gallatin abort:
34871e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
34881e413cf9SAndrew Gallatin 	return err;
34891e413cf9SAndrew Gallatin 
34901e413cf9SAndrew Gallatin }
34911e413cf9SAndrew Gallatin 
34921e413cf9SAndrew Gallatin 
3493053e637fSAndrew Gallatin static void
3494053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3495053e637fSAndrew Gallatin {
3496c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3497053e637fSAndrew Gallatin 
3498053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3499053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3500053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3501053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3502053e637fSAndrew Gallatin 		*nbufs = 1;
3503053e637fSAndrew Gallatin 		return;
3504053e637fSAndrew Gallatin 	}
3505053e637fSAndrew Gallatin 
3506053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3507053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3508053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3509053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3510053e637fSAndrew Gallatin 		*nbufs = 1;
3511053e637fSAndrew Gallatin 		return;
3512053e637fSAndrew Gallatin 	}
3513b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3514053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3515053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3516053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3517053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3518053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3519053e637fSAndrew Gallatin 	if (*nbufs == 3)
3520053e637fSAndrew Gallatin 		*nbufs = 4;
3521b0f7b922SAndrew Gallatin #else
3522b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3523b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3524b0f7b922SAndrew Gallatin 	*nbufs = 1;
3525b0f7b922SAndrew Gallatin #endif
3526053e637fSAndrew Gallatin }
3527053e637fSAndrew Gallatin 
3528b2fc195eSAndrew Gallatin static int
35291e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3530b2fc195eSAndrew Gallatin {
35311e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
35326d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3533b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
35341e413cf9SAndrew Gallatin 	int err, i, slice;
3535b2fc195eSAndrew Gallatin 
35361e413cf9SAndrew Gallatin 
35371e413cf9SAndrew Gallatin 	sc = ss->sc;
35381e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
35391e413cf9SAndrew Gallatin 
354026dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
354126dd49c6SAndrew Gallatin 	(void)tcp_lro_init(&ss->lc);
354226dd49c6SAndrew Gallatin #endif
354326dd49c6SAndrew Gallatin 	ss->lc.ifp = sc->ifp;
3544053e637fSAndrew Gallatin 
35451e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
35461e413cf9SAndrew Gallatin 
35471e413cf9SAndrew Gallatin 	err = 0;
3548c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35491e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
35501e413cf9SAndrew Gallatin 	if (slice == 0) {
3551c6cb3e3fSAndrew Gallatin #endif
35521e413cf9SAndrew Gallatin 		cmd.data0 = slice;
35531e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
35541e413cf9SAndrew Gallatin 		ss->tx.lanai =
35551e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3556c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3557c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3558c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3559c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3560c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35611e413cf9SAndrew Gallatin 	}
3562c6cb3e3fSAndrew Gallatin #endif
35631e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35641e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
35651e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
35661e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
35671e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35681e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35691e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
35701e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
35711e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35721e413cf9SAndrew Gallatin 
35731e413cf9SAndrew Gallatin 	if (err != 0) {
35741e413cf9SAndrew Gallatin 		device_printf(sc->dev,
35751e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
35761e413cf9SAndrew Gallatin 		return EIO;
35771e413cf9SAndrew Gallatin 	}
35781e413cf9SAndrew Gallatin 
35791e413cf9SAndrew Gallatin 	/* stock receive rings */
35801e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
35811e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
35821e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
35831e413cf9SAndrew Gallatin 		if (err) {
35841e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
35851e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
35861e413cf9SAndrew Gallatin 			return ENOMEM;
35871e413cf9SAndrew Gallatin 		}
35881e413cf9SAndrew Gallatin 	}
35891e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
35901e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
35911e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
35921e413cf9SAndrew Gallatin 	}
35931e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
35941e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
35954d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
35964d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
35971e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
35981e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
35991e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
36001e413cf9SAndrew Gallatin 		if (err) {
36011e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
36021e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
36031e413cf9SAndrew Gallatin 			return ENOMEM;
36041e413cf9SAndrew Gallatin 		}
36051e413cf9SAndrew Gallatin 	}
36061e413cf9SAndrew Gallatin 	return 0;
36071e413cf9SAndrew Gallatin }
36081e413cf9SAndrew Gallatin 
36091e413cf9SAndrew Gallatin static int
36101e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
36111e413cf9SAndrew Gallatin {
36121e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
36131e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
36141e413cf9SAndrew Gallatin 	bus_addr_t bus;
36151e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3616c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3617b2fc195eSAndrew Gallatin 
36187d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
36197d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
36207d542e2dSAndrew Gallatin 
3621adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3622b2fc195eSAndrew Gallatin 	if (err != 0) {
3623b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3624b2fc195eSAndrew Gallatin 		return EIO;
3625b2fc195eSAndrew Gallatin 	}
3626b2fc195eSAndrew Gallatin 
36271e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
36281e413cf9SAndrew Gallatin 		/* setup the indirection table */
36291e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
36301e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
36311e413cf9SAndrew Gallatin 				    &cmd);
3632b2fc195eSAndrew Gallatin 
36331e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
36341e413cf9SAndrew Gallatin 				     &cmd);
36351e413cf9SAndrew Gallatin 		if (err != 0) {
36361e413cf9SAndrew Gallatin 			device_printf(sc->dev,
36371e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
36381e413cf9SAndrew Gallatin 			return err;
36391e413cf9SAndrew Gallatin 		}
36401e413cf9SAndrew Gallatin 
36411e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
36421e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
36431e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
36441e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
36451e413cf9SAndrew Gallatin 
36461e413cf9SAndrew Gallatin 		cmd.data0 = 1;
36471e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
36481e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
36491e413cf9SAndrew Gallatin 		if (err != 0) {
36501e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
36511e413cf9SAndrew Gallatin 			return err;
36521e413cf9SAndrew Gallatin 		}
36531e413cf9SAndrew Gallatin 	}
36541e413cf9SAndrew Gallatin 
36551e413cf9SAndrew Gallatin 
36561e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
36571e413cf9SAndrew Gallatin 
36581e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3659053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3660053e637fSAndrew Gallatin 			    &cmd);
3661053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3662053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
36631e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3664053e637fSAndrew Gallatin 		device_printf(sc->dev,
3665053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
36661e413cf9SAndrew Gallatin 			      nbufs);
3667053e637fSAndrew Gallatin 		return EIO;
3668053e637fSAndrew Gallatin 	}
3669b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3670b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3671b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3672c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
36735e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3674b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
36755e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3676b2fc195eSAndrew Gallatin 			     &cmd);
3677053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
36785e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
36790fa7f681SAndrew Gallatin 
36800fa7f681SAndrew Gallatin 	if (err != 0) {
36810fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
36820fa7f681SAndrew Gallatin 		goto abort;
36830fa7f681SAndrew Gallatin 	}
36840fa7f681SAndrew Gallatin 
3685b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3686c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3687c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3688c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3689c6cb3e3fSAndrew Gallatin #else
3690c6cb3e3fSAndrew Gallatin 	     slice < 1;
3691c6cb3e3fSAndrew Gallatin #endif
3692c6cb3e3fSAndrew Gallatin 	     slice++) {
3693c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3694c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3695c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3696c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3697c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
36980fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3699c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3700c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3701c6cb3e3fSAndrew Gallatin 	}
37020fa7f681SAndrew Gallatin 
37030fa7f681SAndrew Gallatin 	if (err != 0) {
37041e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
37050fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
37060fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
37070fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
37080fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
37090fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
37100fa7f681SAndrew Gallatin 				    &cmd);
37110fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
37120fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
37130fa7f681SAndrew Gallatin 	} else {
37140fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
37150fa7f681SAndrew Gallatin 	}
3716b2fc195eSAndrew Gallatin 
3717b2fc195eSAndrew Gallatin 	if (err != 0) {
3718b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3719b2fc195eSAndrew Gallatin 		goto abort;
3720b2fc195eSAndrew Gallatin 	}
3721b2fc195eSAndrew Gallatin 
37221e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
37231e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
37241e413cf9SAndrew Gallatin 		if (err != 0) {
37251e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
37261e413cf9SAndrew Gallatin 				      slice);
37271e413cf9SAndrew Gallatin 			goto abort;
37281e413cf9SAndrew Gallatin 		}
37291e413cf9SAndrew Gallatin 	}
37301e413cf9SAndrew Gallatin 
3731b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
37325e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3733b2fc195eSAndrew Gallatin 	if (err) {
3734b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3735b2fc195eSAndrew Gallatin 		goto abort;
3736b2fc195eSAndrew Gallatin 	}
3737c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3738c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3739c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3740c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3741c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3742c6cb3e3fSAndrew Gallatin 	}
3743c6cb3e3fSAndrew Gallatin #endif
3744b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3745b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3746b2fc195eSAndrew Gallatin 
3747b2fc195eSAndrew Gallatin 	return 0;
3748b2fc195eSAndrew Gallatin 
3749b2fc195eSAndrew Gallatin 
3750b2fc195eSAndrew Gallatin abort:
37516d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3752a98d6cd7SAndrew Gallatin 
3753b2fc195eSAndrew Gallatin 	return err;
3754b2fc195eSAndrew Gallatin }
3755b2fc195eSAndrew Gallatin 
3756b2fc195eSAndrew Gallatin static int
3757a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3758b2fc195eSAndrew Gallatin {
37596d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3760b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3761c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3762c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3763c6cb3e3fSAndrew Gallatin 	int slice;
3764c6cb3e3fSAndrew Gallatin #endif
3765b2fc195eSAndrew Gallatin 
3766c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3767c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3768c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3769c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3770c6cb3e3fSAndrew Gallatin 	}
3771c6cb3e3fSAndrew Gallatin #endif
3772b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3773a393336bSAndrew Gallatin 	if (!down) {
3774b2fc195eSAndrew Gallatin 		old_down_cnt = sc->down_cnt;
377573c7c83fSAndrew Gallatin 		wmb();
37765e7d8541SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3777b2fc195eSAndrew Gallatin 		if (err) {
3778a393336bSAndrew Gallatin 			device_printf(sc->dev,
3779a393336bSAndrew Gallatin 				      "Couldn't bring down link\n");
3780b2fc195eSAndrew Gallatin 		}
3781b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3782b2fc195eSAndrew Gallatin 			/* wait for down irq */
3783dce01b9bSAndrew Gallatin 			DELAY(10 * sc->intr_coal_delay);
3784b2fc195eSAndrew Gallatin 		}
378573c7c83fSAndrew Gallatin 		wmb();
3786b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3787b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "never got down irq\n");
3788b2fc195eSAndrew Gallatin 		}
3789a393336bSAndrew Gallatin 	}
37906d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3791a98d6cd7SAndrew Gallatin 
3792b2fc195eSAndrew Gallatin 	return 0;
3793b2fc195eSAndrew Gallatin }
3794b2fc195eSAndrew Gallatin 
3795dce01b9bSAndrew Gallatin static void
3796dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3797dce01b9bSAndrew Gallatin {
3798dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3799dce01b9bSAndrew Gallatin 	int reg;
3800c68534f1SScott Long 	uint16_t lnk, pectl;
3801dce01b9bSAndrew Gallatin 
3802dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
38033b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
3804dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3805dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3806dce01b9bSAndrew Gallatin 
380783d54b59SAndrew Gallatin 		if (sc->pectl == 0) {
3808dce01b9bSAndrew Gallatin 			pectl = pci_read_config(dev, reg + 0x8, 2);
3809dce01b9bSAndrew Gallatin 			pectl = (pectl & ~0x7000) | (5 << 12);
3810dce01b9bSAndrew Gallatin 			pci_write_config(dev, reg + 0x8, pectl, 2);
381183d54b59SAndrew Gallatin 			sc->pectl = pectl;
381283d54b59SAndrew Gallatin 		} else {
381383d54b59SAndrew Gallatin 			/* restore saved pectl after watchdog reset */
381483d54b59SAndrew Gallatin 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
381583d54b59SAndrew Gallatin 		}
3816dce01b9bSAndrew Gallatin 	}
3817dce01b9bSAndrew Gallatin 
3818dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3819dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3820dce01b9bSAndrew Gallatin }
3821dce01b9bSAndrew Gallatin 
3822dce01b9bSAndrew Gallatin static uint32_t
3823dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3824dce01b9bSAndrew Gallatin {
3825dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3826dce01b9bSAndrew Gallatin 	uint32_t vs;
3827dce01b9bSAndrew Gallatin 
3828dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
38293b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) {
3830dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3831dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3832dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3833dce01b9bSAndrew Gallatin 	}
3834dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3835dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3836dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3837dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3838dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3839dce01b9bSAndrew Gallatin }
3840dce01b9bSAndrew Gallatin 
384172c042dfSAndrew Gallatin static void
384272c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3843dce01b9bSAndrew Gallatin {
3844e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3845a393336bSAndrew Gallatin 	struct mxge_slice_state *ss;
3846a393336bSAndrew Gallatin 	int err, running, s, num_tx_slices = 1;
3847dce01b9bSAndrew Gallatin 	uint32_t reboot;
3848dce01b9bSAndrew Gallatin 	uint16_t cmd;
3849dce01b9bSAndrew Gallatin 
3850dce01b9bSAndrew Gallatin 	err = ENXIO;
3851dce01b9bSAndrew Gallatin 
3852dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3853dce01b9bSAndrew Gallatin 
3854dce01b9bSAndrew Gallatin 	/*
3855dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3856dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3857dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3858dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3859dce01b9bSAndrew Gallatin 	 * again
3860dce01b9bSAndrew Gallatin 	 */
3861dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3862dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3863dce01b9bSAndrew Gallatin 		/*
3864dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3865dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3866dce01b9bSAndrew Gallatin 		 * back, then give up
3867dce01b9bSAndrew Gallatin 		 */
3868dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3869dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3870dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3871dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3872dce01b9bSAndrew Gallatin 		}
3873dce01b9bSAndrew Gallatin 	}
3874dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3875dce01b9bSAndrew Gallatin 		/* print the reboot status */
3876dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3877dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3878dce01b9bSAndrew Gallatin 			      reboot);
3879a393336bSAndrew Gallatin 		running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
3880a393336bSAndrew Gallatin 		if (running) {
3881a393336bSAndrew Gallatin 
3882a393336bSAndrew Gallatin 			/*
3883a393336bSAndrew Gallatin 			 * quiesce NIC so that TX routines will not try to
3884a393336bSAndrew Gallatin 			 * xmit after restoration of BAR
3885a393336bSAndrew Gallatin 			 */
3886a393336bSAndrew Gallatin 
3887a393336bSAndrew Gallatin 			/* Mark the link as down */
3888a393336bSAndrew Gallatin 			if (sc->link_state) {
3889a393336bSAndrew Gallatin 				sc->link_state = 0;
3890a393336bSAndrew Gallatin 				if_link_state_change(sc->ifp,
3891a393336bSAndrew Gallatin 						     LINK_STATE_DOWN);
3892a393336bSAndrew Gallatin 			}
3893a393336bSAndrew Gallatin #ifdef IFNET_BUF_RING
3894a393336bSAndrew Gallatin 			num_tx_slices = sc->num_slices;
3895a393336bSAndrew Gallatin #endif
3896a393336bSAndrew Gallatin 			/* grab all TX locks to ensure no tx  */
3897a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3898a393336bSAndrew Gallatin 				ss = &sc->ss[s];
3899a393336bSAndrew Gallatin 				mtx_lock(&ss->tx.mtx);
3900a393336bSAndrew Gallatin 			}
3901a393336bSAndrew Gallatin 			mxge_close(sc, 1);
3902a393336bSAndrew Gallatin 		}
3903dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3904e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3905e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3906dce01b9bSAndrew Gallatin 
3907dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3908dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
390910882804SAndrew Gallatin 
3910a393336bSAndrew Gallatin 		/* reload f/w */
3911a393336bSAndrew Gallatin 		err = mxge_load_firmware(sc, 0);
3912a393336bSAndrew Gallatin 		if (err) {
3913a393336bSAndrew Gallatin 			device_printf(sc->dev,
3914a393336bSAndrew Gallatin 				      "Unable to re-load f/w\n");
391510882804SAndrew Gallatin 		}
3916a393336bSAndrew Gallatin 		if (running) {
3917a393336bSAndrew Gallatin 			if (!err)
3918a393336bSAndrew Gallatin 				err = mxge_open(sc);
3919a393336bSAndrew Gallatin 			/* release all TX locks */
3920a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3921a393336bSAndrew Gallatin 				ss = &sc->ss[s];
392283d54b59SAndrew Gallatin #ifdef IFNET_BUF_RING
392383d54b59SAndrew Gallatin 				mxge_start_locked(ss);
392483d54b59SAndrew Gallatin #endif
3925a393336bSAndrew Gallatin 				mtx_unlock(&ss->tx.mtx);
3926a393336bSAndrew Gallatin 			}
3927a393336bSAndrew Gallatin 		}
3928a393336bSAndrew Gallatin 		sc->watchdog_resets++;
3929dce01b9bSAndrew Gallatin 	} else {
3930c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
393172c042dfSAndrew Gallatin 			      "NIC did not reboot, not resetting\n");
393272c042dfSAndrew Gallatin 		err = 0;
393372c042dfSAndrew Gallatin 	}
393472c042dfSAndrew Gallatin 	if (err) {
393572c042dfSAndrew Gallatin 		device_printf(sc->dev, "watchdog reset failed\n");
393672c042dfSAndrew Gallatin 	} else {
39376b484a49SAndrew Gallatin 		if (sc->dying == 2)
39386b484a49SAndrew Gallatin 			sc->dying = 0;
39396b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
394072c042dfSAndrew Gallatin 	}
394172c042dfSAndrew Gallatin }
394272c042dfSAndrew Gallatin 
394372c042dfSAndrew Gallatin static void
394472c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
394572c042dfSAndrew Gallatin {
394672c042dfSAndrew Gallatin 	mxge_softc_t *sc = arg;
394772c042dfSAndrew Gallatin 
394872c042dfSAndrew Gallatin 
394972c042dfSAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
395072c042dfSAndrew Gallatin 	mxge_watchdog_reset(sc);
395172c042dfSAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
395272c042dfSAndrew Gallatin }
395372c042dfSAndrew Gallatin 
395472c042dfSAndrew Gallatin static void
395572c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
395672c042dfSAndrew Gallatin {
395772c042dfSAndrew Gallatin 	tx = &sc->ss[slice].tx;
395872c042dfSAndrew Gallatin 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3959c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev,
3960c6cb3e3fSAndrew Gallatin 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3961c6cb3e3fSAndrew Gallatin 		      tx->req, tx->done, tx->queue_active);
3962c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3963c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3964dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3965c6cb3e3fSAndrew Gallatin 		      tx->pkt_done,
39661e413cf9SAndrew Gallatin 		      be32toh(sc->ss->fw_stats->send_done_count));
3967dce01b9bSAndrew Gallatin }
3968dce01b9bSAndrew Gallatin 
3969e749ef6bSAndrew Gallatin static int
3970dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3971dce01b9bSAndrew Gallatin {
3972c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
39731e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3974c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3975dce01b9bSAndrew Gallatin 
3976dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3977dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3978c6cb3e3fSAndrew Gallatin 	for (i = 0;
3979c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3980c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
3981c6cb3e3fSAndrew Gallatin #else
3982c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
3983c6cb3e3fSAndrew Gallatin #endif
3984c6cb3e3fSAndrew Gallatin 	     i++) {
3985c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3986dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3987dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3988c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3989c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
399072c042dfSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause) {
399172c042dfSAndrew Gallatin 				mxge_warn_stuck(sc, tx, i);
399272c042dfSAndrew Gallatin 				taskqueue_enqueue(sc->tq, &sc->watchdog_task);
399372c042dfSAndrew Gallatin 				return (ENXIO);
399472c042dfSAndrew Gallatin 			}
3995c587e59fSAndrew Gallatin 			else
3996c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3997c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3998c587e59fSAndrew Gallatin 		}
3999dce01b9bSAndrew Gallatin 
4000dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
4001dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
4002c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
4003c6cb3e3fSAndrew Gallatin 	}
4004c587e59fSAndrew Gallatin 
4005c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
4006c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
4007e749ef6bSAndrew Gallatin 	return (err);
4008dce01b9bSAndrew Gallatin }
4009dce01b9bSAndrew Gallatin 
4010f3f040d9SGleb Smirnoff static uint64_t
4011f3f040d9SGleb Smirnoff mxge_get_counter(struct ifnet *ifp, ift_counter cnt)
40121e413cf9SAndrew Gallatin {
4013f3f040d9SGleb Smirnoff 	struct mxge_softc *sc;
4014f3f040d9SGleb Smirnoff 	uint64_t rv;
40151e413cf9SAndrew Gallatin 
4016f3f040d9SGleb Smirnoff 	sc = if_getsoftc(ifp);
4017f3f040d9SGleb Smirnoff 	rv = 0;
4018f3f040d9SGleb Smirnoff 
4019f3f040d9SGleb Smirnoff 	switch (cnt) {
4020f3f040d9SGleb Smirnoff 	case IFCOUNTER_IPACKETS:
4021f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4022f3f040d9SGleb Smirnoff 			rv += sc->ss[s].ipackets;
4023f3f040d9SGleb Smirnoff 		return (rv);
4024f3f040d9SGleb Smirnoff 	case IFCOUNTER_OPACKETS:
4025f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4026f3f040d9SGleb Smirnoff 			rv += sc->ss[s].opackets;
4027f3f040d9SGleb Smirnoff 		return (rv);
4028f3f040d9SGleb Smirnoff 	case IFCOUNTER_OERRORS:
4029f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4030f3f040d9SGleb Smirnoff 			rv += sc->ss[s].oerrors;
4031f3f040d9SGleb Smirnoff 		return (rv);
403271032832SAndrew Gallatin #ifdef IFNET_BUF_RING
4033f3f040d9SGleb Smirnoff 	case IFCOUNTER_OBYTES:
4034f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4035f3f040d9SGleb Smirnoff 			rv += sc->ss[s].obytes;
4036f3f040d9SGleb Smirnoff 		return (rv);
4037f3f040d9SGleb Smirnoff 	case IFCOUNTER_OMCASTS:
4038f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4039f3f040d9SGleb Smirnoff 			rv += sc->ss[s].omcasts;
4040f3f040d9SGleb Smirnoff 		return (rv);
4041f3f040d9SGleb Smirnoff 	case IFCOUNTER_OQDROPS:
4042f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
4043f3f040d9SGleb Smirnoff 			rv += sc->ss[s].tx.br->br_drops;
4044f3f040d9SGleb Smirnoff 		return (rv);
404571032832SAndrew Gallatin #endif
4046f3f040d9SGleb Smirnoff 	default:
4047f3f040d9SGleb Smirnoff 		return (if_get_counter_default(ifp, cnt));
40481e413cf9SAndrew Gallatin 	}
40491e413cf9SAndrew Gallatin }
4050c6cb3e3fSAndrew Gallatin 
40511e413cf9SAndrew Gallatin static void
4052dce01b9bSAndrew Gallatin mxge_tick(void *arg)
4053dce01b9bSAndrew Gallatin {
4054dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
40556b484a49SAndrew Gallatin 	u_long pkts = 0;
4056e749ef6bSAndrew Gallatin 	int err = 0;
40576b484a49SAndrew Gallatin 	int running, ticks;
40586b484a49SAndrew Gallatin 	uint16_t cmd;
4059dce01b9bSAndrew Gallatin 
40606b484a49SAndrew Gallatin 	ticks = mxge_ticks;
40616b484a49SAndrew Gallatin 	running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
40626b484a49SAndrew Gallatin 	if (running) {
40631e413cf9SAndrew Gallatin 		if (!sc->watchdog_countdown) {
4064e749ef6bSAndrew Gallatin 			err = mxge_watchdog(sc);
40651e413cf9SAndrew Gallatin 			sc->watchdog_countdown = 4;
40661e413cf9SAndrew Gallatin 		}
40671e413cf9SAndrew Gallatin 		sc->watchdog_countdown--;
40686b484a49SAndrew Gallatin 	}
40696b484a49SAndrew Gallatin 	if (pkts == 0) {
40706b484a49SAndrew Gallatin 		/* ensure NIC did not suffer h/w fault while idle */
40716b484a49SAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
40726b484a49SAndrew Gallatin 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
40736b484a49SAndrew Gallatin 			sc->dying = 2;
40746b484a49SAndrew Gallatin 			taskqueue_enqueue(sc->tq, &sc->watchdog_task);
40756b484a49SAndrew Gallatin 			err = ENXIO;
40766b484a49SAndrew Gallatin 		}
40776b484a49SAndrew Gallatin 		/* look less often if NIC is idle */
40786b484a49SAndrew Gallatin 		ticks *= 4;
40796b484a49SAndrew Gallatin 	}
40806b484a49SAndrew Gallatin 
4081e749ef6bSAndrew Gallatin 	if (err == 0)
40826b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
4083e749ef6bSAndrew Gallatin 
4084dce01b9bSAndrew Gallatin }
4085b2fc195eSAndrew Gallatin 
4086b2fc195eSAndrew Gallatin static int
40876d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
4088b2fc195eSAndrew Gallatin {
4089b2fc195eSAndrew Gallatin 	return EINVAL;
4090b2fc195eSAndrew Gallatin }
4091b2fc195eSAndrew Gallatin 
4092b2fc195eSAndrew Gallatin static int
40936d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
4094b2fc195eSAndrew Gallatin {
4095b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
4096b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
4097b2fc195eSAndrew Gallatin 	int err = 0;
4098b2fc195eSAndrew Gallatin 
4099b2fc195eSAndrew Gallatin 
4100c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
4101053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
4102b2fc195eSAndrew Gallatin 		return EINVAL;
4103a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4104b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
4105b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
4106b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4107a393336bSAndrew Gallatin 		mxge_close(sc, 0);
41086d87a65dSAndrew Gallatin 		err = mxge_open(sc);
4109b2fc195eSAndrew Gallatin 		if (err != 0) {
4110b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
4111a393336bSAndrew Gallatin 			mxge_close(sc, 0);
41126d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
4113b2fc195eSAndrew Gallatin 		}
4114b2fc195eSAndrew Gallatin 	}
4115a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4116b2fc195eSAndrew Gallatin 	return err;
4117b2fc195eSAndrew Gallatin }
4118b2fc195eSAndrew Gallatin 
4119b2fc195eSAndrew Gallatin static void
41206d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
4121b2fc195eSAndrew Gallatin {
41226d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4123b2fc195eSAndrew Gallatin 
4124b2fc195eSAndrew Gallatin 
4125b2fc195eSAndrew Gallatin 	if (sc == NULL)
4126b2fc195eSAndrew Gallatin 		return;
4127b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
4128c406ad2eSAndrew Gallatin 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
4129c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
4130c406ad2eSAndrew Gallatin 	ifmr->ifm_active |= sc->current_media;
4131b2fc195eSAndrew Gallatin }
4132b2fc195eSAndrew Gallatin 
4133b2fc195eSAndrew Gallatin static int
4134df131e84SAndrew Gallatin mxge_fetch_i2c(mxge_softc_t *sc, struct ifi2creq *i2c)
4135df131e84SAndrew Gallatin {
4136df131e84SAndrew Gallatin 	mxge_cmd_t cmd;
4137df131e84SAndrew Gallatin 	uint32_t i2c_args;
4138df131e84SAndrew Gallatin 	int i, ms, err;
4139df131e84SAndrew Gallatin 
4140df131e84SAndrew Gallatin 
4141df131e84SAndrew Gallatin 	if (i2c->dev_addr != 0xA0 &&
4142df131e84SAndrew Gallatin 	    i2c->dev_addr != 0xA2)
4143df131e84SAndrew Gallatin 		return (EINVAL);
4144df131e84SAndrew Gallatin 	if (i2c->len > sizeof(i2c->data))
4145df131e84SAndrew Gallatin 		return (EINVAL);
4146df131e84SAndrew Gallatin 
4147df131e84SAndrew Gallatin 	for (i = 0; i < i2c->len; i++) {
4148df131e84SAndrew Gallatin 		i2c_args = i2c->dev_addr << 0x8;
4149df131e84SAndrew Gallatin 		i2c_args |= i2c->offset + i;
4150df131e84SAndrew Gallatin 		cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
4151df131e84SAndrew Gallatin 		cmd.data1 = i2c_args;
4152df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
4153df131e84SAndrew Gallatin 
4154df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
4155df131e84SAndrew Gallatin 			return (EIO);
4156df131e84SAndrew Gallatin 		/* now we wait for the data to be cached */
4157df131e84SAndrew Gallatin 		cmd.data0 = i2c_args & 0xff;
4158df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
4159df131e84SAndrew Gallatin 		for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
4160df131e84SAndrew Gallatin 			cmd.data0 = i2c_args & 0xff;
4161df131e84SAndrew Gallatin 			err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
4162df131e84SAndrew Gallatin 			if (err == EBUSY)
4163df131e84SAndrew Gallatin 				DELAY(1000);
4164df131e84SAndrew Gallatin 		}
4165df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
4166df131e84SAndrew Gallatin 			return (EIO);
4167df131e84SAndrew Gallatin 		i2c->data[i] = cmd.data0;
4168df131e84SAndrew Gallatin 	}
4169df131e84SAndrew Gallatin 	return (0);
4170df131e84SAndrew Gallatin }
4171df131e84SAndrew Gallatin 
4172df131e84SAndrew Gallatin static int
41736d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
4174b2fc195eSAndrew Gallatin {
41756d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4176b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
4177df131e84SAndrew Gallatin 	struct ifi2creq i2c;
4178b2fc195eSAndrew Gallatin 	int err, mask;
4179b2fc195eSAndrew Gallatin 
4180b2fc195eSAndrew Gallatin 	err = 0;
4181b2fc195eSAndrew Gallatin 	switch (command) {
4182b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
41836d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
4184b2fc195eSAndrew Gallatin 		break;
4185b2fc195eSAndrew Gallatin 
4186b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
4187a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
41888c5d766cSAndrew Gallatin 		if (sc->dying) {
41898c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
41908c5d766cSAndrew Gallatin 			return EINVAL;
41918c5d766cSAndrew Gallatin 		}
4192b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
4193dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
41946d87a65dSAndrew Gallatin 				err = mxge_open(sc);
4195dce01b9bSAndrew Gallatin 			} else {
41960fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
41970fa7f681SAndrew Gallatin 				   flag chages */
41980fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
41990fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
42000fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
42010fa7f681SAndrew Gallatin 			}
4202b2fc195eSAndrew Gallatin 		} else {
4203dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4204a393336bSAndrew Gallatin 				mxge_close(sc, 0);
4205dce01b9bSAndrew Gallatin 			}
4206b2fc195eSAndrew Gallatin 		}
4207a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4208b2fc195eSAndrew Gallatin 		break;
4209b2fc195eSAndrew Gallatin 
4210b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
4211b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
4212a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4213c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4214c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4215c0a1f0afSAndrew Gallatin 			return (EINVAL);
4216c0a1f0afSAndrew Gallatin 		}
42170fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
4218a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4219b2fc195eSAndrew Gallatin 		break;
4220b2fc195eSAndrew Gallatin 
4221b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
4222a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4223b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
4224b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
4225b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
4226*cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO4;
4227aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
42280a7a780eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
4229b2fc195eSAndrew Gallatin 			} else {
4230b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
4231b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
4232b2fc195eSAndrew Gallatin 			}
4233*cbb9ccf7SRyan Moeller 		}
4234*cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM) {
4235b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
4236b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
4237b2fc195eSAndrew Gallatin 			} else {
4238b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
4239b2fc195eSAndrew Gallatin 			}
4240b2fc195eSAndrew Gallatin 		}
4241aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
4242aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
4243aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
4244aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
4245aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
4246aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
4247aed8e389SAndrew Gallatin 			} else {
4248aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4249aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4250aed8e389SAndrew Gallatin 				err = EINVAL;
4251aed8e389SAndrew Gallatin 			}
4252aed8e389SAndrew Gallatin 		}
42530a7a780eSAndrew Gallatin #if IFCAP_TSO6
42540a7a780eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM_IPV6) {
42550a7a780eSAndrew Gallatin 			if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) {
4256*cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO6;
42570a7a780eSAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6
42580a7a780eSAndrew Gallatin 						       | IFCAP_TSO6);
42590a7a780eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP_IPV6
42600a7a780eSAndrew Gallatin 						      | CSUM_UDP);
42610a7a780eSAndrew Gallatin 			} else {
42620a7a780eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM_IPV6;
42630a7a780eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP_IPV6
42640a7a780eSAndrew Gallatin 						     | CSUM_UDP_IPV6);
42650a7a780eSAndrew Gallatin 			}
4266*cbb9ccf7SRyan Moeller 		}
4267*cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM_IPV6) {
426826dd49c6SAndrew Gallatin 			if (IFCAP_RXCSUM_IPV6 & ifp->if_capenable) {
426926dd49c6SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM_IPV6;
42700a7a780eSAndrew Gallatin 			} else {
427126dd49c6SAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM_IPV6;
42720a7a780eSAndrew Gallatin 			}
42730a7a780eSAndrew Gallatin 		}
42740a7a780eSAndrew Gallatin 		if (mask & IFCAP_TSO6) {
42750a7a780eSAndrew Gallatin 			if (IFCAP_TSO6 & ifp->if_capenable) {
42760a7a780eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO6;
42770a7a780eSAndrew Gallatin 			} else if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) {
42780a7a780eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO6;
42790a7a780eSAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
42800a7a780eSAndrew Gallatin 			} else {
42810a7a780eSAndrew Gallatin 				printf("mxge requires tx checksum offload"
42820a7a780eSAndrew Gallatin 				       " be enabled to use TSO\n");
42830a7a780eSAndrew Gallatin 				err = EINVAL;
42840a7a780eSAndrew Gallatin 			}
42850a7a780eSAndrew Gallatin 		}
42860a7a780eSAndrew Gallatin #endif /*IFCAP_TSO6 */
42870a7a780eSAndrew Gallatin 
428826dd49c6SAndrew Gallatin 		if (mask & IFCAP_LRO)
428926dd49c6SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_LRO;
4290c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
4291c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
42920dce6781SAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTSO)
42930dce6781SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
42940dce6781SAndrew Gallatin 
42950dce6781SAndrew Gallatin 		if (!(ifp->if_capabilities & IFCAP_VLAN_HWTSO) ||
42960dce6781SAndrew Gallatin 		    !(ifp->if_capenable & IFCAP_VLAN_HWTAGGING))
42970dce6781SAndrew Gallatin 			ifp->if_capenable &= ~IFCAP_VLAN_HWTSO;
42980dce6781SAndrew Gallatin 
4299a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4300c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4301c792928fSAndrew Gallatin 
4302b2fc195eSAndrew Gallatin 		break;
4303b2fc195eSAndrew Gallatin 
4304b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4305c406ad2eSAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4306c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4307c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4308c0a1f0afSAndrew Gallatin 			return (EINVAL);
4309c0a1f0afSAndrew Gallatin 		}
4310c406ad2eSAndrew Gallatin 		mxge_media_probe(sc);
4311c406ad2eSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4312b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4313b2fc195eSAndrew Gallatin 				    &sc->media, command);
4314b2fc195eSAndrew Gallatin 		break;
4315b2fc195eSAndrew Gallatin 
4316df131e84SAndrew Gallatin 	case SIOCGI2C:
4317df131e84SAndrew Gallatin 		if (sc->connector != MXGE_XFP &&
4318df131e84SAndrew Gallatin 		    sc->connector != MXGE_SFP) {
4319df131e84SAndrew Gallatin 			err = ENXIO;
4320df131e84SAndrew Gallatin 			break;
4321df131e84SAndrew Gallatin 		}
4322df131e84SAndrew Gallatin 		err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c));
4323df131e84SAndrew Gallatin 		if (err != 0)
4324df131e84SAndrew Gallatin 			break;
4325df131e84SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4326df131e84SAndrew Gallatin 		if (sc->dying) {
4327df131e84SAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4328df131e84SAndrew Gallatin 			return (EINVAL);
4329df131e84SAndrew Gallatin 		}
4330df131e84SAndrew Gallatin 		err = mxge_fetch_i2c(sc, &i2c);
4331df131e84SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4332df131e84SAndrew Gallatin 		if (err == 0)
43331b8b041cSBrooks Davis 			err = copyout(&i2c, ifr_data_get_ptr(ifr),
4334df131e84SAndrew Gallatin 			    sizeof(i2c));
4335df131e84SAndrew Gallatin 		break;
4336b2fc195eSAndrew Gallatin 	default:
4337c756fb6eSRavi Pokala 		err = ether_ioctl(ifp, command, data);
4338c756fb6eSRavi Pokala 		break;
4339b2fc195eSAndrew Gallatin 	}
4340b2fc195eSAndrew Gallatin 	return err;
4341b2fc195eSAndrew Gallatin }
4342b2fc195eSAndrew Gallatin 
4343b2fc195eSAndrew Gallatin static void
43446d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4345b2fc195eSAndrew Gallatin {
4346b2fc195eSAndrew Gallatin 
43471e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
43486d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
43496d87a65dSAndrew Gallatin 			  &mxge_flow_control);
43506d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
43516d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
43526d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
43536d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4354d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4355d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
43565e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
43575e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
43585e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
43595e7d8541SAndrew Gallatin 			  &mxge_verbose);
4360dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
43611e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
43621e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
436394c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4364f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
436565c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4366b2fc195eSAndrew Gallatin 
43675e7d8541SAndrew Gallatin 	if (bootverbose)
43685e7d8541SAndrew Gallatin 		mxge_verbose = 1;
43696d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
43706d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4371dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
43721e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
43736d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
43741e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4375bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
43765769c5efSAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4377b2fc195eSAndrew Gallatin 	}
4378f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4379f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4380f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
438165c69066SAndrew Gallatin 
438265c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
438365c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
438465c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
438565c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
438665c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
43871e413cf9SAndrew Gallatin }
43881e413cf9SAndrew Gallatin 
43891e413cf9SAndrew Gallatin 
43901e413cf9SAndrew Gallatin static void
43911e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
43921e413cf9SAndrew Gallatin {
43931e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
43941e413cf9SAndrew Gallatin 	int i;
43951e413cf9SAndrew Gallatin 
43961e413cf9SAndrew Gallatin 
43971e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
43981e413cf9SAndrew Gallatin 		return;
43991e413cf9SAndrew Gallatin 
44001e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44011e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
44021e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
44031e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
44041e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4405c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4406c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4407c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4408c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4409c6cb3e3fSAndrew Gallatin 			}
4410c6cb3e3fSAndrew Gallatin #endif
44111e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
44121e413cf9SAndrew Gallatin 		}
44131e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
44141e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
44151e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
44161e413cf9SAndrew Gallatin 		}
44171e413cf9SAndrew Gallatin 	}
44181e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
44191e413cf9SAndrew Gallatin 	sc->ss = NULL;
44201e413cf9SAndrew Gallatin }
44211e413cf9SAndrew Gallatin 
44221e413cf9SAndrew Gallatin static int
44231e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
44241e413cf9SAndrew Gallatin {
44251e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
44261e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
44271e413cf9SAndrew Gallatin 	size_t bytes;
44281e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
44291e413cf9SAndrew Gallatin 
44301e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
44311e413cf9SAndrew Gallatin 	if (err != 0) {
44321e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
44331e413cf9SAndrew Gallatin 		return err;
44341e413cf9SAndrew Gallatin 	}
44351e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
44361e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
44371e413cf9SAndrew Gallatin 
4438ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->ss) * sc->num_slices;
4439ac2fffa4SPedro F. Giffuni 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
44401e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
44411e413cf9SAndrew Gallatin 		return (ENOMEM);
44421e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44431e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
44441e413cf9SAndrew Gallatin 
44451e413cf9SAndrew Gallatin 		ss->sc = sc;
44461e413cf9SAndrew Gallatin 
44471e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
44481e413cf9SAndrew Gallatin 
44491e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
44501e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
44511e413cf9SAndrew Gallatin 		if (err != 0)
44521e413cf9SAndrew Gallatin 			goto abort;
44531e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
44541e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
44551e413cf9SAndrew Gallatin 
44561e413cf9SAndrew Gallatin 		/*
44571e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
44581e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
44591e413cf9SAndrew Gallatin 		 * slice for now
44601e413cf9SAndrew Gallatin 		 */
4461c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
44621e413cf9SAndrew Gallatin 		if (i > 0)
44631e413cf9SAndrew Gallatin 			continue;
4464c6cb3e3fSAndrew Gallatin #endif
44651e413cf9SAndrew Gallatin 
44661e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
44671e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
44681e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
44691e413cf9SAndrew Gallatin 		if (err != 0)
44701e413cf9SAndrew Gallatin 			goto abort;
44711e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
44721e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
44731e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
44741e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4475c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4476c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4477c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4478c6cb3e3fSAndrew Gallatin #endif
44791e413cf9SAndrew Gallatin 	}
44801e413cf9SAndrew Gallatin 
44811e413cf9SAndrew Gallatin 	return (0);
44821e413cf9SAndrew Gallatin 
44831e413cf9SAndrew Gallatin abort:
44841e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
44851e413cf9SAndrew Gallatin 	return (ENOMEM);
44861e413cf9SAndrew Gallatin }
44871e413cf9SAndrew Gallatin 
44881e413cf9SAndrew Gallatin static void
44891e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
44901e413cf9SAndrew Gallatin {
44911e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
44921e413cf9SAndrew Gallatin 	char *old_fw;
44931e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
44941e413cf9SAndrew Gallatin 
44951e413cf9SAndrew Gallatin 	sc->num_slices = 1;
44961e413cf9SAndrew Gallatin 	/*
44971e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
44981e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
44991e413cf9SAndrew Gallatin 	 */
45001e413cf9SAndrew Gallatin 
45011e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
45021e413cf9SAndrew Gallatin 		return;
45031e413cf9SAndrew Gallatin 
45041e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
45051e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
45061e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
45071e413cf9SAndrew Gallatin 		return;
45081e413cf9SAndrew Gallatin 
45091e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
45101e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
45111e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
45121e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
45131e413cf9SAndrew Gallatin 	else
45141e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
45151e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
45161e413cf9SAndrew Gallatin 	if (status != 0) {
45171e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
45181e413cf9SAndrew Gallatin 		return;
45191e413cf9SAndrew Gallatin 	}
45201e413cf9SAndrew Gallatin 
45211e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
45221e413cf9SAndrew Gallatin 	   is alive */
45231e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
45241e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
45251e413cf9SAndrew Gallatin 	if (status != 0) {
45261e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
45271e413cf9SAndrew Gallatin 		goto abort_with_fw;
45281e413cf9SAndrew Gallatin 	}
45291e413cf9SAndrew Gallatin 
45301e413cf9SAndrew Gallatin 	/* get rx ring size */
45311e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
45321e413cf9SAndrew Gallatin 	if (status != 0) {
45331e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
45341e413cf9SAndrew Gallatin 		goto abort_with_fw;
45351e413cf9SAndrew Gallatin 	}
45361e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
45371e413cf9SAndrew Gallatin 
45381e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
45391e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
45401e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
45411e413cf9SAndrew Gallatin 	if (status != 0) {
45421e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
45431e413cf9SAndrew Gallatin 		goto abort_with_fw;
45441e413cf9SAndrew Gallatin 	}
45451e413cf9SAndrew Gallatin 
45461e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
45471e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
45481e413cf9SAndrew Gallatin 	if (status != 0) {
45491e413cf9SAndrew Gallatin 		device_printf(sc->dev,
45501e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
45511e413cf9SAndrew Gallatin 		goto abort_with_fw;
45521e413cf9SAndrew Gallatin 	}
45531e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
45541e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
45551e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
45561e413cf9SAndrew Gallatin 
45571e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
45581e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
45591e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
45601e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
45611e413cf9SAndrew Gallatin 	} else {
45621e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
45631e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
45641e413cf9SAndrew Gallatin 	}
45651e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
45661e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
45671e413cf9SAndrew Gallatin 		sc->num_slices--;
45681e413cf9SAndrew Gallatin 
45691e413cf9SAndrew Gallatin 	if (mxge_verbose)
45701e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
45711e413cf9SAndrew Gallatin 			      sc->num_slices);
45721e413cf9SAndrew Gallatin 
45731e413cf9SAndrew Gallatin 	return;
45741e413cf9SAndrew Gallatin 
45751e413cf9SAndrew Gallatin abort_with_fw:
45761e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
45771e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
45781e413cf9SAndrew Gallatin }
45791e413cf9SAndrew Gallatin 
45801e413cf9SAndrew Gallatin static int
45811e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
45821e413cf9SAndrew Gallatin {
4583ac2fffa4SPedro F. Giffuni 	size_t bytes;
45841e413cf9SAndrew Gallatin 	int count, err, i, rid;
45851e413cf9SAndrew Gallatin 
45861e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
45871e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
45881e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
45891e413cf9SAndrew Gallatin 
45901e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
45911e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
45921e413cf9SAndrew Gallatin 		return ENXIO;
45931e413cf9SAndrew Gallatin 	}
45941e413cf9SAndrew Gallatin 
45951e413cf9SAndrew Gallatin 	count = sc->num_slices;
45961e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
45971e413cf9SAndrew Gallatin 	if (err != 0) {
45981e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
45991e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
46001e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
46011e413cf9SAndrew Gallatin 	}
46021e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
46031e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
46041e413cf9SAndrew Gallatin 			      count, sc->num_slices);
46051e413cf9SAndrew Gallatin 		device_printf(sc->dev,
46061e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
46071e413cf9SAndrew Gallatin 			      count);
46081e413cf9SAndrew Gallatin 		err = ENOSPC;
46091e413cf9SAndrew Gallatin 		goto abort_with_msix;
46101e413cf9SAndrew Gallatin 	}
4611ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
4612ac2fffa4SPedro F. Giffuni 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
46131e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
46141e413cf9SAndrew Gallatin 		err = ENOMEM;
46151e413cf9SAndrew Gallatin 		goto abort_with_msix;
46161e413cf9SAndrew Gallatin 	}
46171e413cf9SAndrew Gallatin 
46181e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46191e413cf9SAndrew Gallatin 		rid = i + 1;
46201e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
46211e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
46221e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
46231e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
46241e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
46251e413cf9SAndrew Gallatin 				      " for message %d\n", i);
46261e413cf9SAndrew Gallatin 			err = ENXIO;
46271e413cf9SAndrew Gallatin 			goto abort_with_res;
46281e413cf9SAndrew Gallatin 		}
46291e413cf9SAndrew Gallatin 	}
46301e413cf9SAndrew Gallatin 
4631ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
4632ac2fffa4SPedro F. Giffuni 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
46331e413cf9SAndrew Gallatin 
46341e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46351e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
46361e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
463737d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
463837d89b0cSAndrew Gallatin 				     NULL,
463937d89b0cSAndrew Gallatin #endif
464037d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
46411e413cf9SAndrew Gallatin 		if (err != 0) {
46421e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
46431e413cf9SAndrew Gallatin 				      "message %d\n", i);
46441e413cf9SAndrew Gallatin 			goto abort_with_intr;
46451e413cf9SAndrew Gallatin 		}
464621089137SAndrew Gallatin 		bus_describe_intr(sc->dev, sc->msix_irq_res[i],
464721089137SAndrew Gallatin 				  sc->msix_ih[i], "s%d", i);
46481e413cf9SAndrew Gallatin 	}
46491e413cf9SAndrew Gallatin 
46501e413cf9SAndrew Gallatin 	if (mxge_verbose) {
46511e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
46521e413cf9SAndrew Gallatin 			      sc->num_slices);
46531e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
4654da1b038aSJustin Hibbits 			printf(" %jd", rman_get_start(sc->msix_irq_res[i]));
46551e413cf9SAndrew Gallatin 		printf("\n");
46561e413cf9SAndrew Gallatin 	}
46571e413cf9SAndrew Gallatin 	return (0);
46581e413cf9SAndrew Gallatin 
46591e413cf9SAndrew Gallatin abort_with_intr:
46601e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46611e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
46621e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
46631e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
46641e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
46651e413cf9SAndrew Gallatin 		}
46661e413cf9SAndrew Gallatin 	}
46671e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
46681e413cf9SAndrew Gallatin 
46691e413cf9SAndrew Gallatin 
46701e413cf9SAndrew Gallatin abort_with_res:
46711e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
46721e413cf9SAndrew Gallatin 		rid = i + 1;
46731e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
46741e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
46751e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
46761e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
46771e413cf9SAndrew Gallatin 	}
46781e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
46791e413cf9SAndrew Gallatin 
46801e413cf9SAndrew Gallatin 
46811e413cf9SAndrew Gallatin abort_with_msix:
46821e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
46831e413cf9SAndrew Gallatin 
46841e413cf9SAndrew Gallatin abort_with_msix_table:
46851e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
46861e413cf9SAndrew Gallatin 			     sc->msix_table_res);
46871e413cf9SAndrew Gallatin 
46881e413cf9SAndrew Gallatin 	return err;
46891e413cf9SAndrew Gallatin }
46901e413cf9SAndrew Gallatin 
46911e413cf9SAndrew Gallatin static int
46921e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
46931e413cf9SAndrew Gallatin {
46941e413cf9SAndrew Gallatin 	int count, err, rid;
46951e413cf9SAndrew Gallatin 
46961e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
46971e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
46981e413cf9SAndrew Gallatin 		rid = 1;
46991e413cf9SAndrew Gallatin 	} else {
47001e413cf9SAndrew Gallatin 		rid = 0;
470191ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
47021e413cf9SAndrew Gallatin 	}
470343cd6160SJustin Hibbits 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
470443cd6160SJustin Hibbits 					     RF_SHAREABLE | RF_ACTIVE);
47051e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
47061e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
47071e413cf9SAndrew Gallatin 		return ENXIO;
47081e413cf9SAndrew Gallatin 	}
47091e413cf9SAndrew Gallatin 	if (mxge_verbose)
4710da1b038aSJustin Hibbits 		device_printf(sc->dev, "using %s irq %jd\n",
471191ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
47121e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
47131e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
47141e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
471537d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
471637d89b0cSAndrew Gallatin 			     NULL,
471737d89b0cSAndrew Gallatin #endif
471837d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
47191e413cf9SAndrew Gallatin 	if (err != 0) {
47201e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
472191ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
472291ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
47231e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
47241e413cf9SAndrew Gallatin 	}
47251e413cf9SAndrew Gallatin 	return err;
47261e413cf9SAndrew Gallatin }
47271e413cf9SAndrew Gallatin 
47281e413cf9SAndrew Gallatin static void
47291e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
47301e413cf9SAndrew Gallatin {
47311e413cf9SAndrew Gallatin 	int i, rid;
47321e413cf9SAndrew Gallatin 
47331e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
47341e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
47351e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
47361e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
47371e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
47381e413cf9SAndrew Gallatin 		}
47391e413cf9SAndrew Gallatin 	}
47401e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
47411e413cf9SAndrew Gallatin 
47421e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
47431e413cf9SAndrew Gallatin 		rid = i + 1;
47441e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
47451e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
47461e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
47471e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
47481e413cf9SAndrew Gallatin 	}
47491e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
47501e413cf9SAndrew Gallatin 
47511e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
47521e413cf9SAndrew Gallatin 			     sc->msix_table_res);
47531e413cf9SAndrew Gallatin 
47541e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
47551e413cf9SAndrew Gallatin 	return;
47561e413cf9SAndrew Gallatin }
47571e413cf9SAndrew Gallatin 
47581e413cf9SAndrew Gallatin static void
47591e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
47601e413cf9SAndrew Gallatin {
47611e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
47621e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
476391ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
476491ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
47651e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
47661e413cf9SAndrew Gallatin }
47671e413cf9SAndrew Gallatin 
47681e413cf9SAndrew Gallatin static void
47691e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
47701e413cf9SAndrew Gallatin {
47711e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
47721e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
47731e413cf9SAndrew Gallatin 	else
47741e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
47751e413cf9SAndrew Gallatin }
47761e413cf9SAndrew Gallatin 
47771e413cf9SAndrew Gallatin static int
47781e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
47791e413cf9SAndrew Gallatin {
47801e413cf9SAndrew Gallatin 	int err;
47811e413cf9SAndrew Gallatin 
47821e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
47831e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
47841e413cf9SAndrew Gallatin 	else
47851e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
47861e413cf9SAndrew Gallatin 
47871e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
47881e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
47891e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
47901e413cf9SAndrew Gallatin 	}
47911e413cf9SAndrew Gallatin 	return err;
47921e413cf9SAndrew Gallatin }
47931e413cf9SAndrew Gallatin 
4794b2fc195eSAndrew Gallatin 
4795b2fc195eSAndrew Gallatin static int
47966d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4797b2fc195eSAndrew Gallatin {
47980a7a780eSAndrew Gallatin 	mxge_cmd_t cmd;
47996d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4800b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
48011e413cf9SAndrew Gallatin 	int err, rid;
4802b2fc195eSAndrew Gallatin 
4803b2fc195eSAndrew Gallatin 	sc->dev = dev;
48046d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4805b2fc195eSAndrew Gallatin 
480672c042dfSAndrew Gallatin 	TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
4807a4a75d67SJohn Baldwin 	sc->tq = taskqueue_create("mxge_taskq", M_WAITOK,
4808a4a75d67SJohn Baldwin 				  taskqueue_thread_enqueue, &sc->tq);
480972c042dfSAndrew Gallatin 	if (sc->tq == NULL) {
481072c042dfSAndrew Gallatin 		err = ENOMEM;
481172c042dfSAndrew Gallatin 		goto abort_with_nothing;
481272c042dfSAndrew Gallatin 	}
481372c042dfSAndrew Gallatin 
481462ce43ccSScott Long 	err = bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
4815b2fc195eSAndrew Gallatin 				 1,			/* alignment */
48161e413cf9SAndrew Gallatin 				 0,			/* boundary */
4817b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4818b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4819b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4820aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
48215e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
48221e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4823b2fc195eSAndrew Gallatin 				 0,			/* flags */
4824b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4825b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4826b2fc195eSAndrew Gallatin 
4827b2fc195eSAndrew Gallatin 	if (err != 0) {
4828b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4829b2fc195eSAndrew Gallatin 			      err);
483072c042dfSAndrew Gallatin 		goto abort_with_tq;
4831b2fc195eSAndrew Gallatin 	}
4832b2fc195eSAndrew Gallatin 
4833b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4834b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4835b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4836b2fc195eSAndrew Gallatin 		err = ENOSPC;
4837b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4838b2fc195eSAndrew Gallatin 	}
48391e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
48401e413cf9SAndrew Gallatin 
4841a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4842a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4843a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4844a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4845a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4846a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4847b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4848b2fc195eSAndrew Gallatin 
4849dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4850d91b1b49SAndrew Gallatin 
4851dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4852b2fc195eSAndrew Gallatin 
4853b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4854b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
485543cd6160SJustin Hibbits 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
485643cd6160SJustin Hibbits 					     RF_ACTIVE);
4857b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4858b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4859b2fc195eSAndrew Gallatin 		err = ENXIO;
4860b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4861b2fc195eSAndrew Gallatin 	}
4862b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4863b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4864b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4865da1b038aSJustin Hibbits 		device_printf(dev, "impossible memory region size %jd\n",
4866b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4867b2fc195eSAndrew Gallatin 		err = ENXIO;
4868b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4869b2fc195eSAndrew Gallatin 	}
4870b2fc195eSAndrew Gallatin 
4871b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4872b2fc195eSAndrew Gallatin 	   lanai SRAM */
48736d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4874b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4875b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
48766d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4877b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
48786d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
48796d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4880b2fc195eSAndrew Gallatin 	if (err != 0)
4881b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4882b2fc195eSAndrew Gallatin 
4883b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
48846d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4885b2fc195eSAndrew Gallatin 
4886b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
48876d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
48886d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4889b2fc195eSAndrew Gallatin 	if (err != 0)
4890b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4891b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
48926d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4893b2fc195eSAndrew Gallatin 	if (err != 0)
4894b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4895b2fc195eSAndrew Gallatin 
4896a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4897a98d6cd7SAndrew Gallatin 	if (err != 0)
48981e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4899b2fc195eSAndrew Gallatin 
49008fe615baSAndrew Gallatin 	/* select & load the firmware */
49018fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4902b2fc195eSAndrew Gallatin 	if (err != 0)
49031e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
49045e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
49051e413cf9SAndrew Gallatin 
49061e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
49071e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
49081e413cf9SAndrew Gallatin 	if (err != 0)
49091e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
49101e413cf9SAndrew Gallatin 
4911adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4912b2fc195eSAndrew Gallatin 	if (err != 0)
49131e413cf9SAndrew Gallatin 		goto abort_with_slices;
4914b2fc195eSAndrew Gallatin 
4915a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4916a98d6cd7SAndrew Gallatin 	if (err != 0) {
4917a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
49182e084798SAndrew Gallatin 		goto abort_with_slices;
4919a98d6cd7SAndrew Gallatin 	}
4920a98d6cd7SAndrew Gallatin 
49211e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4922a98d6cd7SAndrew Gallatin 	if (err != 0) {
49231e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4924a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4925a98d6cd7SAndrew Gallatin 	}
49261e413cf9SAndrew Gallatin 
4927b245f96cSGleb Smirnoff 	ifp->if_baudrate = IF_Gbps(10);
4928c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
492926dd49c6SAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LINKSTATE | IFCAP_TXCSUM_IPV6 |
493026dd49c6SAndrew Gallatin 		IFCAP_RXCSUM_IPV6;
493126dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
4932eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4933eb6219e3SAndrew Gallatin #endif
493437d89b0cSAndrew Gallatin 
493537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
493637d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
49370dce6781SAndrew Gallatin 
49380dce6781SAndrew Gallatin 	/* Only FW 1.4.32 and newer can do TSO over vlans */
49390dce6781SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
49400dce6781SAndrew Gallatin 	    sc->fw_ver_tiny >= 32)
49410dce6781SAndrew Gallatin 		ifp->if_capabilities |= IFCAP_VLAN_HWTSO;
494237d89b0cSAndrew Gallatin #endif
4943053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4944053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4945053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4946053e637fSAndrew Gallatin 	else
4947053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4948adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4949053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4950aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
49510a7a780eSAndrew Gallatin 	ifp->if_hwassist |= CSUM_TCP_IPV6 | CSUM_UDP_IPV6;
49520a7a780eSAndrew Gallatin 	/* check to see if f/w supports TSO for IPv6 */
49530a7a780eSAndrew Gallatin 	if (!mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd)) {
49540a7a780eSAndrew Gallatin 		if (CSUM_TCP_IPV6)
49550a7a780eSAndrew Gallatin 			ifp->if_capabilities |= IFCAP_TSO6;
49560a7a780eSAndrew Gallatin 		sc->max_tso6_hlen = min(cmd.data0,
49570a7a780eSAndrew Gallatin 					sizeof (sc->ss[0].scratch));
49580a7a780eSAndrew Gallatin 	}
4959b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4960f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4961f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
49626d87a65dSAndrew Gallatin 	ifp->if_init = mxge_init;
4963b2fc195eSAndrew Gallatin 	ifp->if_softc = sc;
4964b2fc195eSAndrew Gallatin 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
49656d87a65dSAndrew Gallatin 	ifp->if_ioctl = mxge_ioctl;
49666d87a65dSAndrew Gallatin 	ifp->if_start = mxge_start;
4967f3f040d9SGleb Smirnoff 	ifp->if_get_counter = mxge_get_counter;
49683d07f894SAndrew Gallatin 	ifp->if_hw_tsomax = IP_MAXPACKET - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
49697eafc4d5SAndrew Gallatin 	ifp->if_hw_tsomaxsegcount = sc->ss[0].tx.max_desc;
49703d07f894SAndrew Gallatin 	ifp->if_hw_tsomaxsegsize = IP_MAXPACKET;
4971c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4972c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4973c587e59fSAndrew Gallatin 		     mxge_media_status);
4974c406ad2eSAndrew Gallatin 	mxge_media_init(sc);
4975c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
49768c5d766cSAndrew Gallatin 	sc->dying = 0;
4977b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4978f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4979f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4980f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4981b2fc195eSAndrew Gallatin 
49826d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4983c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4984c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4985c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4986c6cb3e3fSAndrew Gallatin #endif
49872e084798SAndrew Gallatin 	taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
49882e084798SAndrew Gallatin 				device_get_nameunit(sc->dev));
49896b484a49SAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4990b2fc195eSAndrew Gallatin 	return 0;
4991b2fc195eSAndrew Gallatin 
4992a98d6cd7SAndrew Gallatin abort_with_rings:
4993a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
49941e413cf9SAndrew Gallatin abort_with_slices:
49951e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4996a98d6cd7SAndrew Gallatin abort_with_dmabench:
4997a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4998b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
49996d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
5000b2fc195eSAndrew Gallatin abort_with_cmd_dma:
50016d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
5002b2fc195eSAndrew Gallatin abort_with_mem_res:
5003b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
5004b2fc195eSAndrew Gallatin abort_with_lock:
5005b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
5006a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
5007a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
5008b2fc195eSAndrew Gallatin 	if_free(ifp);
5009b2fc195eSAndrew Gallatin abort_with_parent_dmat:
5010b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
501172c042dfSAndrew Gallatin abort_with_tq:
501272c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
501372c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
501472c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
501572c042dfSAndrew Gallatin 		sc->tq = NULL;
501672c042dfSAndrew Gallatin 	}
5017b2fc195eSAndrew Gallatin abort_with_nothing:
5018b2fc195eSAndrew Gallatin 	return err;
5019b2fc195eSAndrew Gallatin }
5020b2fc195eSAndrew Gallatin 
5021b2fc195eSAndrew Gallatin static int
50226d87a65dSAndrew Gallatin mxge_detach(device_t dev)
5023b2fc195eSAndrew Gallatin {
50246d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
5025b2fc195eSAndrew Gallatin 
502637d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
5027c792928fSAndrew Gallatin 		device_printf(sc->dev,
5028c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
5029c792928fSAndrew Gallatin 		return EBUSY;
5030c792928fSAndrew Gallatin 	}
5031a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
50328c5d766cSAndrew Gallatin 	sc->dying = 1;
5033b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
5034a393336bSAndrew Gallatin 		mxge_close(sc, 0);
5035a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
5036b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
503772c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
503872c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
503972c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
504072c042dfSAndrew Gallatin 		sc->tq = NULL;
504172c042dfSAndrew Gallatin 	}
5042e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
5043dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
5044091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
50451e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
50461e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
5047a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
50481e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
5049a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
50506d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
50516d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
5052b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
5053b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
5054a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
5055a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
5056b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
5057b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
5058b2fc195eSAndrew Gallatin 	return 0;
5059b2fc195eSAndrew Gallatin }
5060b2fc195eSAndrew Gallatin 
5061b2fc195eSAndrew Gallatin static int
50626d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
5063b2fc195eSAndrew Gallatin {
5064b2fc195eSAndrew Gallatin 	return 0;
5065b2fc195eSAndrew Gallatin }
5066b2fc195eSAndrew Gallatin 
5067b2fc195eSAndrew Gallatin /*
5068b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
5069b2fc195eSAndrew Gallatin 
5070b2fc195eSAndrew Gallatin   Local Variables:
5071b2fc195eSAndrew Gallatin   c-file-style:"linux"
5072b2fc195eSAndrew Gallatin   tab-width:8
5073b2fc195eSAndrew Gallatin   End:
5074b2fc195eSAndrew Gallatin */
5075