xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 62ce43ccc8752bba029e80798bc24b9be3d79c6b)
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 	 */
5563b0a4aefSJohn Baldwin 	if (pci_find_cap(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;
886c406ad2eSAndrew Gallatin 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
887c406ad2eSAndrew Gallatin 			err = ENXIO;
888c406ad2eSAndrew Gallatin 			break;
889e0501fd0SAndrew Gallatin 		default:
890b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8916d87a65dSAndrew Gallatin 				      "mxge: command %d "
892b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
893b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
894e0501fd0SAndrew Gallatin 			err = ENXIO;
895e0501fd0SAndrew Gallatin 			break;
896b2fc195eSAndrew Gallatin 		}
897e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
898e0501fd0SAndrew Gallatin 			break;
899b2fc195eSAndrew Gallatin 	}
900e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
9016d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
902b2fc195eSAndrew Gallatin 			      "result = %d\n",
903b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
904e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
905e0501fd0SAndrew Gallatin 	return err;
906b2fc195eSAndrew Gallatin }
907b2fc195eSAndrew Gallatin 
9084da0d523SAndrew Gallatin static int
9094da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
9104da0d523SAndrew Gallatin {
9114da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
9124da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
9134da0d523SAndrew Gallatin 	size_t hdr_offset;
9144da0d523SAndrew Gallatin 	int status;
9154da0d523SAndrew Gallatin 
9164da0d523SAndrew Gallatin 	/* find running firmware header */
9174da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9184da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9194da0d523SAndrew Gallatin 
9204da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9214da0d523SAndrew Gallatin 		device_printf(sc->dev,
9224da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9234da0d523SAndrew Gallatin 			      (int)hdr_offset);
9244da0d523SAndrew Gallatin 		return EIO;
9254da0d523SAndrew Gallatin 	}
9264da0d523SAndrew Gallatin 
9274da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9284da0d523SAndrew Gallatin 	 * validate firmware */
9294da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9304da0d523SAndrew Gallatin 	if (hdr == NULL) {
9314da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9324da0d523SAndrew Gallatin 		return ENOMEM;
9334da0d523SAndrew Gallatin 	}
9344da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9354da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9364da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9374da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9384da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
939b824b7d8SAndrew Gallatin 
940b824b7d8SAndrew Gallatin 	/*
941b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
942b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
943b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
944b824b7d8SAndrew Gallatin 	 */
945b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
946b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
947b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
948b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
949b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
950b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
951b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
952b824b7d8SAndrew Gallatin 	}
953b824b7d8SAndrew Gallatin 
9544da0d523SAndrew Gallatin 	return status;
9554da0d523SAndrew Gallatin }
9564da0d523SAndrew Gallatin 
957b2fc195eSAndrew Gallatin 
958b2fc195eSAndrew Gallatin static int
9591e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
960b2fc195eSAndrew Gallatin {
961b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
962b2fc195eSAndrew Gallatin 	volatile char *submit;
963b2fc195eSAndrew Gallatin 	char buf_bytes[72];
964b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
965b2fc195eSAndrew Gallatin 	int status, i;
966b2fc195eSAndrew Gallatin 
967b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
968b2fc195eSAndrew Gallatin 
969b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9706d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
971b2fc195eSAndrew Gallatin 	if (status) {
9721e413cf9SAndrew Gallatin 		if (!adopt)
9731e413cf9SAndrew Gallatin 			return status;
9744da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9754da0d523SAndrew Gallatin 		   it is new enough */
9764da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9774da0d523SAndrew Gallatin 		if (status) {
9784da0d523SAndrew Gallatin 			device_printf(sc->dev,
9794da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
980b2fc195eSAndrew Gallatin 			return status;
981b2fc195eSAndrew Gallatin 		}
9824da0d523SAndrew Gallatin 		device_printf(sc->dev,
9834da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9841e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9854da0d523SAndrew Gallatin 			device_printf(sc->dev,
9864da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9874da0d523SAndrew Gallatin 				 ".  For optimal\n");
9884da0d523SAndrew Gallatin 			device_printf(sc->dev,
9894da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9904da0d523SAndrew Gallatin 				 "firmware\n");
9914da0d523SAndrew Gallatin 		}
992d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9931e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
994d91b1b49SAndrew Gallatin 		return 0;
9954da0d523SAndrew Gallatin 	}
996b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
997b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
998b2fc195eSAndrew Gallatin 	*confirm = 0;
99973c7c83fSAndrew Gallatin 	wmb();
1000b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
1001b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
1002b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
1003b2fc195eSAndrew Gallatin 	*/
1004b2fc195eSAndrew Gallatin 
10056d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
10066d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
1007b2fc195eSAndrew Gallatin 
1008b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
1009b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
1010b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
1011b2fc195eSAndrew Gallatin 
1012b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
1013b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
1014b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
1015b2fc195eSAndrew Gallatin 	*/
1016b2fc195eSAndrew Gallatin 					/* where the code starts*/
10176d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1018b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1019b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1020b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1021b2fc195eSAndrew Gallatin 
10220fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10236d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
102473c7c83fSAndrew Gallatin 	wmb();
1025b2fc195eSAndrew Gallatin 	DELAY(1000);
102673c7c83fSAndrew Gallatin 	wmb();
1027b2fc195eSAndrew Gallatin 	i = 0;
1028b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1029b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1030b2fc195eSAndrew Gallatin 		i++;
1031b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1032b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1033b2fc195eSAndrew Gallatin 	}
1034b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1035b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1036b2fc195eSAndrew Gallatin 			confirm, *confirm);
1037b2fc195eSAndrew Gallatin 
1038b2fc195eSAndrew Gallatin 		return ENXIO;
1039b2fc195eSAndrew Gallatin 	}
1040b2fc195eSAndrew Gallatin 	return 0;
1041b2fc195eSAndrew Gallatin }
1042b2fc195eSAndrew Gallatin 
1043b2fc195eSAndrew Gallatin static int
10446d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1045b2fc195eSAndrew Gallatin {
10466d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1047b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1048b2fc195eSAndrew Gallatin 	int status;
1049b2fc195eSAndrew Gallatin 
1050b2fc195eSAndrew Gallatin 
1051b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1052b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1053b2fc195eSAndrew Gallatin 
1054b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1055b2fc195eSAndrew Gallatin 
10565e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1057b2fc195eSAndrew Gallatin 	return status;
1058b2fc195eSAndrew Gallatin }
1059b2fc195eSAndrew Gallatin 
1060b2fc195eSAndrew Gallatin static int
10616d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1062b2fc195eSAndrew Gallatin {
10636d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1064b2fc195eSAndrew Gallatin 	int status;
1065b2fc195eSAndrew Gallatin 
1066b2fc195eSAndrew Gallatin 	if (pause)
10675e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1068b2fc195eSAndrew Gallatin 				       &cmd);
1069b2fc195eSAndrew Gallatin 	else
10705e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1071b2fc195eSAndrew Gallatin 				       &cmd);
1072b2fc195eSAndrew Gallatin 
1073b2fc195eSAndrew Gallatin 	if (status) {
1074b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1075b2fc195eSAndrew Gallatin 		return ENXIO;
1076b2fc195eSAndrew Gallatin 	}
1077b2fc195eSAndrew Gallatin 	sc->pause = pause;
1078b2fc195eSAndrew Gallatin 	return 0;
1079b2fc195eSAndrew Gallatin }
1080b2fc195eSAndrew Gallatin 
1081b2fc195eSAndrew Gallatin static void
10826d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1083b2fc195eSAndrew Gallatin {
10846d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1085b2fc195eSAndrew Gallatin 	int status;
1086b2fc195eSAndrew Gallatin 
10871e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10881e413cf9SAndrew Gallatin 		promisc = 1;
10891e413cf9SAndrew Gallatin 
1090b2fc195eSAndrew Gallatin 	if (promisc)
10915e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1092b2fc195eSAndrew Gallatin 				       &cmd);
1093b2fc195eSAndrew Gallatin 	else
10945e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1095b2fc195eSAndrew Gallatin 				       &cmd);
1096b2fc195eSAndrew Gallatin 
1097b2fc195eSAndrew Gallatin 	if (status) {
1098b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1099b2fc195eSAndrew Gallatin 	}
1100b2fc195eSAndrew Gallatin }
1101b2fc195eSAndrew Gallatin 
11020fa7f681SAndrew Gallatin static void
11030fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
11040fa7f681SAndrew Gallatin {
11050fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
11060fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
11070fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
11080fa7f681SAndrew Gallatin 	int err;
11090fa7f681SAndrew Gallatin 
11100fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11110fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11120fa7f681SAndrew Gallatin 		return;
11130fa7f681SAndrew Gallatin 
11140fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11150fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11160fa7f681SAndrew Gallatin 	if (err != 0) {
11170fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11180fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11190fa7f681SAndrew Gallatin 		return;
11200fa7f681SAndrew Gallatin 	}
11210fa7f681SAndrew Gallatin 
1122b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1123b824b7d8SAndrew Gallatin 		return;
11240fa7f681SAndrew Gallatin 
11250fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11260fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11270fa7f681SAndrew Gallatin 		return;
11280fa7f681SAndrew Gallatin 
11290fa7f681SAndrew Gallatin 	/* Flush all the filters */
11300fa7f681SAndrew Gallatin 
11310fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11320fa7f681SAndrew Gallatin 	if (err != 0) {
11330fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11340fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11350fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11360fa7f681SAndrew Gallatin 		return;
11370fa7f681SAndrew Gallatin 	}
11380fa7f681SAndrew Gallatin 
11390fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11400fa7f681SAndrew Gallatin 
1141eb956cd0SRobert Watson 	if_maddr_rlock(ifp);
11420fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11430fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11440fa7f681SAndrew Gallatin 			continue;
11450fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11460fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11470fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11480fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11490fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11500fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11510fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11520fa7f681SAndrew Gallatin 		if (err != 0) {
11530fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11540fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11550fa7f681SAndrew Gallatin 			       "%d\t", err);
11560fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
1157eb956cd0SRobert Watson 			if_maddr_runlock(ifp);
11580fa7f681SAndrew Gallatin 			return;
11590fa7f681SAndrew Gallatin 		}
11600fa7f681SAndrew Gallatin 	}
1161eb956cd0SRobert Watson 	if_maddr_runlock(ifp);
11620fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11630fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11640fa7f681SAndrew Gallatin 	if (err != 0) {
11650fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11660fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11670fa7f681SAndrew Gallatin 	}
11680fa7f681SAndrew Gallatin }
11690fa7f681SAndrew Gallatin 
1170b2fc195eSAndrew Gallatin static int
1171053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1172053e637fSAndrew Gallatin {
1173053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1174053e637fSAndrew Gallatin 	int status;
1175053e637fSAndrew Gallatin 
1176c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1177c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1178053e637fSAndrew Gallatin 
1179053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1180053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1181053e637fSAndrew Gallatin 	cmd.data0 = 0;
1182053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1183053e637fSAndrew Gallatin 			       &cmd);
1184053e637fSAndrew Gallatin 	if (status == 0)
1185c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1186053e637fSAndrew Gallatin 
1187053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1188053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1189053e637fSAndrew Gallatin }
1190053e637fSAndrew Gallatin 
1191053e637fSAndrew Gallatin static int
1192adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1193b2fc195eSAndrew Gallatin {
11941e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11951e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11961e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11976d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11981e413cf9SAndrew Gallatin 	int slice, status;
1199b2fc195eSAndrew Gallatin 
1200b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1201b2fc195eSAndrew Gallatin 	   is alive */
1202b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
12035e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1204b2fc195eSAndrew Gallatin 	if (status != 0) {
1205b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1206b2fc195eSAndrew Gallatin 		return ENXIO;
1207b2fc195eSAndrew Gallatin 	}
1208b2fc195eSAndrew Gallatin 
1209091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1210091feecdSAndrew Gallatin 
12111e413cf9SAndrew Gallatin 
12121e413cf9SAndrew Gallatin 	/* set the intrq size */
12131e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12141e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12151e413cf9SAndrew Gallatin 
12161e413cf9SAndrew Gallatin 	/*
12171e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12181e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12191e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12201e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12211e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12221e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12231e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12241e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12251e413cf9SAndrew Gallatin 	 */
12261e413cf9SAndrew Gallatin 
12271e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12281e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12291e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12301e413cf9SAndrew Gallatin 					   &cmd);
12311e413cf9SAndrew Gallatin 		if (status != 0) {
12321e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12331e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12341e413cf9SAndrew Gallatin 			return status;
12351e413cf9SAndrew Gallatin 		}
12361e413cf9SAndrew Gallatin 		/*
12371e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12381e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12391e413cf9SAndrew Gallatin 		 */
12401e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12411e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1242c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1243c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1244c6cb3e3fSAndrew Gallatin #endif
12451e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12461e413cf9SAndrew Gallatin 					   &cmd);
12471e413cf9SAndrew Gallatin 		if (status != 0) {
12481e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12491e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12501e413cf9SAndrew Gallatin 			return status;
12511e413cf9SAndrew Gallatin 		}
12521e413cf9SAndrew Gallatin 	}
12531e413cf9SAndrew Gallatin 
12541e413cf9SAndrew Gallatin 
1255adae7080SAndrew Gallatin 	if (interrupts_setup) {
1256b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12571e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12581e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12591e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12601e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12611e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12621e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12631e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12641e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12651e413cf9SAndrew Gallatin 						&cmd);
12661e413cf9SAndrew Gallatin 		}
1267adae7080SAndrew Gallatin 	}
1268b2fc195eSAndrew Gallatin 
12696d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12705e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12715e7d8541SAndrew Gallatin 
12725e7d8541SAndrew Gallatin 
12735e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12745e7d8541SAndrew Gallatin 
12755e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12761e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12775e7d8541SAndrew Gallatin 
12785e7d8541SAndrew Gallatin 
12795e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12806d87a65dSAndrew Gallatin 				&cmd);
12815e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1282b2fc195eSAndrew Gallatin 	if (status != 0) {
1283b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1284b2fc195eSAndrew Gallatin 		return status;
1285b2fc195eSAndrew Gallatin 	}
1286b2fc195eSAndrew Gallatin 
12875e7d8541SAndrew Gallatin 
12885e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12895e7d8541SAndrew Gallatin 
12905e7d8541SAndrew Gallatin 
12915e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12928fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12935e7d8541SAndrew Gallatin 
12941e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12951e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12961e413cf9SAndrew Gallatin 
12971e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1298b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12991e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
13001e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
13011e413cf9SAndrew Gallatin 		ss->tx.req = 0;
13021e413cf9SAndrew Gallatin 		ss->tx.done = 0;
13031e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1304c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1305c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1306c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
13071e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
13081e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
13091e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
13101e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
13111e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
13121e413cf9SAndrew Gallatin 		ss->lro_bad_csum = 0;
13131e413cf9SAndrew Gallatin 		ss->lro_queued = 0;
13141e413cf9SAndrew Gallatin 		ss->lro_flushed = 0;
13151e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
1316a393336bSAndrew Gallatin 			bzero(ss->fw_stats, sizeof *ss->fw_stats);
13171e413cf9SAndrew Gallatin 		}
13181e413cf9SAndrew Gallatin 	}
1319b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13206d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1321bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13226d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13230fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
132465c69066SAndrew Gallatin 	if (sc->throttle) {
132565c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
132665c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
132765c69066SAndrew Gallatin 				  &cmd)) {
132865c69066SAndrew Gallatin 			device_printf(sc->dev,
132965c69066SAndrew Gallatin 				      "can't enable throttle\n");
133065c69066SAndrew Gallatin 		}
133165c69066SAndrew Gallatin 	}
1332b2fc195eSAndrew Gallatin 	return status;
1333b2fc195eSAndrew Gallatin }
1334b2fc195eSAndrew Gallatin 
1335b2fc195eSAndrew Gallatin static int
133665c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
133765c69066SAndrew Gallatin {
133865c69066SAndrew Gallatin 	mxge_cmd_t cmd;
133965c69066SAndrew Gallatin 	mxge_softc_t *sc;
134065c69066SAndrew Gallatin 	int err;
134165c69066SAndrew Gallatin 	unsigned int throttle;
134265c69066SAndrew Gallatin 
134365c69066SAndrew Gallatin 	sc = arg1;
134465c69066SAndrew Gallatin 	throttle = sc->throttle;
134565c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
134665c69066SAndrew Gallatin         if (err != 0) {
134765c69066SAndrew Gallatin                 return err;
134865c69066SAndrew Gallatin         }
134965c69066SAndrew Gallatin 
135065c69066SAndrew Gallatin 	if (throttle == sc->throttle)
135165c69066SAndrew Gallatin 		return 0;
135265c69066SAndrew Gallatin 
135365c69066SAndrew Gallatin         if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
135465c69066SAndrew Gallatin                 return EINVAL;
135565c69066SAndrew Gallatin 
135665c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
135765c69066SAndrew Gallatin 	cmd.data0 = throttle;
135865c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
135965c69066SAndrew Gallatin 	if (err == 0)
136065c69066SAndrew Gallatin 		sc->throttle = throttle;
136165c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
136265c69066SAndrew Gallatin 	return err;
136365c69066SAndrew Gallatin }
136465c69066SAndrew Gallatin 
136565c69066SAndrew Gallatin static int
13666d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1367b2fc195eSAndrew Gallatin {
13686d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1369b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1370b2fc195eSAndrew Gallatin         int err;
1371b2fc195eSAndrew Gallatin 
1372b2fc195eSAndrew Gallatin         sc = arg1;
1373b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1374b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1375b2fc195eSAndrew Gallatin         if (err != 0) {
1376b2fc195eSAndrew Gallatin                 return err;
1377b2fc195eSAndrew Gallatin         }
1378b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1379b2fc195eSAndrew Gallatin                 return 0;
1380b2fc195eSAndrew Gallatin 
1381b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1382b2fc195eSAndrew Gallatin                 return EINVAL;
1383b2fc195eSAndrew Gallatin 
1384a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13855e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1386b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13875e7d8541SAndrew Gallatin 
1388a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1389b2fc195eSAndrew Gallatin         return err;
1390b2fc195eSAndrew Gallatin }
1391b2fc195eSAndrew Gallatin 
1392b2fc195eSAndrew Gallatin static int
13936d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1394b2fc195eSAndrew Gallatin {
13956d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1396b2fc195eSAndrew Gallatin         unsigned int enabled;
1397b2fc195eSAndrew Gallatin         int err;
1398b2fc195eSAndrew Gallatin 
1399b2fc195eSAndrew Gallatin         sc = arg1;
1400b2fc195eSAndrew Gallatin         enabled = sc->pause;
1401b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1402b2fc195eSAndrew Gallatin         if (err != 0) {
1403b2fc195eSAndrew Gallatin                 return err;
1404b2fc195eSAndrew Gallatin         }
1405b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1406b2fc195eSAndrew Gallatin                 return 0;
1407b2fc195eSAndrew Gallatin 
1408a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
14096d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1410a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1411b2fc195eSAndrew Gallatin         return err;
1412b2fc195eSAndrew Gallatin }
1413b2fc195eSAndrew Gallatin 
1414b2fc195eSAndrew Gallatin static int
1415f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1416f04b33f8SAndrew Gallatin {
1417f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1418c587e59fSAndrew Gallatin 	int err = 0;
1419f04b33f8SAndrew Gallatin 
1420f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1421f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1422f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1423f04b33f8SAndrew Gallatin 	else
1424f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1425f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1426c587e59fSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1427a393336bSAndrew Gallatin 		mxge_close(sc, 0);
1428f04b33f8SAndrew Gallatin 		err = mxge_open(sc);
1429c587e59fSAndrew Gallatin 	}
1430f04b33f8SAndrew Gallatin 	return err;
1431f04b33f8SAndrew Gallatin }
1432f04b33f8SAndrew Gallatin 
1433f04b33f8SAndrew Gallatin static int
1434276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1435276edd10SAndrew Gallatin {
1436276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1437276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1438276edd10SAndrew Gallatin 	int err;
1439276edd10SAndrew Gallatin 
1440276edd10SAndrew Gallatin 	sc = arg1;
1441276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1442276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1443276edd10SAndrew Gallatin 	if (err != 0)
1444276edd10SAndrew Gallatin 		return err;
1445276edd10SAndrew Gallatin 
1446276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1447276edd10SAndrew Gallatin 		return 0;
1448276edd10SAndrew Gallatin 
1449276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1450276edd10SAndrew Gallatin 		return EINVAL;
1451276edd10SAndrew Gallatin 
1452276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1453f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1454276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1455276edd10SAndrew Gallatin 	return err;
1456276edd10SAndrew Gallatin }
1457276edd10SAndrew Gallatin 
1458276edd10SAndrew Gallatin static int
14596d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1460b2fc195eSAndrew Gallatin {
1461b2fc195eSAndrew Gallatin         int err;
1462b2fc195eSAndrew Gallatin 
1463b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1464b2fc195eSAndrew Gallatin                 return EFAULT;
1465b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1466b2fc195eSAndrew Gallatin         arg1 = NULL;
1467b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1468b2fc195eSAndrew Gallatin 
1469b2fc195eSAndrew Gallatin         return err;
1470b2fc195eSAndrew Gallatin }
1471b2fc195eSAndrew Gallatin 
1472b2fc195eSAndrew Gallatin static void
14731e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14741e413cf9SAndrew Gallatin {
14751e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14761e413cf9SAndrew Gallatin 	int slice;
14771e413cf9SAndrew Gallatin 
14781e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14791e413cf9SAndrew Gallatin 		return;
14801e413cf9SAndrew Gallatin 
14811e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14821e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14831e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14841e413cf9SAndrew Gallatin 			continue;
14851e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14861e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14871e413cf9SAndrew Gallatin 	}
14881e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14891e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14901e413cf9SAndrew Gallatin }
14911e413cf9SAndrew Gallatin 
14921e413cf9SAndrew Gallatin static void
14936d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1494b2fc195eSAndrew Gallatin {
1495b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1496b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14975e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14981e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14991e413cf9SAndrew Gallatin 	int slice;
15001e413cf9SAndrew Gallatin 	char slice_num[8];
1501b2fc195eSAndrew Gallatin 
1502b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1503b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
15041e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1505b2fc195eSAndrew Gallatin 
15065e7d8541SAndrew Gallatin 	/* random information */
15075e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15085e7d8541SAndrew Gallatin 		       "firmware_version",
15095e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
15105e7d8541SAndrew Gallatin 		       0, "firmware version");
15115e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15125e7d8541SAndrew Gallatin 		       "serial_number",
15135e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
15145e7d8541SAndrew Gallatin 		       0, "serial number");
15155e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15165e7d8541SAndrew Gallatin 		       "product_code",
15175e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
15185e7d8541SAndrew Gallatin 		       0, "product_code");
15195e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1520d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1521d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1522d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1523d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15245e7d8541SAndrew Gallatin 		       "tx_boundary",
15251e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
15265e7d8541SAndrew Gallatin 		       0, "tx_boundary");
15275e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1528091feecdSAndrew Gallatin 		       "write_combine",
1529091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1530091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1531091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15325e7d8541SAndrew Gallatin 		       "read_dma_MBs",
15335e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
15345e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
15355e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15365e7d8541SAndrew Gallatin 		       "write_dma_MBs",
15375e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
15385e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
15395e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15405e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
15415e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
15425e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
1543a393336bSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1544a393336bSAndrew Gallatin 		       "watchdog_resets",
1545a393336bSAndrew Gallatin 		       CTLFLAG_RD, &sc->watchdog_resets,
1546a393336bSAndrew Gallatin 		       0, "Number of times NIC was reset");
15475e7d8541SAndrew Gallatin 
15485e7d8541SAndrew Gallatin 
15495e7d8541SAndrew Gallatin 	/* performance related tunables */
1550b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1551b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1552b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15536d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1554b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1555b2fc195eSAndrew Gallatin 
1556b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
155765c69066SAndrew Gallatin 			"throttle",
155865c69066SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
155965c69066SAndrew Gallatin 			0, mxge_change_throttle,
156065c69066SAndrew Gallatin 			"I", "transmit throttling");
156165c69066SAndrew Gallatin 
156265c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1563b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1564b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15656d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1566b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1567b2fc195eSAndrew Gallatin 
1568b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15695e7d8541SAndrew Gallatin 		       "deassert_wait",
15705e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15715e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1572b2fc195eSAndrew Gallatin 
1573b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1574b2fc195eSAndrew Gallatin 	   Need to swap it */
1575b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1576b2fc195eSAndrew Gallatin 			"link_up",
1577b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15786d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1579b2fc195eSAndrew Gallatin 			"I", "link up");
1580b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1581b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1582b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15836d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1584b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1585b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1586adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1587adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1588adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15896d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1590adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1591adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1592adae7080SAndrew Gallatin 			"dropped_bad_phy",
1593adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1594adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1595adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1596adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1597b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1598b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1599b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1600b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
16016d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1602b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1603b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1604adae7080SAndrew Gallatin 			"dropped_link_overflow",
1605adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1606adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1607adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1608adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
16090fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
16100fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
16110fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
16120fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
16130fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
16140fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1615adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1616adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
16176d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1618adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1619b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1620b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1621b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1622b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
16236d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1624b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1625b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1626adae7080SAndrew Gallatin 			"dropped_overrun",
1627adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
16286d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1629adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1630adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1631adae7080SAndrew Gallatin 			"dropped_pause",
1632adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1633adae7080SAndrew Gallatin 			&fw->dropped_pause,
1634adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1635adae7080SAndrew Gallatin 			"I", "dropped_pause");
1636adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1637adae7080SAndrew Gallatin 			"dropped_runt",
1638adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1639adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1640adae7080SAndrew Gallatin 			"I", "dropped_runt");
1641b2fc195eSAndrew Gallatin 
1642a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1643a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1644a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1645a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1646a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1647a0394e33SAndrew Gallatin 
16485e7d8541SAndrew Gallatin 	/* verbose printing? */
1649b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16505e7d8541SAndrew Gallatin 		       "verbose",
16515e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
16525e7d8541SAndrew Gallatin 		       0, "verbose printing");
1653b2fc195eSAndrew Gallatin 
1654053e637fSAndrew Gallatin 	/* lro */
1655276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1656276edd10SAndrew Gallatin 			"lro_cnt",
1657276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1658276edd10SAndrew Gallatin 			0, mxge_change_lro,
1659276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1660053e637fSAndrew Gallatin 
16611e413cf9SAndrew Gallatin 
16621e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
16631e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
16641e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
16651e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
16661e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
16671e413cf9SAndrew Gallatin 
16681e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16691e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16701e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16711e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16721e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16731e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16741e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16751e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16761e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
16771e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1678053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16791e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16801e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16811e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16821e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16831e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16841e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16851e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16861e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16871e413cf9SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
1688053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1689053e637fSAndrew Gallatin 
1690053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16911e413cf9SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16921e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16931e413cf9SAndrew Gallatin 			       "queues");
1694053e637fSAndrew Gallatin 
1695c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16961e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16971e413cf9SAndrew Gallatin 		if (slice > 0)
16981e413cf9SAndrew Gallatin 			continue;
1699c6cb3e3fSAndrew Gallatin #endif
1700c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1701c6cb3e3fSAndrew Gallatin 			       "tx_req",
1702c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1703c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
17041e413cf9SAndrew Gallatin 
17051e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17061e413cf9SAndrew Gallatin 			       "tx_done",
17071e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
17081e413cf9SAndrew Gallatin 			       0, "tx_done");
17091e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17101e413cf9SAndrew Gallatin 			       "tx_pkt_done",
17111e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
17121e413cf9SAndrew Gallatin 			       0, "tx_done");
17131e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17141e413cf9SAndrew Gallatin 			       "tx_stall",
17151e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
17161e413cf9SAndrew Gallatin 			       0, "tx_stall");
17171e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17181e413cf9SAndrew Gallatin 			       "tx_wake",
17191e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
17201e413cf9SAndrew Gallatin 			       0, "tx_wake");
17211e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17221e413cf9SAndrew Gallatin 			       "tx_defrag",
17231e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
17241e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1725c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1726c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1727c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1728c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1729c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1730c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1731c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1732c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1733c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1734c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1735c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1736c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
17371e413cf9SAndrew Gallatin 	}
1738b2fc195eSAndrew Gallatin }
1739b2fc195eSAndrew Gallatin 
1740b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1741b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1742b2fc195eSAndrew Gallatin 
1743b2fc195eSAndrew Gallatin static inline void
17441e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1745b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1746b2fc195eSAndrew Gallatin {
1747b2fc195eSAndrew Gallatin         int idx, starting_slot;
1748b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1749b2fc195eSAndrew Gallatin         while (cnt > 1) {
1750b2fc195eSAndrew Gallatin                 cnt--;
1751b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
17526d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1753b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
175473c7c83fSAndrew Gallatin                 wmb();
1755b2fc195eSAndrew Gallatin         }
1756b2fc195eSAndrew Gallatin }
1757b2fc195eSAndrew Gallatin 
1758b2fc195eSAndrew Gallatin /*
1759b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1760b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1761b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1762b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1763b2fc195eSAndrew Gallatin  */
1764b2fc195eSAndrew Gallatin 
1765b2fc195eSAndrew Gallatin static inline void
17661e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1767b2fc195eSAndrew Gallatin                   int cnt)
1768b2fc195eSAndrew Gallatin {
1769b2fc195eSAndrew Gallatin         int idx, i;
1770b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1771b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1772b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1773b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
17745e7d8541SAndrew Gallatin 	uint8_t last_flags;
1775b2fc195eSAndrew Gallatin 
1776b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1777b2fc195eSAndrew Gallatin 
17785e7d8541SAndrew Gallatin 	last_flags = src->flags;
17795e7d8541SAndrew Gallatin 	src->flags = 0;
178073c7c83fSAndrew Gallatin         wmb();
1781b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1782b2fc195eSAndrew Gallatin         srcp = src;
1783b2fc195eSAndrew Gallatin 
1784b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1785b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
17866d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
178773c7c83fSAndrew Gallatin                         wmb(); /* force write every 32 bytes */
1788b2fc195eSAndrew Gallatin                         srcp += 2;
1789b2fc195eSAndrew Gallatin                         dstp += 2;
1790b2fc195eSAndrew Gallatin                 }
1791b2fc195eSAndrew Gallatin         } else {
1792b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1793b2fc195eSAndrew Gallatin                    that it is submitted below */
17946d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1795b2fc195eSAndrew Gallatin                 i = 0;
1796b2fc195eSAndrew Gallatin         }
1797b2fc195eSAndrew Gallatin         if (i < cnt) {
1798b2fc195eSAndrew Gallatin                 /* submit the first request */
17996d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
180073c7c83fSAndrew Gallatin                 wmb(); /* barrier before setting valid flag */
1801b2fc195eSAndrew Gallatin         }
1802b2fc195eSAndrew Gallatin 
1803b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
18045e7d8541SAndrew Gallatin         src->flags = last_flags;
1805b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1806b2fc195eSAndrew Gallatin         src_ints+=3;
1807b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1808b2fc195eSAndrew Gallatin         dst_ints+=3;
1809b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1810b2fc195eSAndrew Gallatin         tx->req += cnt;
181173c7c83fSAndrew Gallatin         wmb();
1812b2fc195eSAndrew Gallatin }
1813b2fc195eSAndrew Gallatin 
181437d89b0cSAndrew Gallatin #if IFCAP_TSO4
181537d89b0cSAndrew Gallatin 
1816b2fc195eSAndrew Gallatin static void
18171e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
18181e413cf9SAndrew Gallatin 	       int busdma_seg_cnt, int ip_off)
1819aed8e389SAndrew Gallatin {
18201e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1821aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1822aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1823aed8e389SAndrew Gallatin 	struct ip *ip;
1824aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1825aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1826aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1827aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1828aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1829aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1830aed8e389SAndrew Gallatin 	static int once;
1831aed8e389SAndrew Gallatin 
1832aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1833aed8e389SAndrew Gallatin 
1834aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1835aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1836aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1837aed8e389SAndrew Gallatin 	 */
1838aed8e389SAndrew Gallatin 
1839aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1840aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1841aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1842c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1843c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
18441e413cf9SAndrew Gallatin 			   ss->scratch);
18451e413cf9SAndrew Gallatin 		ip = (struct ip *)(ss->scratch + ip_off);
1846aed8e389SAndrew Gallatin 	} else {
1847c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1848aed8e389SAndrew Gallatin 	}
1849c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1850aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1851c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
18521e413cf9SAndrew Gallatin 			   + sizeof (*tcp),  ss->scratch);
1853c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1854aed8e389SAndrew Gallatin 	}
1855aed8e389SAndrew Gallatin 
1856aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1857c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
18584ed8ca8fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1859aed8e389SAndrew Gallatin 
1860aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
18614ed8ca8fSAndrew Gallatin 	if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP)) == 0)) {
18624ed8ca8fSAndrew Gallatin 		/*
18634ed8ca8fSAndrew Gallatin 		 * If packet has full TCP csum, replace it with pseudo hdr
18644ed8ca8fSAndrew Gallatin 		 * sum that the NIC expects, otherwise the NIC will emit
18654ed8ca8fSAndrew Gallatin 		 * packets with bad TCP checksums.
18664ed8ca8fSAndrew Gallatin 		 */
18674ed8ca8fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_TCP;
18684ed8ca8fSAndrew Gallatin 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
18694ed8ca8fSAndrew Gallatin 		tcp->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
18704ed8ca8fSAndrew Gallatin 			htons(IPPROTO_TCP + (m->m_pkthdr.len - cksum_offset)));
18714ed8ca8fSAndrew Gallatin 	}
1872aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1873aed8e389SAndrew Gallatin 
1874aed8e389SAndrew Gallatin 
1875aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1876aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1877aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1878aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1879aed8e389SAndrew Gallatin 
18801e413cf9SAndrew Gallatin 	tx = &ss->tx;
1881aed8e389SAndrew Gallatin 	req = tx->req_list;
1882aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1883aed8e389SAndrew Gallatin 	cnt = 0;
1884aed8e389SAndrew Gallatin 	rdma_count = 0;
1885aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1886aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1887aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1888aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1889aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1890aed8e389SAndrew Gallatin 	 *
1891aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1892aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1893aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1894aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1895aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1896aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1897aed8e389SAndrew Gallatin 	 *
1898aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1899aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1900aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1901aed8e389SAndrew Gallatin 	 */
1902aed8e389SAndrew Gallatin 
1903aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1904aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1905aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1906aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1907e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1908aed8e389SAndrew Gallatin 
1909aed8e389SAndrew Gallatin 		while (len) {
1910aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1911e39a0a37SAndrew Gallatin 			seglen = len;
1912aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1913aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1914aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1915aed8e389SAndrew Gallatin 				/* payload */
1916aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1917aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1918aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1919aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1920aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1921aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1922aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1923aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1924aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1925aed8e389SAndrew Gallatin 				/* header ends */
1926aed8e389SAndrew Gallatin 				rdma_count = -1;
1927aed8e389SAndrew Gallatin 				cum_len_next = 0;
1928aed8e389SAndrew Gallatin 				seglen = -cum_len;
1929aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1930aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1931aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1932aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1933aed8e389SAndrew Gallatin 			    }
1934aed8e389SAndrew Gallatin 
1935aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1936aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1937aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1938aed8e389SAndrew Gallatin 			req->pad = 0;
1939aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1940aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1941aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1942aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1943aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1944aed8e389SAndrew Gallatin 			low += seglen;
1945aed8e389SAndrew Gallatin 			len -= seglen;
1946aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1947aed8e389SAndrew Gallatin 			flags = flags_next;
1948aed8e389SAndrew Gallatin 			req++;
1949aed8e389SAndrew Gallatin 			cnt++;
1950aed8e389SAndrew Gallatin 			rdma_count++;
1951aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1952aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1953aed8e389SAndrew Gallatin 			else
1954aed8e389SAndrew Gallatin 				cksum_offset = 0;
1955adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1956aed8e389SAndrew Gallatin 				goto drop;
1957aed8e389SAndrew Gallatin 		}
1958aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1959aed8e389SAndrew Gallatin 		seg++;
1960aed8e389SAndrew Gallatin 	}
1961aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1962aed8e389SAndrew Gallatin 
1963aed8e389SAndrew Gallatin 	do {
1964aed8e389SAndrew Gallatin 		req--;
1965aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1966aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1967aed8e389SAndrew Gallatin 
1968aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1969aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1970c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1971c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1972c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1973c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1974c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1975c6cb3e3fSAndrew Gallatin 		tx->activate++;
1976c6cb3e3fSAndrew Gallatin 		wmb();
1977c6cb3e3fSAndrew Gallatin 	}
1978c6cb3e3fSAndrew Gallatin #endif
1979aed8e389SAndrew Gallatin 	return;
1980aed8e389SAndrew Gallatin 
1981aed8e389SAndrew Gallatin drop:
1982e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1983aed8e389SAndrew Gallatin 	m_freem(m);
1984c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1985aed8e389SAndrew Gallatin 	if (!once) {
1986adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1987adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1988adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1989aed8e389SAndrew Gallatin 		once = 1;
1990aed8e389SAndrew Gallatin 	}
1991aed8e389SAndrew Gallatin 	return;
1992aed8e389SAndrew Gallatin 
1993aed8e389SAndrew Gallatin }
1994aed8e389SAndrew Gallatin 
199537d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
199637d89b0cSAndrew Gallatin 
199737d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1998c792928fSAndrew Gallatin /*
1999c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
2000c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
2001c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
2002c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
2003c792928fSAndrew Gallatin  */
2004c792928fSAndrew Gallatin static struct mbuf *
2005c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
2006c792928fSAndrew Gallatin {
2007c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2008c792928fSAndrew Gallatin 
2009c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
2010c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
2011c792928fSAndrew Gallatin 		return NULL;
2012c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
2013c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
2014c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2015c792928fSAndrew Gallatin 			return NULL;
2016c792928fSAndrew Gallatin 	}
2017c792928fSAndrew Gallatin 	/*
2018c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2019c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2020c792928fSAndrew Gallatin 	 */
2021c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2022c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2023c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2024c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2025c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2026c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2027c792928fSAndrew Gallatin 	return m;
2028c792928fSAndrew Gallatin }
202937d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2030c792928fSAndrew Gallatin 
2031aed8e389SAndrew Gallatin static void
20321e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2033b2fc195eSAndrew Gallatin {
20341e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2035b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2036b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2037b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
2038b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20391e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2040b2fc195eSAndrew Gallatin 	struct ip *ip;
2041c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
2042aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2043aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
2044b2fc195eSAndrew Gallatin 
2045b2fc195eSAndrew Gallatin 
20461e413cf9SAndrew Gallatin 	sc = ss->sc;
2047b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20481e413cf9SAndrew Gallatin 	tx = &ss->tx;
2049b2fc195eSAndrew Gallatin 
2050c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
205137d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2052c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2053c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2054c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2055c792928fSAndrew Gallatin 			goto drop;
2056c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
2057c792928fSAndrew Gallatin 	}
205837d89b0cSAndrew Gallatin #endif
2059b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2060b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2061b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2062aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2063b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2064adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2065b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2066b2fc195eSAndrew Gallatin 		   to defrag */
2067b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2068b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2069b2fc195eSAndrew Gallatin 			goto drop;
2070b2fc195eSAndrew Gallatin 		}
20711e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2072b2fc195eSAndrew Gallatin 		m = m_tmp;
2073b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2074b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2075aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2076b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2077b2fc195eSAndrew Gallatin 	}
2078adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2079aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2080aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2081b2fc195eSAndrew Gallatin 		goto drop;
2082b2fc195eSAndrew Gallatin 	}
2083b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2084b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20855e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2086b2fc195eSAndrew Gallatin 
208737d89b0cSAndrew Gallatin #if IFCAP_TSO4
2088aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2089aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20901e413cf9SAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, ip_off);
2091aed8e389SAndrew Gallatin 		return;
2092aed8e389SAndrew Gallatin 	}
209337d89b0cSAndrew Gallatin #endif
2094aed8e389SAndrew Gallatin 
2095b2fc195eSAndrew Gallatin 	req = tx->req_list;
2096b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20975e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20985e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2099b2fc195eSAndrew Gallatin 
2100b2fc195eSAndrew Gallatin 	/* checksum offloading? */
2101b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
2102aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2103aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
2104c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
2105c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
21061e413cf9SAndrew Gallatin 				   ss->scratch);
21071e413cf9SAndrew Gallatin 			ip = (struct ip *)(ss->scratch + ip_off);
2108aed8e389SAndrew Gallatin 		} else {
2109c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
2110aed8e389SAndrew Gallatin 		}
2111c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
2112b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
21135e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2114b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
21155e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2116aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2117aed8e389SAndrew Gallatin 	} else {
2118aed8e389SAndrew Gallatin 		odd_flag = 0;
2119b2fc195eSAndrew Gallatin 	}
21205e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21215e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2122b2fc195eSAndrew Gallatin 
2123b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2124b2fc195eSAndrew Gallatin 	cum_len = 0;
2125aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21265e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2127b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2128b2fc195eSAndrew Gallatin 		req->addr_low =
21296d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2130b2fc195eSAndrew Gallatin 		req->addr_high =
21316d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2132b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2133b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2134b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2135b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2136b2fc195eSAndrew Gallatin 		else
2137b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21385e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21395e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21405e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2141aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2142b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2143b2fc195eSAndrew Gallatin 		seg++;
2144b2fc195eSAndrew Gallatin 		req++;
2145b2fc195eSAndrew Gallatin 		req->flags = 0;
2146b2fc195eSAndrew Gallatin 	}
2147b2fc195eSAndrew Gallatin 	req--;
2148b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2149b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2150b2fc195eSAndrew Gallatin 		req++;
2151b2fc195eSAndrew Gallatin 		req->addr_low =
21526d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2153b2fc195eSAndrew Gallatin 		req->addr_high =
21546d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2155b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21565e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21575e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21585e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21595e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2160aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2161b2fc195eSAndrew Gallatin 		cnt++;
2162b2fc195eSAndrew Gallatin 	}
21635e7d8541SAndrew Gallatin 
21645e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
21655e7d8541SAndrew Gallatin #if 0
21665e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21675e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21685e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21695e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21705e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21715e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21725e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21735e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21745e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21755e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21765e7d8541SAndrew Gallatin 	}
21775e7d8541SAndrew Gallatin 	printf("--------------\n");
21785e7d8541SAndrew Gallatin #endif
21795e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21806d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2181c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2182c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2183c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2184c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2185c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2186c6cb3e3fSAndrew Gallatin 		tx->activate++;
2187c6cb3e3fSAndrew Gallatin 		wmb();
2188c6cb3e3fSAndrew Gallatin 	}
2189c6cb3e3fSAndrew Gallatin #endif
2190b2fc195eSAndrew Gallatin 	return;
2191b2fc195eSAndrew Gallatin 
2192b2fc195eSAndrew Gallatin drop:
2193b2fc195eSAndrew Gallatin 	m_freem(m);
2194c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2195b2fc195eSAndrew Gallatin 	return;
2196b2fc195eSAndrew Gallatin }
2197b2fc195eSAndrew Gallatin 
2198c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2199c6cb3e3fSAndrew Gallatin static void
2200c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2201c6cb3e3fSAndrew Gallatin {
2202c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2203c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2204c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2205c6cb3e3fSAndrew Gallatin 	int slice;
2206b2fc195eSAndrew Gallatin 
2207c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2208c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2209c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2210c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2211c6cb3e3fSAndrew Gallatin 			m_freem(m);
2212c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2213c6cb3e3fSAndrew Gallatin 	}
2214c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2215c6cb3e3fSAndrew Gallatin }
22166d914a32SAndrew Gallatin 
2217c6cb3e3fSAndrew Gallatin static inline void
2218c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2219c6cb3e3fSAndrew Gallatin {
2220c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2221c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2222c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2223c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2224c6cb3e3fSAndrew Gallatin 
2225c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2226c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2227c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2228c6cb3e3fSAndrew Gallatin 
2229c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2230c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2231c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2232c6cb3e3fSAndrew Gallatin 			return;
2233c6cb3e3fSAndrew Gallatin 		}
2234c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2235c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2236c6cb3e3fSAndrew Gallatin 
2237c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2238c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2239c6cb3e3fSAndrew Gallatin 	}
2240c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2241c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2242c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2243c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2244c6cb3e3fSAndrew Gallatin 		tx->stall++;
2245c6cb3e3fSAndrew Gallatin 	}
2246c6cb3e3fSAndrew Gallatin }
2247c6cb3e3fSAndrew Gallatin 
2248c6cb3e3fSAndrew Gallatin static int
2249c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2250c6cb3e3fSAndrew Gallatin {
2251c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2252c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2253c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2254c6cb3e3fSAndrew Gallatin 	int err;
2255c6cb3e3fSAndrew Gallatin 
2256c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2257c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2258c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2259c6cb3e3fSAndrew Gallatin 
2260c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2261c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2262c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2263c6cb3e3fSAndrew Gallatin 		return (err);
2264c6cb3e3fSAndrew Gallatin 	}
2265c6cb3e3fSAndrew Gallatin 
2266193cbc4dSMax Laier 	if (!drbr_needs_enqueue(ifp, tx->br) &&
2267c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2268c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2269c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2270c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2271c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2272c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2273c6cb3e3fSAndrew Gallatin 		return (err);
2274c6cb3e3fSAndrew Gallatin 	}
2275c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2276c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2277c6cb3e3fSAndrew Gallatin 	return (0);
2278c6cb3e3fSAndrew Gallatin }
2279c6cb3e3fSAndrew Gallatin 
2280c6cb3e3fSAndrew Gallatin static int
2281c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2282c6cb3e3fSAndrew Gallatin {
2283c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2284c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2285c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2286c6cb3e3fSAndrew Gallatin 	int err = 0;
2287c6cb3e3fSAndrew Gallatin 	int slice;
2288c6cb3e3fSAndrew Gallatin 
2289c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2290c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2291c6cb3e3fSAndrew Gallatin 
2292c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2293c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2294c6cb3e3fSAndrew Gallatin 
2295c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2296c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2297c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2298c6cb3e3fSAndrew Gallatin 	} else {
2299c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2300c6cb3e3fSAndrew Gallatin 	}
2301c6cb3e3fSAndrew Gallatin 
2302c6cb3e3fSAndrew Gallatin 	return (err);
2303c6cb3e3fSAndrew Gallatin }
2304c6cb3e3fSAndrew Gallatin 
2305c6cb3e3fSAndrew Gallatin #else
23066d914a32SAndrew Gallatin 
23076d914a32SAndrew Gallatin static inline void
23081e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2309b2fc195eSAndrew Gallatin {
23101e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2311b2fc195eSAndrew Gallatin 	struct mbuf *m;
2312b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
23131e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2314b2fc195eSAndrew Gallatin 
23151e413cf9SAndrew Gallatin 	sc = ss->sc;
2316b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23171e413cf9SAndrew Gallatin 	tx = &ss->tx;
2318adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
23196d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
23206d914a32SAndrew Gallatin 		if (m == NULL) {
23216d914a32SAndrew Gallatin 			return;
23226d914a32SAndrew Gallatin 		}
2323b2fc195eSAndrew Gallatin 		/* let BPF see it */
2324b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2325b2fc195eSAndrew Gallatin 
2326b2fc195eSAndrew Gallatin 		/* give it to the nic */
23271e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
23286d914a32SAndrew Gallatin 	}
23296d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2330a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2331b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2332adae7080SAndrew Gallatin 		tx->stall++;
2333a82c2581SAndrew Gallatin 	}
2334b2fc195eSAndrew Gallatin }
2335c6cb3e3fSAndrew Gallatin #endif
2336b2fc195eSAndrew Gallatin static void
23376d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2338b2fc195eSAndrew Gallatin {
23396d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
23401e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2341b2fc195eSAndrew Gallatin 
23421e413cf9SAndrew Gallatin 	/* only use the first slice for now */
23431e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
23441e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
23451e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
23461e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2347b2fc195eSAndrew Gallatin }
2348b2fc195eSAndrew Gallatin 
23495e7d8541SAndrew Gallatin /*
23505e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23515e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23525e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23535e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23545e7d8541SAndrew Gallatin  * in a burst
23555e7d8541SAndrew Gallatin  */
23565e7d8541SAndrew Gallatin static inline void
23575e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23585e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
23595e7d8541SAndrew Gallatin {
23605e7d8541SAndrew Gallatin 	uint32_t low;
23615e7d8541SAndrew Gallatin 
23625e7d8541SAndrew Gallatin 	low = src->addr_low;
23635e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2364a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
236573c7c83fSAndrew Gallatin 	wmb();
2366a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
236773c7c83fSAndrew Gallatin 	wmb();
236840385a5fSAndrew Gallatin 	src->addr_low = low;
23695e7d8541SAndrew Gallatin 	dst->addr_low = low;
237073c7c83fSAndrew Gallatin 	wmb();
23715e7d8541SAndrew Gallatin }
23725e7d8541SAndrew Gallatin 
2373b2fc195eSAndrew Gallatin static int
23741e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2375b2fc195eSAndrew Gallatin {
2376b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2377b2fc195eSAndrew Gallatin 	struct mbuf *m;
23781e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2379b2fc195eSAndrew Gallatin 	int cnt, err;
2380b2fc195eSAndrew Gallatin 
2381b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
2382b2fc195eSAndrew Gallatin 	if (m == NULL) {
2383b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2384b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2385b2fc195eSAndrew Gallatin 		goto done;
2386b2fc195eSAndrew Gallatin 	}
2387b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2388b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2389b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2390b2fc195eSAndrew Gallatin 	if (err != 0) {
2391b2fc195eSAndrew Gallatin 		m_free(m);
2392b2fc195eSAndrew Gallatin 		goto done;
2393b2fc195eSAndrew Gallatin 	}
2394b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2395b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23966d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2397b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23986d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2399b2fc195eSAndrew Gallatin 
2400b2fc195eSAndrew Gallatin done:
2401adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2402adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2403b2fc195eSAndrew Gallatin 	return err;
2404b2fc195eSAndrew Gallatin }
2405b2fc195eSAndrew Gallatin 
2406b2fc195eSAndrew Gallatin static int
24071e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2408b2fc195eSAndrew Gallatin {
2409053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2410b2fc195eSAndrew Gallatin 	struct mbuf *m;
24111e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2412053e637fSAndrew Gallatin 	int cnt, err, i;
2413b2fc195eSAndrew Gallatin 
2414053e637fSAndrew Gallatin 	m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2415b2fc195eSAndrew Gallatin 	if (m == NULL) {
2416b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2417b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2418b2fc195eSAndrew Gallatin 		goto done;
2419b2fc195eSAndrew Gallatin 	}
24204d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2421b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2422053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2423b2fc195eSAndrew Gallatin 	if (err != 0) {
2424b2fc195eSAndrew Gallatin 		m_free(m);
2425b2fc195eSAndrew Gallatin 		goto done;
2426b2fc195eSAndrew Gallatin 	}
2427b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2428b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2429b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2430b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2431b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2432053e637fSAndrew Gallatin 
2433b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2434b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2435053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2436053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2437053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2438053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2439053e637fSAndrew Gallatin        }
2440b0f7b922SAndrew Gallatin #endif
2441b2fc195eSAndrew Gallatin 
2442b2fc195eSAndrew Gallatin done:
2443053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2444b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
24455e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
24465e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2447b2fc195eSAndrew Gallatin 		}
2448053e637fSAndrew Gallatin 		idx++;
2449053e637fSAndrew Gallatin 	}
2450b2fc195eSAndrew Gallatin 	return err;
2451b2fc195eSAndrew Gallatin }
2452b2fc195eSAndrew Gallatin 
24539b03b0f3SAndrew Gallatin /*
24549b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
24559b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
24569b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
24579b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2458053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2459053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
24609b03b0f3SAndrew Gallatin  */
24619b03b0f3SAndrew Gallatin 
2462053e637fSAndrew Gallatin static inline uint16_t
2463053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2464053e637fSAndrew Gallatin {
2465053e637fSAndrew Gallatin 	struct ether_header *eh;
2466053e637fSAndrew Gallatin 	struct ip *ip;
2467053e637fSAndrew Gallatin 	uint16_t c;
2468053e637fSAndrew Gallatin 
2469053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2470053e637fSAndrew Gallatin 
2471053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2472053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2473053e637fSAndrew Gallatin 		return 1;
2474053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2475053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2476053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2477053e637fSAndrew Gallatin 		return 1;
2478eb6219e3SAndrew Gallatin #ifdef INET
2479053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2480053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2481053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2482eb6219e3SAndrew Gallatin #else
2483eb6219e3SAndrew Gallatin 	c = 1;
2484eb6219e3SAndrew Gallatin #endif
2485053e637fSAndrew Gallatin 	c ^= 0xffff;
2486053e637fSAndrew Gallatin 	return (c);
24875e7d8541SAndrew Gallatin }
2488053e637fSAndrew Gallatin 
2489c792928fSAndrew Gallatin static void
2490c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2491c792928fSAndrew Gallatin {
2492c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2493c792928fSAndrew Gallatin 	struct ether_header *eh;
2494c792928fSAndrew Gallatin 	uint32_t partial;
2495c792928fSAndrew Gallatin 
2496c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2497c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2498c792928fSAndrew Gallatin 
2499c792928fSAndrew Gallatin 	/*
2500c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2501c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2502c792928fSAndrew Gallatin 	 * header.
2503c792928fSAndrew Gallatin 	 */
2504c792928fSAndrew Gallatin 
2505c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2506c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2507c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2508c792928fSAndrew Gallatin 	(*csum) += ~partial;
2509c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2510c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2511c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2512c792928fSAndrew Gallatin 
2513c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2514c792928fSAndrew Gallatin 	   later consumers expect this */
2515c792928fSAndrew Gallatin 	*csum = htons(*csum);
2516c792928fSAndrew Gallatin 
2517c792928fSAndrew Gallatin 	/* save the tag */
251837d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2519c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
252037d89b0cSAndrew Gallatin #else
252137d89b0cSAndrew Gallatin 	{
252237d89b0cSAndrew Gallatin 		struct m_tag *mtag;
252337d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
252437d89b0cSAndrew Gallatin 				   M_NOWAIT);
252537d89b0cSAndrew Gallatin 		if (mtag == NULL)
252637d89b0cSAndrew Gallatin 			return;
252737d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
252837d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
252937d89b0cSAndrew Gallatin 	}
253037d89b0cSAndrew Gallatin 
253137d89b0cSAndrew Gallatin #endif
253237d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2533c792928fSAndrew Gallatin 
2534c792928fSAndrew Gallatin 	/*
2535c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2536c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2537c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2538c792928fSAndrew Gallatin 	 * type field is already in place.
2539c792928fSAndrew Gallatin 	 */
2540c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2541c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2542c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2543c792928fSAndrew Gallatin }
2544c792928fSAndrew Gallatin 
25455e7d8541SAndrew Gallatin 
25465e7d8541SAndrew Gallatin static inline void
25471e413cf9SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2548b2fc195eSAndrew Gallatin {
25491e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2550b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2551053e637fSAndrew Gallatin 	struct mbuf *m;
2552c792928fSAndrew Gallatin 	struct ether_header *eh;
25531e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2554053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2555b2fc195eSAndrew Gallatin 	int idx;
2556053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2557b2fc195eSAndrew Gallatin 
25581e413cf9SAndrew Gallatin 	sc = ss->sc;
2559b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
25601e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2561b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2562053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2563b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2564b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2565b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25661e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2567053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2568053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2569053e637fSAndrew Gallatin 		return;
2570b2fc195eSAndrew Gallatin 	}
2571053e637fSAndrew Gallatin 
2572b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2573b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2574b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2575b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2576b2fc195eSAndrew Gallatin 
2577b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2578b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2579b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2580b2fc195eSAndrew Gallatin 
2581053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2582053e637fSAndrew Gallatin 	 * aligned */
25835e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2584b2fc195eSAndrew Gallatin 
2585053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2586053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25871e413cf9SAndrew Gallatin 	ss->ipackets++;
2588c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2589c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2590c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2591c792928fSAndrew Gallatin 	}
2592b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2593053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
25941e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2595b2fc195eSAndrew Gallatin 			return;
2596053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2597053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2598053e637fSAndrew Gallatin 		   checksum is good */
2599053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2600053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2601b2fc195eSAndrew Gallatin 	}
2602c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2603c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2604c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2605c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2606c6cb3e3fSAndrew Gallatin 	}
2607053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2608053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2609b2fc195eSAndrew Gallatin }
2610b2fc195eSAndrew Gallatin 
2611b2fc195eSAndrew Gallatin static inline void
26121e413cf9SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2613b2fc195eSAndrew Gallatin {
26141e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2615b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2616c792928fSAndrew Gallatin 	struct ether_header *eh;
2617b2fc195eSAndrew Gallatin 	struct mbuf *m;
26181e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2619b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2620b2fc195eSAndrew Gallatin 	int idx;
2621053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2622b2fc195eSAndrew Gallatin 
26231e413cf9SAndrew Gallatin 	sc = ss->sc;
2624b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26251e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2626b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2627b2fc195eSAndrew Gallatin 	rx->cnt++;
2628b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2629b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2630b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26311e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2632b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2633b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2634b2fc195eSAndrew Gallatin 		return;
2635b2fc195eSAndrew Gallatin 	}
2636b2fc195eSAndrew Gallatin 
2637b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2638b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2639b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2640b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2641b2fc195eSAndrew Gallatin 
2642b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2643b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2644b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2645b2fc195eSAndrew Gallatin 
2646b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2647b2fc195eSAndrew Gallatin 	 * aligned */
26485e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2649b2fc195eSAndrew Gallatin 
26509b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
26519b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
26521e413cf9SAndrew Gallatin 	ss->ipackets++;
2653c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2654c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2655c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2656c792928fSAndrew Gallatin 	}
2657b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2658053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
26591e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2660053e637fSAndrew Gallatin 			return;
2661053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2662053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2663053e637fSAndrew Gallatin 		   checksum is good */
2664053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2665053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2666053e637fSAndrew Gallatin 	}
2667c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2668c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2669c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2670c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2671c6cb3e3fSAndrew Gallatin 	}
2672b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2673b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2674b2fc195eSAndrew Gallatin }
2675b2fc195eSAndrew Gallatin 
2676b2fc195eSAndrew Gallatin static inline void
26771e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
26785e7d8541SAndrew Gallatin {
26791e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26805e7d8541SAndrew Gallatin 	int limit = 0;
26815e7d8541SAndrew Gallatin 	uint16_t length;
26825e7d8541SAndrew Gallatin 	uint16_t checksum;
26835e7d8541SAndrew Gallatin 
26845e7d8541SAndrew Gallatin 
26855e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
26865e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
26875e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2688053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2689b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
26901e413cf9SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum);
26915e7d8541SAndrew Gallatin 		else
26921e413cf9SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum);
26935e7d8541SAndrew Gallatin 		rx_done->cnt++;
2694adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
26955e7d8541SAndrew Gallatin 
26965e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2697f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
26985e7d8541SAndrew Gallatin 			break;
2699053e637fSAndrew Gallatin 	}
2700eb6219e3SAndrew Gallatin #ifdef INET
27011e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_active)) {
2702eb6219e3SAndrew Gallatin 		struct lro_entry *lro = SLIST_FIRST(&ss->lro_active);
27031e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
27041e413cf9SAndrew Gallatin 		mxge_lro_flush(ss, lro);
27055e7d8541SAndrew Gallatin 	}
2706eb6219e3SAndrew Gallatin #endif
27075e7d8541SAndrew Gallatin }
27085e7d8541SAndrew Gallatin 
27095e7d8541SAndrew Gallatin 
27105e7d8541SAndrew Gallatin static inline void
27111e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2712b2fc195eSAndrew Gallatin {
2713b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
27141e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2715b2fc195eSAndrew Gallatin 	struct mbuf *m;
2716b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2717f616ebc7SAndrew Gallatin 	int idx;
2718c6cb3e3fSAndrew Gallatin 	int *flags;
2719b2fc195eSAndrew Gallatin 
27201e413cf9SAndrew Gallatin 	tx = &ss->tx;
27211e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
27225e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2723b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2724b2fc195eSAndrew Gallatin 		tx->done++;
2725b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2726b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2727b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2728b2fc195eSAndrew Gallatin 		if (m != NULL) {
272971032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
273071032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
273171032832SAndrew Gallatin 				ss->omcasts++;
2732c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2733b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2734b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2735b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2736b2fc195eSAndrew Gallatin 			m_freem(m);
2737b2fc195eSAndrew Gallatin 		}
27385e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
27395e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
27405e7d8541SAndrew Gallatin 			tx->pkt_done++;
27415e7d8541SAndrew Gallatin 		}
2742b2fc195eSAndrew Gallatin 	}
2743b2fc195eSAndrew Gallatin 
2744b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2745b2fc195eSAndrew Gallatin            its OK to send packets */
2746c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2747c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2748c6cb3e3fSAndrew Gallatin #else
2749c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2750c6cb3e3fSAndrew Gallatin #endif
27511e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2752c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2753c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2754c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
27551e413cf9SAndrew Gallatin 		ss->tx.wake++;
27561e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2757b2fc195eSAndrew Gallatin 	}
2758c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2759c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2760c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2761c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2762c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2763c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2764c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2765c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2766c6cb3e3fSAndrew Gallatin 			wmb();
2767c6cb3e3fSAndrew Gallatin 		}
2768c6cb3e3fSAndrew Gallatin 	}
2769c6cb3e3fSAndrew Gallatin #endif
2770c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2771c6cb3e3fSAndrew Gallatin 
2772b2fc195eSAndrew Gallatin }
2773b2fc195eSAndrew Gallatin 
277401638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2775c587e59fSAndrew Gallatin {
2776c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2777c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2778c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2779c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
278001638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2781c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2782c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2783c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2784c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2785c587e59fSAndrew Gallatin };
278601638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
278701638550SAndrew Gallatin {
278851bc2092SAndrew Gallatin 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
27894ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
279001638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
279101638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
279256b67858SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
279356b67858SAndrew Gallatin 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
279401638550SAndrew Gallatin };
2795c587e59fSAndrew Gallatin 
2796c587e59fSAndrew Gallatin static void
2797c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type)
2798c587e59fSAndrew Gallatin {
2799c406ad2eSAndrew Gallatin 
2800c406ad2eSAndrew Gallatin 
2801c406ad2eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type,
2802c406ad2eSAndrew Gallatin 		    0, NULL);
2803c406ad2eSAndrew Gallatin 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
2804c406ad2eSAndrew Gallatin 	sc->current_media = media_type;
2805c406ad2eSAndrew Gallatin 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
2806c587e59fSAndrew Gallatin }
2807c587e59fSAndrew Gallatin 
2808c587e59fSAndrew Gallatin static void
2809c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc)
2810c587e59fSAndrew Gallatin {
2811c587e59fSAndrew Gallatin 	char *ptr;
2812c406ad2eSAndrew Gallatin 	int i;
2813c587e59fSAndrew Gallatin 
2814c406ad2eSAndrew Gallatin 	ifmedia_removeall(&sc->media);
2815c406ad2eSAndrew Gallatin 	mxge_media_set(sc, IFM_AUTO);
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");
2826c406ad2eSAndrew Gallatin 		return;
2827c587e59fSAndrew Gallatin 	}
2828c587e59fSAndrew Gallatin 
2829c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
2830dc15eac0SEd Schouten 		ptr = strchr(ptr, '-');
2831c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2832c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2833c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2834c587e59fSAndrew Gallatin 			return;
2835c587e59fSAndrew Gallatin 		}
2836c587e59fSAndrew Gallatin 	}
283730882b10SAndrew Gallatin 	if (*ptr == 'C' || *(ptr +1) == 'C') {
283801638550SAndrew Gallatin 		/* -C is CX4 */
2839c406ad2eSAndrew Gallatin 		sc->connector = MXGE_CX4;
2840c406ad2eSAndrew Gallatin 		mxge_media_set(sc, IFM_10G_CX4);
2841c406ad2eSAndrew Gallatin 	} else if (*ptr == 'Q') {
284201638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2843c406ad2eSAndrew Gallatin 		sc->connector = MXGE_QRF;
2844c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2845c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2846c406ad2eSAndrew Gallatin 	} else if (*ptr == 'R') {
2847c406ad2eSAndrew Gallatin 		/* -R is XFP */
2848c406ad2eSAndrew Gallatin 		sc->connector = MXGE_XFP;
2849c406ad2eSAndrew Gallatin 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
2850c406ad2eSAndrew Gallatin 		/* -S or -2S is SFP+ */
2851c406ad2eSAndrew Gallatin 		sc->connector = MXGE_SFP;
2852c406ad2eSAndrew Gallatin 	} else {
2853c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2854c406ad2eSAndrew Gallatin 	}
2855c587e59fSAndrew Gallatin }
2856c587e59fSAndrew Gallatin 
2857c406ad2eSAndrew Gallatin /*
2858c406ad2eSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2859c406ad2eSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2860c406ad2eSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2861c406ad2eSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2862c406ad2eSAndrew Gallatin  * than in the interrupt handler itself.
2863c406ad2eSAndrew Gallatin  */
2864c406ad2eSAndrew Gallatin static void
2865c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2866c406ad2eSAndrew Gallatin {
2867c406ad2eSAndrew Gallatin 	mxge_cmd_t cmd;
2868c406ad2eSAndrew Gallatin 	char *cage_type;
2869c406ad2eSAndrew Gallatin 
2870c406ad2eSAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
2871c406ad2eSAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
2872c406ad2eSAndrew Gallatin 	uint32_t byte;
2873c406ad2eSAndrew Gallatin 
2874c406ad2eSAndrew Gallatin 	sc->need_media_probe = 0;
2875c406ad2eSAndrew Gallatin 
2876c406ad2eSAndrew Gallatin 	if (sc->connector == MXGE_XFP) {
287701638550SAndrew Gallatin 		/* -R is XFP */
287801638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
287901638550SAndrew Gallatin 		mxge_media_type_entries =
288001638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types) /
288101638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types[0]);
288201638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
288301638550SAndrew Gallatin 		cage_type = "XFP";
2884c406ad2eSAndrew Gallatin 	} else 	if (sc->connector == MXGE_SFP) {
288501638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
288601638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
288701638550SAndrew Gallatin 		mxge_media_type_entries =
288801638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types) /
288901638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types[0]);
289001638550SAndrew Gallatin 		cage_type = "SFP+";
289101638550SAndrew Gallatin 		byte = 3;
2892c406ad2eSAndrew Gallatin 	} else {
2893c406ad2eSAndrew Gallatin 		/* nothing to do; media type cannot change */
2894c587e59fSAndrew Gallatin 		return;
2895c587e59fSAndrew Gallatin 	}
2896c587e59fSAndrew Gallatin 
2897c587e59fSAndrew Gallatin 	/*
2898c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2899c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2900c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2901c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2902c587e59fSAndrew Gallatin 	 * a millisecond
2903c587e59fSAndrew Gallatin 	 */
2904c587e59fSAndrew Gallatin 
2905c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
290601638550SAndrew Gallatin 	cmd.data1 = byte;
290701638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
290801638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2909c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2910c587e59fSAndrew Gallatin 	}
291101638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
291201638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2913c587e59fSAndrew Gallatin 	}
2914c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2915c587e59fSAndrew Gallatin 		return;
2916c587e59fSAndrew Gallatin 	}
2917c587e59fSAndrew Gallatin 
2918c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
291901638550SAndrew Gallatin 	cmd.data0 = byte;
292001638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2921c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2922c587e59fSAndrew Gallatin 		DELAY(1000);
292301638550SAndrew Gallatin 		cmd.data0 = byte;
292401638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2925c587e59fSAndrew Gallatin 	}
2926c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
292701638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
292801638550SAndrew Gallatin 			      cage_type, err, ms);
2929c587e59fSAndrew Gallatin 		return;
2930c587e59fSAndrew Gallatin 	}
2931c587e59fSAndrew Gallatin 
2932c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2933c587e59fSAndrew Gallatin 		if (mxge_verbose)
293401638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2935c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2936c406ad2eSAndrew Gallatin 		if (sc->current_media != mxge_media_types[0].flag) {
2937c406ad2eSAndrew Gallatin 			mxge_media_init(sc);
2938c406ad2eSAndrew Gallatin 			mxge_media_set(sc, mxge_media_types[0].flag);
2939c406ad2eSAndrew Gallatin 		}
2940c587e59fSAndrew Gallatin 		return;
2941c587e59fSAndrew Gallatin 	}
294201638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
2943c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2944c587e59fSAndrew Gallatin 			if (mxge_verbose)
294501638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
294601638550SAndrew Gallatin 					      cage_type,
2947c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2948c587e59fSAndrew Gallatin 
2949c406ad2eSAndrew Gallatin 			if (sc->current_media != mxge_media_types[i].flag) {
2950c406ad2eSAndrew Gallatin 				mxge_media_init(sc);
2951c406ad2eSAndrew Gallatin 				mxge_media_set(sc, mxge_media_types[i].flag);
2952c406ad2eSAndrew Gallatin 			}
2953c587e59fSAndrew Gallatin 			return;
2954c587e59fSAndrew Gallatin 		}
2955c587e59fSAndrew Gallatin 	}
2956c406ad2eSAndrew Gallatin 	if (mxge_verbose)
2957c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "%s media 0x%x unknown\n",
2958c406ad2eSAndrew Gallatin 			      cage_type, cmd.data0);
2959c587e59fSAndrew Gallatin 
2960c587e59fSAndrew Gallatin 	return;
2961c587e59fSAndrew Gallatin }
2962c587e59fSAndrew Gallatin 
2963b2fc195eSAndrew Gallatin static void
29646d87a65dSAndrew Gallatin mxge_intr(void *arg)
2965b2fc195eSAndrew Gallatin {
29661e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
29671e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
29681e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
29691e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
29701e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
29715e7d8541SAndrew Gallatin 	uint32_t send_done_count;
29725e7d8541SAndrew Gallatin 	uint8_t valid;
2973b2fc195eSAndrew Gallatin 
2974b2fc195eSAndrew Gallatin 
2975c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
29761e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
29771e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
29781e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
29791e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
29801e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
29811e413cf9SAndrew Gallatin 		return;
29821e413cf9SAndrew Gallatin 	}
2983c6cb3e3fSAndrew Gallatin #endif
29841e413cf9SAndrew Gallatin 
29855e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
29865e7d8541SAndrew Gallatin 	if (!stats->valid) {
29875e7d8541SAndrew Gallatin 		return;
2988b2fc195eSAndrew Gallatin 	}
29895e7d8541SAndrew Gallatin 	valid = stats->valid;
2990b2fc195eSAndrew Gallatin 
299191ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
29925e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
29935e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
29945e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
29955e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
29965e7d8541SAndrew Gallatin 			stats->valid = 0;
2997dc8731d4SAndrew Gallatin 	} else {
2998dc8731d4SAndrew Gallatin 		stats->valid = 0;
2999dc8731d4SAndrew Gallatin 	}
3000dc8731d4SAndrew Gallatin 
3001dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
30025e7d8541SAndrew Gallatin 	do {
30035e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
30045e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
30055e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
30065e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
3007c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
30081e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
30091e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
30105e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
3011b2fc195eSAndrew Gallatin 		}
301291ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
301373c7c83fSAndrew Gallatin 			wmb();
30145e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
3015b2fc195eSAndrew Gallatin 
3016c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
3017c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
30185e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
30195e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
3020b2fc195eSAndrew Gallatin 			if (sc->link_state) {
30215e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
3022c406ad2eSAndrew Gallatin 				 sc->ifp->if_baudrate = IF_Gbps(10UL);
30235e7d8541SAndrew Gallatin 				if (mxge_verbose)
30245e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
3025b2fc195eSAndrew Gallatin 			} else {
30265e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3027c406ad2eSAndrew Gallatin 				sc->ifp->if_baudrate = 0;
30285e7d8541SAndrew Gallatin 				if (mxge_verbose)
30295e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
3030b2fc195eSAndrew Gallatin 			}
3031c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
3032b2fc195eSAndrew Gallatin 		}
3033b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
30341e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
3035b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
30361e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
30375e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
30385e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
30395e7d8541SAndrew Gallatin 		}
3040c587e59fSAndrew Gallatin 
3041c587e59fSAndrew Gallatin 		if (stats->link_down) {
30425e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3043c587e59fSAndrew Gallatin 			sc->link_state = 0;
3044c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3045c587e59fSAndrew Gallatin 		}
3046b2fc195eSAndrew Gallatin 	}
3047b2fc195eSAndrew Gallatin 
30485e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
30495e7d8541SAndrew Gallatin 	if (valid & 0x1)
30501e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
30511e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3052b2fc195eSAndrew Gallatin }
3053b2fc195eSAndrew Gallatin 
3054b2fc195eSAndrew Gallatin static void
30556d87a65dSAndrew Gallatin mxge_init(void *arg)
3056b2fc195eSAndrew Gallatin {
30573cae7311SAndrew Gallatin 	mxge_softc_t *sc = arg;
30583cae7311SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
30593cae7311SAndrew Gallatin 
30603cae7311SAndrew Gallatin 
30613cae7311SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
30623cae7311SAndrew Gallatin 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
30633cae7311SAndrew Gallatin 		(void) mxge_open(sc);
30643cae7311SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3065b2fc195eSAndrew Gallatin }
3066b2fc195eSAndrew Gallatin 
3067b2fc195eSAndrew Gallatin 
3068b2fc195eSAndrew Gallatin 
3069b2fc195eSAndrew Gallatin static void
30701e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
30711e413cf9SAndrew Gallatin {
30721e413cf9SAndrew Gallatin 	struct lro_entry *lro_entry;
30731e413cf9SAndrew Gallatin 	int i;
30741e413cf9SAndrew Gallatin 
30751e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_free)) {
30761e413cf9SAndrew Gallatin 		lro_entry = SLIST_FIRST(&ss->lro_free);
30771e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
30781e413cf9SAndrew Gallatin 		free(lro_entry, M_DEVBUF);
30791e413cf9SAndrew Gallatin 	}
30801e413cf9SAndrew Gallatin 
30811e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
30821e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
30831e413cf9SAndrew Gallatin 			continue;
30841e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
30851e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
30861e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
30871e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
30881e413cf9SAndrew Gallatin 	}
30891e413cf9SAndrew Gallatin 
30901e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
30911e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
30921e413cf9SAndrew Gallatin 			continue;
30931e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
30941e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
30951e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
30961e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
30971e413cf9SAndrew Gallatin 	}
30981e413cf9SAndrew Gallatin 
30991e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
31001e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
31011e413cf9SAndrew Gallatin 		return;
31021e413cf9SAndrew Gallatin 
31031e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
31041e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
31051e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
31061e413cf9SAndrew Gallatin 			continue;
31071e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
31081e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
31091e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
31101e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
31111e413cf9SAndrew Gallatin 	}
31121e413cf9SAndrew Gallatin }
31131e413cf9SAndrew Gallatin 
31141e413cf9SAndrew Gallatin static void
31156d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3116b2fc195eSAndrew Gallatin {
31171e413cf9SAndrew Gallatin 	int slice;
31181e413cf9SAndrew Gallatin 
31191e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31201e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
31211e413cf9SAndrew Gallatin }
31221e413cf9SAndrew Gallatin 
31231e413cf9SAndrew Gallatin static void
31241e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
31251e413cf9SAndrew Gallatin {
3126b2fc195eSAndrew Gallatin 	int i;
3127b2fc195eSAndrew Gallatin 
3128b2fc195eSAndrew Gallatin 
31291e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
31301e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
31311e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3132b2fc195eSAndrew Gallatin 
31331e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
31341e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
31351e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
31361e413cf9SAndrew Gallatin 
31371e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
31381e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
31391e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
31401e413cf9SAndrew Gallatin 
31411e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
31421e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
31431e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
31441e413cf9SAndrew Gallatin 
31451e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
31461e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
31471e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
31481e413cf9SAndrew Gallatin 
31491e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
31501e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
31511e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
31521e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
31531e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3154b2fc195eSAndrew Gallatin 			}
31551e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
31561e413cf9SAndrew Gallatin 		}
31571e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
31581e413cf9SAndrew Gallatin 	}
31591e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
31601e413cf9SAndrew Gallatin 
31611e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
31621e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
31631e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
31641e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
31651e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
31661e413cf9SAndrew Gallatin 			}
31671e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
31681e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
31691e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
31701e413cf9SAndrew Gallatin 		}
31711e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
31721e413cf9SAndrew Gallatin 	}
31731e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
31741e413cf9SAndrew Gallatin 
31751e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
31761e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
31771e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
31781e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
31791e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
31801e413cf9SAndrew Gallatin 			}
31811e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
31821e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
31831e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
31841e413cf9SAndrew Gallatin 		}
31851e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
31861e413cf9SAndrew Gallatin 	}
31871e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3188b2fc195eSAndrew Gallatin }
3189b2fc195eSAndrew Gallatin 
3190b2fc195eSAndrew Gallatin static void
31916d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3192b2fc195eSAndrew Gallatin {
31931e413cf9SAndrew Gallatin 	int slice;
3194b2fc195eSAndrew Gallatin 
31951e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31961e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3197c2657176SAndrew Gallatin }
3198b2fc195eSAndrew Gallatin 
3199b2fc195eSAndrew Gallatin static int
32001e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
32011e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3202b2fc195eSAndrew Gallatin {
32031e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
32041e413cf9SAndrew Gallatin 	size_t bytes;
32051e413cf9SAndrew Gallatin 	int err, i;
3206b2fc195eSAndrew Gallatin 
3207b2fc195eSAndrew Gallatin 	err = ENOMEM;
3208b2fc195eSAndrew Gallatin 
32091e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3210adae7080SAndrew Gallatin 
32111e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
32121e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3213aed8e389SAndrew Gallatin 
3214b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
32151e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
32161e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32171e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow == NULL)
3218c2ede4b3SMartin Blapp 		return err;
3219b2fc195eSAndrew Gallatin 
32201e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
32211e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32221e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow == NULL)
3223c2ede4b3SMartin Blapp 		return err;
3224b2fc195eSAndrew Gallatin 
32251e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
32261e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
32271e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32281e413cf9SAndrew Gallatin 	if (ss->rx_small.info == NULL)
3229c2ede4b3SMartin Blapp 		return err;
3230b2fc195eSAndrew Gallatin 
32311e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
32321e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32331e413cf9SAndrew Gallatin 	if (ss->rx_big.info == NULL)
3234c2ede4b3SMartin Blapp 		return err;
3235b2fc195eSAndrew Gallatin 
32361e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3237b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3238b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3239b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3240b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3241b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3242b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3243b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3244b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3245b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3246b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3247b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32481e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3249b2fc195eSAndrew Gallatin 	if (err != 0) {
3250b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3251b2fc195eSAndrew Gallatin 			      err);
3252c2ede4b3SMartin Blapp 		return err;
3253b2fc195eSAndrew Gallatin 	}
3254b2fc195eSAndrew Gallatin 
3255b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3256b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3257b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3258b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3259b0f7b922SAndrew Gallatin #else
3260b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3261b0f7b922SAndrew Gallatin #endif
3262b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3263b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3264b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3265053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3266b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3267053e637fSAndrew Gallatin 				 3,			/* num segs */
3268b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3269b0f7b922SAndrew Gallatin #else
3270b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3271b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3272b0f7b922SAndrew Gallatin #endif
3273b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3274b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32751e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3276b2fc195eSAndrew Gallatin 	if (err != 0) {
3277b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3278b2fc195eSAndrew Gallatin 			      err);
3279c2ede4b3SMartin Blapp 		return err;
3280b2fc195eSAndrew Gallatin 	}
32811e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
32821e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
32831e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3284b2fc195eSAndrew Gallatin 		if (err != 0) {
3285b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3286b2fc195eSAndrew Gallatin 				      err);
3287c2ede4b3SMartin Blapp 			return err;
3288b2fc195eSAndrew Gallatin 		}
3289b2fc195eSAndrew Gallatin 	}
32901e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
32911e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3292b2fc195eSAndrew Gallatin 	if (err != 0) {
3293b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3294b2fc195eSAndrew Gallatin 			      err);
3295c2ede4b3SMartin Blapp 		return err;
3296b2fc195eSAndrew Gallatin 	}
3297b2fc195eSAndrew Gallatin 
32981e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32991e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
33001e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3301b2fc195eSAndrew Gallatin 		if (err != 0) {
3302b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3303b2fc195eSAndrew Gallatin 				      err);
3304c2ede4b3SMartin Blapp 			return err;
3305b2fc195eSAndrew Gallatin 		}
3306b2fc195eSAndrew Gallatin 	}
33071e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
33081e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3309b2fc195eSAndrew Gallatin 	if (err != 0) {
3310b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3311b2fc195eSAndrew Gallatin 			      err);
3312c2ede4b3SMartin Blapp 		return err;
33131e413cf9SAndrew Gallatin 	}
33141e413cf9SAndrew Gallatin 
33151e413cf9SAndrew Gallatin 	/* now allocate TX resouces */
33161e413cf9SAndrew Gallatin 
3317c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
33181e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
33191e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
33201e413cf9SAndrew Gallatin 		return 0;
3321c6cb3e3fSAndrew Gallatin #endif
33221e413cf9SAndrew Gallatin 
33231e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
33241e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
33251e413cf9SAndrew Gallatin 
33261e413cf9SAndrew Gallatin 
33271e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
33281e413cf9SAndrew Gallatin 	bytes = 8 +
33291e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
33301e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
33311e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes == NULL)
3332c2ede4b3SMartin Blapp 		return err;
33331e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
33341e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
33351e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
33361e413cf9SAndrew Gallatin 
33371e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
33381e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
33391e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
33401e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
33411e413cf9SAndrew Gallatin 	if (ss->tx.seg_list == NULL)
3342c2ede4b3SMartin Blapp 		return err;
33431e413cf9SAndrew Gallatin 
33441e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
33451e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
33461e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
33471e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
3348c2ede4b3SMartin Blapp 		return err;
33491e413cf9SAndrew Gallatin 
33501e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
33511e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
33521e413cf9SAndrew Gallatin 				 1,			/* alignment */
33531e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
33541e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
33551e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
33561e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
33571e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
33581e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
33591e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
33601e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
33611e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
33621e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
33631e413cf9SAndrew Gallatin 
33641e413cf9SAndrew Gallatin 	if (err != 0) {
33651e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
33661e413cf9SAndrew Gallatin 			      err);
3367c2ede4b3SMartin Blapp 		return err;
33681e413cf9SAndrew Gallatin 	}
33691e413cf9SAndrew Gallatin 
33701e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
33711e413cf9SAndrew Gallatin 	   in the ring */
33721e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
33731e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
33741e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
33751e413cf9SAndrew Gallatin 		if (err != 0) {
33761e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
33771e413cf9SAndrew Gallatin 				      err);
3378c2ede4b3SMartin Blapp 			return err;
33791e413cf9SAndrew Gallatin 		}
3380b2fc195eSAndrew Gallatin 	}
3381b2fc195eSAndrew Gallatin 	return 0;
3382b2fc195eSAndrew Gallatin 
3383b2fc195eSAndrew Gallatin }
3384b2fc195eSAndrew Gallatin 
33851e413cf9SAndrew Gallatin static int
33861e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
33871e413cf9SAndrew Gallatin {
33881e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
33891e413cf9SAndrew Gallatin 	int tx_ring_size;
33901e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
33911e413cf9SAndrew Gallatin 	int err, slice;
33921e413cf9SAndrew Gallatin 
33931e413cf9SAndrew Gallatin 	/* get ring sizes */
33941e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33951e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
33961e413cf9SAndrew Gallatin 	if (err != 0) {
33971e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33981e413cf9SAndrew Gallatin 		goto abort;
33991e413cf9SAndrew Gallatin 	}
34001e413cf9SAndrew Gallatin 
34011e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
34021e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
34031e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
34041e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
34051e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
34061e413cf9SAndrew Gallatin 
34071e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
34081e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
34091e413cf9SAndrew Gallatin 					     rx_ring_entries,
34101e413cf9SAndrew Gallatin 					     tx_ring_entries);
34111e413cf9SAndrew Gallatin 		if (err != 0)
34121e413cf9SAndrew Gallatin 			goto abort;
34131e413cf9SAndrew Gallatin 	}
34141e413cf9SAndrew Gallatin 	return 0;
34151e413cf9SAndrew Gallatin 
34161e413cf9SAndrew Gallatin abort:
34171e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
34181e413cf9SAndrew Gallatin 	return err;
34191e413cf9SAndrew Gallatin 
34201e413cf9SAndrew Gallatin }
34211e413cf9SAndrew Gallatin 
34221e413cf9SAndrew Gallatin 
3423053e637fSAndrew Gallatin static void
3424053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3425053e637fSAndrew Gallatin {
3426c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3427053e637fSAndrew Gallatin 
3428053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3429053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3430053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3431053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3432053e637fSAndrew Gallatin 		*nbufs = 1;
3433053e637fSAndrew Gallatin 		return;
3434053e637fSAndrew Gallatin 	}
3435053e637fSAndrew Gallatin 
3436053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3437053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3438053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3439053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3440053e637fSAndrew Gallatin 		*nbufs = 1;
3441053e637fSAndrew Gallatin 		return;
3442053e637fSAndrew Gallatin 	}
3443b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3444053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3445053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3446053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3447053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3448053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3449053e637fSAndrew Gallatin 	if (*nbufs == 3)
3450053e637fSAndrew Gallatin 		*nbufs = 4;
3451b0f7b922SAndrew Gallatin #else
3452b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3453b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3454b0f7b922SAndrew Gallatin 	*nbufs = 1;
3455b0f7b922SAndrew Gallatin #endif
3456053e637fSAndrew Gallatin }
3457053e637fSAndrew Gallatin 
3458b2fc195eSAndrew Gallatin static int
34591e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3460b2fc195eSAndrew Gallatin {
34611e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
34626d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3463b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
3464053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
34651e413cf9SAndrew Gallatin 	int err, i, slice;
3466b2fc195eSAndrew Gallatin 
34671e413cf9SAndrew Gallatin 
34681e413cf9SAndrew Gallatin 	sc = ss->sc;
34691e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
34701e413cf9SAndrew Gallatin 
34711e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_free);
34721e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_active);
3473053e637fSAndrew Gallatin 
3474053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
3475053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
34761e413cf9SAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF,
34771e413cf9SAndrew Gallatin 			       M_NOWAIT | M_ZERO);
3478053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
3479053e637fSAndrew Gallatin 			sc->lro_cnt = i;
3480053e637fSAndrew Gallatin 			break;
3481053e637fSAndrew Gallatin 		}
34821e413cf9SAndrew Gallatin 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
3483053e637fSAndrew Gallatin 	}
34841e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
34851e413cf9SAndrew Gallatin 
34861e413cf9SAndrew Gallatin 	err = 0;
3487c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
34881e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
34891e413cf9SAndrew Gallatin 	if (slice == 0) {
3490c6cb3e3fSAndrew Gallatin #endif
34911e413cf9SAndrew Gallatin 		cmd.data0 = slice;
34921e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
34931e413cf9SAndrew Gallatin 		ss->tx.lanai =
34941e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3495c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3496c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3497c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3498c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3499c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
35001e413cf9SAndrew Gallatin 	}
3501c6cb3e3fSAndrew Gallatin #endif
35021e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35031e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
35041e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
35051e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
35061e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35071e413cf9SAndrew Gallatin 	cmd.data0 = slice;
35081e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
35091e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
35101e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
35111e413cf9SAndrew Gallatin 
35121e413cf9SAndrew Gallatin 	if (err != 0) {
35131e413cf9SAndrew Gallatin 		device_printf(sc->dev,
35141e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
35151e413cf9SAndrew Gallatin 		return EIO;
35161e413cf9SAndrew Gallatin 	}
35171e413cf9SAndrew Gallatin 
35181e413cf9SAndrew Gallatin 	/* stock receive rings */
35191e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
35201e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
35211e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
35221e413cf9SAndrew Gallatin 		if (err) {
35231e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
35241e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
35251e413cf9SAndrew Gallatin 			return ENOMEM;
35261e413cf9SAndrew Gallatin 		}
35271e413cf9SAndrew Gallatin 	}
35281e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
35291e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
35301e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
35311e413cf9SAndrew Gallatin 	}
35321e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
35331e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
35344d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
35354d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
35361e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
35371e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
35381e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
35391e413cf9SAndrew Gallatin 		if (err) {
35401e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
35411e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
35421e413cf9SAndrew Gallatin 			return ENOMEM;
35431e413cf9SAndrew Gallatin 		}
35441e413cf9SAndrew Gallatin 	}
35451e413cf9SAndrew Gallatin 	return 0;
35461e413cf9SAndrew Gallatin }
35471e413cf9SAndrew Gallatin 
35481e413cf9SAndrew Gallatin static int
35491e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
35501e413cf9SAndrew Gallatin {
35511e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
35521e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
35531e413cf9SAndrew Gallatin 	bus_addr_t bus;
35541e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3555c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3556b2fc195eSAndrew Gallatin 
35577d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
35587d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
35597d542e2dSAndrew Gallatin 
3560adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3561b2fc195eSAndrew Gallatin 	if (err != 0) {
3562b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3563b2fc195eSAndrew Gallatin 		return EIO;
3564b2fc195eSAndrew Gallatin 	}
3565b2fc195eSAndrew Gallatin 
35661e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
35671e413cf9SAndrew Gallatin 		/* setup the indirection table */
35681e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
35691e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
35701e413cf9SAndrew Gallatin 				    &cmd);
3571b2fc195eSAndrew Gallatin 
35721e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
35731e413cf9SAndrew Gallatin 				     &cmd);
35741e413cf9SAndrew Gallatin 		if (err != 0) {
35751e413cf9SAndrew Gallatin 			device_printf(sc->dev,
35761e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
35771e413cf9SAndrew Gallatin 			return err;
35781e413cf9SAndrew Gallatin 		}
35791e413cf9SAndrew Gallatin 
35801e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
35811e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
35821e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
35831e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
35841e413cf9SAndrew Gallatin 
35851e413cf9SAndrew Gallatin 		cmd.data0 = 1;
35861e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
35871e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
35881e413cf9SAndrew Gallatin 		if (err != 0) {
35891e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
35901e413cf9SAndrew Gallatin 			return err;
35911e413cf9SAndrew Gallatin 		}
35921e413cf9SAndrew Gallatin 	}
35931e413cf9SAndrew Gallatin 
35941e413cf9SAndrew Gallatin 
35951e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
35961e413cf9SAndrew Gallatin 
35971e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3598053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3599053e637fSAndrew Gallatin 			    &cmd);
3600053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3601053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
36021e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3603053e637fSAndrew Gallatin 		device_printf(sc->dev,
3604053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
36051e413cf9SAndrew Gallatin 			      nbufs);
3606053e637fSAndrew Gallatin 		return EIO;
3607053e637fSAndrew Gallatin 	}
3608b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3609b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3610b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3611c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
36125e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3613b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
36145e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3615b2fc195eSAndrew Gallatin 			     &cmd);
3616053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
36175e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
36180fa7f681SAndrew Gallatin 
36190fa7f681SAndrew Gallatin 	if (err != 0) {
36200fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
36210fa7f681SAndrew Gallatin 		goto abort;
36220fa7f681SAndrew Gallatin 	}
36230fa7f681SAndrew Gallatin 
3624b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3625c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3626c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3627c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3628c6cb3e3fSAndrew Gallatin #else
3629c6cb3e3fSAndrew Gallatin 	     slice < 1;
3630c6cb3e3fSAndrew Gallatin #endif
3631c6cb3e3fSAndrew Gallatin 	     slice++) {
3632c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3633c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3634c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3635c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3636c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
36370fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3638c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3639c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3640c6cb3e3fSAndrew Gallatin 	}
36410fa7f681SAndrew Gallatin 
36420fa7f681SAndrew Gallatin 	if (err != 0) {
36431e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
36440fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
36450fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
36460fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
36470fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
36480fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
36490fa7f681SAndrew Gallatin 				    &cmd);
36500fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
36510fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
36520fa7f681SAndrew Gallatin 	} else {
36530fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
36540fa7f681SAndrew Gallatin 	}
3655b2fc195eSAndrew Gallatin 
3656b2fc195eSAndrew Gallatin 	if (err != 0) {
3657b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3658b2fc195eSAndrew Gallatin 		goto abort;
3659b2fc195eSAndrew Gallatin 	}
3660b2fc195eSAndrew Gallatin 
36611e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
36621e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
36631e413cf9SAndrew Gallatin 		if (err != 0) {
36641e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
36651e413cf9SAndrew Gallatin 				      slice);
36661e413cf9SAndrew Gallatin 			goto abort;
36671e413cf9SAndrew Gallatin 		}
36681e413cf9SAndrew Gallatin 	}
36691e413cf9SAndrew Gallatin 
3670b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
36715e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3672b2fc195eSAndrew Gallatin 	if (err) {
3673b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3674b2fc195eSAndrew Gallatin 		goto abort;
3675b2fc195eSAndrew Gallatin 	}
3676c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3677c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3678c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3679c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3680c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3681c6cb3e3fSAndrew Gallatin 	}
3682c6cb3e3fSAndrew Gallatin #endif
3683b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3684b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3685b2fc195eSAndrew Gallatin 
3686b2fc195eSAndrew Gallatin 	return 0;
3687b2fc195eSAndrew Gallatin 
3688b2fc195eSAndrew Gallatin 
3689b2fc195eSAndrew Gallatin abort:
36906d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3691a98d6cd7SAndrew Gallatin 
3692b2fc195eSAndrew Gallatin 	return err;
3693b2fc195eSAndrew Gallatin }
3694b2fc195eSAndrew Gallatin 
3695b2fc195eSAndrew Gallatin static int
3696a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3697b2fc195eSAndrew Gallatin {
36986d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3699b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3700c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3701c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3702c6cb3e3fSAndrew Gallatin 	int slice;
3703c6cb3e3fSAndrew Gallatin #endif
3704b2fc195eSAndrew Gallatin 
3705c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3706c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3707c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3708c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3709c6cb3e3fSAndrew Gallatin 	}
3710c6cb3e3fSAndrew Gallatin #endif
3711b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3712a393336bSAndrew Gallatin 	if (!down) {
3713b2fc195eSAndrew Gallatin 		old_down_cnt = sc->down_cnt;
371473c7c83fSAndrew Gallatin 		wmb();
37155e7d8541SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3716b2fc195eSAndrew Gallatin 		if (err) {
3717a393336bSAndrew Gallatin 			device_printf(sc->dev,
3718a393336bSAndrew Gallatin 				      "Couldn't bring down link\n");
3719b2fc195eSAndrew Gallatin 		}
3720b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3721b2fc195eSAndrew Gallatin 			/* wait for down irq */
3722dce01b9bSAndrew Gallatin 			DELAY(10 * sc->intr_coal_delay);
3723b2fc195eSAndrew Gallatin 		}
372473c7c83fSAndrew Gallatin 		wmb();
3725b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3726b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "never got down irq\n");
3727b2fc195eSAndrew Gallatin 		}
3728a393336bSAndrew Gallatin 	}
37296d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3730a98d6cd7SAndrew Gallatin 
3731b2fc195eSAndrew Gallatin 	return 0;
3732b2fc195eSAndrew Gallatin }
3733b2fc195eSAndrew Gallatin 
3734dce01b9bSAndrew Gallatin static void
3735dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3736dce01b9bSAndrew Gallatin {
3737dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3738dce01b9bSAndrew Gallatin 	int reg;
3739dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
3740dce01b9bSAndrew Gallatin 
3741dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
37423b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
3743dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3744dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3745dce01b9bSAndrew Gallatin 
374683d54b59SAndrew Gallatin 		if (sc->pectl == 0) {
3747dce01b9bSAndrew Gallatin 			pectl = pci_read_config(dev, reg + 0x8, 2);
3748dce01b9bSAndrew Gallatin 			pectl = (pectl & ~0x7000) | (5 << 12);
3749dce01b9bSAndrew Gallatin 			pci_write_config(dev, reg + 0x8, pectl, 2);
375083d54b59SAndrew Gallatin 			sc->pectl = pectl;
375183d54b59SAndrew Gallatin 		} else {
375283d54b59SAndrew Gallatin 			/* restore saved pectl after watchdog reset */
375383d54b59SAndrew Gallatin 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
375483d54b59SAndrew Gallatin 		}
3755dce01b9bSAndrew Gallatin 	}
3756dce01b9bSAndrew Gallatin 
3757dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3758dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3759dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
3760dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
3761dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
3762dce01b9bSAndrew Gallatin }
3763dce01b9bSAndrew Gallatin 
3764dce01b9bSAndrew Gallatin static uint32_t
3765dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3766dce01b9bSAndrew Gallatin {
3767dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3768dce01b9bSAndrew Gallatin 	uint32_t vs;
3769dce01b9bSAndrew Gallatin 
3770dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
37713b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) {
3772dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3773dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3774dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3775dce01b9bSAndrew Gallatin 	}
3776dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3777dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3778dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3779dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3780dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3781dce01b9bSAndrew Gallatin }
3782dce01b9bSAndrew Gallatin 
378372c042dfSAndrew Gallatin static void
378472c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3785dce01b9bSAndrew Gallatin {
3786e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3787a393336bSAndrew Gallatin 	struct mxge_slice_state *ss;
3788a393336bSAndrew Gallatin 	int err, running, s, num_tx_slices = 1;
3789dce01b9bSAndrew Gallatin 	uint32_t reboot;
3790dce01b9bSAndrew Gallatin 	uint16_t cmd;
3791dce01b9bSAndrew Gallatin 
3792dce01b9bSAndrew Gallatin 	err = ENXIO;
3793dce01b9bSAndrew Gallatin 
3794dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3795dce01b9bSAndrew Gallatin 
3796dce01b9bSAndrew Gallatin 	/*
3797dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3798dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3799dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3800dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3801dce01b9bSAndrew Gallatin 	 * again
3802dce01b9bSAndrew Gallatin 	 */
3803dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3804dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3805dce01b9bSAndrew Gallatin 		/*
3806dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3807dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3808dce01b9bSAndrew Gallatin 		 * back, then give up
3809dce01b9bSAndrew Gallatin 		 */
3810dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3811dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3812dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3813dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3814dce01b9bSAndrew Gallatin 		}
3815dce01b9bSAndrew Gallatin 	}
3816dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3817dce01b9bSAndrew Gallatin 		/* print the reboot status */
3818dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3819dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3820dce01b9bSAndrew Gallatin 			      reboot);
3821a393336bSAndrew Gallatin 		running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
3822a393336bSAndrew Gallatin 		if (running) {
3823a393336bSAndrew Gallatin 
3824a393336bSAndrew Gallatin 			/*
3825a393336bSAndrew Gallatin 			 * quiesce NIC so that TX routines will not try to
3826a393336bSAndrew Gallatin 			 * xmit after restoration of BAR
3827a393336bSAndrew Gallatin 			 */
3828a393336bSAndrew Gallatin 
3829a393336bSAndrew Gallatin 			/* Mark the link as down */
3830a393336bSAndrew Gallatin 			if (sc->link_state) {
3831a393336bSAndrew Gallatin 				sc->link_state = 0;
3832a393336bSAndrew Gallatin 				if_link_state_change(sc->ifp,
3833a393336bSAndrew Gallatin 						     LINK_STATE_DOWN);
3834a393336bSAndrew Gallatin 			}
3835a393336bSAndrew Gallatin #ifdef IFNET_BUF_RING
3836a393336bSAndrew Gallatin 			num_tx_slices = sc->num_slices;
3837a393336bSAndrew Gallatin #endif
3838a393336bSAndrew Gallatin 			/* grab all TX locks to ensure no tx  */
3839a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3840a393336bSAndrew Gallatin 				ss = &sc->ss[s];
3841a393336bSAndrew Gallatin 				mtx_lock(&ss->tx.mtx);
3842a393336bSAndrew Gallatin 			}
3843a393336bSAndrew Gallatin 			mxge_close(sc, 1);
3844a393336bSAndrew Gallatin 		}
3845dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3846e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3847e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3848dce01b9bSAndrew Gallatin 
3849dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3850dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
385110882804SAndrew Gallatin 
3852a393336bSAndrew Gallatin 		/* reload f/w */
3853a393336bSAndrew Gallatin 		err = mxge_load_firmware(sc, 0);
3854a393336bSAndrew Gallatin 		if (err) {
3855a393336bSAndrew Gallatin 			device_printf(sc->dev,
3856a393336bSAndrew Gallatin 				      "Unable to re-load f/w\n");
385710882804SAndrew Gallatin 		}
3858a393336bSAndrew Gallatin 		if (running) {
3859a393336bSAndrew Gallatin 			if (!err)
3860a393336bSAndrew Gallatin 				err = mxge_open(sc);
3861a393336bSAndrew Gallatin 			/* release all TX locks */
3862a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3863a393336bSAndrew Gallatin 				ss = &sc->ss[s];
386483d54b59SAndrew Gallatin #ifdef IFNET_BUF_RING
386583d54b59SAndrew Gallatin 				mxge_start_locked(ss);
386683d54b59SAndrew Gallatin #endif
3867a393336bSAndrew Gallatin 				mtx_unlock(&ss->tx.mtx);
3868a393336bSAndrew Gallatin 			}
3869a393336bSAndrew Gallatin 		}
3870a393336bSAndrew Gallatin 		sc->watchdog_resets++;
3871dce01b9bSAndrew Gallatin 	} else {
3872c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
387372c042dfSAndrew Gallatin 			      "NIC did not reboot, not resetting\n");
387472c042dfSAndrew Gallatin 		err = 0;
387572c042dfSAndrew Gallatin 	}
387672c042dfSAndrew Gallatin 	if (err) {
387772c042dfSAndrew Gallatin 		device_printf(sc->dev, "watchdog reset failed\n");
387872c042dfSAndrew Gallatin 	} else {
38796b484a49SAndrew Gallatin 		if (sc->dying == 2)
38806b484a49SAndrew Gallatin 			sc->dying = 0;
38816b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
388272c042dfSAndrew Gallatin 	}
388372c042dfSAndrew Gallatin }
388472c042dfSAndrew Gallatin 
388572c042dfSAndrew Gallatin static void
388672c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
388772c042dfSAndrew Gallatin {
388872c042dfSAndrew Gallatin 	mxge_softc_t *sc = arg;
388972c042dfSAndrew Gallatin 
389072c042dfSAndrew Gallatin 
389172c042dfSAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
389272c042dfSAndrew Gallatin 	mxge_watchdog_reset(sc);
389372c042dfSAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
389472c042dfSAndrew Gallatin }
389572c042dfSAndrew Gallatin 
389672c042dfSAndrew Gallatin static void
389772c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
389872c042dfSAndrew Gallatin {
389972c042dfSAndrew Gallatin 	tx = &sc->ss[slice].tx;
390072c042dfSAndrew Gallatin 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3901c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev,
3902c6cb3e3fSAndrew Gallatin 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3903c6cb3e3fSAndrew Gallatin 		      tx->req, tx->done, tx->queue_active);
3904c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3905c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3906dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3907c6cb3e3fSAndrew Gallatin 		      tx->pkt_done,
39081e413cf9SAndrew Gallatin 		      be32toh(sc->ss->fw_stats->send_done_count));
3909dce01b9bSAndrew Gallatin }
3910dce01b9bSAndrew Gallatin 
3911e749ef6bSAndrew Gallatin static int
3912dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3913dce01b9bSAndrew Gallatin {
3914c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
39151e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3916c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3917dce01b9bSAndrew Gallatin 
3918dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3919dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3920c6cb3e3fSAndrew Gallatin 	for (i = 0;
3921c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3922c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
3923c6cb3e3fSAndrew Gallatin #else
3924c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
3925c6cb3e3fSAndrew Gallatin #endif
3926c6cb3e3fSAndrew Gallatin 	     i++) {
3927c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3928dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3929dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3930c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3931c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
393272c042dfSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause) {
393372c042dfSAndrew Gallatin 				mxge_warn_stuck(sc, tx, i);
393472c042dfSAndrew Gallatin 				taskqueue_enqueue(sc->tq, &sc->watchdog_task);
393572c042dfSAndrew Gallatin 				return (ENXIO);
393672c042dfSAndrew Gallatin 			}
3937c587e59fSAndrew Gallatin 			else
3938c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3939c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3940c587e59fSAndrew Gallatin 		}
3941dce01b9bSAndrew Gallatin 
3942dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3943dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3944c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3945c6cb3e3fSAndrew Gallatin 	}
3946c587e59fSAndrew Gallatin 
3947c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3948c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3949e749ef6bSAndrew Gallatin 	return (err);
3950dce01b9bSAndrew Gallatin }
3951dce01b9bSAndrew Gallatin 
39526b484a49SAndrew Gallatin static u_long
39531e413cf9SAndrew Gallatin mxge_update_stats(mxge_softc_t *sc)
39541e413cf9SAndrew Gallatin {
39551e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
39566b484a49SAndrew Gallatin 	u_long pkts = 0;
39571e413cf9SAndrew Gallatin 	u_long ipackets = 0;
3958c6cb3e3fSAndrew Gallatin 	u_long opackets = 0;
395971032832SAndrew Gallatin #ifdef IFNET_BUF_RING
396071032832SAndrew Gallatin 	u_long obytes = 0;
396171032832SAndrew Gallatin 	u_long omcasts = 0;
396271032832SAndrew Gallatin 	u_long odrops = 0;
396371032832SAndrew Gallatin #endif
3964c6cb3e3fSAndrew Gallatin 	u_long oerrors = 0;
39651e413cf9SAndrew Gallatin 	int slice;
39661e413cf9SAndrew Gallatin 
39671e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
39681e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
39691e413cf9SAndrew Gallatin 		ipackets += ss->ipackets;
3970c6cb3e3fSAndrew Gallatin 		opackets += ss->opackets;
397171032832SAndrew Gallatin #ifdef IFNET_BUF_RING
397271032832SAndrew Gallatin 		obytes += ss->obytes;
397371032832SAndrew Gallatin 		omcasts += ss->omcasts;
397471032832SAndrew Gallatin 		odrops += ss->tx.br->br_drops;
397571032832SAndrew Gallatin #endif
3976c6cb3e3fSAndrew Gallatin 		oerrors += ss->oerrors;
39771e413cf9SAndrew Gallatin 	}
39786b484a49SAndrew Gallatin 	pkts = (ipackets - sc->ifp->if_ipackets);
39796b484a49SAndrew Gallatin 	pkts += (opackets - sc->ifp->if_opackets);
39801e413cf9SAndrew Gallatin 	sc->ifp->if_ipackets = ipackets;
3981c6cb3e3fSAndrew Gallatin 	sc->ifp->if_opackets = opackets;
398271032832SAndrew Gallatin #ifdef IFNET_BUF_RING
398371032832SAndrew Gallatin 	sc->ifp->if_obytes = obytes;
398471032832SAndrew Gallatin 	sc->ifp->if_omcasts = omcasts;
398571032832SAndrew Gallatin 	sc->ifp->if_snd.ifq_drops = odrops;
398671032832SAndrew Gallatin #endif
3987c6cb3e3fSAndrew Gallatin 	sc->ifp->if_oerrors = oerrors;
39886b484a49SAndrew Gallatin 	return pkts;
39891e413cf9SAndrew Gallatin }
3990c6cb3e3fSAndrew Gallatin 
39911e413cf9SAndrew Gallatin static void
3992dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3993dce01b9bSAndrew Gallatin {
3994dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
39956b484a49SAndrew Gallatin 	u_long pkts = 0;
3996e749ef6bSAndrew Gallatin 	int err = 0;
39976b484a49SAndrew Gallatin 	int running, ticks;
39986b484a49SAndrew Gallatin 	uint16_t cmd;
3999dce01b9bSAndrew Gallatin 
40006b484a49SAndrew Gallatin 	ticks = mxge_ticks;
40016b484a49SAndrew Gallatin 	running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING;
40026b484a49SAndrew Gallatin 	if (running) {
40031e413cf9SAndrew Gallatin 		/* aggregate stats from different slices */
40046b484a49SAndrew Gallatin 		pkts = mxge_update_stats(sc);
40051e413cf9SAndrew Gallatin 		if (!sc->watchdog_countdown) {
4006e749ef6bSAndrew Gallatin 			err = mxge_watchdog(sc);
40071e413cf9SAndrew Gallatin 			sc->watchdog_countdown = 4;
40081e413cf9SAndrew Gallatin 		}
40091e413cf9SAndrew Gallatin 		sc->watchdog_countdown--;
40106b484a49SAndrew Gallatin 	}
40116b484a49SAndrew Gallatin 	if (pkts == 0) {
40126b484a49SAndrew Gallatin 		/* ensure NIC did not suffer h/w fault while idle */
40136b484a49SAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
40146b484a49SAndrew Gallatin 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
40156b484a49SAndrew Gallatin 			sc->dying = 2;
40166b484a49SAndrew Gallatin 			taskqueue_enqueue(sc->tq, &sc->watchdog_task);
40176b484a49SAndrew Gallatin 			err = ENXIO;
40186b484a49SAndrew Gallatin 		}
40196b484a49SAndrew Gallatin 		/* look less often if NIC is idle */
40206b484a49SAndrew Gallatin 		ticks *= 4;
40216b484a49SAndrew Gallatin 	}
40226b484a49SAndrew Gallatin 
4023e749ef6bSAndrew Gallatin 	if (err == 0)
40246b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
4025e749ef6bSAndrew Gallatin 
4026dce01b9bSAndrew Gallatin }
4027b2fc195eSAndrew Gallatin 
4028b2fc195eSAndrew Gallatin static int
40296d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
4030b2fc195eSAndrew Gallatin {
4031b2fc195eSAndrew Gallatin 	return EINVAL;
4032b2fc195eSAndrew Gallatin }
4033b2fc195eSAndrew Gallatin 
4034b2fc195eSAndrew Gallatin static int
40356d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
4036b2fc195eSAndrew Gallatin {
4037b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
4038b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
4039b2fc195eSAndrew Gallatin 	int err = 0;
4040b2fc195eSAndrew Gallatin 
4041b2fc195eSAndrew Gallatin 
4042c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
4043053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
4044b2fc195eSAndrew Gallatin 		return EINVAL;
4045a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4046b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
4047b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
4048b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4049a393336bSAndrew Gallatin 		mxge_close(sc, 0);
40506d87a65dSAndrew Gallatin 		err = mxge_open(sc);
4051b2fc195eSAndrew Gallatin 		if (err != 0) {
4052b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
4053a393336bSAndrew Gallatin 			mxge_close(sc, 0);
40546d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
4055b2fc195eSAndrew Gallatin 		}
4056b2fc195eSAndrew Gallatin 	}
4057a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4058b2fc195eSAndrew Gallatin 	return err;
4059b2fc195eSAndrew Gallatin }
4060b2fc195eSAndrew Gallatin 
4061b2fc195eSAndrew Gallatin static void
40626d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
4063b2fc195eSAndrew Gallatin {
40646d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4065b2fc195eSAndrew Gallatin 
4066b2fc195eSAndrew Gallatin 
4067b2fc195eSAndrew Gallatin 	if (sc == NULL)
4068b2fc195eSAndrew Gallatin 		return;
4069b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
4070c406ad2eSAndrew Gallatin 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
4071c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
4072c406ad2eSAndrew Gallatin 	ifmr->ifm_active |= sc->current_media;
4073b2fc195eSAndrew Gallatin }
4074b2fc195eSAndrew Gallatin 
4075b2fc195eSAndrew Gallatin static int
40766d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
4077b2fc195eSAndrew Gallatin {
40786d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
4079b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
4080b2fc195eSAndrew Gallatin 	int err, mask;
4081b2fc195eSAndrew Gallatin 
4082b2fc195eSAndrew Gallatin 	err = 0;
4083b2fc195eSAndrew Gallatin 	switch (command) {
4084b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
4085b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
4086b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
4087b2fc195eSAndrew Gallatin 		break;
4088b2fc195eSAndrew Gallatin 
4089b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
40906d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
4091b2fc195eSAndrew Gallatin 		break;
4092b2fc195eSAndrew Gallatin 
4093b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
4094a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
40958c5d766cSAndrew Gallatin 		if (sc->dying) {
40968c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
40978c5d766cSAndrew Gallatin 			return EINVAL;
40988c5d766cSAndrew Gallatin 		}
4099b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
4100dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
41016d87a65dSAndrew Gallatin 				err = mxge_open(sc);
4102dce01b9bSAndrew Gallatin 			} else {
41030fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
41040fa7f681SAndrew Gallatin 				   flag chages */
41050fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
41060fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
41070fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
41080fa7f681SAndrew Gallatin 			}
4109b2fc195eSAndrew Gallatin 		} else {
4110dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
4111a393336bSAndrew Gallatin 				mxge_close(sc, 0);
4112dce01b9bSAndrew Gallatin 			}
4113b2fc195eSAndrew Gallatin 		}
4114a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4115b2fc195eSAndrew Gallatin 		break;
4116b2fc195eSAndrew Gallatin 
4117b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
4118b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
4119a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
41200fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
4121a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4122b2fc195eSAndrew Gallatin 		break;
4123b2fc195eSAndrew Gallatin 
4124b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
4125a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4126b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
4127b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
4128b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
4129aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
4130aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
4131aed8e389SAndrew Gallatin 						      | CSUM_TSO);
4132b2fc195eSAndrew Gallatin 			} else {
4133b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
4134b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
4135b2fc195eSAndrew Gallatin 			}
4136b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
4137b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
4138b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
41395e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
4140b2fc195eSAndrew Gallatin 			} else {
4141b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
41425e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
4143b2fc195eSAndrew Gallatin 			}
4144b2fc195eSAndrew Gallatin 		}
4145aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
4146aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
4147aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
4148aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
4149aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
4150aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
4151aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
4152aed8e389SAndrew Gallatin 			} else {
4153aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4154aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4155aed8e389SAndrew Gallatin 				err = EINVAL;
4156aed8e389SAndrew Gallatin 			}
4157aed8e389SAndrew Gallatin 		}
4158f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
4159f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
4160f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
4161f04b33f8SAndrew Gallatin 			else
4162f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
4163f04b33f8SAndrew Gallatin 		}
4164c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
4165c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
41660dce6781SAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTSO)
41670dce6781SAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
41680dce6781SAndrew Gallatin 
41690dce6781SAndrew Gallatin 		if (!(ifp->if_capabilities & IFCAP_VLAN_HWTSO) ||
41700dce6781SAndrew Gallatin 		    !(ifp->if_capenable & IFCAP_VLAN_HWTAGGING))
41710dce6781SAndrew Gallatin 			ifp->if_capenable &= ~IFCAP_VLAN_HWTSO;
41720dce6781SAndrew Gallatin 
4173a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4174c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4175c792928fSAndrew Gallatin 
4176b2fc195eSAndrew Gallatin 		break;
4177b2fc195eSAndrew Gallatin 
4178b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4179c406ad2eSAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4180c406ad2eSAndrew Gallatin 		mxge_media_probe(sc);
4181c406ad2eSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4182b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4183b2fc195eSAndrew Gallatin 				    &sc->media, command);
4184b2fc195eSAndrew Gallatin                 break;
4185b2fc195eSAndrew Gallatin 
4186b2fc195eSAndrew Gallatin 	default:
4187b2fc195eSAndrew Gallatin 		err = ENOTTY;
4188b2fc195eSAndrew Gallatin         }
4189b2fc195eSAndrew Gallatin 	return err;
4190b2fc195eSAndrew Gallatin }
4191b2fc195eSAndrew Gallatin 
4192b2fc195eSAndrew Gallatin static void
41936d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4194b2fc195eSAndrew Gallatin {
4195b2fc195eSAndrew Gallatin 
41961e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
41976d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
41986d87a65dSAndrew Gallatin 			  &mxge_flow_control);
41996d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
42006d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
42016d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
42026d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4203d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4204d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
42055e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
42065e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
42075e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
42085e7d8541SAndrew Gallatin 			  &mxge_verbose);
4209dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
4210053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
42111e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
42121e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
421394c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4214f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
421565c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4216f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
4217f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
4218b2fc195eSAndrew Gallatin 
42195e7d8541SAndrew Gallatin 	if (bootverbose)
42205e7d8541SAndrew Gallatin 		mxge_verbose = 1;
42216d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
42226d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4223dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
42241e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
42256d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
42261e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4227bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
42285769c5efSAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4229b2fc195eSAndrew Gallatin 	}
4230f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4231f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4232f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
423365c69066SAndrew Gallatin 
423465c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
423565c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
423665c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
423765c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
423865c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
42391e413cf9SAndrew Gallatin }
42401e413cf9SAndrew Gallatin 
42411e413cf9SAndrew Gallatin 
42421e413cf9SAndrew Gallatin static void
42431e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
42441e413cf9SAndrew Gallatin {
42451e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
42461e413cf9SAndrew Gallatin 	int i;
42471e413cf9SAndrew Gallatin 
42481e413cf9SAndrew Gallatin 
42491e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
42501e413cf9SAndrew Gallatin 		return;
42511e413cf9SAndrew Gallatin 
42521e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42531e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
42541e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
42551e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
42561e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4257c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4258c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4259c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4260c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4261c6cb3e3fSAndrew Gallatin 			}
4262c6cb3e3fSAndrew Gallatin #endif
42631e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
42641e413cf9SAndrew Gallatin 		}
42651e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
42661e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
42671e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
42681e413cf9SAndrew Gallatin 		}
42691e413cf9SAndrew Gallatin 	}
42701e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
42711e413cf9SAndrew Gallatin 	sc->ss = NULL;
42721e413cf9SAndrew Gallatin }
42731e413cf9SAndrew Gallatin 
42741e413cf9SAndrew Gallatin static int
42751e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
42761e413cf9SAndrew Gallatin {
42771e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
42781e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
42791e413cf9SAndrew Gallatin 	size_t bytes;
42801e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
42811e413cf9SAndrew Gallatin 
42821e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
42831e413cf9SAndrew Gallatin 	if (err != 0) {
42841e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
42851e413cf9SAndrew Gallatin 		return err;
42861e413cf9SAndrew Gallatin 	}
42871e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
42881e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
42891e413cf9SAndrew Gallatin 
42901e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
42911e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
42921e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
42931e413cf9SAndrew Gallatin 		return (ENOMEM);
42941e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42951e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
42961e413cf9SAndrew Gallatin 
42971e413cf9SAndrew Gallatin 		ss->sc = sc;
42981e413cf9SAndrew Gallatin 
42991e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
43001e413cf9SAndrew Gallatin 
43011e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
43021e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
43031e413cf9SAndrew Gallatin 		if (err != 0)
43041e413cf9SAndrew Gallatin 			goto abort;
43051e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
43061e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
43071e413cf9SAndrew Gallatin 
43081e413cf9SAndrew Gallatin 		/*
43091e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
43101e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
43111e413cf9SAndrew Gallatin 		 * slice for now
43121e413cf9SAndrew Gallatin 		 */
4313c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
43141e413cf9SAndrew Gallatin 		if (i > 0)
43151e413cf9SAndrew Gallatin 			continue;
4316c6cb3e3fSAndrew Gallatin #endif
43171e413cf9SAndrew Gallatin 
43181e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
43191e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
43201e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
43211e413cf9SAndrew Gallatin 		if (err != 0)
43221e413cf9SAndrew Gallatin 			goto abort;
43231e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
43241e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
43251e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
43261e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4327c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4328c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4329c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4330c6cb3e3fSAndrew Gallatin #endif
43311e413cf9SAndrew Gallatin 	}
43321e413cf9SAndrew Gallatin 
43331e413cf9SAndrew Gallatin 	return (0);
43341e413cf9SAndrew Gallatin 
43351e413cf9SAndrew Gallatin abort:
43361e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
43371e413cf9SAndrew Gallatin 	return (ENOMEM);
43381e413cf9SAndrew Gallatin }
43391e413cf9SAndrew Gallatin 
43401e413cf9SAndrew Gallatin static void
43411e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
43421e413cf9SAndrew Gallatin {
43431e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
43441e413cf9SAndrew Gallatin 	char *old_fw;
43451e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
43461e413cf9SAndrew Gallatin 
43471e413cf9SAndrew Gallatin 	sc->num_slices = 1;
43481e413cf9SAndrew Gallatin 	/*
43491e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
43501e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
43511e413cf9SAndrew Gallatin 	 */
43521e413cf9SAndrew Gallatin 
43531e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
43541e413cf9SAndrew Gallatin 		return;
43551e413cf9SAndrew Gallatin 
43561e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
43571e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
43581e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
43591e413cf9SAndrew Gallatin 		return;
43601e413cf9SAndrew Gallatin 
43611e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
43621e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
43631e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
43641e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
43651e413cf9SAndrew Gallatin 	else
43661e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
43671e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
43681e413cf9SAndrew Gallatin 	if (status != 0) {
43691e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
43701e413cf9SAndrew Gallatin 		return;
43711e413cf9SAndrew Gallatin 	}
43721e413cf9SAndrew Gallatin 
43731e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
43741e413cf9SAndrew Gallatin 	   is alive */
43751e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
43761e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
43771e413cf9SAndrew Gallatin 	if (status != 0) {
43781e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
43791e413cf9SAndrew Gallatin 		goto abort_with_fw;
43801e413cf9SAndrew Gallatin 	}
43811e413cf9SAndrew Gallatin 
43821e413cf9SAndrew Gallatin 	/* get rx ring size */
43831e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
43841e413cf9SAndrew Gallatin 	if (status != 0) {
43851e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
43861e413cf9SAndrew Gallatin 		goto abort_with_fw;
43871e413cf9SAndrew Gallatin 	}
43881e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
43891e413cf9SAndrew Gallatin 
43901e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
43911e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
43921e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
43931e413cf9SAndrew Gallatin 	if (status != 0) {
43941e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
43951e413cf9SAndrew Gallatin 		goto abort_with_fw;
43961e413cf9SAndrew Gallatin 	}
43971e413cf9SAndrew Gallatin 
43981e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
43991e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
44001e413cf9SAndrew Gallatin 	if (status != 0) {
44011e413cf9SAndrew Gallatin 		device_printf(sc->dev,
44021e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
44031e413cf9SAndrew Gallatin 		goto abort_with_fw;
44041e413cf9SAndrew Gallatin 	}
44051e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
44061e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
44071e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
44081e413cf9SAndrew Gallatin 
44091e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
44101e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
44111e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
44121e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
44131e413cf9SAndrew Gallatin 	} else {
44141e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
44151e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
44161e413cf9SAndrew Gallatin 	}
44171e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
44181e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
44191e413cf9SAndrew Gallatin 		sc->num_slices--;
44201e413cf9SAndrew Gallatin 
44211e413cf9SAndrew Gallatin 	if (mxge_verbose)
44221e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
44231e413cf9SAndrew Gallatin 			      sc->num_slices);
44241e413cf9SAndrew Gallatin 
44251e413cf9SAndrew Gallatin 	return;
44261e413cf9SAndrew Gallatin 
44271e413cf9SAndrew Gallatin abort_with_fw:
44281e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
44291e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
44301e413cf9SAndrew Gallatin }
44311e413cf9SAndrew Gallatin 
44321e413cf9SAndrew Gallatin static int
44331e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
44341e413cf9SAndrew Gallatin {
44351e413cf9SAndrew Gallatin 	size_t bytes;
44361e413cf9SAndrew Gallatin 	int count, err, i, rid;
44371e413cf9SAndrew Gallatin 
44381e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
44391e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
44401e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
44411e413cf9SAndrew Gallatin 
44421e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
44431e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
44441e413cf9SAndrew Gallatin 		return ENXIO;
44451e413cf9SAndrew Gallatin 	}
44461e413cf9SAndrew Gallatin 
44471e413cf9SAndrew Gallatin 	count = sc->num_slices;
44481e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
44491e413cf9SAndrew Gallatin 	if (err != 0) {
44501e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
44511e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
44521e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
44531e413cf9SAndrew Gallatin 	}
44541e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
44551e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
44561e413cf9SAndrew Gallatin 			      count, sc->num_slices);
44571e413cf9SAndrew Gallatin 		device_printf(sc->dev,
44581e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
44591e413cf9SAndrew Gallatin 			      count);
44601e413cf9SAndrew Gallatin 		err = ENOSPC;
44611e413cf9SAndrew Gallatin 		goto abort_with_msix;
44621e413cf9SAndrew Gallatin 	}
44631e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
44641e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44651e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
44661e413cf9SAndrew Gallatin 		err = ENOMEM;
44671e413cf9SAndrew Gallatin 		goto abort_with_msix;
44681e413cf9SAndrew Gallatin 	}
44691e413cf9SAndrew Gallatin 
44701e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44711e413cf9SAndrew Gallatin 		rid = i + 1;
44721e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
44731e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
44741e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
44751e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
44761e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
44771e413cf9SAndrew Gallatin 				      " for message %d\n", i);
44781e413cf9SAndrew Gallatin 			err = ENXIO;
44791e413cf9SAndrew Gallatin 			goto abort_with_res;
44801e413cf9SAndrew Gallatin 		}
44811e413cf9SAndrew Gallatin 	}
44821e413cf9SAndrew Gallatin 
44831e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
44841e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44851e413cf9SAndrew Gallatin 
44861e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44871e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
44881e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
448937d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
449037d89b0cSAndrew Gallatin 				     NULL,
449137d89b0cSAndrew Gallatin #endif
449237d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
44931e413cf9SAndrew Gallatin 		if (err != 0) {
44941e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
44951e413cf9SAndrew Gallatin 				      "message %d\n", i);
44961e413cf9SAndrew Gallatin 			goto abort_with_intr;
44971e413cf9SAndrew Gallatin 		}
449821089137SAndrew Gallatin 		bus_describe_intr(sc->dev, sc->msix_irq_res[i],
449921089137SAndrew Gallatin 				  sc->msix_ih[i], "s%d", i);
45001e413cf9SAndrew Gallatin 	}
45011e413cf9SAndrew Gallatin 
45021e413cf9SAndrew Gallatin 	if (mxge_verbose) {
45031e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
45041e413cf9SAndrew Gallatin 			      sc->num_slices);
45051e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
45061e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
45071e413cf9SAndrew Gallatin 		printf("\n");
45081e413cf9SAndrew Gallatin 	}
45091e413cf9SAndrew Gallatin 	return (0);
45101e413cf9SAndrew Gallatin 
45111e413cf9SAndrew Gallatin abort_with_intr:
45121e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45131e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
45141e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
45151e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
45161e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
45171e413cf9SAndrew Gallatin 		}
45181e413cf9SAndrew Gallatin 	}
45191e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
45201e413cf9SAndrew Gallatin 
45211e413cf9SAndrew Gallatin 
45221e413cf9SAndrew Gallatin abort_with_res:
45231e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45241e413cf9SAndrew Gallatin 		rid = i + 1;
45251e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
45261e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
45271e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
45281e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
45291e413cf9SAndrew Gallatin 	}
45301e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
45311e413cf9SAndrew Gallatin 
45321e413cf9SAndrew Gallatin 
45331e413cf9SAndrew Gallatin abort_with_msix:
45341e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
45351e413cf9SAndrew Gallatin 
45361e413cf9SAndrew Gallatin abort_with_msix_table:
45371e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
45381e413cf9SAndrew Gallatin 			     sc->msix_table_res);
45391e413cf9SAndrew Gallatin 
45401e413cf9SAndrew Gallatin 	return err;
45411e413cf9SAndrew Gallatin }
45421e413cf9SAndrew Gallatin 
45431e413cf9SAndrew Gallatin static int
45441e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
45451e413cf9SAndrew Gallatin {
45461e413cf9SAndrew Gallatin 	int count, err, rid;
45471e413cf9SAndrew Gallatin 
45481e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
45491e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
45501e413cf9SAndrew Gallatin 		rid = 1;
45511e413cf9SAndrew Gallatin 	} else {
45521e413cf9SAndrew Gallatin 		rid = 0;
455391ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
45541e413cf9SAndrew Gallatin 	}
45551e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
45561e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
45571e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
45581e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
45591e413cf9SAndrew Gallatin 		return ENXIO;
45601e413cf9SAndrew Gallatin 	}
45611e413cf9SAndrew Gallatin 	if (mxge_verbose)
45621e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
456391ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
45641e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
45651e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
45661e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
456737d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
456837d89b0cSAndrew Gallatin 			     NULL,
456937d89b0cSAndrew Gallatin #endif
457037d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
45711e413cf9SAndrew Gallatin 	if (err != 0) {
45721e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
457391ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
457491ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
45751e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
45761e413cf9SAndrew Gallatin 	}
45771e413cf9SAndrew Gallatin 	return err;
45781e413cf9SAndrew Gallatin }
45791e413cf9SAndrew Gallatin 
45801e413cf9SAndrew Gallatin static void
45811e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
45821e413cf9SAndrew Gallatin {
45831e413cf9SAndrew Gallatin 	int i, rid;
45841e413cf9SAndrew Gallatin 
45851e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45861e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
45871e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
45881e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
45891e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
45901e413cf9SAndrew Gallatin 		}
45911e413cf9SAndrew Gallatin 	}
45921e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
45931e413cf9SAndrew Gallatin 
45941e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45951e413cf9SAndrew Gallatin 		rid = i + 1;
45961e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
45971e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
45981e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
45991e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
46001e413cf9SAndrew Gallatin 	}
46011e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
46021e413cf9SAndrew Gallatin 
46031e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
46041e413cf9SAndrew Gallatin 			     sc->msix_table_res);
46051e413cf9SAndrew Gallatin 
46061e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
46071e413cf9SAndrew Gallatin 	return;
46081e413cf9SAndrew Gallatin }
46091e413cf9SAndrew Gallatin 
46101e413cf9SAndrew Gallatin static void
46111e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
46121e413cf9SAndrew Gallatin {
46131e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
46141e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
461591ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
461691ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
46171e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
46181e413cf9SAndrew Gallatin }
46191e413cf9SAndrew Gallatin 
46201e413cf9SAndrew Gallatin static void
46211e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
46221e413cf9SAndrew Gallatin {
46231e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
46241e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
46251e413cf9SAndrew Gallatin 	else
46261e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
46271e413cf9SAndrew Gallatin }
46281e413cf9SAndrew Gallatin 
46291e413cf9SAndrew Gallatin static int
46301e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
46311e413cf9SAndrew Gallatin {
46321e413cf9SAndrew Gallatin 	int err;
46331e413cf9SAndrew Gallatin 
46341e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
46351e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
46361e413cf9SAndrew Gallatin 	else
46371e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
46381e413cf9SAndrew Gallatin 
46391e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
46401e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
46411e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
46421e413cf9SAndrew Gallatin 	}
46431e413cf9SAndrew Gallatin 	return err;
46441e413cf9SAndrew Gallatin }
46451e413cf9SAndrew Gallatin 
4646b2fc195eSAndrew Gallatin 
4647b2fc195eSAndrew Gallatin static int
46486d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4649b2fc195eSAndrew Gallatin {
46506d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4651b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
46521e413cf9SAndrew Gallatin 	int err, rid;
4653b2fc195eSAndrew Gallatin 
4654b2fc195eSAndrew Gallatin 	sc->dev = dev;
46556d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4656b2fc195eSAndrew Gallatin 
465772c042dfSAndrew Gallatin 	TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
4658a4a75d67SJohn Baldwin 	sc->tq = taskqueue_create("mxge_taskq", M_WAITOK,
4659a4a75d67SJohn Baldwin 				  taskqueue_thread_enqueue, &sc->tq);
466072c042dfSAndrew Gallatin 	if (sc->tq == NULL) {
466172c042dfSAndrew Gallatin 		err = ENOMEM;
466272c042dfSAndrew Gallatin 		goto abort_with_nothing;
466372c042dfSAndrew Gallatin 	}
466472c042dfSAndrew Gallatin 
4665*62ce43ccSScott Long 	err = bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
4666b2fc195eSAndrew Gallatin 				 1,			/* alignment */
46671e413cf9SAndrew Gallatin 				 0,			/* boundary */
4668b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4669b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4670b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4671aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
46725e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
46731e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4674b2fc195eSAndrew Gallatin 				 0,			/* flags */
4675b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4676b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4677b2fc195eSAndrew Gallatin 
4678b2fc195eSAndrew Gallatin 	if (err != 0) {
4679b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4680b2fc195eSAndrew Gallatin 			      err);
468172c042dfSAndrew Gallatin 		goto abort_with_tq;
4682b2fc195eSAndrew Gallatin 	}
4683b2fc195eSAndrew Gallatin 
4684b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4685b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4686b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4687b2fc195eSAndrew Gallatin 		err = ENOSPC;
4688b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4689b2fc195eSAndrew Gallatin 	}
46901e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
46911e413cf9SAndrew Gallatin 
4692a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4693a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4694a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4695a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4696a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4697a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4698b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4699b2fc195eSAndrew Gallatin 
4700dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4701d91b1b49SAndrew Gallatin 
4702dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4703b2fc195eSAndrew Gallatin 
4704b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4705b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4706b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4707b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4708b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4709b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4710b2fc195eSAndrew Gallatin 		err = ENXIO;
4711b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4712b2fc195eSAndrew Gallatin 	}
4713b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4714b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4715b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4716b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4717b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4718b2fc195eSAndrew Gallatin 		err = ENXIO;
4719b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4720b2fc195eSAndrew Gallatin 	}
4721b2fc195eSAndrew Gallatin 
4722b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4723b2fc195eSAndrew Gallatin 	   lanai SRAM */
47246d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4725b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4726b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
47276d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4728b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
47296d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
47306d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4731b2fc195eSAndrew Gallatin 	if (err != 0)
4732b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4733b2fc195eSAndrew Gallatin 
4734b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
47356d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4736b2fc195eSAndrew Gallatin 
4737b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
47386d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
47396d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4740b2fc195eSAndrew Gallatin 	if (err != 0)
4741b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4742b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
47436d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4744b2fc195eSAndrew Gallatin 	if (err != 0)
4745b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4746b2fc195eSAndrew Gallatin 
4747a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4748a98d6cd7SAndrew Gallatin 	if (err != 0)
47491e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4750b2fc195eSAndrew Gallatin 
47518fe615baSAndrew Gallatin 	/* select & load the firmware */
47528fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4753b2fc195eSAndrew Gallatin 	if (err != 0)
47541e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
47555e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
47561e413cf9SAndrew Gallatin 
47571e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
47581e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
47591e413cf9SAndrew Gallatin 	if (err != 0)
47601e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
47611e413cf9SAndrew Gallatin 
4762adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4763b2fc195eSAndrew Gallatin 	if (err != 0)
47641e413cf9SAndrew Gallatin 		goto abort_with_slices;
4765b2fc195eSAndrew Gallatin 
4766a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4767a98d6cd7SAndrew Gallatin 	if (err != 0) {
4768a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
47692e084798SAndrew Gallatin 		goto abort_with_slices;
4770a98d6cd7SAndrew Gallatin 	}
4771a98d6cd7SAndrew Gallatin 
47721e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4773a98d6cd7SAndrew Gallatin 	if (err != 0) {
47741e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4775a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4776a98d6cd7SAndrew Gallatin 	}
47771e413cf9SAndrew Gallatin 
4778e5062938SAndrew Gallatin 	ifp->if_baudrate = IF_Gbps(10UL);
4779c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
47802a29e09fSAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LINKSTATE;
4781eb6219e3SAndrew Gallatin #ifdef INET
4782eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4783eb6219e3SAndrew Gallatin #endif
478437d89b0cSAndrew Gallatin 
478537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
478637d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
47870dce6781SAndrew Gallatin 
47880dce6781SAndrew Gallatin 	/* Only FW 1.4.32 and newer can do TSO over vlans */
47890dce6781SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
47900dce6781SAndrew Gallatin 	    sc->fw_ver_tiny >= 32)
47910dce6781SAndrew Gallatin 		ifp->if_capabilities |= IFCAP_VLAN_HWTSO;
479237d89b0cSAndrew Gallatin #endif
4793c792928fSAndrew Gallatin 
4794053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4795053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4796053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4797053e637fSAndrew Gallatin 	else
4798053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4799adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4800053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4801aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
4802b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4803f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4804f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
48055e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
48066d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
4807b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
4808b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
48096d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
48106d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
4811c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4812c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4813c587e59fSAndrew Gallatin 		     mxge_media_status);
4814c406ad2eSAndrew Gallatin 	mxge_media_init(sc);
4815c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
48168c5d766cSAndrew Gallatin 	sc->dying = 0;
4817b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4818f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4819f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4820f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4821b2fc195eSAndrew Gallatin 
48226d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4823c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4824c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4825c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4826c6cb3e3fSAndrew Gallatin #endif
48272e084798SAndrew Gallatin 	taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
48282e084798SAndrew Gallatin 				device_get_nameunit(sc->dev));
48296b484a49SAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4830b2fc195eSAndrew Gallatin 	return 0;
4831b2fc195eSAndrew Gallatin 
4832a98d6cd7SAndrew Gallatin abort_with_rings:
4833a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
48341e413cf9SAndrew Gallatin abort_with_slices:
48351e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4836a98d6cd7SAndrew Gallatin abort_with_dmabench:
4837a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4838b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
48396d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4840b2fc195eSAndrew Gallatin abort_with_cmd_dma:
48416d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4842b2fc195eSAndrew Gallatin abort_with_mem_res:
4843b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4844b2fc195eSAndrew Gallatin abort_with_lock:
4845b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4846a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4847a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4848b2fc195eSAndrew Gallatin 	if_free(ifp);
4849b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4850b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
485172c042dfSAndrew Gallatin abort_with_tq:
485272c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
485372c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
485472c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
485572c042dfSAndrew Gallatin 		sc->tq = NULL;
485672c042dfSAndrew Gallatin 	}
4857b2fc195eSAndrew Gallatin abort_with_nothing:
4858b2fc195eSAndrew Gallatin 	return err;
4859b2fc195eSAndrew Gallatin }
4860b2fc195eSAndrew Gallatin 
4861b2fc195eSAndrew Gallatin static int
48626d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4863b2fc195eSAndrew Gallatin {
48646d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4865b2fc195eSAndrew Gallatin 
486637d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4867c792928fSAndrew Gallatin 		device_printf(sc->dev,
4868c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4869c792928fSAndrew Gallatin 		return EBUSY;
4870c792928fSAndrew Gallatin 	}
4871a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
48728c5d766cSAndrew Gallatin 	sc->dying = 1;
4873b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
4874a393336bSAndrew Gallatin 		mxge_close(sc, 0);
4875a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4876b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
487772c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
487872c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
487972c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
488072c042dfSAndrew Gallatin 		sc->tq = NULL;
488172c042dfSAndrew Gallatin 	}
4882e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4883dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4884091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
48851e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
48861e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4887a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
48881e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4889a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
48906d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
48916d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4892b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4893b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4894a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4895a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4896b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4897b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4898b2fc195eSAndrew Gallatin 	return 0;
4899b2fc195eSAndrew Gallatin }
4900b2fc195eSAndrew Gallatin 
4901b2fc195eSAndrew Gallatin static int
49026d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4903b2fc195eSAndrew Gallatin {
4904b2fc195eSAndrew Gallatin 	return 0;
4905b2fc195eSAndrew Gallatin }
4906b2fc195eSAndrew Gallatin 
4907b2fc195eSAndrew Gallatin /*
4908b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4909b2fc195eSAndrew Gallatin 
4910b2fc195eSAndrew Gallatin   Local Variables:
4911b2fc195eSAndrew Gallatin   c-file-style:"linux"
4912b2fc195eSAndrew Gallatin   tab-width:8
4913b2fc195eSAndrew Gallatin   End:
4914b2fc195eSAndrew Gallatin */
4915