xref: /freebsd/sys/dev/mxge/if_mxge.c (revision c792928f017773e36e1b6e77902d06df27826cd2)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
3b2fc195eSAndrew Gallatin Copyright (c) 2006, 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 
12b2fc195eSAndrew Gallatin  2. Redistributions in binary form must reproduce the above copyright
13b2fc195eSAndrew Gallatin     notice, this list of conditions and the following disclaimer in the
14b2fc195eSAndrew Gallatin     documentation and/or other materials provided with the distribution.
15b2fc195eSAndrew Gallatin 
16b2fc195eSAndrew Gallatin  3. Neither the name of the Myricom Inc, nor the names of its
17b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
18b2fc195eSAndrew Gallatin     this software without specific prior written permission.
19b2fc195eSAndrew Gallatin 
20b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
31b2fc195eSAndrew Gallatin 
32b2fc195eSAndrew Gallatin ***************************************************************************/
33b2fc195eSAndrew Gallatin 
34b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
35b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
36b2fc195eSAndrew Gallatin 
37b2fc195eSAndrew Gallatin #include <sys/param.h>
38b2fc195eSAndrew Gallatin #include <sys/systm.h>
39b2fc195eSAndrew Gallatin #include <sys/linker.h>
40b2fc195eSAndrew Gallatin #include <sys/firmware.h>
41b2fc195eSAndrew Gallatin #include <sys/endian.h>
42b2fc195eSAndrew Gallatin #include <sys/sockio.h>
43b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
44b2fc195eSAndrew Gallatin #include <sys/malloc.h>
45b2fc195eSAndrew Gallatin #include <sys/kdb.h>
46b2fc195eSAndrew Gallatin #include <sys/kernel.h>
474e7f640dSJohn Baldwin #include <sys/lock.h>
48b2fc195eSAndrew Gallatin #include <sys/module.h>
49b2fc195eSAndrew Gallatin #include <sys/memrange.h>
50b2fc195eSAndrew Gallatin #include <sys/socket.h>
51b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
52b2fc195eSAndrew Gallatin #include <sys/sx.h>
53b2fc195eSAndrew Gallatin 
54b2fc195eSAndrew Gallatin #include <net/if.h>
55b2fc195eSAndrew Gallatin #include <net/if_arp.h>
56b2fc195eSAndrew Gallatin #include <net/ethernet.h>
57b2fc195eSAndrew Gallatin #include <net/if_dl.h>
58b2fc195eSAndrew Gallatin #include <net/if_media.h>
59b2fc195eSAndrew Gallatin 
60b2fc195eSAndrew Gallatin #include <net/bpf.h>
61b2fc195eSAndrew Gallatin 
62b2fc195eSAndrew Gallatin #include <net/if_types.h>
63b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
64b2fc195eSAndrew Gallatin #include <net/zlib.h>
65b2fc195eSAndrew Gallatin 
66b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
67b2fc195eSAndrew Gallatin #include <netinet/in.h>
68b2fc195eSAndrew Gallatin #include <netinet/ip.h>
69aed8e389SAndrew Gallatin #include <netinet/tcp.h>
70b2fc195eSAndrew Gallatin 
71b2fc195eSAndrew Gallatin #include <machine/bus.h>
72053e637fSAndrew Gallatin #include <machine/in_cksum.h>
73b2fc195eSAndrew Gallatin #include <machine/resource.h>
74b2fc195eSAndrew Gallatin #include <sys/bus.h>
75b2fc195eSAndrew Gallatin #include <sys/rman.h>
76b2fc195eSAndrew Gallatin 
77b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
78b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
79b2fc195eSAndrew Gallatin 
80b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
81b2fc195eSAndrew Gallatin #include <vm/pmap.h>
82b2fc195eSAndrew Gallatin 
836d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
846d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
856d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
86b2fc195eSAndrew Gallatin 
87b2fc195eSAndrew Gallatin /* tunable params */
886d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
89d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
906d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
915e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
926d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
935e7d8541SAndrew Gallatin static int mxge_verbose = 0;
94dce01b9bSAndrew Gallatin static int mxge_ticks;
956d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
966d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
97b2fc195eSAndrew Gallatin 
986d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
996d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1006d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1016d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1026d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
103b2fc195eSAndrew Gallatin 
1046d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
105b2fc195eSAndrew Gallatin {
106b2fc195eSAndrew Gallatin   /* Device interface */
1076d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1086d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1096d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1106d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
111b2fc195eSAndrew Gallatin   {0, 0}
112b2fc195eSAndrew Gallatin };
113b2fc195eSAndrew Gallatin 
1146d87a65dSAndrew Gallatin static driver_t mxge_driver =
115b2fc195eSAndrew Gallatin {
1166d87a65dSAndrew Gallatin   "mxge",
1176d87a65dSAndrew Gallatin   mxge_methods,
1186d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
119b2fc195eSAndrew Gallatin };
120b2fc195eSAndrew Gallatin 
1216d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
122b2fc195eSAndrew Gallatin 
123b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1246d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1256d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
126b2fc195eSAndrew Gallatin 
1278fe615baSAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc);
1288fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
1298fe615baSAndrew Gallatin 
130b2fc195eSAndrew Gallatin static int
1316d87a65dSAndrew Gallatin mxge_probe(device_t dev)
132b2fc195eSAndrew Gallatin {
1336d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1346d87a65dSAndrew Gallatin       (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) {
135b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
136b2fc195eSAndrew Gallatin 	  return 0;
137b2fc195eSAndrew Gallatin   }
138b2fc195eSAndrew Gallatin   return ENXIO;
139b2fc195eSAndrew Gallatin }
140b2fc195eSAndrew Gallatin 
141b2fc195eSAndrew Gallatin static void
1426d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
143b2fc195eSAndrew Gallatin {
144b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
145b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
146b2fc195eSAndrew Gallatin 	vm_offset_t len;
147b2fc195eSAndrew Gallatin 	int err, action;
148b2fc195eSAndrew Gallatin 
149b2fc195eSAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
150b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
151b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
152b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
153b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
154b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1556d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
156b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
157b2fc195eSAndrew Gallatin 	if (err != 0) {
158b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
159b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
160b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
161b2fc195eSAndrew Gallatin 	} else {
162b2fc195eSAndrew Gallatin 		sc->wc = 1;
163b2fc195eSAndrew Gallatin 	}
164b2fc195eSAndrew Gallatin }
165b2fc195eSAndrew Gallatin 
166b2fc195eSAndrew Gallatin 
167b2fc195eSAndrew Gallatin /* callback to get our DMA address */
168b2fc195eSAndrew Gallatin static void
1696d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
170b2fc195eSAndrew Gallatin 			 int error)
171b2fc195eSAndrew Gallatin {
172b2fc195eSAndrew Gallatin 	if (error == 0) {
173b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
174b2fc195eSAndrew Gallatin 	}
175b2fc195eSAndrew Gallatin }
176b2fc195eSAndrew Gallatin 
177b2fc195eSAndrew Gallatin static int
1786d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
179b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
180b2fc195eSAndrew Gallatin {
181b2fc195eSAndrew Gallatin 	int err;
182b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
183b2fc195eSAndrew Gallatin 
184b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
185b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
186b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
187b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
188b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
189b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
190b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
191b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
192b2fc195eSAndrew Gallatin 				 1,			/* num segs */
193b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
194b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
195b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
196b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
197b2fc195eSAndrew Gallatin 	if (err != 0) {
198b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
199b2fc195eSAndrew Gallatin 		return err;
200b2fc195eSAndrew Gallatin 	}
201b2fc195eSAndrew Gallatin 
202b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
203b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
204b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
205b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
206b2fc195eSAndrew Gallatin 	if (err != 0) {
207b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
208b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
209b2fc195eSAndrew Gallatin 	}
210b2fc195eSAndrew Gallatin 
211b2fc195eSAndrew Gallatin 	/* load the memory */
212b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2136d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
214b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
215b2fc195eSAndrew Gallatin 	if (err != 0) {
216b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
217b2fc195eSAndrew Gallatin 		goto abort_with_mem;
218b2fc195eSAndrew Gallatin 	}
219b2fc195eSAndrew Gallatin 	return 0;
220b2fc195eSAndrew Gallatin 
221b2fc195eSAndrew Gallatin abort_with_mem:
222b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
223b2fc195eSAndrew Gallatin abort_with_dmat:
224b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
225b2fc195eSAndrew Gallatin 	return err;
226b2fc195eSAndrew Gallatin }
227b2fc195eSAndrew Gallatin 
228b2fc195eSAndrew Gallatin 
229b2fc195eSAndrew Gallatin static void
2306d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
231b2fc195eSAndrew Gallatin {
232b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
233b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
234b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
235b2fc195eSAndrew Gallatin }
236b2fc195eSAndrew Gallatin 
237b2fc195eSAndrew Gallatin /*
238b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
239b2fc195eSAndrew Gallatin  * SN=x\0
240b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
241b2fc195eSAndrew Gallatin  * PC=text\0
242b2fc195eSAndrew Gallatin  */
243b2fc195eSAndrew Gallatin 
244b2fc195eSAndrew Gallatin static int
2456d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
246b2fc195eSAndrew Gallatin {
2476d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
248b2fc195eSAndrew Gallatin 
249b2fc195eSAndrew Gallatin 	char *ptr, *limit;
250b2fc195eSAndrew Gallatin 	int i, found_mac;
251b2fc195eSAndrew Gallatin 
252b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2536d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
254b2fc195eSAndrew Gallatin 	found_mac = 0;
255b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
256b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2575e7d8541SAndrew Gallatin 			ptr += 1;
258b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
259b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2605e7d8541SAndrew Gallatin 				ptr += 3;
261b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
262b2fc195eSAndrew Gallatin 					goto abort;
263b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
264b2fc195eSAndrew Gallatin 				found_mac = 1;
265b2fc195eSAndrew Gallatin 			}
2665e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2675e7d8541SAndrew Gallatin 			ptr += 3;
2685e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2695e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2705e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2715e7d8541SAndrew Gallatin 			ptr += 3;
2725e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2735e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
274b2fc195eSAndrew Gallatin 		}
2756d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
276b2fc195eSAndrew Gallatin 	}
277b2fc195eSAndrew Gallatin 
278b2fc195eSAndrew Gallatin 	if (found_mac)
279b2fc195eSAndrew Gallatin 		return 0;
280b2fc195eSAndrew Gallatin 
281b2fc195eSAndrew Gallatin  abort:
282b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
283b2fc195eSAndrew Gallatin 
284b2fc195eSAndrew Gallatin 	return ENXIO;
285b2fc195eSAndrew Gallatin }
286b2fc195eSAndrew Gallatin 
287b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
2888fe615baSAndrew Gallatin static void
2898fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
290b2fc195eSAndrew Gallatin {
291b2fc195eSAndrew Gallatin 	uint32_t val;
2928fe615baSAndrew Gallatin 	unsigned long base, off;
293b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
2948fe615baSAndrew Gallatin 	device_t pdev, mcp55;
2958fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
296b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
297b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
298b2fc195eSAndrew Gallatin 
2998fe615baSAndrew Gallatin 
3008fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3018fe615baSAndrew Gallatin 		return;
3028fe615baSAndrew Gallatin 
3038fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3048fe615baSAndrew Gallatin 	if (pdev == NULL) {
3058fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3068fe615baSAndrew Gallatin 		return;
3078fe615baSAndrew Gallatin 	}
3088fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3098fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3108fe615baSAndrew Gallatin 
3118fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3128fe615baSAndrew Gallatin 		return;
3138fe615baSAndrew Gallatin 
3148fe615baSAndrew Gallatin 	base = 0;
3158fe615baSAndrew Gallatin 
3168fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3178fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3188fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3198fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3208fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3218fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3228fe615baSAndrew Gallatin 		if (mcp55 &&
3238fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3248fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3258fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3268fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3278fe615baSAndrew Gallatin 		}
3288fe615baSAndrew Gallatin 	}
3298fe615baSAndrew Gallatin 	if (!base)
3308fe615baSAndrew Gallatin 		return;
3318fe615baSAndrew Gallatin 
332b2fc195eSAndrew Gallatin 	/* XXXX
333b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
334b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
335b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
336b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
337b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
338b2fc195eSAndrew Gallatin 	*/
339b2fc195eSAndrew Gallatin #if 0
340b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
341b2fc195eSAndrew Gallatin 	   config space */
342b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
343b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
344b2fc195eSAndrew Gallatin 		val |= 0x40;
345b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3468fe615baSAndrew Gallatin 		return;
347b2fc195eSAndrew Gallatin 	}
348b2fc195eSAndrew Gallatin #endif
349b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
350b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
351b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
352b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
353b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
354b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
355b2fc195eSAndrew Gallatin 	 */
356b2fc195eSAndrew Gallatin 
357b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
358b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
359b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
360b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
361b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
362b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
363b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
364b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
365b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
366b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
367b2fc195eSAndrew Gallatin 
3688fe615baSAndrew Gallatin 	off =  base
369b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
370b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
371b2fc195eSAndrew Gallatin 						 + 8 * slot);
372b2fc195eSAndrew Gallatin 
373b2fc195eSAndrew Gallatin 	/* map it into the kernel */
374b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
375b2fc195eSAndrew Gallatin 
376b2fc195eSAndrew Gallatin 
377b2fc195eSAndrew Gallatin 	if (va == NULL) {
378b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3798fe615baSAndrew Gallatin 		return;
380b2fc195eSAndrew Gallatin 	}
381b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
382b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
383b2fc195eSAndrew Gallatin 
384b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
385b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
386b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
387b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
388b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
389b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
390b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3918fe615baSAndrew Gallatin 		return;
392b2fc195eSAndrew Gallatin 	}
393b2fc195eSAndrew Gallatin 
394b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
395b2fc195eSAndrew Gallatin 	val = *ptr32;
396b2fc195eSAndrew Gallatin 
397b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
398b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
399b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4008fe615baSAndrew Gallatin 		return;
401b2fc195eSAndrew Gallatin 	}
402b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
403b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4045e7d8541SAndrew Gallatin 	if (mxge_verbose)
405b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4065e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4075e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
408b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4098fe615baSAndrew Gallatin 	return;
410b2fc195eSAndrew Gallatin }
411b2fc195eSAndrew Gallatin #else
4128fe615baSAndrew Gallatin static void
4136d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
414b2fc195eSAndrew Gallatin {
415b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
416b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4178fe615baSAndrew Gallatin 	return;
418b2fc195eSAndrew Gallatin }
419b2fc195eSAndrew Gallatin #endif
4208fe615baSAndrew Gallatin 
4218fe615baSAndrew Gallatin 
4228fe615baSAndrew Gallatin static int
4238fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4248fe615baSAndrew Gallatin {
4258fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4268fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4278fe615baSAndrew Gallatin 	int status;
4288fe615baSAndrew Gallatin 	uint32_t len;
4298fe615baSAndrew Gallatin 	char *test = " ";
4308fe615baSAndrew Gallatin 
4318fe615baSAndrew Gallatin 
4328fe615baSAndrew Gallatin 	/* Run a small DMA test.
4338fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4348fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4358fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4368fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4378fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4388fe615baSAndrew Gallatin 	 * transfers took to complete.
4398fe615baSAndrew Gallatin 	 */
4408fe615baSAndrew Gallatin 
4418fe615baSAndrew Gallatin 	len = sc->tx.boundary;
4428fe615baSAndrew Gallatin 
4438fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4448fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4458fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4468fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4478fe615baSAndrew Gallatin 	if (status != 0) {
4488fe615baSAndrew Gallatin 		test = "read";
4498fe615baSAndrew Gallatin 		goto abort;
4508fe615baSAndrew Gallatin 	}
4518fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4528fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4538fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4548fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4558fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4568fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4578fe615baSAndrew Gallatin 	if (status != 0) {
4588fe615baSAndrew Gallatin 		test = "write";
4598fe615baSAndrew Gallatin 		goto abort;
4608fe615baSAndrew Gallatin 	}
4618fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
4628fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4638fe615baSAndrew Gallatin 
4648fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4658fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4668fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
4678fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4688fe615baSAndrew Gallatin 	if (status != 0) {
4698fe615baSAndrew Gallatin 		test = "read/write";
4708fe615baSAndrew Gallatin 		goto abort;
4718fe615baSAndrew Gallatin 	}
4728fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4738fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4748fe615baSAndrew Gallatin 
4758fe615baSAndrew Gallatin abort:
4768fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
4778fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4788fe615baSAndrew Gallatin 			      test, status);
4798fe615baSAndrew Gallatin 
4808fe615baSAndrew Gallatin 	return status;
4818fe615baSAndrew Gallatin }
4828fe615baSAndrew Gallatin 
483b2fc195eSAndrew Gallatin /*
484b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
485b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
486b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
487b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
488b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
489b2fc195eSAndrew Gallatin  *
490b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
491b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
492b2fc195eSAndrew Gallatin  *
493b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
494b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
495b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
496b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
497b2fc195eSAndrew Gallatin  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
498b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
499b2fc195eSAndrew Gallatin  * firmware image, and set tx.boundary to 4KB.
500b2fc195eSAndrew Gallatin  */
501b2fc195eSAndrew Gallatin 
5028fe615baSAndrew Gallatin static int
5038fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5048fe615baSAndrew Gallatin {
5058fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5068fe615baSAndrew Gallatin 	int reg, status;
5078fe615baSAndrew Gallatin 	uint16_t pectl;
5088fe615baSAndrew Gallatin 
5098fe615baSAndrew Gallatin 	sc->tx.boundary = 4096;
5108fe615baSAndrew Gallatin 	/*
5118fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5128fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5138fe615baSAndrew Gallatin 	 */
5148fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5158fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5168fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5178fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5188fe615baSAndrew Gallatin 				      pectl);
5198fe615baSAndrew Gallatin 			sc->tx.boundary = 2048;
5208fe615baSAndrew Gallatin 		}
5218fe615baSAndrew Gallatin 	}
5228fe615baSAndrew Gallatin 
5238fe615baSAndrew Gallatin 	/*
5248fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5258fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5268fe615baSAndrew Gallatin 	 */
5278fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5288fe615baSAndrew Gallatin 	status = mxge_load_firmware(sc);
5298fe615baSAndrew Gallatin 	if (status != 0) {
5308fe615baSAndrew Gallatin 		return status;
5318fe615baSAndrew Gallatin 	}
5328fe615baSAndrew Gallatin 
5338fe615baSAndrew Gallatin 	/*
5348fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5358fe615baSAndrew Gallatin 	 */
5368fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5378fe615baSAndrew Gallatin 
5388fe615baSAndrew Gallatin 	/*
5398fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5408fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5418fe615baSAndrew Gallatin 	 */
5428fe615baSAndrew Gallatin 
5438fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5448fe615baSAndrew Gallatin 	if (status == 0)
5458fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5468fe615baSAndrew Gallatin 
5478fe615baSAndrew Gallatin 	if (status != E2BIG)
5488fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5498fe615baSAndrew Gallatin 	if (status == ENOSYS)
5508fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5518fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5528fe615baSAndrew Gallatin 	return status;
5538fe615baSAndrew Gallatin }
5548fe615baSAndrew Gallatin 
5558fe615baSAndrew Gallatin static int
5566d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
557b2fc195eSAndrew Gallatin {
5588fe615baSAndrew Gallatin 	int aligned = 0;
559b2fc195eSAndrew Gallatin 
560d91b1b49SAndrew Gallatin 
561d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
562d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
563d91b1b49SAndrew Gallatin 			aligned = 1;
564d91b1b49SAndrew Gallatin 		else
565d91b1b49SAndrew Gallatin 			aligned = 0;
566d91b1b49SAndrew Gallatin 		if (mxge_verbose)
567d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
568d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
569d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
570d91b1b49SAndrew Gallatin 		goto abort;
571d91b1b49SAndrew Gallatin 	}
572d91b1b49SAndrew Gallatin 
573d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
574d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
575d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
576d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
577d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
578d91b1b49SAndrew Gallatin 			      sc->link_width);
579d91b1b49SAndrew Gallatin 		aligned = 1;
580d91b1b49SAndrew Gallatin 		goto abort;
581d91b1b49SAndrew Gallatin 	}
582d91b1b49SAndrew Gallatin 
5838fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
5848fe615baSAndrew Gallatin 		return 0;
585b2fc195eSAndrew Gallatin 
586b2fc195eSAndrew Gallatin abort:
587b2fc195eSAndrew Gallatin 	if (aligned) {
5886d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
589b2fc195eSAndrew Gallatin 		sc->tx.boundary = 4096;
590b2fc195eSAndrew Gallatin 	} else {
5916d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
592b2fc195eSAndrew Gallatin 		sc->tx.boundary = 2048;
593b2fc195eSAndrew Gallatin 	}
5948fe615baSAndrew Gallatin 	return (mxge_load_firmware(sc));
595b2fc195eSAndrew Gallatin }
596b2fc195eSAndrew Gallatin 
597b2fc195eSAndrew Gallatin union qualhack
598b2fc195eSAndrew Gallatin {
599b2fc195eSAndrew Gallatin         const char *ro_char;
600b2fc195eSAndrew Gallatin         char *rw_char;
601b2fc195eSAndrew Gallatin };
602b2fc195eSAndrew Gallatin 
6034da0d523SAndrew Gallatin static int
6044da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6054da0d523SAndrew Gallatin {
606b824b7d8SAndrew Gallatin 
6074da0d523SAndrew Gallatin 
6084da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6094da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6104da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6114da0d523SAndrew Gallatin 		return EIO;
6124da0d523SAndrew Gallatin 	}
6134da0d523SAndrew Gallatin 
6144da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6154da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6164da0d523SAndrew Gallatin 	if (mxge_verbose)
6174da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6184da0d523SAndrew Gallatin 
619b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
620b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6214da0d523SAndrew Gallatin 
622b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
623b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6244da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6254da0d523SAndrew Gallatin 			      sc->fw_version);
6264da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6274da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6284da0d523SAndrew Gallatin 		return EINVAL;
6294da0d523SAndrew Gallatin 	}
6304da0d523SAndrew Gallatin 	return 0;
6314da0d523SAndrew Gallatin 
6324da0d523SAndrew Gallatin }
633b2fc195eSAndrew Gallatin 
634b2fc195eSAndrew Gallatin static int
6356d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
636b2fc195eSAndrew Gallatin {
63733d54970SLuigi Rizzo 	const struct firmware *fw;
638b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
639b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
640b2fc195eSAndrew Gallatin 	const char *fw_data;
641b2fc195eSAndrew Gallatin 	union qualhack hack;
642b2fc195eSAndrew Gallatin 	int status;
6434da0d523SAndrew Gallatin 	unsigned int i;
6444da0d523SAndrew Gallatin 	char dummy;
645b2fc195eSAndrew Gallatin 
646b2fc195eSAndrew Gallatin 
647b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
648b2fc195eSAndrew Gallatin 
649b2fc195eSAndrew Gallatin 	if (fw == NULL) {
650b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
651b2fc195eSAndrew Gallatin 			      sc->fw_name);
652b2fc195eSAndrew Gallatin 		return ENOENT;
653b2fc195eSAndrew Gallatin 	}
654b2fc195eSAndrew Gallatin 	if (fw->datasize > *limit ||
655b2fc195eSAndrew Gallatin 	    fw->datasize < MCP_HEADER_PTR_OFFSET + 4) {
656b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n",
657b2fc195eSAndrew Gallatin 			      sc->fw_name, (int)fw->datasize, (int) *limit);
658b2fc195eSAndrew Gallatin 		status = ENOSPC;
659b2fc195eSAndrew Gallatin 		goto abort_with_fw;
660b2fc195eSAndrew Gallatin 	}
661b2fc195eSAndrew Gallatin 	*limit = fw->datasize;
662b2fc195eSAndrew Gallatin 
663b2fc195eSAndrew Gallatin 	/* check id */
664b2fc195eSAndrew Gallatin 	fw_data = (const char *)fw->data;
665b2fc195eSAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
666b2fc195eSAndrew Gallatin 			     (fw_data + MCP_HEADER_PTR_OFFSET));
667b2fc195eSAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) {
668b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
669b2fc195eSAndrew Gallatin 		status = EIO;
670b2fc195eSAndrew Gallatin 		goto abort_with_fw;
671b2fc195eSAndrew Gallatin 	}
672b2fc195eSAndrew Gallatin 	hdr = (const void*)(fw_data + hdr_offset);
673b2fc195eSAndrew Gallatin 
6744da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
6754da0d523SAndrew Gallatin 	if (status != 0)
6764da0d523SAndrew Gallatin 		goto abort_with_fw;
677b2fc195eSAndrew Gallatin 
678b2fc195eSAndrew Gallatin 	hack.ro_char = fw_data;
679b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
6804da0d523SAndrew Gallatin 	for (i = 0; i < *limit; i += 256) {
6814da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
6824da0d523SAndrew Gallatin 			      hack.rw_char + i,
6834da0d523SAndrew Gallatin 			      min(256U, (unsigned)(*limit - i)));
6844da0d523SAndrew Gallatin 		mb();
6854da0d523SAndrew Gallatin 		dummy = *sc->sram;
6864da0d523SAndrew Gallatin 		mb();
6874da0d523SAndrew Gallatin 	}
688b2fc195eSAndrew Gallatin 
689b2fc195eSAndrew Gallatin 	status = 0;
690b2fc195eSAndrew Gallatin abort_with_fw:
691b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
692b2fc195eSAndrew Gallatin 	return status;
693b2fc195eSAndrew Gallatin }
694b2fc195eSAndrew Gallatin 
695b2fc195eSAndrew Gallatin /*
696b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
697b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
698b2fc195eSAndrew Gallatin  */
699b2fc195eSAndrew Gallatin 
700b2fc195eSAndrew Gallatin static void
7016d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
702b2fc195eSAndrew Gallatin {
703b2fc195eSAndrew Gallatin 	char buf_bytes[72];
704b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
705b2fc195eSAndrew Gallatin 	volatile char *submit;
706b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
707b2fc195eSAndrew Gallatin 	int i;
708b2fc195eSAndrew Gallatin 
709b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
710b2fc195eSAndrew Gallatin 
711b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
712b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
713b2fc195eSAndrew Gallatin 	*confirm = 0;
714b2fc195eSAndrew Gallatin 	mb();
715b2fc195eSAndrew Gallatin 
716b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
717b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
718b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
719b2fc195eSAndrew Gallatin 	*/
720b2fc195eSAndrew Gallatin 
7216d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7226d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
723b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
724b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
725b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7266d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7276d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
728b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
729b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
730b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
731b2fc195eSAndrew Gallatin 
732b2fc195eSAndrew Gallatin 
7330fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
734b2fc195eSAndrew Gallatin 
7356d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
736b2fc195eSAndrew Gallatin 	mb();
737b2fc195eSAndrew Gallatin 	DELAY(1000);
738b2fc195eSAndrew Gallatin 	mb();
739b2fc195eSAndrew Gallatin 	i = 0;
740b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
741b2fc195eSAndrew Gallatin 		DELAY(1000);
742b2fc195eSAndrew Gallatin 		i++;
743b2fc195eSAndrew Gallatin 	}
744b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
745b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
746b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
747b2fc195eSAndrew Gallatin 			      *confirm);
748b2fc195eSAndrew Gallatin 	}
749b2fc195eSAndrew Gallatin 	return;
750b2fc195eSAndrew Gallatin }
751b2fc195eSAndrew Gallatin 
752b2fc195eSAndrew Gallatin static int
7536d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
754b2fc195eSAndrew Gallatin {
755b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
756b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
757b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
7580fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
759b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
760e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
761b2fc195eSAndrew Gallatin 
762b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
763b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
764b2fc195eSAndrew Gallatin 
765b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
766b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
767b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
768b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
7696d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7706d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
771b2fc195eSAndrew Gallatin 
772b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
773b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
774a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
775b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
776b2fc195eSAndrew Gallatin 	mb();
7776d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
778b2fc195eSAndrew Gallatin 
7795e7d8541SAndrew Gallatin 	/* wait up to 20ms */
780e0501fd0SAndrew Gallatin 	err = EAGAIN;
7815e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
782b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
783b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
784b2fc195eSAndrew Gallatin 		mb();
785e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
786e0501fd0SAndrew Gallatin 		case 0:
787b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
788e0501fd0SAndrew Gallatin 			err = 0;
789e0501fd0SAndrew Gallatin 			break;
790e0501fd0SAndrew Gallatin 		case 0xffffffff:
791e0501fd0SAndrew Gallatin 			DELAY(1000);
792e0501fd0SAndrew Gallatin 			break;
793e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
794e0501fd0SAndrew Gallatin 			err = ENOSYS;
795e0501fd0SAndrew Gallatin 			break;
796e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
797e0501fd0SAndrew Gallatin 			err = E2BIG;
798e0501fd0SAndrew Gallatin 			break;
799e0501fd0SAndrew Gallatin 		default:
800b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8016d87a65dSAndrew Gallatin 				      "mxge: command %d "
802b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
803b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
804e0501fd0SAndrew Gallatin 			err = ENXIO;
805e0501fd0SAndrew Gallatin 			break;
806b2fc195eSAndrew Gallatin 		}
807e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
808e0501fd0SAndrew Gallatin 			break;
809b2fc195eSAndrew Gallatin 	}
810e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8116d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
812b2fc195eSAndrew Gallatin 			      "result = %d\n",
813b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
814e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
815e0501fd0SAndrew Gallatin 	return err;
816b2fc195eSAndrew Gallatin }
817b2fc195eSAndrew Gallatin 
8184da0d523SAndrew Gallatin static int
8194da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8204da0d523SAndrew Gallatin {
8214da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8224da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8234da0d523SAndrew Gallatin 	size_t hdr_offset;
8244da0d523SAndrew Gallatin 	int status;
8254da0d523SAndrew Gallatin 
8264da0d523SAndrew Gallatin 	/* find running firmware header */
8274da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
8284da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
8294da0d523SAndrew Gallatin 
8304da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8314da0d523SAndrew Gallatin 		device_printf(sc->dev,
8324da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
8334da0d523SAndrew Gallatin 			      (int)hdr_offset);
8344da0d523SAndrew Gallatin 		return EIO;
8354da0d523SAndrew Gallatin 	}
8364da0d523SAndrew Gallatin 
8374da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
8384da0d523SAndrew Gallatin 	 * validate firmware */
8394da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
8404da0d523SAndrew Gallatin 	if (hdr == NULL) {
8414da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
8424da0d523SAndrew Gallatin 		return ENOMEM;
8434da0d523SAndrew Gallatin 	}
8444da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8454da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
8464da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
8474da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
8484da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
849b824b7d8SAndrew Gallatin 
850b824b7d8SAndrew Gallatin 	/*
851b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
852b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
853b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
854b824b7d8SAndrew Gallatin 	 */
855b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
856b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
857b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
858b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
859b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
860b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
861b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
862b824b7d8SAndrew Gallatin 	}
863b824b7d8SAndrew Gallatin 
8644da0d523SAndrew Gallatin 	return status;
8654da0d523SAndrew Gallatin }
8664da0d523SAndrew Gallatin 
867b2fc195eSAndrew Gallatin 
868b2fc195eSAndrew Gallatin static int
8696d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc)
870b2fc195eSAndrew Gallatin {
871b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
872b2fc195eSAndrew Gallatin 	volatile char *submit;
873b2fc195eSAndrew Gallatin 	char buf_bytes[72];
874b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
875b2fc195eSAndrew Gallatin 	int status, i;
876b2fc195eSAndrew Gallatin 
877b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
878b2fc195eSAndrew Gallatin 
879b2fc195eSAndrew Gallatin 	size = sc->sram_size;
8806d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
881b2fc195eSAndrew Gallatin 	if (status) {
8824da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
8834da0d523SAndrew Gallatin 		   it is new enough */
8844da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
8854da0d523SAndrew Gallatin 		if (status) {
8864da0d523SAndrew Gallatin 			device_printf(sc->dev,
8874da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
888b2fc195eSAndrew Gallatin 			return status;
889b2fc195eSAndrew Gallatin 		}
8904da0d523SAndrew Gallatin 		device_printf(sc->dev,
8914da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
8924da0d523SAndrew Gallatin 		if (sc->tx.boundary == 4096) {
8934da0d523SAndrew Gallatin 			device_printf(sc->dev,
8944da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
8954da0d523SAndrew Gallatin 				 ".  For optimal\n");
8964da0d523SAndrew Gallatin 			device_printf(sc->dev,
8974da0d523SAndrew Gallatin 				 "performance consider loading optimized "
8984da0d523SAndrew Gallatin 				 "firmware\n");
8994da0d523SAndrew Gallatin 		}
900d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
901d91b1b49SAndrew Gallatin 		sc->tx.boundary = 2048;
902d91b1b49SAndrew Gallatin 		return 0;
9034da0d523SAndrew Gallatin 	}
904b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
905b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
906b2fc195eSAndrew Gallatin 	*confirm = 0;
907b2fc195eSAndrew Gallatin 	mb();
908b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
909b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
910b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
911b2fc195eSAndrew Gallatin 	*/
912b2fc195eSAndrew Gallatin 
9136d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9146d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
915b2fc195eSAndrew Gallatin 
916b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
917b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
918b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
919b2fc195eSAndrew Gallatin 
920b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
921b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
922b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
923b2fc195eSAndrew Gallatin 	*/
924b2fc195eSAndrew Gallatin 					/* where the code starts*/
9256d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
926b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
927b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
928b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
929b2fc195eSAndrew Gallatin 
9300fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9316d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
932b2fc195eSAndrew Gallatin 	mb();
933b2fc195eSAndrew Gallatin 	DELAY(1000);
934b2fc195eSAndrew Gallatin 	mb();
935b2fc195eSAndrew Gallatin 	i = 0;
936b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
937b2fc195eSAndrew Gallatin 		DELAY(1000*10);
938b2fc195eSAndrew Gallatin 		i++;
939b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
940b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
941b2fc195eSAndrew Gallatin 	}
942b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
943b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
944b2fc195eSAndrew Gallatin 			confirm, *confirm);
945b2fc195eSAndrew Gallatin 
946b2fc195eSAndrew Gallatin 		return ENXIO;
947b2fc195eSAndrew Gallatin 	}
948b2fc195eSAndrew Gallatin 	return 0;
949b2fc195eSAndrew Gallatin }
950b2fc195eSAndrew Gallatin 
951b2fc195eSAndrew Gallatin static int
9526d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
953b2fc195eSAndrew Gallatin {
9546d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
955b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
956b2fc195eSAndrew Gallatin 	int status;
957b2fc195eSAndrew Gallatin 
958b2fc195eSAndrew Gallatin 
959b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
960b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
961b2fc195eSAndrew Gallatin 
962b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
963b2fc195eSAndrew Gallatin 
9645e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
965b2fc195eSAndrew Gallatin 	return status;
966b2fc195eSAndrew Gallatin }
967b2fc195eSAndrew Gallatin 
968b2fc195eSAndrew Gallatin static int
9696d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
970b2fc195eSAndrew Gallatin {
9716d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
972b2fc195eSAndrew Gallatin 	int status;
973b2fc195eSAndrew Gallatin 
974b2fc195eSAndrew Gallatin 	if (pause)
9755e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
976b2fc195eSAndrew Gallatin 				       &cmd);
977b2fc195eSAndrew Gallatin 	else
9785e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
979b2fc195eSAndrew Gallatin 				       &cmd);
980b2fc195eSAndrew Gallatin 
981b2fc195eSAndrew Gallatin 	if (status) {
982b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
983b2fc195eSAndrew Gallatin 		return ENXIO;
984b2fc195eSAndrew Gallatin 	}
985b2fc195eSAndrew Gallatin 	sc->pause = pause;
986b2fc195eSAndrew Gallatin 	return 0;
987b2fc195eSAndrew Gallatin }
988b2fc195eSAndrew Gallatin 
989b2fc195eSAndrew Gallatin static void
9906d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
991b2fc195eSAndrew Gallatin {
9926d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
993b2fc195eSAndrew Gallatin 	int status;
994b2fc195eSAndrew Gallatin 
995b2fc195eSAndrew Gallatin 	if (promisc)
9965e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
997b2fc195eSAndrew Gallatin 				       &cmd);
998b2fc195eSAndrew Gallatin 	else
9995e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1000b2fc195eSAndrew Gallatin 				       &cmd);
1001b2fc195eSAndrew Gallatin 
1002b2fc195eSAndrew Gallatin 	if (status) {
1003b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1004b2fc195eSAndrew Gallatin 	}
1005b2fc195eSAndrew Gallatin }
1006b2fc195eSAndrew Gallatin 
10070fa7f681SAndrew Gallatin static void
10080fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10090fa7f681SAndrew Gallatin {
10100fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
10110fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
10120fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
10130fa7f681SAndrew Gallatin 	int err;
10140fa7f681SAndrew Gallatin 
10150fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
10160fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
10170fa7f681SAndrew Gallatin 		return;
10180fa7f681SAndrew Gallatin 
10190fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
10200fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10210fa7f681SAndrew Gallatin 	if (err != 0) {
10220fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
10230fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
10240fa7f681SAndrew Gallatin 		return;
10250fa7f681SAndrew Gallatin 	}
10260fa7f681SAndrew Gallatin 
1027b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1028b824b7d8SAndrew Gallatin 		return;
10290fa7f681SAndrew Gallatin 
10300fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
10310fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
10320fa7f681SAndrew Gallatin 		return;
10330fa7f681SAndrew Gallatin 
10340fa7f681SAndrew Gallatin 	/* Flush all the filters */
10350fa7f681SAndrew Gallatin 
10360fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10370fa7f681SAndrew Gallatin 	if (err != 0) {
10380fa7f681SAndrew Gallatin 		device_printf(sc->dev,
10390fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
10400fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
10410fa7f681SAndrew Gallatin 		return;
10420fa7f681SAndrew Gallatin 	}
10430fa7f681SAndrew Gallatin 
10440fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
10450fa7f681SAndrew Gallatin 
10460fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
10470fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10480fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
10490fa7f681SAndrew Gallatin 			continue;
10500fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10510fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
10520fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10530fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
10540fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
10550fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
10560fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10570fa7f681SAndrew Gallatin 		if (err != 0) {
10580fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
10590fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
10600fa7f681SAndrew Gallatin 			       "%d\t", err);
10610fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
10620fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
10630fa7f681SAndrew Gallatin 			return;
10640fa7f681SAndrew Gallatin 		}
10650fa7f681SAndrew Gallatin 	}
10660fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
10670fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
10680fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10690fa7f681SAndrew Gallatin 	if (err != 0) {
10700fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
10710fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
10720fa7f681SAndrew Gallatin 	}
10730fa7f681SAndrew Gallatin }
10740fa7f681SAndrew Gallatin 
1075b2fc195eSAndrew Gallatin static int
1076053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1077053e637fSAndrew Gallatin {
1078053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1079053e637fSAndrew Gallatin 	int status;
1080053e637fSAndrew Gallatin 
1081c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1082c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1083053e637fSAndrew Gallatin 
1084053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1085053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1086053e637fSAndrew Gallatin 	cmd.data0 = 0;
1087053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1088053e637fSAndrew Gallatin 			       &cmd);
1089053e637fSAndrew Gallatin 	if (status == 0)
1090c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1091053e637fSAndrew Gallatin 
1092053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1093053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1094053e637fSAndrew Gallatin }
1095053e637fSAndrew Gallatin 
1096053e637fSAndrew Gallatin static int
1097adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1098b2fc195eSAndrew Gallatin {
1099b2fc195eSAndrew Gallatin 
11006d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11015e7d8541SAndrew Gallatin 	size_t bytes;
11025e7d8541SAndrew Gallatin 	int status;
1103b2fc195eSAndrew Gallatin 
1104b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1105b2fc195eSAndrew Gallatin 	   is alive */
1106b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11075e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1108b2fc195eSAndrew Gallatin 	if (status != 0) {
1109b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1110b2fc195eSAndrew Gallatin 		return ENXIO;
1111b2fc195eSAndrew Gallatin 	}
1112b2fc195eSAndrew Gallatin 
1113091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1114091feecdSAndrew Gallatin 
1115adae7080SAndrew Gallatin 	if (interrupts_setup) {
1116b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
1117adae7080SAndrew Gallatin 		bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry);
11185e7d8541SAndrew Gallatin 		memset(sc->rx_done.entry, 0, bytes);
11195e7d8541SAndrew Gallatin 		cmd.data0 = (uint32_t)bytes;
11205e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11215e7d8541SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr);
11225e7d8541SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr);
11235e7d8541SAndrew Gallatin 		status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd);
1124adae7080SAndrew Gallatin 	}
1125b2fc195eSAndrew Gallatin 
11266d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
11275e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
11285e7d8541SAndrew Gallatin 
11295e7d8541SAndrew Gallatin 
11305e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
11315e7d8541SAndrew Gallatin 
11325e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
11335e7d8541SAndrew Gallatin 	sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
11345e7d8541SAndrew Gallatin 
11355e7d8541SAndrew Gallatin 
11365e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
11376d87a65dSAndrew Gallatin 				&cmd);
11385e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1139b2fc195eSAndrew Gallatin 	if (status != 0) {
1140b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1141b2fc195eSAndrew Gallatin 		return status;
1142b2fc195eSAndrew Gallatin 	}
1143b2fc195eSAndrew Gallatin 
11445e7d8541SAndrew Gallatin 
11455e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
11465e7d8541SAndrew Gallatin 
11475e7d8541SAndrew Gallatin 
11485e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
11498fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
11505e7d8541SAndrew Gallatin 
1151b2fc195eSAndrew Gallatin 	/* reset mcp/driver shared state back to 0 */
11525e7d8541SAndrew Gallatin 	sc->rx_done.idx = 0;
11535e7d8541SAndrew Gallatin 	sc->rx_done.cnt = 0;
1154b2fc195eSAndrew Gallatin 	sc->tx.req = 0;
1155b2fc195eSAndrew Gallatin 	sc->tx.done = 0;
11565e7d8541SAndrew Gallatin 	sc->tx.pkt_done = 0;
1157a82c2581SAndrew Gallatin 	sc->tx.wake = 0;
1158adae7080SAndrew Gallatin 	sc->tx_defrag = 0;
1159a82c2581SAndrew Gallatin 	sc->tx.stall = 0;
1160b2fc195eSAndrew Gallatin 	sc->rx_big.cnt = 0;
1161b2fc195eSAndrew Gallatin 	sc->rx_small.cnt = 0;
1162b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
1163a98d6cd7SAndrew Gallatin 	sc->fw_stats->valid = 0;
1164a98d6cd7SAndrew Gallatin 	sc->fw_stats->send_done_count = 0;
1165053e637fSAndrew Gallatin 	sc->lro_bad_csum = 0;
1166053e637fSAndrew Gallatin 	sc->lro_queued = 0;
1167053e637fSAndrew Gallatin 	sc->lro_flushed = 0;
11686d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
11696d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
11706d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
11710fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1172b2fc195eSAndrew Gallatin 	return status;
1173b2fc195eSAndrew Gallatin }
1174b2fc195eSAndrew Gallatin 
1175b2fc195eSAndrew Gallatin static int
11766d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1177b2fc195eSAndrew Gallatin {
11786d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1179b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1180b2fc195eSAndrew Gallatin         int err;
1181b2fc195eSAndrew Gallatin 
1182b2fc195eSAndrew Gallatin         sc = arg1;
1183b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1184b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1185b2fc195eSAndrew Gallatin         if (err != 0) {
1186b2fc195eSAndrew Gallatin                 return err;
1187b2fc195eSAndrew Gallatin         }
1188b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1189b2fc195eSAndrew Gallatin                 return 0;
1190b2fc195eSAndrew Gallatin 
1191b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1192b2fc195eSAndrew Gallatin                 return EINVAL;
1193b2fc195eSAndrew Gallatin 
1194a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
11955e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1196b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
11975e7d8541SAndrew Gallatin 
1198a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1199b2fc195eSAndrew Gallatin         return err;
1200b2fc195eSAndrew Gallatin }
1201b2fc195eSAndrew Gallatin 
1202b2fc195eSAndrew Gallatin static int
12036d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1204b2fc195eSAndrew Gallatin {
12056d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1206b2fc195eSAndrew Gallatin         unsigned int enabled;
1207b2fc195eSAndrew Gallatin         int err;
1208b2fc195eSAndrew Gallatin 
1209b2fc195eSAndrew Gallatin         sc = arg1;
1210b2fc195eSAndrew Gallatin         enabled = sc->pause;
1211b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1212b2fc195eSAndrew Gallatin         if (err != 0) {
1213b2fc195eSAndrew Gallatin                 return err;
1214b2fc195eSAndrew Gallatin         }
1215b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1216b2fc195eSAndrew Gallatin                 return 0;
1217b2fc195eSAndrew Gallatin 
1218a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
12196d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1220a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1221b2fc195eSAndrew Gallatin         return err;
1222b2fc195eSAndrew Gallatin }
1223b2fc195eSAndrew Gallatin 
1224b2fc195eSAndrew Gallatin static int
12256d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1226b2fc195eSAndrew Gallatin {
1227b2fc195eSAndrew Gallatin         int err;
1228b2fc195eSAndrew Gallatin 
1229b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1230b2fc195eSAndrew Gallatin                 return EFAULT;
1231b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1232b2fc195eSAndrew Gallatin         arg1 = NULL;
1233b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1234b2fc195eSAndrew Gallatin 
1235b2fc195eSAndrew Gallatin         return err;
1236b2fc195eSAndrew Gallatin }
1237b2fc195eSAndrew Gallatin 
1238b2fc195eSAndrew Gallatin static void
12396d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1240b2fc195eSAndrew Gallatin {
1241b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1242b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
12435e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
1244b2fc195eSAndrew Gallatin 
1245b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1246b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
1247b2fc195eSAndrew Gallatin 	fw = sc->fw_stats;
1248b2fc195eSAndrew Gallatin 
12495e7d8541SAndrew Gallatin 	/* random information */
12505e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
12515e7d8541SAndrew Gallatin 		       "firmware_version",
12525e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
12535e7d8541SAndrew Gallatin 		       0, "firmware version");
12545e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
12555e7d8541SAndrew Gallatin 		       "serial_number",
12565e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
12575e7d8541SAndrew Gallatin 		       0, "serial number");
12585e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
12595e7d8541SAndrew Gallatin 		       "product_code",
12605e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
12615e7d8541SAndrew Gallatin 		       0, "product_code");
12625e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1263d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1264d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1265d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1266d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12675e7d8541SAndrew Gallatin 		       "tx_boundary",
12685e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.boundary,
12695e7d8541SAndrew Gallatin 		       0, "tx_boundary");
12705e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1271091feecdSAndrew Gallatin 		       "write_combine",
1272091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1273091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1274091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12755e7d8541SAndrew Gallatin 		       "read_dma_MBs",
12765e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
12775e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
12785e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12795e7d8541SAndrew Gallatin 		       "write_dma_MBs",
12805e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
12815e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
12825e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12835e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
12845e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
12855e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
12865e7d8541SAndrew Gallatin 
12875e7d8541SAndrew Gallatin 
12885e7d8541SAndrew Gallatin 	/* performance related tunables */
1289b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1290b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1291b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
12926d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1293b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1294b2fc195eSAndrew Gallatin 
1295b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1296b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1297b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
12986d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1299b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1300b2fc195eSAndrew Gallatin 
1301b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13025e7d8541SAndrew Gallatin 		       "deassert_wait",
13035e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
13045e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1305b2fc195eSAndrew Gallatin 
1306b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1307b2fc195eSAndrew Gallatin 	   Need to swap it */
1308b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1309b2fc195eSAndrew Gallatin 			"link_up",
1310b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
13116d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1312b2fc195eSAndrew Gallatin 			"I", "link up");
1313b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1314b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1315b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
13166d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1317b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1318b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1319adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1320adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1321adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
13226d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1323adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1324adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1325adae7080SAndrew Gallatin 			"dropped_bad_phy",
1326adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1327adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1328adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1329adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1330b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1331b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1332b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1333b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
13346d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1335b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1336b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1337adae7080SAndrew Gallatin 			"dropped_link_overflow",
1338adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1339adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1340adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1341adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
13420fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
13430fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
13440fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
13450fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
13460fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
13470fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1348adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1349adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
13506d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1351adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1352b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1353b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1354b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1355b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
13566d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1357b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1358b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1359adae7080SAndrew Gallatin 			"dropped_overrun",
1360adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
13616d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1362adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1363adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1364adae7080SAndrew Gallatin 			"dropped_pause",
1365adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1366adae7080SAndrew Gallatin 			&fw->dropped_pause,
1367adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1368adae7080SAndrew Gallatin 			"I", "dropped_pause");
1369adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1370adae7080SAndrew Gallatin 			"dropped_runt",
1371adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1372adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1373adae7080SAndrew Gallatin 			"I", "dropped_runt");
1374b2fc195eSAndrew Gallatin 
1375b2fc195eSAndrew Gallatin 	/* host counters exported for debugging */
1376b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13775e7d8541SAndrew Gallatin 		       "rx_small_cnt",
13785e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_small.cnt,
13795e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
13805e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13815e7d8541SAndrew Gallatin 		       "rx_big_cnt",
13825e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_big.cnt,
13835e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
13845e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1385b2fc195eSAndrew Gallatin 		       "tx_req",
1386b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.req,
1387b2fc195eSAndrew Gallatin 		       0, "tx_req");
1388b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1389b2fc195eSAndrew Gallatin 		       "tx_done",
1390b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.done,
1391b2fc195eSAndrew Gallatin 		       0, "tx_done");
1392b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13935e7d8541SAndrew Gallatin 		       "tx_pkt_done",
13945e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.pkt_done,
13955e7d8541SAndrew Gallatin 		       0, "tx_done");
1396a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1397a82c2581SAndrew Gallatin 		       "tx_stall",
1398a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.stall,
1399a82c2581SAndrew Gallatin 		       0, "tx_stall");
1400a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1401a82c2581SAndrew Gallatin 		       "tx_wake",
1402a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.wake,
1403a82c2581SAndrew Gallatin 		       0, "tx_wake");
1404adae7080SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1405adae7080SAndrew Gallatin 		       "tx_defrag",
1406adae7080SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_defrag,
1407adae7080SAndrew Gallatin 		       0, "tx_defrag");
14085e7d8541SAndrew Gallatin 
14095e7d8541SAndrew Gallatin 	/* verbose printing? */
1410b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14115e7d8541SAndrew Gallatin 		       "verbose",
14125e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
14135e7d8541SAndrew Gallatin 		       0, "verbose printing");
1414b2fc195eSAndrew Gallatin 
1415053e637fSAndrew Gallatin 	/* lro */
1416053e637fSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1417adae7080SAndrew Gallatin 		       "lro_cnt", CTLFLAG_RD, &sc->lro_cnt,
1418053e637fSAndrew Gallatin 		       0, "number of lro merge queues");
1419053e637fSAndrew Gallatin 
1420053e637fSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1421053e637fSAndrew Gallatin 		       "lro_flushed", CTLFLAG_RD, &sc->lro_flushed,
1422053e637fSAndrew Gallatin 		       0, "number of lro merge queues flushed");
1423053e637fSAndrew Gallatin 
1424053e637fSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1425053e637fSAndrew Gallatin 		       "lro_queued", CTLFLAG_RD, &sc->lro_queued,
1426053e637fSAndrew Gallatin 		       0, "number of frames appended to lro merge queues");
1427053e637fSAndrew Gallatin 
1428b2fc195eSAndrew Gallatin }
1429b2fc195eSAndrew Gallatin 
1430b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1431b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1432b2fc195eSAndrew Gallatin 
1433b2fc195eSAndrew Gallatin static inline void
14346d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx,
1435b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1436b2fc195eSAndrew Gallatin {
1437b2fc195eSAndrew Gallatin         int idx, starting_slot;
1438b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1439b2fc195eSAndrew Gallatin         while (cnt > 1) {
1440b2fc195eSAndrew Gallatin                 cnt--;
1441b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
14426d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1443b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1444b2fc195eSAndrew Gallatin                 mb();
1445b2fc195eSAndrew Gallatin         }
1446b2fc195eSAndrew Gallatin }
1447b2fc195eSAndrew Gallatin 
1448b2fc195eSAndrew Gallatin /*
1449b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1450b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1451b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1452b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1453b2fc195eSAndrew Gallatin  */
1454b2fc195eSAndrew Gallatin 
1455b2fc195eSAndrew Gallatin static inline void
14566d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src,
1457b2fc195eSAndrew Gallatin                   int cnt)
1458b2fc195eSAndrew Gallatin {
1459b2fc195eSAndrew Gallatin         int idx, i;
1460b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1461b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1462b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1463b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
14645e7d8541SAndrew Gallatin 	uint8_t last_flags;
1465b2fc195eSAndrew Gallatin 
1466b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1467b2fc195eSAndrew Gallatin 
14685e7d8541SAndrew Gallatin 	last_flags = src->flags;
14695e7d8541SAndrew Gallatin 	src->flags = 0;
1470b2fc195eSAndrew Gallatin         mb();
1471b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1472b2fc195eSAndrew Gallatin         srcp = src;
1473b2fc195eSAndrew Gallatin 
1474b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1475b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
14766d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1477b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1478b2fc195eSAndrew Gallatin                         srcp += 2;
1479b2fc195eSAndrew Gallatin                         dstp += 2;
1480b2fc195eSAndrew Gallatin                 }
1481b2fc195eSAndrew Gallatin         } else {
1482b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1483b2fc195eSAndrew Gallatin                    that it is submitted below */
14846d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1485b2fc195eSAndrew Gallatin                 i = 0;
1486b2fc195eSAndrew Gallatin         }
1487b2fc195eSAndrew Gallatin         if (i < cnt) {
1488b2fc195eSAndrew Gallatin                 /* submit the first request */
14896d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1490b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1491b2fc195eSAndrew Gallatin         }
1492b2fc195eSAndrew Gallatin 
1493b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
14945e7d8541SAndrew Gallatin         src->flags = last_flags;
1495b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1496b2fc195eSAndrew Gallatin         src_ints+=3;
1497b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1498b2fc195eSAndrew Gallatin         dst_ints+=3;
1499b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1500b2fc195eSAndrew Gallatin         tx->req += cnt;
1501b2fc195eSAndrew Gallatin         mb();
1502b2fc195eSAndrew Gallatin }
1503b2fc195eSAndrew Gallatin 
1504b2fc195eSAndrew Gallatin static void
1505c792928fSAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt,
1506c792928fSAndrew Gallatin 	       int ip_off)
1507aed8e389SAndrew Gallatin {
1508aed8e389SAndrew Gallatin 	mxge_tx_buf_t *tx;
1509aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1510aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1511aed8e389SAndrew Gallatin 	struct ip *ip;
1512aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1513aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1514aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1515aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1516aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1517aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1518aed8e389SAndrew Gallatin 	static int once;
1519aed8e389SAndrew Gallatin 
1520aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1521aed8e389SAndrew Gallatin 
1522aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1523aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1524aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1525aed8e389SAndrew Gallatin 	 */
1526aed8e389SAndrew Gallatin 
1527aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1528aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1529aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1530c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1531c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
1532aed8e389SAndrew Gallatin 			   sc->scratch);
1533c792928fSAndrew Gallatin 		ip = (struct ip *)(sc->scratch + ip_off);
1534aed8e389SAndrew Gallatin 	} else {
1535c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1536aed8e389SAndrew Gallatin 	}
1537c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1538aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1539c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
1540aed8e389SAndrew Gallatin 			   + sizeof (*tcp),  sc->scratch);
1541c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1542aed8e389SAndrew Gallatin 	}
1543aed8e389SAndrew Gallatin 
1544aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1545c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1546aed8e389SAndrew Gallatin 
1547aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1548c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1549aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1550aed8e389SAndrew Gallatin 
1551aed8e389SAndrew Gallatin 
1552aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1553aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1554aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1555aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1556aed8e389SAndrew Gallatin 
1557aed8e389SAndrew Gallatin 	tx = &sc->tx;
1558aed8e389SAndrew Gallatin 	req = tx->req_list;
1559aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1560aed8e389SAndrew Gallatin 	cnt = 0;
1561aed8e389SAndrew Gallatin 	rdma_count = 0;
1562aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1563aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1564aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1565aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1566aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1567aed8e389SAndrew Gallatin 	 *
1568aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1569aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1570aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1571aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1572aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1573aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1574aed8e389SAndrew Gallatin 	 *
1575aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1576aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1577aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1578aed8e389SAndrew Gallatin 	 */
1579aed8e389SAndrew Gallatin 
1580aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1581aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1582aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1583aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1584e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1585aed8e389SAndrew Gallatin 
1586aed8e389SAndrew Gallatin 		while (len) {
1587aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1588e39a0a37SAndrew Gallatin 			seglen = len;
1589aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1590aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1591aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1592aed8e389SAndrew Gallatin 				/* payload */
1593aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1594aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1595aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1596aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1597aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1598aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1599aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1600aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1601aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1602aed8e389SAndrew Gallatin 				/* header ends */
1603aed8e389SAndrew Gallatin 				rdma_count = -1;
1604aed8e389SAndrew Gallatin 				cum_len_next = 0;
1605aed8e389SAndrew Gallatin 				seglen = -cum_len;
1606aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1607aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1608aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1609aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1610aed8e389SAndrew Gallatin 			    }
1611aed8e389SAndrew Gallatin 
1612aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1613aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1614aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1615aed8e389SAndrew Gallatin 			req->pad = 0;
1616aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1617aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1618aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1619aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1620aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1621aed8e389SAndrew Gallatin 			low += seglen;
1622aed8e389SAndrew Gallatin 			len -= seglen;
1623aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1624aed8e389SAndrew Gallatin 			flags = flags_next;
1625aed8e389SAndrew Gallatin 			req++;
1626aed8e389SAndrew Gallatin 			cnt++;
1627aed8e389SAndrew Gallatin 			rdma_count++;
1628aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1629aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1630aed8e389SAndrew Gallatin 			else
1631aed8e389SAndrew Gallatin 				cksum_offset = 0;
1632adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1633aed8e389SAndrew Gallatin 				goto drop;
1634aed8e389SAndrew Gallatin 		}
1635aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1636aed8e389SAndrew Gallatin 		seg++;
1637aed8e389SAndrew Gallatin 	}
1638aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1639aed8e389SAndrew Gallatin 
1640aed8e389SAndrew Gallatin 	do {
1641aed8e389SAndrew Gallatin 		req--;
1642aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1643aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1644aed8e389SAndrew Gallatin 
1645aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1646aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1647aed8e389SAndrew Gallatin 	return;
1648aed8e389SAndrew Gallatin 
1649aed8e389SAndrew Gallatin drop:
1650e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1651aed8e389SAndrew Gallatin 	m_freem(m);
1652aed8e389SAndrew Gallatin 	sc->ifp->if_oerrors++;
1653aed8e389SAndrew Gallatin 	if (!once) {
1654adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1655adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1656adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1657aed8e389SAndrew Gallatin 		once = 1;
1658aed8e389SAndrew Gallatin 	}
1659aed8e389SAndrew Gallatin 	return;
1660aed8e389SAndrew Gallatin 
1661aed8e389SAndrew Gallatin }
1662aed8e389SAndrew Gallatin 
1663c792928fSAndrew Gallatin /*
1664c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1665c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1666c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1667c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1668c792928fSAndrew Gallatin  */
1669c792928fSAndrew Gallatin static struct mbuf *
1670c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1671c792928fSAndrew Gallatin {
1672c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1673c792928fSAndrew Gallatin 
1674c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1675c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1676c792928fSAndrew Gallatin 		return NULL;
1677c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1678c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1679c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1680c792928fSAndrew Gallatin 			return NULL;
1681c792928fSAndrew Gallatin 	}
1682c792928fSAndrew Gallatin 	/*
1683c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
1684c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
1685c792928fSAndrew Gallatin 	 */
1686c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
1687c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
1688c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
1689c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
1690c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
1691c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
1692c792928fSAndrew Gallatin 	return m;
1693c792928fSAndrew Gallatin }
1694c792928fSAndrew Gallatin 
1695aed8e389SAndrew Gallatin static void
16966d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m)
1697b2fc195eSAndrew Gallatin {
1698b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1699b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1700b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1701b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
17026d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1703b2fc195eSAndrew Gallatin 	struct ip *ip;
1704c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
1705aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1706aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1707b2fc195eSAndrew Gallatin 
1708b2fc195eSAndrew Gallatin 
1709b2fc195eSAndrew Gallatin 
1710b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1711b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1712b2fc195eSAndrew Gallatin 
1713c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
1714c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
1715c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
1716c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1717c792928fSAndrew Gallatin 			goto drop;
1718c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
1719c792928fSAndrew Gallatin 	}
1720c792928fSAndrew Gallatin 
1721b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1722b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1723b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1724aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1725b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1726adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
1727b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1728b2fc195eSAndrew Gallatin 		   to defrag */
1729b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1730b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1731b2fc195eSAndrew Gallatin 			goto drop;
1732b2fc195eSAndrew Gallatin 		}
1733adae7080SAndrew Gallatin 		sc->tx_defrag++;
1734b2fc195eSAndrew Gallatin 		m = m_tmp;
1735b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1736b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1737aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1738b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1739b2fc195eSAndrew Gallatin 	}
1740adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
1741aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1742aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1743b2fc195eSAndrew Gallatin 		goto drop;
1744b2fc195eSAndrew Gallatin 	}
1745b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1746b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
17475e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1748b2fc195eSAndrew Gallatin 
1749aed8e389SAndrew Gallatin 
1750aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1751aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
1752c792928fSAndrew Gallatin 		mxge_encap_tso(sc, m, cnt, ip_off);
1753aed8e389SAndrew Gallatin 		return;
1754aed8e389SAndrew Gallatin 	}
1755aed8e389SAndrew Gallatin 
1756b2fc195eSAndrew Gallatin 	req = tx->req_list;
1757b2fc195eSAndrew Gallatin 	cksum_offset = 0;
17585e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
17595e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1760b2fc195eSAndrew Gallatin 
1761b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1762b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1763aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
1764aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
1765c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1766c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
1767aed8e389SAndrew Gallatin 				   sc->scratch);
1768c792928fSAndrew Gallatin 			ip = (struct ip *)(sc->scratch + ip_off);
1769aed8e389SAndrew Gallatin 		} else {
1770c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
1771aed8e389SAndrew Gallatin 		}
1772c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
1773b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
17745e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1775b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
17765e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1777aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
1778aed8e389SAndrew Gallatin 	} else {
1779aed8e389SAndrew Gallatin 		odd_flag = 0;
1780b2fc195eSAndrew Gallatin 	}
17815e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
17825e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
1783b2fc195eSAndrew Gallatin 
1784b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
1785b2fc195eSAndrew Gallatin 	cum_len = 0;
1786aed8e389SAndrew Gallatin 	seg = tx->seg_list;
17875e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
1788b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1789b2fc195eSAndrew Gallatin 		req->addr_low =
17906d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1791b2fc195eSAndrew Gallatin 		req->addr_high =
17926d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1793b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
1794b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
1795b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
1796b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
1797b2fc195eSAndrew Gallatin 		else
1798b2fc195eSAndrew Gallatin 			cksum_offset = 0;
17995e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18005e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
18015e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1802aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1803b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
1804b2fc195eSAndrew Gallatin 		seg++;
1805b2fc195eSAndrew Gallatin 		req++;
1806b2fc195eSAndrew Gallatin 		req->flags = 0;
1807b2fc195eSAndrew Gallatin 	}
1808b2fc195eSAndrew Gallatin 	req--;
1809b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
1810b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
1811b2fc195eSAndrew Gallatin 		req++;
1812b2fc195eSAndrew Gallatin 		req->addr_low =
18136d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
1814b2fc195eSAndrew Gallatin 		req->addr_high =
18156d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
1816b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
18175e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
18185e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18195e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
18205e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1821aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1822b2fc195eSAndrew Gallatin 		cnt++;
1823b2fc195eSAndrew Gallatin 	}
18245e7d8541SAndrew Gallatin 
18255e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
18265e7d8541SAndrew Gallatin #if 0
18275e7d8541SAndrew Gallatin 	/* print what the firmware will see */
18285e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
18295e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
18305e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
18315e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
18325e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
18335e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
18345e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
18355e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
18365e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
18375e7d8541SAndrew Gallatin 	}
18385e7d8541SAndrew Gallatin 	printf("--------------\n");
18395e7d8541SAndrew Gallatin #endif
18405e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
18416d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1842b2fc195eSAndrew Gallatin 	return;
1843b2fc195eSAndrew Gallatin 
1844b2fc195eSAndrew Gallatin drop:
1845b2fc195eSAndrew Gallatin 	m_freem(m);
1846b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
1847b2fc195eSAndrew Gallatin 	return;
1848b2fc195eSAndrew Gallatin }
1849b2fc195eSAndrew Gallatin 
1850b2fc195eSAndrew Gallatin 
18516d914a32SAndrew Gallatin 
18526d914a32SAndrew Gallatin 
18536d914a32SAndrew Gallatin static inline void
18546d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc)
1855b2fc195eSAndrew Gallatin {
1856b2fc195eSAndrew Gallatin 	struct mbuf *m;
1857b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1858adae7080SAndrew Gallatin 	mxge_tx_buf_t *tx;
1859b2fc195eSAndrew Gallatin 
1860b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1861adae7080SAndrew Gallatin 	tx = &sc->tx;
1862adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
18636d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
18646d914a32SAndrew Gallatin 		if (m == NULL) {
18656d914a32SAndrew Gallatin 			return;
18666d914a32SAndrew Gallatin 		}
1867b2fc195eSAndrew Gallatin 		/* let BPF see it */
1868b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
1869b2fc195eSAndrew Gallatin 
1870b2fc195eSAndrew Gallatin 		/* give it to the nic */
18716d87a65dSAndrew Gallatin 		mxge_encap(sc, m);
18726d914a32SAndrew Gallatin 	}
18736d914a32SAndrew Gallatin 	/* ran out of transmit slots */
1874a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
1875b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1876adae7080SAndrew Gallatin 		tx->stall++;
1877a82c2581SAndrew Gallatin 	}
1878b2fc195eSAndrew Gallatin }
1879b2fc195eSAndrew Gallatin 
1880b2fc195eSAndrew Gallatin static void
18816d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
1882b2fc195eSAndrew Gallatin {
18836d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
1884b2fc195eSAndrew Gallatin 
1885b2fc195eSAndrew Gallatin 
1886a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->tx_mtx);
18876d87a65dSAndrew Gallatin 	mxge_start_locked(sc);
1888a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->tx_mtx);
1889b2fc195eSAndrew Gallatin }
1890b2fc195eSAndrew Gallatin 
18915e7d8541SAndrew Gallatin /*
18925e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
18935e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
18945e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
18955e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
18965e7d8541SAndrew Gallatin  * in a burst
18975e7d8541SAndrew Gallatin  */
18985e7d8541SAndrew Gallatin static inline void
18995e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
19005e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
19015e7d8541SAndrew Gallatin {
19025e7d8541SAndrew Gallatin 	uint32_t low;
19035e7d8541SAndrew Gallatin 
19045e7d8541SAndrew Gallatin 	low = src->addr_low;
19055e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
1906a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
1907a1480dfbSAndrew Gallatin 	mb();
1908a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
19095e7d8541SAndrew Gallatin 	mb();
191040385a5fSAndrew Gallatin 	src->addr_low = low;
19115e7d8541SAndrew Gallatin 	dst->addr_low = low;
19125e7d8541SAndrew Gallatin 	mb();
19135e7d8541SAndrew Gallatin }
19145e7d8541SAndrew Gallatin 
1915b2fc195eSAndrew Gallatin static int
19166d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1917b2fc195eSAndrew Gallatin {
1918b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1919b2fc195eSAndrew Gallatin 	struct mbuf *m;
19206d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_small;
1921b2fc195eSAndrew Gallatin 	int cnt, err;
1922b2fc195eSAndrew Gallatin 
1923b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
1924b2fc195eSAndrew Gallatin 	if (m == NULL) {
1925b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1926b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1927b2fc195eSAndrew Gallatin 		goto done;
1928b2fc195eSAndrew Gallatin 	}
1929b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
1930b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1931b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1932b2fc195eSAndrew Gallatin 	if (err != 0) {
1933b2fc195eSAndrew Gallatin 		m_free(m);
1934b2fc195eSAndrew Gallatin 		goto done;
1935b2fc195eSAndrew Gallatin 	}
1936b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1937b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
19386d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1939b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
19406d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1941b2fc195eSAndrew Gallatin 
1942b2fc195eSAndrew Gallatin done:
1943adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
1944adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
1945b2fc195eSAndrew Gallatin 	return err;
1946b2fc195eSAndrew Gallatin }
1947b2fc195eSAndrew Gallatin 
1948b2fc195eSAndrew Gallatin static int
19496d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1950b2fc195eSAndrew Gallatin {
1951053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
1952b2fc195eSAndrew Gallatin 	struct mbuf *m;
19536d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_big;
1954053e637fSAndrew Gallatin 	int cnt, err, i;
1955b2fc195eSAndrew Gallatin 
1956053e637fSAndrew Gallatin 	m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
1957b2fc195eSAndrew Gallatin 	if (m == NULL) {
1958b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1959b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1960b2fc195eSAndrew Gallatin 		goto done;
1961b2fc195eSAndrew Gallatin 	}
1962053e637fSAndrew Gallatin 	m->m_len = rx->cl_size;
1963b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1964053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
1965b2fc195eSAndrew Gallatin 	if (err != 0) {
1966b2fc195eSAndrew Gallatin 		m_free(m);
1967b2fc195eSAndrew Gallatin 		goto done;
1968b2fc195eSAndrew Gallatin 	}
1969b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1970053e637fSAndrew Gallatin 
1971053e637fSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1972053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
1973053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
1974053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
1975053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
1976053e637fSAndrew Gallatin        }
1977053e637fSAndrew Gallatin 
1978b2fc195eSAndrew Gallatin 
1979b2fc195eSAndrew Gallatin done:
1980053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
1981b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
19825e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
19835e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
1984b2fc195eSAndrew Gallatin 		}
1985053e637fSAndrew Gallatin 		idx++;
1986053e637fSAndrew Gallatin 	}
1987b2fc195eSAndrew Gallatin 	return err;
1988b2fc195eSAndrew Gallatin }
1989b2fc195eSAndrew Gallatin 
19909b03b0f3SAndrew Gallatin /*
19919b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
19929b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
19939b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
19949b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
1995053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
1996053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
19979b03b0f3SAndrew Gallatin  */
19989b03b0f3SAndrew Gallatin 
1999053e637fSAndrew Gallatin static inline uint16_t
2000053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2001053e637fSAndrew Gallatin {
2002053e637fSAndrew Gallatin 	struct ether_header *eh;
2003053e637fSAndrew Gallatin 	struct ip *ip;
2004053e637fSAndrew Gallatin 	uint16_t c;
2005053e637fSAndrew Gallatin 
2006053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2007053e637fSAndrew Gallatin 
2008053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2009053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2010053e637fSAndrew Gallatin 		return 1;
2011053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2012053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2013053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2014053e637fSAndrew Gallatin 		return 1;
2015053e637fSAndrew Gallatin 
2016053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2017053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2018053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2019053e637fSAndrew Gallatin 	c ^= 0xffff;
2020053e637fSAndrew Gallatin 	return (c);
20215e7d8541SAndrew Gallatin }
2022053e637fSAndrew Gallatin 
2023c792928fSAndrew Gallatin static void
2024c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2025c792928fSAndrew Gallatin {
2026c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2027c792928fSAndrew Gallatin 	struct ether_header *eh;
2028c792928fSAndrew Gallatin 	uint32_t partial;
2029c792928fSAndrew Gallatin 
2030c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2031c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2032c792928fSAndrew Gallatin 
2033c792928fSAndrew Gallatin 	/*
2034c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2035c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2036c792928fSAndrew Gallatin 	 * header.
2037c792928fSAndrew Gallatin 	 */
2038c792928fSAndrew Gallatin 
2039c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2040c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2041c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2042c792928fSAndrew Gallatin 	(*csum) += ~partial;
2043c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2044c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2045c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2046c792928fSAndrew Gallatin 
2047c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2048c792928fSAndrew Gallatin 	   later consumers expect this */
2049c792928fSAndrew Gallatin 	*csum = htons(*csum);
2050c792928fSAndrew Gallatin 
2051c792928fSAndrew Gallatin 	/* save the tag */
2052c792928fSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2053c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
2054c792928fSAndrew Gallatin 
2055c792928fSAndrew Gallatin 	/*
2056c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2057c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2058c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2059c792928fSAndrew Gallatin 	 * type field is already in place.
2060c792928fSAndrew Gallatin 	 */
2061c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2062c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2063c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2064c792928fSAndrew Gallatin }
2065c792928fSAndrew Gallatin 
20665e7d8541SAndrew Gallatin 
20675e7d8541SAndrew Gallatin static inline void
2068053e637fSAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, uint32_t len, uint32_t csum)
2069b2fc195eSAndrew Gallatin {
2070b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2071053e637fSAndrew Gallatin 	struct mbuf *m;
2072c792928fSAndrew Gallatin 	struct ether_header *eh;
20736d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
2074053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2075b2fc195eSAndrew Gallatin 	int idx;
2076053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2077b2fc195eSAndrew Gallatin 
2078b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
2079053e637fSAndrew Gallatin 	rx = &sc->rx_big;
2080b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2081053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2082b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2083b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2084b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
20856d87a65dSAndrew Gallatin 	if (mxge_get_buf_big(sc, rx->extra_map, idx)) {
2086053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2087053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2088053e637fSAndrew Gallatin 		return;
2089b2fc195eSAndrew Gallatin 	}
2090053e637fSAndrew Gallatin 
2091b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2092b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2093b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2094b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2095b2fc195eSAndrew Gallatin 
2096b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2097b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2098b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2099b2fc195eSAndrew Gallatin 
2100053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2101053e637fSAndrew Gallatin 	 * aligned */
21025e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2103b2fc195eSAndrew Gallatin 
2104053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2105053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
21069b03b0f3SAndrew Gallatin 	ifp->if_ipackets++;
2107c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2108c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2109c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2110c792928fSAndrew Gallatin 	}
2111b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2112053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
2113053e637fSAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum)))
2114b2fc195eSAndrew Gallatin 			return;
2115053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2116053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2117053e637fSAndrew Gallatin 		   checksum is good */
2118053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2119053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2120b2fc195eSAndrew Gallatin 	}
2121053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2122053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2123b2fc195eSAndrew Gallatin }
2124b2fc195eSAndrew Gallatin 
2125b2fc195eSAndrew Gallatin static inline void
21265e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum)
2127b2fc195eSAndrew Gallatin {
2128b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2129c792928fSAndrew Gallatin 	struct ether_header *eh;
2130b2fc195eSAndrew Gallatin 	struct mbuf *m;
21316d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
2132b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2133b2fc195eSAndrew Gallatin 	int idx;
2134053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2135b2fc195eSAndrew Gallatin 
2136b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
2137b2fc195eSAndrew Gallatin 	rx = &sc->rx_small;
2138b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2139b2fc195eSAndrew Gallatin 	rx->cnt++;
2140b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2141b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2142b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
21436d87a65dSAndrew Gallatin 	if (mxge_get_buf_small(sc, rx->extra_map, idx)) {
2144b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2145b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2146b2fc195eSAndrew Gallatin 		return;
2147b2fc195eSAndrew Gallatin 	}
2148b2fc195eSAndrew Gallatin 
2149b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2150b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2151b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2152b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2153b2fc195eSAndrew Gallatin 
2154b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2155b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2156b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2157b2fc195eSAndrew Gallatin 
2158b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2159b2fc195eSAndrew Gallatin 	 * aligned */
21605e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2161b2fc195eSAndrew Gallatin 
21629b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
21639b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
21649b03b0f3SAndrew Gallatin 	ifp->if_ipackets++;
2165c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2166c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2167c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2168c792928fSAndrew Gallatin 	}
2169b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2170053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
2171053e637fSAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum)))
2172053e637fSAndrew Gallatin 			return;
2173053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2174053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2175053e637fSAndrew Gallatin 		   checksum is good */
2176053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2177053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2178053e637fSAndrew Gallatin 	}
2179b2fc195eSAndrew Gallatin 
2180b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2181b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2182b2fc195eSAndrew Gallatin }
2183b2fc195eSAndrew Gallatin 
2184b2fc195eSAndrew Gallatin static inline void
21855e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc)
21865e7d8541SAndrew Gallatin {
21875e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
2188053e637fSAndrew Gallatin 	struct lro_entry *lro;
21895e7d8541SAndrew Gallatin 	int limit = 0;
21905e7d8541SAndrew Gallatin 	uint16_t length;
21915e7d8541SAndrew Gallatin 	uint16_t checksum;
21925e7d8541SAndrew Gallatin 
21935e7d8541SAndrew Gallatin 
21945e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
21955e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
21965e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2197053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2198b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
21995e7d8541SAndrew Gallatin 			mxge_rx_done_small(sc, length, checksum);
22005e7d8541SAndrew Gallatin 		else
22015e7d8541SAndrew Gallatin 			mxge_rx_done_big(sc, length, checksum);
22025e7d8541SAndrew Gallatin 		rx_done->cnt++;
2203adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
22045e7d8541SAndrew Gallatin 
22055e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2206adae7080SAndrew Gallatin 		if (__predict_false(++limit > 2 * rx_done->mask))
22075e7d8541SAndrew Gallatin 			break;
2208053e637fSAndrew Gallatin 	}
2209053e637fSAndrew Gallatin 	while(!SLIST_EMPTY(&sc->lro_active)) {
2210053e637fSAndrew Gallatin 		lro = SLIST_FIRST(&sc->lro_active);
2211053e637fSAndrew Gallatin 		SLIST_REMOVE_HEAD(&sc->lro_active, next);
2212053e637fSAndrew Gallatin 		mxge_lro_flush(sc, lro);
22135e7d8541SAndrew Gallatin 	}
22145e7d8541SAndrew Gallatin }
22155e7d8541SAndrew Gallatin 
22165e7d8541SAndrew Gallatin 
22175e7d8541SAndrew Gallatin static inline void
22186d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx)
2219b2fc195eSAndrew Gallatin {
2220b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
22216d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
2222b2fc195eSAndrew Gallatin 	struct mbuf *m;
2223b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
22245e7d8541SAndrew Gallatin 	int idx, limit;
2225b2fc195eSAndrew Gallatin 
22265e7d8541SAndrew Gallatin 	limit = 0;
2227b2fc195eSAndrew Gallatin 	tx = &sc->tx;
2228b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
22295e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2230b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2231b2fc195eSAndrew Gallatin 		tx->done++;
2232b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2233b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2234b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2235b2fc195eSAndrew Gallatin 		if (m != NULL) {
2236b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2237b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2238b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2239b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2240b2fc195eSAndrew Gallatin 			m_freem(m);
2241b2fc195eSAndrew Gallatin 		}
22425e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
22435e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
22445e7d8541SAndrew Gallatin 			tx->pkt_done++;
22455e7d8541SAndrew Gallatin 		}
22465e7d8541SAndrew Gallatin 		/* limit potential for livelock by only handling
22475e7d8541SAndrew Gallatin 		   2 full tx rings per call */
22485e7d8541SAndrew Gallatin 		if (__predict_false(++limit >  2 * tx->mask))
22495e7d8541SAndrew Gallatin 			break;
2250b2fc195eSAndrew Gallatin 	}
2251b2fc195eSAndrew Gallatin 
2252b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2253b2fc195eSAndrew Gallatin            its OK to send packets */
2254b2fc195eSAndrew Gallatin 
2255b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2256b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2257a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->tx_mtx);
2258b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2259a82c2581SAndrew Gallatin 		sc->tx.wake++;
22606d87a65dSAndrew Gallatin 		mxge_start_locked(sc);
2261a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->tx_mtx);
2262b2fc195eSAndrew Gallatin 	}
2263b2fc195eSAndrew Gallatin }
2264b2fc195eSAndrew Gallatin 
2265b2fc195eSAndrew Gallatin static void
22666d87a65dSAndrew Gallatin mxge_intr(void *arg)
2267b2fc195eSAndrew Gallatin {
22686d87a65dSAndrew Gallatin 	mxge_softc_t *sc = arg;
22695e7d8541SAndrew Gallatin 	mcp_irq_data_t *stats = sc->fw_stats;
22705e7d8541SAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
22715e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
22725e7d8541SAndrew Gallatin 	uint32_t send_done_count;
22735e7d8541SAndrew Gallatin 	uint8_t valid;
2274b2fc195eSAndrew Gallatin 
2275b2fc195eSAndrew Gallatin 
22765e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
22775e7d8541SAndrew Gallatin 	if (!stats->valid) {
22785e7d8541SAndrew Gallatin 		return;
2279b2fc195eSAndrew Gallatin 	}
22805e7d8541SAndrew Gallatin 	valid = stats->valid;
2281b2fc195eSAndrew Gallatin 
2282dc8731d4SAndrew Gallatin 	if (!sc->msi_enabled) {
22835e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
22845e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
22855e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
22865e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
22875e7d8541SAndrew Gallatin 			stats->valid = 0;
2288dc8731d4SAndrew Gallatin 	} else {
2289dc8731d4SAndrew Gallatin 		stats->valid = 0;
2290dc8731d4SAndrew Gallatin 	}
2291dc8731d4SAndrew Gallatin 
2292dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
22935e7d8541SAndrew Gallatin 	do {
22945e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
22955e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
22965e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
22975e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
22985e7d8541SAndrew Gallatin 			mxge_tx_done(sc, (int)send_done_count);
22995e7d8541SAndrew Gallatin 			mxge_clean_rx_done(sc);
23005e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2301b2fc195eSAndrew Gallatin 		}
23025e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2303b2fc195eSAndrew Gallatin 
23045e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
23055e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
23065e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2307b2fc195eSAndrew Gallatin 			if (sc->link_state) {
23085e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
23095e7d8541SAndrew Gallatin 				if (mxge_verbose)
23105e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2311b2fc195eSAndrew Gallatin 			} else {
23125e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
23135e7d8541SAndrew Gallatin 				if (mxge_verbose)
23145e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2315b2fc195eSAndrew Gallatin 			}
2316b2fc195eSAndrew Gallatin 		}
2317b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
2318b2fc195eSAndrew Gallatin 		    be32toh(sc->fw_stats->rdma_tags_available)) {
2319b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
2320b2fc195eSAndrew Gallatin 				be32toh(sc->fw_stats->rdma_tags_available);
23215e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
23225e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
23235e7d8541SAndrew Gallatin 		}
23245e7d8541SAndrew Gallatin 		sc->down_cnt += stats->link_down;
2325b2fc195eSAndrew Gallatin 	}
2326b2fc195eSAndrew Gallatin 
23275e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
23285e7d8541SAndrew Gallatin 	if (valid & 0x1)
23295e7d8541SAndrew Gallatin 	    *sc->irq_claim = be32toh(3);
23305e7d8541SAndrew Gallatin 	*(sc->irq_claim + 1) = be32toh(3);
2331b2fc195eSAndrew Gallatin }
2332b2fc195eSAndrew Gallatin 
2333b2fc195eSAndrew Gallatin static void
23346d87a65dSAndrew Gallatin mxge_init(void *arg)
2335b2fc195eSAndrew Gallatin {
2336b2fc195eSAndrew Gallatin }
2337b2fc195eSAndrew Gallatin 
2338b2fc195eSAndrew Gallatin 
2339b2fc195eSAndrew Gallatin 
2340b2fc195eSAndrew Gallatin static void
23416d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2342b2fc195eSAndrew Gallatin {
2343b2fc195eSAndrew Gallatin 	int i;
2344b2fc195eSAndrew Gallatin 
2345b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2346b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2347b2fc195eSAndrew Gallatin 			continue;
2348b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2349b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2350b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2351b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2352b2fc195eSAndrew Gallatin 	}
2353b2fc195eSAndrew Gallatin 
23549b03b0f3SAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
23559b03b0f3SAndrew Gallatin 		if (sc->rx_small.info[i].m == NULL)
2356b2fc195eSAndrew Gallatin 			continue;
23579b03b0f3SAndrew Gallatin 		bus_dmamap_unload(sc->rx_small.dmat,
23589b03b0f3SAndrew Gallatin 				  sc->rx_small.info[i].map);
23599b03b0f3SAndrew Gallatin 		m_freem(sc->rx_small.info[i].m);
23609b03b0f3SAndrew Gallatin 		sc->rx_small.info[i].m = NULL;
2361b2fc195eSAndrew Gallatin 	}
2362b2fc195eSAndrew Gallatin 
2363b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2364dce01b9bSAndrew Gallatin 		sc->tx.info[i].flag = 0;
2365b2fc195eSAndrew Gallatin 		if (sc->tx.info[i].m == NULL)
2366b2fc195eSAndrew Gallatin 			continue;
2367b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->tx.dmat,
2368b2fc195eSAndrew Gallatin 				  sc->tx.info[i].map);
2369b2fc195eSAndrew Gallatin 		m_freem(sc->tx.info[i].m);
2370b2fc195eSAndrew Gallatin 		sc->tx.info[i].m = NULL;
2371b2fc195eSAndrew Gallatin 	}
2372b2fc195eSAndrew Gallatin }
2373b2fc195eSAndrew Gallatin 
2374b2fc195eSAndrew Gallatin static void
23756d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2376b2fc195eSAndrew Gallatin {
2377b2fc195eSAndrew Gallatin 	int i;
2378b2fc195eSAndrew Gallatin 
2379adae7080SAndrew Gallatin 	if (sc->rx_done.entry != NULL)
2380adae7080SAndrew Gallatin 		mxge_dma_free(&sc->rx_done.dma);
2381adae7080SAndrew Gallatin 	sc->rx_done.entry = NULL;
2382aed8e389SAndrew Gallatin 	if (sc->tx.req_bytes != NULL)
2383b2fc195eSAndrew Gallatin 		free(sc->tx.req_bytes, M_DEVBUF);
2384aed8e389SAndrew Gallatin 	if (sc->tx.seg_list != NULL)
2385aed8e389SAndrew Gallatin 		free(sc->tx.seg_list, M_DEVBUF);
2386b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow != NULL)
2387b2fc195eSAndrew Gallatin 		free(sc->rx_small.shadow, M_DEVBUF);
2388b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow != NULL)
2389b2fc195eSAndrew Gallatin 		free(sc->rx_big.shadow, M_DEVBUF);
2390b2fc195eSAndrew Gallatin 	if (sc->tx.info != NULL) {
2391c2657176SAndrew Gallatin 		if (sc->tx.dmat != NULL) {
2392b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->tx.mask; i++) {
2393b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->tx.dmat,
2394b2fc195eSAndrew Gallatin 						   sc->tx.info[i].map);
2395b2fc195eSAndrew Gallatin 			}
2396c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->tx.dmat);
2397c2657176SAndrew Gallatin 		}
2398b2fc195eSAndrew Gallatin 		free(sc->tx.info, M_DEVBUF);
2399b2fc195eSAndrew Gallatin 	}
2400b2fc195eSAndrew Gallatin 	if (sc->rx_small.info != NULL) {
2401c2657176SAndrew Gallatin 		if (sc->rx_small.dmat != NULL) {
2402b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_small.mask; i++) {
2403b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_small.dmat,
2404b2fc195eSAndrew Gallatin 						   sc->rx_small.info[i].map);
2405b2fc195eSAndrew Gallatin 			}
2406c2657176SAndrew Gallatin 			bus_dmamap_destroy(sc->rx_small.dmat,
2407c2657176SAndrew Gallatin 					   sc->rx_small.extra_map);
2408c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_small.dmat);
2409c2657176SAndrew Gallatin 		}
2410b2fc195eSAndrew Gallatin 		free(sc->rx_small.info, M_DEVBUF);
2411b2fc195eSAndrew Gallatin 	}
2412b2fc195eSAndrew Gallatin 	if (sc->rx_big.info != NULL) {
2413c2657176SAndrew Gallatin 		if (sc->rx_big.dmat != NULL) {
2414b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_big.mask; i++) {
2415b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_big.dmat,
2416b2fc195eSAndrew Gallatin 						   sc->rx_big.info[i].map);
2417b2fc195eSAndrew Gallatin 			}
2418b2fc195eSAndrew Gallatin 			bus_dmamap_destroy(sc->rx_big.dmat,
2419b2fc195eSAndrew Gallatin 					   sc->rx_big.extra_map);
2420b2fc195eSAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_big.dmat);
2421b2fc195eSAndrew Gallatin 		}
2422c2657176SAndrew Gallatin 		free(sc->rx_big.info, M_DEVBUF);
2423c2657176SAndrew Gallatin 	}
2424c2657176SAndrew Gallatin }
2425b2fc195eSAndrew Gallatin 
2426b2fc195eSAndrew Gallatin static int
24276d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
2428b2fc195eSAndrew Gallatin {
24296d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2430b2fc195eSAndrew Gallatin 	int tx_ring_size, rx_ring_size;
2431b2fc195eSAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
2432b2fc195eSAndrew Gallatin 	int i, err;
2433b2fc195eSAndrew Gallatin 	unsigned long bytes;
2434b2fc195eSAndrew Gallatin 
2435b2fc195eSAndrew Gallatin 	/* get ring sizes */
24365e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
2437b2fc195eSAndrew Gallatin 	tx_ring_size = cmd.data0;
24385e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
2439b2fc195eSAndrew Gallatin 	if (err != 0) {
2440b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Cannot determine ring sizes\n");
2441b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2442b2fc195eSAndrew Gallatin 	}
2443b2fc195eSAndrew Gallatin 
2444b2fc195eSAndrew Gallatin 	rx_ring_size = cmd.data0;
2445b2fc195eSAndrew Gallatin 
2446b2fc195eSAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
2447b2fc195eSAndrew Gallatin 	rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t);
244876bb9c5eSAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
2449a82c2581SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
245076bb9c5eSAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
2451b2fc195eSAndrew Gallatin 
2452b2fc195eSAndrew Gallatin 	sc->tx.mask = tx_ring_entries - 1;
2453adae7080SAndrew Gallatin 	sc->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
2454b2fc195eSAndrew Gallatin 	sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1;
2455adae7080SAndrew Gallatin 	sc->rx_done.mask = (2 * rx_ring_entries) - 1;
2456b2fc195eSAndrew Gallatin 
2457b2fc195eSAndrew Gallatin 	err = ENOMEM;
2458b2fc195eSAndrew Gallatin 
2459adae7080SAndrew Gallatin 	/* allocate interrupt queues */
2460adae7080SAndrew Gallatin 	bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry);
2461adae7080SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096);
2462adae7080SAndrew Gallatin 	if (err != 0)
2463adae7080SAndrew Gallatin 		goto abort_with_nothing;
2464adae7080SAndrew Gallatin 	sc->rx_done.entry = sc->rx_done.dma.addr;
2465adae7080SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
2466adae7080SAndrew Gallatin 
2467b2fc195eSAndrew Gallatin 	/* allocate the tx request copy block */
2468b2fc195eSAndrew Gallatin 	bytes = 8 +
2469adae7080SAndrew Gallatin 		sizeof (*sc->tx.req_list) * (sc->tx.max_desc + 4);
2470b2fc195eSAndrew Gallatin 	sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
2471b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes == NULL)
2472adae7080SAndrew Gallatin 		goto abort_with_alloc;
2473b2fc195eSAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
2474b2fc195eSAndrew Gallatin 	sc->tx.req_list = (mcp_kreq_ether_send_t *)
2475b2fc195eSAndrew Gallatin 		((unsigned long)(sc->tx.req_bytes + 7) & ~7UL);
2476b2fc195eSAndrew Gallatin 
2477aed8e389SAndrew Gallatin 	/* allocate the tx busdma segment list */
2478adae7080SAndrew Gallatin 	bytes = sizeof (*sc->tx.seg_list) * sc->tx.max_desc;
2479aed8e389SAndrew Gallatin 	sc->tx.seg_list = (bus_dma_segment_t *)
2480aed8e389SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
2481aed8e389SAndrew Gallatin 	if (sc->tx.seg_list == NULL)
2482aed8e389SAndrew Gallatin 		goto abort_with_alloc;
2483aed8e389SAndrew Gallatin 
2484b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
2485b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow);
2486b2fc195eSAndrew Gallatin 	sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2487b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow == NULL)
2488b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2489b2fc195eSAndrew Gallatin 
2490b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow);
2491b2fc195eSAndrew Gallatin 	sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2492b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow == NULL)
2493b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2494b2fc195eSAndrew Gallatin 
2495b2fc195eSAndrew Gallatin 	/* allocate the host info rings */
2496b2fc195eSAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*sc->tx.info);
2497b2fc195eSAndrew Gallatin 	sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2498b2fc195eSAndrew Gallatin 	if (sc->tx.info == NULL)
2499b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2500b2fc195eSAndrew Gallatin 
2501b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.info);
2502b2fc195eSAndrew Gallatin 	sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2503b2fc195eSAndrew Gallatin 	if (sc->rx_small.info == NULL)
2504b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2505b2fc195eSAndrew Gallatin 
2506b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.info);
2507b2fc195eSAndrew Gallatin 	sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2508b2fc195eSAndrew Gallatin 	if (sc->rx_big.info == NULL)
2509b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2510b2fc195eSAndrew Gallatin 
2511b2fc195eSAndrew Gallatin 	/* allocate the busdma resources */
2512b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2513b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2514b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* boundary */
2515b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2516b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2517b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2518aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
2519adae7080SAndrew Gallatin 				 sc->tx.max_desc - 2,	/* num segs */
2520b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* maxsegsize */
2521b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2522b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2523b2fc195eSAndrew Gallatin 				 &sc->tx.dmat);		/* tag */
2524b2fc195eSAndrew Gallatin 
2525b2fc195eSAndrew Gallatin 	if (err != 0) {
2526b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
2527b2fc195eSAndrew Gallatin 			      err);
2528b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2529b2fc195eSAndrew Gallatin 	}
2530b2fc195eSAndrew Gallatin 
2531b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2532b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2533b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2534b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2535b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2536b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2537b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2538b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2539b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2540b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2541b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2542b2fc195eSAndrew Gallatin 				 &sc->rx_small.dmat);	/* tag */
2543b2fc195eSAndrew Gallatin 	if (err != 0) {
2544b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2545b2fc195eSAndrew Gallatin 			      err);
2546b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2547b2fc195eSAndrew Gallatin 	}
2548b2fc195eSAndrew Gallatin 
2549b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2550b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2551b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2552b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2553b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2554b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2555053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
2556053e637fSAndrew Gallatin 				 3,			/* num segs */
2557b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2558b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2559b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2560b2fc195eSAndrew Gallatin 				 &sc->rx_big.dmat);	/* tag */
2561b2fc195eSAndrew Gallatin 	if (err != 0) {
2562b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2563b2fc195eSAndrew Gallatin 			      err);
2564b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2565b2fc195eSAndrew Gallatin 	}
2566b2fc195eSAndrew Gallatin 
2567b2fc195eSAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
2568b2fc195eSAndrew Gallatin 	   in each ring */
2569b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2570b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->tx.dmat, 0,
2571b2fc195eSAndrew Gallatin 					&sc->tx.info[i].map);
2572b2fc195eSAndrew Gallatin 		if (err != 0) {
2573b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
2574b2fc195eSAndrew Gallatin 			      err);
2575b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2576b2fc195eSAndrew Gallatin 		}
2577b2fc195eSAndrew Gallatin 	}
2578b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2579b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_small.dmat, 0,
2580b2fc195eSAndrew Gallatin 					&sc->rx_small.info[i].map);
2581b2fc195eSAndrew Gallatin 		if (err != 0) {
2582b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2583b2fc195eSAndrew Gallatin 				      err);
2584b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2585b2fc195eSAndrew Gallatin 		}
2586b2fc195eSAndrew Gallatin 	}
2587b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_small.dmat, 0,
2588b2fc195eSAndrew Gallatin 				&sc->rx_small.extra_map);
2589b2fc195eSAndrew Gallatin 	if (err != 0) {
2590b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2591b2fc195eSAndrew Gallatin 			      err);
2592b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2593b2fc195eSAndrew Gallatin 	}
2594b2fc195eSAndrew Gallatin 
2595b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2596b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_big.dmat, 0,
2597b2fc195eSAndrew Gallatin 					&sc->rx_big.info[i].map);
2598b2fc195eSAndrew Gallatin 		if (err != 0) {
2599b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2600b2fc195eSAndrew Gallatin 			      err);
2601b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2602b2fc195eSAndrew Gallatin 		}
2603b2fc195eSAndrew Gallatin 	}
2604b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_big.dmat, 0,
2605b2fc195eSAndrew Gallatin 				&sc->rx_big.extra_map);
2606b2fc195eSAndrew Gallatin 	if (err != 0) {
2607b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2608b2fc195eSAndrew Gallatin 			      err);
2609b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2610b2fc195eSAndrew Gallatin 	}
2611b2fc195eSAndrew Gallatin 	return 0;
2612b2fc195eSAndrew Gallatin 
2613b2fc195eSAndrew Gallatin abort_with_alloc:
26146d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2615b2fc195eSAndrew Gallatin 
2616b2fc195eSAndrew Gallatin abort_with_nothing:
2617b2fc195eSAndrew Gallatin 	return err;
2618b2fc195eSAndrew Gallatin }
2619b2fc195eSAndrew Gallatin 
2620053e637fSAndrew Gallatin static void
2621053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
2622053e637fSAndrew Gallatin {
2623c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
2624053e637fSAndrew Gallatin 
2625053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
2626053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
2627053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
2628053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
2629053e637fSAndrew Gallatin 		*nbufs = 1;
2630053e637fSAndrew Gallatin 		return;
2631053e637fSAndrew Gallatin 	}
2632053e637fSAndrew Gallatin 
2633053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
2634053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
2635053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
2636053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
2637053e637fSAndrew Gallatin 		*nbufs = 1;
2638053e637fSAndrew Gallatin 		return;
2639053e637fSAndrew Gallatin 	}
2640053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
2641053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
2642053e637fSAndrew Gallatin 	*big_buf_size = 4096;
2643053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
2644053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
2645053e637fSAndrew Gallatin 	if (*nbufs == 3)
2646053e637fSAndrew Gallatin 		*nbufs = 4;
2647053e637fSAndrew Gallatin }
2648053e637fSAndrew Gallatin 
2649b2fc195eSAndrew Gallatin static int
26506d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc)
2651b2fc195eSAndrew Gallatin {
26526d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2653053e637fSAndrew Gallatin 	int i, err, big_bytes;
2654b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
26550fa7f681SAndrew Gallatin 	bus_addr_t bus;
2656053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
2657b2fc195eSAndrew Gallatin 
2658053e637fSAndrew Gallatin 	SLIST_INIT(&sc->lro_free);
2659053e637fSAndrew Gallatin 	SLIST_INIT(&sc->lro_active);
2660053e637fSAndrew Gallatin 
2661053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
2662053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
2663053e637fSAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
2664053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
2665053e637fSAndrew Gallatin 			sc->lro_cnt = i;
2666053e637fSAndrew Gallatin 			break;
2667053e637fSAndrew Gallatin 		}
2668053e637fSAndrew Gallatin 		SLIST_INSERT_HEAD(&sc->lro_free, lro_entry, next);
2669053e637fSAndrew Gallatin 	}
2670b2fc195eSAndrew Gallatin 
26717d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
26727d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
26737d542e2dSAndrew Gallatin 
2674adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
2675b2fc195eSAndrew Gallatin 	if (err != 0) {
2676b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
2677b2fc195eSAndrew Gallatin 		return EIO;
2678b2fc195eSAndrew Gallatin 	}
2679b2fc195eSAndrew Gallatin 
2680053e637fSAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes,
2681053e637fSAndrew Gallatin 			   &sc->rx_big.cl_size, &sc->rx_big.nbufs);
2682b2fc195eSAndrew Gallatin 
2683053e637fSAndrew Gallatin 	cmd.data0 = sc->rx_big.nbufs;
2684053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
2685053e637fSAndrew Gallatin 			    &cmd);
2686053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
2687053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
2688053e637fSAndrew Gallatin 	if (err && sc->rx_big.nbufs > 1) {
2689053e637fSAndrew Gallatin 		device_printf(sc->dev,
2690053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
2691053e637fSAndrew Gallatin 			      sc->rx_big.nbufs);
2692053e637fSAndrew Gallatin 		return EIO;
2693053e637fSAndrew Gallatin 	}
2694b2fc195eSAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
2695b2fc195eSAndrew Gallatin 
26965e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
2697b2fc195eSAndrew Gallatin 	sc->tx.lanai =
2698b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
26996d87a65dSAndrew Gallatin 	err |= mxge_send_cmd(sc,
27005e7d8541SAndrew Gallatin 				 MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
2701b2fc195eSAndrew Gallatin 	sc->rx_small.lanai =
2702b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
27035e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
2704b2fc195eSAndrew Gallatin 	sc->rx_big.lanai =
2705b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
2706b2fc195eSAndrew Gallatin 
2707b2fc195eSAndrew Gallatin 	if (err != 0) {
2708b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
2709b2fc195eSAndrew Gallatin 			      "failed to get ring sizes or locations\n");
2710a98d6cd7SAndrew Gallatin 		return EIO;
2711b2fc195eSAndrew Gallatin 	}
2712b2fc195eSAndrew Gallatin 
2713b2fc195eSAndrew Gallatin 	/* stock receive rings */
2714b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2715b2fc195eSAndrew Gallatin 		map = sc->rx_small.info[i].map;
27166d87a65dSAndrew Gallatin 		err = mxge_get_buf_small(sc, map, i);
2717b2fc195eSAndrew Gallatin 		if (err) {
2718b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
2719b2fc195eSAndrew Gallatin 				      i, sc->rx_small.mask + 1);
2720b2fc195eSAndrew Gallatin 			goto abort;
2721b2fc195eSAndrew Gallatin 		}
2722b2fc195eSAndrew Gallatin 	}
2723b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2724053e637fSAndrew Gallatin 		sc->rx_big.shadow[i].addr_low = 0xffffffff;
2725053e637fSAndrew Gallatin 		sc->rx_big.shadow[i].addr_high = 0xffffffff;
2726053e637fSAndrew Gallatin 	}
2727053e637fSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i += sc->rx_big.nbufs) {
2728b2fc195eSAndrew Gallatin 		map = sc->rx_big.info[i].map;
27296d87a65dSAndrew Gallatin 		err = mxge_get_buf_big(sc, map, i);
2730b2fc195eSAndrew Gallatin 		if (err) {
2731b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
2732b2fc195eSAndrew Gallatin 				      i, sc->rx_big.mask + 1);
2733b2fc195eSAndrew Gallatin 			goto abort;
2734b2fc195eSAndrew Gallatin 		}
2735b2fc195eSAndrew Gallatin 	}
2736b2fc195eSAndrew Gallatin 
2737b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
2738b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
2739b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
2740c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
27415e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
2742b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
27435e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
2744b2fc195eSAndrew Gallatin 			     &cmd);
2745053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
27465e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
27470fa7f681SAndrew Gallatin 
27480fa7f681SAndrew Gallatin 	if (err != 0) {
27490fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
27500fa7f681SAndrew Gallatin 		goto abort;
27510fa7f681SAndrew Gallatin 	}
27520fa7f681SAndrew Gallatin 
2753b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
27546d87a65dSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr);
27556d87a65dSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr);
27560fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
27570fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
27580fa7f681SAndrew Gallatin 
27590fa7f681SAndrew Gallatin 	if (err != 0) {
27600fa7f681SAndrew Gallatin 		bus = sc->fw_stats_dma.bus_addr;
27610fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
27620fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
27630fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
27640fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
27650fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
27660fa7f681SAndrew Gallatin 				    &cmd);
27670fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
27680fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
27690fa7f681SAndrew Gallatin 	} else {
27700fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
27710fa7f681SAndrew Gallatin 	}
2772b2fc195eSAndrew Gallatin 
2773b2fc195eSAndrew Gallatin 	if (err != 0) {
2774b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
2775b2fc195eSAndrew Gallatin 		goto abort;
2776b2fc195eSAndrew Gallatin 	}
2777b2fc195eSAndrew Gallatin 
2778b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
27795e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
2780b2fc195eSAndrew Gallatin 	if (err) {
2781b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
2782b2fc195eSAndrew Gallatin 		goto abort;
2783b2fc195eSAndrew Gallatin 	}
2784b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
2785b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2786b2fc195eSAndrew Gallatin 
2787b2fc195eSAndrew Gallatin 	return 0;
2788b2fc195eSAndrew Gallatin 
2789b2fc195eSAndrew Gallatin 
2790b2fc195eSAndrew Gallatin abort:
27916d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2792a98d6cd7SAndrew Gallatin 
2793b2fc195eSAndrew Gallatin 	return err;
2794b2fc195eSAndrew Gallatin }
2795b2fc195eSAndrew Gallatin 
2796b2fc195eSAndrew Gallatin static int
27976d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
2798b2fc195eSAndrew Gallatin {
2799053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
28006d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2801b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
2802b2fc195eSAndrew Gallatin 
2803b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2804b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
2805b2fc195eSAndrew Gallatin 	mb();
28065e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
2807b2fc195eSAndrew Gallatin 	if (err) {
2808b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
2809b2fc195eSAndrew Gallatin 	}
2810b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2811b2fc195eSAndrew Gallatin 		/* wait for down irq */
2812dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
2813b2fc195eSAndrew Gallatin 	}
2814b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2815b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
2816b2fc195eSAndrew Gallatin 	}
2817a98d6cd7SAndrew Gallatin 
28186d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2819a98d6cd7SAndrew Gallatin 
2820053e637fSAndrew Gallatin 	while (!SLIST_EMPTY(&sc->lro_free)) {
2821053e637fSAndrew Gallatin 		lro_entry = SLIST_FIRST(&sc->lro_free);
2822053e637fSAndrew Gallatin 		SLIST_REMOVE_HEAD(&sc->lro_free, next);
2823053e637fSAndrew Gallatin 	}
2824b2fc195eSAndrew Gallatin 	return 0;
2825b2fc195eSAndrew Gallatin }
2826b2fc195eSAndrew Gallatin 
2827dce01b9bSAndrew Gallatin static void
2828dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
2829dce01b9bSAndrew Gallatin {
2830dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2831dce01b9bSAndrew Gallatin 	int reg;
2832dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
2833dce01b9bSAndrew Gallatin 
2834dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
2835dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
2836dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
2837dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
2838dce01b9bSAndrew Gallatin 
2839dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
2840dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
2841dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
2842dce01b9bSAndrew Gallatin 	}
2843dce01b9bSAndrew Gallatin 
2844dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
2845dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
2846dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2847dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
2848dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2849dce01b9bSAndrew Gallatin }
2850dce01b9bSAndrew Gallatin 
2851dce01b9bSAndrew Gallatin static uint32_t
2852dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
2853dce01b9bSAndrew Gallatin {
2854dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2855dce01b9bSAndrew Gallatin 	uint32_t vs;
2856dce01b9bSAndrew Gallatin 
2857dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
2858dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
2859dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
2860dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
2861dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
2862dce01b9bSAndrew Gallatin 	}
2863dce01b9bSAndrew Gallatin 	/* enable read32 mode */
2864dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
2865dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
2866dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
2867dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
2868dce01b9bSAndrew Gallatin }
2869dce01b9bSAndrew Gallatin 
2870dce01b9bSAndrew Gallatin static void
2871dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
2872dce01b9bSAndrew Gallatin {
2873dce01b9bSAndrew Gallatin 	int err;
2874dce01b9bSAndrew Gallatin 	uint32_t reboot;
2875dce01b9bSAndrew Gallatin 	uint16_t cmd;
2876dce01b9bSAndrew Gallatin 
2877dce01b9bSAndrew Gallatin 	err = ENXIO;
2878dce01b9bSAndrew Gallatin 
2879dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
2880dce01b9bSAndrew Gallatin 
2881dce01b9bSAndrew Gallatin 	/*
2882dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
2883dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
2884dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
2885dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
2886dce01b9bSAndrew Gallatin 	 * again
2887dce01b9bSAndrew Gallatin 	 */
2888dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2889dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
2890dce01b9bSAndrew Gallatin 		/*
2891dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
2892dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
2893dce01b9bSAndrew Gallatin 		 * back, then give up
2894dce01b9bSAndrew Gallatin 		 */
2895dce01b9bSAndrew Gallatin 		DELAY(1000*100);
2896dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2897dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
2898dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
2899dce01b9bSAndrew Gallatin 			goto abort;
2900dce01b9bSAndrew Gallatin 		}
2901dce01b9bSAndrew Gallatin 	}
2902dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
2903dce01b9bSAndrew Gallatin 		/* print the reboot status */
2904dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
2905dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
2906dce01b9bSAndrew Gallatin 			      reboot);
2907dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
2908dce01b9bSAndrew Gallatin 
2909dce01b9bSAndrew Gallatin 		/* XXXX waiting for pci_cfg_restore() to be exported */
2910dce01b9bSAndrew Gallatin 		goto abort; /* just abort for now */
2911dce01b9bSAndrew Gallatin 
2912dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
2913dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
2914dce01b9bSAndrew Gallatin 	} else {
2915dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC did not reboot, ring state:\n");
2916dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "tx.req=%d tx.done=%d\n",
2917dce01b9bSAndrew Gallatin 			      sc->tx.req, sc->tx.done);
2918dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
2919dce01b9bSAndrew Gallatin 			      sc->tx.pkt_done,
2920dce01b9bSAndrew Gallatin 			      be32toh(sc->fw_stats->send_done_count));
2921dce01b9bSAndrew Gallatin 	}
2922dce01b9bSAndrew Gallatin 
2923dce01b9bSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
2924dce01b9bSAndrew Gallatin 		mxge_close(sc);
2925dce01b9bSAndrew Gallatin 		err = mxge_open(sc);
2926dce01b9bSAndrew Gallatin 	}
2927dce01b9bSAndrew Gallatin 
2928dce01b9bSAndrew Gallatin abort:
2929dce01b9bSAndrew Gallatin 	/*
2930dce01b9bSAndrew Gallatin 	 * stop the watchdog if the nic is dead, to avoid spamming the
2931dce01b9bSAndrew Gallatin 	 * console
2932dce01b9bSAndrew Gallatin 	 */
2933dce01b9bSAndrew Gallatin 	if (err != 0) {
2934dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
2935dce01b9bSAndrew Gallatin 	}
2936dce01b9bSAndrew Gallatin }
2937dce01b9bSAndrew Gallatin 
2938dce01b9bSAndrew Gallatin static void
2939dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
2940dce01b9bSAndrew Gallatin {
2941dce01b9bSAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
2942dce01b9bSAndrew Gallatin 
2943dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
2944dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
2945dce01b9bSAndrew Gallatin 	if (tx->req != tx->done &&
2946dce01b9bSAndrew Gallatin 	    tx->watchdog_req != tx->watchdog_done &&
2947dce01b9bSAndrew Gallatin 	    tx->done == tx->watchdog_done)
2948dce01b9bSAndrew Gallatin 		mxge_watchdog_reset(sc);
2949dce01b9bSAndrew Gallatin 
2950dce01b9bSAndrew Gallatin 	tx->watchdog_req = tx->req;
2951dce01b9bSAndrew Gallatin 	tx->watchdog_done = tx->done;
2952dce01b9bSAndrew Gallatin }
2953dce01b9bSAndrew Gallatin 
2954dce01b9bSAndrew Gallatin static void
2955dce01b9bSAndrew Gallatin mxge_tick(void *arg)
2956dce01b9bSAndrew Gallatin {
2957dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
2958dce01b9bSAndrew Gallatin 
2959dce01b9bSAndrew Gallatin 
2960dce01b9bSAndrew Gallatin 	/* Synchronize with possible callout reset/stop. */
2961dce01b9bSAndrew Gallatin 	if (callout_pending(&sc->co_hdl) ||
2962dce01b9bSAndrew Gallatin 	    !callout_active(&sc->co_hdl)) {
2963dce01b9bSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2964dce01b9bSAndrew Gallatin 		return;
2965dce01b9bSAndrew Gallatin 	}
2966dce01b9bSAndrew Gallatin 
2967dce01b9bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
2968dce01b9bSAndrew Gallatin 	mxge_watchdog(sc);
2969dce01b9bSAndrew Gallatin }
2970b2fc195eSAndrew Gallatin 
2971b2fc195eSAndrew Gallatin static int
29726d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
2973b2fc195eSAndrew Gallatin {
2974b2fc195eSAndrew Gallatin 	return EINVAL;
2975b2fc195eSAndrew Gallatin }
2976b2fc195eSAndrew Gallatin 
2977b2fc195eSAndrew Gallatin static int
29786d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
2979b2fc195eSAndrew Gallatin {
2980b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
2981b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
2982b2fc195eSAndrew Gallatin 	int err = 0;
2983b2fc195eSAndrew Gallatin 
2984b2fc195eSAndrew Gallatin 
2985c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
2986053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
2987b2fc195eSAndrew Gallatin 		return EINVAL;
2988a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
2989b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
2990b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
2991b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
2992dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
29936d87a65dSAndrew Gallatin 		mxge_close(sc);
29946d87a65dSAndrew Gallatin 		err = mxge_open(sc);
2995b2fc195eSAndrew Gallatin 		if (err != 0) {
2996b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
29976d87a65dSAndrew Gallatin 			mxge_close(sc);
29986d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
2999b2fc195eSAndrew Gallatin 		}
3000dce01b9bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3001b2fc195eSAndrew Gallatin 	}
3002a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3003b2fc195eSAndrew Gallatin 	return err;
3004b2fc195eSAndrew Gallatin }
3005b2fc195eSAndrew Gallatin 
3006b2fc195eSAndrew Gallatin static void
30076d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
3008b2fc195eSAndrew Gallatin {
30096d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3010b2fc195eSAndrew Gallatin 
3011b2fc195eSAndrew Gallatin 
3012b2fc195eSAndrew Gallatin 	if (sc == NULL)
3013b2fc195eSAndrew Gallatin 		return;
3014b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3015b2fc195eSAndrew Gallatin 	ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0;
3016b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
3017b2fc195eSAndrew Gallatin 	ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0;
3018b2fc195eSAndrew Gallatin }
3019b2fc195eSAndrew Gallatin 
3020b2fc195eSAndrew Gallatin static int
30216d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
3022b2fc195eSAndrew Gallatin {
30236d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3024b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
3025b2fc195eSAndrew Gallatin 	int err, mask;
3026b2fc195eSAndrew Gallatin 
3027b2fc195eSAndrew Gallatin 	err = 0;
3028b2fc195eSAndrew Gallatin 	switch (command) {
3029b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
3030b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
3031b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
3032b2fc195eSAndrew Gallatin 		break;
3033b2fc195eSAndrew Gallatin 
3034b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
30356d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
3036b2fc195eSAndrew Gallatin 		break;
3037b2fc195eSAndrew Gallatin 
3038b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
3039a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3040b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
3041dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
30426d87a65dSAndrew Gallatin 				err = mxge_open(sc);
3043dce01b9bSAndrew Gallatin 				callout_reset(&sc->co_hdl, mxge_ticks,
3044dce01b9bSAndrew Gallatin 					      mxge_tick, sc);
3045dce01b9bSAndrew Gallatin 			} else {
30460fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
30470fa7f681SAndrew Gallatin 				   flag chages */
30480fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
30490fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
30500fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
30510fa7f681SAndrew Gallatin 			}
3052b2fc195eSAndrew Gallatin 		} else {
3053dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
30546d87a65dSAndrew Gallatin 				mxge_close(sc);
3055dce01b9bSAndrew Gallatin 				callout_stop(&sc->co_hdl);
3056dce01b9bSAndrew Gallatin 			}
3057b2fc195eSAndrew Gallatin 		}
3058a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3059b2fc195eSAndrew Gallatin 		break;
3060b2fc195eSAndrew Gallatin 
3061b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
3062b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
3063a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
30640fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
3065a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3066b2fc195eSAndrew Gallatin 		break;
3067b2fc195eSAndrew Gallatin 
3068b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
3069a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3070b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
3071b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
3072b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
3073aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
3074aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3075aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3076b2fc195eSAndrew Gallatin 			} else {
3077b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3078b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3079b2fc195eSAndrew Gallatin 			}
3080b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3081b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
3082b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
30835e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
3084b2fc195eSAndrew Gallatin 			} else {
3085b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
30865e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
3087b2fc195eSAndrew Gallatin 			}
3088b2fc195eSAndrew Gallatin 		}
3089aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
3090aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
3091aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
3092aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
3093aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
3094aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
3095aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
3096aed8e389SAndrew Gallatin 			} else {
3097aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
3098aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
3099aed8e389SAndrew Gallatin 				err = EINVAL;
3100aed8e389SAndrew Gallatin 			}
3101aed8e389SAndrew Gallatin 		}
3102c792928fSAndrew Gallatin 
3103c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
3104c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
3105a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3106c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
3107c792928fSAndrew Gallatin 
3108b2fc195eSAndrew Gallatin 		break;
3109b2fc195eSAndrew Gallatin 
3110b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
3111b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
3112b2fc195eSAndrew Gallatin 				    &sc->media, command);
3113b2fc195eSAndrew Gallatin                 break;
3114b2fc195eSAndrew Gallatin 
3115b2fc195eSAndrew Gallatin 	default:
3116b2fc195eSAndrew Gallatin 		err = ENOTTY;
3117b2fc195eSAndrew Gallatin         }
3118b2fc195eSAndrew Gallatin 	return err;
3119b2fc195eSAndrew Gallatin }
3120b2fc195eSAndrew Gallatin 
3121b2fc195eSAndrew Gallatin static void
31226d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
3123b2fc195eSAndrew Gallatin {
3124b2fc195eSAndrew Gallatin 
31256d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
31266d87a65dSAndrew Gallatin 			  &mxge_flow_control);
31276d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
31286d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
31296d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
31306d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
3131d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
3132d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
31335e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
31345e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
31355e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
31365e7d8541SAndrew Gallatin 			  &mxge_verbose);
3137dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
3138053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
3139b2fc195eSAndrew Gallatin 
31405e7d8541SAndrew Gallatin 	if (bootverbose)
31415e7d8541SAndrew Gallatin 		mxge_verbose = 1;
31426d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
31436d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
3144dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
3145dce01b9bSAndrew Gallatin 		mxge_ticks = hz;
31466d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
3147053e637fSAndrew Gallatin 
3148b2fc195eSAndrew Gallatin }
3149b2fc195eSAndrew Gallatin 
3150b2fc195eSAndrew Gallatin static int
31516d87a65dSAndrew Gallatin mxge_attach(device_t dev)
3152b2fc195eSAndrew Gallatin {
31536d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
3154b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
3155dce01b9bSAndrew Gallatin 	int count, rid, err;
3156b2fc195eSAndrew Gallatin 
3157b2fc195eSAndrew Gallatin 	sc->dev = dev;
31586d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
3159b2fc195eSAndrew Gallatin 
3160b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
3161b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3162b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3163b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3164b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3165b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3166aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
31675e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
3168b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
3169b2fc195eSAndrew Gallatin 				 0,			/* flags */
3170b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
3171b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
3172b2fc195eSAndrew Gallatin 
3173b2fc195eSAndrew Gallatin 	if (err != 0) {
3174b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
3175b2fc195eSAndrew Gallatin 			      err);
3176b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
3177b2fc195eSAndrew Gallatin 	}
3178b2fc195eSAndrew Gallatin 
3179b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
3180b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
3181b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
3182b2fc195eSAndrew Gallatin 		err = ENOSPC;
3183b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
3184b2fc195eSAndrew Gallatin 	}
3185a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
3186a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
3187a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
3188a98d6cd7SAndrew Gallatin 	snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx",
3189a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
3190a98d6cd7SAndrew Gallatin 	mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF);
3191a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
3192a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
3193a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
3194b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
3195b2fc195eSAndrew Gallatin 
3196dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
3197d91b1b49SAndrew Gallatin 
3198dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
3199b2fc195eSAndrew Gallatin 
3200b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
3201b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
3202b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
3203b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
3204b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
3205b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
3206b2fc195eSAndrew Gallatin 		err = ENXIO;
3207b2fc195eSAndrew Gallatin 		goto abort_with_lock;
3208b2fc195eSAndrew Gallatin 	}
3209b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
3210b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
3211b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
3212b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
3213b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
3214b2fc195eSAndrew Gallatin 		err = ENXIO;
3215b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3216b2fc195eSAndrew Gallatin 	}
3217b2fc195eSAndrew Gallatin 
3218b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
3219b2fc195eSAndrew Gallatin 	   lanai SRAM */
32206d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
3221b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
3222b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
32236d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
3224b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
32256d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
32266d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
3227b2fc195eSAndrew Gallatin 	if (err != 0)
3228b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3229b2fc195eSAndrew Gallatin 
3230b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
32316d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
3232b2fc195eSAndrew Gallatin 
3233b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
32346d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
32356d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
3236b2fc195eSAndrew Gallatin 	if (err != 0)
3237b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3238b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
32396d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
3240b2fc195eSAndrew Gallatin 	if (err != 0)
3241b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
3242b2fc195eSAndrew Gallatin 
32436d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->fw_stats_dma,
3244b2fc195eSAndrew Gallatin 			     sizeof (*sc->fw_stats), 64);
3245b2fc195eSAndrew Gallatin 	if (err != 0)
3246b2fc195eSAndrew Gallatin 		goto abort_with_zeropad_dma;
32475e7d8541SAndrew Gallatin 	sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr;
3248b2fc195eSAndrew Gallatin 
3249a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
3250a98d6cd7SAndrew Gallatin 	if (err != 0)
3251a98d6cd7SAndrew Gallatin 		goto abort_with_fw_stats;
3252b2fc195eSAndrew Gallatin 
3253b2fc195eSAndrew Gallatin 	/* Add our ithread  */
3254dc8731d4SAndrew Gallatin 	count = pci_msi_count(dev);
3255dc8731d4SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(dev, &count) == 0) {
3256dc8731d4SAndrew Gallatin 		rid = 1;
3257dc8731d4SAndrew Gallatin 		sc->msi_enabled = 1;
3258dc8731d4SAndrew Gallatin 	} else {
3259b2fc195eSAndrew Gallatin 		rid = 0;
3260dc8731d4SAndrew Gallatin 	}
3261b2fc195eSAndrew Gallatin 	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0,
3262b2fc195eSAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
3263b2fc195eSAndrew Gallatin 	if (sc->irq_res == NULL) {
3264b2fc195eSAndrew Gallatin 		device_printf(dev, "could not alloc interrupt\n");
3265adae7080SAndrew Gallatin 		goto abort_with_dmabench;
3266b2fc195eSAndrew Gallatin 	}
3267d91b1b49SAndrew Gallatin 	if (mxge_verbose)
3268dc8731d4SAndrew Gallatin 		device_printf(dev, "using %s irq %ld\n",
3269dc8731d4SAndrew Gallatin 			      sc->msi_enabled ? "MSI" : "INTx",
3270dc8731d4SAndrew Gallatin 			      rman_get_start(sc->irq_res));
32718fe615baSAndrew Gallatin 	/* select & load the firmware */
32728fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
3273b2fc195eSAndrew Gallatin 	if (err != 0)
3274b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
32755e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
3276adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
3277b2fc195eSAndrew Gallatin 	if (err != 0)
3278b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
3279b2fc195eSAndrew Gallatin 
3280a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
3281a98d6cd7SAndrew Gallatin 	if (err != 0) {
3282a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
3283a98d6cd7SAndrew Gallatin 		goto abort_with_irq_res;
3284a98d6cd7SAndrew Gallatin 	}
3285a98d6cd7SAndrew Gallatin 
3286a98d6cd7SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
3287a98d6cd7SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
3288ef544f63SPaolo Pisati 			     NULL, mxge_intr, sc, &sc->ih);
3289a98d6cd7SAndrew Gallatin 	if (err != 0) {
3290a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
3291a98d6cd7SAndrew Gallatin 	}
3292b2fc195eSAndrew Gallatin 	/* hook into the network stack */
3293b2fc195eSAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
3294b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
3295c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
3296c792928fSAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
3297c792928fSAndrew Gallatin 
3298053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
3299053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
3300053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
3301053e637fSAndrew Gallatin 	else
3302053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
3303adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
3304053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
3305aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
3306b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
33075e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
33086d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
3309b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
3310b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
33116d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
33126d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
3313b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
3314b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
3315053e637fSAndrew Gallatin 	if (ifp->if_capabilities & IFCAP_JUMBO_MTU)
3316c792928fSAndrew Gallatin 		ifp->if_mtu = 9000;
3317b2fc195eSAndrew Gallatin 
3318b2fc195eSAndrew Gallatin 	/* Initialise the ifmedia structure */
33196d87a65dSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
33206d87a65dSAndrew Gallatin 		     mxge_media_status);
3321b2fc195eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
33226d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
3323b2fc195eSAndrew Gallatin 	return 0;
3324b2fc195eSAndrew Gallatin 
3325a98d6cd7SAndrew Gallatin abort_with_rings:
3326a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3327b2fc195eSAndrew Gallatin abort_with_irq_res:
3328dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3329dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3330dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3331dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
3332a98d6cd7SAndrew Gallatin abort_with_dmabench:
3333a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
33345e7d8541SAndrew Gallatin abort_with_fw_stats:
33356d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3336b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
33376d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
3338b2fc195eSAndrew Gallatin abort_with_cmd_dma:
33396d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3340b2fc195eSAndrew Gallatin abort_with_mem_res:
3341b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3342b2fc195eSAndrew Gallatin abort_with_lock:
3343b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3344a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3345a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3346a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3347b2fc195eSAndrew Gallatin 	if_free(ifp);
3348b2fc195eSAndrew Gallatin abort_with_parent_dmat:
3349b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3350b2fc195eSAndrew Gallatin 
3351b2fc195eSAndrew Gallatin abort_with_nothing:
3352b2fc195eSAndrew Gallatin 	return err;
3353b2fc195eSAndrew Gallatin }
3354b2fc195eSAndrew Gallatin 
3355b2fc195eSAndrew Gallatin static int
33566d87a65dSAndrew Gallatin mxge_detach(device_t dev)
3357b2fc195eSAndrew Gallatin {
33586d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
3359b2fc195eSAndrew Gallatin 
3360c792928fSAndrew Gallatin 	if (sc->ifp->if_vlantrunk != NULL) {
3361c792928fSAndrew Gallatin 		device_printf(sc->dev,
3362c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
3363c792928fSAndrew Gallatin 		return EBUSY;
3364c792928fSAndrew Gallatin 	}
3365a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3366b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
33676d87a65dSAndrew Gallatin 		mxge_close(sc);
3368dce01b9bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3369a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3370b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
3371dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
3372091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
3373a98d6cd7SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
3374a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3375dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3376dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3377dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3378dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
3379dc8731d4SAndrew Gallatin 
33805e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
33815e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
33826d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3383a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
33846d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
33856d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3386b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3387b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3388a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3389a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3390a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3391b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
3392b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3393b2fc195eSAndrew Gallatin 	return 0;
3394b2fc195eSAndrew Gallatin }
3395b2fc195eSAndrew Gallatin 
3396b2fc195eSAndrew Gallatin static int
33976d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
3398b2fc195eSAndrew Gallatin {
3399b2fc195eSAndrew Gallatin 	return 0;
3400b2fc195eSAndrew Gallatin }
3401b2fc195eSAndrew Gallatin 
3402b2fc195eSAndrew Gallatin /*
3403b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
3404b2fc195eSAndrew Gallatin 
3405b2fc195eSAndrew Gallatin   Local Variables:
3406b2fc195eSAndrew Gallatin   c-file-style:"linux"
3407b2fc195eSAndrew Gallatin   tab-width:8
3408b2fc195eSAndrew Gallatin   End:
3409b2fc195eSAndrew Gallatin */
3410