xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 0dce6781aeea4d179bb92ed8dd73fb364b21a25f)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
301638550SAndrew Gallatin Copyright (c) 2006-2009, Myricom Inc.
4b2fc195eSAndrew Gallatin All rights reserved.
5b2fc195eSAndrew Gallatin 
6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
8b2fc195eSAndrew Gallatin 
9b2fc195eSAndrew Gallatin  1. Redistributions of source code must retain the above copyright notice,
10b2fc195eSAndrew Gallatin     this list of conditions and the following disclaimer.
11b2fc195eSAndrew Gallatin 
12eb8e82f5SAndrew Gallatin  2. Neither the name of the Myricom Inc, nor the names of its
13b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
14b2fc195eSAndrew Gallatin     this software without specific prior written permission.
15b2fc195eSAndrew Gallatin 
16b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
27b2fc195eSAndrew Gallatin 
28b2fc195eSAndrew Gallatin ***************************************************************************/
29b2fc195eSAndrew Gallatin 
30b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
31b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
32b2fc195eSAndrew Gallatin 
33b2fc195eSAndrew Gallatin #include <sys/param.h>
34b2fc195eSAndrew Gallatin #include <sys/systm.h>
35b2fc195eSAndrew Gallatin #include <sys/linker.h>
36b2fc195eSAndrew Gallatin #include <sys/firmware.h>
37b2fc195eSAndrew Gallatin #include <sys/endian.h>
38b2fc195eSAndrew Gallatin #include <sys/sockio.h>
39b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
40b2fc195eSAndrew Gallatin #include <sys/malloc.h>
41b2fc195eSAndrew Gallatin #include <sys/kdb.h>
42b2fc195eSAndrew Gallatin #include <sys/kernel.h>
434e7f640dSJohn Baldwin #include <sys/lock.h>
44b2fc195eSAndrew Gallatin #include <sys/module.h>
45b2fc195eSAndrew Gallatin #include <sys/socket.h>
46b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
47b2fc195eSAndrew Gallatin #include <sys/sx.h>
4872c042dfSAndrew Gallatin #include <sys/taskqueue.h>
49b2fc195eSAndrew Gallatin 
5071032832SAndrew Gallatin /* count xmits ourselves, rather than via drbr */
5171032832SAndrew Gallatin #define NO_SLOW_STATS
52b2fc195eSAndrew Gallatin #include <net/if.h>
53b2fc195eSAndrew Gallatin #include <net/if_arp.h>
54b2fc195eSAndrew Gallatin #include <net/ethernet.h>
55b2fc195eSAndrew Gallatin #include <net/if_dl.h>
56b2fc195eSAndrew Gallatin #include <net/if_media.h>
57b2fc195eSAndrew Gallatin 
58b2fc195eSAndrew Gallatin #include <net/bpf.h>
59b2fc195eSAndrew Gallatin 
60b2fc195eSAndrew Gallatin #include <net/if_types.h>
61b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
62b2fc195eSAndrew Gallatin #include <net/zlib.h>
63b2fc195eSAndrew Gallatin 
64b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
65b2fc195eSAndrew Gallatin #include <netinet/in.h>
66b2fc195eSAndrew Gallatin #include <netinet/ip.h>
67aed8e389SAndrew Gallatin #include <netinet/tcp.h>
68b2fc195eSAndrew Gallatin 
69b2fc195eSAndrew Gallatin #include <machine/bus.h>
70053e637fSAndrew Gallatin #include <machine/in_cksum.h>
71b2fc195eSAndrew Gallatin #include <machine/resource.h>
72b2fc195eSAndrew Gallatin #include <sys/bus.h>
73b2fc195eSAndrew Gallatin #include <sys/rman.h>
741e413cf9SAndrew Gallatin #include <sys/smp.h>
75b2fc195eSAndrew Gallatin 
76b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
77b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
78e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
79b2fc195eSAndrew Gallatin 
80b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
81b2fc195eSAndrew Gallatin #include <vm/pmap.h>
82b2fc195eSAndrew Gallatin 
83c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
84c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
85c2c14a69SAndrew Gallatin #endif
86c2c14a69SAndrew Gallatin 
876d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
886d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
891e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
906d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
91869c7348SAndrew Gallatin #ifdef IFNET_BUF_RING
92869c7348SAndrew Gallatin #include <sys/buf_ring.h>
93869c7348SAndrew Gallatin #endif
94b2fc195eSAndrew Gallatin 
95eb6219e3SAndrew Gallatin #include "opt_inet.h"
96eb6219e3SAndrew Gallatin 
97b2fc195eSAndrew Gallatin /* tunable params */
986d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
99d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
1006d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1015e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1026d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1035e7d8541SAndrew Gallatin static int mxge_verbose = 0;
104f04b33f8SAndrew Gallatin static int mxge_lro_cnt = 8;
105dce01b9bSAndrew Gallatin static int mxge_ticks;
1061e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1075769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
1081e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
109f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
11065c69066SAndrew Gallatin static int mxge_throttle = 0;
1116d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1126d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1131e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1141e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
115b2fc195eSAndrew Gallatin 
1166d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1176d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1186d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1196d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1206d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
121b2fc195eSAndrew Gallatin 
1226d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
123b2fc195eSAndrew Gallatin {
124b2fc195eSAndrew Gallatin   /* Device interface */
1256d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1266d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1276d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1286d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
129b2fc195eSAndrew Gallatin   {0, 0}
130b2fc195eSAndrew Gallatin };
131b2fc195eSAndrew Gallatin 
1326d87a65dSAndrew Gallatin static driver_t mxge_driver =
133b2fc195eSAndrew Gallatin {
1346d87a65dSAndrew Gallatin   "mxge",
1356d87a65dSAndrew Gallatin   mxge_methods,
1366d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
137b2fc195eSAndrew Gallatin };
138b2fc195eSAndrew Gallatin 
1396d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
140b2fc195eSAndrew Gallatin 
141b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1426d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1436d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
144f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
145b2fc195eSAndrew Gallatin 
1461e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1478fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
148a393336bSAndrew Gallatin static int mxge_close(mxge_softc_t *sc, int down);
149276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
150276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1518fe615baSAndrew Gallatin 
152b2fc195eSAndrew Gallatin static int
1536d87a65dSAndrew Gallatin mxge_probe(device_t dev)
154b2fc195eSAndrew Gallatin {
15501638550SAndrew Gallatin 	int rev;
15601638550SAndrew Gallatin 
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 
200b2fc195eSAndrew Gallatin /* callback to get our DMA address */
201b2fc195eSAndrew Gallatin static void
2026d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
203b2fc195eSAndrew Gallatin 			 int error)
204b2fc195eSAndrew Gallatin {
205b2fc195eSAndrew Gallatin 	if (error == 0) {
206b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
207b2fc195eSAndrew Gallatin 	}
208b2fc195eSAndrew Gallatin }
209b2fc195eSAndrew Gallatin 
210b2fc195eSAndrew Gallatin static int
2116d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
212b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
213b2fc195eSAndrew Gallatin {
214b2fc195eSAndrew Gallatin 	int err;
215b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2161e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2171e413cf9SAndrew Gallatin 
2181e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2191e413cf9SAndrew Gallatin 		boundary = 0;
2201e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2211e413cf9SAndrew Gallatin 	} else {
2221e413cf9SAndrew Gallatin 		boundary = 4096;
2231e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2241e413cf9SAndrew Gallatin 	}
225b2fc195eSAndrew Gallatin 
226b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
227b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
228b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2291e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
230b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
231b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
232b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
233b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
234b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2351e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
236b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
237b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
238b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
239b2fc195eSAndrew Gallatin 	if (err != 0) {
240b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
241b2fc195eSAndrew Gallatin 		return err;
242b2fc195eSAndrew Gallatin 	}
243b2fc195eSAndrew Gallatin 
244b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
245b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
246b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
247b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
248b2fc195eSAndrew Gallatin 	if (err != 0) {
249b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
250b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
251b2fc195eSAndrew Gallatin 	}
252b2fc195eSAndrew Gallatin 
253b2fc195eSAndrew Gallatin 	/* load the memory */
254b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2556d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
256b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
257b2fc195eSAndrew Gallatin 	if (err != 0) {
258b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
259b2fc195eSAndrew Gallatin 		goto abort_with_mem;
260b2fc195eSAndrew Gallatin 	}
261b2fc195eSAndrew Gallatin 	return 0;
262b2fc195eSAndrew Gallatin 
263b2fc195eSAndrew Gallatin abort_with_mem:
264b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
265b2fc195eSAndrew Gallatin abort_with_dmat:
266b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
267b2fc195eSAndrew Gallatin 	return err;
268b2fc195eSAndrew Gallatin }
269b2fc195eSAndrew Gallatin 
270b2fc195eSAndrew Gallatin 
271b2fc195eSAndrew Gallatin static void
2726d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
273b2fc195eSAndrew Gallatin {
274b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
275b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
276b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
277b2fc195eSAndrew Gallatin }
278b2fc195eSAndrew Gallatin 
279b2fc195eSAndrew Gallatin /*
280b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
281b2fc195eSAndrew Gallatin  * SN=x\0
282b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
283b2fc195eSAndrew Gallatin  * PC=text\0
284b2fc195eSAndrew Gallatin  */
285b2fc195eSAndrew Gallatin 
286b2fc195eSAndrew Gallatin static int
2876d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
288b2fc195eSAndrew Gallatin {
2896d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
290b2fc195eSAndrew Gallatin 
291b2fc195eSAndrew Gallatin 	char *ptr, *limit;
292b2fc195eSAndrew Gallatin 	int i, found_mac;
293b2fc195eSAndrew Gallatin 
294b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2956d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
296b2fc195eSAndrew Gallatin 	found_mac = 0;
297b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
298b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2995e7d8541SAndrew Gallatin 			ptr += 1;
300b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
301b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
3025e7d8541SAndrew Gallatin 				ptr += 3;
303b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
304b2fc195eSAndrew Gallatin 					goto abort;
305b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
306b2fc195eSAndrew Gallatin 				found_mac = 1;
307b2fc195eSAndrew Gallatin 			}
3085e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
3095e7d8541SAndrew Gallatin 			ptr += 3;
3105e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
3115e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
3125e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
3135e7d8541SAndrew Gallatin 			ptr += 3;
3145e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
3155e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
316b2fc195eSAndrew Gallatin 		}
3176d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
318b2fc195eSAndrew Gallatin 	}
319b2fc195eSAndrew Gallatin 
320b2fc195eSAndrew Gallatin 	if (found_mac)
321b2fc195eSAndrew Gallatin 		return 0;
322b2fc195eSAndrew Gallatin 
323b2fc195eSAndrew Gallatin  abort:
324b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
325b2fc195eSAndrew Gallatin 
326b2fc195eSAndrew Gallatin 	return ENXIO;
327b2fc195eSAndrew Gallatin }
328b2fc195eSAndrew Gallatin 
3290d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3308fe615baSAndrew Gallatin static void
3318fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
332b2fc195eSAndrew Gallatin {
333b2fc195eSAndrew Gallatin 	uint32_t val;
3348fe615baSAndrew Gallatin 	unsigned long base, off;
335b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3368fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3378fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
338b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
339b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
340b2fc195eSAndrew Gallatin 
3418fe615baSAndrew Gallatin 
3428fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3438fe615baSAndrew Gallatin 		return;
3448fe615baSAndrew Gallatin 
3458fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3468fe615baSAndrew Gallatin 	if (pdev == NULL) {
3478fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3488fe615baSAndrew Gallatin 		return;
3498fe615baSAndrew Gallatin 	}
3508fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3518fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3528fe615baSAndrew Gallatin 
3538fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3548fe615baSAndrew Gallatin 		return;
3558fe615baSAndrew Gallatin 
3568fe615baSAndrew Gallatin 	base = 0;
3578fe615baSAndrew Gallatin 
3588fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3598fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3608fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3618fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3628fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3638fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3648fe615baSAndrew Gallatin 		if (mcp55 &&
3658fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3668fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3678fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3688fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3698fe615baSAndrew Gallatin 		}
3708fe615baSAndrew Gallatin 	}
3718fe615baSAndrew Gallatin 	if (!base)
3728fe615baSAndrew Gallatin 		return;
3738fe615baSAndrew Gallatin 
374b2fc195eSAndrew Gallatin 	/* XXXX
375b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
376b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
377b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
378b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
379b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
380b2fc195eSAndrew Gallatin 	*/
381b2fc195eSAndrew Gallatin #if 0
382b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
383b2fc195eSAndrew Gallatin 	   config space */
384b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
385b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
386b2fc195eSAndrew Gallatin 		val |= 0x40;
387b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3888fe615baSAndrew Gallatin 		return;
389b2fc195eSAndrew Gallatin 	}
390b2fc195eSAndrew Gallatin #endif
391b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
392b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
393b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
394b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
395b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
396b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
397b2fc195eSAndrew Gallatin 	 */
398b2fc195eSAndrew Gallatin 
399b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
400b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
401b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
402b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
403b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
404b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
405b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
406b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
407b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
408b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
409b2fc195eSAndrew Gallatin 
4108fe615baSAndrew Gallatin 	off =  base
411b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
412b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
413b2fc195eSAndrew Gallatin 						 + 8 * slot);
414b2fc195eSAndrew Gallatin 
415b2fc195eSAndrew Gallatin 	/* map it into the kernel */
416b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
417b2fc195eSAndrew Gallatin 
418b2fc195eSAndrew Gallatin 
419b2fc195eSAndrew Gallatin 	if (va == NULL) {
420b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4218fe615baSAndrew Gallatin 		return;
422b2fc195eSAndrew Gallatin 	}
423b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
424b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
425b2fc195eSAndrew Gallatin 
426b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
427b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
428b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
429b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
430b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
431b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
432b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4338fe615baSAndrew Gallatin 		return;
434b2fc195eSAndrew Gallatin 	}
435b2fc195eSAndrew Gallatin 
436b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
437b2fc195eSAndrew Gallatin 	val = *ptr32;
438b2fc195eSAndrew Gallatin 
439b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
440b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
441b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4428fe615baSAndrew Gallatin 		return;
443b2fc195eSAndrew Gallatin 	}
444b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
445b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4465e7d8541SAndrew Gallatin 	if (mxge_verbose)
447b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4485e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4495e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
450b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4518fe615baSAndrew Gallatin 	return;
452b2fc195eSAndrew Gallatin }
453b2fc195eSAndrew Gallatin #else
4548fe615baSAndrew Gallatin static void
455f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
456b2fc195eSAndrew Gallatin {
457b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
458b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4598fe615baSAndrew Gallatin 	return;
460b2fc195eSAndrew Gallatin }
461b2fc195eSAndrew Gallatin #endif
4628fe615baSAndrew Gallatin 
4638fe615baSAndrew Gallatin 
4648fe615baSAndrew Gallatin static int
4658fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4668fe615baSAndrew Gallatin {
4678fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4688fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4698fe615baSAndrew Gallatin 	int status;
4708fe615baSAndrew Gallatin 	uint32_t len;
4718fe615baSAndrew Gallatin 	char *test = " ";
4728fe615baSAndrew Gallatin 
4738fe615baSAndrew Gallatin 
4748fe615baSAndrew Gallatin 	/* Run a small DMA test.
4758fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4768fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4778fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4788fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4798fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4808fe615baSAndrew Gallatin 	 * transfers took to complete.
4818fe615baSAndrew Gallatin 	 */
4828fe615baSAndrew Gallatin 
4831e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4848fe615baSAndrew Gallatin 
4858fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4868fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4878fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4888fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4898fe615baSAndrew Gallatin 	if (status != 0) {
4908fe615baSAndrew Gallatin 		test = "read";
4918fe615baSAndrew Gallatin 		goto abort;
4928fe615baSAndrew Gallatin 	}
4938fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4948fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4958fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4968fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4978fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4988fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4998fe615baSAndrew Gallatin 	if (status != 0) {
5008fe615baSAndrew Gallatin 		test = "write";
5018fe615baSAndrew Gallatin 		goto abort;
5028fe615baSAndrew Gallatin 	}
5038fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5048fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5058fe615baSAndrew Gallatin 
5068fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5078fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5088fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5098fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5108fe615baSAndrew Gallatin 	if (status != 0) {
5118fe615baSAndrew Gallatin 		test = "read/write";
5128fe615baSAndrew Gallatin 		goto abort;
5138fe615baSAndrew Gallatin 	}
5148fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5158fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5168fe615baSAndrew Gallatin 
5178fe615baSAndrew Gallatin abort:
5188fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5198fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5208fe615baSAndrew Gallatin 			      test, status);
5218fe615baSAndrew Gallatin 
5228fe615baSAndrew Gallatin 	return status;
5238fe615baSAndrew Gallatin }
5248fe615baSAndrew Gallatin 
525b2fc195eSAndrew Gallatin /*
526b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
527b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
528b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
529b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
530b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
531b2fc195eSAndrew Gallatin  *
532b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
533b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
534b2fc195eSAndrew Gallatin  *
535b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
536b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
537b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
538b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5391e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
540b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5411e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
542b2fc195eSAndrew Gallatin  */
543b2fc195eSAndrew Gallatin 
5448fe615baSAndrew Gallatin static int
5458fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5468fe615baSAndrew Gallatin {
5478fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5488fe615baSAndrew Gallatin 	int reg, status;
5498fe615baSAndrew Gallatin 	uint16_t pectl;
5508fe615baSAndrew Gallatin 
5511e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5528fe615baSAndrew Gallatin 	/*
5538fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5548fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5558fe615baSAndrew Gallatin 	 */
5568fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5578fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5588fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5598fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5608fe615baSAndrew Gallatin 				      pectl);
5611e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5628fe615baSAndrew Gallatin 		}
5638fe615baSAndrew Gallatin 	}
5648fe615baSAndrew Gallatin 
5658fe615baSAndrew Gallatin 	/*
5668fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5678fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5688fe615baSAndrew Gallatin 	 */
5698fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5701e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5718fe615baSAndrew Gallatin 	if (status != 0) {
5728fe615baSAndrew Gallatin 		return status;
5738fe615baSAndrew Gallatin 	}
5748fe615baSAndrew Gallatin 
5758fe615baSAndrew Gallatin 	/*
5768fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5778fe615baSAndrew Gallatin 	 */
5788fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5798fe615baSAndrew Gallatin 
5808fe615baSAndrew Gallatin 	/*
5818fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5828fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5838fe615baSAndrew Gallatin 	 */
5848fe615baSAndrew Gallatin 
5858fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5868fe615baSAndrew Gallatin 	if (status == 0)
5878fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5888fe615baSAndrew Gallatin 
5898fe615baSAndrew Gallatin 	if (status != E2BIG)
5908fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5918fe615baSAndrew Gallatin 	if (status == ENOSYS)
5928fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5938fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5948fe615baSAndrew Gallatin 	return status;
5958fe615baSAndrew Gallatin }
5968fe615baSAndrew Gallatin 
5978fe615baSAndrew Gallatin static int
5986d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
599b2fc195eSAndrew Gallatin {
6008fe615baSAndrew Gallatin 	int aligned = 0;
60165c69066SAndrew Gallatin 	int force_firmware = mxge_force_firmware;
602b2fc195eSAndrew Gallatin 
60365c69066SAndrew Gallatin 	if (sc->throttle)
60465c69066SAndrew Gallatin 		force_firmware = sc->throttle;
605d91b1b49SAndrew Gallatin 
60665c69066SAndrew Gallatin 	if (force_firmware != 0) {
60765c69066SAndrew Gallatin 		if (force_firmware == 1)
608d91b1b49SAndrew Gallatin 			aligned = 1;
609d91b1b49SAndrew Gallatin 		else
610d91b1b49SAndrew Gallatin 			aligned = 0;
611d91b1b49SAndrew Gallatin 		if (mxge_verbose)
612d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
613d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
614d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
615d91b1b49SAndrew Gallatin 		goto abort;
616d91b1b49SAndrew Gallatin 	}
617d91b1b49SAndrew Gallatin 
618d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
619d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
620d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
621d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
622d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
623d91b1b49SAndrew Gallatin 			      sc->link_width);
624d91b1b49SAndrew Gallatin 		aligned = 1;
625d91b1b49SAndrew Gallatin 		goto abort;
626d91b1b49SAndrew Gallatin 	}
627d91b1b49SAndrew Gallatin 
6288fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6298fe615baSAndrew Gallatin 		return 0;
630b2fc195eSAndrew Gallatin 
631b2fc195eSAndrew Gallatin abort:
632b2fc195eSAndrew Gallatin 	if (aligned) {
6336d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6341e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
635b2fc195eSAndrew Gallatin 	} else {
6366d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6371e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
638b2fc195eSAndrew Gallatin 	}
6391e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
640b2fc195eSAndrew Gallatin }
641b2fc195eSAndrew Gallatin 
642b2fc195eSAndrew Gallatin union qualhack
643b2fc195eSAndrew Gallatin {
644b2fc195eSAndrew Gallatin         const char *ro_char;
645b2fc195eSAndrew Gallatin         char *rw_char;
646b2fc195eSAndrew Gallatin };
647b2fc195eSAndrew Gallatin 
6484da0d523SAndrew Gallatin static int
6494da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6504da0d523SAndrew Gallatin {
651b824b7d8SAndrew Gallatin 
6524da0d523SAndrew Gallatin 
6534da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6544da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6554da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6564da0d523SAndrew Gallatin 		return EIO;
6574da0d523SAndrew Gallatin 	}
6584da0d523SAndrew Gallatin 
6594da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6604da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6614da0d523SAndrew Gallatin 	if (mxge_verbose)
6624da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6634da0d523SAndrew Gallatin 
664b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
665b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6664da0d523SAndrew Gallatin 
667b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
668b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6694da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6704da0d523SAndrew Gallatin 			      sc->fw_version);
6714da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6724da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6734da0d523SAndrew Gallatin 		return EINVAL;
6744da0d523SAndrew Gallatin 	}
6754da0d523SAndrew Gallatin 	return 0;
6764da0d523SAndrew Gallatin 
6774da0d523SAndrew Gallatin }
678b2fc195eSAndrew Gallatin 
679f9ae0280SAndrew Gallatin static void *
680f9ae0280SAndrew Gallatin z_alloc(void *nil, u_int items, u_int size)
681f9ae0280SAndrew Gallatin {
682f9ae0280SAndrew Gallatin         void *ptr;
683f9ae0280SAndrew Gallatin 
684f9ae0280SAndrew Gallatin         ptr = malloc(items * size, M_TEMP, M_NOWAIT);
685f9ae0280SAndrew Gallatin         return ptr;
686f9ae0280SAndrew Gallatin }
687f9ae0280SAndrew Gallatin 
688f9ae0280SAndrew Gallatin static void
689f9ae0280SAndrew Gallatin z_free(void *nil, void *ptr)
690f9ae0280SAndrew Gallatin {
691f9ae0280SAndrew Gallatin         free(ptr, M_TEMP);
692f9ae0280SAndrew Gallatin }
693f9ae0280SAndrew Gallatin 
694f9ae0280SAndrew Gallatin 
695b2fc195eSAndrew Gallatin static int
6966d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
697b2fc195eSAndrew Gallatin {
698f9ae0280SAndrew Gallatin 	z_stream zs;
699f9ae0280SAndrew Gallatin 	char *inflate_buffer;
70033d54970SLuigi Rizzo 	const struct firmware *fw;
701b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
702b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
703b2fc195eSAndrew Gallatin 	int status;
7044da0d523SAndrew Gallatin 	unsigned int i;
7054da0d523SAndrew Gallatin 	char dummy;
706f9ae0280SAndrew Gallatin 	size_t fw_len;
707b2fc195eSAndrew Gallatin 
708b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
709b2fc195eSAndrew Gallatin 	if (fw == NULL) {
710b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
711b2fc195eSAndrew Gallatin 			      sc->fw_name);
712b2fc195eSAndrew Gallatin 		return ENOENT;
713b2fc195eSAndrew Gallatin 	}
714b2fc195eSAndrew Gallatin 
715f9ae0280SAndrew Gallatin 
716f9ae0280SAndrew Gallatin 
717f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
718f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
719f9ae0280SAndrew Gallatin 	zs.zalloc = z_alloc;
720f9ae0280SAndrew Gallatin 	zs.zfree = z_free;
721f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
722f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
723b2fc195eSAndrew Gallatin 		status = EIO;
724b2fc195eSAndrew Gallatin 		goto abort_with_fw;
725b2fc195eSAndrew Gallatin 	}
726f9ae0280SAndrew Gallatin 
727f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
728f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
729f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
730f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
731f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
732f9ae0280SAndrew Gallatin 		goto abort_with_zs;
733f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
734f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
735f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
736f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
737f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
738f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
739f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
740f9ae0280SAndrew Gallatin 		status = EIO;
741f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
742f9ae0280SAndrew Gallatin 	}
743f9ae0280SAndrew Gallatin 
744f9ae0280SAndrew Gallatin 	/* check id */
745f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
746f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
747f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
748f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
749f9ae0280SAndrew Gallatin 		status = EIO;
750f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
751f9ae0280SAndrew Gallatin 	}
752f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
753b2fc195eSAndrew Gallatin 
7544da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7554da0d523SAndrew Gallatin 	if (status != 0)
756f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
757b2fc195eSAndrew Gallatin 
758b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
759f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7604da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
761f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
762f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
76373c7c83fSAndrew Gallatin 		wmb();
7644da0d523SAndrew Gallatin 		dummy = *sc->sram;
76573c7c83fSAndrew Gallatin 		wmb();
7664da0d523SAndrew Gallatin 	}
767b2fc195eSAndrew Gallatin 
768f9ae0280SAndrew Gallatin 	*limit = fw_len;
769b2fc195eSAndrew Gallatin 	status = 0;
770f9ae0280SAndrew Gallatin abort_with_buffer:
771f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
772f9ae0280SAndrew Gallatin abort_with_zs:
773f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
774b2fc195eSAndrew Gallatin abort_with_fw:
775b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
776b2fc195eSAndrew Gallatin 	return status;
777b2fc195eSAndrew Gallatin }
778b2fc195eSAndrew Gallatin 
779b2fc195eSAndrew Gallatin /*
780b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
781b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
782b2fc195eSAndrew Gallatin  */
783b2fc195eSAndrew Gallatin 
784b2fc195eSAndrew Gallatin static void
7856d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
786b2fc195eSAndrew Gallatin {
787b2fc195eSAndrew Gallatin 	char buf_bytes[72];
788b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
789b2fc195eSAndrew Gallatin 	volatile char *submit;
790b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
791b2fc195eSAndrew Gallatin 	int i;
792b2fc195eSAndrew Gallatin 
793b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
794b2fc195eSAndrew Gallatin 
795b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
796b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
797b2fc195eSAndrew Gallatin 	*confirm = 0;
79873c7c83fSAndrew Gallatin 	wmb();
799b2fc195eSAndrew Gallatin 
800b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
801b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
802b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
803b2fc195eSAndrew Gallatin 	*/
804b2fc195eSAndrew Gallatin 
8056d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8066d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
807b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
808b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
809b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
8106d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
8116d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
812b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
813b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
814b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
815b2fc195eSAndrew Gallatin 
816b2fc195eSAndrew Gallatin 
8170fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
818b2fc195eSAndrew Gallatin 
8196d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
82073c7c83fSAndrew Gallatin 	wmb();
821b2fc195eSAndrew Gallatin 	DELAY(1000);
82273c7c83fSAndrew Gallatin 	wmb();
823b2fc195eSAndrew Gallatin 	i = 0;
824b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
825b2fc195eSAndrew Gallatin 		DELAY(1000);
826b2fc195eSAndrew Gallatin 		i++;
827b2fc195eSAndrew Gallatin 	}
828b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
829b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
830b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
831b2fc195eSAndrew Gallatin 			      *confirm);
832b2fc195eSAndrew Gallatin 	}
833b2fc195eSAndrew Gallatin 	return;
834b2fc195eSAndrew Gallatin }
835b2fc195eSAndrew Gallatin 
836b2fc195eSAndrew Gallatin static int
8376d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
838b2fc195eSAndrew Gallatin {
839b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
840b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
841b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8420fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
843b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
844e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
845b2fc195eSAndrew Gallatin 
846b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
847b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
848b2fc195eSAndrew Gallatin 
849b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
850b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
851b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
852b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8536d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8546d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
855b2fc195eSAndrew Gallatin 
856b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
857b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
858a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
859b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
86073c7c83fSAndrew Gallatin 	wmb();
8616d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
862b2fc195eSAndrew Gallatin 
8635e7d8541SAndrew Gallatin 	/* wait up to 20ms */
864e0501fd0SAndrew Gallatin 	err = EAGAIN;
8655e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
866b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
867b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
86873c7c83fSAndrew Gallatin 		wmb();
869e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
870e0501fd0SAndrew Gallatin 		case 0:
871b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
872e0501fd0SAndrew Gallatin 			err = 0;
873e0501fd0SAndrew Gallatin 			break;
874e0501fd0SAndrew Gallatin 		case 0xffffffff:
875e0501fd0SAndrew Gallatin 			DELAY(1000);
876e0501fd0SAndrew Gallatin 			break;
877e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
878e0501fd0SAndrew Gallatin 			err = ENOSYS;
879e0501fd0SAndrew Gallatin 			break;
880e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
881e0501fd0SAndrew Gallatin 			err = E2BIG;
882e0501fd0SAndrew Gallatin 			break;
883c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
884c587e59fSAndrew Gallatin 			err = EBUSY;
885c587e59fSAndrew Gallatin 			break;
886e0501fd0SAndrew Gallatin 		default:
887b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8886d87a65dSAndrew Gallatin 				      "mxge: command %d "
889b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
890b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
891e0501fd0SAndrew Gallatin 			err = ENXIO;
892e0501fd0SAndrew Gallatin 			break;
893b2fc195eSAndrew Gallatin 		}
894e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
895e0501fd0SAndrew Gallatin 			break;
896b2fc195eSAndrew Gallatin 	}
897e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8986d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
899b2fc195eSAndrew Gallatin 			      "result = %d\n",
900b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
901e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
902e0501fd0SAndrew Gallatin 	return err;
903b2fc195eSAndrew Gallatin }
904b2fc195eSAndrew Gallatin 
9054da0d523SAndrew Gallatin static int
9064da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
9074da0d523SAndrew Gallatin {
9084da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
9094da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
9104da0d523SAndrew Gallatin 	size_t hdr_offset;
9114da0d523SAndrew Gallatin 	int status;
9124da0d523SAndrew Gallatin 
9134da0d523SAndrew Gallatin 	/* find running firmware header */
9144da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9154da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9164da0d523SAndrew Gallatin 
9174da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9184da0d523SAndrew Gallatin 		device_printf(sc->dev,
9194da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9204da0d523SAndrew Gallatin 			      (int)hdr_offset);
9214da0d523SAndrew Gallatin 		return EIO;
9224da0d523SAndrew Gallatin 	}
9234da0d523SAndrew Gallatin 
9244da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9254da0d523SAndrew Gallatin 	 * validate firmware */
9264da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9274da0d523SAndrew Gallatin 	if (hdr == NULL) {
9284da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9294da0d523SAndrew Gallatin 		return ENOMEM;
9304da0d523SAndrew Gallatin 	}
9314da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9324da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9334da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9344da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9354da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
936b824b7d8SAndrew Gallatin 
937b824b7d8SAndrew Gallatin 	/*
938b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
939b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
940b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
941b824b7d8SAndrew Gallatin 	 */
942b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
943b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
944b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
945b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
946b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
947b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
948b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
949b824b7d8SAndrew Gallatin 	}
950b824b7d8SAndrew Gallatin 
9514da0d523SAndrew Gallatin 	return status;
9524da0d523SAndrew Gallatin }
9534da0d523SAndrew Gallatin 
954b2fc195eSAndrew Gallatin 
955b2fc195eSAndrew Gallatin static int
9561e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
957b2fc195eSAndrew Gallatin {
958b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
959b2fc195eSAndrew Gallatin 	volatile char *submit;
960b2fc195eSAndrew Gallatin 	char buf_bytes[72];
961b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
962b2fc195eSAndrew Gallatin 	int status, i;
963b2fc195eSAndrew Gallatin 
964b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
965b2fc195eSAndrew Gallatin 
966b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9676d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
968b2fc195eSAndrew Gallatin 	if (status) {
9691e413cf9SAndrew Gallatin 		if (!adopt)
9701e413cf9SAndrew Gallatin 			return status;
9714da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9724da0d523SAndrew Gallatin 		   it is new enough */
9734da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9744da0d523SAndrew Gallatin 		if (status) {
9754da0d523SAndrew Gallatin 			device_printf(sc->dev,
9764da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
977b2fc195eSAndrew Gallatin 			return status;
978b2fc195eSAndrew Gallatin 		}
9794da0d523SAndrew Gallatin 		device_printf(sc->dev,
9804da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9811e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9824da0d523SAndrew Gallatin 			device_printf(sc->dev,
9834da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9844da0d523SAndrew Gallatin 				 ".  For optimal\n");
9854da0d523SAndrew Gallatin 			device_printf(sc->dev,
9864da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9874da0d523SAndrew Gallatin 				 "firmware\n");
9884da0d523SAndrew Gallatin 		}
989d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9901e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
991d91b1b49SAndrew Gallatin 		return 0;
9924da0d523SAndrew Gallatin 	}
993b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
994b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
995b2fc195eSAndrew Gallatin 	*confirm = 0;
99673c7c83fSAndrew Gallatin 	wmb();
997b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
998b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
999b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
1000b2fc195eSAndrew Gallatin 	*/
1001b2fc195eSAndrew Gallatin 
10026d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
10036d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
1004b2fc195eSAndrew Gallatin 
1005b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
1006b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
1007b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
1008b2fc195eSAndrew Gallatin 
1009b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
1010b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
1011b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
1012b2fc195eSAndrew Gallatin 	*/
1013b2fc195eSAndrew Gallatin 					/* where the code starts*/
10146d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1015b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1016b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1017b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1018b2fc195eSAndrew Gallatin 
10190fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10206d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
102173c7c83fSAndrew Gallatin 	wmb();
1022b2fc195eSAndrew Gallatin 	DELAY(1000);
102373c7c83fSAndrew Gallatin 	wmb();
1024b2fc195eSAndrew Gallatin 	i = 0;
1025b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1026b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1027b2fc195eSAndrew Gallatin 		i++;
1028b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1029b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1030b2fc195eSAndrew Gallatin 	}
1031b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1032b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1033b2fc195eSAndrew Gallatin 			confirm, *confirm);
1034b2fc195eSAndrew Gallatin 
1035b2fc195eSAndrew Gallatin 		return ENXIO;
1036b2fc195eSAndrew Gallatin 	}
1037b2fc195eSAndrew Gallatin 	return 0;
1038b2fc195eSAndrew Gallatin }
1039b2fc195eSAndrew Gallatin 
1040b2fc195eSAndrew Gallatin static int
10416d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1042b2fc195eSAndrew Gallatin {
10436d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1044b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1045b2fc195eSAndrew Gallatin 	int status;
1046b2fc195eSAndrew Gallatin 
1047b2fc195eSAndrew Gallatin 
1048b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1049b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1050b2fc195eSAndrew Gallatin 
1051b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1052b2fc195eSAndrew Gallatin 
10535e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1054b2fc195eSAndrew Gallatin 	return status;
1055b2fc195eSAndrew Gallatin }
1056b2fc195eSAndrew Gallatin 
1057b2fc195eSAndrew Gallatin static int
10586d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1059b2fc195eSAndrew Gallatin {
10606d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1061b2fc195eSAndrew Gallatin 	int status;
1062b2fc195eSAndrew Gallatin 
1063b2fc195eSAndrew Gallatin 	if (pause)
10645e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1065b2fc195eSAndrew Gallatin 				       &cmd);
1066b2fc195eSAndrew Gallatin 	else
10675e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1068b2fc195eSAndrew Gallatin 				       &cmd);
1069b2fc195eSAndrew Gallatin 
1070b2fc195eSAndrew Gallatin 	if (status) {
1071b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1072b2fc195eSAndrew Gallatin 		return ENXIO;
1073b2fc195eSAndrew Gallatin 	}
1074b2fc195eSAndrew Gallatin 	sc->pause = pause;
1075b2fc195eSAndrew Gallatin 	return 0;
1076b2fc195eSAndrew Gallatin }
1077b2fc195eSAndrew Gallatin 
1078b2fc195eSAndrew Gallatin static void
10796d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1080b2fc195eSAndrew Gallatin {
10816d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1082b2fc195eSAndrew Gallatin 	int status;
1083b2fc195eSAndrew Gallatin 
10841e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10851e413cf9SAndrew Gallatin 		promisc = 1;
10861e413cf9SAndrew Gallatin 
1087b2fc195eSAndrew Gallatin 	if (promisc)
10885e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1089b2fc195eSAndrew Gallatin 				       &cmd);
1090b2fc195eSAndrew Gallatin 	else
10915e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1092b2fc195eSAndrew Gallatin 				       &cmd);
1093b2fc195eSAndrew Gallatin 
1094b2fc195eSAndrew Gallatin 	if (status) {
1095b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1096b2fc195eSAndrew Gallatin 	}
1097b2fc195eSAndrew Gallatin }
1098b2fc195eSAndrew Gallatin 
10990fa7f681SAndrew Gallatin static void
11000fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
11010fa7f681SAndrew Gallatin {
11020fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
11030fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
11040fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
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 
11220fa7f681SAndrew Gallatin 	if (ifp->if_flags & 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 */
11370fa7f681SAndrew Gallatin 
1138eb956cd0SRobert Watson 	if_maddr_rlock(ifp);
11390fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11400fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11410fa7f681SAndrew Gallatin 			continue;
11420fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11430fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11440fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11450fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11460fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11470fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11480fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11490fa7f681SAndrew Gallatin 		if (err != 0) {
11500fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11510fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11520fa7f681SAndrew Gallatin 			       "%d\t", err);
11530fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
1154eb956cd0SRobert Watson 			if_maddr_runlock(ifp);
11550fa7f681SAndrew Gallatin 			return;
11560fa7f681SAndrew Gallatin 		}
11570fa7f681SAndrew Gallatin 	}
1158eb956cd0SRobert Watson 	if_maddr_runlock(ifp);
11590fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11600fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11610fa7f681SAndrew Gallatin 	if (err != 0) {
11620fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11630fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11640fa7f681SAndrew Gallatin 	}
11650fa7f681SAndrew Gallatin }
11660fa7f681SAndrew Gallatin 
1167b2fc195eSAndrew Gallatin static int
1168053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1169053e637fSAndrew Gallatin {
1170053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1171053e637fSAndrew Gallatin 	int status;
1172053e637fSAndrew Gallatin 
1173c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1174c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1175053e637fSAndrew Gallatin 
1176053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1177053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1178053e637fSAndrew Gallatin 	cmd.data0 = 0;
1179053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1180053e637fSAndrew Gallatin 			       &cmd);
1181053e637fSAndrew Gallatin 	if (status == 0)
1182c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1183053e637fSAndrew Gallatin 
1184053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1185053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1186053e637fSAndrew Gallatin }
1187053e637fSAndrew Gallatin 
1188053e637fSAndrew Gallatin static int
1189adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1190b2fc195eSAndrew Gallatin {
11911e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11921e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11931e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11946d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11951e413cf9SAndrew Gallatin 	int slice, status;
1196b2fc195eSAndrew Gallatin 
1197b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1198b2fc195eSAndrew Gallatin 	   is alive */
1199b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
12005e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1201b2fc195eSAndrew Gallatin 	if (status != 0) {
1202b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1203b2fc195eSAndrew Gallatin 		return ENXIO;
1204b2fc195eSAndrew Gallatin 	}
1205b2fc195eSAndrew Gallatin 
1206091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1207091feecdSAndrew Gallatin 
12081e413cf9SAndrew Gallatin 
12091e413cf9SAndrew Gallatin 	/* set the intrq size */
12101e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12111e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12121e413cf9SAndrew Gallatin 
12131e413cf9SAndrew Gallatin 	/*
12141e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12151e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12161e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12171e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12181e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12191e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12201e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12211e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12221e413cf9SAndrew Gallatin 	 */
12231e413cf9SAndrew Gallatin 
12241e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12251e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12261e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12271e413cf9SAndrew Gallatin 					   &cmd);
12281e413cf9SAndrew Gallatin 		if (status != 0) {
12291e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12301e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12311e413cf9SAndrew Gallatin 			return status;
12321e413cf9SAndrew Gallatin 		}
12331e413cf9SAndrew Gallatin 		/*
12341e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12351e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12361e413cf9SAndrew Gallatin 		 */
12371e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12381e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1239c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1240c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1241c6cb3e3fSAndrew Gallatin #endif
12421e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12431e413cf9SAndrew Gallatin 					   &cmd);
12441e413cf9SAndrew Gallatin 		if (status != 0) {
12451e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12461e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12471e413cf9SAndrew Gallatin 			return status;
12481e413cf9SAndrew Gallatin 		}
12491e413cf9SAndrew Gallatin 	}
12501e413cf9SAndrew Gallatin 
12511e413cf9SAndrew Gallatin 
1252adae7080SAndrew Gallatin 	if (interrupts_setup) {
1253b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12541e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12551e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12561e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12571e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12581e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12591e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12601e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12611e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12621e413cf9SAndrew Gallatin 						&cmd);
12631e413cf9SAndrew Gallatin 		}
1264adae7080SAndrew Gallatin 	}
1265b2fc195eSAndrew Gallatin 
12666d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12675e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12685e7d8541SAndrew Gallatin 
12695e7d8541SAndrew Gallatin 
12705e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12715e7d8541SAndrew Gallatin 
12725e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12731e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12745e7d8541SAndrew Gallatin 
12755e7d8541SAndrew Gallatin 
12765e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12776d87a65dSAndrew Gallatin 				&cmd);
12785e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1279b2fc195eSAndrew Gallatin 	if (status != 0) {
1280b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1281b2fc195eSAndrew Gallatin 		return status;
1282b2fc195eSAndrew Gallatin 	}
1283b2fc195eSAndrew Gallatin 
12845e7d8541SAndrew Gallatin 
12855e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12865e7d8541SAndrew Gallatin 
12875e7d8541SAndrew Gallatin 
12885e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12898fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12905e7d8541SAndrew Gallatin 
12911e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12921e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12931e413cf9SAndrew Gallatin 
12941e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1295b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12961e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12971e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12981e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12991e413cf9SAndrew Gallatin 		ss->tx.done = 0;
13001e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1301c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1302c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1303c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
13041e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
13051e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
13061e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
13071e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
13081e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
13091e413cf9SAndrew Gallatin 		ss->lro_bad_csum = 0;
13101e413cf9SAndrew Gallatin 		ss->lro_queued = 0;
13111e413cf9SAndrew Gallatin 		ss->lro_flushed = 0;
13121e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
1313a393336bSAndrew Gallatin 			bzero(ss->fw_stats, sizeof *ss->fw_stats);
13141e413cf9SAndrew Gallatin 		}
13151e413cf9SAndrew Gallatin 	}
1316b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13176d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1318bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13196d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13200fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
132165c69066SAndrew Gallatin 	if (sc->throttle) {
132265c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
132365c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
132465c69066SAndrew Gallatin 				  &cmd)) {
132565c69066SAndrew Gallatin 			device_printf(sc->dev,
132665c69066SAndrew Gallatin 				      "can't enable throttle\n");
132765c69066SAndrew Gallatin 		}
132865c69066SAndrew Gallatin 	}
1329b2fc195eSAndrew Gallatin 	return status;
1330b2fc195eSAndrew Gallatin }
1331b2fc195eSAndrew Gallatin 
1332b2fc195eSAndrew Gallatin static int
133365c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
133465c69066SAndrew Gallatin {
133565c69066SAndrew Gallatin 	mxge_cmd_t cmd;
133665c69066SAndrew Gallatin 	mxge_softc_t *sc;
133765c69066SAndrew Gallatin 	int err;
133865c69066SAndrew Gallatin 	unsigned int throttle;
133965c69066SAndrew Gallatin 
134065c69066SAndrew Gallatin 	sc = arg1;
134165c69066SAndrew Gallatin 	throttle = sc->throttle;
134265c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
134365c69066SAndrew Gallatin         if (err != 0) {
134465c69066SAndrew Gallatin                 return err;
134565c69066SAndrew Gallatin         }
134665c69066SAndrew Gallatin 
134765c69066SAndrew Gallatin 	if (throttle == sc->throttle)
134865c69066SAndrew Gallatin 		return 0;
134965c69066SAndrew Gallatin 
135065c69066SAndrew Gallatin         if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
135165c69066SAndrew Gallatin                 return EINVAL;
135265c69066SAndrew Gallatin 
135365c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
135465c69066SAndrew Gallatin 	cmd.data0 = throttle;
135565c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
135665c69066SAndrew Gallatin 	if (err == 0)
135765c69066SAndrew Gallatin 		sc->throttle = throttle;
135865c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
135965c69066SAndrew Gallatin 	return err;
136065c69066SAndrew Gallatin }
136165c69066SAndrew Gallatin 
136265c69066SAndrew Gallatin static int
13636d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1364b2fc195eSAndrew Gallatin {
13656d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1366b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1367b2fc195eSAndrew Gallatin         int err;
1368b2fc195eSAndrew Gallatin 
1369b2fc195eSAndrew Gallatin         sc = arg1;
1370b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1371b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1372b2fc195eSAndrew Gallatin         if (err != 0) {
1373b2fc195eSAndrew Gallatin                 return err;
1374b2fc195eSAndrew Gallatin         }
1375b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1376b2fc195eSAndrew Gallatin                 return 0;
1377b2fc195eSAndrew Gallatin 
1378b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1379b2fc195eSAndrew Gallatin                 return EINVAL;
1380b2fc195eSAndrew Gallatin 
1381a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13825e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1383b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13845e7d8541SAndrew Gallatin 
1385a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1386b2fc195eSAndrew Gallatin         return err;
1387b2fc195eSAndrew Gallatin }
1388b2fc195eSAndrew Gallatin 
1389b2fc195eSAndrew Gallatin static int
13906d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1391b2fc195eSAndrew Gallatin {
13926d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1393b2fc195eSAndrew Gallatin         unsigned int enabled;
1394b2fc195eSAndrew Gallatin         int err;
1395b2fc195eSAndrew Gallatin 
1396b2fc195eSAndrew Gallatin         sc = arg1;
1397b2fc195eSAndrew Gallatin         enabled = sc->pause;
1398b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1399b2fc195eSAndrew Gallatin         if (err != 0) {
1400b2fc195eSAndrew Gallatin                 return err;
1401b2fc195eSAndrew Gallatin         }
1402b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1403b2fc195eSAndrew Gallatin                 return 0;
1404b2fc195eSAndrew Gallatin 
1405a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
14066d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1407a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1408b2fc195eSAndrew Gallatin         return err;
1409b2fc195eSAndrew Gallatin }
1410b2fc195eSAndrew Gallatin 
1411b2fc195eSAndrew Gallatin static int
1412f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1413f04b33f8SAndrew Gallatin {
1414f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1415c587e59fSAndrew Gallatin 	int err = 0;
1416f04b33f8SAndrew Gallatin 
1417f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1418f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1419f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1420f04b33f8SAndrew Gallatin 	else
1421f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1422f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1423c587e59fSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1424a393336bSAndrew Gallatin 		mxge_close(sc, 0);
1425f04b33f8SAndrew Gallatin 		err = mxge_open(sc);
1426c587e59fSAndrew Gallatin 	}
1427f04b33f8SAndrew Gallatin 	return err;
1428f04b33f8SAndrew Gallatin }
1429f04b33f8SAndrew Gallatin 
1430f04b33f8SAndrew Gallatin static int
1431276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1432276edd10SAndrew Gallatin {
1433276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1434276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1435276edd10SAndrew Gallatin 	int err;
1436276edd10SAndrew Gallatin 
1437276edd10SAndrew Gallatin 	sc = arg1;
1438276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1439276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1440276edd10SAndrew Gallatin 	if (err != 0)
1441276edd10SAndrew Gallatin 		return err;
1442276edd10SAndrew Gallatin 
1443276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1444276edd10SAndrew Gallatin 		return 0;
1445276edd10SAndrew Gallatin 
1446276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1447276edd10SAndrew Gallatin 		return EINVAL;
1448276edd10SAndrew Gallatin 
1449276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1450f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1451276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1452276edd10SAndrew Gallatin 	return err;
1453276edd10SAndrew Gallatin }
1454276edd10SAndrew Gallatin 
1455276edd10SAndrew Gallatin static int
14566d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1457b2fc195eSAndrew Gallatin {
1458b2fc195eSAndrew Gallatin         int err;
1459b2fc195eSAndrew Gallatin 
1460b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1461b2fc195eSAndrew Gallatin                 return EFAULT;
1462b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1463b2fc195eSAndrew Gallatin         arg1 = NULL;
1464b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1465b2fc195eSAndrew Gallatin 
1466b2fc195eSAndrew Gallatin         return err;
1467b2fc195eSAndrew Gallatin }
1468b2fc195eSAndrew Gallatin 
1469b2fc195eSAndrew Gallatin static void
14701e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14711e413cf9SAndrew Gallatin {
14721e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14731e413cf9SAndrew Gallatin 	int slice;
14741e413cf9SAndrew Gallatin 
14751e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14761e413cf9SAndrew Gallatin 		return;
14771e413cf9SAndrew Gallatin 
14781e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14791e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14801e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14811e413cf9SAndrew Gallatin 			continue;
14821e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14831e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14841e413cf9SAndrew Gallatin 	}
14851e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14861e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14871e413cf9SAndrew Gallatin }
14881e413cf9SAndrew Gallatin 
14891e413cf9SAndrew Gallatin static void
14906d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1491b2fc195eSAndrew Gallatin {
1492b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1493b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14945e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14951e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14961e413cf9SAndrew Gallatin 	int slice;
14971e413cf9SAndrew Gallatin 	char slice_num[8];
1498b2fc195eSAndrew Gallatin 
1499b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1500b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
15011e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1502b2fc195eSAndrew Gallatin 
15035e7d8541SAndrew Gallatin 	/* random information */
15045e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15055e7d8541SAndrew Gallatin 		       "firmware_version",
15065e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
15075e7d8541SAndrew Gallatin 		       0, "firmware version");
15085e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15095e7d8541SAndrew Gallatin 		       "serial_number",
15105e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
15115e7d8541SAndrew Gallatin 		       0, "serial number");
15125e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15135e7d8541SAndrew Gallatin 		       "product_code",
15145e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
15155e7d8541SAndrew Gallatin 		       0, "product_code");
15165e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1517d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1518d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1519d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1520d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15215e7d8541SAndrew Gallatin 		       "tx_boundary",
15221e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
15235e7d8541SAndrew Gallatin 		       0, "tx_boundary");
15245e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1525091feecdSAndrew Gallatin 		       "write_combine",
1526091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1527091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1528091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15295e7d8541SAndrew Gallatin 		       "read_dma_MBs",
15305e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
15315e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
15325e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15335e7d8541SAndrew Gallatin 		       "write_dma_MBs",
15345e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
15355e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
15365e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15375e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
15385e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
15395e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
1540a393336bSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1541a393336bSAndrew Gallatin 		       "watchdog_resets",
1542a393336bSAndrew Gallatin 		       CTLFLAG_RD, &sc->watchdog_resets,
1543a393336bSAndrew Gallatin 		       0, "Number of times NIC was reset");
15445e7d8541SAndrew Gallatin 
15455e7d8541SAndrew Gallatin 
15465e7d8541SAndrew Gallatin 	/* performance related tunables */
1547b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1548b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1549b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15506d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1551b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1552b2fc195eSAndrew Gallatin 
1553b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
155465c69066SAndrew Gallatin 			"throttle",
155565c69066SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
155665c69066SAndrew Gallatin 			0, mxge_change_throttle,
155765c69066SAndrew Gallatin 			"I", "transmit throttling");
155865c69066SAndrew Gallatin 
155965c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1560b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1561b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15626d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1563b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1564b2fc195eSAndrew Gallatin 
1565b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15665e7d8541SAndrew Gallatin 		       "deassert_wait",
15675e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15685e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1569b2fc195eSAndrew Gallatin 
1570b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1571b2fc195eSAndrew Gallatin 	   Need to swap it */
1572b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1573b2fc195eSAndrew Gallatin 			"link_up",
1574b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15756d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1576b2fc195eSAndrew Gallatin 			"I", "link up");
1577b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1578b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1579b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15806d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1581b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1582b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1583adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1584adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1585adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15866d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1587adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1588adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1589adae7080SAndrew Gallatin 			"dropped_bad_phy",
1590adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1591adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1592adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1593adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1594b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1595b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1596b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1597b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
15986d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1599b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1600b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1601adae7080SAndrew Gallatin 			"dropped_link_overflow",
1602adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1603adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1604adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1605adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
16060fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
16070fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
16080fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
16090fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
16100fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
16110fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1612adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1613adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
16146d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1615adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1616b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1617b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1618b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1619b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
16206d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1621b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1622b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1623adae7080SAndrew Gallatin 			"dropped_overrun",
1624adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
16256d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1626adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1627adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1628adae7080SAndrew Gallatin 			"dropped_pause",
1629adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1630adae7080SAndrew Gallatin 			&fw->dropped_pause,
1631adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1632adae7080SAndrew Gallatin 			"I", "dropped_pause");
1633adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1634adae7080SAndrew Gallatin 			"dropped_runt",
1635adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1636adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1637adae7080SAndrew Gallatin 			"I", "dropped_runt");
1638b2fc195eSAndrew Gallatin 
1639a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1640a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1641a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1642a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1643a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1644a0394e33SAndrew Gallatin 
16455e7d8541SAndrew Gallatin 	/* verbose printing? */
1646b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16475e7d8541SAndrew Gallatin 		       "verbose",
16485e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
16495e7d8541SAndrew Gallatin 		       0, "verbose printing");
1650b2fc195eSAndrew Gallatin 
1651053e637fSAndrew Gallatin 	/* lro */
1652276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1653276edd10SAndrew Gallatin 			"lro_cnt",
1654276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1655276edd10SAndrew Gallatin 			0, mxge_change_lro,
1656276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1657053e637fSAndrew Gallatin 
16581e413cf9SAndrew Gallatin 
16591e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
16601e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
16611e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
16621e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
16631e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
16641e413cf9SAndrew Gallatin 
16651e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16661e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16671e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16681e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16691e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16701e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16711e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16721e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16731e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
16741e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1675053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16761e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16771e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16781e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16791e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16801e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16811e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16821e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16831e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16841e413cf9SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
1685053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1686053e637fSAndrew Gallatin 
1687053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16881e413cf9SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16891e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16901e413cf9SAndrew Gallatin 			       "queues");
1691053e637fSAndrew Gallatin 
1692c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16931e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16941e413cf9SAndrew Gallatin 		if (slice > 0)
16951e413cf9SAndrew Gallatin 			continue;
1696c6cb3e3fSAndrew Gallatin #endif
1697c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1698c6cb3e3fSAndrew Gallatin 			       "tx_req",
1699c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1700c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
17011e413cf9SAndrew Gallatin 
17021e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17031e413cf9SAndrew Gallatin 			       "tx_done",
17041e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
17051e413cf9SAndrew Gallatin 			       0, "tx_done");
17061e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17071e413cf9SAndrew Gallatin 			       "tx_pkt_done",
17081e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
17091e413cf9SAndrew Gallatin 			       0, "tx_done");
17101e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17111e413cf9SAndrew Gallatin 			       "tx_stall",
17121e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
17131e413cf9SAndrew Gallatin 			       0, "tx_stall");
17141e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17151e413cf9SAndrew Gallatin 			       "tx_wake",
17161e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
17171e413cf9SAndrew Gallatin 			       0, "tx_wake");
17181e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17191e413cf9SAndrew Gallatin 			       "tx_defrag",
17201e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
17211e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1722c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1723c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1724c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1725c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1726c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1727c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1728c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1729c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1730c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1731c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1732c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1733c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
17341e413cf9SAndrew Gallatin 	}
1735b2fc195eSAndrew Gallatin }
1736b2fc195eSAndrew Gallatin 
1737b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1738b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1739b2fc195eSAndrew Gallatin 
1740b2fc195eSAndrew Gallatin static inline void
17411e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1742b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1743b2fc195eSAndrew Gallatin {
1744b2fc195eSAndrew Gallatin         int idx, starting_slot;
1745b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1746b2fc195eSAndrew Gallatin         while (cnt > 1) {
1747b2fc195eSAndrew Gallatin                 cnt--;
1748b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
17496d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1750b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
175173c7c83fSAndrew Gallatin                 wmb();
1752b2fc195eSAndrew Gallatin         }
1753b2fc195eSAndrew Gallatin }
1754b2fc195eSAndrew Gallatin 
1755b2fc195eSAndrew Gallatin /*
1756b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1757b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1758b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1759b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1760b2fc195eSAndrew Gallatin  */
1761b2fc195eSAndrew Gallatin 
1762b2fc195eSAndrew Gallatin static inline void
17631e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1764b2fc195eSAndrew Gallatin                   int cnt)
1765b2fc195eSAndrew Gallatin {
1766b2fc195eSAndrew Gallatin         int idx, i;
1767b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1768b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1769b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1770b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
17715e7d8541SAndrew Gallatin 	uint8_t last_flags;
1772b2fc195eSAndrew Gallatin 
1773b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1774b2fc195eSAndrew Gallatin 
17755e7d8541SAndrew Gallatin 	last_flags = src->flags;
17765e7d8541SAndrew Gallatin 	src->flags = 0;
177773c7c83fSAndrew Gallatin         wmb();
1778b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1779b2fc195eSAndrew Gallatin         srcp = src;
1780b2fc195eSAndrew Gallatin 
1781b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1782b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
17836d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
178473c7c83fSAndrew Gallatin                         wmb(); /* force write every 32 bytes */
1785b2fc195eSAndrew Gallatin                         srcp += 2;
1786b2fc195eSAndrew Gallatin                         dstp += 2;
1787b2fc195eSAndrew Gallatin                 }
1788b2fc195eSAndrew Gallatin         } else {
1789b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1790b2fc195eSAndrew Gallatin                    that it is submitted below */
17916d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1792b2fc195eSAndrew Gallatin                 i = 0;
1793b2fc195eSAndrew Gallatin         }
1794b2fc195eSAndrew Gallatin         if (i < cnt) {
1795b2fc195eSAndrew Gallatin                 /* submit the first request */
17966d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
179773c7c83fSAndrew Gallatin                 wmb(); /* barrier before setting valid flag */
1798b2fc195eSAndrew Gallatin         }
1799b2fc195eSAndrew Gallatin 
1800b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
18015e7d8541SAndrew Gallatin         src->flags = last_flags;
1802b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1803b2fc195eSAndrew Gallatin         src_ints+=3;
1804b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1805b2fc195eSAndrew Gallatin         dst_ints+=3;
1806b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1807b2fc195eSAndrew Gallatin         tx->req += cnt;
180873c7c83fSAndrew Gallatin         wmb();
1809b2fc195eSAndrew Gallatin }
1810b2fc195eSAndrew Gallatin 
181137d89b0cSAndrew Gallatin #if IFCAP_TSO4
181237d89b0cSAndrew Gallatin 
1813b2fc195eSAndrew Gallatin static void
18141e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
18151e413cf9SAndrew Gallatin 	       int busdma_seg_cnt, int ip_off)
1816aed8e389SAndrew Gallatin {
18171e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1818aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1819aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1820aed8e389SAndrew Gallatin 	struct ip *ip;
1821aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1822aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1823aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1824aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1825aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1826aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1827aed8e389SAndrew Gallatin 	static int once;
1828aed8e389SAndrew Gallatin 
1829aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1830aed8e389SAndrew Gallatin 
1831aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1832aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1833aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1834aed8e389SAndrew Gallatin 	 */
1835aed8e389SAndrew Gallatin 
1836aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1837aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1838aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1839c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1840c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
18411e413cf9SAndrew Gallatin 			   ss->scratch);
18421e413cf9SAndrew Gallatin 		ip = (struct ip *)(ss->scratch + ip_off);
1843aed8e389SAndrew Gallatin 	} else {
1844c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1845aed8e389SAndrew Gallatin 	}
1846c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1847aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1848c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
18491e413cf9SAndrew Gallatin 			   + sizeof (*tcp),  ss->scratch);
1850c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1851aed8e389SAndrew Gallatin 	}
1852aed8e389SAndrew Gallatin 
1853aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1854c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1855aed8e389SAndrew Gallatin 
1856aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1857c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1858aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1859aed8e389SAndrew Gallatin 
1860aed8e389SAndrew Gallatin 
1861aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1862aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1863aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1864aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1865aed8e389SAndrew 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++;
1937aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1938aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1939aed8e389SAndrew Gallatin 			else
1940aed8e389SAndrew Gallatin 				cksum_offset = 0;
1941adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1942aed8e389SAndrew Gallatin 				goto drop;
1943aed8e389SAndrew Gallatin 		}
1944aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1945aed8e389SAndrew Gallatin 		seg++;
1946aed8e389SAndrew Gallatin 	}
1947aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1948aed8e389SAndrew Gallatin 
1949aed8e389SAndrew Gallatin 	do {
1950aed8e389SAndrew Gallatin 		req--;
1951aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1952aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1953aed8e389SAndrew Gallatin 
1954aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1955aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1956c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1957c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1958c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1959c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1960c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1961c6cb3e3fSAndrew Gallatin 		tx->activate++;
1962c6cb3e3fSAndrew Gallatin 		wmb();
1963c6cb3e3fSAndrew Gallatin 	}
1964c6cb3e3fSAndrew Gallatin #endif
1965aed8e389SAndrew Gallatin 	return;
1966aed8e389SAndrew Gallatin 
1967aed8e389SAndrew Gallatin drop:
1968e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1969aed8e389SAndrew Gallatin 	m_freem(m);
1970c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1971aed8e389SAndrew Gallatin 	if (!once) {
1972adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1973adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1974adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1975aed8e389SAndrew Gallatin 		once = 1;
1976aed8e389SAndrew Gallatin 	}
1977aed8e389SAndrew Gallatin 	return;
1978aed8e389SAndrew Gallatin 
1979aed8e389SAndrew Gallatin }
1980aed8e389SAndrew Gallatin 
198137d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
198237d89b0cSAndrew Gallatin 
198337d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1984c792928fSAndrew Gallatin /*
1985c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1986c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1987c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1988c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1989c792928fSAndrew Gallatin  */
1990c792928fSAndrew Gallatin static struct mbuf *
1991c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1992c792928fSAndrew Gallatin {
1993c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1994c792928fSAndrew Gallatin 
1995c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1996c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1997c792928fSAndrew Gallatin 		return NULL;
1998c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1999c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
2000c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2001c792928fSAndrew Gallatin 			return NULL;
2002c792928fSAndrew Gallatin 	}
2003c792928fSAndrew Gallatin 	/*
2004c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2005c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2006c792928fSAndrew Gallatin 	 */
2007c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2008c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2009c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2010c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2011c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2012c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2013c792928fSAndrew Gallatin 	return m;
2014c792928fSAndrew Gallatin }
201537d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2016c792928fSAndrew Gallatin 
2017aed8e389SAndrew Gallatin static void
20181e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2019b2fc195eSAndrew Gallatin {
20201e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2021b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2022b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2023b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
2024b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20251e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2026b2fc195eSAndrew Gallatin 	struct ip *ip;
2027c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
2028aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2029aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
2030b2fc195eSAndrew Gallatin 
2031b2fc195eSAndrew Gallatin 
20321e413cf9SAndrew Gallatin 	sc = ss->sc;
2033b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20341e413cf9SAndrew Gallatin 	tx = &ss->tx;
2035b2fc195eSAndrew Gallatin 
2036c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
203737d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2038c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2039c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2040c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2041c792928fSAndrew Gallatin 			goto drop;
2042c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
2043c792928fSAndrew Gallatin 	}
204437d89b0cSAndrew Gallatin #endif
2045b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2046b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2047b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2048aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2049b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2050adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2051b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2052b2fc195eSAndrew Gallatin 		   to defrag */
2053b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2054b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2055b2fc195eSAndrew Gallatin 			goto drop;
2056b2fc195eSAndrew Gallatin 		}
20571e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2058b2fc195eSAndrew Gallatin 		m = m_tmp;
2059b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2060b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2061aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2062b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2063b2fc195eSAndrew Gallatin 	}
2064adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2065aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2066aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2067b2fc195eSAndrew Gallatin 		goto drop;
2068b2fc195eSAndrew Gallatin 	}
2069b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2070b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20715e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2072b2fc195eSAndrew Gallatin 
207337d89b0cSAndrew Gallatin #if IFCAP_TSO4
2074aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2075aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20761e413cf9SAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, ip_off);
2077aed8e389SAndrew Gallatin 		return;
2078aed8e389SAndrew Gallatin 	}
207937d89b0cSAndrew Gallatin #endif
2080aed8e389SAndrew Gallatin 
2081b2fc195eSAndrew Gallatin 	req = tx->req_list;
2082b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20835e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20845e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2085b2fc195eSAndrew Gallatin 
2086b2fc195eSAndrew Gallatin 	/* checksum offloading? */
2087b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
2088aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2089aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
2090c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
2091c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
20921e413cf9SAndrew Gallatin 				   ss->scratch);
20931e413cf9SAndrew Gallatin 			ip = (struct ip *)(ss->scratch + ip_off);
2094aed8e389SAndrew Gallatin 		} else {
2095c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
2096aed8e389SAndrew Gallatin 		}
2097c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
2098b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20995e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2100b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
21015e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2102aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2103aed8e389SAndrew Gallatin 	} else {
2104aed8e389SAndrew Gallatin 		odd_flag = 0;
2105b2fc195eSAndrew Gallatin 	}
21065e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21075e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2108b2fc195eSAndrew Gallatin 
2109b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2110b2fc195eSAndrew Gallatin 	cum_len = 0;
2111aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21125e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2113b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2114b2fc195eSAndrew Gallatin 		req->addr_low =
21156d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2116b2fc195eSAndrew Gallatin 		req->addr_high =
21176d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2118b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2119b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2120b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2121b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2122b2fc195eSAndrew Gallatin 		else
2123b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21245e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21255e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21265e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2127aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2128b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2129b2fc195eSAndrew Gallatin 		seg++;
2130b2fc195eSAndrew Gallatin 		req++;
2131b2fc195eSAndrew Gallatin 		req->flags = 0;
2132b2fc195eSAndrew Gallatin 	}
2133b2fc195eSAndrew Gallatin 	req--;
2134b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2135b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2136b2fc195eSAndrew Gallatin 		req++;
2137b2fc195eSAndrew Gallatin 		req->addr_low =
21386d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2139b2fc195eSAndrew Gallatin 		req->addr_high =
21406d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2141b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21425e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21435e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21445e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21455e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2146aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2147b2fc195eSAndrew Gallatin 		cnt++;
2148b2fc195eSAndrew Gallatin 	}
21495e7d8541SAndrew Gallatin 
21505e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
21515e7d8541SAndrew Gallatin #if 0
21525e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21535e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21545e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21555e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21565e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21575e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21585e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21595e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21605e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21615e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21625e7d8541SAndrew Gallatin 	}
21635e7d8541SAndrew Gallatin 	printf("--------------\n");
21645e7d8541SAndrew Gallatin #endif
21655e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21666d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2167c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2168c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2169c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2170c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2171c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2172c6cb3e3fSAndrew Gallatin 		tx->activate++;
2173c6cb3e3fSAndrew Gallatin 		wmb();
2174c6cb3e3fSAndrew Gallatin 	}
2175c6cb3e3fSAndrew Gallatin #endif
2176b2fc195eSAndrew Gallatin 	return;
2177b2fc195eSAndrew Gallatin 
2178b2fc195eSAndrew Gallatin drop:
2179b2fc195eSAndrew Gallatin 	m_freem(m);
2180c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2181b2fc195eSAndrew Gallatin 	return;
2182b2fc195eSAndrew Gallatin }
2183b2fc195eSAndrew Gallatin 
2184c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2185c6cb3e3fSAndrew Gallatin static void
2186c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2187c6cb3e3fSAndrew Gallatin {
2188c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2189c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2190c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2191c6cb3e3fSAndrew Gallatin 	int slice;
2192b2fc195eSAndrew Gallatin 
2193c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2194c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2195c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2196c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2197c6cb3e3fSAndrew Gallatin 			m_freem(m);
2198c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2199c6cb3e3fSAndrew Gallatin 	}
2200c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2201c6cb3e3fSAndrew Gallatin }
22026d914a32SAndrew Gallatin 
2203c6cb3e3fSAndrew Gallatin static inline void
2204c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2205c6cb3e3fSAndrew Gallatin {
2206c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2207c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2208c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2209c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2210c6cb3e3fSAndrew Gallatin 
2211c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2212c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2213c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2214c6cb3e3fSAndrew Gallatin 
2215c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2216c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2217c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2218c6cb3e3fSAndrew Gallatin 			return;
2219c6cb3e3fSAndrew Gallatin 		}
2220c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2221c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2222c6cb3e3fSAndrew Gallatin 
2223c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2224c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2225c6cb3e3fSAndrew Gallatin 	}
2226c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2227c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2228c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2229c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2230c6cb3e3fSAndrew Gallatin 		tx->stall++;
2231c6cb3e3fSAndrew Gallatin 	}
2232c6cb3e3fSAndrew Gallatin }
2233c6cb3e3fSAndrew Gallatin 
2234c6cb3e3fSAndrew Gallatin static int
2235c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2236c6cb3e3fSAndrew Gallatin {
2237c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2238c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2239c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2240c6cb3e3fSAndrew Gallatin 	int err;
2241c6cb3e3fSAndrew Gallatin 
2242c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2243c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2244c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2245c6cb3e3fSAndrew Gallatin 
2246c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2247c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2248c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2249c6cb3e3fSAndrew Gallatin 		return (err);
2250c6cb3e3fSAndrew Gallatin 	}
2251c6cb3e3fSAndrew Gallatin 
2252193cbc4dSMax Laier 	if (!drbr_needs_enqueue(ifp, tx->br) &&
2253c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2254c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2255c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2256c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2257c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2258c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2259c6cb3e3fSAndrew Gallatin 		return (err);
2260c6cb3e3fSAndrew Gallatin 	}
2261c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2262c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2263c6cb3e3fSAndrew Gallatin 	return (0);
2264c6cb3e3fSAndrew Gallatin }
2265c6cb3e3fSAndrew Gallatin 
2266c6cb3e3fSAndrew Gallatin static int
2267c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2268c6cb3e3fSAndrew Gallatin {
2269c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2270c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2271c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2272c6cb3e3fSAndrew Gallatin 	int err = 0;
2273c6cb3e3fSAndrew Gallatin 	int slice;
2274c6cb3e3fSAndrew Gallatin 
2275c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2276c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2277c6cb3e3fSAndrew Gallatin 
2278c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2279c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2280c6cb3e3fSAndrew Gallatin 
2281c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2282c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2283c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2284c6cb3e3fSAndrew Gallatin 	} else {
2285c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2286c6cb3e3fSAndrew Gallatin 	}
2287c6cb3e3fSAndrew Gallatin 
2288c6cb3e3fSAndrew Gallatin 	return (err);
2289c6cb3e3fSAndrew Gallatin }
2290c6cb3e3fSAndrew Gallatin 
2291c6cb3e3fSAndrew Gallatin #else
22926d914a32SAndrew Gallatin 
22936d914a32SAndrew Gallatin static inline void
22941e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2295b2fc195eSAndrew Gallatin {
22961e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2297b2fc195eSAndrew Gallatin 	struct mbuf *m;
2298b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
22991e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2300b2fc195eSAndrew Gallatin 
23011e413cf9SAndrew Gallatin 	sc = ss->sc;
2302b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23031e413cf9SAndrew Gallatin 	tx = &ss->tx;
2304adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
23056d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
23066d914a32SAndrew Gallatin 		if (m == NULL) {
23076d914a32SAndrew Gallatin 			return;
23086d914a32SAndrew Gallatin 		}
2309b2fc195eSAndrew Gallatin 		/* let BPF see it */
2310b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2311b2fc195eSAndrew Gallatin 
2312b2fc195eSAndrew Gallatin 		/* give it to the nic */
23131e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
23146d914a32SAndrew Gallatin 	}
23156d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2316a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2317b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2318adae7080SAndrew Gallatin 		tx->stall++;
2319a82c2581SAndrew Gallatin 	}
2320b2fc195eSAndrew Gallatin }
2321c6cb3e3fSAndrew Gallatin #endif
2322b2fc195eSAndrew Gallatin static void
23236d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2324b2fc195eSAndrew Gallatin {
23256d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
23261e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2327b2fc195eSAndrew Gallatin 
23281e413cf9SAndrew Gallatin 	/* only use the first slice for now */
23291e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
23301e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
23311e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
23321e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2333b2fc195eSAndrew Gallatin }
2334b2fc195eSAndrew Gallatin 
23355e7d8541SAndrew Gallatin /*
23365e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23375e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23385e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23395e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23405e7d8541SAndrew Gallatin  * in a burst
23415e7d8541SAndrew Gallatin  */
23425e7d8541SAndrew Gallatin static inline void
23435e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23445e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
23455e7d8541SAndrew Gallatin {
23465e7d8541SAndrew Gallatin 	uint32_t low;
23475e7d8541SAndrew Gallatin 
23485e7d8541SAndrew Gallatin 	low = src->addr_low;
23495e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2350a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
235173c7c83fSAndrew Gallatin 	wmb();
2352a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
235373c7c83fSAndrew Gallatin 	wmb();
235440385a5fSAndrew Gallatin 	src->addr_low = low;
23555e7d8541SAndrew Gallatin 	dst->addr_low = low;
235673c7c83fSAndrew Gallatin 	wmb();
23575e7d8541SAndrew Gallatin }
23585e7d8541SAndrew Gallatin 
2359b2fc195eSAndrew Gallatin static int
23601e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2361b2fc195eSAndrew Gallatin {
2362b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2363b2fc195eSAndrew Gallatin 	struct mbuf *m;
23641e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2365b2fc195eSAndrew Gallatin 	int cnt, err;
2366b2fc195eSAndrew Gallatin 
2367b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
2368b2fc195eSAndrew Gallatin 	if (m == NULL) {
2369b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2370b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2371b2fc195eSAndrew Gallatin 		goto done;
2372b2fc195eSAndrew Gallatin 	}
2373b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2374b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2375b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2376b2fc195eSAndrew Gallatin 	if (err != 0) {
2377b2fc195eSAndrew Gallatin 		m_free(m);
2378b2fc195eSAndrew Gallatin 		goto done;
2379b2fc195eSAndrew Gallatin 	}
2380b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2381b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23826d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2383b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23846d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2385b2fc195eSAndrew Gallatin 
2386b2fc195eSAndrew Gallatin done:
2387adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2388adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2389b2fc195eSAndrew Gallatin 	return err;
2390b2fc195eSAndrew Gallatin }
2391b2fc195eSAndrew Gallatin 
2392b2fc195eSAndrew Gallatin static int
23931e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2394b2fc195eSAndrew Gallatin {
2395053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2396b2fc195eSAndrew Gallatin 	struct mbuf *m;
23971e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2398053e637fSAndrew Gallatin 	int cnt, err, i;
2399b2fc195eSAndrew Gallatin 
2400a0394e33SAndrew Gallatin 	if (rx->cl_size == MCLBYTES)
2401a0394e33SAndrew Gallatin 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2402a0394e33SAndrew Gallatin 	else
2403053e637fSAndrew Gallatin 		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2404b2fc195eSAndrew Gallatin 	if (m == NULL) {
2405b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2406b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2407b2fc195eSAndrew Gallatin 		goto done;
2408b2fc195eSAndrew Gallatin 	}
24094d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2410b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2411053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2412b2fc195eSAndrew Gallatin 	if (err != 0) {
2413b2fc195eSAndrew Gallatin 		m_free(m);
2414b2fc195eSAndrew Gallatin 		goto done;
2415b2fc195eSAndrew Gallatin 	}
2416b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2417b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2418b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2419b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2420b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2421053e637fSAndrew Gallatin 
2422b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2423b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2424053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2425053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2426053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2427053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2428053e637fSAndrew Gallatin        }
2429b0f7b922SAndrew Gallatin #endif
2430b2fc195eSAndrew Gallatin 
2431b2fc195eSAndrew Gallatin done:
2432053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2433b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
24345e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
24355e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2436b2fc195eSAndrew Gallatin 		}
2437053e637fSAndrew Gallatin 		idx++;
2438053e637fSAndrew Gallatin 	}
2439b2fc195eSAndrew Gallatin 	return err;
2440b2fc195eSAndrew Gallatin }
2441b2fc195eSAndrew Gallatin 
24429b03b0f3SAndrew Gallatin /*
24439b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
24449b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
24459b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
24469b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2447053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2448053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
24499b03b0f3SAndrew Gallatin  */
24509b03b0f3SAndrew Gallatin 
2451053e637fSAndrew Gallatin static inline uint16_t
2452053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2453053e637fSAndrew Gallatin {
2454053e637fSAndrew Gallatin 	struct ether_header *eh;
2455053e637fSAndrew Gallatin 	struct ip *ip;
2456053e637fSAndrew Gallatin 	uint16_t c;
2457053e637fSAndrew Gallatin 
2458053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2459053e637fSAndrew Gallatin 
2460053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2461053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2462053e637fSAndrew Gallatin 		return 1;
2463053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2464053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2465053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2466053e637fSAndrew Gallatin 		return 1;
2467eb6219e3SAndrew Gallatin #ifdef INET
2468053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2469053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2470053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2471eb6219e3SAndrew Gallatin #else
2472eb6219e3SAndrew Gallatin 	c = 1;
2473eb6219e3SAndrew Gallatin #endif
2474053e637fSAndrew Gallatin 	c ^= 0xffff;
2475053e637fSAndrew Gallatin 	return (c);
24765e7d8541SAndrew Gallatin }
2477053e637fSAndrew Gallatin 
2478c792928fSAndrew Gallatin static void
2479c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2480c792928fSAndrew Gallatin {
2481c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2482c792928fSAndrew Gallatin 	struct ether_header *eh;
2483c792928fSAndrew Gallatin 	uint32_t partial;
2484c792928fSAndrew Gallatin 
2485c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2486c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2487c792928fSAndrew Gallatin 
2488c792928fSAndrew Gallatin 	/*
2489c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2490c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2491c792928fSAndrew Gallatin 	 * header.
2492c792928fSAndrew Gallatin 	 */
2493c792928fSAndrew Gallatin 
2494c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2495c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2496c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2497c792928fSAndrew Gallatin 	(*csum) += ~partial;
2498c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2499c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2500c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2501c792928fSAndrew Gallatin 
2502c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2503c792928fSAndrew Gallatin 	   later consumers expect this */
2504c792928fSAndrew Gallatin 	*csum = htons(*csum);
2505c792928fSAndrew Gallatin 
2506c792928fSAndrew Gallatin 	/* save the tag */
250737d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2508c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
250937d89b0cSAndrew Gallatin #else
251037d89b0cSAndrew Gallatin 	{
251137d89b0cSAndrew Gallatin 		struct m_tag *mtag;
251237d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
251337d89b0cSAndrew Gallatin 				   M_NOWAIT);
251437d89b0cSAndrew Gallatin 		if (mtag == NULL)
251537d89b0cSAndrew Gallatin 			return;
251637d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
251737d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
251837d89b0cSAndrew Gallatin 	}
251937d89b0cSAndrew Gallatin 
252037d89b0cSAndrew Gallatin #endif
252137d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2522c792928fSAndrew Gallatin 
2523c792928fSAndrew Gallatin 	/*
2524c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2525c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2526c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2527c792928fSAndrew Gallatin 	 * type field is already in place.
2528c792928fSAndrew Gallatin 	 */
2529c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2530c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2531c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2532c792928fSAndrew Gallatin }
2533c792928fSAndrew Gallatin 
25345e7d8541SAndrew Gallatin 
25355e7d8541SAndrew Gallatin static inline void
25361e413cf9SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2537b2fc195eSAndrew Gallatin {
25381e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2539b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2540053e637fSAndrew Gallatin 	struct mbuf *m;
2541c792928fSAndrew Gallatin 	struct ether_header *eh;
25421e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2543053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2544b2fc195eSAndrew Gallatin 	int idx;
2545053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2546b2fc195eSAndrew Gallatin 
25471e413cf9SAndrew Gallatin 	sc = ss->sc;
2548b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
25491e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2550b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2551053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2552b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2553b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2554b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25551e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2556053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2557053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2558053e637fSAndrew Gallatin 		return;
2559b2fc195eSAndrew Gallatin 	}
2560053e637fSAndrew Gallatin 
2561b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2562b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2563b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2564b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2565b2fc195eSAndrew Gallatin 
2566b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2567b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2568b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2569b2fc195eSAndrew Gallatin 
2570053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2571053e637fSAndrew Gallatin 	 * aligned */
25725e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2573b2fc195eSAndrew Gallatin 
2574053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2575053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25761e413cf9SAndrew Gallatin 	ss->ipackets++;
2577c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2578c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2579c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2580c792928fSAndrew Gallatin 	}
2581b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2582053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
25831e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2584b2fc195eSAndrew Gallatin 			return;
2585053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2586053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2587053e637fSAndrew Gallatin 		   checksum is good */
2588053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2589053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2590b2fc195eSAndrew Gallatin 	}
2591c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2592c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2593c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2594c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2595c6cb3e3fSAndrew Gallatin 	}
2596053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2597053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2598b2fc195eSAndrew Gallatin }
2599b2fc195eSAndrew Gallatin 
2600b2fc195eSAndrew Gallatin static inline void
26011e413cf9SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2602b2fc195eSAndrew Gallatin {
26031e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2604b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2605c792928fSAndrew Gallatin 	struct ether_header *eh;
2606b2fc195eSAndrew Gallatin 	struct mbuf *m;
26071e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2608b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2609b2fc195eSAndrew Gallatin 	int idx;
2610053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2611b2fc195eSAndrew Gallatin 
26121e413cf9SAndrew Gallatin 	sc = ss->sc;
2613b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26141e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2615b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2616b2fc195eSAndrew Gallatin 	rx->cnt++;
2617b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2618b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2619b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26201e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2621b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2622b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2623b2fc195eSAndrew Gallatin 		return;
2624b2fc195eSAndrew Gallatin 	}
2625b2fc195eSAndrew Gallatin 
2626b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2627b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2628b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2629b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2630b2fc195eSAndrew Gallatin 
2631b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2632b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2633b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2634b2fc195eSAndrew Gallatin 
2635b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2636b2fc195eSAndrew Gallatin 	 * aligned */
26375e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2638b2fc195eSAndrew Gallatin 
26399b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
26409b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
26411e413cf9SAndrew Gallatin 	ss->ipackets++;
2642c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2643c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2644c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2645c792928fSAndrew Gallatin 	}
2646b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2647053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
26481e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2649053e637fSAndrew Gallatin 			return;
2650053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2651053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2652053e637fSAndrew Gallatin 		   checksum is good */
2653053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2654053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2655053e637fSAndrew Gallatin 	}
2656c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2657c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2658c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2659c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2660c6cb3e3fSAndrew Gallatin 	}
2661b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2662b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2663b2fc195eSAndrew Gallatin }
2664b2fc195eSAndrew Gallatin 
2665b2fc195eSAndrew Gallatin static inline void
26661e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
26675e7d8541SAndrew Gallatin {
26681e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26695e7d8541SAndrew Gallatin 	int limit = 0;
26705e7d8541SAndrew Gallatin 	uint16_t length;
26715e7d8541SAndrew Gallatin 	uint16_t checksum;
26725e7d8541SAndrew Gallatin 
26735e7d8541SAndrew Gallatin 
26745e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
26755e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
26765e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2677053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2678b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
26791e413cf9SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum);
26805e7d8541SAndrew Gallatin 		else
26811e413cf9SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum);
26825e7d8541SAndrew Gallatin 		rx_done->cnt++;
2683adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
26845e7d8541SAndrew Gallatin 
26855e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2686f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
26875e7d8541SAndrew Gallatin 			break;
2688053e637fSAndrew Gallatin 	}
2689eb6219e3SAndrew Gallatin #ifdef INET
26901e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_active)) {
2691eb6219e3SAndrew Gallatin 		struct lro_entry *lro = SLIST_FIRST(&ss->lro_active);
26921e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
26931e413cf9SAndrew Gallatin 		mxge_lro_flush(ss, lro);
26945e7d8541SAndrew Gallatin 	}
2695eb6219e3SAndrew Gallatin #endif
26965e7d8541SAndrew Gallatin }
26975e7d8541SAndrew Gallatin 
26985e7d8541SAndrew Gallatin 
26995e7d8541SAndrew Gallatin static inline void
27001e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2701b2fc195eSAndrew Gallatin {
2702b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
27031e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2704b2fc195eSAndrew Gallatin 	struct mbuf *m;
2705b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2706f616ebc7SAndrew Gallatin 	int idx;
2707c6cb3e3fSAndrew Gallatin 	int *flags;
2708b2fc195eSAndrew Gallatin 
27091e413cf9SAndrew Gallatin 	tx = &ss->tx;
27101e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
27115e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2712b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2713b2fc195eSAndrew Gallatin 		tx->done++;
2714b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2715b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2716b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2717b2fc195eSAndrew Gallatin 		if (m != NULL) {
271871032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
271971032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
272071032832SAndrew Gallatin 				ss->omcasts++;
2721c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2722b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2723b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2724b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2725b2fc195eSAndrew Gallatin 			m_freem(m);
2726b2fc195eSAndrew Gallatin 		}
27275e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
27285e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
27295e7d8541SAndrew Gallatin 			tx->pkt_done++;
27305e7d8541SAndrew Gallatin 		}
2731b2fc195eSAndrew Gallatin 	}
2732b2fc195eSAndrew Gallatin 
2733b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2734b2fc195eSAndrew Gallatin            its OK to send packets */
2735c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2736c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2737c6cb3e3fSAndrew Gallatin #else
2738c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2739c6cb3e3fSAndrew Gallatin #endif
27401e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2741c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2742c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2743c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
27441e413cf9SAndrew Gallatin 		ss->tx.wake++;
27451e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2746b2fc195eSAndrew Gallatin 	}
2747c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2748c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2749c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2750c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2751c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2752c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2753c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2754c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2755c6cb3e3fSAndrew Gallatin 			wmb();
2756c6cb3e3fSAndrew Gallatin 		}
2757c6cb3e3fSAndrew Gallatin 	}
2758c6cb3e3fSAndrew Gallatin #endif
2759c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2760c6cb3e3fSAndrew Gallatin 
2761b2fc195eSAndrew Gallatin }
2762b2fc195eSAndrew Gallatin 
276301638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2764c587e59fSAndrew Gallatin {
2765c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2766c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2767c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2768c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
276901638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2770c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2771c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2772c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2773c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2774c587e59fSAndrew Gallatin };
277501638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
277601638550SAndrew Gallatin {
277751bc2092SAndrew Gallatin 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
27784ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
277901638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
278001638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
278101638550SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"}
278201638550SAndrew Gallatin };
2783c587e59fSAndrew Gallatin 
2784c587e59fSAndrew Gallatin static void
2785c587e59fSAndrew Gallatin mxge_set_media(mxge_softc_t *sc, int type)
2786c587e59fSAndrew Gallatin {
2787c587e59fSAndrew Gallatin 	sc->media_flags |= type;
2788c587e59fSAndrew Gallatin 	ifmedia_add(&sc->media, sc->media_flags, 0, NULL);
2789c587e59fSAndrew Gallatin 	ifmedia_set(&sc->media, sc->media_flags);
2790c587e59fSAndrew Gallatin }
2791c587e59fSAndrew Gallatin 
2792c587e59fSAndrew Gallatin 
2793c587e59fSAndrew Gallatin /*
2794c587e59fSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2795c587e59fSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2796c587e59fSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2797c587e59fSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2798c587e59fSAndrew Gallatin  * than in the interrupt handler itself.   This need only be done
2799c587e59fSAndrew Gallatin  * once, not each time the link is up.
2800c587e59fSAndrew Gallatin  */
2801c587e59fSAndrew Gallatin static void
2802c587e59fSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2803c587e59fSAndrew Gallatin {
2804c587e59fSAndrew Gallatin 	mxge_cmd_t cmd;
280501638550SAndrew Gallatin 	char *cage_type;
2806c587e59fSAndrew Gallatin 	char *ptr;
280701638550SAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
280801638550SAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
280901638550SAndrew Gallatin 	uint32_t byte;
2810c587e59fSAndrew Gallatin 
2811c587e59fSAndrew Gallatin 	sc->need_media_probe = 0;
2812c587e59fSAndrew Gallatin 
2813c587e59fSAndrew Gallatin 	/* if we've already set a media type, we're done */
2814c587e59fSAndrew Gallatin 	if (sc->media_flags  != (IFM_ETHER | IFM_AUTO))
2815c587e59fSAndrew Gallatin 		return;
2816c587e59fSAndrew Gallatin 
2817c587e59fSAndrew Gallatin 	/*
2818c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2819c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2820c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2821c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2822c587e59fSAndrew Gallatin 	 */
2823c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2824c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2825c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2826c587e59fSAndrew Gallatin 	}
2827c587e59fSAndrew Gallatin 
2828c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
282937d89b0cSAndrew Gallatin 		ptr = index(ptr, '-');
2830c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2831c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2832c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2833c587e59fSAndrew Gallatin 			return;
2834c587e59fSAndrew Gallatin 		}
2835c587e59fSAndrew Gallatin 	}
2836c587e59fSAndrew Gallatin 	if (*ptr == 'C') {
283701638550SAndrew Gallatin 		/* -C is CX4 */
2838c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2839c587e59fSAndrew Gallatin 		return;
2840c587e59fSAndrew Gallatin 	}
2841c587e59fSAndrew Gallatin 	else if (*ptr == 'Q') {
284201638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2843c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2844c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2845c587e59fSAndrew Gallatin 		return;
2846c587e59fSAndrew Gallatin 	}
2847c587e59fSAndrew Gallatin 
284801638550SAndrew Gallatin 	if (*ptr == 'R') {
284901638550SAndrew Gallatin 		/* -R is XFP */
285001638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
285101638550SAndrew Gallatin 		mxge_media_type_entries =
285201638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types) /
285301638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types[0]);
285401638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
285501638550SAndrew Gallatin 		cage_type = "XFP";
285601638550SAndrew Gallatin 	}
285701638550SAndrew Gallatin 
285801638550SAndrew Gallatin 	if (*ptr == 'S' || *(ptr +1) == 'S') {
285901638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
286001638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
286101638550SAndrew Gallatin 		mxge_media_type_entries =
286201638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types) /
286301638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types[0]);
286401638550SAndrew Gallatin 		cage_type = "SFP+";
286501638550SAndrew Gallatin 		byte = 3;
286601638550SAndrew Gallatin 	}
286701638550SAndrew Gallatin 
286801638550SAndrew Gallatin 	if (mxge_media_types == NULL) {
2869c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2870c587e59fSAndrew Gallatin 		return;
2871c587e59fSAndrew Gallatin 	}
2872c587e59fSAndrew Gallatin 
2873c587e59fSAndrew Gallatin 	/*
2874c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2875c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2876c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2877c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2878c587e59fSAndrew Gallatin 	 * a millisecond
2879c587e59fSAndrew Gallatin 	 */
2880c587e59fSAndrew Gallatin 
2881c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
288201638550SAndrew Gallatin 	cmd.data1 = byte;
288301638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
288401638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2885c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2886c587e59fSAndrew Gallatin 	}
288701638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
288801638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2889c587e59fSAndrew Gallatin 	}
2890c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2891c587e59fSAndrew Gallatin 		return;
2892c587e59fSAndrew Gallatin 	}
2893c587e59fSAndrew Gallatin 
2894c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
289501638550SAndrew Gallatin 	cmd.data0 = byte;
289601638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2897c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2898c587e59fSAndrew Gallatin 		DELAY(1000);
289901638550SAndrew Gallatin 		cmd.data0 = byte;
290001638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2901c587e59fSAndrew Gallatin 	}
2902c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
290301638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
290401638550SAndrew Gallatin 			      cage_type, err, ms);
2905c587e59fSAndrew Gallatin 		return;
2906c587e59fSAndrew Gallatin 	}
2907c587e59fSAndrew Gallatin 
2908c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2909c587e59fSAndrew Gallatin 		if (mxge_verbose)
291001638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2911c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
291251bc2092SAndrew Gallatin 		mxge_set_media(sc, mxge_media_types[0].flag);
2913c587e59fSAndrew Gallatin 		return;
2914c587e59fSAndrew Gallatin 	}
291501638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
2916c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2917c587e59fSAndrew Gallatin 			if (mxge_verbose)
291801638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
291901638550SAndrew Gallatin 					      cage_type,
2920c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2921c587e59fSAndrew Gallatin 
2922c587e59fSAndrew Gallatin 			mxge_set_media(sc, mxge_media_types[i].flag);
2923c587e59fSAndrew Gallatin 			return;
2924c587e59fSAndrew Gallatin 		}
2925c587e59fSAndrew Gallatin 	}
292601638550SAndrew Gallatin 	device_printf(sc->dev, "%s media 0x%x unknown\n", cage_type,
292701638550SAndrew Gallatin 		      cmd.data0);
2928c587e59fSAndrew Gallatin 
2929c587e59fSAndrew Gallatin 	return;
2930c587e59fSAndrew Gallatin }
2931c587e59fSAndrew Gallatin 
2932b2fc195eSAndrew Gallatin static void
29336d87a65dSAndrew Gallatin mxge_intr(void *arg)
2934b2fc195eSAndrew Gallatin {
29351e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
29361e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
29371e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
29381e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
29391e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
29405e7d8541SAndrew Gallatin 	uint32_t send_done_count;
29415e7d8541SAndrew Gallatin 	uint8_t valid;
2942b2fc195eSAndrew Gallatin 
2943b2fc195eSAndrew Gallatin 
2944c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
29451e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
29461e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
29471e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
29481e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
29491e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
29501e413cf9SAndrew Gallatin 		return;
29511e413cf9SAndrew Gallatin 	}
2952c6cb3e3fSAndrew Gallatin #endif
29531e413cf9SAndrew Gallatin 
29545e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
29555e7d8541SAndrew Gallatin 	if (!stats->valid) {
29565e7d8541SAndrew Gallatin 		return;
2957b2fc195eSAndrew Gallatin 	}
29585e7d8541SAndrew Gallatin 	valid = stats->valid;
2959b2fc195eSAndrew Gallatin 
296091ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
29615e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
29625e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
29635e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
29645e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
29655e7d8541SAndrew Gallatin 			stats->valid = 0;
2966dc8731d4SAndrew Gallatin 	} else {
2967dc8731d4SAndrew Gallatin 		stats->valid = 0;
2968dc8731d4SAndrew Gallatin 	}
2969dc8731d4SAndrew Gallatin 
2970dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
29715e7d8541SAndrew Gallatin 	do {
29725e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
29735e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
29745e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
29755e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
2976c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
29771e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
29781e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
29795e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2980b2fc195eSAndrew Gallatin 		}
298191ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
298273c7c83fSAndrew Gallatin 			wmb();
29835e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2984b2fc195eSAndrew Gallatin 
2985c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
2986c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
29875e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
29885e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2989b2fc195eSAndrew Gallatin 			if (sc->link_state) {
29905e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
29915e7d8541SAndrew Gallatin 				if (mxge_verbose)
29925e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2993b2fc195eSAndrew Gallatin 			} else {
29945e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
29955e7d8541SAndrew Gallatin 				if (mxge_verbose)
29965e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2997b2fc195eSAndrew Gallatin 			}
2998c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
2999b2fc195eSAndrew Gallatin 		}
3000b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
30011e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
3002b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
30031e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
30045e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
30055e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
30065e7d8541SAndrew Gallatin 		}
3007c587e59fSAndrew Gallatin 
3008c587e59fSAndrew Gallatin 		if (stats->link_down) {
30095e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3010c587e59fSAndrew Gallatin 			sc->link_state = 0;
3011c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3012c587e59fSAndrew Gallatin 		}
3013b2fc195eSAndrew Gallatin 	}
3014b2fc195eSAndrew Gallatin 
30155e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
30165e7d8541SAndrew Gallatin 	if (valid & 0x1)
30171e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
30181e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3019b2fc195eSAndrew Gallatin }
3020b2fc195eSAndrew Gallatin 
3021b2fc195eSAndrew Gallatin static void
30226d87a65dSAndrew Gallatin mxge_init(void *arg)
3023b2fc195eSAndrew Gallatin {
3024b2fc195eSAndrew Gallatin }
3025b2fc195eSAndrew Gallatin 
3026b2fc195eSAndrew Gallatin 
3027b2fc195eSAndrew Gallatin 
3028b2fc195eSAndrew Gallatin static void
30291e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
30301e413cf9SAndrew Gallatin {
30311e413cf9SAndrew Gallatin 	struct lro_entry *lro_entry;
30321e413cf9SAndrew Gallatin 	int i;
30331e413cf9SAndrew Gallatin 
30341e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_free)) {
30351e413cf9SAndrew Gallatin 		lro_entry = SLIST_FIRST(&ss->lro_free);
30361e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
30371e413cf9SAndrew Gallatin 		free(lro_entry, M_DEVBUF);
30381e413cf9SAndrew Gallatin 	}
30391e413cf9SAndrew Gallatin 
30401e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
30411e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
30421e413cf9SAndrew Gallatin 			continue;
30431e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
30441e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
30451e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
30461e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
30471e413cf9SAndrew Gallatin 	}
30481e413cf9SAndrew Gallatin 
30491e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
30501e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
30511e413cf9SAndrew Gallatin 			continue;
30521e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
30531e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
30541e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
30551e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
30561e413cf9SAndrew Gallatin 	}
30571e413cf9SAndrew Gallatin 
30581e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
30591e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30601e413cf9SAndrew Gallatin 		return;
30611e413cf9SAndrew Gallatin 
30621e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30631e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
30641e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
30651e413cf9SAndrew Gallatin 			continue;
30661e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
30671e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
30681e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
30691e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
30701e413cf9SAndrew Gallatin 	}
30711e413cf9SAndrew Gallatin }
30721e413cf9SAndrew Gallatin 
30731e413cf9SAndrew Gallatin static void
30746d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3075b2fc195eSAndrew Gallatin {
30761e413cf9SAndrew Gallatin 	int slice;
30771e413cf9SAndrew Gallatin 
30781e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
30791e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
30801e413cf9SAndrew Gallatin }
30811e413cf9SAndrew Gallatin 
30821e413cf9SAndrew Gallatin static void
30831e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
30841e413cf9SAndrew Gallatin {
3085b2fc195eSAndrew Gallatin 	int i;
3086b2fc195eSAndrew Gallatin 
3087b2fc195eSAndrew Gallatin 
30881e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
30891e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
30901e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3091b2fc195eSAndrew Gallatin 
30921e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
30931e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
30941e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
30951e413cf9SAndrew Gallatin 
30961e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
30971e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
30981e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
30991e413cf9SAndrew Gallatin 
31001e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
31011e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
31021e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
31031e413cf9SAndrew Gallatin 
31041e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
31051e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
31061e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
31071e413cf9SAndrew Gallatin 
31081e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
31091e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
31101e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
31111e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
31121e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3113b2fc195eSAndrew Gallatin 			}
31141e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
31151e413cf9SAndrew Gallatin 		}
31161e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
31171e413cf9SAndrew Gallatin 	}
31181e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
31191e413cf9SAndrew Gallatin 
31201e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
31211e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
31221e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
31231e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
31241e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
31251e413cf9SAndrew Gallatin 			}
31261e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
31271e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
31281e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
31291e413cf9SAndrew Gallatin 		}
31301e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
31311e413cf9SAndrew Gallatin 	}
31321e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
31331e413cf9SAndrew Gallatin 
31341e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
31351e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
31361e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
31371e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
31381e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
31391e413cf9SAndrew Gallatin 			}
31401e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
31411e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
31421e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
31431e413cf9SAndrew Gallatin 		}
31441e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
31451e413cf9SAndrew Gallatin 	}
31461e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3147b2fc195eSAndrew Gallatin }
3148b2fc195eSAndrew Gallatin 
3149b2fc195eSAndrew Gallatin static void
31506d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3151b2fc195eSAndrew Gallatin {
31521e413cf9SAndrew Gallatin 	int slice;
3153b2fc195eSAndrew Gallatin 
31541e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31551e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3156c2657176SAndrew Gallatin }
3157b2fc195eSAndrew Gallatin 
3158b2fc195eSAndrew Gallatin static int
31591e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
31601e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3161b2fc195eSAndrew Gallatin {
31621e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
31631e413cf9SAndrew Gallatin 	size_t bytes;
31641e413cf9SAndrew Gallatin 	int err, i;
3165b2fc195eSAndrew Gallatin 
3166b2fc195eSAndrew Gallatin 	err = ENOMEM;
3167b2fc195eSAndrew Gallatin 
31681e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3169adae7080SAndrew Gallatin 
31701e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
31711e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3172aed8e389SAndrew Gallatin 
3173b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
31741e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
31751e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31761e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow == NULL)
3177c2ede4b3SMartin Blapp 		return err;
3178b2fc195eSAndrew Gallatin 
31791e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
31801e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31811e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow == NULL)
3182c2ede4b3SMartin Blapp 		return err;
3183b2fc195eSAndrew Gallatin 
31841e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
31851e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
31861e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31871e413cf9SAndrew Gallatin 	if (ss->rx_small.info == NULL)
3188c2ede4b3SMartin Blapp 		return err;
3189b2fc195eSAndrew Gallatin 
31901e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
31911e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31921e413cf9SAndrew Gallatin 	if (ss->rx_big.info == NULL)
3193c2ede4b3SMartin Blapp 		return err;
3194b2fc195eSAndrew Gallatin 
31951e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3196b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3197b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3198b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3199b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3200b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3201b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3202b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3203b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3204b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3205b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3206b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32071e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3208b2fc195eSAndrew Gallatin 	if (err != 0) {
3209b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3210b2fc195eSAndrew Gallatin 			      err);
3211c2ede4b3SMartin Blapp 		return err;
3212b2fc195eSAndrew Gallatin 	}
3213b2fc195eSAndrew Gallatin 
3214b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3215b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3216b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3217b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3218b0f7b922SAndrew Gallatin #else
3219b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3220b0f7b922SAndrew Gallatin #endif
3221b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3222b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3223b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3224053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3225b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3226053e637fSAndrew Gallatin 				 3,			/* num segs */
3227b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3228b0f7b922SAndrew Gallatin #else
3229b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3230b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3231b0f7b922SAndrew Gallatin #endif
3232b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3233b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32341e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3235b2fc195eSAndrew Gallatin 	if (err != 0) {
3236b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3237b2fc195eSAndrew Gallatin 			      err);
3238c2ede4b3SMartin Blapp 		return err;
3239b2fc195eSAndrew Gallatin 	}
32401e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
32411e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
32421e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3243b2fc195eSAndrew Gallatin 		if (err != 0) {
3244b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3245b2fc195eSAndrew Gallatin 				      err);
3246c2ede4b3SMartin Blapp 			return err;
3247b2fc195eSAndrew Gallatin 		}
3248b2fc195eSAndrew Gallatin 	}
32491e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
32501e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3251b2fc195eSAndrew Gallatin 	if (err != 0) {
3252b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3253b2fc195eSAndrew Gallatin 			      err);
3254c2ede4b3SMartin Blapp 		return err;
3255b2fc195eSAndrew Gallatin 	}
3256b2fc195eSAndrew Gallatin 
32571e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32581e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
32591e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3260b2fc195eSAndrew Gallatin 		if (err != 0) {
3261b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3262b2fc195eSAndrew Gallatin 				      err);
3263c2ede4b3SMartin Blapp 			return err;
3264b2fc195eSAndrew Gallatin 		}
3265b2fc195eSAndrew Gallatin 	}
32661e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
32671e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3268b2fc195eSAndrew Gallatin 	if (err != 0) {
3269b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3270b2fc195eSAndrew Gallatin 			      err);
3271c2ede4b3SMartin Blapp 		return err;
32721e413cf9SAndrew Gallatin 	}
32731e413cf9SAndrew Gallatin 
32741e413cf9SAndrew Gallatin 	/* now allocate TX resouces */
32751e413cf9SAndrew Gallatin 
3276c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
32771e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
32781e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
32791e413cf9SAndrew Gallatin 		return 0;
3280c6cb3e3fSAndrew Gallatin #endif
32811e413cf9SAndrew Gallatin 
32821e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
32831e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
32841e413cf9SAndrew Gallatin 
32851e413cf9SAndrew Gallatin 
32861e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
32871e413cf9SAndrew Gallatin 	bytes = 8 +
32881e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
32891e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
32901e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes == NULL)
3291c2ede4b3SMartin Blapp 		return err;
32921e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
32931e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
32941e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
32951e413cf9SAndrew Gallatin 
32961e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
32971e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
32981e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
32991e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
33001e413cf9SAndrew Gallatin 	if (ss->tx.seg_list == NULL)
3301c2ede4b3SMartin Blapp 		return err;
33021e413cf9SAndrew Gallatin 
33031e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
33041e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
33051e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
33061e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
3307c2ede4b3SMartin Blapp 		return err;
33081e413cf9SAndrew Gallatin 
33091e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
33101e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
33111e413cf9SAndrew Gallatin 				 1,			/* alignment */
33121e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
33131e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
33141e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
33151e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
33161e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
33171e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
33181e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
33191e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
33201e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
33211e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
33221e413cf9SAndrew Gallatin 
33231e413cf9SAndrew Gallatin 	if (err != 0) {
33241e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
33251e413cf9SAndrew Gallatin 			      err);
3326c2ede4b3SMartin Blapp 		return err;
33271e413cf9SAndrew Gallatin 	}
33281e413cf9SAndrew Gallatin 
33291e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
33301e413cf9SAndrew Gallatin 	   in the ring */
33311e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
33321e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
33331e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
33341e413cf9SAndrew Gallatin 		if (err != 0) {
33351e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
33361e413cf9SAndrew Gallatin 				      err);
3337c2ede4b3SMartin Blapp 			return err;
33381e413cf9SAndrew Gallatin 		}
3339b2fc195eSAndrew Gallatin 	}
3340b2fc195eSAndrew Gallatin 	return 0;
3341b2fc195eSAndrew Gallatin 
3342b2fc195eSAndrew Gallatin }
3343b2fc195eSAndrew Gallatin 
33441e413cf9SAndrew Gallatin static int
33451e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
33461e413cf9SAndrew Gallatin {
33471e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
33481e413cf9SAndrew Gallatin 	int tx_ring_size;
33491e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
33501e413cf9SAndrew Gallatin 	int err, slice;
33511e413cf9SAndrew Gallatin 
33521e413cf9SAndrew Gallatin 	/* get ring sizes */
33531e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33541e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
33551e413cf9SAndrew Gallatin 	if (err != 0) {
33561e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33571e413cf9SAndrew Gallatin 		goto abort;
33581e413cf9SAndrew Gallatin 	}
33591e413cf9SAndrew Gallatin 
33601e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
33611e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
33621e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
33631e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
33641e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
33651e413cf9SAndrew Gallatin 
33661e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33671e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
33681e413cf9SAndrew Gallatin 					     rx_ring_entries,
33691e413cf9SAndrew Gallatin 					     tx_ring_entries);
33701e413cf9SAndrew Gallatin 		if (err != 0)
33711e413cf9SAndrew Gallatin 			goto abort;
33721e413cf9SAndrew Gallatin 	}
33731e413cf9SAndrew Gallatin 	return 0;
33741e413cf9SAndrew Gallatin 
33751e413cf9SAndrew Gallatin abort:
33761e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
33771e413cf9SAndrew Gallatin 	return err;
33781e413cf9SAndrew Gallatin 
33791e413cf9SAndrew Gallatin }
33801e413cf9SAndrew Gallatin 
33811e413cf9SAndrew Gallatin 
3382053e637fSAndrew Gallatin static void
3383053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3384053e637fSAndrew Gallatin {
3385c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3386053e637fSAndrew Gallatin 
3387053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3388053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3389053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3390053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3391053e637fSAndrew Gallatin 		*nbufs = 1;
3392053e637fSAndrew Gallatin 		return;
3393053e637fSAndrew Gallatin 	}
3394053e637fSAndrew Gallatin 
3395053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3396053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3397053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3398053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3399053e637fSAndrew Gallatin 		*nbufs = 1;
3400053e637fSAndrew Gallatin 		return;
3401053e637fSAndrew Gallatin 	}
3402b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3403053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3404053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3405053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3406053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3407053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3408053e637fSAndrew Gallatin 	if (*nbufs == 3)
3409053e637fSAndrew Gallatin 		*nbufs = 4;
3410b0f7b922SAndrew Gallatin #else
3411b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3412b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3413b0f7b922SAndrew Gallatin 	*nbufs = 1;
3414b0f7b922SAndrew Gallatin #endif
3415053e637fSAndrew Gallatin }
3416053e637fSAndrew Gallatin 
3417b2fc195eSAndrew Gallatin static int
34181e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3419b2fc195eSAndrew Gallatin {
34201e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
34216d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3422b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
3423053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
34241e413cf9SAndrew Gallatin 	int err, i, slice;
3425b2fc195eSAndrew Gallatin 
34261e413cf9SAndrew Gallatin 
34271e413cf9SAndrew Gallatin 	sc = ss->sc;
34281e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
34291e413cf9SAndrew Gallatin 
34301e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_free);
34311e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_active);
3432053e637fSAndrew Gallatin 
3433053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
3434053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
34351e413cf9SAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF,
34361e413cf9SAndrew Gallatin 			       M_NOWAIT | M_ZERO);
3437053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
3438053e637fSAndrew Gallatin 			sc->lro_cnt = i;
3439053e637fSAndrew Gallatin 			break;
3440053e637fSAndrew Gallatin 		}
34411e413cf9SAndrew Gallatin 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
3442053e637fSAndrew Gallatin 	}
34431e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
34441e413cf9SAndrew Gallatin 
34451e413cf9SAndrew Gallatin 	err = 0;
3446c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
34471e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
34481e413cf9SAndrew Gallatin 	if (slice == 0) {
3449c6cb3e3fSAndrew Gallatin #endif
34501e413cf9SAndrew Gallatin 		cmd.data0 = slice;
34511e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
34521e413cf9SAndrew Gallatin 		ss->tx.lanai =
34531e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3454c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3455c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3456c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3457c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3458c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
34591e413cf9SAndrew Gallatin 	}
3460c6cb3e3fSAndrew Gallatin #endif
34611e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34621e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
34631e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
34641e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
34651e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34661e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34671e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
34681e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
34691e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34701e413cf9SAndrew Gallatin 
34711e413cf9SAndrew Gallatin 	if (err != 0) {
34721e413cf9SAndrew Gallatin 		device_printf(sc->dev,
34731e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
34741e413cf9SAndrew Gallatin 		return EIO;
34751e413cf9SAndrew Gallatin 	}
34761e413cf9SAndrew Gallatin 
34771e413cf9SAndrew Gallatin 	/* stock receive rings */
34781e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
34791e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
34801e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
34811e413cf9SAndrew Gallatin 		if (err) {
34821e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
34831e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
34841e413cf9SAndrew Gallatin 			return ENOMEM;
34851e413cf9SAndrew Gallatin 		}
34861e413cf9SAndrew Gallatin 	}
34871e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
34881e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
34891e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
34901e413cf9SAndrew Gallatin 	}
34911e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
34921e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
34934d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
34944d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
34951e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
34961e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
34971e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
34981e413cf9SAndrew Gallatin 		if (err) {
34991e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
35001e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
35011e413cf9SAndrew Gallatin 			return ENOMEM;
35021e413cf9SAndrew Gallatin 		}
35031e413cf9SAndrew Gallatin 	}
35041e413cf9SAndrew Gallatin 	return 0;
35051e413cf9SAndrew Gallatin }
35061e413cf9SAndrew Gallatin 
35071e413cf9SAndrew Gallatin static int
35081e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
35091e413cf9SAndrew Gallatin {
35101e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
35111e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
35121e413cf9SAndrew Gallatin 	bus_addr_t bus;
35131e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3514c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3515b2fc195eSAndrew Gallatin 
35167d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
35177d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
35187d542e2dSAndrew Gallatin 
3519adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3520b2fc195eSAndrew Gallatin 	if (err != 0) {
3521b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3522b2fc195eSAndrew Gallatin 		return EIO;
3523b2fc195eSAndrew Gallatin 	}
3524b2fc195eSAndrew Gallatin 
35251e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
35261e413cf9SAndrew Gallatin 		/* setup the indirection table */
35271e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
35281e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
35291e413cf9SAndrew Gallatin 				    &cmd);
3530b2fc195eSAndrew Gallatin 
35311e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
35321e413cf9SAndrew Gallatin 				     &cmd);
35331e413cf9SAndrew Gallatin 		if (err != 0) {
35341e413cf9SAndrew Gallatin 			device_printf(sc->dev,
35351e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
35361e413cf9SAndrew Gallatin 			return err;
35371e413cf9SAndrew Gallatin 		}
35381e413cf9SAndrew Gallatin 
35391e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
35401e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
35411e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
35421e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
35431e413cf9SAndrew Gallatin 
35441e413cf9SAndrew Gallatin 		cmd.data0 = 1;
35451e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
35461e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
35471e413cf9SAndrew Gallatin 		if (err != 0) {
35481e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
35491e413cf9SAndrew Gallatin 			return err;
35501e413cf9SAndrew Gallatin 		}
35511e413cf9SAndrew Gallatin 	}
35521e413cf9SAndrew Gallatin 
35531e413cf9SAndrew Gallatin 
35541e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
35551e413cf9SAndrew Gallatin 
35561e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3557053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3558053e637fSAndrew Gallatin 			    &cmd);
3559053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3560053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
35611e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3562053e637fSAndrew Gallatin 		device_printf(sc->dev,
3563053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
35641e413cf9SAndrew Gallatin 			      nbufs);
3565053e637fSAndrew Gallatin 		return EIO;
3566053e637fSAndrew Gallatin 	}
3567b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3568b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3569b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3570c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
35715e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3572b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
35735e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3574b2fc195eSAndrew Gallatin 			     &cmd);
3575053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
35765e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
35770fa7f681SAndrew Gallatin 
35780fa7f681SAndrew Gallatin 	if (err != 0) {
35790fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
35800fa7f681SAndrew Gallatin 		goto abort;
35810fa7f681SAndrew Gallatin 	}
35820fa7f681SAndrew Gallatin 
3583b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3584c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3585c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3586c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3587c6cb3e3fSAndrew Gallatin #else
3588c6cb3e3fSAndrew Gallatin 	     slice < 1;
3589c6cb3e3fSAndrew Gallatin #endif
3590c6cb3e3fSAndrew Gallatin 	     slice++) {
3591c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3592c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3593c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3594c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3595c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
35960fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3597c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3598c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3599c6cb3e3fSAndrew Gallatin 	}
36000fa7f681SAndrew Gallatin 
36010fa7f681SAndrew Gallatin 	if (err != 0) {
36021e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
36030fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
36040fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
36050fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
36060fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
36070fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
36080fa7f681SAndrew Gallatin 				    &cmd);
36090fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
36100fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
36110fa7f681SAndrew Gallatin 	} else {
36120fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
36130fa7f681SAndrew Gallatin 	}
3614b2fc195eSAndrew Gallatin 
3615b2fc195eSAndrew Gallatin 	if (err != 0) {
3616b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3617b2fc195eSAndrew Gallatin 		goto abort;
3618b2fc195eSAndrew Gallatin 	}
3619b2fc195eSAndrew Gallatin 
36201e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
36211e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
36221e413cf9SAndrew Gallatin 		if (err != 0) {
36231e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
36241e413cf9SAndrew Gallatin 				      slice);
36251e413cf9SAndrew Gallatin 			goto abort;
36261e413cf9SAndrew Gallatin 		}
36271e413cf9SAndrew Gallatin 	}
36281e413cf9SAndrew Gallatin 
3629b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
36305e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3631b2fc195eSAndrew Gallatin 	if (err) {
3632b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3633b2fc195eSAndrew Gallatin 		goto abort;
3634b2fc195eSAndrew Gallatin 	}
3635c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3636c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3637c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3638c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3639c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3640c6cb3e3fSAndrew Gallatin 	}
3641c6cb3e3fSAndrew Gallatin #endif
3642b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3643b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3644b2fc195eSAndrew Gallatin 
3645b2fc195eSAndrew Gallatin 	return 0;
3646b2fc195eSAndrew Gallatin 
3647b2fc195eSAndrew Gallatin 
3648b2fc195eSAndrew Gallatin abort:
36496d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3650a98d6cd7SAndrew Gallatin 
3651b2fc195eSAndrew Gallatin 	return err;
3652b2fc195eSAndrew Gallatin }
3653b2fc195eSAndrew Gallatin 
3654b2fc195eSAndrew Gallatin static int
3655a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3656b2fc195eSAndrew Gallatin {
36576d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3658b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3659c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3660c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3661c6cb3e3fSAndrew Gallatin 	int slice;
3662c6cb3e3fSAndrew Gallatin #endif
3663b2fc195eSAndrew Gallatin 
3664c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3665c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3666c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3667c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3668c6cb3e3fSAndrew Gallatin 	}
3669c6cb3e3fSAndrew Gallatin #endif
3670b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3671a393336bSAndrew Gallatin 	if (!down) {
3672b2fc195eSAndrew Gallatin 		old_down_cnt = sc->down_cnt;
367373c7c83fSAndrew Gallatin 		wmb();
36745e7d8541SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3675b2fc195eSAndrew Gallatin 		if (err) {
3676a393336bSAndrew Gallatin 			device_printf(sc->dev,
3677a393336bSAndrew Gallatin 				      "Couldn't bring down link\n");
3678b2fc195eSAndrew Gallatin 		}
3679b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3680b2fc195eSAndrew Gallatin 			/* wait for down irq */
3681dce01b9bSAndrew Gallatin 			DELAY(10 * sc->intr_coal_delay);
3682b2fc195eSAndrew Gallatin 		}
368373c7c83fSAndrew Gallatin 		wmb();
3684b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3685b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "never got down irq\n");
3686b2fc195eSAndrew Gallatin 		}
3687a393336bSAndrew Gallatin 	}
36886d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3689a98d6cd7SAndrew Gallatin 
3690b2fc195eSAndrew Gallatin 	return 0;
3691b2fc195eSAndrew Gallatin }
3692b2fc195eSAndrew Gallatin 
3693dce01b9bSAndrew Gallatin static void
3694dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3695dce01b9bSAndrew Gallatin {
3696dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3697dce01b9bSAndrew Gallatin 	int reg;
3698dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
3699dce01b9bSAndrew Gallatin 
3700dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
3701dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
3702dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3703dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3704dce01b9bSAndrew Gallatin 
370583d54b59SAndrew Gallatin 		if (sc->pectl == 0) {
3706dce01b9bSAndrew Gallatin 			pectl = pci_read_config(dev, reg + 0x8, 2);
3707dce01b9bSAndrew Gallatin 			pectl = (pectl & ~0x7000) | (5 << 12);
3708dce01b9bSAndrew Gallatin 			pci_write_config(dev, reg + 0x8, pectl, 2);
370983d54b59SAndrew Gallatin 			sc->pectl = pectl;
371083d54b59SAndrew Gallatin 		} else {
371183d54b59SAndrew Gallatin 			/* restore saved pectl after watchdog reset */
371283d54b59SAndrew Gallatin 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
371383d54b59SAndrew Gallatin 		}
3714dce01b9bSAndrew Gallatin 	}
3715dce01b9bSAndrew Gallatin 
3716dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3717dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3718dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
3719dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
3720dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
3721dce01b9bSAndrew Gallatin }
3722dce01b9bSAndrew Gallatin 
3723dce01b9bSAndrew Gallatin static uint32_t
3724dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3725dce01b9bSAndrew Gallatin {
3726dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3727dce01b9bSAndrew Gallatin 	uint32_t vs;
3728dce01b9bSAndrew Gallatin 
3729dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
3730dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
3731dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3732dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3733dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3734dce01b9bSAndrew Gallatin 	}
3735dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3736dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3737dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3738dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3739dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3740dce01b9bSAndrew Gallatin }
3741dce01b9bSAndrew Gallatin 
374272c042dfSAndrew Gallatin static void
374372c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3744dce01b9bSAndrew Gallatin {
3745e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3746a393336bSAndrew Gallatin 	struct mxge_slice_state *ss;
3747a393336bSAndrew Gallatin 	int err, running, s, num_tx_slices = 1;
3748dce01b9bSAndrew Gallatin 	uint32_t reboot;
3749dce01b9bSAndrew Gallatin 	uint16_t cmd;
3750dce01b9bSAndrew Gallatin 
3751dce01b9bSAndrew Gallatin 	err = ENXIO;
3752dce01b9bSAndrew Gallatin 
3753dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3754dce01b9bSAndrew Gallatin 
3755dce01b9bSAndrew Gallatin 	/*
3756dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3757dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3758dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3759dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3760dce01b9bSAndrew Gallatin 	 * again
3761dce01b9bSAndrew Gallatin 	 */
3762dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3763dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3764dce01b9bSAndrew Gallatin 		/*
3765dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3766dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3767dce01b9bSAndrew Gallatin 		 * back, then give up
3768dce01b9bSAndrew Gallatin 		 */
3769dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3770dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3771dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3772dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3773dce01b9bSAndrew Gallatin 		}
3774dce01b9bSAndrew Gallatin 	}
3775dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3776dce01b9bSAndrew Gallatin 		/* print the reboot status */
3777dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3778dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3779dce01b9bSAndrew Gallatin 			      reboot);
3780a393336bSAndrew Gallatin 		running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
3781a393336bSAndrew Gallatin 		if (running) {
3782a393336bSAndrew Gallatin 
3783a393336bSAndrew Gallatin 			/*
3784a393336bSAndrew Gallatin 			 * quiesce NIC so that TX routines will not try to
3785a393336bSAndrew Gallatin 			 * xmit after restoration of BAR
3786a393336bSAndrew Gallatin 			 */
3787a393336bSAndrew Gallatin 
3788a393336bSAndrew Gallatin 			/* Mark the link as down */
3789a393336bSAndrew Gallatin 			if (sc->link_state) {
3790a393336bSAndrew Gallatin 				sc->link_state = 0;
3791a393336bSAndrew Gallatin 				if_link_state_change(sc->ifp,
3792a393336bSAndrew Gallatin 						     LINK_STATE_DOWN);
3793a393336bSAndrew Gallatin 			}
3794a393336bSAndrew Gallatin #ifdef IFNET_BUF_RING
3795a393336bSAndrew Gallatin 			num_tx_slices = sc->num_slices;
3796a393336bSAndrew Gallatin #endif
3797a393336bSAndrew Gallatin 			/* grab all TX locks to ensure no tx  */
3798a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3799a393336bSAndrew Gallatin 				ss = &sc->ss[s];
3800a393336bSAndrew Gallatin 				mtx_lock(&ss->tx.mtx);
3801a393336bSAndrew Gallatin 			}
3802a393336bSAndrew Gallatin 			mxge_close(sc, 1);
3803a393336bSAndrew Gallatin 		}
3804dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3805e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3806e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3807dce01b9bSAndrew Gallatin 
3808dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3809dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
381010882804SAndrew Gallatin 
3811a393336bSAndrew Gallatin 		/* reload f/w */
3812a393336bSAndrew Gallatin 		err = mxge_load_firmware(sc, 0);
3813a393336bSAndrew Gallatin 		if (err) {
3814a393336bSAndrew Gallatin 			device_printf(sc->dev,
3815a393336bSAndrew Gallatin 				      "Unable to re-load f/w\n");
381610882804SAndrew Gallatin 		}
3817a393336bSAndrew Gallatin 		if (running) {
3818a393336bSAndrew Gallatin 			if (!err)
3819a393336bSAndrew Gallatin 				err = mxge_open(sc);
3820a393336bSAndrew Gallatin 			/* release all TX locks */
3821a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3822a393336bSAndrew Gallatin 				ss = &sc->ss[s];
382383d54b59SAndrew Gallatin #ifdef IFNET_BUF_RING
382483d54b59SAndrew Gallatin 				mxge_start_locked(ss);
382583d54b59SAndrew Gallatin #endif
3826a393336bSAndrew Gallatin 				mtx_unlock(&ss->tx.mtx);
3827a393336bSAndrew Gallatin 			}
3828a393336bSAndrew Gallatin 		}
3829a393336bSAndrew Gallatin 		sc->watchdog_resets++;
3830dce01b9bSAndrew Gallatin 	} else {
3831c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
383272c042dfSAndrew Gallatin 			      "NIC did not reboot, not resetting\n");
383372c042dfSAndrew Gallatin 		err = 0;
383472c042dfSAndrew Gallatin 	}
383572c042dfSAndrew Gallatin 	if (err) {
383672c042dfSAndrew Gallatin 		device_printf(sc->dev, "watchdog reset failed\n");
383772c042dfSAndrew Gallatin 	} else {
38386b484a49SAndrew Gallatin 		if (sc->dying == 2)
38396b484a49SAndrew Gallatin 			sc->dying = 0;
38406b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
384172c042dfSAndrew Gallatin 	}
384272c042dfSAndrew Gallatin }
384372c042dfSAndrew Gallatin 
384472c042dfSAndrew Gallatin static void
384572c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
384672c042dfSAndrew Gallatin {
384772c042dfSAndrew Gallatin 	mxge_softc_t *sc = arg;
384872c042dfSAndrew Gallatin 
384972c042dfSAndrew Gallatin 
385072c042dfSAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
385172c042dfSAndrew Gallatin 	mxge_watchdog_reset(sc);
385272c042dfSAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
385372c042dfSAndrew Gallatin }
385472c042dfSAndrew Gallatin 
385572c042dfSAndrew Gallatin static void
385672c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
385772c042dfSAndrew Gallatin {
385872c042dfSAndrew Gallatin 	tx = &sc->ss[slice].tx;
385972c042dfSAndrew Gallatin 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3860c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev,
3861c6cb3e3fSAndrew Gallatin 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3862c6cb3e3fSAndrew Gallatin 		      tx->req, tx->done, tx->queue_active);
3863c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3864c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3865dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3866c6cb3e3fSAndrew Gallatin 		      tx->pkt_done,
38671e413cf9SAndrew Gallatin 		      be32toh(sc->ss->fw_stats->send_done_count));
3868dce01b9bSAndrew Gallatin }
3869dce01b9bSAndrew Gallatin 
3870e749ef6bSAndrew Gallatin static int
3871dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3872dce01b9bSAndrew Gallatin {
3873c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
38741e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3875c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3876dce01b9bSAndrew Gallatin 
3877dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3878dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3879c6cb3e3fSAndrew Gallatin 	for (i = 0;
3880c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3881c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
3882c6cb3e3fSAndrew Gallatin #else
3883c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
3884c6cb3e3fSAndrew Gallatin #endif
3885c6cb3e3fSAndrew Gallatin 	     i++) {
3886c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3887dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3888dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3889c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3890c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
389172c042dfSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause) {
389272c042dfSAndrew Gallatin 				mxge_warn_stuck(sc, tx, i);
389372c042dfSAndrew Gallatin 				taskqueue_enqueue(sc->tq, &sc->watchdog_task);
389472c042dfSAndrew Gallatin 				return (ENXIO);
389572c042dfSAndrew Gallatin 			}
3896c587e59fSAndrew Gallatin 			else
3897c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3898c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3899c587e59fSAndrew Gallatin 		}
3900dce01b9bSAndrew Gallatin 
3901dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3902dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3903c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3904c6cb3e3fSAndrew Gallatin 	}
3905c587e59fSAndrew Gallatin 
3906c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3907c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3908e749ef6bSAndrew Gallatin 	return (err);
3909dce01b9bSAndrew Gallatin }
3910dce01b9bSAndrew Gallatin 
39116b484a49SAndrew Gallatin static u_long
39121e413cf9SAndrew Gallatin mxge_update_stats(mxge_softc_t *sc)
39131e413cf9SAndrew Gallatin {
39141e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
39156b484a49SAndrew Gallatin 	u_long pkts = 0;
39161e413cf9SAndrew Gallatin 	u_long ipackets = 0;
3917c6cb3e3fSAndrew Gallatin 	u_long opackets = 0;
391871032832SAndrew Gallatin #ifdef IFNET_BUF_RING
391971032832SAndrew Gallatin 	u_long obytes = 0;
392071032832SAndrew Gallatin 	u_long omcasts = 0;
392171032832SAndrew Gallatin 	u_long odrops = 0;
392271032832SAndrew Gallatin #endif
3923c6cb3e3fSAndrew Gallatin 	u_long oerrors = 0;
39241e413cf9SAndrew Gallatin 	int slice;
39251e413cf9SAndrew Gallatin 
39261e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
39271e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
39281e413cf9SAndrew Gallatin 		ipackets += ss->ipackets;
3929c6cb3e3fSAndrew Gallatin 		opackets += ss->opackets;
393071032832SAndrew Gallatin #ifdef IFNET_BUF_RING
393171032832SAndrew Gallatin 		obytes += ss->obytes;
393271032832SAndrew Gallatin 		omcasts += ss->omcasts;
393371032832SAndrew Gallatin 		odrops += ss->tx.br->br_drops;
393471032832SAndrew Gallatin #endif
3935c6cb3e3fSAndrew Gallatin 		oerrors += ss->oerrors;
39361e413cf9SAndrew Gallatin 	}
39376b484a49SAndrew Gallatin 	pkts = (ipackets - sc->ifp->if_ipackets);
39386b484a49SAndrew Gallatin 	pkts += (opackets - sc->ifp->if_opackets);
39391e413cf9SAndrew Gallatin 	sc->ifp->if_ipackets = ipackets;
3940c6cb3e3fSAndrew Gallatin 	sc->ifp->if_opackets = opackets;
394171032832SAndrew Gallatin #ifdef IFNET_BUF_RING
394271032832SAndrew Gallatin 	sc->ifp->if_obytes = obytes;
394371032832SAndrew Gallatin 	sc->ifp->if_omcasts = omcasts;
394471032832SAndrew Gallatin 	sc->ifp->if_snd.ifq_drops = odrops;
394571032832SAndrew Gallatin #endif
3946c6cb3e3fSAndrew Gallatin 	sc->ifp->if_oerrors = oerrors;
39476b484a49SAndrew Gallatin 	return pkts;
39481e413cf9SAndrew Gallatin }
3949c6cb3e3fSAndrew Gallatin 
39501e413cf9SAndrew Gallatin static void
3951dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3952dce01b9bSAndrew Gallatin {
3953dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
39546b484a49SAndrew Gallatin 	u_long pkts = 0;
3955e749ef6bSAndrew Gallatin 	int err = 0;
39566b484a49SAndrew Gallatin 	int running, ticks;
39576b484a49SAndrew Gallatin 	uint16_t cmd;
3958dce01b9bSAndrew Gallatin 
39596b484a49SAndrew Gallatin 	ticks = mxge_ticks;
39606b484a49SAndrew Gallatin 	running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
39616b484a49SAndrew Gallatin 	if (running) {
39621e413cf9SAndrew Gallatin 		/* aggregate stats from different slices */
39636b484a49SAndrew Gallatin 		pkts = mxge_update_stats(sc);
39641e413cf9SAndrew Gallatin 		if (!sc->watchdog_countdown) {
3965e749ef6bSAndrew Gallatin 			err = mxge_watchdog(sc);
39661e413cf9SAndrew Gallatin 			sc->watchdog_countdown = 4;
39671e413cf9SAndrew Gallatin 		}
39681e413cf9SAndrew Gallatin 		sc->watchdog_countdown--;
39696b484a49SAndrew Gallatin 	}
39706b484a49SAndrew Gallatin 	if (pkts == 0) {
39716b484a49SAndrew Gallatin 		/* ensure NIC did not suffer h/w fault while idle */
39726b484a49SAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
39736b484a49SAndrew Gallatin 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
39746b484a49SAndrew Gallatin 			sc->dying = 2;
39756b484a49SAndrew Gallatin 			taskqueue_enqueue(sc->tq, &sc->watchdog_task);
39766b484a49SAndrew Gallatin 			err = ENXIO;
39776b484a49SAndrew Gallatin 		}
39786b484a49SAndrew Gallatin 		/* look less often if NIC is idle */
39796b484a49SAndrew Gallatin 		ticks *= 4;
39806b484a49SAndrew Gallatin 	}
39816b484a49SAndrew Gallatin 
3982e749ef6bSAndrew Gallatin 	if (err == 0)
39836b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
3984e749ef6bSAndrew Gallatin 
3985dce01b9bSAndrew Gallatin }
3986b2fc195eSAndrew Gallatin 
3987b2fc195eSAndrew Gallatin static int
39886d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
3989b2fc195eSAndrew Gallatin {
3990b2fc195eSAndrew Gallatin 	return EINVAL;
3991b2fc195eSAndrew Gallatin }
3992b2fc195eSAndrew Gallatin 
3993b2fc195eSAndrew Gallatin static int
39946d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3995b2fc195eSAndrew Gallatin {
3996b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
3997b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3998b2fc195eSAndrew Gallatin 	int err = 0;
3999b2fc195eSAndrew Gallatin 
4000b2fc195eSAndrew Gallatin 
4001c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
4002053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
4003b2fc195eSAndrew Gallatin 		return EINVAL;
4004a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4005b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
4006b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
4007b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4008a393336bSAndrew Gallatin 		mxge_close(sc, 0);
40096d87a65dSAndrew Gallatin 		err = mxge_open(sc);
4010b2fc195eSAndrew Gallatin 		if (err != 0) {
4011b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
4012a393336bSAndrew Gallatin 			mxge_close(sc, 0);
40136d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
4014b2fc195eSAndrew Gallatin 		}
4015b2fc195eSAndrew Gallatin 	}
4016a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4017b2fc195eSAndrew Gallatin 	return err;
4018b2fc195eSAndrew Gallatin }
4019b2fc195eSAndrew Gallatin 
4020b2fc195eSAndrew Gallatin static void
40216d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
4022b2fc195eSAndrew Gallatin {
40236d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4024b2fc195eSAndrew Gallatin 
4025b2fc195eSAndrew Gallatin 
4026b2fc195eSAndrew Gallatin 	if (sc == NULL)
4027b2fc195eSAndrew Gallatin 		return;
4028b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
4029c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
4030b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
4031c587e59fSAndrew Gallatin 	ifmr->ifm_active |= sc->link_state ? IFM_FDX : 0;
4032b2fc195eSAndrew Gallatin }
4033b2fc195eSAndrew Gallatin 
4034b2fc195eSAndrew Gallatin static int
40356d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
4036b2fc195eSAndrew Gallatin {
40376d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4038b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
4039b2fc195eSAndrew Gallatin 	int err, mask;
4040b2fc195eSAndrew Gallatin 
4041b2fc195eSAndrew Gallatin 	err = 0;
4042b2fc195eSAndrew Gallatin 	switch (command) {
4043b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
4044b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
4045b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
4046b2fc195eSAndrew Gallatin 		break;
4047b2fc195eSAndrew Gallatin 
4048b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
40496d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
4050b2fc195eSAndrew Gallatin 		break;
4051b2fc195eSAndrew Gallatin 
4052b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
4053a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
40548c5d766cSAndrew Gallatin 		if (sc->dying) {
40558c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
40568c5d766cSAndrew Gallatin 			return EINVAL;
40578c5d766cSAndrew Gallatin 		}
4058b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
4059dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
40606d87a65dSAndrew Gallatin 				err = mxge_open(sc);
4061dce01b9bSAndrew Gallatin 			} else {
40620fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
40630fa7f681SAndrew Gallatin 				   flag chages */
40640fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
40650fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
40660fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
40670fa7f681SAndrew Gallatin 			}
4068b2fc195eSAndrew Gallatin 		} else {
4069dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4070a393336bSAndrew Gallatin 				mxge_close(sc, 0);
4071dce01b9bSAndrew Gallatin 			}
4072b2fc195eSAndrew Gallatin 		}
4073a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4074b2fc195eSAndrew Gallatin 		break;
4075b2fc195eSAndrew Gallatin 
4076b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
4077b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
4078a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
40790fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
4080a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4081b2fc195eSAndrew Gallatin 		break;
4082b2fc195eSAndrew Gallatin 
4083b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
4084a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4085b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
4086b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
4087b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
4088aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
4089aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
4090aed8e389SAndrew Gallatin 						      | CSUM_TSO);
4091b2fc195eSAndrew Gallatin 			} else {
4092b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
4093b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
4094b2fc195eSAndrew Gallatin 			}
4095b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
4096b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
4097b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
40985e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
4099b2fc195eSAndrew Gallatin 			} else {
4100b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
41015e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
4102b2fc195eSAndrew Gallatin 			}
4103b2fc195eSAndrew Gallatin 		}
4104aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
4105aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
4106aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
4107aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
4108aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
4109aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
4110aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
4111aed8e389SAndrew Gallatin 			} else {
4112aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4113aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4114aed8e389SAndrew Gallatin 				err = EINVAL;
4115aed8e389SAndrew Gallatin 			}
4116aed8e389SAndrew Gallatin 		}
4117f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
4118f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
4119f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
4120f04b33f8SAndrew Gallatin 			else
4121f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
4122f04b33f8SAndrew Gallatin 		}
4123c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
4124c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
41250dce6781SAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTSO)
41260dce6781SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
41270dce6781SAndrew Gallatin 
41280dce6781SAndrew Gallatin 		if (!(ifp->if_capabilities & IFCAP_VLAN_HWTSO) ||
41290dce6781SAndrew Gallatin 		    !(ifp->if_capenable & IFCAP_VLAN_HWTAGGING))
41300dce6781SAndrew Gallatin 			ifp->if_capenable &= ~IFCAP_VLAN_HWTSO;
41310dce6781SAndrew Gallatin 
4132a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4133c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4134c792928fSAndrew Gallatin 
4135b2fc195eSAndrew Gallatin 		break;
4136b2fc195eSAndrew Gallatin 
4137b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4138b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4139b2fc195eSAndrew Gallatin 				    &sc->media, command);
4140b2fc195eSAndrew Gallatin                 break;
4141b2fc195eSAndrew Gallatin 
4142b2fc195eSAndrew Gallatin 	default:
4143b2fc195eSAndrew Gallatin 		err = ENOTTY;
4144b2fc195eSAndrew Gallatin         }
4145b2fc195eSAndrew Gallatin 	return err;
4146b2fc195eSAndrew Gallatin }
4147b2fc195eSAndrew Gallatin 
4148b2fc195eSAndrew Gallatin static void
41496d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4150b2fc195eSAndrew Gallatin {
4151b2fc195eSAndrew Gallatin 
41521e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
41536d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
41546d87a65dSAndrew Gallatin 			  &mxge_flow_control);
41556d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
41566d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
41576d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
41586d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4159d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4160d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
41615e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
41625e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
41635e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
41645e7d8541SAndrew Gallatin 			  &mxge_verbose);
4165dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
4166053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
41671e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
41681e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
416994c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4170f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
417165c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4172f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
4173f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
4174b2fc195eSAndrew Gallatin 
41755e7d8541SAndrew Gallatin 	if (bootverbose)
41765e7d8541SAndrew Gallatin 		mxge_verbose = 1;
41776d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
41786d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4179dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
41801e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
41816d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
41821e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4183bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
41845769c5efSAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4185b2fc195eSAndrew Gallatin 	}
4186f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4187f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4188f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
418965c69066SAndrew Gallatin 
419065c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
419165c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
419265c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
419365c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
419465c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
41951e413cf9SAndrew Gallatin }
41961e413cf9SAndrew Gallatin 
41971e413cf9SAndrew Gallatin 
41981e413cf9SAndrew Gallatin static void
41991e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
42001e413cf9SAndrew Gallatin {
42011e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
42021e413cf9SAndrew Gallatin 	int i;
42031e413cf9SAndrew Gallatin 
42041e413cf9SAndrew Gallatin 
42051e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
42061e413cf9SAndrew Gallatin 		return;
42071e413cf9SAndrew Gallatin 
42081e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42091e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
42101e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
42111e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
42121e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4213c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4214c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4215c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4216c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4217c6cb3e3fSAndrew Gallatin 			}
4218c6cb3e3fSAndrew Gallatin #endif
42191e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
42201e413cf9SAndrew Gallatin 		}
42211e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
42221e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
42231e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
42241e413cf9SAndrew Gallatin 		}
42251e413cf9SAndrew Gallatin 	}
42261e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
42271e413cf9SAndrew Gallatin 	sc->ss = NULL;
42281e413cf9SAndrew Gallatin }
42291e413cf9SAndrew Gallatin 
42301e413cf9SAndrew Gallatin static int
42311e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
42321e413cf9SAndrew Gallatin {
42331e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
42341e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
42351e413cf9SAndrew Gallatin 	size_t bytes;
42361e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
42371e413cf9SAndrew Gallatin 
42381e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
42391e413cf9SAndrew Gallatin 	if (err != 0) {
42401e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
42411e413cf9SAndrew Gallatin 		return err;
42421e413cf9SAndrew Gallatin 	}
42431e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
42441e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
42451e413cf9SAndrew Gallatin 
42461e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
42471e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
42481e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
42491e413cf9SAndrew Gallatin 		return (ENOMEM);
42501e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42511e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
42521e413cf9SAndrew Gallatin 
42531e413cf9SAndrew Gallatin 		ss->sc = sc;
42541e413cf9SAndrew Gallatin 
42551e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
42561e413cf9SAndrew Gallatin 
42571e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
42581e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
42591e413cf9SAndrew Gallatin 		if (err != 0)
42601e413cf9SAndrew Gallatin 			goto abort;
42611e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
42621e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
42631e413cf9SAndrew Gallatin 
42641e413cf9SAndrew Gallatin 		/*
42651e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
42661e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
42671e413cf9SAndrew Gallatin 		 * slice for now
42681e413cf9SAndrew Gallatin 		 */
4269c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
42701e413cf9SAndrew Gallatin 		if (i > 0)
42711e413cf9SAndrew Gallatin 			continue;
4272c6cb3e3fSAndrew Gallatin #endif
42731e413cf9SAndrew Gallatin 
42741e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
42751e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
42761e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
42771e413cf9SAndrew Gallatin 		if (err != 0)
42781e413cf9SAndrew Gallatin 			goto abort;
42791e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
42801e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
42811e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
42821e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4283c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4284c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4285c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4286c6cb3e3fSAndrew Gallatin #endif
42871e413cf9SAndrew Gallatin 	}
42881e413cf9SAndrew Gallatin 
42891e413cf9SAndrew Gallatin 	return (0);
42901e413cf9SAndrew Gallatin 
42911e413cf9SAndrew Gallatin abort:
42921e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
42931e413cf9SAndrew Gallatin 	return (ENOMEM);
42941e413cf9SAndrew Gallatin }
42951e413cf9SAndrew Gallatin 
42961e413cf9SAndrew Gallatin static void
42971e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
42981e413cf9SAndrew Gallatin {
42991e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
43001e413cf9SAndrew Gallatin 	char *old_fw;
43011e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
43021e413cf9SAndrew Gallatin 
43031e413cf9SAndrew Gallatin 	sc->num_slices = 1;
43041e413cf9SAndrew Gallatin 	/*
43051e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
43061e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
43071e413cf9SAndrew Gallatin 	 */
43081e413cf9SAndrew Gallatin 
43091e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
43101e413cf9SAndrew Gallatin 		return;
43111e413cf9SAndrew Gallatin 
43121e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
43131e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
43141e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
43151e413cf9SAndrew Gallatin 		return;
43161e413cf9SAndrew Gallatin 
43171e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
43181e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
43191e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
43201e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
43211e413cf9SAndrew Gallatin 	else
43221e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
43231e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
43241e413cf9SAndrew Gallatin 	if (status != 0) {
43251e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
43261e413cf9SAndrew Gallatin 		return;
43271e413cf9SAndrew Gallatin 	}
43281e413cf9SAndrew Gallatin 
43291e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
43301e413cf9SAndrew Gallatin 	   is alive */
43311e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
43321e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
43331e413cf9SAndrew Gallatin 	if (status != 0) {
43341e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
43351e413cf9SAndrew Gallatin 		goto abort_with_fw;
43361e413cf9SAndrew Gallatin 	}
43371e413cf9SAndrew Gallatin 
43381e413cf9SAndrew Gallatin 	/* get rx ring size */
43391e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
43401e413cf9SAndrew Gallatin 	if (status != 0) {
43411e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
43421e413cf9SAndrew Gallatin 		goto abort_with_fw;
43431e413cf9SAndrew Gallatin 	}
43441e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
43451e413cf9SAndrew Gallatin 
43461e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
43471e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
43481e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
43491e413cf9SAndrew Gallatin 	if (status != 0) {
43501e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
43511e413cf9SAndrew Gallatin 		goto abort_with_fw;
43521e413cf9SAndrew Gallatin 	}
43531e413cf9SAndrew Gallatin 
43541e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
43551e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
43561e413cf9SAndrew Gallatin 	if (status != 0) {
43571e413cf9SAndrew Gallatin 		device_printf(sc->dev,
43581e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
43591e413cf9SAndrew Gallatin 		goto abort_with_fw;
43601e413cf9SAndrew Gallatin 	}
43611e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
43621e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
43631e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
43641e413cf9SAndrew Gallatin 
43651e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
43661e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
43671e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
43681e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
43691e413cf9SAndrew Gallatin 	} else {
43701e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
43711e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
43721e413cf9SAndrew Gallatin 	}
43731e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
43741e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
43751e413cf9SAndrew Gallatin 		sc->num_slices--;
43761e413cf9SAndrew Gallatin 
43771e413cf9SAndrew Gallatin 	if (mxge_verbose)
43781e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
43791e413cf9SAndrew Gallatin 			      sc->num_slices);
43801e413cf9SAndrew Gallatin 
43811e413cf9SAndrew Gallatin 	return;
43821e413cf9SAndrew Gallatin 
43831e413cf9SAndrew Gallatin abort_with_fw:
43841e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
43851e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
43861e413cf9SAndrew Gallatin }
43871e413cf9SAndrew Gallatin 
43881e413cf9SAndrew Gallatin static int
43891e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
43901e413cf9SAndrew Gallatin {
43911e413cf9SAndrew Gallatin 	size_t bytes;
43921e413cf9SAndrew Gallatin 	int count, err, i, rid;
43931e413cf9SAndrew Gallatin 
43941e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
43951e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
43961e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
43971e413cf9SAndrew Gallatin 
43981e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
43991e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
44001e413cf9SAndrew Gallatin 		return ENXIO;
44011e413cf9SAndrew Gallatin 	}
44021e413cf9SAndrew Gallatin 
44031e413cf9SAndrew Gallatin 	count = sc->num_slices;
44041e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
44051e413cf9SAndrew Gallatin 	if (err != 0) {
44061e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
44071e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
44081e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
44091e413cf9SAndrew Gallatin 	}
44101e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
44111e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
44121e413cf9SAndrew Gallatin 			      count, sc->num_slices);
44131e413cf9SAndrew Gallatin 		device_printf(sc->dev,
44141e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
44151e413cf9SAndrew Gallatin 			      count);
44161e413cf9SAndrew Gallatin 		err = ENOSPC;
44171e413cf9SAndrew Gallatin 		goto abort_with_msix;
44181e413cf9SAndrew Gallatin 	}
44191e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
44201e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44211e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
44221e413cf9SAndrew Gallatin 		err = ENOMEM;
44231e413cf9SAndrew Gallatin 		goto abort_with_msix;
44241e413cf9SAndrew Gallatin 	}
44251e413cf9SAndrew Gallatin 
44261e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44271e413cf9SAndrew Gallatin 		rid = i + 1;
44281e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
44291e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
44301e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
44311e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
44321e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
44331e413cf9SAndrew Gallatin 				      " for message %d\n", i);
44341e413cf9SAndrew Gallatin 			err = ENXIO;
44351e413cf9SAndrew Gallatin 			goto abort_with_res;
44361e413cf9SAndrew Gallatin 		}
44371e413cf9SAndrew Gallatin 	}
44381e413cf9SAndrew Gallatin 
44391e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
44401e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44411e413cf9SAndrew Gallatin 
44421e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44431e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
44441e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
444537d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
444637d89b0cSAndrew Gallatin 				     NULL,
444737d89b0cSAndrew Gallatin #endif
444837d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
44491e413cf9SAndrew Gallatin 		if (err != 0) {
44501e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
44511e413cf9SAndrew Gallatin 				      "message %d\n", i);
44521e413cf9SAndrew Gallatin 			goto abort_with_intr;
44531e413cf9SAndrew Gallatin 		}
44541e413cf9SAndrew Gallatin 	}
44551e413cf9SAndrew Gallatin 
44561e413cf9SAndrew Gallatin 	if (mxge_verbose) {
44571e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
44581e413cf9SAndrew Gallatin 			      sc->num_slices);
44591e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
44601e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
44611e413cf9SAndrew Gallatin 		printf("\n");
44621e413cf9SAndrew Gallatin 	}
44631e413cf9SAndrew Gallatin 	return (0);
44641e413cf9SAndrew Gallatin 
44651e413cf9SAndrew Gallatin abort_with_intr:
44661e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44671e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
44681e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
44691e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
44701e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
44711e413cf9SAndrew Gallatin 		}
44721e413cf9SAndrew Gallatin 	}
44731e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
44741e413cf9SAndrew Gallatin 
44751e413cf9SAndrew Gallatin 
44761e413cf9SAndrew Gallatin abort_with_res:
44771e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44781e413cf9SAndrew Gallatin 		rid = i + 1;
44791e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
44801e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
44811e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
44821e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
44831e413cf9SAndrew Gallatin 	}
44841e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
44851e413cf9SAndrew Gallatin 
44861e413cf9SAndrew Gallatin 
44871e413cf9SAndrew Gallatin abort_with_msix:
44881e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
44891e413cf9SAndrew Gallatin 
44901e413cf9SAndrew Gallatin abort_with_msix_table:
44911e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
44921e413cf9SAndrew Gallatin 			     sc->msix_table_res);
44931e413cf9SAndrew Gallatin 
44941e413cf9SAndrew Gallatin 	return err;
44951e413cf9SAndrew Gallatin }
44961e413cf9SAndrew Gallatin 
44971e413cf9SAndrew Gallatin static int
44981e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
44991e413cf9SAndrew Gallatin {
45001e413cf9SAndrew Gallatin 	int count, err, rid;
45011e413cf9SAndrew Gallatin 
45021e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
45031e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
45041e413cf9SAndrew Gallatin 		rid = 1;
45051e413cf9SAndrew Gallatin 	} else {
45061e413cf9SAndrew Gallatin 		rid = 0;
450791ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
45081e413cf9SAndrew Gallatin 	}
45091e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
45101e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
45111e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
45121e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
45131e413cf9SAndrew Gallatin 		return ENXIO;
45141e413cf9SAndrew Gallatin 	}
45151e413cf9SAndrew Gallatin 	if (mxge_verbose)
45161e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
451791ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
45181e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
45191e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
45201e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
452137d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
452237d89b0cSAndrew Gallatin 			     NULL,
452337d89b0cSAndrew Gallatin #endif
452437d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
45251e413cf9SAndrew Gallatin 	if (err != 0) {
45261e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
452791ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
452891ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
45291e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
45301e413cf9SAndrew Gallatin 	}
45311e413cf9SAndrew Gallatin 	return err;
45321e413cf9SAndrew Gallatin }
45331e413cf9SAndrew Gallatin 
45341e413cf9SAndrew Gallatin static void
45351e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
45361e413cf9SAndrew Gallatin {
45371e413cf9SAndrew Gallatin 	int i, rid;
45381e413cf9SAndrew Gallatin 
45391e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45401e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
45411e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
45421e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
45431e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
45441e413cf9SAndrew Gallatin 		}
45451e413cf9SAndrew Gallatin 	}
45461e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
45471e413cf9SAndrew Gallatin 
45481e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45491e413cf9SAndrew Gallatin 		rid = i + 1;
45501e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
45511e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
45521e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
45531e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
45541e413cf9SAndrew Gallatin 	}
45551e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
45561e413cf9SAndrew Gallatin 
45571e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
45581e413cf9SAndrew Gallatin 			     sc->msix_table_res);
45591e413cf9SAndrew Gallatin 
45601e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
45611e413cf9SAndrew Gallatin 	return;
45621e413cf9SAndrew Gallatin }
45631e413cf9SAndrew Gallatin 
45641e413cf9SAndrew Gallatin static void
45651e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
45661e413cf9SAndrew Gallatin {
45671e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
45681e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
456991ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
457091ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
45711e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
45721e413cf9SAndrew Gallatin }
45731e413cf9SAndrew Gallatin 
45741e413cf9SAndrew Gallatin static void
45751e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
45761e413cf9SAndrew Gallatin {
45771e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
45781e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
45791e413cf9SAndrew Gallatin 	else
45801e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
45811e413cf9SAndrew Gallatin }
45821e413cf9SAndrew Gallatin 
45831e413cf9SAndrew Gallatin static int
45841e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
45851e413cf9SAndrew Gallatin {
45861e413cf9SAndrew Gallatin 	int err;
45871e413cf9SAndrew Gallatin 
45881e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
45891e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
45901e413cf9SAndrew Gallatin 	else
45911e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
45921e413cf9SAndrew Gallatin 
45931e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
45941e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
45951e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
45961e413cf9SAndrew Gallatin 	}
45971e413cf9SAndrew Gallatin 	return err;
45981e413cf9SAndrew Gallatin }
45991e413cf9SAndrew Gallatin 
4600b2fc195eSAndrew Gallatin 
4601b2fc195eSAndrew Gallatin static int
46026d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4603b2fc195eSAndrew Gallatin {
46046d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4605b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
46061e413cf9SAndrew Gallatin 	int err, rid;
4607b2fc195eSAndrew Gallatin 
4608b2fc195eSAndrew Gallatin 	sc->dev = dev;
46096d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4610b2fc195eSAndrew Gallatin 
461172c042dfSAndrew Gallatin 	TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
461272c042dfSAndrew Gallatin 	sc->tq = taskqueue_create_fast("mxge_taskq", M_WAITOK,
461372c042dfSAndrew Gallatin 				       taskqueue_thread_enqueue,
461472c042dfSAndrew Gallatin 				       &sc->tq);
461572c042dfSAndrew Gallatin 	if (sc->tq == NULL) {
461672c042dfSAndrew Gallatin 		err = ENOMEM;
461772c042dfSAndrew Gallatin 		goto abort_with_nothing;
461872c042dfSAndrew Gallatin 	}
461972c042dfSAndrew Gallatin 	taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
462072c042dfSAndrew Gallatin 				device_get_nameunit(sc->dev));
462172c042dfSAndrew Gallatin 
4622b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
4623b2fc195eSAndrew Gallatin 				 1,			/* alignment */
46241e413cf9SAndrew Gallatin 				 0,			/* boundary */
4625b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4626b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4627b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4628aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
46295e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
46301e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4631b2fc195eSAndrew Gallatin 				 0,			/* flags */
4632b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4633b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4634b2fc195eSAndrew Gallatin 
4635b2fc195eSAndrew Gallatin 	if (err != 0) {
4636b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4637b2fc195eSAndrew Gallatin 			      err);
463872c042dfSAndrew Gallatin 		goto abort_with_tq;
4639b2fc195eSAndrew Gallatin 	}
4640b2fc195eSAndrew Gallatin 
4641b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4642b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4643b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4644b2fc195eSAndrew Gallatin 		err = ENOSPC;
4645b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4646b2fc195eSAndrew Gallatin 	}
46471e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
46481e413cf9SAndrew Gallatin 
4649a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4650a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4651a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4652a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4653a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4654a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4655b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4656b2fc195eSAndrew Gallatin 
4657dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4658d91b1b49SAndrew Gallatin 
4659dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4660b2fc195eSAndrew Gallatin 
4661b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4662b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4663b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4664b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4665b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4666b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4667b2fc195eSAndrew Gallatin 		err = ENXIO;
4668b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4669b2fc195eSAndrew Gallatin 	}
4670b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4671b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4672b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4673b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4674b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4675b2fc195eSAndrew Gallatin 		err = ENXIO;
4676b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4677b2fc195eSAndrew Gallatin 	}
4678b2fc195eSAndrew Gallatin 
4679b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4680b2fc195eSAndrew Gallatin 	   lanai SRAM */
46816d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4682b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4683b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
46846d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4685b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
46866d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
46876d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4688b2fc195eSAndrew Gallatin 	if (err != 0)
4689b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4690b2fc195eSAndrew Gallatin 
4691b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
46926d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4693b2fc195eSAndrew Gallatin 
4694b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
46956d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
46966d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4697b2fc195eSAndrew Gallatin 	if (err != 0)
4698b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4699b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
47006d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4701b2fc195eSAndrew Gallatin 	if (err != 0)
4702b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4703b2fc195eSAndrew Gallatin 
4704a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4705a98d6cd7SAndrew Gallatin 	if (err != 0)
47061e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4707b2fc195eSAndrew Gallatin 
47088fe615baSAndrew Gallatin 	/* select & load the firmware */
47098fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4710b2fc195eSAndrew Gallatin 	if (err != 0)
47111e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
47125e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
47131e413cf9SAndrew Gallatin 
47141e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
47151e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
47161e413cf9SAndrew Gallatin 	if (err != 0)
47171e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
47181e413cf9SAndrew Gallatin 
4719adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4720b2fc195eSAndrew Gallatin 	if (err != 0)
47211e413cf9SAndrew Gallatin 		goto abort_with_slices;
4722b2fc195eSAndrew Gallatin 
4723a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4724a98d6cd7SAndrew Gallatin 	if (err != 0) {
4725a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
47261e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
4727a98d6cd7SAndrew Gallatin 	}
4728a98d6cd7SAndrew Gallatin 
47291e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4730a98d6cd7SAndrew Gallatin 	if (err != 0) {
47311e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4732a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4733a98d6cd7SAndrew Gallatin 	}
47341e413cf9SAndrew Gallatin 
4735e5062938SAndrew Gallatin 	ifp->if_baudrate = IF_Gbps(10UL);
4736c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
4737eb6219e3SAndrew Gallatin 		IFCAP_VLAN_MTU;
4738eb6219e3SAndrew Gallatin #ifdef INET
4739eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4740eb6219e3SAndrew Gallatin #endif
474137d89b0cSAndrew Gallatin 
474237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
474337d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
47440dce6781SAndrew Gallatin 
47450dce6781SAndrew Gallatin 	/* Only FW 1.4.32 and newer can do TSO over vlans */
47460dce6781SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
47470dce6781SAndrew Gallatin 	    sc->fw_ver_tiny >= 32)
47480dce6781SAndrew Gallatin 		ifp->if_capabilities |= IFCAP_VLAN_HWTSO;
474937d89b0cSAndrew Gallatin #endif
4750c792928fSAndrew Gallatin 
4751053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4752053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4753053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4754053e637fSAndrew Gallatin 	else
4755053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4756adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4757053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4758aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
4759b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4760f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4761f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
47625e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
47636d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
4764b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
4765b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
47666d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
47676d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
4768c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4769c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4770c587e59fSAndrew Gallatin 		     mxge_media_status);
4771c587e59fSAndrew Gallatin 	mxge_set_media(sc, IFM_ETHER | IFM_AUTO);
4772c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
47738c5d766cSAndrew Gallatin 	sc->dying = 0;
4774b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4775f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4776f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4777f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4778b2fc195eSAndrew Gallatin 
47796d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4780c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4781c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4782c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4783c6cb3e3fSAndrew Gallatin #endif
47846b484a49SAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4785b2fc195eSAndrew Gallatin 	return 0;
4786b2fc195eSAndrew Gallatin 
4787a98d6cd7SAndrew Gallatin abort_with_rings:
4788a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
47891e413cf9SAndrew Gallatin abort_with_slices:
47901e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4791a98d6cd7SAndrew Gallatin abort_with_dmabench:
4792a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4793b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
47946d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4795b2fc195eSAndrew Gallatin abort_with_cmd_dma:
47966d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4797b2fc195eSAndrew Gallatin abort_with_mem_res:
4798b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4799b2fc195eSAndrew Gallatin abort_with_lock:
4800b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4801a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4802a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4803b2fc195eSAndrew Gallatin 	if_free(ifp);
4804b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4805b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
480672c042dfSAndrew Gallatin abort_with_tq:
480772c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
480872c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
480972c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
481072c042dfSAndrew Gallatin 		sc->tq = NULL;
481172c042dfSAndrew Gallatin 	}
4812b2fc195eSAndrew Gallatin abort_with_nothing:
4813b2fc195eSAndrew Gallatin 	return err;
4814b2fc195eSAndrew Gallatin }
4815b2fc195eSAndrew Gallatin 
4816b2fc195eSAndrew Gallatin static int
48176d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4818b2fc195eSAndrew Gallatin {
48196d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4820b2fc195eSAndrew Gallatin 
482137d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4822c792928fSAndrew Gallatin 		device_printf(sc->dev,
4823c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4824c792928fSAndrew Gallatin 		return EBUSY;
4825c792928fSAndrew Gallatin 	}
4826a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
48278c5d766cSAndrew Gallatin 	sc->dying = 1;
4828b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
4829a393336bSAndrew Gallatin 		mxge_close(sc, 0);
4830a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4831b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
483272c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
483372c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
483472c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
483572c042dfSAndrew Gallatin 		sc->tq = NULL;
483672c042dfSAndrew Gallatin 	}
4837e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4838dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4839091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
48401e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
48411e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4842a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
48431e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4844a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
48456d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
48466d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4847b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4848b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4849a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4850a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4851b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4852b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4853b2fc195eSAndrew Gallatin 	return 0;
4854b2fc195eSAndrew Gallatin }
4855b2fc195eSAndrew Gallatin 
4856b2fc195eSAndrew Gallatin static int
48576d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4858b2fc195eSAndrew Gallatin {
4859b2fc195eSAndrew Gallatin 	return 0;
4860b2fc195eSAndrew Gallatin }
4861b2fc195eSAndrew Gallatin 
4862b2fc195eSAndrew Gallatin /*
4863b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4864b2fc195eSAndrew Gallatin 
4865b2fc195eSAndrew Gallatin   Local Variables:
4866b2fc195eSAndrew Gallatin   c-file-style:"linux"
4867b2fc195eSAndrew Gallatin   tab-width:8
4868b2fc195eSAndrew Gallatin   End:
4869b2fc195eSAndrew Gallatin */
4870