xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 93037a67bf4b734ad10dc867340ba3633c379a8d)
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 #include <sys/buf_ring.h>
96b2fc195eSAndrew Gallatin 
97eb6219e3SAndrew Gallatin #include "opt_inet.h"
980a7a780eSAndrew Gallatin #include "opt_inet6.h"
99eb6219e3SAndrew Gallatin 
100b2fc195eSAndrew Gallatin /* tunable params */
1016d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
102d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
1036d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1045e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1056d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1065e7d8541SAndrew Gallatin static int mxge_verbose = 0;
107dce01b9bSAndrew Gallatin static int mxge_ticks;
1081e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1095769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
1101e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
111f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
11265c69066SAndrew Gallatin static int mxge_throttle = 0;
1136d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1146d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1151e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1161e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
117b2fc195eSAndrew Gallatin 
1186d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1196d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1206d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1216d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1226d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
123b2fc195eSAndrew Gallatin 
1246d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
125b2fc195eSAndrew Gallatin {
126b2fc195eSAndrew Gallatin   /* Device interface */
1276d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1286d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1296d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1306d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
13161bfd867SSofian Brabez 
13261bfd867SSofian Brabez   DEVMETHOD_END
133b2fc195eSAndrew Gallatin };
134b2fc195eSAndrew Gallatin 
1356d87a65dSAndrew Gallatin static driver_t mxge_driver =
136b2fc195eSAndrew Gallatin {
1376d87a65dSAndrew Gallatin   "mxge",
1386d87a65dSAndrew Gallatin   mxge_methods,
1396d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
140b2fc195eSAndrew Gallatin };
141b2fc195eSAndrew Gallatin 
142b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
14399b29e34SJohn Baldwin DRIVER_MODULE(mxge, pci, mxge_driver, 0, 0);
1446d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
145f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
146b2fc195eSAndrew Gallatin 
1471e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1488fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
149a393336bSAndrew Gallatin static int mxge_close(mxge_softc_t *sc, int down);
150276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
151276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1528fe615baSAndrew Gallatin 
153b2fc195eSAndrew Gallatin static int
1546d87a65dSAndrew Gallatin mxge_probe(device_t dev)
155b2fc195eSAndrew Gallatin {
15601638550SAndrew Gallatin 	int rev;
15701638550SAndrew Gallatin 
1586d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
159f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
160f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
16101638550SAndrew Gallatin 		rev = pci_get_revid(dev);
16201638550SAndrew Gallatin 		switch (rev) {
16301638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
164b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
16501638550SAndrew Gallatin 			break;
16601638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
16701638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
16801638550SAndrew Gallatin 			break;
16901638550SAndrew Gallatin 		default:
17001638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
17101638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
17201638550SAndrew Gallatin 				      rev);
17301638550SAndrew Gallatin 			break;
17401638550SAndrew Gallatin 		}
175b2fc195eSAndrew Gallatin 		return 0;
176b2fc195eSAndrew Gallatin 	}
177b2fc195eSAndrew Gallatin 	return ENXIO;
178b2fc195eSAndrew Gallatin }
179b2fc195eSAndrew Gallatin 
180b2fc195eSAndrew Gallatin static void
1816d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
182b2fc195eSAndrew Gallatin {
183f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
184b2fc195eSAndrew Gallatin 	vm_offset_t len;
18547c2e987SAndrew Gallatin 	int err;
186b2fc195eSAndrew Gallatin 
1874d69a9d0SAndrew Gallatin 	sc->wc = 1;
188b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
189c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
190c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
19147c2e987SAndrew Gallatin 	if (err != 0) {
192c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
193c2c14a69SAndrew Gallatin 			      err);
1944d69a9d0SAndrew Gallatin 		sc->wc = 0;
195b2fc195eSAndrew Gallatin 	}
196f9ae0280SAndrew Gallatin #endif
197b2fc195eSAndrew Gallatin }
198b2fc195eSAndrew Gallatin 
199b2fc195eSAndrew Gallatin /* callback to get our DMA address */
200b2fc195eSAndrew Gallatin static void
2016d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
202b2fc195eSAndrew Gallatin 			 int error)
203b2fc195eSAndrew Gallatin {
204b2fc195eSAndrew Gallatin 	if (error == 0) {
205b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
206b2fc195eSAndrew Gallatin 	}
207b2fc195eSAndrew Gallatin }
208b2fc195eSAndrew Gallatin 
209b2fc195eSAndrew Gallatin static int
2106d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
211b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
212b2fc195eSAndrew Gallatin {
213b2fc195eSAndrew Gallatin 	int err;
214b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2151e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2161e413cf9SAndrew Gallatin 
2171e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2181e413cf9SAndrew Gallatin 		boundary = 0;
2191e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2201e413cf9SAndrew Gallatin 	} else {
2211e413cf9SAndrew Gallatin 		boundary = 4096;
2221e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2231e413cf9SAndrew Gallatin 	}
224b2fc195eSAndrew Gallatin 
225b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
226b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
227b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2281e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
229b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
230b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
231b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
232b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
233b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2341e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
235b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
236b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
237b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
238b2fc195eSAndrew Gallatin 	if (err != 0) {
239b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
240b2fc195eSAndrew Gallatin 		return err;
241b2fc195eSAndrew Gallatin 	}
242b2fc195eSAndrew Gallatin 
243b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
244b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
245b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
246b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
247b2fc195eSAndrew Gallatin 	if (err != 0) {
248b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
249b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
250b2fc195eSAndrew Gallatin 	}
251b2fc195eSAndrew Gallatin 
252b2fc195eSAndrew Gallatin 	/* load the memory */
253b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2546d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
255b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
256b2fc195eSAndrew Gallatin 	if (err != 0) {
257b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
258b2fc195eSAndrew Gallatin 		goto abort_with_mem;
259b2fc195eSAndrew Gallatin 	}
260b2fc195eSAndrew Gallatin 	return 0;
261b2fc195eSAndrew Gallatin 
262b2fc195eSAndrew Gallatin abort_with_mem:
263b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
264b2fc195eSAndrew Gallatin abort_with_dmat:
265b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
266b2fc195eSAndrew Gallatin 	return err;
267b2fc195eSAndrew Gallatin }
268b2fc195eSAndrew Gallatin 
269b2fc195eSAndrew Gallatin static void
2706d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
271b2fc195eSAndrew Gallatin {
272b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
273b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
274b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
275b2fc195eSAndrew Gallatin }
276b2fc195eSAndrew Gallatin 
277b2fc195eSAndrew Gallatin /*
278b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
279b2fc195eSAndrew Gallatin  * SN=x\0
280b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
281b2fc195eSAndrew Gallatin  * PC=text\0
282b2fc195eSAndrew Gallatin  */
283b2fc195eSAndrew Gallatin 
284b2fc195eSAndrew Gallatin static int
2856d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
286b2fc195eSAndrew Gallatin {
287dedbe836SAndrew Gallatin 	char *ptr;
288a4b233ddSAndrew Gallatin 	int i, found_mac, found_sn2;
289dedbe836SAndrew Gallatin 	char *endptr;
290b2fc195eSAndrew Gallatin 
291b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
292b2fc195eSAndrew Gallatin 	found_mac = 0;
293a4b233ddSAndrew Gallatin 	found_sn2 = 0;
294dedbe836SAndrew Gallatin 	while (*ptr != '\0') {
295dedbe836SAndrew Gallatin 		if (strncmp(ptr, "MAC=", 4) == 0) {
296dedbe836SAndrew Gallatin 			ptr += 4;
297dedbe836SAndrew Gallatin 			for (i = 0;;) {
298dedbe836SAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
299dedbe836SAndrew Gallatin 				if (endptr - ptr != 2)
300b2fc195eSAndrew Gallatin 					goto abort;
301dedbe836SAndrew Gallatin 				ptr = endptr;
302dedbe836SAndrew Gallatin 				if (++i == 6)
303dedbe836SAndrew Gallatin 					break;
304dedbe836SAndrew Gallatin 				if (*ptr++ != ':')
305dedbe836SAndrew Gallatin 					goto abort;
306b2fc195eSAndrew Gallatin 			}
307dedbe836SAndrew Gallatin 			found_mac = 1;
308dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "PC=", 3) == 0) {
3095e7d8541SAndrew Gallatin 			ptr += 3;
310dedbe836SAndrew Gallatin 			strlcpy(sc->product_code_string, ptr,
311dedbe836SAndrew Gallatin 			    sizeof(sc->product_code_string));
312dedbe836SAndrew Gallatin 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
3135e7d8541SAndrew Gallatin 			ptr += 3;
314dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
315dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
316dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
317a4b233ddSAndrew Gallatin 			/* SN2 takes precedence over SN */
318a4b233ddSAndrew Gallatin 			ptr += 4;
319a4b233ddSAndrew Gallatin 			found_sn2 = 1;
320dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
321dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
322b2fc195eSAndrew Gallatin 		}
323dedbe836SAndrew Gallatin 		while (*ptr++ != '\0') {}
324b2fc195eSAndrew Gallatin 	}
325b2fc195eSAndrew Gallatin 
326b2fc195eSAndrew Gallatin 	if (found_mac)
327b2fc195eSAndrew Gallatin 		return 0;
328b2fc195eSAndrew Gallatin 
329b2fc195eSAndrew Gallatin  abort:
330b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
331b2fc195eSAndrew Gallatin 
332b2fc195eSAndrew Gallatin 	return ENXIO;
333b2fc195eSAndrew Gallatin }
334b2fc195eSAndrew Gallatin 
3350d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3368fe615baSAndrew Gallatin static void
3378fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
338b2fc195eSAndrew Gallatin {
339b2fc195eSAndrew Gallatin 	uint32_t val;
3408fe615baSAndrew Gallatin 	unsigned long base, off;
341b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3428fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3438fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
344b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
345b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
346b2fc195eSAndrew Gallatin 
3478fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3488fe615baSAndrew Gallatin 		return;
3498fe615baSAndrew Gallatin 
3508fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3518fe615baSAndrew Gallatin 	if (pdev == NULL) {
3528fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3538fe615baSAndrew Gallatin 		return;
3548fe615baSAndrew Gallatin 	}
3558fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3568fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3578fe615baSAndrew Gallatin 
3588fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3598fe615baSAndrew Gallatin 		return;
3608fe615baSAndrew Gallatin 
3618fe615baSAndrew Gallatin 	base = 0;
3628fe615baSAndrew Gallatin 
3638fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3648fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3658fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3668fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3678fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3688fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3698fe615baSAndrew Gallatin 		if (mcp55 &&
3708fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3718fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3728fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3738fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3748fe615baSAndrew Gallatin 		}
3758fe615baSAndrew Gallatin 	}
3768fe615baSAndrew Gallatin 	if (!base)
3778fe615baSAndrew Gallatin 		return;
3788fe615baSAndrew Gallatin 
379b2fc195eSAndrew Gallatin 	/* XXXX
380b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
381b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
382b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
383b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
384b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
385b2fc195eSAndrew Gallatin 	*/
386b2fc195eSAndrew Gallatin #if 0
387b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
388b2fc195eSAndrew Gallatin 	   config space */
389b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
390b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
391b2fc195eSAndrew Gallatin 		val |= 0x40;
392b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3938fe615baSAndrew Gallatin 		return;
394b2fc195eSAndrew Gallatin 	}
395b2fc195eSAndrew Gallatin #endif
396b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
397b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
398b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
399b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
400b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
401b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
402b2fc195eSAndrew Gallatin 	 */
403b2fc195eSAndrew Gallatin 
404b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
405b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
406b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
407b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
408b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
409b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
410b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
411b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
412b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
413b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
414b2fc195eSAndrew Gallatin 
4158fe615baSAndrew Gallatin 	off =  base
416b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
417b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
418b2fc195eSAndrew Gallatin 						 + 8 * slot);
419b2fc195eSAndrew Gallatin 
420b2fc195eSAndrew Gallatin 	/* map it into the kernel */
421b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
422b2fc195eSAndrew Gallatin 
423b2fc195eSAndrew Gallatin 	if (va == NULL) {
424b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4258fe615baSAndrew Gallatin 		return;
426b2fc195eSAndrew Gallatin 	}
427b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
428b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
429b2fc195eSAndrew Gallatin 
430b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
431b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
432b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
433b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
434b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
435b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
4367ae99f80SJohn Baldwin 		pmap_unmapdev(va, PAGE_SIZE);
4378fe615baSAndrew Gallatin 		return;
438b2fc195eSAndrew Gallatin 	}
439b2fc195eSAndrew Gallatin 
440b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
441b2fc195eSAndrew Gallatin 	val = *ptr32;
442b2fc195eSAndrew Gallatin 
443b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
444b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
4457ae99f80SJohn Baldwin 		pmap_unmapdev(va, PAGE_SIZE);
4468fe615baSAndrew Gallatin 		return;
447b2fc195eSAndrew Gallatin 	}
448b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
4497ae99f80SJohn Baldwin 	pmap_unmapdev(va, PAGE_SIZE);
4505e7d8541SAndrew Gallatin 	if (mxge_verbose)
451b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4525e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4535e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
454b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4558fe615baSAndrew Gallatin 	return;
456b2fc195eSAndrew Gallatin }
457b2fc195eSAndrew Gallatin #else
4588fe615baSAndrew Gallatin static void
459f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
460b2fc195eSAndrew Gallatin {
461b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
462b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4638fe615baSAndrew Gallatin 	return;
464b2fc195eSAndrew Gallatin }
465b2fc195eSAndrew Gallatin #endif
4668fe615baSAndrew Gallatin 
4678fe615baSAndrew Gallatin static int
4688fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4698fe615baSAndrew Gallatin {
4708fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4718fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4728fe615baSAndrew Gallatin 	int status;
4738fe615baSAndrew Gallatin 	uint32_t len;
4748fe615baSAndrew Gallatin 	char *test = " ";
4758fe615baSAndrew Gallatin 
4768fe615baSAndrew Gallatin 	/* Run a small DMA test.
4778fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4788fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4798fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4808fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4818fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4828fe615baSAndrew Gallatin 	 * transfers took to complete.
4838fe615baSAndrew Gallatin 	 */
4848fe615baSAndrew Gallatin 
4851e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4868fe615baSAndrew Gallatin 
4878fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4888fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4898fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4908fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4918fe615baSAndrew Gallatin 	if (status != 0) {
4928fe615baSAndrew Gallatin 		test = "read";
4938fe615baSAndrew Gallatin 		goto abort;
4948fe615baSAndrew Gallatin 	}
4958fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4968fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4978fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4988fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4998fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
5008fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5018fe615baSAndrew Gallatin 	if (status != 0) {
5028fe615baSAndrew Gallatin 		test = "write";
5038fe615baSAndrew Gallatin 		goto abort;
5048fe615baSAndrew Gallatin 	}
5058fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5068fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5078fe615baSAndrew Gallatin 
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 * 0x10001;
5118fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5128fe615baSAndrew Gallatin 	if (status != 0) {
5138fe615baSAndrew Gallatin 		test = "read/write";
5148fe615baSAndrew Gallatin 		goto abort;
5158fe615baSAndrew Gallatin 	}
5168fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5178fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5188fe615baSAndrew Gallatin 
5198fe615baSAndrew Gallatin abort:
5208fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5218fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5228fe615baSAndrew Gallatin 			      test, status);
5238fe615baSAndrew Gallatin 
5248fe615baSAndrew Gallatin 	return status;
5258fe615baSAndrew Gallatin }
5268fe615baSAndrew Gallatin 
527b2fc195eSAndrew Gallatin /*
528b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
529b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
530b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
531b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
532b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
533b2fc195eSAndrew Gallatin  *
534b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
535b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
536b2fc195eSAndrew Gallatin  *
537b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
538b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
539b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
540b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5411e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
542b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5431e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
544b2fc195eSAndrew Gallatin  */
545b2fc195eSAndrew Gallatin 
5468fe615baSAndrew Gallatin static int
5478fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5488fe615baSAndrew Gallatin {
5498fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5508fe615baSAndrew Gallatin 	int reg, status;
5518fe615baSAndrew Gallatin 	uint16_t pectl;
5528fe615baSAndrew Gallatin 
5531e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5548fe615baSAndrew Gallatin 	/*
5558fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5568fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5578fe615baSAndrew Gallatin 	 */
5583b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
5598fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5608fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5618fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5628fe615baSAndrew Gallatin 				      pectl);
5631e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5648fe615baSAndrew Gallatin 		}
5658fe615baSAndrew Gallatin 	}
5668fe615baSAndrew Gallatin 
5678fe615baSAndrew Gallatin 	/*
5688fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5698fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5708fe615baSAndrew Gallatin 	 */
5718fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5721e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5738fe615baSAndrew Gallatin 	if (status != 0) {
5748fe615baSAndrew Gallatin 		return status;
5758fe615baSAndrew Gallatin 	}
5768fe615baSAndrew Gallatin 
5778fe615baSAndrew Gallatin 	/*
5788fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5798fe615baSAndrew Gallatin 	 */
5808fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5818fe615baSAndrew Gallatin 
5828fe615baSAndrew Gallatin 	/*
5838fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
584a4b233ddSAndrew Gallatin 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5858fe615baSAndrew Gallatin 	 */
586a4b233ddSAndrew Gallatin 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
587a4b233ddSAndrew Gallatin 		return 0;
5888fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5898fe615baSAndrew Gallatin 	if (status == 0)
5908fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5918fe615baSAndrew Gallatin 
5928fe615baSAndrew Gallatin 	if (status != E2BIG)
5938fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5948fe615baSAndrew Gallatin 	if (status == ENOSYS)
5958fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5968fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5978fe615baSAndrew Gallatin 	return status;
5988fe615baSAndrew Gallatin }
5998fe615baSAndrew Gallatin 
6008fe615baSAndrew Gallatin static int
6016d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
602b2fc195eSAndrew Gallatin {
6038fe615baSAndrew Gallatin 	int aligned = 0;
60465c69066SAndrew Gallatin 	int force_firmware = mxge_force_firmware;
605b2fc195eSAndrew Gallatin 
60665c69066SAndrew Gallatin 	if (sc->throttle)
60765c69066SAndrew Gallatin 		force_firmware = sc->throttle;
608d91b1b49SAndrew Gallatin 
60965c69066SAndrew Gallatin 	if (force_firmware != 0) {
61065c69066SAndrew Gallatin 		if (force_firmware == 1)
611d91b1b49SAndrew Gallatin 			aligned = 1;
612d91b1b49SAndrew Gallatin 		else
613d91b1b49SAndrew Gallatin 			aligned = 0;
614d91b1b49SAndrew Gallatin 		if (mxge_verbose)
615d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
616d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
617d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
618d91b1b49SAndrew Gallatin 		goto abort;
619d91b1b49SAndrew Gallatin 	}
620d91b1b49SAndrew Gallatin 
621d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
622d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
623d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
624d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
625d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
626d91b1b49SAndrew Gallatin 			      sc->link_width);
627d91b1b49SAndrew Gallatin 		aligned = 1;
628d91b1b49SAndrew Gallatin 		goto abort;
629d91b1b49SAndrew Gallatin 	}
630d91b1b49SAndrew Gallatin 
6318fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6328fe615baSAndrew Gallatin 		return 0;
633b2fc195eSAndrew Gallatin 
634b2fc195eSAndrew Gallatin abort:
635b2fc195eSAndrew Gallatin 	if (aligned) {
6366d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6371e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
638b2fc195eSAndrew Gallatin 	} else {
6396d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6401e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
641b2fc195eSAndrew Gallatin 	}
6421e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
643b2fc195eSAndrew Gallatin }
644b2fc195eSAndrew Gallatin 
6454da0d523SAndrew Gallatin static int
6464da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6474da0d523SAndrew Gallatin {
648b824b7d8SAndrew Gallatin 
6494da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6504da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6514da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6524da0d523SAndrew Gallatin 		return EIO;
6534da0d523SAndrew Gallatin 	}
6544da0d523SAndrew Gallatin 
6554da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
656dedbe836SAndrew Gallatin 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6574da0d523SAndrew Gallatin 	if (mxge_verbose)
6584da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6594da0d523SAndrew Gallatin 
660b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
661b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6624da0d523SAndrew Gallatin 
663b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
664b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6654da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6664da0d523SAndrew Gallatin 			      sc->fw_version);
6674da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6684da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6694da0d523SAndrew Gallatin 		return EINVAL;
6704da0d523SAndrew Gallatin 	}
6714da0d523SAndrew Gallatin 	return 0;
6724da0d523SAndrew Gallatin 
6734da0d523SAndrew Gallatin }
674b2fc195eSAndrew Gallatin 
675b2fc195eSAndrew Gallatin static int
6766d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
677b2fc195eSAndrew Gallatin {
678f9ae0280SAndrew Gallatin 	z_stream zs;
679f9ae0280SAndrew Gallatin 	char *inflate_buffer;
68033d54970SLuigi Rizzo 	const struct firmware *fw;
681b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
682b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
683b2fc195eSAndrew Gallatin 	int status;
6844da0d523SAndrew Gallatin 	unsigned int i;
685f9ae0280SAndrew Gallatin 	size_t fw_len;
686b2fc195eSAndrew Gallatin 
687b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
688b2fc195eSAndrew Gallatin 	if (fw == NULL) {
689b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
690b2fc195eSAndrew Gallatin 			      sc->fw_name);
691b2fc195eSAndrew Gallatin 		return ENOENT;
692b2fc195eSAndrew Gallatin 	}
693b2fc195eSAndrew Gallatin 
694f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
695f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
6961dbf944aSXin LI 	zs.zalloc = zcalloc_nowait;
6971dbf944aSXin LI 	zs.zfree = zcfree;
698f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
699f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
700b2fc195eSAndrew Gallatin 		status = EIO;
701b2fc195eSAndrew Gallatin 		goto abort_with_fw;
702b2fc195eSAndrew Gallatin 	}
703f9ae0280SAndrew Gallatin 
704f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
705f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
706f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
707f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
708f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
709f9ae0280SAndrew Gallatin 		goto abort_with_zs;
710f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
711f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
712f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
713f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
714f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
715f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
716f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
717f9ae0280SAndrew Gallatin 		status = EIO;
718f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
719f9ae0280SAndrew Gallatin 	}
720f9ae0280SAndrew Gallatin 
721f9ae0280SAndrew Gallatin 	/* check id */
722f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
723f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
724f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
725f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
726f9ae0280SAndrew Gallatin 		status = EIO;
727f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
728f9ae0280SAndrew Gallatin 	}
729f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
730b2fc195eSAndrew Gallatin 
7314da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7324da0d523SAndrew Gallatin 	if (status != 0)
733f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
734b2fc195eSAndrew Gallatin 
735b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
736f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7374da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
738f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
739f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
74073c7c83fSAndrew Gallatin 		wmb();
741b08a56adSJohn Baldwin 		(void)*sc->sram;
74273c7c83fSAndrew Gallatin 		wmb();
7434da0d523SAndrew Gallatin 	}
744b2fc195eSAndrew Gallatin 
745f9ae0280SAndrew Gallatin 	*limit = fw_len;
746b2fc195eSAndrew Gallatin 	status = 0;
747f9ae0280SAndrew Gallatin abort_with_buffer:
748f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
749f9ae0280SAndrew Gallatin abort_with_zs:
750f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
751b2fc195eSAndrew Gallatin abort_with_fw:
752b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
753b2fc195eSAndrew Gallatin 	return status;
754b2fc195eSAndrew Gallatin }
755b2fc195eSAndrew Gallatin 
756b2fc195eSAndrew Gallatin /*
757b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
758b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
759b2fc195eSAndrew Gallatin  */
760b2fc195eSAndrew Gallatin 
761b2fc195eSAndrew Gallatin static void
7626d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
763b2fc195eSAndrew Gallatin {
764b2fc195eSAndrew Gallatin 	char buf_bytes[72];
765b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
766b2fc195eSAndrew Gallatin 	volatile char *submit;
767b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
768b2fc195eSAndrew Gallatin 	int i;
769b2fc195eSAndrew Gallatin 
770aa54c242SJohn Baldwin 	buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
771b2fc195eSAndrew Gallatin 
772b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
773b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
774b2fc195eSAndrew Gallatin 	*confirm = 0;
77573c7c83fSAndrew Gallatin 	wmb();
776b2fc195eSAndrew Gallatin 
777b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
778b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
779b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
780b2fc195eSAndrew Gallatin 	*/
781b2fc195eSAndrew Gallatin 
7826d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7836d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
784b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
785b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
786b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7876d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7886d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
789b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
790b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
791b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
792b2fc195eSAndrew Gallatin 
7930fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
794b2fc195eSAndrew Gallatin 
7956d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
79673c7c83fSAndrew Gallatin 	wmb();
797b2fc195eSAndrew Gallatin 	DELAY(1000);
79873c7c83fSAndrew Gallatin 	wmb();
799b2fc195eSAndrew Gallatin 	i = 0;
800b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
801b2fc195eSAndrew Gallatin 		DELAY(1000);
802b2fc195eSAndrew Gallatin 		i++;
803b2fc195eSAndrew Gallatin 	}
804b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
805b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
806b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
807b2fc195eSAndrew Gallatin 			      *confirm);
808b2fc195eSAndrew Gallatin 	}
809b2fc195eSAndrew Gallatin 	return;
810b2fc195eSAndrew Gallatin }
811b2fc195eSAndrew Gallatin 
812b2fc195eSAndrew Gallatin static int
8136d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
814b2fc195eSAndrew Gallatin {
815b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
816b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
817b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8180fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
819b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
820e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
821b2fc195eSAndrew Gallatin 
822b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
823aa54c242SJohn Baldwin 	buf = (mcp_cmd_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
824b2fc195eSAndrew Gallatin 
825b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
826b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
827b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
828b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8296d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8306d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
831b2fc195eSAndrew Gallatin 
832b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
833b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
834a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
835b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
83673c7c83fSAndrew Gallatin 	wmb();
8376d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
838b2fc195eSAndrew Gallatin 
8395e7d8541SAndrew Gallatin 	/* wait up to 20ms */
840e0501fd0SAndrew Gallatin 	err = EAGAIN;
8415e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
842b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
843b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
84473c7c83fSAndrew Gallatin 		wmb();
845e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
846e0501fd0SAndrew Gallatin 		case 0:
847b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
848e0501fd0SAndrew Gallatin 			err = 0;
849e0501fd0SAndrew Gallatin 			break;
850e0501fd0SAndrew Gallatin 		case 0xffffffff:
851e0501fd0SAndrew Gallatin 			DELAY(1000);
852e0501fd0SAndrew Gallatin 			break;
853e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
854e0501fd0SAndrew Gallatin 			err = ENOSYS;
855e0501fd0SAndrew Gallatin 			break;
856e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
857e0501fd0SAndrew Gallatin 			err = E2BIG;
858e0501fd0SAndrew Gallatin 			break;
859c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
860c587e59fSAndrew Gallatin 			err = EBUSY;
861c587e59fSAndrew Gallatin 			break;
862c406ad2eSAndrew Gallatin 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
863c406ad2eSAndrew Gallatin 			err = ENXIO;
864c406ad2eSAndrew Gallatin 			break;
865e0501fd0SAndrew Gallatin 		default:
866b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8676d87a65dSAndrew Gallatin 				      "mxge: command %d "
868b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
869b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
870e0501fd0SAndrew Gallatin 			err = ENXIO;
871e0501fd0SAndrew Gallatin 			break;
872b2fc195eSAndrew Gallatin 		}
873e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
874e0501fd0SAndrew Gallatin 			break;
875b2fc195eSAndrew Gallatin 	}
876e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8776d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
878b2fc195eSAndrew Gallatin 			      "result = %d\n",
879b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
880e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
881e0501fd0SAndrew Gallatin 	return err;
882b2fc195eSAndrew Gallatin }
883b2fc195eSAndrew Gallatin 
8844da0d523SAndrew Gallatin static int
8854da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8864da0d523SAndrew Gallatin {
8874da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8884da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8894da0d523SAndrew Gallatin 	size_t hdr_offset;
8904da0d523SAndrew Gallatin 	int status;
8914da0d523SAndrew Gallatin 
8924da0d523SAndrew Gallatin 	/* find running firmware header */
8934da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
8944da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
8954da0d523SAndrew Gallatin 
8964da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8974da0d523SAndrew Gallatin 		device_printf(sc->dev,
8984da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
8994da0d523SAndrew Gallatin 			      (int)hdr_offset);
9004da0d523SAndrew Gallatin 		return EIO;
9014da0d523SAndrew Gallatin 	}
9024da0d523SAndrew Gallatin 
9034da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9044da0d523SAndrew Gallatin 	 * validate firmware */
9054da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9064da0d523SAndrew Gallatin 	if (hdr == NULL) {
9074da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9084da0d523SAndrew Gallatin 		return ENOMEM;
9094da0d523SAndrew Gallatin 	}
9104da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9114da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9124da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9134da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9144da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
915b824b7d8SAndrew Gallatin 
916b824b7d8SAndrew Gallatin 	/*
917b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
918b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
919b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
920b824b7d8SAndrew Gallatin 	 */
921b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
922b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
923b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
924b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
925b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
926b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
927b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
928b824b7d8SAndrew Gallatin 	}
929b824b7d8SAndrew Gallatin 
9304da0d523SAndrew Gallatin 	return status;
9314da0d523SAndrew Gallatin }
9324da0d523SAndrew Gallatin 
933b2fc195eSAndrew Gallatin static int
9341e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
935b2fc195eSAndrew Gallatin {
936b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
937b2fc195eSAndrew Gallatin 	volatile char *submit;
938b2fc195eSAndrew Gallatin 	char buf_bytes[72];
939b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
940b2fc195eSAndrew Gallatin 	int status, i;
941b2fc195eSAndrew Gallatin 
942aa54c242SJohn Baldwin 	buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
943b2fc195eSAndrew Gallatin 
944b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9456d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
946b2fc195eSAndrew Gallatin 	if (status) {
9471e413cf9SAndrew Gallatin 		if (!adopt)
9481e413cf9SAndrew Gallatin 			return status;
9494da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9504da0d523SAndrew Gallatin 		   it is new enough */
9514da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9524da0d523SAndrew Gallatin 		if (status) {
9534da0d523SAndrew Gallatin 			device_printf(sc->dev,
9544da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
955b2fc195eSAndrew Gallatin 			return status;
956b2fc195eSAndrew Gallatin 		}
9574da0d523SAndrew Gallatin 		device_printf(sc->dev,
9584da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9591e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9604da0d523SAndrew Gallatin 			device_printf(sc->dev,
9614da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9624da0d523SAndrew Gallatin 				 ".  For optimal\n");
9634da0d523SAndrew Gallatin 			device_printf(sc->dev,
9644da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9654da0d523SAndrew Gallatin 				 "firmware\n");
9664da0d523SAndrew Gallatin 		}
967d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9681e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
969d91b1b49SAndrew Gallatin 		return 0;
9704da0d523SAndrew Gallatin 	}
971b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
972b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
973b2fc195eSAndrew Gallatin 	*confirm = 0;
97473c7c83fSAndrew Gallatin 	wmb();
975b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
976b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
977b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
978b2fc195eSAndrew Gallatin 	*/
979b2fc195eSAndrew Gallatin 
9806d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9816d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
982b2fc195eSAndrew Gallatin 
983b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
984b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
985b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
986b2fc195eSAndrew Gallatin 
987b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
988b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
989b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
990b2fc195eSAndrew Gallatin 	*/
991b2fc195eSAndrew Gallatin 					/* where the code starts*/
9926d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
993b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
994b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
995b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
996b2fc195eSAndrew Gallatin 
9970fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9986d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
99973c7c83fSAndrew Gallatin 	wmb();
1000b2fc195eSAndrew Gallatin 	DELAY(1000);
100173c7c83fSAndrew Gallatin 	wmb();
1002b2fc195eSAndrew Gallatin 	i = 0;
1003b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1004b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1005b2fc195eSAndrew Gallatin 		i++;
1006b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1007b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1008b2fc195eSAndrew Gallatin 	}
1009b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1010b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1011b2fc195eSAndrew Gallatin 			confirm, *confirm);
1012b2fc195eSAndrew Gallatin 
1013b2fc195eSAndrew Gallatin 		return ENXIO;
1014b2fc195eSAndrew Gallatin 	}
1015b2fc195eSAndrew Gallatin 	return 0;
1016b2fc195eSAndrew Gallatin }
1017b2fc195eSAndrew Gallatin 
1018b2fc195eSAndrew Gallatin static int
10196d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1020b2fc195eSAndrew Gallatin {
10216d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1022b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1023b2fc195eSAndrew Gallatin 	int status;
1024b2fc195eSAndrew Gallatin 
1025b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1026b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1027b2fc195eSAndrew Gallatin 
1028b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1029b2fc195eSAndrew Gallatin 
10305e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1031b2fc195eSAndrew Gallatin 	return status;
1032b2fc195eSAndrew Gallatin }
1033b2fc195eSAndrew Gallatin 
1034b2fc195eSAndrew Gallatin static int
10356d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1036b2fc195eSAndrew Gallatin {
10376d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1038b2fc195eSAndrew Gallatin 	int status;
1039b2fc195eSAndrew Gallatin 
1040b2fc195eSAndrew Gallatin 	if (pause)
10415e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1042b2fc195eSAndrew Gallatin 				       &cmd);
1043b2fc195eSAndrew Gallatin 	else
10445e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1045b2fc195eSAndrew Gallatin 				       &cmd);
1046b2fc195eSAndrew Gallatin 
1047b2fc195eSAndrew Gallatin 	if (status) {
1048b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1049b2fc195eSAndrew Gallatin 		return ENXIO;
1050b2fc195eSAndrew Gallatin 	}
1051b2fc195eSAndrew Gallatin 	sc->pause = pause;
1052b2fc195eSAndrew Gallatin 	return 0;
1053b2fc195eSAndrew Gallatin }
1054b2fc195eSAndrew Gallatin 
1055b2fc195eSAndrew Gallatin static void
10566d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1057b2fc195eSAndrew Gallatin {
10586d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1059b2fc195eSAndrew Gallatin 	int status;
1060b2fc195eSAndrew Gallatin 
10611e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10621e413cf9SAndrew Gallatin 		promisc = 1;
10631e413cf9SAndrew Gallatin 
1064b2fc195eSAndrew Gallatin 	if (promisc)
10655e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1066b2fc195eSAndrew Gallatin 				       &cmd);
1067b2fc195eSAndrew Gallatin 	else
10685e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1069b2fc195eSAndrew Gallatin 				       &cmd);
1070b2fc195eSAndrew Gallatin 
1071b2fc195eSAndrew Gallatin 	if (status) {
1072b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1073b2fc195eSAndrew Gallatin 	}
1074b2fc195eSAndrew Gallatin }
1075b2fc195eSAndrew Gallatin 
10762a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx {
10772a0f8518SGleb Smirnoff 	mxge_softc_t *sc;
10782a0f8518SGleb Smirnoff 	int error;
10792a0f8518SGleb Smirnoff };
10802a0f8518SGleb Smirnoff 
10812a0f8518SGleb Smirnoff static u_int
10822a0f8518SGleb Smirnoff mxge_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
10832a0f8518SGleb Smirnoff {
10842a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx *ctx = arg;
10852a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
10862a0f8518SGleb Smirnoff 
10872a0f8518SGleb Smirnoff 	if (ctx->error != 0)
10882a0f8518SGleb Smirnoff 		return (0);
10892a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl), &cmd.data0, 4);
10902a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl) + 4, &cmd.data1, 2);
10912a0f8518SGleb Smirnoff 	cmd.data0 = htonl(cmd.data0);
10922a0f8518SGleb Smirnoff 	cmd.data1 = htonl(cmd.data1);
10932a0f8518SGleb Smirnoff 
10942a0f8518SGleb Smirnoff 	ctx->error = mxge_send_cmd(ctx->sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10952a0f8518SGleb Smirnoff 
10962a0f8518SGleb Smirnoff 	return (1);
10972a0f8518SGleb Smirnoff }
10982a0f8518SGleb Smirnoff 
10990fa7f681SAndrew Gallatin static void
11000fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
11010fa7f681SAndrew Gallatin {
11022a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx ctx;
1103*93037a67SJustin Hibbits 	if_t ifp = sc->ifp;
11042a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
11050fa7f681SAndrew Gallatin 	int err;
11060fa7f681SAndrew Gallatin 
11070fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11080fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11090fa7f681SAndrew Gallatin 		return;
11100fa7f681SAndrew Gallatin 
11110fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11120fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11130fa7f681SAndrew Gallatin 	if (err != 0) {
11140fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11150fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11160fa7f681SAndrew Gallatin 		return;
11170fa7f681SAndrew Gallatin 	}
11180fa7f681SAndrew Gallatin 
1119b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1120b824b7d8SAndrew Gallatin 		return;
11210fa7f681SAndrew Gallatin 
1122*93037a67SJustin Hibbits 	if (if_getflags(ifp) & IFF_ALLMULTI)
11230fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11240fa7f681SAndrew Gallatin 		return;
11250fa7f681SAndrew Gallatin 
11260fa7f681SAndrew Gallatin 	/* Flush all the filters */
11270fa7f681SAndrew Gallatin 
11280fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11290fa7f681SAndrew Gallatin 	if (err != 0) {
11300fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11310fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11320fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11330fa7f681SAndrew Gallatin 		return;
11340fa7f681SAndrew Gallatin 	}
11350fa7f681SAndrew Gallatin 
11360fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11372a0f8518SGleb Smirnoff 	ctx.sc = sc;
11382a0f8518SGleb Smirnoff 	ctx.error = 0;
11392a0f8518SGleb Smirnoff 	if_foreach_llmaddr(ifp, mxge_add_maddr, &ctx);
11402a0f8518SGleb Smirnoff 	if (ctx.error != 0) {
11412a0f8518SGleb Smirnoff 		device_printf(sc->dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
11422a0f8518SGleb Smirnoff 		    "error status:" "%d\t", ctx.error);
11430fa7f681SAndrew Gallatin 		/* abort, leaving multicast filtering off */
11440fa7f681SAndrew Gallatin 		return;
11450fa7f681SAndrew Gallatin 	}
11462a0f8518SGleb Smirnoff 
11470fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11480fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11490fa7f681SAndrew Gallatin 	if (err != 0) {
11500fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11510fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11520fa7f681SAndrew Gallatin 	}
11530fa7f681SAndrew Gallatin }
11540fa7f681SAndrew Gallatin 
1155b2fc195eSAndrew Gallatin static int
1156053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1157053e637fSAndrew Gallatin {
1158053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1159053e637fSAndrew Gallatin 	int status;
1160053e637fSAndrew Gallatin 
1161c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1162c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1163053e637fSAndrew Gallatin 
1164053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1165053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1166053e637fSAndrew Gallatin 	cmd.data0 = 0;
1167053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1168053e637fSAndrew Gallatin 			       &cmd);
1169053e637fSAndrew Gallatin 	if (status == 0)
1170c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1171053e637fSAndrew Gallatin 
1172053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1173053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1174053e637fSAndrew Gallatin }
1175053e637fSAndrew Gallatin 
1176053e637fSAndrew Gallatin static int
1177adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1178b2fc195eSAndrew Gallatin {
11791e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11801e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11811e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11826d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11831e413cf9SAndrew Gallatin 	int slice, status;
1184b2fc195eSAndrew Gallatin 
1185b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1186b2fc195eSAndrew Gallatin 	   is alive */
1187b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11885e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1189b2fc195eSAndrew Gallatin 	if (status != 0) {
1190b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1191b2fc195eSAndrew Gallatin 		return ENXIO;
1192b2fc195eSAndrew Gallatin 	}
1193b2fc195eSAndrew Gallatin 
1194091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1195091feecdSAndrew Gallatin 
11961e413cf9SAndrew Gallatin 	/* set the intrq size */
11971e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
11981e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11991e413cf9SAndrew Gallatin 
12001e413cf9SAndrew Gallatin 	/*
12011e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12021e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12031e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12041e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12051e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12061e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12071e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12081e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12091e413cf9SAndrew Gallatin 	 */
12101e413cf9SAndrew Gallatin 
12111e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12121e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12131e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12141e413cf9SAndrew Gallatin 					   &cmd);
12151e413cf9SAndrew Gallatin 		if (status != 0) {
12161e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12171e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12181e413cf9SAndrew Gallatin 			return status;
12191e413cf9SAndrew Gallatin 		}
12201e413cf9SAndrew Gallatin 		/*
12211e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12221e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12231e413cf9SAndrew Gallatin 		 */
12241e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12251e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1226c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
12271e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12281e413cf9SAndrew Gallatin 					   &cmd);
12291e413cf9SAndrew Gallatin 		if (status != 0) {
12301e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12311e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12321e413cf9SAndrew Gallatin 			return status;
12331e413cf9SAndrew Gallatin 		}
12341e413cf9SAndrew Gallatin 	}
12351e413cf9SAndrew Gallatin 
1236adae7080SAndrew Gallatin 	if (interrupts_setup) {
1237b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12381e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12391e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12401e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12411e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12421e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12431e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12441e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12451e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12461e413cf9SAndrew Gallatin 						&cmd);
12471e413cf9SAndrew Gallatin 		}
1248adae7080SAndrew Gallatin 	}
1249b2fc195eSAndrew Gallatin 
12506d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12515e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12525e7d8541SAndrew Gallatin 
12535e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12545e7d8541SAndrew Gallatin 
12555e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12561e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12575e7d8541SAndrew Gallatin 
12585e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12596d87a65dSAndrew Gallatin 				&cmd);
12605e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1261b2fc195eSAndrew Gallatin 	if (status != 0) {
1262b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1263b2fc195eSAndrew Gallatin 		return status;
1264b2fc195eSAndrew Gallatin 	}
1265b2fc195eSAndrew Gallatin 
12665e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12675e7d8541SAndrew Gallatin 
12685e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12698fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12705e7d8541SAndrew Gallatin 
12711e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12721e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12731e413cf9SAndrew Gallatin 
12741e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1275b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12761e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12771e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12781e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12791e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12801e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1281c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1282c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1283c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
12841e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
12851e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
12861e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
12871e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
12881e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
128926dd49c6SAndrew Gallatin 		ss->lc.lro_bad_csum = 0;
129026dd49c6SAndrew Gallatin 		ss->lc.lro_queued = 0;
129126dd49c6SAndrew Gallatin 		ss->lc.lro_flushed = 0;
12921e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
1293a393336bSAndrew Gallatin 			bzero(ss->fw_stats, sizeof *ss->fw_stats);
12941e413cf9SAndrew Gallatin 		}
12951e413cf9SAndrew Gallatin 	}
1296b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
12976d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1298*93037a67SJustin Hibbits 	mxge_change_promisc(sc, if_getflags(sc->ifp) & IFF_PROMISC);
12996d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13000fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
130165c69066SAndrew Gallatin 	if (sc->throttle) {
130265c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
130365c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
130465c69066SAndrew Gallatin 				  &cmd)) {
130565c69066SAndrew Gallatin 			device_printf(sc->dev,
130665c69066SAndrew Gallatin 				      "can't enable throttle\n");
130765c69066SAndrew Gallatin 		}
130865c69066SAndrew Gallatin 	}
1309b2fc195eSAndrew Gallatin 	return status;
1310b2fc195eSAndrew Gallatin }
1311b2fc195eSAndrew Gallatin 
1312b2fc195eSAndrew Gallatin static int
131365c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
131465c69066SAndrew Gallatin {
131565c69066SAndrew Gallatin 	mxge_cmd_t cmd;
131665c69066SAndrew Gallatin 	mxge_softc_t *sc;
131765c69066SAndrew Gallatin 	int err;
131865c69066SAndrew Gallatin 	unsigned int throttle;
131965c69066SAndrew Gallatin 
132065c69066SAndrew Gallatin 	sc = arg1;
132165c69066SAndrew Gallatin 	throttle = sc->throttle;
132265c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
132365c69066SAndrew Gallatin 	if (err != 0) {
132465c69066SAndrew Gallatin 		return err;
132565c69066SAndrew Gallatin 	}
132665c69066SAndrew Gallatin 
132765c69066SAndrew Gallatin 	if (throttle == sc->throttle)
132865c69066SAndrew Gallatin 		return 0;
132965c69066SAndrew Gallatin 
133065c69066SAndrew Gallatin 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
133165c69066SAndrew Gallatin 		return EINVAL;
133265c69066SAndrew Gallatin 
133365c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
133465c69066SAndrew Gallatin 	cmd.data0 = throttle;
133565c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
133665c69066SAndrew Gallatin 	if (err == 0)
133765c69066SAndrew Gallatin 		sc->throttle = throttle;
133865c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
133965c69066SAndrew Gallatin 	return err;
134065c69066SAndrew Gallatin }
134165c69066SAndrew Gallatin 
134265c69066SAndrew Gallatin static int
13436d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1344b2fc195eSAndrew Gallatin {
13456d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1346b2fc195eSAndrew Gallatin 	unsigned int intr_coal_delay;
1347b2fc195eSAndrew Gallatin 	int err;
1348b2fc195eSAndrew Gallatin 
1349b2fc195eSAndrew Gallatin 	sc = arg1;
1350b2fc195eSAndrew Gallatin 	intr_coal_delay = sc->intr_coal_delay;
1351b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1352b2fc195eSAndrew Gallatin 	if (err != 0) {
1353b2fc195eSAndrew Gallatin 		return err;
1354b2fc195eSAndrew Gallatin 	}
1355b2fc195eSAndrew Gallatin 	if (intr_coal_delay == sc->intr_coal_delay)
1356b2fc195eSAndrew Gallatin 		return 0;
1357b2fc195eSAndrew Gallatin 
1358b2fc195eSAndrew Gallatin 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1359b2fc195eSAndrew Gallatin 		return EINVAL;
1360b2fc195eSAndrew Gallatin 
1361a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13625e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1363b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13645e7d8541SAndrew Gallatin 
1365a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1366b2fc195eSAndrew Gallatin 	return err;
1367b2fc195eSAndrew Gallatin }
1368b2fc195eSAndrew Gallatin 
1369b2fc195eSAndrew Gallatin static int
13706d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1371b2fc195eSAndrew Gallatin {
13726d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1373b2fc195eSAndrew Gallatin 	unsigned int enabled;
1374b2fc195eSAndrew Gallatin 	int err;
1375b2fc195eSAndrew Gallatin 
1376b2fc195eSAndrew Gallatin 	sc = arg1;
1377b2fc195eSAndrew Gallatin 	enabled = sc->pause;
1378b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
1379b2fc195eSAndrew Gallatin 	if (err != 0) {
1380b2fc195eSAndrew Gallatin 		return err;
1381b2fc195eSAndrew Gallatin 	}
1382b2fc195eSAndrew Gallatin 	if (enabled == sc->pause)
1383b2fc195eSAndrew Gallatin 		return 0;
1384b2fc195eSAndrew Gallatin 
1385a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13866d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1387a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1388b2fc195eSAndrew Gallatin 	return err;
1389b2fc195eSAndrew Gallatin }
1390b2fc195eSAndrew Gallatin 
1391b2fc195eSAndrew Gallatin static int
13926d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1393b2fc195eSAndrew Gallatin {
1394b2fc195eSAndrew Gallatin 	int err;
1395b2fc195eSAndrew Gallatin 
1396b2fc195eSAndrew Gallatin 	if (arg1 == NULL)
1397b2fc195eSAndrew Gallatin 		return EFAULT;
1398b2fc195eSAndrew Gallatin 	arg2 = be32toh(*(int *)arg1);
1399b2fc195eSAndrew Gallatin 	arg1 = NULL;
1400b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, arg1, arg2, req);
1401b2fc195eSAndrew Gallatin 
1402b2fc195eSAndrew Gallatin 	return err;
1403b2fc195eSAndrew Gallatin }
1404b2fc195eSAndrew Gallatin 
1405b2fc195eSAndrew Gallatin static void
14061e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14071e413cf9SAndrew Gallatin {
14081e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14091e413cf9SAndrew Gallatin 	int slice;
14101e413cf9SAndrew Gallatin 
14111e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14121e413cf9SAndrew Gallatin 		return;
14131e413cf9SAndrew Gallatin 
14141e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14151e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14161e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14171e413cf9SAndrew Gallatin 			continue;
14181e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14191e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14201e413cf9SAndrew Gallatin 	}
14211e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14221e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14231e413cf9SAndrew Gallatin }
14241e413cf9SAndrew Gallatin 
14251e413cf9SAndrew Gallatin static void
14266d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1427b2fc195eSAndrew Gallatin {
1428b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1429b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14305e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14311e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14321e413cf9SAndrew Gallatin 	int slice;
14331e413cf9SAndrew Gallatin 	char slice_num[8];
1434b2fc195eSAndrew Gallatin 
1435b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1436b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14371e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1438b2fc195eSAndrew Gallatin 
14395e7d8541SAndrew Gallatin 	/* random information */
14405e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14415e7d8541SAndrew Gallatin 		       "firmware_version",
1442f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->fw_version,
14435e7d8541SAndrew Gallatin 		       0, "firmware version");
14445e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14455e7d8541SAndrew Gallatin 		       "serial_number",
1446f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->serial_number_string,
14475e7d8541SAndrew Gallatin 		       0, "serial number");
14485e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14495e7d8541SAndrew Gallatin 		       "product_code",
1450f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->product_code_string,
14515e7d8541SAndrew Gallatin 		       0, "product_code");
14525e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1453d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1454d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1455d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1456d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14575e7d8541SAndrew Gallatin 		       "tx_boundary",
14581e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14595e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14605e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1461091feecdSAndrew Gallatin 		       "write_combine",
1462091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1463091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1464091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14655e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14665e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14675e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14685e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14695e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14705e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14715e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14725e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14735e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14745e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14755e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
1476a393336bSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1477a393336bSAndrew Gallatin 		       "watchdog_resets",
1478a393336bSAndrew Gallatin 		       CTLFLAG_RD, &sc->watchdog_resets,
1479a393336bSAndrew Gallatin 		       0, "Number of times NIC was reset");
14805e7d8541SAndrew Gallatin 
14815e7d8541SAndrew Gallatin 	/* performance related tunables */
1482b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14837029da5cSPawel Biernacki 	    "intr_coal_delay", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
14847029da5cSPawel Biernacki 	    sc, 0, mxge_change_intr_coal, "I",
14857029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1486b2fc195eSAndrew Gallatin 
1487b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14887029da5cSPawel Biernacki 	    "throttle", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14897029da5cSPawel Biernacki 	    mxge_change_throttle, "I", "transmit throttling");
149065c69066SAndrew Gallatin 
149165c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1492b2fc195eSAndrew Gallatin 	    "flow_control_enabled",
14937029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14947029da5cSPawel Biernacki 	    mxge_change_flow_control, "I",
14957029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1496b2fc195eSAndrew Gallatin 
1497b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14985e7d8541SAndrew Gallatin 		       "deassert_wait",
14995e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15005e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1501b2fc195eSAndrew Gallatin 
1502b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1503b2fc195eSAndrew Gallatin 	   Need to swap it */
1504b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15057029da5cSPawel Biernacki 	    "link_up", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15067029da5cSPawel Biernacki 	    &fw->link_up, 0, mxge_handle_be32, "I", "link up");
1507b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15087029da5cSPawel Biernacki 	    "rdma_tags_available", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15097029da5cSPawel Biernacki 	    &fw->rdma_tags_available, 0, mxge_handle_be32, "I",
15107029da5cSPawel Biernacki 	    "rdma_tags_available");
1511b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15127029da5cSPawel Biernacki 	    "dropped_bad_crc32", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15137029da5cSPawel Biernacki 	    &fw->dropped_bad_crc32, 0, mxge_handle_be32, "I",
15147029da5cSPawel Biernacki 	    "dropped_bad_crc32");
1515adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15167029da5cSPawel Biernacki 	    "dropped_bad_phy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15177029da5cSPawel Biernacki 	    &fw->dropped_bad_phy, 0, mxge_handle_be32, "I", "dropped_bad_phy");
1518b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1519b2fc195eSAndrew Gallatin 	    "dropped_link_error_or_filtered",
15207029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15217029da5cSPawel Biernacki 	    &fw->dropped_link_error_or_filtered, 0, mxge_handle_be32, "I",
15227029da5cSPawel Biernacki 	    "dropped_link_error_or_filtered");
1523b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1524adae7080SAndrew Gallatin 	    "dropped_link_overflow",
15257029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15267029da5cSPawel Biernacki 	    &fw->dropped_link_overflow, 0, mxge_handle_be32, "I",
15277029da5cSPawel Biernacki 	    "dropped_link_overflow");
1528adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15290fa7f681SAndrew Gallatin 	    "dropped_multicast_filtered",
15307029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15317029da5cSPawel Biernacki 	    &fw->dropped_multicast_filtered, 0, mxge_handle_be32, "I",
15327029da5cSPawel Biernacki 	    "dropped_multicast_filtered");
15330fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1534adae7080SAndrew Gallatin 	    "dropped_no_big_buffer",
15357029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15367029da5cSPawel Biernacki 	    &fw->dropped_no_big_buffer, 0, mxge_handle_be32, "I",
15377029da5cSPawel Biernacki 	    "dropped_no_big_buffer");
1538b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1539b2fc195eSAndrew Gallatin 	    "dropped_no_small_buffer",
15407029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15417029da5cSPawel Biernacki 	    &fw->dropped_no_small_buffer, 0, mxge_handle_be32, "I",
15427029da5cSPawel Biernacki 	    "dropped_no_small_buffer");
1543b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1544adae7080SAndrew Gallatin 	    "dropped_overrun",
15457029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15467029da5cSPawel Biernacki 	    &fw->dropped_overrun, 0, mxge_handle_be32, "I",
15477029da5cSPawel Biernacki 	    "dropped_overrun");
1548adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15497029da5cSPawel Biernacki 	    "dropped_pause", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15507029da5cSPawel Biernacki 	    &fw->dropped_pause, 0, mxge_handle_be32, "I", "dropped_pause");
1551adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15527029da5cSPawel Biernacki 	    "dropped_runt", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15537029da5cSPawel Biernacki 	    &fw->dropped_runt, 0, mxge_handle_be32, "I", "dropped_runt");
1554b2fc195eSAndrew Gallatin 
1555a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1556a0394e33SAndrew Gallatin 	    "dropped_unicast_filtered",
15577029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15587029da5cSPawel Biernacki 	    &fw->dropped_unicast_filtered, 0, mxge_handle_be32, "I",
15597029da5cSPawel Biernacki 	    "dropped_unicast_filtered");
1560a0394e33SAndrew Gallatin 
15615e7d8541SAndrew Gallatin 	/* verbose printing? */
1562b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15635e7d8541SAndrew Gallatin 		       "verbose",
15645e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15655e7d8541SAndrew Gallatin 		       0, "verbose printing");
1566b2fc195eSAndrew Gallatin 
15671e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
15681e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15691e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
15701e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15717029da5cSPawel Biernacki 		    "slice", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15721e413cf9SAndrew Gallatin 
15731e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
15741e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
15751e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
15761e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
15771e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
15781e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
15791e413cf9SAndrew Gallatin 		ss->sysctl_tree =
15801e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
15817029da5cSPawel Biernacki 			    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15821e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1583053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15841e413cf9SAndrew Gallatin 			       "rx_small_cnt",
15851e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
15861e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
15871e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15881e413cf9SAndrew Gallatin 			       "rx_big_cnt",
15891e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
15901e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
1591e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
159226dd49c6SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lc.lro_flushed,
1593053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1594053e637fSAndrew Gallatin 
1595e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
159626dd49c6SAndrew Gallatin 			       "lro_bad_csum", CTLFLAG_RD, &ss->lc.lro_bad_csum,
159726dd49c6SAndrew Gallatin 			       0, "number of bad csums preventing LRO");
159826dd49c6SAndrew Gallatin 
1599e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
160026dd49c6SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lc.lro_queued,
16011e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16021e413cf9SAndrew Gallatin 			       "queues");
1603053e637fSAndrew Gallatin 
1604c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1605c6cb3e3fSAndrew Gallatin 			       "tx_req",
1606c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1607c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16081e413cf9SAndrew Gallatin 
16091e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16101e413cf9SAndrew Gallatin 			       "tx_done",
16111e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16121e413cf9SAndrew Gallatin 			       0, "tx_done");
16131e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16141e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16151e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16161e413cf9SAndrew Gallatin 			       0, "tx_done");
16171e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16181e413cf9SAndrew Gallatin 			       "tx_stall",
16191e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16201e413cf9SAndrew Gallatin 			       0, "tx_stall");
16211e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16221e413cf9SAndrew Gallatin 			       "tx_wake",
16231e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16241e413cf9SAndrew Gallatin 			       0, "tx_wake");
16251e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16261e413cf9SAndrew Gallatin 			       "tx_defrag",
16271e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16281e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1629c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1630c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1631c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1632c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1633c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1634c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1635c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1636c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1637c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1638c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1639c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1640c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
16411e413cf9SAndrew Gallatin 	}
1642b2fc195eSAndrew Gallatin }
1643b2fc195eSAndrew Gallatin 
1644b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1645b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1646b2fc195eSAndrew Gallatin 
1647b2fc195eSAndrew Gallatin static inline void
16481e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1649b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1650b2fc195eSAndrew Gallatin {
1651b2fc195eSAndrew Gallatin 	int idx, starting_slot;
1652b2fc195eSAndrew Gallatin 	starting_slot = tx->req;
1653b2fc195eSAndrew Gallatin 	while (cnt > 1) {
1654b2fc195eSAndrew Gallatin 		cnt--;
1655b2fc195eSAndrew Gallatin 		idx = (starting_slot + cnt) & tx->mask;
16566d87a65dSAndrew Gallatin 		mxge_pio_copy(&tx->lanai[idx],
1657b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
165873c7c83fSAndrew Gallatin 		wmb();
1659b2fc195eSAndrew Gallatin 	}
1660b2fc195eSAndrew Gallatin }
1661b2fc195eSAndrew Gallatin 
1662b2fc195eSAndrew Gallatin /*
1663b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1664b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1665b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1666b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1667b2fc195eSAndrew Gallatin  */
1668b2fc195eSAndrew Gallatin 
1669b2fc195eSAndrew Gallatin static inline void
16701e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1671b2fc195eSAndrew Gallatin 		  int cnt)
1672b2fc195eSAndrew Gallatin {
1673b2fc195eSAndrew Gallatin 	int idx, i;
1674b2fc195eSAndrew Gallatin 	uint32_t *src_ints;
1675b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1676b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *srcp;
1677b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
16785e7d8541SAndrew Gallatin 	uint8_t last_flags;
1679b2fc195eSAndrew Gallatin 
1680b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1681b2fc195eSAndrew Gallatin 
16825e7d8541SAndrew Gallatin 	last_flags = src->flags;
16835e7d8541SAndrew Gallatin 	src->flags = 0;
168473c7c83fSAndrew Gallatin 	wmb();
1685b2fc195eSAndrew Gallatin 	dst = dstp = &tx->lanai[idx];
1686b2fc195eSAndrew Gallatin 	srcp = src;
1687b2fc195eSAndrew Gallatin 
1688b2fc195eSAndrew Gallatin 	if ((idx + cnt) < tx->mask) {
1689b2fc195eSAndrew Gallatin 		for (i = 0; i < (cnt - 1); i += 2) {
16906d87a65dSAndrew Gallatin 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
169173c7c83fSAndrew Gallatin 			wmb(); /* force write every 32 bytes */
1692b2fc195eSAndrew Gallatin 			srcp += 2;
1693b2fc195eSAndrew Gallatin 			dstp += 2;
1694b2fc195eSAndrew Gallatin 		}
1695b2fc195eSAndrew Gallatin 	} else {
1696b2fc195eSAndrew Gallatin 		/* submit all but the first request, and ensure
1697b2fc195eSAndrew Gallatin 		   that it is submitted below */
16986d87a65dSAndrew Gallatin 		mxge_submit_req_backwards(tx, src, cnt);
1699b2fc195eSAndrew Gallatin 		i = 0;
1700b2fc195eSAndrew Gallatin 	}
1701b2fc195eSAndrew Gallatin 	if (i < cnt) {
1702b2fc195eSAndrew Gallatin 		/* submit the first request */
17036d87a65dSAndrew Gallatin 		mxge_pio_copy(dstp, srcp, sizeof(*src));
170473c7c83fSAndrew Gallatin 		wmb(); /* barrier before setting valid flag */
1705b2fc195eSAndrew Gallatin 	}
1706b2fc195eSAndrew Gallatin 
1707b2fc195eSAndrew Gallatin 	/* re-write the last 32-bits with the valid flags */
17085e7d8541SAndrew Gallatin 	src->flags = last_flags;
1709b2fc195eSAndrew Gallatin 	src_ints = (uint32_t *)src;
1710b2fc195eSAndrew Gallatin 	src_ints+=3;
1711b2fc195eSAndrew Gallatin 	dst_ints = (volatile uint32_t *)dst;
1712b2fc195eSAndrew Gallatin 	dst_ints+=3;
1713b2fc195eSAndrew Gallatin 	*dst_ints =  *src_ints;
1714b2fc195eSAndrew Gallatin 	tx->req += cnt;
171573c7c83fSAndrew Gallatin 	wmb();
1716b2fc195eSAndrew Gallatin }
1717b2fc195eSAndrew Gallatin 
17180a7a780eSAndrew Gallatin static int
17190a7a780eSAndrew Gallatin mxge_parse_tx(struct mxge_slice_state *ss, struct mbuf *m,
17200a7a780eSAndrew Gallatin     struct mxge_pkt_info *pi)
17210a7a780eSAndrew Gallatin {
17220a7a780eSAndrew Gallatin 	struct ether_vlan_header *eh;
17230a7a780eSAndrew Gallatin 	uint16_t etype;
17240a7a780eSAndrew Gallatin 	int tso = m->m_pkthdr.csum_flags & (CSUM_TSO);
17250a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17260a7a780eSAndrew Gallatin 	int nxt;
17270a7a780eSAndrew Gallatin #endif
17280a7a780eSAndrew Gallatin 
17290a7a780eSAndrew Gallatin 	eh = mtod(m, struct ether_vlan_header *);
17300a7a780eSAndrew Gallatin 	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
17310a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_proto);
17320a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
17330a7a780eSAndrew Gallatin 	} else {
17340a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_encap_proto);
17350a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN;
17360a7a780eSAndrew Gallatin 	}
17370a7a780eSAndrew Gallatin 
17380a7a780eSAndrew Gallatin 	switch (etype) {
17390a7a780eSAndrew Gallatin 	case ETHERTYPE_IP:
17400a7a780eSAndrew Gallatin 		/*
17410a7a780eSAndrew Gallatin 		 * ensure ip header is in first mbuf, copy it to a
17420a7a780eSAndrew Gallatin 		 * scratch buffer if not
17430a7a780eSAndrew Gallatin 		 */
17440a7a780eSAndrew Gallatin 		pi->ip = (struct ip *)(m->m_data + pi->ip_off);
17450a7a780eSAndrew Gallatin 		pi->ip6 = NULL;
17460a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip))) {
17470a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip),
17480a7a780eSAndrew Gallatin 			    ss->scratch);
17490a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17500a7a780eSAndrew Gallatin 		}
17510a7a780eSAndrew Gallatin 		pi->ip_hlen = pi->ip->ip_hl << 2;
17520a7a780eSAndrew Gallatin 		if (!tso)
17530a7a780eSAndrew Gallatin 			return 0;
17540a7a780eSAndrew Gallatin 
17550a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17560a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17570a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17580a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17590a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17600a7a780eSAndrew Gallatin 		}
17610a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip + pi->ip_hlen);
17620a7a780eSAndrew Gallatin 		break;
17630a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17640a7a780eSAndrew Gallatin 	case ETHERTYPE_IPV6:
17650a7a780eSAndrew Gallatin 		pi->ip6 = (struct ip6_hdr *)(m->m_data + pi->ip_off);
17660a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip6))) {
17670a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip6),
17680a7a780eSAndrew Gallatin 			    ss->scratch);
17690a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17700a7a780eSAndrew Gallatin 		}
17710a7a780eSAndrew Gallatin 		nxt = 0;
17720a7a780eSAndrew Gallatin 		pi->ip_hlen = ip6_lasthdr(m, pi->ip_off, IPPROTO_IPV6, &nxt);
17730a7a780eSAndrew Gallatin 		pi->ip_hlen -= pi->ip_off;
17740a7a780eSAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
17750a7a780eSAndrew Gallatin 			return EINVAL;
17760a7a780eSAndrew Gallatin 
17770a7a780eSAndrew Gallatin 		if (!tso)
17780a7a780eSAndrew Gallatin 			return 0;
17790a7a780eSAndrew Gallatin 
17800a7a780eSAndrew Gallatin 		if (pi->ip_off + pi->ip_hlen > ss->sc->max_tso6_hlen)
17810a7a780eSAndrew Gallatin 			return EINVAL;
17820a7a780eSAndrew Gallatin 
17830a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17840a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17850a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17860a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17870a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17880a7a780eSAndrew Gallatin 		}
17890a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip6 + pi->ip_hlen);
17900a7a780eSAndrew Gallatin 		break;
17910a7a780eSAndrew Gallatin #endif
17920a7a780eSAndrew Gallatin 	default:
17930a7a780eSAndrew Gallatin 		return EINVAL;
17940a7a780eSAndrew Gallatin 	}
17950a7a780eSAndrew Gallatin 	return 0;
17960a7a780eSAndrew Gallatin }
17970a7a780eSAndrew Gallatin 
179837d89b0cSAndrew Gallatin #if IFCAP_TSO4
179937d89b0cSAndrew Gallatin 
1800b2fc195eSAndrew Gallatin static void
18011e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
18020a7a780eSAndrew Gallatin 	       int busdma_seg_cnt, struct mxge_pkt_info *pi)
1803aed8e389SAndrew Gallatin {
18041e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1805aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1806aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1807aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1808aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1809aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
18100a7a780eSAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss, sum;
1811aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1812aed8e389SAndrew Gallatin 	static int once;
1813aed8e389SAndrew Gallatin 
1814aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1815aed8e389SAndrew Gallatin 
1816aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1817aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1818aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1819aed8e389SAndrew Gallatin 	 */
1820aed8e389SAndrew Gallatin 
18210a7a780eSAndrew Gallatin 	cksum_offset = pi->ip_off + pi->ip_hlen;
18220a7a780eSAndrew Gallatin 	cum_len = -(cksum_offset + (pi->tcp->th_off << 2));
1823aed8e389SAndrew Gallatin 
1824aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
18250a7a780eSAndrew Gallatin 	if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) == 0)) {
18264ed8ca8fSAndrew Gallatin 		/*
18274ed8ca8fSAndrew Gallatin 		 * If packet has full TCP csum, replace it with pseudo hdr
18284ed8ca8fSAndrew Gallatin 		 * sum that the NIC expects, otherwise the NIC will emit
18294ed8ca8fSAndrew Gallatin 		 * packets with bad TCP checksums.
18304ed8ca8fSAndrew Gallatin 		 */
18314ed8ca8fSAndrew Gallatin 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
18320a7a780eSAndrew Gallatin 		if (pi->ip6) {
18330a7a780eSAndrew Gallatin #if (CSUM_TCP_IPV6 != 0) && defined(INET6)
18340a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP_IPV6;
18350a7a780eSAndrew Gallatin 			sum = in6_cksum_pseudo(pi->ip6,
18360a7a780eSAndrew Gallatin 			    m->m_pkthdr.len - cksum_offset,
18370a7a780eSAndrew Gallatin 			    IPPROTO_TCP, 0);
18380a7a780eSAndrew Gallatin #endif
18390a7a780eSAndrew Gallatin 		} else {
1840abc5b96bSAndrew Gallatin #ifdef INET
18410a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP;
18420a7a780eSAndrew Gallatin 			sum = in_pseudo(pi->ip->ip_src.s_addr,
18430a7a780eSAndrew Gallatin 			    pi->ip->ip_dst.s_addr,
18440a7a780eSAndrew Gallatin 			    htons(IPPROTO_TCP + (m->m_pkthdr.len -
18450a7a780eSAndrew Gallatin 				    cksum_offset)));
1846abc5b96bSAndrew Gallatin #endif
18470a7a780eSAndrew Gallatin 		}
18480a7a780eSAndrew Gallatin 		m_copyback(m, offsetof(struct tcphdr, th_sum) +
18490a7a780eSAndrew Gallatin 		    cksum_offset, sizeof(sum), (caddr_t)&sum);
18504ed8ca8fSAndrew Gallatin 	}
1851aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1852aed8e389SAndrew Gallatin 
1853aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1854aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1855aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1856aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1857aed8e389SAndrew Gallatin 
18580a7a780eSAndrew Gallatin 	if (pi->ip6) {
18590a7a780eSAndrew Gallatin 		/*
18600a7a780eSAndrew Gallatin 		 * for IPv6 TSO, the "checksum offset" is re-purposed
18610a7a780eSAndrew Gallatin 		 * to store the TCP header len
18620a7a780eSAndrew Gallatin 		 */
18630a7a780eSAndrew Gallatin 		cksum_offset = (pi->tcp->th_off << 2);
18640a7a780eSAndrew Gallatin 	}
18650a7a780eSAndrew Gallatin 
18661e413cf9SAndrew Gallatin 	tx = &ss->tx;
1867aed8e389SAndrew Gallatin 	req = tx->req_list;
1868aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1869aed8e389SAndrew Gallatin 	cnt = 0;
1870aed8e389SAndrew Gallatin 	rdma_count = 0;
1871aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1872aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1873aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1874aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1875aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1876aed8e389SAndrew Gallatin 	 *
1877aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1878aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1879aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1880aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1881aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1882aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1883aed8e389SAndrew Gallatin 	 *
1884aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1885aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1886aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1887aed8e389SAndrew Gallatin 	 */
1888aed8e389SAndrew Gallatin 
1889aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1890aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1891aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1892aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1893e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1894aed8e389SAndrew Gallatin 
1895aed8e389SAndrew Gallatin 		while (len) {
1896aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1897e39a0a37SAndrew Gallatin 			seglen = len;
1898aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1899aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1900aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1901aed8e389SAndrew Gallatin 				/* payload */
1902aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1903aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1904aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1905aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1906aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1907aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1908aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1909aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1910aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1911aed8e389SAndrew Gallatin 				/* header ends */
1912aed8e389SAndrew Gallatin 				rdma_count = -1;
1913aed8e389SAndrew Gallatin 				cum_len_next = 0;
1914aed8e389SAndrew Gallatin 				seglen = -cum_len;
1915aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1916aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1917aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1918aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1919aed8e389SAndrew Gallatin 			    }
1920aed8e389SAndrew Gallatin 
1921aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1922aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1923aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1924aed8e389SAndrew Gallatin 			req->pad = 0;
1925aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1926aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1927aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1928aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1929aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1930aed8e389SAndrew Gallatin 			low += seglen;
1931aed8e389SAndrew Gallatin 			len -= seglen;
1932aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1933aed8e389SAndrew Gallatin 			flags = flags_next;
1934aed8e389SAndrew Gallatin 			req++;
1935aed8e389SAndrew Gallatin 			cnt++;
1936aed8e389SAndrew Gallatin 			rdma_count++;
19370a7a780eSAndrew Gallatin 			if (cksum_offset != 0 && !pi->ip6) {
1938aed8e389SAndrew Gallatin 				if (__predict_false(cksum_offset > seglen))
1939aed8e389SAndrew Gallatin 					cksum_offset -= seglen;
1940aed8e389SAndrew Gallatin 				else
1941aed8e389SAndrew Gallatin 					cksum_offset = 0;
19420a7a780eSAndrew Gallatin 			}
1943adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1944aed8e389SAndrew Gallatin 				goto drop;
1945aed8e389SAndrew Gallatin 		}
1946aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1947aed8e389SAndrew Gallatin 		seg++;
1948aed8e389SAndrew Gallatin 	}
1949aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1950aed8e389SAndrew Gallatin 
1951aed8e389SAndrew Gallatin 	do {
1952aed8e389SAndrew Gallatin 		req--;
1953aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1954aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1955aed8e389SAndrew Gallatin 
1956aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1957aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
19586147584bSElliott Mitchell 
1959c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1960c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1961c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1962c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1963c6cb3e3fSAndrew Gallatin 		tx->activate++;
1964c6cb3e3fSAndrew Gallatin 		wmb();
1965c6cb3e3fSAndrew Gallatin 	}
19666147584bSElliott Mitchell 
1967aed8e389SAndrew Gallatin 	return;
1968aed8e389SAndrew Gallatin 
1969aed8e389SAndrew Gallatin drop:
1970e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1971aed8e389SAndrew Gallatin 	m_freem(m);
1972c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1973aed8e389SAndrew Gallatin 	if (!once) {
1974adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1975adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1976adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1977aed8e389SAndrew Gallatin 		once = 1;
1978aed8e389SAndrew Gallatin 	}
1979aed8e389SAndrew Gallatin 	return;
1980aed8e389SAndrew Gallatin 
1981aed8e389SAndrew Gallatin }
1982aed8e389SAndrew Gallatin 
198337d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
198437d89b0cSAndrew Gallatin 
198537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1986c792928fSAndrew Gallatin /*
1987c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1988c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1989c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1990c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1991c792928fSAndrew Gallatin  */
1992c792928fSAndrew Gallatin static struct mbuf *
1993c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1994c792928fSAndrew Gallatin {
1995c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1996c792928fSAndrew Gallatin 
1997c6499eccSGleb Smirnoff 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
1998c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1999c792928fSAndrew Gallatin 		return NULL;
2000c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
2001c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
2002c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2003c792928fSAndrew Gallatin 			return NULL;
2004c792928fSAndrew Gallatin 	}
2005c792928fSAndrew Gallatin 	/*
2006c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2007c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2008c792928fSAndrew Gallatin 	 */
2009c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2010c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2011c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2012c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2013c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2014c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2015c792928fSAndrew Gallatin 	return m;
2016c792928fSAndrew Gallatin }
201737d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2018c792928fSAndrew Gallatin 
2019aed8e389SAndrew Gallatin static void
20201e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2021b2fc195eSAndrew Gallatin {
20220a7a780eSAndrew Gallatin 	struct mxge_pkt_info pi = {0,0,0,0};
20231e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2024b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2025b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2026b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
20271e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
20280a7a780eSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
2029aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2030aed8e389SAndrew Gallatin 	uint8_t flags, cksum_offset;
2031b2fc195eSAndrew Gallatin 
20321e413cf9SAndrew Gallatin 	sc = ss->sc;
20331e413cf9SAndrew Gallatin 	tx = &ss->tx;
2034b2fc195eSAndrew Gallatin 
203537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2036c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2037c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2038c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
20390a7a780eSAndrew Gallatin 			goto drop_without_m;
2040c792928fSAndrew Gallatin 	}
204137d89b0cSAndrew Gallatin #endif
20420a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
20430a7a780eSAndrew Gallatin 	    (CSUM_TSO | CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
20440a7a780eSAndrew Gallatin 		if (mxge_parse_tx(ss, m, &pi))
20450a7a780eSAndrew Gallatin 			goto drop;
20460a7a780eSAndrew Gallatin 	}
20470a7a780eSAndrew Gallatin 
2048b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2049b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2050b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2051aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2052b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2053adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2054b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2055b2fc195eSAndrew Gallatin 		   to defrag */
2056b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2057b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2058b2fc195eSAndrew Gallatin 			goto drop;
2059b2fc195eSAndrew Gallatin 		}
20601e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2061b2fc195eSAndrew Gallatin 		m = m_tmp;
2062b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2063b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2064aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2065b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2066b2fc195eSAndrew Gallatin 	}
2067adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2068aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2069aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2070b2fc195eSAndrew Gallatin 		goto drop;
2071b2fc195eSAndrew Gallatin 	}
2072b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2073b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20745e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2075b2fc195eSAndrew Gallatin 
207637d89b0cSAndrew Gallatin #if IFCAP_TSO4
2077aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2078aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20790a7a780eSAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, &pi);
2080aed8e389SAndrew Gallatin 		return;
2081aed8e389SAndrew Gallatin 	}
208237d89b0cSAndrew Gallatin #endif
2083aed8e389SAndrew Gallatin 
2084b2fc195eSAndrew Gallatin 	req = tx->req_list;
2085b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20865e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20875e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2088b2fc195eSAndrew Gallatin 
2089b2fc195eSAndrew Gallatin 	/* checksum offloading? */
20900a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
20910a7a780eSAndrew Gallatin 	    (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
2092aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2093aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
20940a7a780eSAndrew Gallatin 		cksum_offset = pi.ip_off + pi.ip_hlen;
2095b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20965e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2097b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
20985e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2099aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2100aed8e389SAndrew Gallatin 	} else {
2101aed8e389SAndrew Gallatin 		odd_flag = 0;
2102b2fc195eSAndrew Gallatin 	}
21035e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21045e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2105b2fc195eSAndrew Gallatin 
2106b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2107b2fc195eSAndrew Gallatin 	cum_len = 0;
2108aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21095e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2110b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2111b2fc195eSAndrew Gallatin 		req->addr_low =
21126d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2113b2fc195eSAndrew Gallatin 		req->addr_high =
21146d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2115b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2116b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2117b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2118b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2119b2fc195eSAndrew Gallatin 		else
2120b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21215e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21225e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21235e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2124aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2125b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2126b2fc195eSAndrew Gallatin 		seg++;
2127b2fc195eSAndrew Gallatin 		req++;
2128b2fc195eSAndrew Gallatin 		req->flags = 0;
2129b2fc195eSAndrew Gallatin 	}
2130b2fc195eSAndrew Gallatin 	req--;
2131b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2132b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2133b2fc195eSAndrew Gallatin 		req++;
2134b2fc195eSAndrew Gallatin 		req->addr_low =
21356d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2136b2fc195eSAndrew Gallatin 		req->addr_high =
21376d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2138b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21395e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21405e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21415e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21425e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2143aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2144b2fc195eSAndrew Gallatin 		cnt++;
2145b2fc195eSAndrew Gallatin 	}
21465e7d8541SAndrew Gallatin 
21475e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
21485e7d8541SAndrew Gallatin #if 0
21495e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21505e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21515e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21525e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21535e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21545e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21555e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21565e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21575e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21585e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21595e7d8541SAndrew Gallatin 	}
21605e7d8541SAndrew Gallatin 	printf("--------------\n");
21615e7d8541SAndrew Gallatin #endif
21625e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21636d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
21646147584bSElliott Mitchell 
2165c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2166c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2167c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2168c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2169c6cb3e3fSAndrew Gallatin 		tx->activate++;
2170c6cb3e3fSAndrew Gallatin 		wmb();
2171c6cb3e3fSAndrew Gallatin 	}
21726147584bSElliott Mitchell 
2173b2fc195eSAndrew Gallatin 	return;
2174b2fc195eSAndrew Gallatin 
2175b2fc195eSAndrew Gallatin drop:
2176b2fc195eSAndrew Gallatin 	m_freem(m);
21770a7a780eSAndrew Gallatin drop_without_m:
2178c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2179b2fc195eSAndrew Gallatin 	return;
2180b2fc195eSAndrew Gallatin }
2181b2fc195eSAndrew Gallatin 
2182c6cb3e3fSAndrew Gallatin static void
2183*93037a67SJustin Hibbits mxge_qflush(if_t ifp)
2184c6cb3e3fSAndrew Gallatin {
2185*93037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
2186c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2187c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2188c6cb3e3fSAndrew Gallatin 	int slice;
2189b2fc195eSAndrew Gallatin 
2190c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2191c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2192c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2193c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2194c6cb3e3fSAndrew Gallatin 			m_freem(m);
2195c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2196c6cb3e3fSAndrew Gallatin 	}
2197c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2198c6cb3e3fSAndrew Gallatin }
21996d914a32SAndrew Gallatin 
2200c6cb3e3fSAndrew Gallatin static inline void
2201c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2202c6cb3e3fSAndrew Gallatin {
2203c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2204c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2205*93037a67SJustin Hibbits 	if_t ifp;
2206c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2207c6cb3e3fSAndrew Gallatin 
2208c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2209c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2210c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2211c6cb3e3fSAndrew Gallatin 
2212c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2213c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2214c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2215c6cb3e3fSAndrew Gallatin 			return;
2216c6cb3e3fSAndrew Gallatin 		}
2217c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2218c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2219c6cb3e3fSAndrew Gallatin 
2220c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2221c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2222c6cb3e3fSAndrew Gallatin 	}
2223c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2224c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2225c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2226c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2227c6cb3e3fSAndrew Gallatin 		tx->stall++;
2228c6cb3e3fSAndrew Gallatin 	}
2229c6cb3e3fSAndrew Gallatin }
2230c6cb3e3fSAndrew Gallatin 
2231c6cb3e3fSAndrew Gallatin static int
2232c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2233c6cb3e3fSAndrew Gallatin {
2234c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2235*93037a67SJustin Hibbits 	if_t ifp;
2236c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2237c6cb3e3fSAndrew Gallatin 	int err;
2238c6cb3e3fSAndrew Gallatin 
2239c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2240c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2241c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2242c6cb3e3fSAndrew Gallatin 
2243c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2244c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2245c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2246c6cb3e3fSAndrew Gallatin 		return (err);
2247c6cb3e3fSAndrew Gallatin 	}
2248c6cb3e3fSAndrew Gallatin 
2249193cbc4dSMax Laier 	if (!drbr_needs_enqueue(ifp, tx->br) &&
2250c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2251c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2252c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2253c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2254c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2255c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2256c6cb3e3fSAndrew Gallatin 		return (err);
2257c6cb3e3fSAndrew Gallatin 	}
2258c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2259c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2260c6cb3e3fSAndrew Gallatin 	return (0);
2261c6cb3e3fSAndrew Gallatin }
2262c6cb3e3fSAndrew Gallatin 
2263c6cb3e3fSAndrew Gallatin static int
2264*93037a67SJustin Hibbits mxge_transmit(if_t ifp, struct mbuf *m)
2265c6cb3e3fSAndrew Gallatin {
2266*93037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
2267c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2268c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2269c6cb3e3fSAndrew Gallatin 	int err = 0;
2270c6cb3e3fSAndrew Gallatin 	int slice;
2271c6cb3e3fSAndrew Gallatin 
2272c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2273c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2274c6cb3e3fSAndrew Gallatin 
2275c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2276c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2277c6cb3e3fSAndrew Gallatin 
2278c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2279c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2280c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2281c6cb3e3fSAndrew Gallatin 	} else {
2282c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2283c6cb3e3fSAndrew Gallatin 	}
2284c6cb3e3fSAndrew Gallatin 
2285c6cb3e3fSAndrew Gallatin 	return (err);
2286c6cb3e3fSAndrew Gallatin }
2287c6cb3e3fSAndrew Gallatin 
2288b2fc195eSAndrew Gallatin static void
2289*93037a67SJustin Hibbits mxge_start(if_t ifp)
2290b2fc195eSAndrew Gallatin {
2291*93037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
22921e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2293b2fc195eSAndrew Gallatin 
22941e413cf9SAndrew Gallatin 	/* only use the first slice for now */
22951e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
22961e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
22971e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
22981e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2299b2fc195eSAndrew Gallatin }
2300b2fc195eSAndrew Gallatin 
23015e7d8541SAndrew Gallatin /*
23025e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23035e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23045e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23055e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23065e7d8541SAndrew Gallatin  * in a burst
23075e7d8541SAndrew Gallatin  */
23085e7d8541SAndrew Gallatin static inline void
23095e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23105e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
23115e7d8541SAndrew Gallatin {
23125e7d8541SAndrew Gallatin 	uint32_t low;
23135e7d8541SAndrew Gallatin 
23145e7d8541SAndrew Gallatin 	low = src->addr_low;
23155e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2316a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
231773c7c83fSAndrew Gallatin 	wmb();
2318a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
231973c7c83fSAndrew Gallatin 	wmb();
232040385a5fSAndrew Gallatin 	src->addr_low = low;
23215e7d8541SAndrew Gallatin 	dst->addr_low = low;
232273c7c83fSAndrew Gallatin 	wmb();
23235e7d8541SAndrew Gallatin }
23245e7d8541SAndrew Gallatin 
2325b2fc195eSAndrew Gallatin static int
23261e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2327b2fc195eSAndrew Gallatin {
2328b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2329b2fc195eSAndrew Gallatin 	struct mbuf *m;
23301e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2331b2fc195eSAndrew Gallatin 	int cnt, err;
2332b2fc195eSAndrew Gallatin 
2333c6499eccSGleb Smirnoff 	m = m_gethdr(M_NOWAIT, MT_DATA);
2334b2fc195eSAndrew Gallatin 	if (m == NULL) {
2335b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2336b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2337b2fc195eSAndrew Gallatin 		goto done;
2338b2fc195eSAndrew Gallatin 	}
2339b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2340b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2341b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2342b2fc195eSAndrew Gallatin 	if (err != 0) {
2343b2fc195eSAndrew Gallatin 		m_free(m);
2344b2fc195eSAndrew Gallatin 		goto done;
2345b2fc195eSAndrew Gallatin 	}
2346b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2347b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23486d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2349b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23506d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2351b2fc195eSAndrew Gallatin 
2352b2fc195eSAndrew Gallatin done:
2353adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2354adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2355b2fc195eSAndrew Gallatin 	return err;
2356b2fc195eSAndrew Gallatin }
2357b2fc195eSAndrew Gallatin 
2358b2fc195eSAndrew Gallatin static int
23591e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2360b2fc195eSAndrew Gallatin {
2361053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2362b2fc195eSAndrew Gallatin 	struct mbuf *m;
23631e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2364053e637fSAndrew Gallatin 	int cnt, err, i;
2365b2fc195eSAndrew Gallatin 
2366c6499eccSGleb Smirnoff 	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2367b2fc195eSAndrew Gallatin 	if (m == NULL) {
2368b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2369b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2370b2fc195eSAndrew Gallatin 		goto done;
2371b2fc195eSAndrew Gallatin 	}
23724d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2373b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2374053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2375b2fc195eSAndrew Gallatin 	if (err != 0) {
2376b2fc195eSAndrew Gallatin 		m_free(m);
2377b2fc195eSAndrew Gallatin 		goto done;
2378b2fc195eSAndrew Gallatin 	}
2379b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2380b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2381b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2382b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2383b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2384053e637fSAndrew Gallatin 
2385b2fc195eSAndrew Gallatin done:
2386053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2387b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
23885e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
23895e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2390b2fc195eSAndrew Gallatin 		}
2391053e637fSAndrew Gallatin 		idx++;
2392053e637fSAndrew Gallatin 	}
2393b2fc195eSAndrew Gallatin 	return err;
2394b2fc195eSAndrew Gallatin }
2395b2fc195eSAndrew Gallatin 
239626dd49c6SAndrew Gallatin #ifdef INET6
239726dd49c6SAndrew Gallatin 
239826dd49c6SAndrew Gallatin static uint16_t
239926dd49c6SAndrew Gallatin mxge_csum_generic(uint16_t *raw, int len)
240026dd49c6SAndrew Gallatin {
240126dd49c6SAndrew Gallatin 	uint32_t csum;
240226dd49c6SAndrew Gallatin 
240326dd49c6SAndrew Gallatin 	csum = 0;
240426dd49c6SAndrew Gallatin 	while (len > 0) {
240526dd49c6SAndrew Gallatin 		csum += *raw;
240626dd49c6SAndrew Gallatin 		raw++;
240726dd49c6SAndrew Gallatin 		len -= 2;
240826dd49c6SAndrew Gallatin 	}
240926dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
241026dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
241126dd49c6SAndrew Gallatin 	return (uint16_t)csum;
241226dd49c6SAndrew Gallatin }
241326dd49c6SAndrew Gallatin 
241426dd49c6SAndrew Gallatin static inline uint16_t
241526dd49c6SAndrew Gallatin mxge_rx_csum6(void *p, struct mbuf *m, uint32_t csum)
241626dd49c6SAndrew Gallatin {
241726dd49c6SAndrew Gallatin 	uint32_t partial;
241826dd49c6SAndrew Gallatin 	int nxt, cksum_offset;
241926dd49c6SAndrew Gallatin 	struct ip6_hdr *ip6 = p;
242026dd49c6SAndrew Gallatin 	uint16_t c;
242126dd49c6SAndrew Gallatin 
242226dd49c6SAndrew Gallatin 	nxt = ip6->ip6_nxt;
242326dd49c6SAndrew Gallatin 	cksum_offset = sizeof (*ip6) + ETHER_HDR_LEN;
242426dd49c6SAndrew Gallatin 	if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) {
242526dd49c6SAndrew Gallatin 		cksum_offset = ip6_lasthdr(m, ETHER_HDR_LEN,
242626dd49c6SAndrew Gallatin 					   IPPROTO_IPV6, &nxt);
242726dd49c6SAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
242826dd49c6SAndrew Gallatin 			return (1);
242926dd49c6SAndrew Gallatin 	}
243026dd49c6SAndrew Gallatin 
243126dd49c6SAndrew Gallatin 	/*
243226dd49c6SAndrew Gallatin 	 * IPv6 headers do not contain a checksum, and hence
243326dd49c6SAndrew Gallatin 	 * do not checksum to zero, so they don't "fall out"
243426dd49c6SAndrew Gallatin 	 * of the partial checksum calculation like IPv4
243526dd49c6SAndrew Gallatin 	 * headers do.  We need to fix the partial checksum by
243626dd49c6SAndrew Gallatin 	 * subtracting the checksum of the IPv6 header.
243726dd49c6SAndrew Gallatin 	 */
243826dd49c6SAndrew Gallatin 
243926dd49c6SAndrew Gallatin 	partial = mxge_csum_generic((uint16_t *)ip6, cksum_offset -
244026dd49c6SAndrew Gallatin 				    ETHER_HDR_LEN);
244126dd49c6SAndrew Gallatin 	csum += ~partial;
244226dd49c6SAndrew Gallatin 	csum +=	 (csum < ~partial);
244326dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
244426dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
244526dd49c6SAndrew Gallatin 	c = in6_cksum_pseudo(ip6, m->m_pkthdr.len - cksum_offset, nxt,
244626dd49c6SAndrew Gallatin 			     csum);
244726dd49c6SAndrew Gallatin 	c ^= 0xffff;
244826dd49c6SAndrew Gallatin 	return (c);
244926dd49c6SAndrew Gallatin }
245026dd49c6SAndrew Gallatin #endif /* INET6 */
24519b03b0f3SAndrew Gallatin /*
24529b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
24539b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
24549b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
24559b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2456053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2457053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
24589b03b0f3SAndrew Gallatin  */
24599b03b0f3SAndrew Gallatin 
2460053e637fSAndrew Gallatin static inline uint16_t
2461053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2462053e637fSAndrew Gallatin {
2463053e637fSAndrew Gallatin 	struct ether_header *eh;
246426dd49c6SAndrew Gallatin #ifdef INET
2465053e637fSAndrew Gallatin 	struct ip *ip;
246626dd49c6SAndrew Gallatin #endif
2467abc5b96bSAndrew Gallatin #if defined(INET) || defined(INET6)
2468*93037a67SJustin Hibbits 	int cap = if_getcapenable(m->m_pkthdr.rcvif);
2469abc5b96bSAndrew Gallatin #endif
247026dd49c6SAndrew Gallatin 	uint16_t c, etype;
247126dd49c6SAndrew Gallatin 
2472053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
247326dd49c6SAndrew Gallatin 	etype = ntohs(eh->ether_type);
247426dd49c6SAndrew Gallatin 	switch (etype) {
2475eb6219e3SAndrew Gallatin #ifdef INET
247626dd49c6SAndrew Gallatin 	case ETHERTYPE_IP:
247726dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM) == 0)
247826dd49c6SAndrew Gallatin 			return (1);
247926dd49c6SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
248026dd49c6SAndrew Gallatin 		if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP)
248126dd49c6SAndrew Gallatin 			return (1);
2482053e637fSAndrew Gallatin 		c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
248326dd49c6SAndrew Gallatin 			      htonl(ntohs(csum) + ntohs(ip->ip_len) -
248426dd49c6SAndrew Gallatin 				    (ip->ip_hl << 2) + ip->ip_p));
2485053e637fSAndrew Gallatin 		c ^= 0xffff;
248626dd49c6SAndrew Gallatin 		break;
248726dd49c6SAndrew Gallatin #endif
248826dd49c6SAndrew Gallatin #ifdef INET6
248926dd49c6SAndrew Gallatin 	case ETHERTYPE_IPV6:
249026dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM_IPV6) == 0)
249126dd49c6SAndrew Gallatin 			return (1);
249226dd49c6SAndrew Gallatin 		c = mxge_rx_csum6((eh + 1), m, csum);
249326dd49c6SAndrew Gallatin 		break;
249426dd49c6SAndrew Gallatin #endif
249526dd49c6SAndrew Gallatin 	default:
249626dd49c6SAndrew Gallatin 		c = 1;
249726dd49c6SAndrew Gallatin 	}
2498053e637fSAndrew Gallatin 	return (c);
24995e7d8541SAndrew Gallatin }
2500053e637fSAndrew Gallatin 
2501c792928fSAndrew Gallatin static void
2502c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2503c792928fSAndrew Gallatin {
2504c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2505c792928fSAndrew Gallatin 	uint32_t partial;
2506c792928fSAndrew Gallatin 
2507c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2508c792928fSAndrew Gallatin 
2509c792928fSAndrew Gallatin 	/*
2510c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2511c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2512c792928fSAndrew Gallatin 	 * header.
2513c792928fSAndrew Gallatin 	 */
2514c792928fSAndrew Gallatin 
2515c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2516c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2517c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2518c792928fSAndrew Gallatin 	(*csum) += ~partial;
2519c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2520c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2521c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2522c792928fSAndrew Gallatin 
2523c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2524c792928fSAndrew Gallatin 	   later consumers expect this */
2525c792928fSAndrew Gallatin 	*csum = htons(*csum);
2526c792928fSAndrew Gallatin 
2527c792928fSAndrew Gallatin 	/* save the tag */
252837d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2529c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
253037d89b0cSAndrew Gallatin #else
253137d89b0cSAndrew Gallatin 	{
253237d89b0cSAndrew Gallatin 		struct m_tag *mtag;
253337d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
253437d89b0cSAndrew Gallatin 				   M_NOWAIT);
253537d89b0cSAndrew Gallatin 		if (mtag == NULL)
253637d89b0cSAndrew Gallatin 			return;
253737d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
253837d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
253937d89b0cSAndrew Gallatin 	}
254037d89b0cSAndrew Gallatin 
254137d89b0cSAndrew Gallatin #endif
254237d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2543c792928fSAndrew Gallatin 
2544c792928fSAndrew Gallatin 	/*
2545c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2546c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2547c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2548c792928fSAndrew Gallatin 	 * type field is already in place.
2549c792928fSAndrew Gallatin 	 */
2550c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2551c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2552c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2553c792928fSAndrew Gallatin }
2554c792928fSAndrew Gallatin 
25555e7d8541SAndrew Gallatin static inline void
255626dd49c6SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len,
255726dd49c6SAndrew Gallatin 		 uint32_t csum, int lro)
2558b2fc195eSAndrew Gallatin {
25591e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2560*93037a67SJustin Hibbits 	if_t ifp;
2561053e637fSAndrew Gallatin 	struct mbuf *m;
2562c792928fSAndrew Gallatin 	struct ether_header *eh;
25631e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2564053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2565b2fc195eSAndrew Gallatin 	int idx;
2566b2fc195eSAndrew Gallatin 
25671e413cf9SAndrew Gallatin 	sc = ss->sc;
2568b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
25691e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2570b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2571053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2572b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2573b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2574b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25751e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2576053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2577f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2578053e637fSAndrew Gallatin 		return;
2579b2fc195eSAndrew Gallatin 	}
2580053e637fSAndrew Gallatin 
2581b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2582b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2583b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2584b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2585b2fc195eSAndrew Gallatin 
2586b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2587b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2588b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2589b2fc195eSAndrew Gallatin 
2590053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2591053e637fSAndrew Gallatin 	 * aligned */
25925e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2593b2fc195eSAndrew Gallatin 
2594053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2595053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25961e413cf9SAndrew Gallatin 	ss->ipackets++;
2597c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2598c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2599c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2600c792928fSAndrew Gallatin 	}
2601eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2602eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2603eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2604eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2605eacb70baSSepherosa Ziehau 	}
2606b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2607*93037a67SJustin Hibbits 	if ((if_getcapenable(ifp) & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
260826dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
260926dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2610053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
261126dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
261226dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
261326dd49c6SAndrew Gallatin 
261426dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
261526dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, 0)))
261626dd49c6SAndrew Gallatin 			return;
261726dd49c6SAndrew Gallatin #endif
2618b2fc195eSAndrew Gallatin 	}
2619053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2620*93037a67SJustin Hibbits 	if_input(ifp, m);
2621b2fc195eSAndrew Gallatin }
2622b2fc195eSAndrew Gallatin 
2623b2fc195eSAndrew Gallatin static inline void
262426dd49c6SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len,
262526dd49c6SAndrew Gallatin 		   uint32_t csum, int lro)
2626b2fc195eSAndrew Gallatin {
26271e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2628*93037a67SJustin Hibbits 	if_t ifp;
2629c792928fSAndrew Gallatin 	struct ether_header *eh;
2630b2fc195eSAndrew Gallatin 	struct mbuf *m;
26311e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2632b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2633b2fc195eSAndrew Gallatin 	int idx;
2634b2fc195eSAndrew Gallatin 
26351e413cf9SAndrew Gallatin 	sc = ss->sc;
2636b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26371e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2638b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2639b2fc195eSAndrew Gallatin 	rx->cnt++;
2640b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2641b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2642b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26431e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2644b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2645f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2646b2fc195eSAndrew Gallatin 		return;
2647b2fc195eSAndrew Gallatin 	}
2648b2fc195eSAndrew Gallatin 
2649b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2650b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2651b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2652b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2653b2fc195eSAndrew Gallatin 
2654b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2655b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2656b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2657b2fc195eSAndrew Gallatin 
2658b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2659b2fc195eSAndrew Gallatin 	 * aligned */
26605e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2661b2fc195eSAndrew Gallatin 
26629b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
26639b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
26641e413cf9SAndrew Gallatin 	ss->ipackets++;
2665c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2666c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2667c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2668c792928fSAndrew Gallatin 	}
2669eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2670eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2671eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2672eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2673eacb70baSSepherosa Ziehau 	}
2674b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2675*93037a67SJustin Hibbits 	if ((if_getcapenable(ifp) & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
267626dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
267726dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2678053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
267926dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
268026dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
268126dd49c6SAndrew Gallatin 
268226dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
268326dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, csum)))
268426dd49c6SAndrew Gallatin 			return;
268526dd49c6SAndrew Gallatin #endif
2686053e637fSAndrew Gallatin 	}
2687b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2688*93037a67SJustin Hibbits 	if_input(ifp, m);
2689b2fc195eSAndrew Gallatin }
2690b2fc195eSAndrew Gallatin 
2691b2fc195eSAndrew Gallatin static inline void
26921e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
26935e7d8541SAndrew Gallatin {
26941e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26955e7d8541SAndrew Gallatin 	int limit = 0;
26965e7d8541SAndrew Gallatin 	uint16_t length;
26975e7d8541SAndrew Gallatin 	uint16_t checksum;
269826dd49c6SAndrew Gallatin 	int lro;
26995e7d8541SAndrew Gallatin 
2700*93037a67SJustin Hibbits 	lro = if_getcapenable(ss->sc->ifp) & IFCAP_LRO;
27015e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
27025e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
27035e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2704053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2705b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
270626dd49c6SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum, lro);
27075e7d8541SAndrew Gallatin 		else
270826dd49c6SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum, lro);
27095e7d8541SAndrew Gallatin 		rx_done->cnt++;
2710adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
27115e7d8541SAndrew Gallatin 
27125e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2713f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
27145e7d8541SAndrew Gallatin 			break;
2715053e637fSAndrew Gallatin 	}
271626dd49c6SAndrew Gallatin #if defined(INET)  || defined (INET6)
27176dd38b87SSepherosa Ziehau 	tcp_lro_flush_all(&ss->lc);
2718eb6219e3SAndrew Gallatin #endif
27195e7d8541SAndrew Gallatin }
27205e7d8541SAndrew Gallatin 
27215e7d8541SAndrew Gallatin static inline void
27221e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2723b2fc195eSAndrew Gallatin {
2724*93037a67SJustin Hibbits 	if_t ifp __unused;
27251e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2726b2fc195eSAndrew Gallatin 	struct mbuf *m;
2727b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2728f616ebc7SAndrew Gallatin 	int idx;
2729c6cb3e3fSAndrew Gallatin 	int *flags;
2730b2fc195eSAndrew Gallatin 
27311e413cf9SAndrew Gallatin 	tx = &ss->tx;
27321e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
27335e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2734b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2735b2fc195eSAndrew Gallatin 		tx->done++;
2736b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2737b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2738b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2739b2fc195eSAndrew Gallatin 		if (m != NULL) {
274071032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
274171032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
274271032832SAndrew Gallatin 				ss->omcasts++;
2743c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2744b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2745b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2746b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2747b2fc195eSAndrew Gallatin 			m_freem(m);
2748b2fc195eSAndrew Gallatin 		}
27495e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
27505e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
27515e7d8541SAndrew Gallatin 			tx->pkt_done++;
27525e7d8541SAndrew Gallatin 		}
2753b2fc195eSAndrew Gallatin 	}
2754b2fc195eSAndrew Gallatin 
2755b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2756b2fc195eSAndrew Gallatin 	   its OK to send packets */
2757c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
27581e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2759c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2760c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2761c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
27621e413cf9SAndrew Gallatin 		ss->tx.wake++;
27631e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2764b2fc195eSAndrew Gallatin 	}
2765c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2766c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2767c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2768c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2769c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2770c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2771c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2772c6cb3e3fSAndrew Gallatin 			wmb();
2773c6cb3e3fSAndrew Gallatin 		}
2774c6cb3e3fSAndrew Gallatin 	}
2775c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2776b2fc195eSAndrew Gallatin }
2777b2fc195eSAndrew Gallatin 
277801638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2779c587e59fSAndrew Gallatin {
2780c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2781c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2782c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2783c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
278401638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2785c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2786c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2787c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2788c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2789c587e59fSAndrew Gallatin };
279001638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
279101638550SAndrew Gallatin {
279251bc2092SAndrew Gallatin 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
27934ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
279401638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
279501638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
279656b67858SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
279756b67858SAndrew Gallatin 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
279801638550SAndrew Gallatin };
2799c587e59fSAndrew Gallatin 
2800c587e59fSAndrew Gallatin static void
2801c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type)
2802c587e59fSAndrew Gallatin {
2803c406ad2eSAndrew Gallatin 
2804c406ad2eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type,
2805c406ad2eSAndrew Gallatin 		    0, NULL);
2806c406ad2eSAndrew Gallatin 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
2807c406ad2eSAndrew Gallatin 	sc->current_media = media_type;
2808c406ad2eSAndrew Gallatin 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
2809c587e59fSAndrew Gallatin }
2810c587e59fSAndrew Gallatin 
2811c587e59fSAndrew Gallatin static void
2812c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc)
2813c587e59fSAndrew Gallatin {
2814c587e59fSAndrew Gallatin 	char *ptr;
2815c406ad2eSAndrew Gallatin 	int i;
2816c587e59fSAndrew Gallatin 
2817c406ad2eSAndrew Gallatin 	ifmedia_removeall(&sc->media);
2818c406ad2eSAndrew Gallatin 	mxge_media_set(sc, IFM_AUTO);
2819c587e59fSAndrew Gallatin 
2820c587e59fSAndrew Gallatin 	/*
2821c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2822c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2823c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2824c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2825c587e59fSAndrew Gallatin 	 */
2826c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2827c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2828c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2829c406ad2eSAndrew Gallatin 		return;
2830c587e59fSAndrew Gallatin 	}
2831c587e59fSAndrew Gallatin 
2832c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
2833dc15eac0SEd Schouten 		ptr = strchr(ptr, '-');
2834c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2835c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2836c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2837c587e59fSAndrew Gallatin 			return;
2838c587e59fSAndrew Gallatin 		}
2839c587e59fSAndrew Gallatin 	}
284030882b10SAndrew Gallatin 	if (*ptr == 'C' || *(ptr +1) == 'C') {
284101638550SAndrew Gallatin 		/* -C is CX4 */
2842c406ad2eSAndrew Gallatin 		sc->connector = MXGE_CX4;
2843c406ad2eSAndrew Gallatin 		mxge_media_set(sc, IFM_10G_CX4);
2844c406ad2eSAndrew Gallatin 	} else if (*ptr == 'Q') {
284501638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2846c406ad2eSAndrew Gallatin 		sc->connector = MXGE_QRF;
2847c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2848c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2849c406ad2eSAndrew Gallatin 	} else if (*ptr == 'R') {
2850c406ad2eSAndrew Gallatin 		/* -R is XFP */
2851c406ad2eSAndrew Gallatin 		sc->connector = MXGE_XFP;
2852c406ad2eSAndrew Gallatin 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
2853c406ad2eSAndrew Gallatin 		/* -S or -2S is SFP+ */
2854c406ad2eSAndrew Gallatin 		sc->connector = MXGE_SFP;
2855c406ad2eSAndrew Gallatin 	} else {
2856c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2857c406ad2eSAndrew Gallatin 	}
2858c587e59fSAndrew Gallatin }
2859c587e59fSAndrew Gallatin 
2860c406ad2eSAndrew Gallatin /*
2861c406ad2eSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2862c406ad2eSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2863c406ad2eSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2864c406ad2eSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2865c406ad2eSAndrew Gallatin  * than in the interrupt handler itself.
2866c406ad2eSAndrew Gallatin  */
2867c406ad2eSAndrew Gallatin static void
2868c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2869c406ad2eSAndrew Gallatin {
2870c406ad2eSAndrew Gallatin 	mxge_cmd_t cmd;
2871c406ad2eSAndrew Gallatin 	char *cage_type;
2872c406ad2eSAndrew Gallatin 
2873c406ad2eSAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
2874c406ad2eSAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
2875c406ad2eSAndrew Gallatin 	uint32_t byte;
2876c406ad2eSAndrew Gallatin 
2877c406ad2eSAndrew Gallatin 	sc->need_media_probe = 0;
2878c406ad2eSAndrew Gallatin 
2879c406ad2eSAndrew Gallatin 	if (sc->connector == MXGE_XFP) {
288001638550SAndrew Gallatin 		/* -R is XFP */
288101638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
288201638550SAndrew Gallatin 		mxge_media_type_entries =
288373a1170aSPedro F. Giffuni 			nitems(mxge_xfp_media_types);
288401638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
288501638550SAndrew Gallatin 		cage_type = "XFP";
2886c406ad2eSAndrew Gallatin 	} else 	if (sc->connector == MXGE_SFP) {
288701638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
288801638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
288901638550SAndrew Gallatin 		mxge_media_type_entries =
289073a1170aSPedro F. Giffuni 			nitems(mxge_sfp_media_types);
289101638550SAndrew Gallatin 		cage_type = "SFP+";
289201638550SAndrew Gallatin 		byte = 3;
2893c406ad2eSAndrew Gallatin 	} else {
2894c406ad2eSAndrew Gallatin 		/* nothing to do; media type cannot change */
2895c587e59fSAndrew Gallatin 		return;
2896c587e59fSAndrew Gallatin 	}
2897c587e59fSAndrew Gallatin 
2898c587e59fSAndrew Gallatin 	/*
2899c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2900c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2901c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2902c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2903c587e59fSAndrew Gallatin 	 * a millisecond
2904c587e59fSAndrew Gallatin 	 */
2905c587e59fSAndrew Gallatin 
2906c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
290701638550SAndrew Gallatin 	cmd.data1 = byte;
290801638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
290901638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2910c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2911c587e59fSAndrew Gallatin 	}
291201638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
291301638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2914c587e59fSAndrew Gallatin 	}
2915c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2916c587e59fSAndrew Gallatin 		return;
2917c587e59fSAndrew Gallatin 	}
2918c587e59fSAndrew Gallatin 
2919c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
292001638550SAndrew Gallatin 	cmd.data0 = byte;
292101638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2922c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2923c587e59fSAndrew Gallatin 		DELAY(1000);
292401638550SAndrew Gallatin 		cmd.data0 = byte;
292501638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2926c587e59fSAndrew Gallatin 	}
2927c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
292801638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
292901638550SAndrew Gallatin 			      cage_type, err, ms);
2930c587e59fSAndrew Gallatin 		return;
2931c587e59fSAndrew Gallatin 	}
2932c587e59fSAndrew Gallatin 
2933c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2934c587e59fSAndrew Gallatin 		if (mxge_verbose)
293501638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2936c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2937c406ad2eSAndrew Gallatin 		if (sc->current_media != mxge_media_types[0].flag) {
2938c406ad2eSAndrew Gallatin 			mxge_media_init(sc);
2939c406ad2eSAndrew Gallatin 			mxge_media_set(sc, mxge_media_types[0].flag);
2940c406ad2eSAndrew Gallatin 		}
2941c587e59fSAndrew Gallatin 		return;
2942c587e59fSAndrew Gallatin 	}
294301638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
2944c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2945c587e59fSAndrew Gallatin 			if (mxge_verbose)
294601638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
294701638550SAndrew Gallatin 					      cage_type,
2948c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2949c587e59fSAndrew Gallatin 
2950c406ad2eSAndrew Gallatin 			if (sc->current_media != mxge_media_types[i].flag) {
2951c406ad2eSAndrew Gallatin 				mxge_media_init(sc);
2952c406ad2eSAndrew Gallatin 				mxge_media_set(sc, mxge_media_types[i].flag);
2953c406ad2eSAndrew Gallatin 			}
2954c587e59fSAndrew Gallatin 			return;
2955c587e59fSAndrew Gallatin 		}
2956c587e59fSAndrew Gallatin 	}
2957c406ad2eSAndrew Gallatin 	if (mxge_verbose)
2958c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "%s media 0x%x unknown\n",
2959c406ad2eSAndrew Gallatin 			      cage_type, cmd.data0);
2960c587e59fSAndrew Gallatin 
2961c587e59fSAndrew Gallatin 	return;
2962c587e59fSAndrew Gallatin }
2963c587e59fSAndrew Gallatin 
2964b2fc195eSAndrew Gallatin static void
29656d87a65dSAndrew Gallatin mxge_intr(void *arg)
2966b2fc195eSAndrew Gallatin {
29671e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
29681e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
29691e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
29701e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
29711e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
29725e7d8541SAndrew Gallatin 	uint32_t send_done_count;
29735e7d8541SAndrew Gallatin 	uint8_t valid;
2974b2fc195eSAndrew Gallatin 
29755e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
29765e7d8541SAndrew Gallatin 	if (!stats->valid) {
29775e7d8541SAndrew Gallatin 		return;
2978b2fc195eSAndrew Gallatin 	}
29795e7d8541SAndrew Gallatin 	valid = stats->valid;
2980b2fc195eSAndrew Gallatin 
298191ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
29825e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
29835e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
29845e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
29855e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
29865e7d8541SAndrew Gallatin 			stats->valid = 0;
2987dc8731d4SAndrew Gallatin 	} else {
2988dc8731d4SAndrew Gallatin 		stats->valid = 0;
2989dc8731d4SAndrew Gallatin 	}
2990dc8731d4SAndrew Gallatin 
2991dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
29925e7d8541SAndrew Gallatin 	do {
29935e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
29945e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
29955e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
29965e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
2997c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
29981e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
29991e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
30005e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
3001b2fc195eSAndrew Gallatin 		}
300291ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
300373c7c83fSAndrew Gallatin 			wmb();
30045e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
3005b2fc195eSAndrew Gallatin 
3006c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
3007c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
30085e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
30095e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
3010b2fc195eSAndrew Gallatin 			if (sc->link_state) {
30115e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
30125e7d8541SAndrew Gallatin 				if (mxge_verbose)
30135e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
3014b2fc195eSAndrew Gallatin 			} else {
30155e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
30165e7d8541SAndrew Gallatin 				if (mxge_verbose)
30175e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
3018b2fc195eSAndrew Gallatin 			}
3019c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
3020b2fc195eSAndrew Gallatin 		}
3021b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
30221e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
3023b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
30241e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
30255e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
30265e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
30275e7d8541SAndrew Gallatin 		}
3028c587e59fSAndrew Gallatin 
3029c587e59fSAndrew Gallatin 		if (stats->link_down) {
30305e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3031c587e59fSAndrew Gallatin 			sc->link_state = 0;
3032c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3033c587e59fSAndrew Gallatin 		}
3034b2fc195eSAndrew Gallatin 	}
3035b2fc195eSAndrew Gallatin 
30365e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
30375e7d8541SAndrew Gallatin 	if (valid & 0x1)
30381e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
30391e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3040b2fc195eSAndrew Gallatin }
3041b2fc195eSAndrew Gallatin 
3042b2fc195eSAndrew Gallatin static void
30436d87a65dSAndrew Gallatin mxge_init(void *arg)
3044b2fc195eSAndrew Gallatin {
30453cae7311SAndrew Gallatin 	mxge_softc_t *sc = arg;
3046*93037a67SJustin Hibbits 	if_t ifp = sc->ifp;
30473cae7311SAndrew Gallatin 
30483cae7311SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3049*93037a67SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
30503cae7311SAndrew Gallatin 		(void) mxge_open(sc);
30513cae7311SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3052b2fc195eSAndrew Gallatin }
3053b2fc195eSAndrew Gallatin 
3054b2fc195eSAndrew Gallatin static void
30551e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
30561e413cf9SAndrew Gallatin {
30571e413cf9SAndrew Gallatin 	int i;
30581e413cf9SAndrew Gallatin 
305926dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
306026dd49c6SAndrew Gallatin 	tcp_lro_free(&ss->lc);
306126dd49c6SAndrew Gallatin #endif
30621e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
30631e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
30641e413cf9SAndrew Gallatin 			continue;
30651e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
30661e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
30671e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
30681e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
30691e413cf9SAndrew Gallatin 	}
30701e413cf9SAndrew Gallatin 
30711e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
30721e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
30731e413cf9SAndrew Gallatin 			continue;
30741e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
30751e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
30761e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
30771e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
30781e413cf9SAndrew Gallatin 	}
30791e413cf9SAndrew Gallatin 
30801e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
30811e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30821e413cf9SAndrew Gallatin 		return;
30831e413cf9SAndrew Gallatin 
30841e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30851e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
30861e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
30871e413cf9SAndrew Gallatin 			continue;
30881e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
30891e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
30901e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
30911e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
30921e413cf9SAndrew Gallatin 	}
30931e413cf9SAndrew Gallatin }
30941e413cf9SAndrew Gallatin 
30951e413cf9SAndrew Gallatin static void
30966d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3097b2fc195eSAndrew Gallatin {
30981e413cf9SAndrew Gallatin 	int slice;
30991e413cf9SAndrew Gallatin 
31001e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31011e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
31021e413cf9SAndrew Gallatin }
31031e413cf9SAndrew Gallatin 
31041e413cf9SAndrew Gallatin static void
31051e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
31061e413cf9SAndrew Gallatin {
3107b2fc195eSAndrew Gallatin 	int i;
3108b2fc195eSAndrew Gallatin 
31091e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
31101e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
31111e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3112b2fc195eSAndrew Gallatin 
31131e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
31141e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
31151e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
31161e413cf9SAndrew Gallatin 
31171e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
31181e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
31191e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
31201e413cf9SAndrew Gallatin 
31211e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
31221e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
31231e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
31241e413cf9SAndrew Gallatin 
31251e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
31261e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
31271e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
31281e413cf9SAndrew Gallatin 
31291e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
31301e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
31311e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
31321e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
31331e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3134b2fc195eSAndrew Gallatin 			}
31351e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
31361e413cf9SAndrew Gallatin 		}
31371e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
31381e413cf9SAndrew Gallatin 	}
31391e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
31401e413cf9SAndrew Gallatin 
31411e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
31421e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
31431e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
31441e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
31451e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
31461e413cf9SAndrew Gallatin 			}
31471e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
31481e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
31491e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
31501e413cf9SAndrew Gallatin 		}
31511e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
31521e413cf9SAndrew Gallatin 	}
31531e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
31541e413cf9SAndrew Gallatin 
31551e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
31561e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
31571e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
31581e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
31591e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
31601e413cf9SAndrew Gallatin 			}
31611e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
31621e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
31631e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
31641e413cf9SAndrew Gallatin 		}
31651e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
31661e413cf9SAndrew Gallatin 	}
31671e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3168b2fc195eSAndrew Gallatin }
3169b2fc195eSAndrew Gallatin 
3170b2fc195eSAndrew Gallatin static void
31716d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3172b2fc195eSAndrew Gallatin {
31731e413cf9SAndrew Gallatin 	int slice;
3174b2fc195eSAndrew Gallatin 
31751e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31761e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3177c2657176SAndrew Gallatin }
3178b2fc195eSAndrew Gallatin 
3179b2fc195eSAndrew Gallatin static int
31801e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
31811e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3182b2fc195eSAndrew Gallatin {
31831e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
31841e413cf9SAndrew Gallatin 	size_t bytes;
31851e413cf9SAndrew Gallatin 	int err, i;
3186b2fc195eSAndrew Gallatin 
31871e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3188adae7080SAndrew Gallatin 
31891e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
31901e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3191aed8e389SAndrew Gallatin 
3192b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
31931e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
31941e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3195b2fc195eSAndrew Gallatin 
31961e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
31971e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3198b2fc195eSAndrew Gallatin 
31991e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
32001e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
32011e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3202b2fc195eSAndrew Gallatin 
32031e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
32041e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3205b2fc195eSAndrew Gallatin 
32061e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3207b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3208b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3209b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3210b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3211b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3212b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3213b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3214b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3215b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3216b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3217b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32181e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3219b2fc195eSAndrew Gallatin 	if (err != 0) {
3220b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3221b2fc195eSAndrew Gallatin 			      err);
3222c2ede4b3SMartin Blapp 		return err;
3223b2fc195eSAndrew Gallatin 	}
3224b2fc195eSAndrew Gallatin 
3225b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3226b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3227b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3228b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3229b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3230b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3231053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3232b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3233b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3234b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3235b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32361e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3237b2fc195eSAndrew Gallatin 	if (err != 0) {
3238b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3239b2fc195eSAndrew Gallatin 			      err);
3240c2ede4b3SMartin Blapp 		return err;
3241b2fc195eSAndrew Gallatin 	}
32421e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
32431e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
32441e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3245b2fc195eSAndrew Gallatin 		if (err != 0) {
3246b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3247b2fc195eSAndrew Gallatin 				      err);
3248c2ede4b3SMartin Blapp 			return err;
3249b2fc195eSAndrew Gallatin 		}
3250b2fc195eSAndrew Gallatin 	}
32511e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
32521e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3253b2fc195eSAndrew Gallatin 	if (err != 0) {
3254b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3255b2fc195eSAndrew Gallatin 			      err);
3256c2ede4b3SMartin Blapp 		return err;
3257b2fc195eSAndrew Gallatin 	}
3258b2fc195eSAndrew Gallatin 
32591e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32601e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
32611e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3262b2fc195eSAndrew Gallatin 		if (err != 0) {
3263b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3264b2fc195eSAndrew Gallatin 				      err);
3265c2ede4b3SMartin Blapp 			return err;
3266b2fc195eSAndrew Gallatin 		}
3267b2fc195eSAndrew Gallatin 	}
32681e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
32691e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3270b2fc195eSAndrew Gallatin 	if (err != 0) {
3271b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3272b2fc195eSAndrew Gallatin 			      err);
3273c2ede4b3SMartin Blapp 		return err;
32741e413cf9SAndrew Gallatin 	}
32751e413cf9SAndrew Gallatin 
3276b78540b1SGabor Kovesdan 	/* now allocate TX resources */
32771e413cf9SAndrew Gallatin 
32781e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
32791e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
32801e413cf9SAndrew Gallatin 
32811e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
32821e413cf9SAndrew Gallatin 	bytes = 8 +
32831e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
32841e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
32851e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
32861e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
3287aa54c242SJohn Baldwin 		((uintptr_t)(ss->tx.req_bytes + 7) & ~7UL);
32881e413cf9SAndrew Gallatin 
32891e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
32901e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
32911e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
32921e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
32931e413cf9SAndrew Gallatin 
32941e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
32951e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
32961e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32971e413cf9SAndrew Gallatin 
32981e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
32991e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
33001e413cf9SAndrew Gallatin 				 1,			/* alignment */
33011e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
33021e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
33031e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
33041e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
33051e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
33061e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
33071e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
33081e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
33091e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
33101e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
33111e413cf9SAndrew Gallatin 
33121e413cf9SAndrew Gallatin 	if (err != 0) {
33131e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
33141e413cf9SAndrew Gallatin 			      err);
3315c2ede4b3SMartin Blapp 		return err;
33161e413cf9SAndrew Gallatin 	}
33171e413cf9SAndrew Gallatin 
33181e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
33191e413cf9SAndrew Gallatin 	   in the ring */
33201e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
33211e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
33221e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
33231e413cf9SAndrew Gallatin 		if (err != 0) {
33241e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
33251e413cf9SAndrew Gallatin 				      err);
3326c2ede4b3SMartin Blapp 			return err;
33271e413cf9SAndrew Gallatin 		}
3328b2fc195eSAndrew Gallatin 	}
3329b2fc195eSAndrew Gallatin 	return 0;
3330b2fc195eSAndrew Gallatin 
3331b2fc195eSAndrew Gallatin }
3332b2fc195eSAndrew Gallatin 
33331e413cf9SAndrew Gallatin static int
33341e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
33351e413cf9SAndrew Gallatin {
33361e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
33371e413cf9SAndrew Gallatin 	int tx_ring_size;
33381e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
33391e413cf9SAndrew Gallatin 	int err, slice;
33401e413cf9SAndrew Gallatin 
33411e413cf9SAndrew Gallatin 	/* get ring sizes */
33421e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33431e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
33441e413cf9SAndrew Gallatin 	if (err != 0) {
33451e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33461e413cf9SAndrew Gallatin 		goto abort;
33471e413cf9SAndrew Gallatin 	}
33481e413cf9SAndrew Gallatin 
33491e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
33501e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
3351*93037a67SJustin Hibbits 	if_setsendqlen(sc->ifp, tx_ring_entries - 1);
3352*93037a67SJustin Hibbits 	if_setsendqready(sc->ifp);
33531e413cf9SAndrew Gallatin 
33541e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33551e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
33561e413cf9SAndrew Gallatin 					     rx_ring_entries,
33571e413cf9SAndrew Gallatin 					     tx_ring_entries);
33581e413cf9SAndrew Gallatin 		if (err != 0)
33591e413cf9SAndrew Gallatin 			goto abort;
33601e413cf9SAndrew Gallatin 	}
33611e413cf9SAndrew Gallatin 	return 0;
33621e413cf9SAndrew Gallatin 
33631e413cf9SAndrew Gallatin abort:
33641e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
33651e413cf9SAndrew Gallatin 	return err;
33661e413cf9SAndrew Gallatin 
33671e413cf9SAndrew Gallatin }
33681e413cf9SAndrew Gallatin 
3369053e637fSAndrew Gallatin static void
3370053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3371053e637fSAndrew Gallatin {
3372c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3373053e637fSAndrew Gallatin 
3374053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3375053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3376053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3377053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3378053e637fSAndrew Gallatin 		*nbufs = 1;
3379053e637fSAndrew Gallatin 		return;
3380053e637fSAndrew Gallatin 	}
3381053e637fSAndrew Gallatin 
3382053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3383053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3384053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3385053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3386053e637fSAndrew Gallatin 		*nbufs = 1;
3387053e637fSAndrew Gallatin 		return;
3388053e637fSAndrew Gallatin 	}
3389b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3390b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3391b0f7b922SAndrew Gallatin 	*nbufs = 1;
3392053e637fSAndrew Gallatin }
3393053e637fSAndrew Gallatin 
3394b2fc195eSAndrew Gallatin static int
33951e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3396b2fc195eSAndrew Gallatin {
33971e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
33986d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3399b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
34001e413cf9SAndrew Gallatin 	int err, i, slice;
3401b2fc195eSAndrew Gallatin 
34021e413cf9SAndrew Gallatin 	sc = ss->sc;
34031e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
34041e413cf9SAndrew Gallatin 
340526dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
340626dd49c6SAndrew Gallatin 	(void)tcp_lro_init(&ss->lc);
340726dd49c6SAndrew Gallatin #endif
340826dd49c6SAndrew Gallatin 	ss->lc.ifp = sc->ifp;
3409053e637fSAndrew Gallatin 
34101e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
34111e413cf9SAndrew Gallatin 
34121e413cf9SAndrew Gallatin 	err = 0;
34136147584bSElliott Mitchell 
34141e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34151e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
34161e413cf9SAndrew Gallatin 	ss->tx.lanai =
34171e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3418c6cb3e3fSAndrew Gallatin 	ss->tx.send_go = (volatile uint32_t *)
3419c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3420c6cb3e3fSAndrew Gallatin 	ss->tx.send_stop = (volatile uint32_t *)
3421c6cb3e3fSAndrew Gallatin 	(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
34226147584bSElliott Mitchell 
34231e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34241e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
34251e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
34261e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
34271e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34281e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34291e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
34301e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
34311e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34321e413cf9SAndrew Gallatin 
34331e413cf9SAndrew Gallatin 	if (err != 0) {
34341e413cf9SAndrew Gallatin 		device_printf(sc->dev,
34351e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
34361e413cf9SAndrew Gallatin 		return EIO;
34371e413cf9SAndrew Gallatin 	}
34381e413cf9SAndrew Gallatin 
34391e413cf9SAndrew Gallatin 	/* stock receive rings */
34401e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
34411e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
34421e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
34431e413cf9SAndrew Gallatin 		if (err) {
34441e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
34451e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
34461e413cf9SAndrew Gallatin 			return ENOMEM;
34471e413cf9SAndrew Gallatin 		}
34481e413cf9SAndrew Gallatin 	}
34491e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
34501e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
34511e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
34521e413cf9SAndrew Gallatin 	}
34531e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
34541e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
3455*93037a67SJustin Hibbits 	ss->rx_big.mlen = if_getmtu(ss->sc->ifp) + ETHER_HDR_LEN +
34564d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
34571e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
34581e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
34591e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
34601e413cf9SAndrew Gallatin 		if (err) {
34611e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
34621e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
34631e413cf9SAndrew Gallatin 			return ENOMEM;
34641e413cf9SAndrew Gallatin 		}
34651e413cf9SAndrew Gallatin 	}
34661e413cf9SAndrew Gallatin 	return 0;
34671e413cf9SAndrew Gallatin }
34681e413cf9SAndrew Gallatin 
34691e413cf9SAndrew Gallatin static int
34701e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
34711e413cf9SAndrew Gallatin {
34721e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
34731e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
34741e413cf9SAndrew Gallatin 	bus_addr_t bus;
34751e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3476c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3477b2fc195eSAndrew Gallatin 
34787d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
3479*93037a67SJustin Hibbits 	bcopy(if_getlladdr(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
34807d542e2dSAndrew Gallatin 
3481adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3482b2fc195eSAndrew Gallatin 	if (err != 0) {
3483b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3484b2fc195eSAndrew Gallatin 		return EIO;
3485b2fc195eSAndrew Gallatin 	}
3486b2fc195eSAndrew Gallatin 
34871e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
34881e413cf9SAndrew Gallatin 		/* setup the indirection table */
34891e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
34901e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
34911e413cf9SAndrew Gallatin 				    &cmd);
3492b2fc195eSAndrew Gallatin 
34931e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
34941e413cf9SAndrew Gallatin 				     &cmd);
34951e413cf9SAndrew Gallatin 		if (err != 0) {
34961e413cf9SAndrew Gallatin 			device_printf(sc->dev,
34971e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
34981e413cf9SAndrew Gallatin 			return err;
34991e413cf9SAndrew Gallatin 		}
35001e413cf9SAndrew Gallatin 
35011e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
35021e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
35031e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
35041e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
35051e413cf9SAndrew Gallatin 
35061e413cf9SAndrew Gallatin 		cmd.data0 = 1;
35071e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
35081e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
35091e413cf9SAndrew Gallatin 		if (err != 0) {
35101e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
35111e413cf9SAndrew Gallatin 			return err;
35121e413cf9SAndrew Gallatin 		}
35131e413cf9SAndrew Gallatin 	}
35141e413cf9SAndrew Gallatin 
3515*93037a67SJustin Hibbits 	mxge_choose_params(if_getmtu(sc->ifp), &big_bytes, &cl_size, &nbufs);
35161e413cf9SAndrew Gallatin 
35171e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3518053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3519053e637fSAndrew Gallatin 			    &cmd);
3520053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3521053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
35221e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3523053e637fSAndrew Gallatin 		device_printf(sc->dev,
3524053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
35251e413cf9SAndrew Gallatin 			      nbufs);
3526053e637fSAndrew Gallatin 		return EIO;
3527053e637fSAndrew Gallatin 	}
3528b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3529b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3530b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3531*93037a67SJustin Hibbits 	cmd.data0 = if_getmtu(sc->ifp) + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
35325e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3533b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
35345e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3535b2fc195eSAndrew Gallatin 			     &cmd);
3536053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
35375e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
35380fa7f681SAndrew Gallatin 
35390fa7f681SAndrew Gallatin 	if (err != 0) {
35400fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
35410fa7f681SAndrew Gallatin 		goto abort;
35420fa7f681SAndrew Gallatin 	}
35430fa7f681SAndrew Gallatin 
3544b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
35456147584bSElliott Mitchell 	for (slice = 0; slice < sc->num_slices; slice++) {
3546c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3547c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3548c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3549c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3550c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
35510fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3552c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3553c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3554c6cb3e3fSAndrew Gallatin 	}
35550fa7f681SAndrew Gallatin 
35560fa7f681SAndrew Gallatin 	if (err != 0) {
35571e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
35580fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
35590fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
35600fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
35610fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
35620fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
35630fa7f681SAndrew Gallatin 				    &cmd);
35640fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
35650fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
35660fa7f681SAndrew Gallatin 	} else {
35670fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
35680fa7f681SAndrew Gallatin 	}
3569b2fc195eSAndrew Gallatin 
3570b2fc195eSAndrew Gallatin 	if (err != 0) {
3571b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3572b2fc195eSAndrew Gallatin 		goto abort;
3573b2fc195eSAndrew Gallatin 	}
3574b2fc195eSAndrew Gallatin 
35751e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
35761e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
35771e413cf9SAndrew Gallatin 		if (err != 0) {
35781e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
35791e413cf9SAndrew Gallatin 				      slice);
35801e413cf9SAndrew Gallatin 			goto abort;
35811e413cf9SAndrew Gallatin 		}
35821e413cf9SAndrew Gallatin 	}
35831e413cf9SAndrew Gallatin 
3584b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
35855e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3586b2fc195eSAndrew Gallatin 	if (err) {
3587b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3588b2fc195eSAndrew Gallatin 		goto abort;
3589b2fc195eSAndrew Gallatin 	}
3590c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3591c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3592c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3593c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3594c6cb3e3fSAndrew Gallatin 	}
3595*93037a67SJustin Hibbits 	if_setdrvflagbits(sc->ifp, IFF_DRV_RUNNING, 0);
3596*93037a67SJustin Hibbits 	if_setdrvflagbits(sc->ifp, 0, IFF_DRV_OACTIVE);
3597b2fc195eSAndrew Gallatin 
3598b2fc195eSAndrew Gallatin 	return 0;
3599b2fc195eSAndrew Gallatin 
3600b2fc195eSAndrew Gallatin abort:
36016d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3602a98d6cd7SAndrew Gallatin 
3603b2fc195eSAndrew Gallatin 	return err;
3604b2fc195eSAndrew Gallatin }
3605b2fc195eSAndrew Gallatin 
3606b2fc195eSAndrew Gallatin static int
3607a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3608b2fc195eSAndrew Gallatin {
36096d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3610b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3611c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3612c6cb3e3fSAndrew Gallatin 	int slice;
3613b2fc195eSAndrew Gallatin 
3614c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3615c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3616c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3617c6cb3e3fSAndrew Gallatin 	}
3618*93037a67SJustin Hibbits 	if_setdrvflagbits(sc->ifp, 0, IFF_DRV_RUNNING);
3619a393336bSAndrew Gallatin 	if (!down) {
3620b2fc195eSAndrew Gallatin 		old_down_cnt = sc->down_cnt;
362173c7c83fSAndrew Gallatin 		wmb();
36225e7d8541SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3623b2fc195eSAndrew Gallatin 		if (err) {
3624a393336bSAndrew Gallatin 			device_printf(sc->dev,
3625a393336bSAndrew Gallatin 				      "Couldn't bring down link\n");
3626b2fc195eSAndrew Gallatin 		}
3627b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3628b2fc195eSAndrew Gallatin 			/* wait for down irq */
3629dce01b9bSAndrew Gallatin 			DELAY(10 * sc->intr_coal_delay);
3630b2fc195eSAndrew Gallatin 		}
363173c7c83fSAndrew Gallatin 		wmb();
3632b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3633b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "never got down irq\n");
3634b2fc195eSAndrew Gallatin 		}
3635a393336bSAndrew Gallatin 	}
36366d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3637a98d6cd7SAndrew Gallatin 
3638b2fc195eSAndrew Gallatin 	return 0;
3639b2fc195eSAndrew Gallatin }
3640b2fc195eSAndrew Gallatin 
3641dce01b9bSAndrew Gallatin static void
3642dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3643dce01b9bSAndrew Gallatin {
3644dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3645dce01b9bSAndrew Gallatin 	int reg;
3646c68534f1SScott Long 	uint16_t lnk, pectl;
3647dce01b9bSAndrew Gallatin 
3648dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
36493b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
3650dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3651dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3652dce01b9bSAndrew Gallatin 
365383d54b59SAndrew Gallatin 		if (sc->pectl == 0) {
3654dce01b9bSAndrew Gallatin 			pectl = pci_read_config(dev, reg + 0x8, 2);
3655dce01b9bSAndrew Gallatin 			pectl = (pectl & ~0x7000) | (5 << 12);
3656dce01b9bSAndrew Gallatin 			pci_write_config(dev, reg + 0x8, pectl, 2);
365783d54b59SAndrew Gallatin 			sc->pectl = pectl;
365883d54b59SAndrew Gallatin 		} else {
365983d54b59SAndrew Gallatin 			/* restore saved pectl after watchdog reset */
366083d54b59SAndrew Gallatin 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
366183d54b59SAndrew Gallatin 		}
3662dce01b9bSAndrew Gallatin 	}
3663dce01b9bSAndrew Gallatin 
3664dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3665dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3666dce01b9bSAndrew Gallatin }
3667dce01b9bSAndrew Gallatin 
3668dce01b9bSAndrew Gallatin static uint32_t
3669dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3670dce01b9bSAndrew Gallatin {
3671dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3672dce01b9bSAndrew Gallatin 	uint32_t vs;
3673dce01b9bSAndrew Gallatin 
3674dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
36753b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) {
3676dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3677dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3678dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3679dce01b9bSAndrew Gallatin 	}
3680dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3681dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3682dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3683dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3684dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3685dce01b9bSAndrew Gallatin }
3686dce01b9bSAndrew Gallatin 
368772c042dfSAndrew Gallatin static void
368872c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3689dce01b9bSAndrew Gallatin {
3690e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3691a393336bSAndrew Gallatin 	struct mxge_slice_state *ss;
3692a393336bSAndrew Gallatin 	int err, running, s, num_tx_slices = 1;
3693dce01b9bSAndrew Gallatin 	uint32_t reboot;
3694dce01b9bSAndrew Gallatin 	uint16_t cmd;
3695dce01b9bSAndrew Gallatin 
3696dce01b9bSAndrew Gallatin 	err = ENXIO;
3697dce01b9bSAndrew Gallatin 
3698dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3699dce01b9bSAndrew Gallatin 
3700dce01b9bSAndrew Gallatin 	/*
3701dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3702dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3703dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3704dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3705dce01b9bSAndrew Gallatin 	 * again
3706dce01b9bSAndrew Gallatin 	 */
3707dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3708dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3709dce01b9bSAndrew Gallatin 		/*
3710dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3711dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3712dce01b9bSAndrew Gallatin 		 * back, then give up
3713dce01b9bSAndrew Gallatin 		 */
3714dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3715dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3716dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3717dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3718dce01b9bSAndrew Gallatin 		}
3719dce01b9bSAndrew Gallatin 	}
3720dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3721dce01b9bSAndrew Gallatin 		/* print the reboot status */
3722dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3723dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3724dce01b9bSAndrew Gallatin 			      reboot);
3725*93037a67SJustin Hibbits 		running = if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING;
3726a393336bSAndrew Gallatin 		if (running) {
3727a393336bSAndrew Gallatin 			/*
3728a393336bSAndrew Gallatin 			 * quiesce NIC so that TX routines will not try to
3729a393336bSAndrew Gallatin 			 * xmit after restoration of BAR
3730a393336bSAndrew Gallatin 			 */
3731a393336bSAndrew Gallatin 
3732a393336bSAndrew Gallatin 			/* Mark the link as down */
3733a393336bSAndrew Gallatin 			if (sc->link_state) {
3734a393336bSAndrew Gallatin 				sc->link_state = 0;
3735a393336bSAndrew Gallatin 				if_link_state_change(sc->ifp,
3736a393336bSAndrew Gallatin 						     LINK_STATE_DOWN);
3737a393336bSAndrew Gallatin 			}
37386147584bSElliott Mitchell 
3739a393336bSAndrew Gallatin 			num_tx_slices = sc->num_slices;
37406147584bSElliott Mitchell 
3741a393336bSAndrew Gallatin 			/* grab all TX locks to ensure no tx  */
3742a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3743a393336bSAndrew Gallatin 				ss = &sc->ss[s];
3744a393336bSAndrew Gallatin 				mtx_lock(&ss->tx.mtx);
3745a393336bSAndrew Gallatin 			}
3746a393336bSAndrew Gallatin 			mxge_close(sc, 1);
3747a393336bSAndrew Gallatin 		}
3748dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3749e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3750e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3751dce01b9bSAndrew Gallatin 
3752dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3753dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
375410882804SAndrew Gallatin 
3755a393336bSAndrew Gallatin 		/* reload f/w */
3756a393336bSAndrew Gallatin 		err = mxge_load_firmware(sc, 0);
3757a393336bSAndrew Gallatin 		if (err) {
3758a393336bSAndrew Gallatin 			device_printf(sc->dev,
3759a393336bSAndrew Gallatin 				      "Unable to re-load f/w\n");
376010882804SAndrew Gallatin 		}
3761a393336bSAndrew Gallatin 		if (running) {
3762a393336bSAndrew Gallatin 			if (!err)
3763a393336bSAndrew Gallatin 				err = mxge_open(sc);
3764a393336bSAndrew Gallatin 			/* release all TX locks */
3765a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3766a393336bSAndrew Gallatin 				ss = &sc->ss[s];
376783d54b59SAndrew Gallatin 				mxge_start_locked(ss);
3768a393336bSAndrew Gallatin 				mtx_unlock(&ss->tx.mtx);
3769a393336bSAndrew Gallatin 			}
3770a393336bSAndrew Gallatin 		}
3771a393336bSAndrew Gallatin 		sc->watchdog_resets++;
3772dce01b9bSAndrew Gallatin 	} else {
3773c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
377472c042dfSAndrew Gallatin 			      "NIC did not reboot, not resetting\n");
377572c042dfSAndrew Gallatin 		err = 0;
377672c042dfSAndrew Gallatin 	}
377772c042dfSAndrew Gallatin 	if (err) {
377872c042dfSAndrew Gallatin 		device_printf(sc->dev, "watchdog reset failed\n");
377972c042dfSAndrew Gallatin 	} else {
37806b484a49SAndrew Gallatin 		if (sc->dying == 2)
37816b484a49SAndrew Gallatin 			sc->dying = 0;
37826b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
378372c042dfSAndrew Gallatin 	}
378472c042dfSAndrew Gallatin }
378572c042dfSAndrew Gallatin 
378672c042dfSAndrew Gallatin static void
378772c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
378872c042dfSAndrew Gallatin {
378972c042dfSAndrew Gallatin 	mxge_softc_t *sc = arg;
379072c042dfSAndrew Gallatin 
379172c042dfSAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
379272c042dfSAndrew Gallatin 	mxge_watchdog_reset(sc);
379372c042dfSAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
379472c042dfSAndrew Gallatin }
379572c042dfSAndrew Gallatin 
379672c042dfSAndrew Gallatin static void
379772c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
379872c042dfSAndrew Gallatin {
379972c042dfSAndrew Gallatin 	tx = &sc->ss[slice].tx;
380072c042dfSAndrew Gallatin 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3801c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev,
3802c6cb3e3fSAndrew Gallatin 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3803c6cb3e3fSAndrew Gallatin 		      tx->req, tx->done, tx->queue_active);
3804c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3805c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3806dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3807c6cb3e3fSAndrew Gallatin 		      tx->pkt_done,
38081e413cf9SAndrew Gallatin 		      be32toh(sc->ss->fw_stats->send_done_count));
3809dce01b9bSAndrew Gallatin }
3810dce01b9bSAndrew Gallatin 
3811e749ef6bSAndrew Gallatin static int
3812dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3813dce01b9bSAndrew Gallatin {
3814c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
38151e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3816c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3817dce01b9bSAndrew Gallatin 
3818dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3819dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
38206147584bSElliott Mitchell 	for (i = 0; (i < sc->num_slices) && (err == 0); i++) {
3821c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3822dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3823dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3824c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3825c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
382672c042dfSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause) {
382772c042dfSAndrew Gallatin 				mxge_warn_stuck(sc, tx, i);
382872c042dfSAndrew Gallatin 				taskqueue_enqueue(sc->tq, &sc->watchdog_task);
382972c042dfSAndrew Gallatin 				return (ENXIO);
383072c042dfSAndrew Gallatin 			}
3831c587e59fSAndrew Gallatin 			else
3832c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3833c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3834c587e59fSAndrew Gallatin 		}
3835dce01b9bSAndrew Gallatin 
3836dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3837dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3838c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3839c6cb3e3fSAndrew Gallatin 	}
3840c587e59fSAndrew Gallatin 
3841c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3842c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3843e749ef6bSAndrew Gallatin 	return (err);
3844dce01b9bSAndrew Gallatin }
3845dce01b9bSAndrew Gallatin 
3846f3f040d9SGleb Smirnoff static uint64_t
3847*93037a67SJustin Hibbits mxge_get_counter(if_t ifp, ift_counter cnt)
38481e413cf9SAndrew Gallatin {
3849f3f040d9SGleb Smirnoff 	struct mxge_softc *sc;
3850f3f040d9SGleb Smirnoff 	uint64_t rv;
38511e413cf9SAndrew Gallatin 
3852f3f040d9SGleb Smirnoff 	sc = if_getsoftc(ifp);
3853f3f040d9SGleb Smirnoff 	rv = 0;
3854f3f040d9SGleb Smirnoff 
3855f3f040d9SGleb Smirnoff 	switch (cnt) {
3856f3f040d9SGleb Smirnoff 	case IFCOUNTER_IPACKETS:
3857f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3858f3f040d9SGleb Smirnoff 			rv += sc->ss[s].ipackets;
3859f3f040d9SGleb Smirnoff 		return (rv);
3860f3f040d9SGleb Smirnoff 	case IFCOUNTER_OPACKETS:
3861f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3862f3f040d9SGleb Smirnoff 			rv += sc->ss[s].opackets;
3863f3f040d9SGleb Smirnoff 		return (rv);
3864f3f040d9SGleb Smirnoff 	case IFCOUNTER_OERRORS:
3865f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3866f3f040d9SGleb Smirnoff 			rv += sc->ss[s].oerrors;
3867f3f040d9SGleb Smirnoff 		return (rv);
3868f3f040d9SGleb Smirnoff 	case IFCOUNTER_OBYTES:
3869f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3870f3f040d9SGleb Smirnoff 			rv += sc->ss[s].obytes;
3871f3f040d9SGleb Smirnoff 		return (rv);
3872f3f040d9SGleb Smirnoff 	case IFCOUNTER_OMCASTS:
3873f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3874f3f040d9SGleb Smirnoff 			rv += sc->ss[s].omcasts;
3875f3f040d9SGleb Smirnoff 		return (rv);
3876f3f040d9SGleb Smirnoff 	case IFCOUNTER_OQDROPS:
3877f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3878f3f040d9SGleb Smirnoff 			rv += sc->ss[s].tx.br->br_drops;
3879f3f040d9SGleb Smirnoff 		return (rv);
3880f3f040d9SGleb Smirnoff 	default:
3881f3f040d9SGleb Smirnoff 		return (if_get_counter_default(ifp, cnt));
38821e413cf9SAndrew Gallatin 	}
38831e413cf9SAndrew Gallatin }
3884c6cb3e3fSAndrew Gallatin 
38851e413cf9SAndrew Gallatin static void
3886dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3887dce01b9bSAndrew Gallatin {
3888dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
38896b484a49SAndrew Gallatin 	u_long pkts = 0;
3890e749ef6bSAndrew Gallatin 	int err = 0;
38916b484a49SAndrew Gallatin 	int running, ticks;
38926b484a49SAndrew Gallatin 	uint16_t cmd;
3893dce01b9bSAndrew Gallatin 
38946b484a49SAndrew Gallatin 	ticks = mxge_ticks;
3895*93037a67SJustin Hibbits 	running = if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING;
38966b484a49SAndrew Gallatin 	if (running) {
38971e413cf9SAndrew Gallatin 		if (!sc->watchdog_countdown) {
3898e749ef6bSAndrew Gallatin 			err = mxge_watchdog(sc);
38991e413cf9SAndrew Gallatin 			sc->watchdog_countdown = 4;
39001e413cf9SAndrew Gallatin 		}
39011e413cf9SAndrew Gallatin 		sc->watchdog_countdown--;
39026b484a49SAndrew Gallatin 	}
39036b484a49SAndrew Gallatin 	if (pkts == 0) {
39046b484a49SAndrew Gallatin 		/* ensure NIC did not suffer h/w fault while idle */
39056b484a49SAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
39066b484a49SAndrew Gallatin 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
39076b484a49SAndrew Gallatin 			sc->dying = 2;
39086b484a49SAndrew Gallatin 			taskqueue_enqueue(sc->tq, &sc->watchdog_task);
39096b484a49SAndrew Gallatin 			err = ENXIO;
39106b484a49SAndrew Gallatin 		}
39116b484a49SAndrew Gallatin 		/* look less often if NIC is idle */
39126b484a49SAndrew Gallatin 		ticks *= 4;
39136b484a49SAndrew Gallatin 	}
39146b484a49SAndrew Gallatin 
3915e749ef6bSAndrew Gallatin 	if (err == 0)
39166b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
3917e749ef6bSAndrew Gallatin 
3918dce01b9bSAndrew Gallatin }
3919b2fc195eSAndrew Gallatin 
3920b2fc195eSAndrew Gallatin static int
3921*93037a67SJustin Hibbits mxge_media_change(if_t ifp)
3922b2fc195eSAndrew Gallatin {
3923b2fc195eSAndrew Gallatin 	return EINVAL;
3924b2fc195eSAndrew Gallatin }
3925b2fc195eSAndrew Gallatin 
3926b2fc195eSAndrew Gallatin static int
39276d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3928b2fc195eSAndrew Gallatin {
3929*93037a67SJustin Hibbits 	if_t ifp = sc->ifp;
3930b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3931b2fc195eSAndrew Gallatin 	int err = 0;
3932b2fc195eSAndrew Gallatin 
3933c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3934053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3935b2fc195eSAndrew Gallatin 		return EINVAL;
3936a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3937*93037a67SJustin Hibbits 	old_mtu = if_getmtu(ifp);
3938*93037a67SJustin Hibbits 	if_setmtu(ifp, mtu);
3939*93037a67SJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
3940a393336bSAndrew Gallatin 		mxge_close(sc, 0);
39416d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3942b2fc195eSAndrew Gallatin 		if (err != 0) {
3943*93037a67SJustin Hibbits 			if_setmtu(ifp, old_mtu);
3944a393336bSAndrew Gallatin 			mxge_close(sc, 0);
39456d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3946b2fc195eSAndrew Gallatin 		}
3947b2fc195eSAndrew Gallatin 	}
3948a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3949b2fc195eSAndrew Gallatin 	return err;
3950b2fc195eSAndrew Gallatin }
3951b2fc195eSAndrew Gallatin 
3952b2fc195eSAndrew Gallatin static void
3953*93037a67SJustin Hibbits mxge_media_status(if_t ifp, struct ifmediareq *ifmr)
3954b2fc195eSAndrew Gallatin {
3955*93037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
3956b2fc195eSAndrew Gallatin 
3957b2fc195eSAndrew Gallatin 	if (sc == NULL)
3958b2fc195eSAndrew Gallatin 		return;
3959b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3960c406ad2eSAndrew Gallatin 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
3961c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3962c406ad2eSAndrew Gallatin 	ifmr->ifm_active |= sc->current_media;
3963b2fc195eSAndrew Gallatin }
3964b2fc195eSAndrew Gallatin 
3965b2fc195eSAndrew Gallatin static int
3966df131e84SAndrew Gallatin mxge_fetch_i2c(mxge_softc_t *sc, struct ifi2creq *i2c)
3967df131e84SAndrew Gallatin {
3968df131e84SAndrew Gallatin 	mxge_cmd_t cmd;
3969df131e84SAndrew Gallatin 	uint32_t i2c_args;
3970df131e84SAndrew Gallatin 	int i, ms, err;
3971df131e84SAndrew Gallatin 
3972df131e84SAndrew Gallatin 	if (i2c->dev_addr != 0xA0 &&
3973df131e84SAndrew Gallatin 	    i2c->dev_addr != 0xA2)
3974df131e84SAndrew Gallatin 		return (EINVAL);
3975df131e84SAndrew Gallatin 	if (i2c->len > sizeof(i2c->data))
3976df131e84SAndrew Gallatin 		return (EINVAL);
3977df131e84SAndrew Gallatin 
3978df131e84SAndrew Gallatin 	for (i = 0; i < i2c->len; i++) {
3979df131e84SAndrew Gallatin 		i2c_args = i2c->dev_addr << 0x8;
3980df131e84SAndrew Gallatin 		i2c_args |= i2c->offset + i;
3981df131e84SAndrew Gallatin 		cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
3982df131e84SAndrew Gallatin 		cmd.data1 = i2c_args;
3983df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
3984df131e84SAndrew Gallatin 
3985df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
3986df131e84SAndrew Gallatin 			return (EIO);
3987df131e84SAndrew Gallatin 		/* now we wait for the data to be cached */
3988df131e84SAndrew Gallatin 		cmd.data0 = i2c_args & 0xff;
3989df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3990df131e84SAndrew Gallatin 		for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
3991df131e84SAndrew Gallatin 			cmd.data0 = i2c_args & 0xff;
3992df131e84SAndrew Gallatin 			err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3993df131e84SAndrew Gallatin 			if (err == EBUSY)
3994df131e84SAndrew Gallatin 				DELAY(1000);
3995df131e84SAndrew Gallatin 		}
3996df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
3997df131e84SAndrew Gallatin 			return (EIO);
3998df131e84SAndrew Gallatin 		i2c->data[i] = cmd.data0;
3999df131e84SAndrew Gallatin 	}
4000df131e84SAndrew Gallatin 	return (0);
4001df131e84SAndrew Gallatin }
4002df131e84SAndrew Gallatin 
4003df131e84SAndrew Gallatin static int
4004*93037a67SJustin Hibbits mxge_ioctl(if_t ifp, u_long command, caddr_t data)
4005b2fc195eSAndrew Gallatin {
4006*93037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
4007b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
4008df131e84SAndrew Gallatin 	struct ifi2creq i2c;
4009b2fc195eSAndrew Gallatin 	int err, mask;
4010b2fc195eSAndrew Gallatin 
4011b2fc195eSAndrew Gallatin 	err = 0;
4012b2fc195eSAndrew Gallatin 	switch (command) {
4013b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
40146d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
4015b2fc195eSAndrew Gallatin 		break;
4016b2fc195eSAndrew Gallatin 
4017b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
4018a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
40198c5d766cSAndrew Gallatin 		if (sc->dying) {
40208c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
40218c5d766cSAndrew Gallatin 			return EINVAL;
40228c5d766cSAndrew Gallatin 		}
4023*93037a67SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
4024*93037a67SJustin Hibbits 			if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) {
40256d87a65dSAndrew Gallatin 				err = mxge_open(sc);
4026dce01b9bSAndrew Gallatin 			} else {
40270fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
40280fa7f681SAndrew Gallatin 				   flag chages */
40290fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
4030*93037a67SJustin Hibbits 						    if_getflags(ifp) & IFF_PROMISC);
40310fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
40320fa7f681SAndrew Gallatin 			}
4033b2fc195eSAndrew Gallatin 		} else {
4034*93037a67SJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
4035a393336bSAndrew Gallatin 				mxge_close(sc, 0);
4036dce01b9bSAndrew Gallatin 			}
4037b2fc195eSAndrew Gallatin 		}
4038a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4039b2fc195eSAndrew Gallatin 		break;
4040b2fc195eSAndrew Gallatin 
4041b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
4042b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
4043a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4044c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4045c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4046c0a1f0afSAndrew Gallatin 			return (EINVAL);
4047c0a1f0afSAndrew Gallatin 		}
40480fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
4049a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4050b2fc195eSAndrew Gallatin 		break;
4051b2fc195eSAndrew Gallatin 
4052b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
4053a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4054*93037a67SJustin Hibbits 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
4055b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
4056*93037a67SJustin Hibbits 			if (IFCAP_TXCSUM & if_getcapenable(ifp)) {
4057cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO4;
4058*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, (IFCAP_TXCSUM|IFCAP_TSO4));
4059*93037a67SJustin Hibbits 				if_sethwassistbits(ifp, 0, (CSUM_TCP | CSUM_UDP));
4060b2fc195eSAndrew Gallatin 			} else {
4061*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TXCSUM, 0);
4062*93037a67SJustin Hibbits 				if_sethwassistbits(ifp, (CSUM_TCP | CSUM_UDP), 0);
4063b2fc195eSAndrew Gallatin 			}
4064cbb9ccf7SRyan Moeller 		}
4065cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM) {
4066*93037a67SJustin Hibbits 			if (IFCAP_RXCSUM & if_getcapenable(ifp)) {
4067*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_RXCSUM);
4068b2fc195eSAndrew Gallatin 			} else {
4069*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_RXCSUM, 0);
4070b2fc195eSAndrew Gallatin 			}
4071b2fc195eSAndrew Gallatin 		}
4072aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
4073*93037a67SJustin Hibbits 			if (IFCAP_TSO4 & if_getcapenable(ifp)) {
4074*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_TSO4);
4075*93037a67SJustin Hibbits 			} else if (IFCAP_TXCSUM & if_getcapenable(ifp)) {
4076*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TSO4, 0);
4077*93037a67SJustin Hibbits 				if_sethwassistbits(ifp, CSUM_TSO, 0);
4078aed8e389SAndrew Gallatin 			} else {
4079aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4080aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4081aed8e389SAndrew Gallatin 				err = EINVAL;
4082aed8e389SAndrew Gallatin 			}
4083aed8e389SAndrew Gallatin 		}
40840a7a780eSAndrew Gallatin #if IFCAP_TSO6
40850a7a780eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM_IPV6) {
4086*93037a67SJustin Hibbits 			if (IFCAP_TXCSUM_IPV6 & if_getcapenable(ifp)) {
4087cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO6;
4088*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0,
4089*93037a67SJustin Hibbits 				    IFCAP_TXCSUM_IPV6 | IFCAP_TSO6);
4090*93037a67SJustin Hibbits 				if_sethwassistbits(ifp, 0,
4091*93037a67SJustin Hibbits 				    CSUM_TCP_IPV6 | CSUM_UDP);
40920a7a780eSAndrew Gallatin 			} else {
4093*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TXCSUM_IPV6, 0);
4094*93037a67SJustin Hibbits 				if_sethwassistbits(ifp,
4095*93037a67SJustin Hibbits 				    CSUM_TCP_IPV6 | CSUM_UDP_IPV6, 0);
40960a7a780eSAndrew Gallatin 			}
4097cbb9ccf7SRyan Moeller 		}
4098cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM_IPV6) {
4099*93037a67SJustin Hibbits 			if (IFCAP_RXCSUM_IPV6 & if_getcapenable(ifp)) {
4100*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_RXCSUM_IPV6);
41010a7a780eSAndrew Gallatin 			} else {
4102*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_RXCSUM_IPV6, 0);
41030a7a780eSAndrew Gallatin 			}
41040a7a780eSAndrew Gallatin 		}
41050a7a780eSAndrew Gallatin 		if (mask & IFCAP_TSO6) {
4106*93037a67SJustin Hibbits 			if (IFCAP_TSO6 & if_getcapenable(ifp)) {
4107*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_TSO6);
4108*93037a67SJustin Hibbits 			} else if (IFCAP_TXCSUM_IPV6 & if_getcapenable(ifp)) {
4109*93037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TSO6, 0);
4110*93037a67SJustin Hibbits 				if_sethwassistbits(ifp, CSUM_TSO, 0);
41110a7a780eSAndrew Gallatin 			} else {
41120a7a780eSAndrew Gallatin 				printf("mxge requires tx checksum offload"
41130a7a780eSAndrew Gallatin 				       " be enabled to use TSO\n");
41140a7a780eSAndrew Gallatin 				err = EINVAL;
41150a7a780eSAndrew Gallatin 			}
41160a7a780eSAndrew Gallatin 		}
41170a7a780eSAndrew Gallatin #endif /*IFCAP_TSO6 */
41180a7a780eSAndrew Gallatin 
411926dd49c6SAndrew Gallatin 		if (mask & IFCAP_LRO)
4120*93037a67SJustin Hibbits 			if_togglecapenable(ifp, IFCAP_LRO);
4121c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
4122*93037a67SJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
41230dce6781SAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTSO)
4124*93037a67SJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_HWTSO);
41250dce6781SAndrew Gallatin 
4126*93037a67SJustin Hibbits 		if (!(if_getcapabilities(ifp) & IFCAP_VLAN_HWTSO) ||
4127*93037a67SJustin Hibbits 		    !(if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING))
4128*93037a67SJustin Hibbits 			if_setcapenablebit(ifp, 0, IFCAP_VLAN_HWTSO);
41290dce6781SAndrew Gallatin 
4130a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4131c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4132c792928fSAndrew Gallatin 
4133b2fc195eSAndrew Gallatin 		break;
4134b2fc195eSAndrew Gallatin 
4135b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4136c406ad2eSAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4137c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4138c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4139c0a1f0afSAndrew Gallatin 			return (EINVAL);
4140c0a1f0afSAndrew Gallatin 		}
4141c406ad2eSAndrew Gallatin 		mxge_media_probe(sc);
4142c406ad2eSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4143b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4144b2fc195eSAndrew Gallatin 				    &sc->media, command);
4145b2fc195eSAndrew Gallatin 		break;
4146b2fc195eSAndrew Gallatin 
4147df131e84SAndrew Gallatin 	case SIOCGI2C:
4148df131e84SAndrew Gallatin 		if (sc->connector != MXGE_XFP &&
4149df131e84SAndrew Gallatin 		    sc->connector != MXGE_SFP) {
4150df131e84SAndrew Gallatin 			err = ENXIO;
4151df131e84SAndrew Gallatin 			break;
4152df131e84SAndrew Gallatin 		}
4153df131e84SAndrew Gallatin 		err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c));
4154df131e84SAndrew Gallatin 		if (err != 0)
4155df131e84SAndrew Gallatin 			break;
4156df131e84SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4157df131e84SAndrew Gallatin 		if (sc->dying) {
4158df131e84SAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4159df131e84SAndrew Gallatin 			return (EINVAL);
4160df131e84SAndrew Gallatin 		}
4161df131e84SAndrew Gallatin 		err = mxge_fetch_i2c(sc, &i2c);
4162df131e84SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4163df131e84SAndrew Gallatin 		if (err == 0)
41641b8b041cSBrooks Davis 			err = copyout(&i2c, ifr_data_get_ptr(ifr),
4165df131e84SAndrew Gallatin 			    sizeof(i2c));
4166df131e84SAndrew Gallatin 		break;
4167b2fc195eSAndrew Gallatin 	default:
4168c756fb6eSRavi Pokala 		err = ether_ioctl(ifp, command, data);
4169c756fb6eSRavi Pokala 		break;
4170b2fc195eSAndrew Gallatin 	}
4171b2fc195eSAndrew Gallatin 	return err;
4172b2fc195eSAndrew Gallatin }
4173b2fc195eSAndrew Gallatin 
4174b2fc195eSAndrew Gallatin static void
41756d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4176b2fc195eSAndrew Gallatin {
4177b2fc195eSAndrew Gallatin 
41781e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
41796d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
41806d87a65dSAndrew Gallatin 			  &mxge_flow_control);
41816d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
41826d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
41836d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
41846d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4185d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4186d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
41875e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
41885e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
41895e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
41905e7d8541SAndrew Gallatin 			  &mxge_verbose);
4191dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
41921e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
41931e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
419494c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4195f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
419665c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4197b2fc195eSAndrew Gallatin 
41985e7d8541SAndrew Gallatin 	if (bootverbose)
41995e7d8541SAndrew Gallatin 		mxge_verbose = 1;
42006d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
42016d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4202dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
42031e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
42046d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
42051e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4206bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
42075769c5efSAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4208b2fc195eSAndrew Gallatin 	}
4209f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4210f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4211f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
421265c69066SAndrew Gallatin 
421365c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
421465c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
421565c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
421665c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
421765c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
42181e413cf9SAndrew Gallatin }
42191e413cf9SAndrew Gallatin 
42201e413cf9SAndrew Gallatin static void
42211e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
42221e413cf9SAndrew Gallatin {
42231e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
42241e413cf9SAndrew Gallatin 	int i;
42251e413cf9SAndrew Gallatin 
42261e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
42271e413cf9SAndrew Gallatin 		return;
42281e413cf9SAndrew Gallatin 
42291e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42301e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
42311e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
42321e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
42331e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4234c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4235c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4236c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4237c6cb3e3fSAndrew Gallatin 			}
42381e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
42391e413cf9SAndrew Gallatin 		}
42401e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
42411e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
42421e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
42431e413cf9SAndrew Gallatin 		}
42441e413cf9SAndrew Gallatin 	}
42451e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
42461e413cf9SAndrew Gallatin 	sc->ss = NULL;
42471e413cf9SAndrew Gallatin }
42481e413cf9SAndrew Gallatin 
42491e413cf9SAndrew Gallatin static int
42501e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
42511e413cf9SAndrew Gallatin {
42521e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
42531e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
42541e413cf9SAndrew Gallatin 	size_t bytes;
42551e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
42561e413cf9SAndrew Gallatin 
42571e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
42581e413cf9SAndrew Gallatin 	if (err != 0) {
42591e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
42601e413cf9SAndrew Gallatin 		return err;
42611e413cf9SAndrew Gallatin 	}
42621e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
42631e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
42641e413cf9SAndrew Gallatin 
4265ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->ss) * sc->num_slices;
4266ac2fffa4SPedro F. Giffuni 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
42671e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
42681e413cf9SAndrew Gallatin 		return (ENOMEM);
42691e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42701e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
42711e413cf9SAndrew Gallatin 
42721e413cf9SAndrew Gallatin 		ss->sc = sc;
42731e413cf9SAndrew Gallatin 
42741e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
42751e413cf9SAndrew Gallatin 
42761e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
42771e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
42781e413cf9SAndrew Gallatin 		if (err != 0)
42791e413cf9SAndrew Gallatin 			goto abort;
42801e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
42811e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
42821e413cf9SAndrew Gallatin 
42831e413cf9SAndrew Gallatin 		/*
42841e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
42851e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
42861e413cf9SAndrew Gallatin 		 * slice for now
42871e413cf9SAndrew Gallatin 		 */
42881e413cf9SAndrew Gallatin 
42891e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
42901e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
42911e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
42921e413cf9SAndrew Gallatin 		if (err != 0)
42931e413cf9SAndrew Gallatin 			goto abort;
42941e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
42951e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
42961e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
42971e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4298c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4299c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
43001e413cf9SAndrew Gallatin 	}
43011e413cf9SAndrew Gallatin 
43021e413cf9SAndrew Gallatin 	return (0);
43031e413cf9SAndrew Gallatin 
43041e413cf9SAndrew Gallatin abort:
43051e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
43061e413cf9SAndrew Gallatin 	return (ENOMEM);
43071e413cf9SAndrew Gallatin }
43081e413cf9SAndrew Gallatin 
43091e413cf9SAndrew Gallatin static void
43101e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
43111e413cf9SAndrew Gallatin {
43121e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
43131e413cf9SAndrew Gallatin 	char *old_fw;
43141e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
43151e413cf9SAndrew Gallatin 
43161e413cf9SAndrew Gallatin 	sc->num_slices = 1;
43171e413cf9SAndrew Gallatin 	/*
43181e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
43191e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
43201e413cf9SAndrew Gallatin 	 */
43211e413cf9SAndrew Gallatin 
43221e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
43231e413cf9SAndrew Gallatin 		return;
43241e413cf9SAndrew Gallatin 
43251e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
43261e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
43271e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
43281e413cf9SAndrew Gallatin 		return;
43291e413cf9SAndrew Gallatin 
43301e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
43311e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
43321e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
43331e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
43341e413cf9SAndrew Gallatin 	else
43351e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
43361e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
43371e413cf9SAndrew Gallatin 	if (status != 0) {
43381e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
43391e413cf9SAndrew Gallatin 		return;
43401e413cf9SAndrew Gallatin 	}
43411e413cf9SAndrew Gallatin 
43421e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
43431e413cf9SAndrew Gallatin 	   is alive */
43441e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
43451e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
43461e413cf9SAndrew Gallatin 	if (status != 0) {
43471e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
43481e413cf9SAndrew Gallatin 		goto abort_with_fw;
43491e413cf9SAndrew Gallatin 	}
43501e413cf9SAndrew Gallatin 
43511e413cf9SAndrew Gallatin 	/* get rx ring size */
43521e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
43531e413cf9SAndrew Gallatin 	if (status != 0) {
43541e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
43551e413cf9SAndrew Gallatin 		goto abort_with_fw;
43561e413cf9SAndrew Gallatin 	}
43571e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
43581e413cf9SAndrew Gallatin 
43591e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
43601e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
43611e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
43621e413cf9SAndrew Gallatin 	if (status != 0) {
43631e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
43641e413cf9SAndrew Gallatin 		goto abort_with_fw;
43651e413cf9SAndrew Gallatin 	}
43661e413cf9SAndrew Gallatin 
43671e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
43681e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
43691e413cf9SAndrew Gallatin 	if (status != 0) {
43701e413cf9SAndrew Gallatin 		device_printf(sc->dev,
43711e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
43721e413cf9SAndrew Gallatin 		goto abort_with_fw;
43731e413cf9SAndrew Gallatin 	}
43741e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
43751e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
43761e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
43771e413cf9SAndrew Gallatin 
43781e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
43791e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
43801e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
43811e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
43821e413cf9SAndrew Gallatin 	} else {
43831e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
43841e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
43851e413cf9SAndrew Gallatin 	}
43861e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
43871e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
43881e413cf9SAndrew Gallatin 		sc->num_slices--;
43891e413cf9SAndrew Gallatin 
43901e413cf9SAndrew Gallatin 	if (mxge_verbose)
43911e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
43921e413cf9SAndrew Gallatin 			      sc->num_slices);
43931e413cf9SAndrew Gallatin 
43941e413cf9SAndrew Gallatin 	return;
43951e413cf9SAndrew Gallatin 
43961e413cf9SAndrew Gallatin abort_with_fw:
43971e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
43981e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
43991e413cf9SAndrew Gallatin }
44001e413cf9SAndrew Gallatin 
44011e413cf9SAndrew Gallatin static int
44021e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
44031e413cf9SAndrew Gallatin {
4404ac2fffa4SPedro F. Giffuni 	size_t bytes;
44051e413cf9SAndrew Gallatin 	int count, err, i, rid;
44061e413cf9SAndrew Gallatin 
44071e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
44081e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
44091e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
44101e413cf9SAndrew Gallatin 
44111e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
44121e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
44131e413cf9SAndrew Gallatin 		return ENXIO;
44141e413cf9SAndrew Gallatin 	}
44151e413cf9SAndrew Gallatin 
44161e413cf9SAndrew Gallatin 	count = sc->num_slices;
44171e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
44181e413cf9SAndrew Gallatin 	if (err != 0) {
44191e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
44201e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
44211e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
44221e413cf9SAndrew Gallatin 	}
44231e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
44241e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
44251e413cf9SAndrew Gallatin 			      count, sc->num_slices);
44261e413cf9SAndrew Gallatin 		device_printf(sc->dev,
44271e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
44281e413cf9SAndrew Gallatin 			      count);
44291e413cf9SAndrew Gallatin 		err = ENOSPC;
44301e413cf9SAndrew Gallatin 		goto abort_with_msix;
44311e413cf9SAndrew Gallatin 	}
4432ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
4433ac2fffa4SPedro F. Giffuni 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44341e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
44351e413cf9SAndrew Gallatin 		err = ENOMEM;
44361e413cf9SAndrew Gallatin 		goto abort_with_msix;
44371e413cf9SAndrew Gallatin 	}
44381e413cf9SAndrew Gallatin 
44391e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44401e413cf9SAndrew Gallatin 		rid = i + 1;
44411e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
44421e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
44431e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
44441e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
44451e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
44461e413cf9SAndrew Gallatin 				      " for message %d\n", i);
44471e413cf9SAndrew Gallatin 			err = ENXIO;
44481e413cf9SAndrew Gallatin 			goto abort_with_res;
44491e413cf9SAndrew Gallatin 		}
44501e413cf9SAndrew Gallatin 	}
44511e413cf9SAndrew Gallatin 
4452ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
4453ac2fffa4SPedro F. Giffuni 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44541e413cf9SAndrew Gallatin 
44551e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44561e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
4457c98b837aSElliott Mitchell 				     INTR_TYPE_NET | INTR_MPSAFE, NULL,
445837d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
44591e413cf9SAndrew Gallatin 		if (err != 0) {
44601e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
44611e413cf9SAndrew Gallatin 				      "message %d\n", i);
44621e413cf9SAndrew Gallatin 			goto abort_with_intr;
44631e413cf9SAndrew Gallatin 		}
446421089137SAndrew Gallatin 		bus_describe_intr(sc->dev, sc->msix_irq_res[i],
446521089137SAndrew Gallatin 				  sc->msix_ih[i], "s%d", i);
44661e413cf9SAndrew Gallatin 	}
44671e413cf9SAndrew Gallatin 
44681e413cf9SAndrew Gallatin 	if (mxge_verbose) {
44691e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
44701e413cf9SAndrew Gallatin 			      sc->num_slices);
44711e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
4472da1b038aSJustin Hibbits 			printf(" %jd", rman_get_start(sc->msix_irq_res[i]));
44731e413cf9SAndrew Gallatin 		printf("\n");
44741e413cf9SAndrew Gallatin 	}
44751e413cf9SAndrew Gallatin 	return (0);
44761e413cf9SAndrew Gallatin 
44771e413cf9SAndrew Gallatin abort_with_intr:
44781e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44791e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
44801e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
44811e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
44821e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
44831e413cf9SAndrew Gallatin 		}
44841e413cf9SAndrew Gallatin 	}
44851e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
44861e413cf9SAndrew Gallatin 
44871e413cf9SAndrew Gallatin abort_with_res:
44881e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44891e413cf9SAndrew Gallatin 		rid = i + 1;
44901e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
44911e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
44921e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
44931e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
44941e413cf9SAndrew Gallatin 	}
44951e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
44961e413cf9SAndrew Gallatin 
44971e413cf9SAndrew Gallatin abort_with_msix:
44981e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
44991e413cf9SAndrew Gallatin 
45001e413cf9SAndrew Gallatin abort_with_msix_table:
45011e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
45021e413cf9SAndrew Gallatin 			     sc->msix_table_res);
45031e413cf9SAndrew Gallatin 
45041e413cf9SAndrew Gallatin 	return err;
45051e413cf9SAndrew Gallatin }
45061e413cf9SAndrew Gallatin 
45071e413cf9SAndrew Gallatin static int
45081e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
45091e413cf9SAndrew Gallatin {
45101e413cf9SAndrew Gallatin 	int count, err, rid;
45111e413cf9SAndrew Gallatin 
45121e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
45131e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
45141e413cf9SAndrew Gallatin 		rid = 1;
45151e413cf9SAndrew Gallatin 	} else {
45161e413cf9SAndrew Gallatin 		rid = 0;
451791ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
45181e413cf9SAndrew Gallatin 	}
451943cd6160SJustin Hibbits 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
452043cd6160SJustin Hibbits 					     RF_SHAREABLE | RF_ACTIVE);
45211e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
45221e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
45231e413cf9SAndrew Gallatin 		return ENXIO;
45241e413cf9SAndrew Gallatin 	}
45251e413cf9SAndrew Gallatin 	if (mxge_verbose)
4526da1b038aSJustin Hibbits 		device_printf(sc->dev, "using %s irq %jd\n",
452791ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
45281e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
45291e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
4530c98b837aSElliott Mitchell 			     INTR_TYPE_NET | INTR_MPSAFE, NULL,
453137d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
45321e413cf9SAndrew Gallatin 	if (err != 0) {
45331e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
453491ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
453591ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
45361e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
45371e413cf9SAndrew Gallatin 	}
45381e413cf9SAndrew Gallatin 	return err;
45391e413cf9SAndrew Gallatin }
45401e413cf9SAndrew Gallatin 
45411e413cf9SAndrew Gallatin static void
45421e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
45431e413cf9SAndrew Gallatin {
45441e413cf9SAndrew Gallatin 	int i, rid;
45451e413cf9SAndrew Gallatin 
45461e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45471e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
45481e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
45491e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
45501e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
45511e413cf9SAndrew Gallatin 		}
45521e413cf9SAndrew Gallatin 	}
45531e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
45541e413cf9SAndrew Gallatin 
45551e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45561e413cf9SAndrew Gallatin 		rid = i + 1;
45571e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
45581e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
45591e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
45601e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
45611e413cf9SAndrew Gallatin 	}
45621e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
45631e413cf9SAndrew Gallatin 
45641e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
45651e413cf9SAndrew Gallatin 			     sc->msix_table_res);
45661e413cf9SAndrew Gallatin 
45671e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
45681e413cf9SAndrew Gallatin 	return;
45691e413cf9SAndrew Gallatin }
45701e413cf9SAndrew Gallatin 
45711e413cf9SAndrew Gallatin static void
45721e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
45731e413cf9SAndrew Gallatin {
45741e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
45751e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
457691ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
457791ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
45781e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
45791e413cf9SAndrew Gallatin }
45801e413cf9SAndrew Gallatin 
45811e413cf9SAndrew Gallatin static void
45821e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
45831e413cf9SAndrew Gallatin {
45841e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
45851e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
45861e413cf9SAndrew Gallatin 	else
45871e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
45881e413cf9SAndrew Gallatin }
45891e413cf9SAndrew Gallatin 
45901e413cf9SAndrew Gallatin static int
45911e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
45921e413cf9SAndrew Gallatin {
45931e413cf9SAndrew Gallatin 	int err;
45941e413cf9SAndrew Gallatin 
45951e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
45961e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
45971e413cf9SAndrew Gallatin 	else
45981e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
45991e413cf9SAndrew Gallatin 
46001e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
46011e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
46021e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
46031e413cf9SAndrew Gallatin 	}
46041e413cf9SAndrew Gallatin 	return err;
46051e413cf9SAndrew Gallatin }
46061e413cf9SAndrew Gallatin 
4607b2fc195eSAndrew Gallatin static int
46086d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4609b2fc195eSAndrew Gallatin {
46100a7a780eSAndrew Gallatin 	mxge_cmd_t cmd;
46116d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4612*93037a67SJustin Hibbits 	if_t ifp;
46131e413cf9SAndrew Gallatin 	int err, rid;
4614b2fc195eSAndrew Gallatin 
4615b2fc195eSAndrew Gallatin 	sc->dev = dev;
46166d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4617b2fc195eSAndrew Gallatin 
461872c042dfSAndrew Gallatin 	TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
4619a4a75d67SJohn Baldwin 	sc->tq = taskqueue_create("mxge_taskq", M_WAITOK,
4620a4a75d67SJohn Baldwin 				  taskqueue_thread_enqueue, &sc->tq);
462172c042dfSAndrew Gallatin 	if (sc->tq == NULL) {
462272c042dfSAndrew Gallatin 		err = ENOMEM;
462372c042dfSAndrew Gallatin 		goto abort_with_nothing;
462472c042dfSAndrew Gallatin 	}
462572c042dfSAndrew Gallatin 
462662ce43ccSScott Long 	err = bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
4627b2fc195eSAndrew Gallatin 				 1,			/* alignment */
46281e413cf9SAndrew Gallatin 				 0,			/* boundary */
4629b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4630b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4631b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4632aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
46335e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
46341e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4635b2fc195eSAndrew Gallatin 				 0,			/* flags */
4636b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4637b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4638b2fc195eSAndrew Gallatin 
4639b2fc195eSAndrew Gallatin 	if (err != 0) {
4640b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4641b2fc195eSAndrew Gallatin 			      err);
464272c042dfSAndrew Gallatin 		goto abort_with_tq;
4643b2fc195eSAndrew Gallatin 	}
4644b2fc195eSAndrew Gallatin 
4645b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4646b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4647b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4648b2fc195eSAndrew Gallatin 		err = ENOSPC;
4649b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4650b2fc195eSAndrew Gallatin 	}
46511e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
46521e413cf9SAndrew Gallatin 
4653a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4654a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4655a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4656a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4657a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4658a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4659b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4660b2fc195eSAndrew Gallatin 
4661dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4662d91b1b49SAndrew Gallatin 
4663dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4664b2fc195eSAndrew Gallatin 
4665b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4666b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
466743cd6160SJustin Hibbits 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
466843cd6160SJustin Hibbits 					     RF_ACTIVE);
4669b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4670b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4671b2fc195eSAndrew Gallatin 		err = ENXIO;
4672b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4673b2fc195eSAndrew Gallatin 	}
4674b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4675b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4676b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4677da1b038aSJustin Hibbits 		device_printf(dev, "impossible memory region size %jd\n",
4678b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4679b2fc195eSAndrew Gallatin 		err = ENXIO;
4680b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4681b2fc195eSAndrew Gallatin 	}
4682b2fc195eSAndrew Gallatin 
4683b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4684b2fc195eSAndrew Gallatin 	   lanai SRAM */
46856d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4686b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4687b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
46886d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4689b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
46906d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
46916d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4692b2fc195eSAndrew Gallatin 	if (err != 0)
4693b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4694b2fc195eSAndrew Gallatin 
4695b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
46966d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4697b2fc195eSAndrew Gallatin 
4698b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
46996d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
47006d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4701b2fc195eSAndrew Gallatin 	if (err != 0)
4702b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4703b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
47046d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4705b2fc195eSAndrew Gallatin 	if (err != 0)
4706b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4707b2fc195eSAndrew Gallatin 
4708a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4709a98d6cd7SAndrew Gallatin 	if (err != 0)
47101e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4711b2fc195eSAndrew Gallatin 
47128fe615baSAndrew Gallatin 	/* select & load the firmware */
47138fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4714b2fc195eSAndrew Gallatin 	if (err != 0)
47151e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
47165e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
47171e413cf9SAndrew Gallatin 
47181e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
47191e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
47201e413cf9SAndrew Gallatin 	if (err != 0)
47211e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
47221e413cf9SAndrew Gallatin 
4723adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4724b2fc195eSAndrew Gallatin 	if (err != 0)
47251e413cf9SAndrew Gallatin 		goto abort_with_slices;
4726b2fc195eSAndrew Gallatin 
4727a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4728a98d6cd7SAndrew Gallatin 	if (err != 0) {
4729a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
47302e084798SAndrew Gallatin 		goto abort_with_slices;
4731a98d6cd7SAndrew Gallatin 	}
4732a98d6cd7SAndrew Gallatin 
47331e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4734a98d6cd7SAndrew Gallatin 	if (err != 0) {
47351e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4736a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4737a98d6cd7SAndrew Gallatin 	}
47381e413cf9SAndrew Gallatin 
4739*93037a67SJustin Hibbits 	if_setbaudrate(ifp, IF_Gbps(10));
4740*93037a67SJustin Hibbits 	if_setcapabilities(ifp, IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
474126dd49c6SAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LINKSTATE | IFCAP_TXCSUM_IPV6 |
4742*93037a67SJustin Hibbits 		IFCAP_RXCSUM_IPV6);
474326dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
4744*93037a67SJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_LRO, 0);
4745eb6219e3SAndrew Gallatin #endif
474637d89b0cSAndrew Gallatin 
474737d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
4748*93037a67SJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM, 0);
47490dce6781SAndrew Gallatin 
47500dce6781SAndrew Gallatin 	/* Only FW 1.4.32 and newer can do TSO over vlans */
47510dce6781SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
47520dce6781SAndrew Gallatin 	    sc->fw_ver_tiny >= 32)
4753*93037a67SJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTSO, 0);
475437d89b0cSAndrew Gallatin #endif
4755053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4756053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4757*93037a67SJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_JUMBO_MTU, 0);
4758053e637fSAndrew Gallatin 	else
4759053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4760adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4761053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4762*93037a67SJustin Hibbits 	if_sethwassist(ifp, CSUM_TCP | CSUM_UDP | CSUM_TSO);
4763*93037a67SJustin Hibbits 	if_sethwassistbits(ifp, CSUM_TCP_IPV6 | CSUM_UDP_IPV6, 0);
47640a7a780eSAndrew Gallatin 	/* check to see if f/w supports TSO for IPv6 */
47650a7a780eSAndrew Gallatin 	if (!mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd)) {
47660a7a780eSAndrew Gallatin 		if (CSUM_TCP_IPV6)
4767*93037a67SJustin Hibbits 			if_setcapabilitiesbit(ifp, IFCAP_TSO6, 0);
47680a7a780eSAndrew Gallatin 		sc->max_tso6_hlen = min(cmd.data0,
47690a7a780eSAndrew Gallatin 					sizeof (sc->ss[0].scratch));
47700a7a780eSAndrew Gallatin 	}
4771*93037a67SJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
4772f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4773*93037a67SJustin Hibbits 		if_setcapenablebit(ifp, 0, IFCAP_LRO);
4774*93037a67SJustin Hibbits 	if_setinitfn(ifp, mxge_init);
4775*93037a67SJustin Hibbits 	if_setsoftc(ifp, sc);
4776*93037a67SJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
4777*93037a67SJustin Hibbits 	if_setioctlfn(ifp, mxge_ioctl);
4778*93037a67SJustin Hibbits 	if_setstartfn(ifp, mxge_start);
4779*93037a67SJustin Hibbits 	if_setgetcounterfn(ifp, mxge_get_counter);
4780*93037a67SJustin Hibbits 	if_sethwtsomax(ifp, IP_MAXPACKET - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN));
4781*93037a67SJustin Hibbits 	if_sethwtsomaxsegcount(ifp, sc->ss[0].tx.max_desc);
4782*93037a67SJustin Hibbits 	if_sethwtsomaxsegsize(ifp, IP_MAXPACKET);
4783c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4784c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4785c587e59fSAndrew Gallatin 		     mxge_media_status);
4786c406ad2eSAndrew Gallatin 	mxge_media_init(sc);
4787c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
47888c5d766cSAndrew Gallatin 	sc->dying = 0;
4789b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4790f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4791f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4792f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4793b2fc195eSAndrew Gallatin 
47946d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4795*93037a67SJustin Hibbits 	if_settransmitfn(ifp, mxge_transmit);
4796*93037a67SJustin Hibbits 	if_setqflushfn(ifp, mxge_qflush);
47972e084798SAndrew Gallatin 	taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
47982e084798SAndrew Gallatin 				device_get_nameunit(sc->dev));
47996b484a49SAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4800b2fc195eSAndrew Gallatin 	return 0;
4801b2fc195eSAndrew Gallatin 
4802a98d6cd7SAndrew Gallatin abort_with_rings:
4803a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
48041e413cf9SAndrew Gallatin abort_with_slices:
48051e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4806a98d6cd7SAndrew Gallatin abort_with_dmabench:
4807a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4808b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
48096d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4810b2fc195eSAndrew Gallatin abort_with_cmd_dma:
48116d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4812b2fc195eSAndrew Gallatin abort_with_mem_res:
4813b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4814b2fc195eSAndrew Gallatin abort_with_lock:
4815b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4816a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4817a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4818b2fc195eSAndrew Gallatin 	if_free(ifp);
4819b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4820b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
482172c042dfSAndrew Gallatin abort_with_tq:
482272c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
482372c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
482472c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
482572c042dfSAndrew Gallatin 		sc->tq = NULL;
482672c042dfSAndrew Gallatin 	}
4827b2fc195eSAndrew Gallatin abort_with_nothing:
4828b2fc195eSAndrew Gallatin 	return err;
4829b2fc195eSAndrew Gallatin }
4830b2fc195eSAndrew Gallatin 
4831b2fc195eSAndrew Gallatin static int
48326d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4833b2fc195eSAndrew Gallatin {
48346d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4835b2fc195eSAndrew Gallatin 
483637d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4837c792928fSAndrew Gallatin 		device_printf(sc->dev,
4838c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4839c792928fSAndrew Gallatin 		return EBUSY;
4840c792928fSAndrew Gallatin 	}
4841a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
48428c5d766cSAndrew Gallatin 	sc->dying = 1;
4843*93037a67SJustin Hibbits 	if (if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING)
4844a393336bSAndrew Gallatin 		mxge_close(sc, 0);
4845a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4846b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
484772c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
484872c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
484972c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
485072c042dfSAndrew Gallatin 		sc->tq = NULL;
485172c042dfSAndrew Gallatin 	}
4852e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4853dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4854091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
48551e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
48561e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4857a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
48581e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4859a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
48606d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
48616d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4862b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4863b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4864a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4865a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4866b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4867b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4868b2fc195eSAndrew Gallatin 	return 0;
4869b2fc195eSAndrew Gallatin }
4870b2fc195eSAndrew Gallatin 
4871b2fc195eSAndrew Gallatin static int
48726d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4873b2fc195eSAndrew Gallatin {
4874b2fc195eSAndrew Gallatin 	return 0;
4875b2fc195eSAndrew Gallatin }
4876b2fc195eSAndrew Gallatin 
4877b2fc195eSAndrew Gallatin /*
4878b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4879b2fc195eSAndrew Gallatin 
4880b2fc195eSAndrew Gallatin   Local Variables:
4881b2fc195eSAndrew Gallatin   c-file-style:"linux"
4882b2fc195eSAndrew Gallatin   tab-width:8
4883b2fc195eSAndrew Gallatin   End:
4884b2fc195eSAndrew Gallatin */
4885