xref: /freebsd/sys/dev/mxge/if_mxge.c (revision aed8e389c1c32712b6c2ab374b41aa0ce3fbea76)
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>
47b2fc195eSAndrew Gallatin #include <sys/module.h>
48b2fc195eSAndrew Gallatin #include <sys/memrange.h>
49b2fc195eSAndrew Gallatin #include <sys/socket.h>
50b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
51b2fc195eSAndrew Gallatin #include <sys/sx.h>
52b2fc195eSAndrew Gallatin 
53b2fc195eSAndrew Gallatin #include <net/if.h>
54b2fc195eSAndrew Gallatin #include <net/if_arp.h>
55b2fc195eSAndrew Gallatin #include <net/ethernet.h>
56b2fc195eSAndrew Gallatin #include <net/if_dl.h>
57b2fc195eSAndrew Gallatin #include <net/if_media.h>
58b2fc195eSAndrew Gallatin 
59b2fc195eSAndrew Gallatin #include <net/bpf.h>
60b2fc195eSAndrew Gallatin 
61b2fc195eSAndrew Gallatin #include <net/if_types.h>
62b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
63b2fc195eSAndrew Gallatin #include <net/zlib.h>
64b2fc195eSAndrew Gallatin 
65b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
66b2fc195eSAndrew Gallatin #include <netinet/in.h>
67b2fc195eSAndrew Gallatin #include <netinet/ip.h>
68aed8e389SAndrew Gallatin #include <netinet/tcp.h>
69b2fc195eSAndrew Gallatin 
70b2fc195eSAndrew Gallatin #include <machine/bus.h>
71b2fc195eSAndrew Gallatin #include <machine/resource.h>
72b2fc195eSAndrew Gallatin #include <sys/bus.h>
73b2fc195eSAndrew Gallatin #include <sys/rman.h>
74b2fc195eSAndrew Gallatin 
75b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
76b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
77b2fc195eSAndrew Gallatin 
78b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
79b2fc195eSAndrew Gallatin #include <vm/pmap.h>
80b2fc195eSAndrew Gallatin 
816d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
826d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
836d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
84b2fc195eSAndrew Gallatin 
85b2fc195eSAndrew Gallatin /* tunable params */
866d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
875e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024;
886d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
895e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
906d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
915e7d8541SAndrew Gallatin static int mxge_verbose = 0;
926d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
936d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
94b2fc195eSAndrew Gallatin 
956d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
966d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
976d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
986d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
996d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
100b2fc195eSAndrew Gallatin 
1016d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
102b2fc195eSAndrew Gallatin {
103b2fc195eSAndrew Gallatin   /* Device interface */
1046d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1056d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1066d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1076d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
108b2fc195eSAndrew Gallatin   {0, 0}
109b2fc195eSAndrew Gallatin };
110b2fc195eSAndrew Gallatin 
1116d87a65dSAndrew Gallatin static driver_t mxge_driver =
112b2fc195eSAndrew Gallatin {
1136d87a65dSAndrew Gallatin   "mxge",
1146d87a65dSAndrew Gallatin   mxge_methods,
1156d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
116b2fc195eSAndrew Gallatin };
117b2fc195eSAndrew Gallatin 
1186d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
119b2fc195eSAndrew Gallatin 
120b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1216d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1226d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
123b2fc195eSAndrew Gallatin 
124b2fc195eSAndrew Gallatin static int
1256d87a65dSAndrew Gallatin mxge_probe(device_t dev)
126b2fc195eSAndrew Gallatin {
1276d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1286d87a65dSAndrew Gallatin       (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) {
129b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
130b2fc195eSAndrew Gallatin 	  return 0;
131b2fc195eSAndrew Gallatin   }
132b2fc195eSAndrew Gallatin   return ENXIO;
133b2fc195eSAndrew Gallatin }
134b2fc195eSAndrew Gallatin 
135b2fc195eSAndrew Gallatin static void
1366d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
137b2fc195eSAndrew Gallatin {
138b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
139b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
140b2fc195eSAndrew Gallatin 	vm_offset_t len;
141b2fc195eSAndrew Gallatin 	int err, action;
142b2fc195eSAndrew Gallatin 
143b2fc195eSAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
144b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
145b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
146b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
147b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
148b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1496d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
150b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
151b2fc195eSAndrew Gallatin 	if (err != 0) {
152b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
153b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
154b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
155b2fc195eSAndrew Gallatin 	} else {
156b2fc195eSAndrew Gallatin 		sc->wc = 1;
157b2fc195eSAndrew Gallatin 	}
158b2fc195eSAndrew Gallatin }
159b2fc195eSAndrew Gallatin 
160b2fc195eSAndrew Gallatin 
161b2fc195eSAndrew Gallatin /* callback to get our DMA address */
162b2fc195eSAndrew Gallatin static void
1636d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
164b2fc195eSAndrew Gallatin 			 int error)
165b2fc195eSAndrew Gallatin {
166b2fc195eSAndrew Gallatin 	if (error == 0) {
167b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
168b2fc195eSAndrew Gallatin 	}
169b2fc195eSAndrew Gallatin }
170b2fc195eSAndrew Gallatin 
171b2fc195eSAndrew Gallatin static int
1726d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
173b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
174b2fc195eSAndrew Gallatin {
175b2fc195eSAndrew Gallatin 	int err;
176b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
177b2fc195eSAndrew Gallatin 
178b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
179b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
180b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
181b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
182b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
183b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
184b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
185b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
186b2fc195eSAndrew Gallatin 				 1,			/* num segs */
187b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
188b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
189b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
190b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
191b2fc195eSAndrew Gallatin 	if (err != 0) {
192b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
193b2fc195eSAndrew Gallatin 		return err;
194b2fc195eSAndrew Gallatin 	}
195b2fc195eSAndrew Gallatin 
196b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
197b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
198b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
199b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
200b2fc195eSAndrew Gallatin 	if (err != 0) {
201b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
202b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
203b2fc195eSAndrew Gallatin 	}
204b2fc195eSAndrew Gallatin 
205b2fc195eSAndrew Gallatin 	/* load the memory */
206b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2076d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
208b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
209b2fc195eSAndrew Gallatin 	if (err != 0) {
210b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
211b2fc195eSAndrew Gallatin 		goto abort_with_mem;
212b2fc195eSAndrew Gallatin 	}
213b2fc195eSAndrew Gallatin 	return 0;
214b2fc195eSAndrew Gallatin 
215b2fc195eSAndrew Gallatin abort_with_mem:
216b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
217b2fc195eSAndrew Gallatin abort_with_dmat:
218b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
219b2fc195eSAndrew Gallatin 	return err;
220b2fc195eSAndrew Gallatin }
221b2fc195eSAndrew Gallatin 
222b2fc195eSAndrew Gallatin 
223b2fc195eSAndrew Gallatin static void
2246d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
225b2fc195eSAndrew Gallatin {
226b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
227b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
228b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
229b2fc195eSAndrew Gallatin }
230b2fc195eSAndrew Gallatin 
231b2fc195eSAndrew Gallatin /*
232b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
233b2fc195eSAndrew Gallatin  * SN=x\0
234b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
235b2fc195eSAndrew Gallatin  * PC=text\0
236b2fc195eSAndrew Gallatin  */
237b2fc195eSAndrew Gallatin 
238b2fc195eSAndrew Gallatin static int
2396d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
240b2fc195eSAndrew Gallatin {
2416d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
242b2fc195eSAndrew Gallatin 
243b2fc195eSAndrew Gallatin 	char *ptr, *limit;
244b2fc195eSAndrew Gallatin 	int i, found_mac;
245b2fc195eSAndrew Gallatin 
246b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2476d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
248b2fc195eSAndrew Gallatin 	found_mac = 0;
249b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
250b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2515e7d8541SAndrew Gallatin 			ptr += 1;
252b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
253b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2545e7d8541SAndrew Gallatin 				ptr += 3;
255b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
256b2fc195eSAndrew Gallatin 					goto abort;
257b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
258b2fc195eSAndrew Gallatin 				found_mac = 1;
259b2fc195eSAndrew Gallatin 			}
2605e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2615e7d8541SAndrew Gallatin 			ptr += 3;
2625e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2635e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2645e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2655e7d8541SAndrew Gallatin 			ptr += 3;
2665e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2675e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
268b2fc195eSAndrew Gallatin 		}
2696d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
270b2fc195eSAndrew Gallatin 	}
271b2fc195eSAndrew Gallatin 
272b2fc195eSAndrew Gallatin 	if (found_mac)
273b2fc195eSAndrew Gallatin 		return 0;
274b2fc195eSAndrew Gallatin 
275b2fc195eSAndrew Gallatin  abort:
276b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
277b2fc195eSAndrew Gallatin 
278b2fc195eSAndrew Gallatin 	return ENXIO;
279b2fc195eSAndrew Gallatin }
280b2fc195eSAndrew Gallatin 
281b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
282b2fc195eSAndrew Gallatin static int
2836d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
284b2fc195eSAndrew Gallatin {
285b2fc195eSAndrew Gallatin 	uint32_t val;
286b2fc195eSAndrew Gallatin 	unsigned long off;
287b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
288b2fc195eSAndrew Gallatin 	uint16_t vendor_id, device_id;
289b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
290b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
291b2fc195eSAndrew Gallatin 
292b2fc195eSAndrew Gallatin 	/* XXXX
293b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
294b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
295b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
296b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
297b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
298b2fc195eSAndrew Gallatin 	*/
299b2fc195eSAndrew Gallatin #if 0
300b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
301b2fc195eSAndrew Gallatin 	   config space */
302b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
303b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
304b2fc195eSAndrew Gallatin 		val |= 0x40;
305b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
306b2fc195eSAndrew Gallatin 		return 0;
307b2fc195eSAndrew Gallatin 	}
308b2fc195eSAndrew Gallatin #endif
309b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
310b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
311b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
312b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
313b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
314b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
315b2fc195eSAndrew Gallatin 	 */
316b2fc195eSAndrew Gallatin 
317b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
318b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
319b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
320b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
321b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
322b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
323b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
324b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
325b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
326b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
327b2fc195eSAndrew Gallatin 
328b2fc195eSAndrew Gallatin 	off =  0xe0000000UL
329b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
330b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
331b2fc195eSAndrew Gallatin 						 + 8 * slot);
332b2fc195eSAndrew Gallatin 
333b2fc195eSAndrew Gallatin 	/* map it into the kernel */
334b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
335b2fc195eSAndrew Gallatin 
336b2fc195eSAndrew Gallatin 
337b2fc195eSAndrew Gallatin 	if (va == NULL) {
338b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
339b2fc195eSAndrew Gallatin 		return EIO;
340b2fc195eSAndrew Gallatin 	}
341b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
342b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
343b2fc195eSAndrew Gallatin 
344b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
345b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
346b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
347b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
348b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
349b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
350b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
351b2fc195eSAndrew Gallatin 		return EIO;
352b2fc195eSAndrew Gallatin 	}
353b2fc195eSAndrew Gallatin 
354b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
355b2fc195eSAndrew Gallatin 	val = *ptr32;
356b2fc195eSAndrew Gallatin 
357b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
358b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
359b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
360b2fc195eSAndrew Gallatin 		return EIO;
361b2fc195eSAndrew Gallatin 	}
362b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
363b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3645e7d8541SAndrew Gallatin 	if (mxge_verbose)
365b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
3665e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
3675e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
368b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
369b2fc195eSAndrew Gallatin 	return 0;
370b2fc195eSAndrew Gallatin }
371b2fc195eSAndrew Gallatin #else
372b2fc195eSAndrew Gallatin static int
3736d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
374b2fc195eSAndrew Gallatin {
375b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
376b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
377b2fc195eSAndrew Gallatin 	return ENXIO;
378b2fc195eSAndrew Gallatin }
379b2fc195eSAndrew Gallatin #endif
380b2fc195eSAndrew Gallatin /*
381b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
382b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
383b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
384b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
385b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
386b2fc195eSAndrew Gallatin  *
387b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
388b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
389b2fc195eSAndrew Gallatin  *
390b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
391b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
392b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
393b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
394b2fc195eSAndrew Gallatin  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
395b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
396b2fc195eSAndrew Gallatin  * firmware image, and set tx.boundary to 4KB.
397b2fc195eSAndrew Gallatin  */
398b2fc195eSAndrew Gallatin 
399b2fc195eSAndrew Gallatin static void
4006d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
401b2fc195eSAndrew Gallatin {
402b2fc195eSAndrew Gallatin 	int err, aligned = 0;
403b2fc195eSAndrew Gallatin 	device_t pdev;
404b2fc195eSAndrew Gallatin 	uint16_t pvend, pdid;
405b2fc195eSAndrew Gallatin 
406b2fc195eSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
407b2fc195eSAndrew Gallatin 	if (pdev == NULL) {
408b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
409b2fc195eSAndrew Gallatin 		goto abort;
410b2fc195eSAndrew Gallatin 	}
411b2fc195eSAndrew Gallatin 	pvend = pci_read_config(pdev, PCIR_VENDOR, 2);
412b2fc195eSAndrew Gallatin 	pdid = pci_read_config(pdev, PCIR_DEVICE, 2);
413b2fc195eSAndrew Gallatin 
414b2fc195eSAndrew Gallatin 	/* see if we can enable ECRC's on an upstream
415b2fc195eSAndrew Gallatin 	   Nvidia bridge */
4166d87a65dSAndrew Gallatin 	if (mxge_nvidia_ecrc_enable &&
417b2fc195eSAndrew Gallatin 	    (pvend == 0x10de && pdid == 0x005d)) {
4186d87a65dSAndrew Gallatin 		err = mxge_enable_nvidia_ecrc(sc, pdev);
419b2fc195eSAndrew Gallatin 		if (err == 0) {
420b2fc195eSAndrew Gallatin 			aligned = 1;
4215e7d8541SAndrew Gallatin 			if (mxge_verbose)
422b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
4235e7d8541SAndrew Gallatin 					      "Assuming aligned completions"
4245e7d8541SAndrew Gallatin 					      " (ECRC)\n");
425b2fc195eSAndrew Gallatin 		}
426b2fc195eSAndrew Gallatin 	}
427b2fc195eSAndrew Gallatin 	/* see if the upstream bridge is known to
428b2fc195eSAndrew Gallatin 	   provided aligned completions */
429b2fc195eSAndrew Gallatin 	if (/* HT2000  */ (pvend == 0x1166 && pdid == 0x0132) ||
430b2fc195eSAndrew Gallatin 	    /* Ontario */ (pvend == 0x10b5 && pdid == 0x8532)) {
4315e7d8541SAndrew Gallatin 		if (mxge_verbose)
432b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
4335e7d8541SAndrew Gallatin 				      "Assuming aligned completions "
4345e7d8541SAndrew Gallatin 				      "(0x%x:0x%x)\n", pvend, pdid);
435b2fc195eSAndrew Gallatin 	}
436b2fc195eSAndrew Gallatin 
437b2fc195eSAndrew Gallatin abort:
438b2fc195eSAndrew Gallatin 	if (aligned) {
4396d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
440b2fc195eSAndrew Gallatin 		sc->tx.boundary = 4096;
441b2fc195eSAndrew Gallatin 	} else {
4426d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
443b2fc195eSAndrew Gallatin 		sc->tx.boundary = 2048;
444b2fc195eSAndrew Gallatin 	}
445b2fc195eSAndrew Gallatin }
446b2fc195eSAndrew Gallatin 
447b2fc195eSAndrew Gallatin union qualhack
448b2fc195eSAndrew Gallatin {
449b2fc195eSAndrew Gallatin         const char *ro_char;
450b2fc195eSAndrew Gallatin         char *rw_char;
451b2fc195eSAndrew Gallatin };
452b2fc195eSAndrew Gallatin 
4534da0d523SAndrew Gallatin static int
4544da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
4554da0d523SAndrew Gallatin {
4564da0d523SAndrew Gallatin 	int major, minor;
4574da0d523SAndrew Gallatin 
4584da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
4594da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
4604da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
4614da0d523SAndrew Gallatin 		return EIO;
4624da0d523SAndrew Gallatin 	}
4634da0d523SAndrew Gallatin 
4644da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
4654da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
4664da0d523SAndrew Gallatin 	if (mxge_verbose)
4674da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
4684da0d523SAndrew Gallatin 
4694da0d523SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d", &major, &minor);
4704da0d523SAndrew Gallatin 
4714da0d523SAndrew Gallatin 	if (!(major == MXGEFW_VERSION_MAJOR
4724da0d523SAndrew Gallatin 	      && minor == MXGEFW_VERSION_MINOR)) {
4734da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
4744da0d523SAndrew Gallatin 			      sc->fw_version);
4754da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
4764da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
4774da0d523SAndrew Gallatin 		return EINVAL;
4784da0d523SAndrew Gallatin 	}
4794da0d523SAndrew Gallatin 	return 0;
4804da0d523SAndrew Gallatin 
4814da0d523SAndrew Gallatin }
482b2fc195eSAndrew Gallatin 
483b2fc195eSAndrew Gallatin static int
4846d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
485b2fc195eSAndrew Gallatin {
486b2fc195eSAndrew Gallatin 	struct firmware *fw;
487b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
488b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
489b2fc195eSAndrew Gallatin 	const char *fw_data;
490b2fc195eSAndrew Gallatin 	union qualhack hack;
491b2fc195eSAndrew Gallatin 	int status;
4924da0d523SAndrew Gallatin 	unsigned int i;
4934da0d523SAndrew Gallatin 	char dummy;
494b2fc195eSAndrew Gallatin 
495b2fc195eSAndrew Gallatin 
496b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
497b2fc195eSAndrew Gallatin 
498b2fc195eSAndrew Gallatin 	if (fw == NULL) {
499b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
500b2fc195eSAndrew Gallatin 			      sc->fw_name);
501b2fc195eSAndrew Gallatin 		return ENOENT;
502b2fc195eSAndrew Gallatin 	}
503b2fc195eSAndrew Gallatin 	if (fw->datasize > *limit ||
504b2fc195eSAndrew Gallatin 	    fw->datasize < MCP_HEADER_PTR_OFFSET + 4) {
505b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n",
506b2fc195eSAndrew Gallatin 			      sc->fw_name, (int)fw->datasize, (int) *limit);
507b2fc195eSAndrew Gallatin 		status = ENOSPC;
508b2fc195eSAndrew Gallatin 		goto abort_with_fw;
509b2fc195eSAndrew Gallatin 	}
510b2fc195eSAndrew Gallatin 	*limit = fw->datasize;
511b2fc195eSAndrew Gallatin 
512b2fc195eSAndrew Gallatin 	/* check id */
513b2fc195eSAndrew Gallatin 	fw_data = (const char *)fw->data;
514b2fc195eSAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
515b2fc195eSAndrew Gallatin 			     (fw_data + MCP_HEADER_PTR_OFFSET));
516b2fc195eSAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) {
517b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
518b2fc195eSAndrew Gallatin 		status = EIO;
519b2fc195eSAndrew Gallatin 		goto abort_with_fw;
520b2fc195eSAndrew Gallatin 	}
521b2fc195eSAndrew Gallatin 	hdr = (const void*)(fw_data + hdr_offset);
522b2fc195eSAndrew Gallatin 
5234da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
5244da0d523SAndrew Gallatin 	if (status != 0)
5254da0d523SAndrew Gallatin 		goto abort_with_fw;
526b2fc195eSAndrew Gallatin 
527b2fc195eSAndrew Gallatin 	hack.ro_char = fw_data;
528b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
5294da0d523SAndrew Gallatin 	for (i = 0; i < *limit; i += 256) {
5304da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
5314da0d523SAndrew Gallatin 			      hack.rw_char + i,
5324da0d523SAndrew Gallatin 			      min(256U, (unsigned)(*limit - i)));
5334da0d523SAndrew Gallatin 		mb();
5344da0d523SAndrew Gallatin 		dummy = *sc->sram;
5354da0d523SAndrew Gallatin 		mb();
5364da0d523SAndrew Gallatin 	}
537b2fc195eSAndrew Gallatin 
538b2fc195eSAndrew Gallatin 	status = 0;
539b2fc195eSAndrew Gallatin abort_with_fw:
540b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
541b2fc195eSAndrew Gallatin 	return status;
542b2fc195eSAndrew Gallatin }
543b2fc195eSAndrew Gallatin 
544b2fc195eSAndrew Gallatin /*
545b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
546b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
547b2fc195eSAndrew Gallatin  */
548b2fc195eSAndrew Gallatin 
549b2fc195eSAndrew Gallatin static void
5506d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
551b2fc195eSAndrew Gallatin {
552b2fc195eSAndrew Gallatin 	char buf_bytes[72];
553b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
554b2fc195eSAndrew Gallatin 	volatile char *submit;
555b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
556b2fc195eSAndrew Gallatin 	int i;
557b2fc195eSAndrew Gallatin 
558b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
559b2fc195eSAndrew Gallatin 
560b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
561b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
562b2fc195eSAndrew Gallatin 	*confirm = 0;
563b2fc195eSAndrew Gallatin 	mb();
564b2fc195eSAndrew Gallatin 
565b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
566b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
567b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
568b2fc195eSAndrew Gallatin 	*/
569b2fc195eSAndrew Gallatin 
5706d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
5716d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
572b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
573b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
574b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
5756d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
5766d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
577b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
578b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
579b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
580b2fc195eSAndrew Gallatin 
581b2fc195eSAndrew Gallatin 
582b2fc195eSAndrew Gallatin 	submit = (volatile char *)(sc->sram + 0xfc01c0);
583b2fc195eSAndrew Gallatin 
5846d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
585b2fc195eSAndrew Gallatin 	mb();
586b2fc195eSAndrew Gallatin 	DELAY(1000);
587b2fc195eSAndrew Gallatin 	mb();
588b2fc195eSAndrew Gallatin 	i = 0;
589b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
590b2fc195eSAndrew Gallatin 		DELAY(1000);
591b2fc195eSAndrew Gallatin 		i++;
592b2fc195eSAndrew Gallatin 	}
593b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
594b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
595b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
596b2fc195eSAndrew Gallatin 			      *confirm);
597b2fc195eSAndrew Gallatin 	}
598b2fc195eSAndrew Gallatin 	return;
599b2fc195eSAndrew Gallatin }
600b2fc195eSAndrew Gallatin 
601b2fc195eSAndrew Gallatin static int
6026d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
603b2fc195eSAndrew Gallatin {
604b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
605b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
606b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
6075e7d8541SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_CMD_OFFSET;
608b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
609b2fc195eSAndrew Gallatin 	int sleep_total = 0;
610b2fc195eSAndrew Gallatin 
611b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
612b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
613b2fc195eSAndrew Gallatin 
614b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
615b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
616b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
617b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
6186d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
6196d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
620b2fc195eSAndrew Gallatin 
621b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
622b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
623b2fc195eSAndrew Gallatin 	mtx_lock(&sc->cmd_lock);
624b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
625b2fc195eSAndrew Gallatin 	mb();
6266d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
627b2fc195eSAndrew Gallatin 
6285e7d8541SAndrew Gallatin 	/* wait up to 20ms */
6295e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
630b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
631b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
632b2fc195eSAndrew Gallatin 		mb();
633b2fc195eSAndrew Gallatin 		if (response->result != 0xffffffff) {
634b2fc195eSAndrew Gallatin 			if (response->result == 0) {
635b2fc195eSAndrew Gallatin 				data->data0 = be32toh(response->data);
636b2fc195eSAndrew Gallatin 				mtx_unlock(&sc->cmd_lock);
637b2fc195eSAndrew Gallatin 				return 0;
638b2fc195eSAndrew Gallatin 			} else {
639b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
6406d87a65dSAndrew Gallatin 					      "mxge: command %d "
641b2fc195eSAndrew Gallatin 					      "failed, result = %d\n",
642b2fc195eSAndrew Gallatin 					      cmd, be32toh(response->result));
643b2fc195eSAndrew Gallatin 				mtx_unlock(&sc->cmd_lock);
644b2fc195eSAndrew Gallatin 				return ENXIO;
645b2fc195eSAndrew Gallatin 			}
646b2fc195eSAndrew Gallatin 		}
6475e7d8541SAndrew Gallatin 		DELAY(1000);
648b2fc195eSAndrew Gallatin 	}
649b2fc195eSAndrew Gallatin 	mtx_unlock(&sc->cmd_lock);
6506d87a65dSAndrew Gallatin 	device_printf(sc->dev, "mxge: command %d timed out"
651b2fc195eSAndrew Gallatin 		      "result = %d\n",
652b2fc195eSAndrew Gallatin 		      cmd, be32toh(response->result));
653b2fc195eSAndrew Gallatin 	return EAGAIN;
654b2fc195eSAndrew Gallatin }
655b2fc195eSAndrew Gallatin 
6564da0d523SAndrew Gallatin static int
6574da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
6584da0d523SAndrew Gallatin {
6594da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
6604da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
6614da0d523SAndrew Gallatin 	size_t hdr_offset;
6624da0d523SAndrew Gallatin 	int status;
6634da0d523SAndrew Gallatin 
6644da0d523SAndrew Gallatin 	/* find running firmware header */
6654da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
6664da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
6674da0d523SAndrew Gallatin 
6684da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
6694da0d523SAndrew Gallatin 		device_printf(sc->dev,
6704da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
6714da0d523SAndrew Gallatin 			      (int)hdr_offset);
6724da0d523SAndrew Gallatin 		return EIO;
6734da0d523SAndrew Gallatin 	}
6744da0d523SAndrew Gallatin 
6754da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
6764da0d523SAndrew Gallatin 	 * validate firmware */
6774da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
6784da0d523SAndrew Gallatin 	if (hdr == NULL) {
6794da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
6804da0d523SAndrew Gallatin 		return ENOMEM;
6814da0d523SAndrew Gallatin 	}
6824da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
6834da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
6844da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
6854da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
6864da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
6874da0d523SAndrew Gallatin 	return status;
6884da0d523SAndrew Gallatin }
6894da0d523SAndrew Gallatin 
690b2fc195eSAndrew Gallatin 
691b2fc195eSAndrew Gallatin static int
6926d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc)
693b2fc195eSAndrew Gallatin {
694b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
695b2fc195eSAndrew Gallatin 	volatile char *submit;
696b2fc195eSAndrew Gallatin 	char buf_bytes[72];
697b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
698b2fc195eSAndrew Gallatin 	int status, i;
699b2fc195eSAndrew Gallatin 
700b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
701b2fc195eSAndrew Gallatin 
702b2fc195eSAndrew Gallatin 	size = sc->sram_size;
7036d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
704b2fc195eSAndrew Gallatin 	if (status) {
7054da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
7064da0d523SAndrew Gallatin 		   it is new enough */
7074da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
7084da0d523SAndrew Gallatin 		if (status) {
7094da0d523SAndrew Gallatin 			device_printf(sc->dev,
7104da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
711b2fc195eSAndrew Gallatin 			return status;
712b2fc195eSAndrew Gallatin 		}
7134da0d523SAndrew Gallatin 		device_printf(sc->dev,
7144da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
7154da0d523SAndrew Gallatin 		if (sc->tx.boundary == 4096) {
7164da0d523SAndrew Gallatin 			device_printf(sc->dev,
7174da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
7184da0d523SAndrew Gallatin 				 ".  For optimal\n");
7194da0d523SAndrew Gallatin 			device_printf(sc->dev,
7204da0d523SAndrew Gallatin 				 "performance consider loading optimized "
7214da0d523SAndrew Gallatin 				 "firmware\n");
7224da0d523SAndrew Gallatin 		}
7234da0d523SAndrew Gallatin 
7244da0d523SAndrew Gallatin 	}
725b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
726b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
727b2fc195eSAndrew Gallatin 	*confirm = 0;
728b2fc195eSAndrew Gallatin 	mb();
729b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
730b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
731b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
732b2fc195eSAndrew Gallatin 	*/
733b2fc195eSAndrew Gallatin 
7346d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7356d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
736b2fc195eSAndrew Gallatin 
737b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
738b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
739b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
740b2fc195eSAndrew Gallatin 
741b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
742b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
743b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
744b2fc195eSAndrew Gallatin 	*/
745b2fc195eSAndrew Gallatin 					/* where the code starts*/
7466d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
747b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
748b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
749b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
750b2fc195eSAndrew Gallatin 
751b2fc195eSAndrew Gallatin 	submit = (volatile char *)(sc->sram + 0xfc0000);
7526d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
753b2fc195eSAndrew Gallatin 	mb();
754b2fc195eSAndrew Gallatin 	DELAY(1000);
755b2fc195eSAndrew Gallatin 	mb();
756b2fc195eSAndrew Gallatin 	i = 0;
757b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
758b2fc195eSAndrew Gallatin 		DELAY(1000*10);
759b2fc195eSAndrew Gallatin 		i++;
760b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
761b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
762b2fc195eSAndrew Gallatin 	}
763b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
764b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
765b2fc195eSAndrew Gallatin 			confirm, *confirm);
766b2fc195eSAndrew Gallatin 
767b2fc195eSAndrew Gallatin 		return ENXIO;
768b2fc195eSAndrew Gallatin 	}
769b2fc195eSAndrew Gallatin 	return 0;
770b2fc195eSAndrew Gallatin }
771b2fc195eSAndrew Gallatin 
772b2fc195eSAndrew Gallatin static int
7736d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
774b2fc195eSAndrew Gallatin {
7756d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
776b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
777b2fc195eSAndrew Gallatin 	int status;
778b2fc195eSAndrew Gallatin 
779b2fc195eSAndrew Gallatin 
780b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
781b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
782b2fc195eSAndrew Gallatin 
783b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
784b2fc195eSAndrew Gallatin 
7855e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
786b2fc195eSAndrew Gallatin 	return status;
787b2fc195eSAndrew Gallatin }
788b2fc195eSAndrew Gallatin 
789b2fc195eSAndrew Gallatin static int
7906d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
791b2fc195eSAndrew Gallatin {
7926d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
793b2fc195eSAndrew Gallatin 	int status;
794b2fc195eSAndrew Gallatin 
795b2fc195eSAndrew Gallatin 	if (pause)
7965e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
797b2fc195eSAndrew Gallatin 				       &cmd);
798b2fc195eSAndrew Gallatin 	else
7995e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
800b2fc195eSAndrew Gallatin 				       &cmd);
801b2fc195eSAndrew Gallatin 
802b2fc195eSAndrew Gallatin 	if (status) {
803b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
804b2fc195eSAndrew Gallatin 		return ENXIO;
805b2fc195eSAndrew Gallatin 	}
806b2fc195eSAndrew Gallatin 	sc->pause = pause;
807b2fc195eSAndrew Gallatin 	return 0;
808b2fc195eSAndrew Gallatin }
809b2fc195eSAndrew Gallatin 
810b2fc195eSAndrew Gallatin static void
8116d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
812b2fc195eSAndrew Gallatin {
8136d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
814b2fc195eSAndrew Gallatin 	int status;
815b2fc195eSAndrew Gallatin 
816b2fc195eSAndrew Gallatin 	if (promisc)
8175e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
818b2fc195eSAndrew Gallatin 				       &cmd);
819b2fc195eSAndrew Gallatin 	else
8205e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
821b2fc195eSAndrew Gallatin 				       &cmd);
822b2fc195eSAndrew Gallatin 
823b2fc195eSAndrew Gallatin 	if (status) {
824b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
825b2fc195eSAndrew Gallatin 	}
826b2fc195eSAndrew Gallatin }
827b2fc195eSAndrew Gallatin 
828b2fc195eSAndrew Gallatin static int
8296d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc)
830b2fc195eSAndrew Gallatin {
831b2fc195eSAndrew Gallatin 
8326d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
8335e7d8541SAndrew Gallatin 	mxge_dma_t dmabench_dma;
8345e7d8541SAndrew Gallatin 	size_t bytes;
8355e7d8541SAndrew Gallatin 	int status;
836b2fc195eSAndrew Gallatin 
837b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
838b2fc195eSAndrew Gallatin 	   is alive */
839b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
8405e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
841b2fc195eSAndrew Gallatin 	if (status != 0) {
842b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
843b2fc195eSAndrew Gallatin 		return ENXIO;
844b2fc195eSAndrew Gallatin 	}
845b2fc195eSAndrew Gallatin 
846091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
847091feecdSAndrew Gallatin 
848b2fc195eSAndrew Gallatin 	/* Now exchange information about interrupts  */
8495e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\
8505e7d8541SAndrew Gallatin 	memset(sc->rx_done.entry, 0, bytes);
8515e7d8541SAndrew Gallatin 	cmd.data0 = (uint32_t)bytes;
8525e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
8535e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr);
8545e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr);
8555e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd);
856b2fc195eSAndrew Gallatin 
8576d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
8585e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
8595e7d8541SAndrew Gallatin 
8605e7d8541SAndrew Gallatin 
8615e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
8625e7d8541SAndrew Gallatin 
8635e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
8645e7d8541SAndrew Gallatin 	sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
8655e7d8541SAndrew Gallatin 
8665e7d8541SAndrew Gallatin 
8675e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
8686d87a65dSAndrew Gallatin 				&cmd);
8695e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
870b2fc195eSAndrew Gallatin 	if (status != 0) {
871b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
872b2fc195eSAndrew Gallatin 		return status;
873b2fc195eSAndrew Gallatin 	}
874b2fc195eSAndrew Gallatin 
8755e7d8541SAndrew Gallatin 
8765e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
8775e7d8541SAndrew Gallatin 
8785e7d8541SAndrew Gallatin 
8795e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
8805e7d8541SAndrew Gallatin 	sc->read_dma = sc->write_dma = sc->read_write_dma = 0;
8815e7d8541SAndrew Gallatin 	status = mxge_dma_alloc(sc, &dmabench_dma, 4096, 4096);
8825e7d8541SAndrew Gallatin 	if (status)
8835e7d8541SAndrew Gallatin 		goto dmabench_fail;
8845e7d8541SAndrew Gallatin 
8855e7d8541SAndrew Gallatin 	/* Read DMA */
8865e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
8875e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
8885e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10000;
8895e7d8541SAndrew Gallatin 
8905e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
8915e7d8541SAndrew Gallatin 	if (status != 0)
8925e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read dma benchmark failed\n");
8935e7d8541SAndrew Gallatin 	else
8945e7d8541SAndrew Gallatin 		sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
8955e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
8965e7d8541SAndrew Gallatin 
8975e7d8541SAndrew Gallatin 	/* Write DMA */
8985e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
8995e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
9005e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x1;
9015e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
9025e7d8541SAndrew Gallatin 	if (status != 0)
9035e7d8541SAndrew Gallatin 		device_printf(sc->dev, "write dma benchmark failed\n");
9045e7d8541SAndrew Gallatin 	else
9055e7d8541SAndrew Gallatin 		sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
9065e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
9075e7d8541SAndrew Gallatin 	/* Read/Write DMA */
9085e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
9095e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
9105e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10001;
9115e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
9125e7d8541SAndrew Gallatin 	if (status != 0)
9135e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read/write dma benchmark failed\n");
9145e7d8541SAndrew Gallatin 	else
9155e7d8541SAndrew Gallatin 		sc->read_write_dma =
9165e7d8541SAndrew Gallatin 			((cmd.data0>>16) * sc->tx.boundary * 2 * 2) /
9175e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
9185e7d8541SAndrew Gallatin 
9195e7d8541SAndrew Gallatin 	mxge_dma_free(&dmabench_dma);
9205e7d8541SAndrew Gallatin 
9215e7d8541SAndrew Gallatin dmabench_fail:
922b2fc195eSAndrew Gallatin 	/* reset mcp/driver shared state back to 0 */
9235e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
9245e7d8541SAndrew Gallatin 	sc->rx_done.idx = 0;
9255e7d8541SAndrew Gallatin 	sc->rx_done.cnt = 0;
926b2fc195eSAndrew Gallatin 	sc->tx.req = 0;
927b2fc195eSAndrew Gallatin 	sc->tx.done = 0;
9285e7d8541SAndrew Gallatin 	sc->tx.pkt_done = 0;
929b2fc195eSAndrew Gallatin 	sc->rx_big.cnt = 0;
930b2fc195eSAndrew Gallatin 	sc->rx_small.cnt = 0;
931b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
9326d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
9336d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
9346d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
935b2fc195eSAndrew Gallatin 	return status;
936b2fc195eSAndrew Gallatin }
937b2fc195eSAndrew Gallatin 
938b2fc195eSAndrew Gallatin static int
9396d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
940b2fc195eSAndrew Gallatin {
9416d87a65dSAndrew Gallatin         mxge_softc_t *sc;
942b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
943b2fc195eSAndrew Gallatin         int err;
944b2fc195eSAndrew Gallatin 
945b2fc195eSAndrew Gallatin         sc = arg1;
946b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
947b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
948b2fc195eSAndrew Gallatin         if (err != 0) {
949b2fc195eSAndrew Gallatin                 return err;
950b2fc195eSAndrew Gallatin         }
951b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
952b2fc195eSAndrew Gallatin                 return 0;
953b2fc195eSAndrew Gallatin 
954b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
955b2fc195eSAndrew Gallatin                 return EINVAL;
956b2fc195eSAndrew Gallatin 
957b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
9585e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
959b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
9605e7d8541SAndrew Gallatin 
961b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
962b2fc195eSAndrew Gallatin         return err;
963b2fc195eSAndrew Gallatin }
964b2fc195eSAndrew Gallatin 
965b2fc195eSAndrew Gallatin static int
9666d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
967b2fc195eSAndrew Gallatin {
9686d87a65dSAndrew Gallatin         mxge_softc_t *sc;
969b2fc195eSAndrew Gallatin         unsigned int enabled;
970b2fc195eSAndrew Gallatin         int err;
971b2fc195eSAndrew Gallatin 
972b2fc195eSAndrew Gallatin         sc = arg1;
973b2fc195eSAndrew Gallatin         enabled = sc->pause;
974b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
975b2fc195eSAndrew Gallatin         if (err != 0) {
976b2fc195eSAndrew Gallatin                 return err;
977b2fc195eSAndrew Gallatin         }
978b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
979b2fc195eSAndrew Gallatin                 return 0;
980b2fc195eSAndrew Gallatin 
981b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
9826d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
983b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
984b2fc195eSAndrew Gallatin         return err;
985b2fc195eSAndrew Gallatin }
986b2fc195eSAndrew Gallatin 
987b2fc195eSAndrew Gallatin static int
9886d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
989b2fc195eSAndrew Gallatin {
990b2fc195eSAndrew Gallatin         int err;
991b2fc195eSAndrew Gallatin 
992b2fc195eSAndrew Gallatin         if (arg1 == NULL)
993b2fc195eSAndrew Gallatin                 return EFAULT;
994b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
995b2fc195eSAndrew Gallatin         arg1 = NULL;
996b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
997b2fc195eSAndrew Gallatin 
998b2fc195eSAndrew Gallatin         return err;
999b2fc195eSAndrew Gallatin }
1000b2fc195eSAndrew Gallatin 
1001b2fc195eSAndrew Gallatin static void
10026d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1003b2fc195eSAndrew Gallatin {
1004b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1005b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
10065e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
1007b2fc195eSAndrew Gallatin 
1008b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1009b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
1010b2fc195eSAndrew Gallatin 	fw = sc->fw_stats;
1011b2fc195eSAndrew Gallatin 
10125e7d8541SAndrew Gallatin 	/* random information */
10135e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
10145e7d8541SAndrew Gallatin 		       "firmware_version",
10155e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
10165e7d8541SAndrew Gallatin 		       0, "firmware version");
10175e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
10185e7d8541SAndrew Gallatin 		       "serial_number",
10195e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
10205e7d8541SAndrew Gallatin 		       0, "serial number");
10215e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
10225e7d8541SAndrew Gallatin 		       "product_code",
10235e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
10245e7d8541SAndrew Gallatin 		       0, "product_code");
10255e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10265e7d8541SAndrew Gallatin 		       "tx_boundary",
10275e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.boundary,
10285e7d8541SAndrew Gallatin 		       0, "tx_boundary");
10295e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1030091feecdSAndrew Gallatin 		       "write_combine",
1031091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1032091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1033091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10345e7d8541SAndrew Gallatin 		       "read_dma_MBs",
10355e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
10365e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
10375e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10385e7d8541SAndrew Gallatin 		       "write_dma_MBs",
10395e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
10405e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
10415e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10425e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
10435e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
10445e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
10455e7d8541SAndrew Gallatin 
10465e7d8541SAndrew Gallatin 
10475e7d8541SAndrew Gallatin 	/* performance related tunables */
1048b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1049b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1050b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
10516d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1052b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1053b2fc195eSAndrew Gallatin 
1054b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1055b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1056b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
10576d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1058b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1059b2fc195eSAndrew Gallatin 
1060b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10615e7d8541SAndrew Gallatin 		       "deassert_wait",
10625e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
10635e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1064b2fc195eSAndrew Gallatin 
1065b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1066b2fc195eSAndrew Gallatin 	   Need to swap it */
1067b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1068b2fc195eSAndrew Gallatin 			"link_up",
1069b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
10706d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1071b2fc195eSAndrew Gallatin 			"I", "link up");
1072b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1073b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1074b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
10756d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1076b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1077b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1078b2fc195eSAndrew Gallatin 			"dropped_link_overflow",
1079b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
10806d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1081b2fc195eSAndrew Gallatin 			"I", "dropped_link_overflow");
1082b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1083b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1084b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1085b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
10866d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1087b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1088b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1089b2fc195eSAndrew Gallatin 			"dropped_runt",
1090b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
10916d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1092b2fc195eSAndrew Gallatin 			"I", "dropped_runt");
1093b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1094b2fc195eSAndrew Gallatin 			"dropped_overrun",
1095b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
10966d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1097b2fc195eSAndrew Gallatin 			"I", "dropped_overrun");
1098b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1099b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1100b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1101b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
11026d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1103b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1104b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1105b2fc195eSAndrew Gallatin 			"dropped_no_big_buffer",
1106b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
11076d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1108b2fc195eSAndrew Gallatin 			"I", "dropped_no_big_buffer");
1109b2fc195eSAndrew Gallatin 
1110b2fc195eSAndrew Gallatin 	/* host counters exported for debugging */
1111b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11125e7d8541SAndrew Gallatin 		       "rx_small_cnt",
11135e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_small.cnt,
11145e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
11155e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11165e7d8541SAndrew Gallatin 		       "rx_big_cnt",
11175e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_big.cnt,
11185e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
11195e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1120b2fc195eSAndrew Gallatin 		       "tx_req",
1121b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.req,
1122b2fc195eSAndrew Gallatin 		       0, "tx_req");
1123b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1124b2fc195eSAndrew Gallatin 		       "tx_done",
1125b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.done,
1126b2fc195eSAndrew Gallatin 		       0, "tx_done");
1127b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11285e7d8541SAndrew Gallatin 		       "tx_pkt_done",
11295e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.pkt_done,
11305e7d8541SAndrew Gallatin 		       0, "tx_done");
11315e7d8541SAndrew Gallatin 
11325e7d8541SAndrew Gallatin 	/* verbose printing? */
1133b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11345e7d8541SAndrew Gallatin 		       "verbose",
11355e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
11365e7d8541SAndrew Gallatin 		       0, "verbose printing");
1137b2fc195eSAndrew Gallatin 
1138b2fc195eSAndrew Gallatin }
1139b2fc195eSAndrew Gallatin 
1140b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1141b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1142b2fc195eSAndrew Gallatin 
1143b2fc195eSAndrew Gallatin static inline void
11446d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx,
1145b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1146b2fc195eSAndrew Gallatin {
1147b2fc195eSAndrew Gallatin         int idx, starting_slot;
1148b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1149b2fc195eSAndrew Gallatin         while (cnt > 1) {
1150b2fc195eSAndrew Gallatin                 cnt--;
1151b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
11526d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1153b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1154b2fc195eSAndrew Gallatin                 mb();
1155b2fc195eSAndrew Gallatin         }
1156b2fc195eSAndrew Gallatin }
1157b2fc195eSAndrew Gallatin 
1158b2fc195eSAndrew Gallatin /*
1159b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1160b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1161b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1162b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1163b2fc195eSAndrew Gallatin  */
1164b2fc195eSAndrew Gallatin 
1165b2fc195eSAndrew Gallatin static inline void
11666d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src,
1167b2fc195eSAndrew Gallatin                   int cnt)
1168b2fc195eSAndrew Gallatin {
1169b2fc195eSAndrew Gallatin         int idx, i;
1170b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1171b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1172b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1173b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
11745e7d8541SAndrew Gallatin 	uint8_t last_flags;
1175b2fc195eSAndrew Gallatin 
1176b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1177b2fc195eSAndrew Gallatin 
11785e7d8541SAndrew Gallatin 	last_flags = src->flags;
11795e7d8541SAndrew Gallatin 	src->flags = 0;
1180b2fc195eSAndrew Gallatin         mb();
1181b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1182b2fc195eSAndrew Gallatin         srcp = src;
1183b2fc195eSAndrew Gallatin 
1184b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1185b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
11866d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1187b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1188b2fc195eSAndrew Gallatin                         srcp += 2;
1189b2fc195eSAndrew Gallatin                         dstp += 2;
1190b2fc195eSAndrew Gallatin                 }
1191b2fc195eSAndrew Gallatin         } else {
1192b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1193b2fc195eSAndrew Gallatin                    that it is submitted below */
11946d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1195b2fc195eSAndrew Gallatin                 i = 0;
1196b2fc195eSAndrew Gallatin         }
1197b2fc195eSAndrew Gallatin         if (i < cnt) {
1198b2fc195eSAndrew Gallatin                 /* submit the first request */
11996d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1200b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1201b2fc195eSAndrew Gallatin         }
1202b2fc195eSAndrew Gallatin 
1203b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
12045e7d8541SAndrew Gallatin         src->flags = last_flags;
1205b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1206b2fc195eSAndrew Gallatin         src_ints+=3;
1207b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1208b2fc195eSAndrew Gallatin         dst_ints+=3;
1209b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1210b2fc195eSAndrew Gallatin         tx->req += cnt;
1211b2fc195eSAndrew Gallatin         mb();
1212b2fc195eSAndrew Gallatin }
1213b2fc195eSAndrew Gallatin 
1214b2fc195eSAndrew Gallatin static inline void
12156d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt)
1216b2fc195eSAndrew Gallatin {
1217b2fc195eSAndrew Gallatin     tx->req += cnt;
1218b2fc195eSAndrew Gallatin     mb();
1219b2fc195eSAndrew Gallatin     while (cnt >= 4) {
12206d87a65dSAndrew Gallatin 	    mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64);
1221b2fc195eSAndrew Gallatin 	    mb();
1222b2fc195eSAndrew Gallatin 	    src += 4;
1223b2fc195eSAndrew Gallatin 	    cnt -= 4;
1224b2fc195eSAndrew Gallatin     }
1225b2fc195eSAndrew Gallatin     if (cnt > 0) {
1226b2fc195eSAndrew Gallatin 	    /* pad it to 64 bytes.  The src is 64 bytes bigger than it
1227b2fc195eSAndrew Gallatin 	       needs to be so that we don't overrun it */
12286d87a65dSAndrew Gallatin 	    mxge_pio_copy(tx->wc_fifo + (cnt<<18), src, 64);
1229b2fc195eSAndrew Gallatin 	    mb();
1230b2fc195eSAndrew Gallatin     }
1231b2fc195eSAndrew Gallatin }
1232b2fc195eSAndrew Gallatin 
1233b2fc195eSAndrew Gallatin static void
1234aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt)
1235aed8e389SAndrew Gallatin {
1236aed8e389SAndrew Gallatin 	mxge_tx_buf_t *tx;
1237aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1238aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1239aed8e389SAndrew Gallatin 	struct ether_header *eh;
1240aed8e389SAndrew Gallatin 	struct ip *ip;
1241aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1242aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1243aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1244aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1245aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1246aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1247aed8e389SAndrew Gallatin 	static int once;
1248aed8e389SAndrew Gallatin 
1249aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1250aed8e389SAndrew Gallatin 
1251aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1252aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1253aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1254aed8e389SAndrew Gallatin 	 */
1255aed8e389SAndrew Gallatin 
1256aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1257aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1258aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1259aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh)
1260aed8e389SAndrew Gallatin 			    + sizeof (*ip))) {
1261aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1262aed8e389SAndrew Gallatin 			   sc->scratch);
1263aed8e389SAndrew Gallatin 		eh = (struct ether_header *)sc->scratch;
1264aed8e389SAndrew Gallatin 	} else {
1265aed8e389SAndrew Gallatin 		eh = mtod(m, struct ether_header *);
1266aed8e389SAndrew Gallatin 	}
1267aed8e389SAndrew Gallatin 	ip = (struct ip *) (eh + 1);
1268aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2)
1269aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1270aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2)
1271aed8e389SAndrew Gallatin 			   + sizeof (*tcp),  sc->scratch);
1272aed8e389SAndrew Gallatin 		eh = (struct ether_header *) sc->scratch;
1273aed8e389SAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1274aed8e389SAndrew Gallatin 	}
1275aed8e389SAndrew Gallatin 
1276aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1277aed8e389SAndrew Gallatin 	cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2));
1278aed8e389SAndrew Gallatin 
1279aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1280aed8e389SAndrew Gallatin 	cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1281aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1282aed8e389SAndrew Gallatin 
1283aed8e389SAndrew Gallatin 
1284aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1285aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1286aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1287aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1288aed8e389SAndrew Gallatin 
1289aed8e389SAndrew Gallatin 	tx = &sc->tx;
1290aed8e389SAndrew Gallatin 	req = tx->req_list;
1291aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1292aed8e389SAndrew Gallatin 	cnt = 0;
1293aed8e389SAndrew Gallatin 	rdma_count = 0;
1294aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1295aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1296aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1297aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1298aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1299aed8e389SAndrew Gallatin 	 *
1300aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1301aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1302aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1303aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1304aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1305aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1306aed8e389SAndrew Gallatin 	 *
1307aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1308aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1309aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1310aed8e389SAndrew Gallatin 	 */
1311aed8e389SAndrew Gallatin 
1312aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1313aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1314aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1315aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1316aed8e389SAndrew Gallatin 		len = seglen = seg->ds_len;
1317aed8e389SAndrew Gallatin 
1318aed8e389SAndrew Gallatin 		while (len) {
1319aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1320aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1321aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1322aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1323aed8e389SAndrew Gallatin 				/* payload */
1324aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1325aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1326aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1327aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1328aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1329aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1330aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1331aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1332aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1333aed8e389SAndrew Gallatin 				/* header ends */
1334aed8e389SAndrew Gallatin 				rdma_count = -1;
1335aed8e389SAndrew Gallatin 				cum_len_next = 0;
1336aed8e389SAndrew Gallatin 				seglen = -cum_len;
1337aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1338aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1339aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1340aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1341aed8e389SAndrew Gallatin 			    }
1342aed8e389SAndrew Gallatin 
1343aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1344aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1345aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1346aed8e389SAndrew Gallatin 			req->pad = 0;
1347aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1348aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1349aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1350aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1351aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1352aed8e389SAndrew Gallatin 			low += seglen;
1353aed8e389SAndrew Gallatin 			len -= seglen;
1354aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1355aed8e389SAndrew Gallatin 			flags = flags_next;
1356aed8e389SAndrew Gallatin 			req++;
1357aed8e389SAndrew Gallatin 			cnt++;
1358aed8e389SAndrew Gallatin 			rdma_count++;
1359aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1360aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1361aed8e389SAndrew Gallatin 			else
1362aed8e389SAndrew Gallatin 				cksum_offset = 0;
1363aed8e389SAndrew Gallatin 			if (__predict_false(cnt > MXGE_MAX_SEND_DESC))
1364aed8e389SAndrew Gallatin 				goto drop;
1365aed8e389SAndrew Gallatin 		}
1366aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1367aed8e389SAndrew Gallatin 		seg++;
1368aed8e389SAndrew Gallatin 	}
1369aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1370aed8e389SAndrew Gallatin 
1371aed8e389SAndrew Gallatin 	do {
1372aed8e389SAndrew Gallatin 		req--;
1373aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1374aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1375aed8e389SAndrew Gallatin 
1376aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1377aed8e389SAndrew Gallatin 	if (tx->wc_fifo == NULL)
1378aed8e389SAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1379aed8e389SAndrew Gallatin 	else
1380aed8e389SAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1381aed8e389SAndrew Gallatin 	return;
1382aed8e389SAndrew Gallatin 
1383aed8e389SAndrew Gallatin drop:
1384aed8e389SAndrew Gallatin 	m_freem(m);
1385aed8e389SAndrew Gallatin 	sc->ifp->if_oerrors++;
1386aed8e389SAndrew Gallatin 	if (!once) {
1387aed8e389SAndrew Gallatin 		printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n");
1388aed8e389SAndrew Gallatin 		printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list);
1389aed8e389SAndrew Gallatin 		once = 1;
1390aed8e389SAndrew Gallatin 	}
1391aed8e389SAndrew Gallatin 	return;
1392aed8e389SAndrew Gallatin 
1393aed8e389SAndrew Gallatin }
1394aed8e389SAndrew Gallatin 
1395aed8e389SAndrew Gallatin static void
13966d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m)
1397b2fc195eSAndrew Gallatin {
1398b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1399b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1400b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1401b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
14026d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1403b2fc195eSAndrew Gallatin 	struct ether_header *eh;
1404b2fc195eSAndrew Gallatin 	struct ip *ip;
1405aed8e389SAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
1406aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1407aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1408b2fc195eSAndrew Gallatin 
1409b2fc195eSAndrew Gallatin 
1410b2fc195eSAndrew Gallatin 
1411b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1412b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1413b2fc195eSAndrew Gallatin 
1414b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1415b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1416b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1417aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1418b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1419b2fc195eSAndrew Gallatin 	if (err == EFBIG) {
1420b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1421b2fc195eSAndrew Gallatin 		   to defrag */
1422b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1423b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1424b2fc195eSAndrew Gallatin 			goto drop;
1425b2fc195eSAndrew Gallatin 		}
1426b2fc195eSAndrew Gallatin 		m = m_tmp;
1427b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1428b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1429aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1430b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1431b2fc195eSAndrew Gallatin 	}
1432b2fc195eSAndrew Gallatin 	if (err != 0) {
1433aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1434aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1435b2fc195eSAndrew Gallatin 		goto drop;
1436b2fc195eSAndrew Gallatin 	}
1437b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1438b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
14395e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1440b2fc195eSAndrew Gallatin 
1441aed8e389SAndrew Gallatin 
1442aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1443aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
1444aed8e389SAndrew Gallatin 		mxge_encap_tso(sc, m, cnt);
1445aed8e389SAndrew Gallatin 		return;
1446aed8e389SAndrew Gallatin 	}
1447aed8e389SAndrew Gallatin 
1448b2fc195eSAndrew Gallatin 	req = tx->req_list;
1449b2fc195eSAndrew Gallatin 	cksum_offset = 0;
14505e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
14515e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1452b2fc195eSAndrew Gallatin 
1453b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1454b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1455aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
1456aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
1457aed8e389SAndrew Gallatin 		if (__predict_false(m->m_len < sizeof (*eh)
1458aed8e389SAndrew Gallatin 				    + sizeof (*ip))) {
1459aed8e389SAndrew Gallatin 			m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1460aed8e389SAndrew Gallatin 				   sc->scratch);
1461aed8e389SAndrew Gallatin 			eh = (struct ether_header *)sc->scratch;
1462aed8e389SAndrew Gallatin 		} else {
1463b2fc195eSAndrew Gallatin 			eh = mtod(m, struct ether_header *);
1464aed8e389SAndrew Gallatin 		}
1465b2fc195eSAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1466b2fc195eSAndrew Gallatin 		cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1467b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
14685e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1469b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
14705e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1471aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
1472aed8e389SAndrew Gallatin 	} else {
1473aed8e389SAndrew Gallatin 		odd_flag = 0;
1474b2fc195eSAndrew Gallatin 	}
14755e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
14765e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
1477b2fc195eSAndrew Gallatin 
1478b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
1479b2fc195eSAndrew Gallatin 	cum_len = 0;
1480aed8e389SAndrew Gallatin 	seg = tx->seg_list;
14815e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
1482b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1483b2fc195eSAndrew Gallatin 		req->addr_low =
14846d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1485b2fc195eSAndrew Gallatin 		req->addr_high =
14866d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1487b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
1488b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
1489b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
1490b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
1491b2fc195eSAndrew Gallatin 		else
1492b2fc195eSAndrew Gallatin 			cksum_offset = 0;
14935e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
14945e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
14955e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1496aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1497b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
1498b2fc195eSAndrew Gallatin 		seg++;
1499b2fc195eSAndrew Gallatin 		req++;
1500b2fc195eSAndrew Gallatin 		req->flags = 0;
1501b2fc195eSAndrew Gallatin 	}
1502b2fc195eSAndrew Gallatin 	req--;
1503b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
1504b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
1505b2fc195eSAndrew Gallatin 		req++;
1506b2fc195eSAndrew Gallatin 		req->addr_low =
15076d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
1508b2fc195eSAndrew Gallatin 		req->addr_high =
15096d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
1510b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
15115e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
15125e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
15135e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
15145e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1515aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1516b2fc195eSAndrew Gallatin 		cnt++;
1517b2fc195eSAndrew Gallatin 	}
15185e7d8541SAndrew Gallatin 
15195e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
15205e7d8541SAndrew Gallatin #if 0
15215e7d8541SAndrew Gallatin 	/* print what the firmware will see */
15225e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
15235e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
15245e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
15255e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
15265e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
15275e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
15285e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
15295e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
15305e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
15315e7d8541SAndrew Gallatin 	}
15325e7d8541SAndrew Gallatin 	printf("--------------\n");
15335e7d8541SAndrew Gallatin #endif
15345e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1535b2fc195eSAndrew Gallatin 	if (tx->wc_fifo == NULL)
15366d87a65dSAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1537b2fc195eSAndrew Gallatin 	else
15386d87a65dSAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1539b2fc195eSAndrew Gallatin 	return;
1540b2fc195eSAndrew Gallatin 
1541b2fc195eSAndrew Gallatin drop:
1542b2fc195eSAndrew Gallatin 	m_freem(m);
1543b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
1544b2fc195eSAndrew Gallatin 	return;
1545b2fc195eSAndrew Gallatin }
1546b2fc195eSAndrew Gallatin 
1547b2fc195eSAndrew Gallatin 
15486d914a32SAndrew Gallatin 
15496d914a32SAndrew Gallatin 
15506d914a32SAndrew Gallatin static inline void
15516d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc)
1552b2fc195eSAndrew Gallatin {
1553b2fc195eSAndrew Gallatin 	struct mbuf *m;
1554b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1555b2fc195eSAndrew Gallatin 
1556b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
15576d914a32SAndrew Gallatin 	while ((sc->tx.mask - (sc->tx.req - sc->tx.done))
15586d914a32SAndrew Gallatin 	       > MXGE_MAX_SEND_DESC) {
1559b2fc195eSAndrew Gallatin 
15606d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
15616d914a32SAndrew Gallatin 		if (m == NULL) {
15626d914a32SAndrew Gallatin 			return;
15636d914a32SAndrew Gallatin 		}
1564b2fc195eSAndrew Gallatin 		/* let BPF see it */
1565b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
1566b2fc195eSAndrew Gallatin 
1567b2fc195eSAndrew Gallatin 		/* give it to the nic */
15686d87a65dSAndrew Gallatin 		mxge_encap(sc, m);
15696d914a32SAndrew Gallatin 	}
15706d914a32SAndrew Gallatin 	/* ran out of transmit slots */
1571b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1572b2fc195eSAndrew Gallatin }
1573b2fc195eSAndrew Gallatin 
1574b2fc195eSAndrew Gallatin static void
15756d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
1576b2fc195eSAndrew Gallatin {
15776d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
1578b2fc195eSAndrew Gallatin 
1579b2fc195eSAndrew Gallatin 
1580b2fc195eSAndrew Gallatin 	mtx_lock(&sc->tx_lock);
15816d87a65dSAndrew Gallatin 	mxge_start_locked(sc);
1582b2fc195eSAndrew Gallatin 	mtx_unlock(&sc->tx_lock);
1583b2fc195eSAndrew Gallatin }
1584b2fc195eSAndrew Gallatin 
15855e7d8541SAndrew Gallatin /*
15865e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
15875e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
15885e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
15895e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
15905e7d8541SAndrew Gallatin  * in a burst
15915e7d8541SAndrew Gallatin  */
15925e7d8541SAndrew Gallatin static inline void
15935e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
15945e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
15955e7d8541SAndrew Gallatin {
15965e7d8541SAndrew Gallatin 	uint32_t low;
15975e7d8541SAndrew Gallatin 
15985e7d8541SAndrew Gallatin 	low = src->addr_low;
15995e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
16005e7d8541SAndrew Gallatin 	mxge_pio_copy(dst, src, 8 * sizeof (*src));
16015e7d8541SAndrew Gallatin 	mb();
16025e7d8541SAndrew Gallatin 	dst->addr_low = low;
16035e7d8541SAndrew Gallatin 	mb();
16045e7d8541SAndrew Gallatin }
16055e7d8541SAndrew Gallatin 
1606b2fc195eSAndrew Gallatin static int
16076d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1608b2fc195eSAndrew Gallatin {
1609b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1610b2fc195eSAndrew Gallatin 	struct mbuf *m;
16116d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_small;
1612b2fc195eSAndrew Gallatin 	int cnt, err;
1613b2fc195eSAndrew Gallatin 
1614b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
1615b2fc195eSAndrew Gallatin 	if (m == NULL) {
1616b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1617b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1618b2fc195eSAndrew Gallatin 		goto done;
1619b2fc195eSAndrew Gallatin 	}
1620b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
1621b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1622b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1623b2fc195eSAndrew Gallatin 	if (err != 0) {
1624b2fc195eSAndrew Gallatin 		m_free(m);
1625b2fc195eSAndrew Gallatin 		goto done;
1626b2fc195eSAndrew Gallatin 	}
1627b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1628b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
16296d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1630b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
16316d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1632b2fc195eSAndrew Gallatin 
1633b2fc195eSAndrew Gallatin done:
1634b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
16355e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
16365e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
16375e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
16385e7d8541SAndrew Gallatin 		else {
1639b2fc195eSAndrew Gallatin 			mb();
16405e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
16415e7d8541SAndrew Gallatin 		}
1642b2fc195eSAndrew Gallatin         }
1643b2fc195eSAndrew Gallatin 	return err;
1644b2fc195eSAndrew Gallatin }
1645b2fc195eSAndrew Gallatin 
1646b2fc195eSAndrew Gallatin static int
16476d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1648b2fc195eSAndrew Gallatin {
1649b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1650b2fc195eSAndrew Gallatin 	struct mbuf *m;
16516d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_big;
1652b2fc195eSAndrew Gallatin 	int cnt, err;
1653b2fc195eSAndrew Gallatin 
1654b2fc195eSAndrew Gallatin 	m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes);
1655b2fc195eSAndrew Gallatin 	if (m == NULL) {
1656b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1657b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1658b2fc195eSAndrew Gallatin 		goto done;
1659b2fc195eSAndrew Gallatin 	}
1660b2fc195eSAndrew Gallatin 	m->m_len = sc->big_bytes;
1661b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1662b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1663b2fc195eSAndrew Gallatin 	if (err != 0) {
1664b2fc195eSAndrew Gallatin 		m_free(m);
1665b2fc195eSAndrew Gallatin 		goto done;
1666b2fc195eSAndrew Gallatin 	}
1667b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1668b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
16696d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1670b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
16716d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1672b2fc195eSAndrew Gallatin 
1673b2fc195eSAndrew Gallatin done:
1674b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
16755e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
16765e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
16775e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
16785e7d8541SAndrew Gallatin 		else {
1679b2fc195eSAndrew Gallatin 			mb();
16805e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
16815e7d8541SAndrew Gallatin 		}
1682b2fc195eSAndrew Gallatin         }
1683b2fc195eSAndrew Gallatin 	return err;
1684b2fc195eSAndrew Gallatin }
1685b2fc195eSAndrew Gallatin 
1686b2fc195eSAndrew Gallatin static inline void
16875e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
16885e7d8541SAndrew Gallatin {
16895e7d8541SAndrew Gallatin 	struct ether_header *eh;
16905e7d8541SAndrew Gallatin 	struct ip *ip;
16915e7d8541SAndrew Gallatin 
16925e7d8541SAndrew Gallatin 	eh = mtod(m, struct ether_header *);
16935e7d8541SAndrew Gallatin 	if (__predict_true(eh->ether_type ==  htons(ETHERTYPE_IP))) {
16945e7d8541SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
16955e7d8541SAndrew Gallatin 		if (__predict_true(ip->ip_p == IPPROTO_TCP ||
16965e7d8541SAndrew Gallatin 				   ip->ip_p == IPPROTO_UDP)) {
16975e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_data = csum;
16985e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_flags = CSUM_DATA_VALID;
16995e7d8541SAndrew Gallatin 		}
17005e7d8541SAndrew Gallatin 	}
17015e7d8541SAndrew Gallatin }
17025e7d8541SAndrew Gallatin 
17035e7d8541SAndrew Gallatin static inline void
17045e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum)
1705b2fc195eSAndrew Gallatin {
1706b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1707b2fc195eSAndrew Gallatin 	struct mbuf *m = 0; 		/* -Wunitialized */
1708b2fc195eSAndrew Gallatin 	struct mbuf *m_prev = 0;	/* -Wunitialized */
1709b2fc195eSAndrew Gallatin 	struct mbuf *m_head = 0;
1710b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
17116d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1712b2fc195eSAndrew Gallatin 	int idx;
1713b2fc195eSAndrew Gallatin 
1714b2fc195eSAndrew Gallatin 
1715b2fc195eSAndrew Gallatin 	rx = &sc->rx_big;
1716b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1717b2fc195eSAndrew Gallatin 	while (len > 0) {
1718b2fc195eSAndrew Gallatin 		idx = rx->cnt & rx->mask;
1719b2fc195eSAndrew Gallatin                 rx->cnt++;
1720b2fc195eSAndrew Gallatin 		/* save a pointer to the received mbuf */
1721b2fc195eSAndrew Gallatin 		m = rx->info[idx].m;
1722b2fc195eSAndrew Gallatin 		/* try to replace the received mbuf */
17236d87a65dSAndrew Gallatin 		if (mxge_get_buf_big(sc, rx->extra_map, idx)) {
1724b2fc195eSAndrew Gallatin 			goto drop;
1725b2fc195eSAndrew Gallatin 		}
1726b2fc195eSAndrew Gallatin 		/* unmap the received buffer */
1727b2fc195eSAndrew Gallatin 		old_map = rx->info[idx].map;
1728b2fc195eSAndrew Gallatin 		bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1729b2fc195eSAndrew Gallatin 		bus_dmamap_unload(rx->dmat, old_map);
1730b2fc195eSAndrew Gallatin 
1731b2fc195eSAndrew Gallatin 		/* swap the bus_dmamap_t's */
1732b2fc195eSAndrew Gallatin 		rx->info[idx].map = rx->extra_map;
1733b2fc195eSAndrew Gallatin 		rx->extra_map = old_map;
1734b2fc195eSAndrew Gallatin 
1735b2fc195eSAndrew Gallatin 		/* chain multiple segments together */
1736b2fc195eSAndrew Gallatin 		if (!m_head) {
1737b2fc195eSAndrew Gallatin 			m_head = m;
1738b2fc195eSAndrew Gallatin 			/* mcp implicitly skips 1st bytes so that
1739b2fc195eSAndrew Gallatin 			 * packet is properly aligned */
17405e7d8541SAndrew Gallatin 			m->m_data += MXGEFW_PAD;
1741b2fc195eSAndrew Gallatin 			m->m_pkthdr.len = len;
17425e7d8541SAndrew Gallatin 			m->m_len = sc->big_bytes - MXGEFW_PAD;
1743b2fc195eSAndrew Gallatin 		} else {
1744b2fc195eSAndrew Gallatin 			m->m_len = sc->big_bytes;
1745b2fc195eSAndrew Gallatin 			m->m_flags &= ~M_PKTHDR;
1746b2fc195eSAndrew Gallatin 			m_prev->m_next = m;
1747b2fc195eSAndrew Gallatin 		}
1748b2fc195eSAndrew Gallatin 		len -= m->m_len;
1749b2fc195eSAndrew Gallatin 		m_prev = m;
1750b2fc195eSAndrew Gallatin 	}
1751b2fc195eSAndrew Gallatin 
1752b2fc195eSAndrew Gallatin 	/* trim trailing garbage from the last mbuf in the chain.  If
1753b2fc195eSAndrew Gallatin 	 * there is any garbage, len will be negative */
1754b2fc195eSAndrew Gallatin 	m->m_len += len;
1755b2fc195eSAndrew Gallatin 
1756b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
17575e7d8541SAndrew Gallatin 	if (sc->csum_flag)
17585e7d8541SAndrew Gallatin 		mxge_rx_csum(m_head, csum);
1759b2fc195eSAndrew Gallatin 
1760b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1761b2fc195eSAndrew Gallatin 	m_head->m_pkthdr.rcvif = ifp;
1762b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1763b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m_head);
1764b2fc195eSAndrew Gallatin 	return;
1765b2fc195eSAndrew Gallatin 
1766b2fc195eSAndrew Gallatin drop:
1767b2fc195eSAndrew Gallatin 	/* drop the frame -- the old mbuf(s) are re-cycled by running
1768b2fc195eSAndrew Gallatin 	   every slot through the allocator */
1769b2fc195eSAndrew Gallatin         if (m_head) {
1770b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1771b2fc195eSAndrew Gallatin                 m_freem(m_head);
1772b2fc195eSAndrew Gallatin         } else {
17735e7d8541SAndrew Gallatin                 len -= (sc->big_bytes + MXGEFW_PAD);
1774b2fc195eSAndrew Gallatin         }
1775b2fc195eSAndrew Gallatin         while ((int)len > 0) {
1776b2fc195eSAndrew Gallatin                 idx = rx->cnt & rx->mask;
1777b2fc195eSAndrew Gallatin                 rx->cnt++;
1778b2fc195eSAndrew Gallatin                 m = rx->info[idx].m;
17796d87a65dSAndrew Gallatin                 if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) {
1780b2fc195eSAndrew Gallatin 			m_freem(m);
1781b2fc195eSAndrew Gallatin 			/* unmap the received buffer */
1782b2fc195eSAndrew Gallatin 			old_map = rx->info[idx].map;
1783b2fc195eSAndrew Gallatin 			bus_dmamap_sync(rx->dmat, old_map,
1784b2fc195eSAndrew Gallatin 					BUS_DMASYNC_POSTREAD);
1785b2fc195eSAndrew Gallatin 			bus_dmamap_unload(rx->dmat, old_map);
1786b2fc195eSAndrew Gallatin 
1787b2fc195eSAndrew Gallatin 			/* swap the bus_dmamap_t's */
1788b2fc195eSAndrew Gallatin 			rx->info[idx].map = rx->extra_map;
1789b2fc195eSAndrew Gallatin 			rx->extra_map = old_map;
1790b2fc195eSAndrew Gallatin 		}
1791b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1792b2fc195eSAndrew Gallatin         }
1793b2fc195eSAndrew Gallatin 
1794b2fc195eSAndrew Gallatin 	ifp->if_ierrors++;
1795b2fc195eSAndrew Gallatin 
1796b2fc195eSAndrew Gallatin }
1797b2fc195eSAndrew Gallatin 
1798b2fc195eSAndrew Gallatin static inline void
17995e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum)
1800b2fc195eSAndrew Gallatin {
1801b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1802b2fc195eSAndrew Gallatin 	struct mbuf *m;
18036d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1804b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
1805b2fc195eSAndrew Gallatin 	int idx;
1806b2fc195eSAndrew Gallatin 
1807b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1808b2fc195eSAndrew Gallatin 	rx = &sc->rx_small;
1809b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
1810b2fc195eSAndrew Gallatin 	rx->cnt++;
1811b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
1812b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
1813b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
18146d87a65dSAndrew Gallatin 	if (mxge_get_buf_small(sc, rx->extra_map, idx)) {
1815b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
1816b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
1817b2fc195eSAndrew Gallatin 		return;
1818b2fc195eSAndrew Gallatin 	}
1819b2fc195eSAndrew Gallatin 
1820b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
1821b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
1822b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1823b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
1824b2fc195eSAndrew Gallatin 
1825b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
1826b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
1827b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
1828b2fc195eSAndrew Gallatin 
1829b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
1830b2fc195eSAndrew Gallatin 	 * aligned */
18315e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
1832b2fc195eSAndrew Gallatin 
1833b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
18345e7d8541SAndrew Gallatin 	if (sc->csum_flag)
18355e7d8541SAndrew Gallatin 		mxge_rx_csum(m, csum);
1836b2fc195eSAndrew Gallatin 
1837b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1838b2fc195eSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
1839b2fc195eSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
1840b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1841b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
1842b2fc195eSAndrew Gallatin }
1843b2fc195eSAndrew Gallatin 
1844b2fc195eSAndrew Gallatin static inline void
18455e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc)
18465e7d8541SAndrew Gallatin {
18475e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
18485e7d8541SAndrew Gallatin 	int limit = 0;
18495e7d8541SAndrew Gallatin 	uint16_t length;
18505e7d8541SAndrew Gallatin 	uint16_t checksum;
18515e7d8541SAndrew Gallatin 
18525e7d8541SAndrew Gallatin 
18535e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
18545e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
18555e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
18565e7d8541SAndrew Gallatin 		checksum = ntohs(rx_done->entry[rx_done->idx].checksum);
18575e7d8541SAndrew Gallatin 		if (length <= MHLEN)
18585e7d8541SAndrew Gallatin 			mxge_rx_done_small(sc, length, checksum);
18595e7d8541SAndrew Gallatin 		else
18605e7d8541SAndrew Gallatin 			mxge_rx_done_big(sc, length, checksum);
18615e7d8541SAndrew Gallatin 		rx_done->cnt++;
18625e7d8541SAndrew Gallatin 		rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1);
18635e7d8541SAndrew Gallatin 
18645e7d8541SAndrew Gallatin 		/* limit potential for livelock */
18655e7d8541SAndrew Gallatin 		if (__predict_false(++limit > 2 * mxge_max_intr_slots))
18665e7d8541SAndrew Gallatin 			break;
18675e7d8541SAndrew Gallatin 
18685e7d8541SAndrew Gallatin 	}
18695e7d8541SAndrew Gallatin }
18705e7d8541SAndrew Gallatin 
18715e7d8541SAndrew Gallatin 
18725e7d8541SAndrew Gallatin static inline void
18736d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx)
1874b2fc195eSAndrew Gallatin {
1875b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
18766d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1877b2fc195eSAndrew Gallatin 	struct mbuf *m;
1878b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
18795e7d8541SAndrew Gallatin 	int idx, limit;
1880b2fc195eSAndrew Gallatin 
18815e7d8541SAndrew Gallatin 	limit = 0;
1882b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1883b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
18845e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
1885b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
1886b2fc195eSAndrew Gallatin 		tx->done++;
1887b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
1888b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
1889b2fc195eSAndrew Gallatin 		   segment per-mbuf */
1890b2fc195eSAndrew Gallatin 		if (m != NULL) {
1891b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
1892b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
1893b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
1894b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
1895b2fc195eSAndrew Gallatin 			m_freem(m);
1896b2fc195eSAndrew Gallatin 		}
18975e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
18985e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
18995e7d8541SAndrew Gallatin 			tx->pkt_done++;
19005e7d8541SAndrew Gallatin 		}
19015e7d8541SAndrew Gallatin 		/* limit potential for livelock by only handling
19025e7d8541SAndrew Gallatin 		   2 full tx rings per call */
19035e7d8541SAndrew Gallatin 		if (__predict_false(++limit >  2 * tx->mask))
19045e7d8541SAndrew Gallatin 			break;
1905b2fc195eSAndrew Gallatin 	}
1906b2fc195eSAndrew Gallatin 
1907b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
1908b2fc195eSAndrew Gallatin            its OK to send packets */
1909b2fc195eSAndrew Gallatin 
1910b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
1911b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
1912b2fc195eSAndrew Gallatin 		mtx_lock(&sc->tx_lock);
1913b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
19146d87a65dSAndrew Gallatin 		mxge_start_locked(sc);
1915b2fc195eSAndrew Gallatin 		mtx_unlock(&sc->tx_lock);
1916b2fc195eSAndrew Gallatin 	}
1917b2fc195eSAndrew Gallatin }
1918b2fc195eSAndrew Gallatin 
1919b2fc195eSAndrew Gallatin static void
19206d87a65dSAndrew Gallatin mxge_intr(void *arg)
1921b2fc195eSAndrew Gallatin {
19226d87a65dSAndrew Gallatin 	mxge_softc_t *sc = arg;
19235e7d8541SAndrew Gallatin 	mcp_irq_data_t *stats = sc->fw_stats;
19245e7d8541SAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
19255e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
19265e7d8541SAndrew Gallatin 	uint32_t send_done_count;
19275e7d8541SAndrew Gallatin 	uint8_t valid;
1928b2fc195eSAndrew Gallatin 
1929b2fc195eSAndrew Gallatin 
19305e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
19315e7d8541SAndrew Gallatin 	if (!stats->valid) {
19325e7d8541SAndrew Gallatin 		return;
1933b2fc195eSAndrew Gallatin 	}
19345e7d8541SAndrew Gallatin 	valid = stats->valid;
1935b2fc195eSAndrew Gallatin 
19365e7d8541SAndrew Gallatin 	/* lower legacy IRQ  */
19375e7d8541SAndrew Gallatin 	*sc->irq_deassert = 0;
1938b2fc195eSAndrew Gallatin 	mb();
19395e7d8541SAndrew Gallatin 	if (!mxge_deassert_wait)
19405e7d8541SAndrew Gallatin 		/* don't wait for conf. that irq is low */
19415e7d8541SAndrew Gallatin 		stats->valid = 0;
19425e7d8541SAndrew Gallatin 	do {
19435e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
19445e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
19455e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
19465e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
19475e7d8541SAndrew Gallatin 			mxge_tx_done(sc, (int)send_done_count);
19485e7d8541SAndrew Gallatin 			mxge_clean_rx_done(sc);
19495e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
1950b2fc195eSAndrew Gallatin 		}
19515e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
1952b2fc195eSAndrew Gallatin 
19535e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
19545e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
19555e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
1956b2fc195eSAndrew Gallatin 			if (sc->link_state) {
19575e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
19585e7d8541SAndrew Gallatin 				if (mxge_verbose)
19595e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
1960b2fc195eSAndrew Gallatin 			} else {
19615e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
19625e7d8541SAndrew Gallatin 				if (mxge_verbose)
19635e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
1964b2fc195eSAndrew Gallatin 			}
1965b2fc195eSAndrew Gallatin 		}
1966b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
1967b2fc195eSAndrew Gallatin 		    be32toh(sc->fw_stats->rdma_tags_available)) {
1968b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
1969b2fc195eSAndrew Gallatin 				be32toh(sc->fw_stats->rdma_tags_available);
19705e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
19715e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
19725e7d8541SAndrew Gallatin 		}
19735e7d8541SAndrew Gallatin 		sc->down_cnt += stats->link_down;
1974b2fc195eSAndrew Gallatin 	}
1975b2fc195eSAndrew Gallatin 
19765e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
19775e7d8541SAndrew Gallatin 	if (valid & 0x1)
19785e7d8541SAndrew Gallatin 	    *sc->irq_claim = be32toh(3);
19795e7d8541SAndrew Gallatin 	*(sc->irq_claim + 1) = be32toh(3);
1980b2fc195eSAndrew Gallatin }
1981b2fc195eSAndrew Gallatin 
1982b2fc195eSAndrew Gallatin static void
19836d87a65dSAndrew Gallatin mxge_watchdog(struct ifnet *ifp)
1984b2fc195eSAndrew Gallatin {
1985b2fc195eSAndrew Gallatin 	printf("%s called\n", __FUNCTION__);
1986b2fc195eSAndrew Gallatin }
1987b2fc195eSAndrew Gallatin 
1988b2fc195eSAndrew Gallatin static void
19896d87a65dSAndrew Gallatin mxge_init(void *arg)
1990b2fc195eSAndrew Gallatin {
1991b2fc195eSAndrew Gallatin }
1992b2fc195eSAndrew Gallatin 
1993b2fc195eSAndrew Gallatin 
1994b2fc195eSAndrew Gallatin 
1995b2fc195eSAndrew Gallatin static void
19966d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
1997b2fc195eSAndrew Gallatin {
1998b2fc195eSAndrew Gallatin 	int i;
1999b2fc195eSAndrew Gallatin 
2000b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2001b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2002b2fc195eSAndrew Gallatin 			continue;
2003b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2004b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2005b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2006b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2007b2fc195eSAndrew Gallatin 	}
2008b2fc195eSAndrew Gallatin 
2009b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2010b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2011b2fc195eSAndrew Gallatin 			continue;
2012b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2013b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2014b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2015b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2016b2fc195eSAndrew Gallatin 	}
2017b2fc195eSAndrew Gallatin 
2018b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2019b2fc195eSAndrew Gallatin 		if (sc->tx.info[i].m == NULL)
2020b2fc195eSAndrew Gallatin 			continue;
2021b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->tx.dmat,
2022b2fc195eSAndrew Gallatin 				  sc->tx.info[i].map);
2023b2fc195eSAndrew Gallatin 		m_freem(sc->tx.info[i].m);
2024b2fc195eSAndrew Gallatin 		sc->tx.info[i].m = NULL;
2025b2fc195eSAndrew Gallatin 	}
2026b2fc195eSAndrew Gallatin }
2027b2fc195eSAndrew Gallatin 
2028b2fc195eSAndrew Gallatin static void
20296d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2030b2fc195eSAndrew Gallatin {
2031b2fc195eSAndrew Gallatin 	int i;
2032b2fc195eSAndrew Gallatin 
2033aed8e389SAndrew Gallatin 	if (sc->tx.req_bytes != NULL)
2034b2fc195eSAndrew Gallatin 		free(sc->tx.req_bytes, M_DEVBUF);
2035aed8e389SAndrew Gallatin 	if (sc->tx.seg_list != NULL)
2036aed8e389SAndrew Gallatin 		free(sc->tx.seg_list, M_DEVBUF);
2037b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow != NULL)
2038b2fc195eSAndrew Gallatin 		free(sc->rx_small.shadow, M_DEVBUF);
2039b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow != NULL)
2040b2fc195eSAndrew Gallatin 		free(sc->rx_big.shadow, M_DEVBUF);
2041b2fc195eSAndrew Gallatin 	if (sc->tx.info != NULL) {
2042b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->tx.mask; i++) {
2043b2fc195eSAndrew Gallatin 			if (sc->tx.info[i].map != NULL)
2044b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->tx.dmat,
2045b2fc195eSAndrew Gallatin 						   sc->tx.info[i].map);
2046b2fc195eSAndrew Gallatin 		}
2047b2fc195eSAndrew Gallatin 		free(sc->tx.info, M_DEVBUF);
2048b2fc195eSAndrew Gallatin 	}
2049b2fc195eSAndrew Gallatin 	if (sc->rx_small.info != NULL) {
2050b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->rx_small.mask; i++) {
2051b2fc195eSAndrew Gallatin 			if (sc->rx_small.info[i].map != NULL)
2052b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_small.dmat,
2053b2fc195eSAndrew Gallatin 						   sc->rx_small.info[i].map);
2054b2fc195eSAndrew Gallatin 		}
2055b2fc195eSAndrew Gallatin 		free(sc->rx_small.info, M_DEVBUF);
2056b2fc195eSAndrew Gallatin 	}
2057b2fc195eSAndrew Gallatin 	if (sc->rx_big.info != NULL) {
2058b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->rx_big.mask; i++) {
2059b2fc195eSAndrew Gallatin 			if (sc->rx_big.info[i].map != NULL)
2060b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_big.dmat,
2061b2fc195eSAndrew Gallatin 						   sc->rx_big.info[i].map);
2062b2fc195eSAndrew Gallatin 		}
2063b2fc195eSAndrew Gallatin 		free(sc->rx_big.info, M_DEVBUF);
2064b2fc195eSAndrew Gallatin 	}
2065b2fc195eSAndrew Gallatin 	if (sc->rx_big.extra_map != NULL)
2066b2fc195eSAndrew Gallatin 		bus_dmamap_destroy(sc->rx_big.dmat,
2067b2fc195eSAndrew Gallatin 				   sc->rx_big.extra_map);
2068b2fc195eSAndrew Gallatin 	if (sc->rx_small.extra_map != NULL)
2069b2fc195eSAndrew Gallatin 		bus_dmamap_destroy(sc->rx_small.dmat,
2070b2fc195eSAndrew Gallatin 				   sc->rx_small.extra_map);
2071b2fc195eSAndrew Gallatin 	if (sc->tx.dmat != NULL)
2072b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->tx.dmat);
2073b2fc195eSAndrew Gallatin 	if (sc->rx_small.dmat != NULL)
2074b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->rx_small.dmat);
2075b2fc195eSAndrew Gallatin 	if (sc->rx_big.dmat != NULL)
2076b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->rx_big.dmat);
2077b2fc195eSAndrew Gallatin }
2078b2fc195eSAndrew Gallatin 
2079b2fc195eSAndrew Gallatin static int
20806d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
2081b2fc195eSAndrew Gallatin {
20826d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2083b2fc195eSAndrew Gallatin 	int tx_ring_size, rx_ring_size;
2084b2fc195eSAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
2085b2fc195eSAndrew Gallatin 	int i, err;
2086b2fc195eSAndrew Gallatin 	unsigned long bytes;
2087b2fc195eSAndrew Gallatin 
2088b2fc195eSAndrew Gallatin 	/* get ring sizes */
20895e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
2090b2fc195eSAndrew Gallatin 	tx_ring_size = cmd.data0;
20915e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
2092b2fc195eSAndrew Gallatin 	if (err != 0) {
2093b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Cannot determine ring sizes\n");
2094b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2095b2fc195eSAndrew Gallatin 	}
2096b2fc195eSAndrew Gallatin 
2097b2fc195eSAndrew Gallatin 	rx_ring_size = cmd.data0;
2098b2fc195eSAndrew Gallatin 
2099b2fc195eSAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
2100b2fc195eSAndrew Gallatin 	rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t);
2101b2fc195eSAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
210276bb9c5eSAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
210376bb9c5eSAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
2104b2fc195eSAndrew Gallatin 
2105b2fc195eSAndrew Gallatin 	sc->tx.mask = tx_ring_entries - 1;
2106b2fc195eSAndrew Gallatin 	sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1;
2107b2fc195eSAndrew Gallatin 
2108b2fc195eSAndrew Gallatin 	err = ENOMEM;
2109b2fc195eSAndrew Gallatin 
2110b2fc195eSAndrew Gallatin 	/* allocate the tx request copy block */
2111b2fc195eSAndrew Gallatin 	bytes = 8 +
21125e7d8541SAndrew Gallatin 		sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4);
2113b2fc195eSAndrew Gallatin 	sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
2114b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes == NULL)
2115b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2116b2fc195eSAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
2117b2fc195eSAndrew Gallatin 	sc->tx.req_list = (mcp_kreq_ether_send_t *)
2118b2fc195eSAndrew Gallatin 		((unsigned long)(sc->tx.req_bytes + 7) & ~7UL);
2119b2fc195eSAndrew Gallatin 
2120aed8e389SAndrew Gallatin 	/* allocate the tx busdma segment list */
2121aed8e389SAndrew Gallatin 	bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC;
2122aed8e389SAndrew Gallatin 	sc->tx.seg_list = (bus_dma_segment_t *)
2123aed8e389SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
2124aed8e389SAndrew Gallatin 	if (sc->tx.seg_list == NULL)
2125aed8e389SAndrew Gallatin 		goto abort_with_alloc;
2126aed8e389SAndrew Gallatin 
2127b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
2128b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow);
2129b2fc195eSAndrew Gallatin 	sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2130b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow == NULL)
2131b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2132b2fc195eSAndrew Gallatin 
2133b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow);
2134b2fc195eSAndrew Gallatin 	sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2135b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow == NULL)
2136b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2137b2fc195eSAndrew Gallatin 
2138b2fc195eSAndrew Gallatin 	/* allocate the host info rings */
2139b2fc195eSAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*sc->tx.info);
2140b2fc195eSAndrew Gallatin 	sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2141b2fc195eSAndrew Gallatin 	if (sc->tx.info == NULL)
2142b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2143b2fc195eSAndrew Gallatin 
2144b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.info);
2145b2fc195eSAndrew Gallatin 	sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2146b2fc195eSAndrew Gallatin 	if (sc->rx_small.info == NULL)
2147b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2148b2fc195eSAndrew Gallatin 
2149b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.info);
2150b2fc195eSAndrew Gallatin 	sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2151b2fc195eSAndrew Gallatin 	if (sc->rx_big.info == NULL)
2152b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2153b2fc195eSAndrew Gallatin 
2154b2fc195eSAndrew Gallatin 	/* allocate the busdma resources */
2155b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2156b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2157b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* boundary */
2158b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2159b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2160b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2161aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
2162aed8e389SAndrew Gallatin 				 MXGE_MAX_SEND_DESC/2,	/* num segs */
2163b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* maxsegsize */
2164b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2165b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2166b2fc195eSAndrew Gallatin 				 &sc->tx.dmat);		/* tag */
2167b2fc195eSAndrew Gallatin 
2168b2fc195eSAndrew Gallatin 	if (err != 0) {
2169b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
2170b2fc195eSAndrew Gallatin 			      err);
2171b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2172b2fc195eSAndrew Gallatin 	}
2173b2fc195eSAndrew Gallatin 
2174b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2175b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2176b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2177b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2178b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2179b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2180b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2181b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2182b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2183b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2184b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2185b2fc195eSAndrew Gallatin 				 &sc->rx_small.dmat);	/* tag */
2186b2fc195eSAndrew Gallatin 	if (err != 0) {
2187b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2188b2fc195eSAndrew Gallatin 			      err);
2189b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2190b2fc195eSAndrew Gallatin 	}
2191b2fc195eSAndrew Gallatin 
2192b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2193b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2194b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2195b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2196b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2197b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2198b2fc195eSAndrew Gallatin 				 4096,			/* maxsize */
2199b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2200b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2201b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2202b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2203b2fc195eSAndrew Gallatin 				 &sc->rx_big.dmat);	/* tag */
2204b2fc195eSAndrew Gallatin 	if (err != 0) {
2205b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2206b2fc195eSAndrew Gallatin 			      err);
2207b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2208b2fc195eSAndrew Gallatin 	}
2209b2fc195eSAndrew Gallatin 
2210b2fc195eSAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
2211b2fc195eSAndrew Gallatin 	   in each ring */
2212b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2213b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->tx.dmat, 0,
2214b2fc195eSAndrew Gallatin 					&sc->tx.info[i].map);
2215b2fc195eSAndrew Gallatin 		if (err != 0) {
2216b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
2217b2fc195eSAndrew Gallatin 			      err);
2218b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2219b2fc195eSAndrew Gallatin 		}
2220b2fc195eSAndrew Gallatin 	}
2221b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2222b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_small.dmat, 0,
2223b2fc195eSAndrew Gallatin 					&sc->rx_small.info[i].map);
2224b2fc195eSAndrew Gallatin 		if (err != 0) {
2225b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2226b2fc195eSAndrew Gallatin 				      err);
2227b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2228b2fc195eSAndrew Gallatin 		}
2229b2fc195eSAndrew Gallatin 	}
2230b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_small.dmat, 0,
2231b2fc195eSAndrew Gallatin 				&sc->rx_small.extra_map);
2232b2fc195eSAndrew Gallatin 	if (err != 0) {
2233b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2234b2fc195eSAndrew Gallatin 			      err);
2235b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2236b2fc195eSAndrew Gallatin 	}
2237b2fc195eSAndrew Gallatin 
2238b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2239b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_big.dmat, 0,
2240b2fc195eSAndrew Gallatin 					&sc->rx_big.info[i].map);
2241b2fc195eSAndrew Gallatin 		if (err != 0) {
2242b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2243b2fc195eSAndrew Gallatin 			      err);
2244b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2245b2fc195eSAndrew Gallatin 		}
2246b2fc195eSAndrew Gallatin 	}
2247b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_big.dmat, 0,
2248b2fc195eSAndrew Gallatin 				&sc->rx_big.extra_map);
2249b2fc195eSAndrew Gallatin 	if (err != 0) {
2250b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2251b2fc195eSAndrew Gallatin 			      err);
2252b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2253b2fc195eSAndrew Gallatin 	}
2254b2fc195eSAndrew Gallatin 	return 0;
2255b2fc195eSAndrew Gallatin 
2256b2fc195eSAndrew Gallatin abort_with_alloc:
22576d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2258b2fc195eSAndrew Gallatin 
2259b2fc195eSAndrew Gallatin abort_with_nothing:
2260b2fc195eSAndrew Gallatin 	return err;
2261b2fc195eSAndrew Gallatin }
2262b2fc195eSAndrew Gallatin 
2263b2fc195eSAndrew Gallatin static int
22646d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc)
2265b2fc195eSAndrew Gallatin {
22666d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2267b2fc195eSAndrew Gallatin 	int i, err;
2268b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2269b2fc195eSAndrew Gallatin 
2270b2fc195eSAndrew Gallatin 
22717d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
22727d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
22737d542e2dSAndrew Gallatin 
22746d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2275b2fc195eSAndrew Gallatin 	if (err != 0) {
2276b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
2277b2fc195eSAndrew Gallatin 		return EIO;
2278b2fc195eSAndrew Gallatin 	}
2279b2fc195eSAndrew Gallatin 
2280b2fc195eSAndrew Gallatin 	if (MCLBYTES >=
22815e7d8541SAndrew Gallatin 	    sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD)
2282b2fc195eSAndrew Gallatin 		sc->big_bytes = MCLBYTES;
2283b2fc195eSAndrew Gallatin 	else
2284b2fc195eSAndrew Gallatin 		sc->big_bytes = MJUMPAGESIZE;
2285b2fc195eSAndrew Gallatin 
22866d87a65dSAndrew Gallatin 	err = mxge_alloc_rings(sc);
2287b2fc195eSAndrew Gallatin 	if (err != 0) {
2288b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
2289b2fc195eSAndrew Gallatin 		return err;
2290b2fc195eSAndrew Gallatin 	}
2291b2fc195eSAndrew Gallatin 
2292b2fc195eSAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
2293b2fc195eSAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
22946d87a65dSAndrew Gallatin 			     mxge_intr, sc, &sc->ih);
2295b2fc195eSAndrew Gallatin 	if (err != 0) {
2296b2fc195eSAndrew Gallatin 		goto abort_with_rings;
2297b2fc195eSAndrew Gallatin 	}
2298b2fc195eSAndrew Gallatin 
2299b2fc195eSAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
2300b2fc195eSAndrew Gallatin 
23015e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
2302b2fc195eSAndrew Gallatin 	sc->tx.lanai =
2303b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
23046d87a65dSAndrew Gallatin 	err |= mxge_send_cmd(sc,
23055e7d8541SAndrew Gallatin 				 MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
2306b2fc195eSAndrew Gallatin 	sc->rx_small.lanai =
2307b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
23085e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
2309b2fc195eSAndrew Gallatin 	sc->rx_big.lanai =
2310b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
2311b2fc195eSAndrew Gallatin 
2312b2fc195eSAndrew Gallatin 	if (err != 0) {
2313b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
2314b2fc195eSAndrew Gallatin 			      "failed to get ring sizes or locations\n");
2315b2fc195eSAndrew Gallatin 		err = EIO;
2316b2fc195eSAndrew Gallatin 		goto abort_with_irq;
2317b2fc195eSAndrew Gallatin 	}
2318b2fc195eSAndrew Gallatin 
2319b2fc195eSAndrew Gallatin 	if (sc->wc) {
2320b2fc195eSAndrew Gallatin 		sc->tx.wc_fifo = sc->sram + 0x200000;
2321b2fc195eSAndrew Gallatin 		sc->rx_small.wc_fifo = sc->sram + 0x300000;
2322b2fc195eSAndrew Gallatin 		sc->rx_big.wc_fifo = sc->sram + 0x340000;
2323b2fc195eSAndrew Gallatin 	} else {
2324b2fc195eSAndrew Gallatin 		sc->tx.wc_fifo = 0;
2325b2fc195eSAndrew Gallatin 		sc->rx_small.wc_fifo = 0;
2326b2fc195eSAndrew Gallatin 		sc->rx_big.wc_fifo = 0;
2327b2fc195eSAndrew Gallatin 	}
2328b2fc195eSAndrew Gallatin 
2329b2fc195eSAndrew Gallatin 
2330b2fc195eSAndrew Gallatin 	/* stock receive rings */
2331b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2332b2fc195eSAndrew Gallatin 		map = sc->rx_small.info[i].map;
23336d87a65dSAndrew Gallatin 		err = mxge_get_buf_small(sc, map, i);
2334b2fc195eSAndrew Gallatin 		if (err) {
2335b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
2336b2fc195eSAndrew Gallatin 				      i, sc->rx_small.mask + 1);
2337b2fc195eSAndrew Gallatin 			goto abort;
2338b2fc195eSAndrew Gallatin 		}
2339b2fc195eSAndrew Gallatin 	}
2340b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2341b2fc195eSAndrew Gallatin 		map = sc->rx_big.info[i].map;
23426d87a65dSAndrew Gallatin 		err = mxge_get_buf_big(sc, map, i);
2343b2fc195eSAndrew Gallatin 		if (err) {
2344b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
2345b2fc195eSAndrew Gallatin 				      i, sc->rx_big.mask + 1);
2346b2fc195eSAndrew Gallatin 			goto abort;
2347b2fc195eSAndrew Gallatin 		}
2348b2fc195eSAndrew Gallatin 	}
2349b2fc195eSAndrew Gallatin 
2350b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
2351b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
2352b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
2353b2fc195eSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN;
23545e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
2355b2fc195eSAndrew Gallatin 	cmd.data0 = MHLEN;
23565e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
2357b2fc195eSAndrew Gallatin 			     &cmd);
2358b2fc195eSAndrew Gallatin 	cmd.data0 = sc->big_bytes;
23595e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
2360b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
23616d87a65dSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr);
23626d87a65dSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr);
23635e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA, &cmd);
2364b2fc195eSAndrew Gallatin 
2365b2fc195eSAndrew Gallatin 	if (err != 0) {
2366b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
2367b2fc195eSAndrew Gallatin 		goto abort;
2368b2fc195eSAndrew Gallatin 	}
2369b2fc195eSAndrew Gallatin 
2370b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
23715e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
2372b2fc195eSAndrew Gallatin 	if (err) {
2373b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
2374b2fc195eSAndrew Gallatin 		goto abort;
2375b2fc195eSAndrew Gallatin 	}
2376b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
2377b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2378b2fc195eSAndrew Gallatin 
2379b2fc195eSAndrew Gallatin 	return 0;
2380b2fc195eSAndrew Gallatin 
2381b2fc195eSAndrew Gallatin 
2382b2fc195eSAndrew Gallatin abort:
23836d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2384b2fc195eSAndrew Gallatin abort_with_irq:
2385b2fc195eSAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
2386b2fc195eSAndrew Gallatin abort_with_rings:
23876d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2388b2fc195eSAndrew Gallatin 	return err;
2389b2fc195eSAndrew Gallatin }
2390b2fc195eSAndrew Gallatin 
2391b2fc195eSAndrew Gallatin static int
23926d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
2393b2fc195eSAndrew Gallatin {
23946d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2395b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
2396b2fc195eSAndrew Gallatin 
2397b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2398b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
2399b2fc195eSAndrew Gallatin 	mb();
24005e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
2401b2fc195eSAndrew Gallatin 	if (err) {
2402b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
2403b2fc195eSAndrew Gallatin 	}
2404b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2405b2fc195eSAndrew Gallatin 		/* wait for down irq */
24066d87a65dSAndrew Gallatin 		(void)tsleep(&sc->down_cnt, PWAIT, "down mxge", hz);
2407b2fc195eSAndrew Gallatin 	}
2408b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2409b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
2410b2fc195eSAndrew Gallatin 	}
2411b2fc195eSAndrew Gallatin 	if (sc->ih != NULL)
2412b2fc195eSAndrew Gallatin 		bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
24136d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
24146d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2415b2fc195eSAndrew Gallatin 	return 0;
2416b2fc195eSAndrew Gallatin }
2417b2fc195eSAndrew Gallatin 
2418b2fc195eSAndrew Gallatin 
2419b2fc195eSAndrew Gallatin static int
24206d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
2421b2fc195eSAndrew Gallatin {
2422b2fc195eSAndrew Gallatin 	return EINVAL;
2423b2fc195eSAndrew Gallatin }
2424b2fc195eSAndrew Gallatin 
2425b2fc195eSAndrew Gallatin static int
24266d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
2427b2fc195eSAndrew Gallatin {
2428b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
2429b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
2430b2fc195eSAndrew Gallatin 	int err = 0;
2431b2fc195eSAndrew Gallatin 
2432b2fc195eSAndrew Gallatin 
2433b2fc195eSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN;
24346d87a65dSAndrew Gallatin 	if ((real_mtu > MXGE_MAX_ETHER_MTU) ||
2435b2fc195eSAndrew Gallatin 	    real_mtu < 60)
2436b2fc195eSAndrew Gallatin 		return EINVAL;
2437b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
2438b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
2439b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
2440b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
24416d87a65dSAndrew Gallatin 		mxge_close(sc);
24426d87a65dSAndrew Gallatin 		err = mxge_open(sc);
2443b2fc195eSAndrew Gallatin 		if (err != 0) {
2444b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
24456d87a65dSAndrew Gallatin 			mxge_close(sc);
24466d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
2447b2fc195eSAndrew Gallatin 		}
2448b2fc195eSAndrew Gallatin 	}
2449b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
2450b2fc195eSAndrew Gallatin 	return err;
2451b2fc195eSAndrew Gallatin }
2452b2fc195eSAndrew Gallatin 
2453b2fc195eSAndrew Gallatin static void
24546d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
2455b2fc195eSAndrew Gallatin {
24566d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2457b2fc195eSAndrew Gallatin 
2458b2fc195eSAndrew Gallatin 
2459b2fc195eSAndrew Gallatin 	if (sc == NULL)
2460b2fc195eSAndrew Gallatin 		return;
2461b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
2462b2fc195eSAndrew Gallatin 	ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0;
2463b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
2464b2fc195eSAndrew Gallatin 	ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0;
2465b2fc195eSAndrew Gallatin }
2466b2fc195eSAndrew Gallatin 
2467b2fc195eSAndrew Gallatin static int
24686d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
2469b2fc195eSAndrew Gallatin {
24706d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2471b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
2472b2fc195eSAndrew Gallatin 	int err, mask;
2473b2fc195eSAndrew Gallatin 
2474b2fc195eSAndrew Gallatin 	err = 0;
2475b2fc195eSAndrew Gallatin 	switch (command) {
2476b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
2477b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
2478b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
2479b2fc195eSAndrew Gallatin 		break;
2480b2fc195eSAndrew Gallatin 
2481b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
24826d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
2483b2fc195eSAndrew Gallatin 		break;
2484b2fc195eSAndrew Gallatin 
2485b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
2486b2fc195eSAndrew Gallatin 		sx_xlock(&sc->driver_lock);
2487b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
2488b2fc195eSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
24896d87a65dSAndrew Gallatin 				err = mxge_open(sc);
2490b2fc195eSAndrew Gallatin 		} else {
2491b2fc195eSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
24926d87a65dSAndrew Gallatin 				mxge_close(sc);
2493b2fc195eSAndrew Gallatin 		}
2494b2fc195eSAndrew Gallatin 		sx_xunlock(&sc->driver_lock);
2495b2fc195eSAndrew Gallatin 		break;
2496b2fc195eSAndrew Gallatin 
2497b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
2498b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
2499b2fc195eSAndrew Gallatin 		err = 0;
2500b2fc195eSAndrew Gallatin 		break;
2501b2fc195eSAndrew Gallatin 
2502b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
2503b2fc195eSAndrew Gallatin 		sx_xlock(&sc->driver_lock);
2504b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
2505b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
2506b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
2507aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
2508aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
2509aed8e389SAndrew Gallatin 						      | CSUM_TSO);
2510b2fc195eSAndrew Gallatin 			} else {
2511b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
2512b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
2513b2fc195eSAndrew Gallatin 			}
2514b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
2515b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
2516b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
25175e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
2518b2fc195eSAndrew Gallatin 			} else {
2519b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
25205e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
2521b2fc195eSAndrew Gallatin 			}
2522b2fc195eSAndrew Gallatin 		}
2523aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
2524aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
2525aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
2526aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
2527aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
2528aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
2529aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
2530aed8e389SAndrew Gallatin 			} else {
2531aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
2532aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
2533aed8e389SAndrew Gallatin 				err = EINVAL;
2534aed8e389SAndrew Gallatin 			}
2535aed8e389SAndrew Gallatin 		}
2536b2fc195eSAndrew Gallatin 		sx_xunlock(&sc->driver_lock);
2537b2fc195eSAndrew Gallatin 		break;
2538b2fc195eSAndrew Gallatin 
2539b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
2540b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
2541b2fc195eSAndrew Gallatin 				    &sc->media, command);
2542b2fc195eSAndrew Gallatin                 break;
2543b2fc195eSAndrew Gallatin 
2544b2fc195eSAndrew Gallatin 	default:
2545b2fc195eSAndrew Gallatin 		err = ENOTTY;
2546b2fc195eSAndrew Gallatin         }
2547b2fc195eSAndrew Gallatin 	return err;
2548b2fc195eSAndrew Gallatin }
2549b2fc195eSAndrew Gallatin 
2550b2fc195eSAndrew Gallatin static void
25516d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
2552b2fc195eSAndrew Gallatin {
2553b2fc195eSAndrew Gallatin 
25546d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
25556d87a65dSAndrew Gallatin 			  &mxge_flow_control);
25566d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
25576d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
25586d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
25596d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
25605e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
25615e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
25625e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
25635e7d8541SAndrew Gallatin 			  &mxge_verbose);
2564b2fc195eSAndrew Gallatin 
25655e7d8541SAndrew Gallatin 	if (bootverbose)
25665e7d8541SAndrew Gallatin 		mxge_verbose = 1;
25676d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
25686d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
25696d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
2570b2fc195eSAndrew Gallatin }
2571b2fc195eSAndrew Gallatin 
2572b2fc195eSAndrew Gallatin static int
25736d87a65dSAndrew Gallatin mxge_attach(device_t dev)
2574b2fc195eSAndrew Gallatin {
25756d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
2576b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2577b2fc195eSAndrew Gallatin 	size_t bytes;
25785e7d8541SAndrew Gallatin 	int rid, err;
2579b2fc195eSAndrew Gallatin 	uint16_t cmd;
2580b2fc195eSAndrew Gallatin 
2581b2fc195eSAndrew Gallatin 	sc->dev = dev;
25826d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
2583b2fc195eSAndrew Gallatin 
2584b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
2585b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2586b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2587b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2588b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2589b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2590aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
25915e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
2592b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2593b2fc195eSAndrew Gallatin 				 0,			/* flags */
2594b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2595b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
2596b2fc195eSAndrew Gallatin 
2597b2fc195eSAndrew Gallatin 	if (err != 0) {
2598b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
2599b2fc195eSAndrew Gallatin 			      err);
2600b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2601b2fc195eSAndrew Gallatin 	}
2602b2fc195eSAndrew Gallatin 
2603b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
2604b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
2605b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
2606b2fc195eSAndrew Gallatin 		err = ENOSPC;
2607b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
2608b2fc195eSAndrew Gallatin 	}
2609b2fc195eSAndrew Gallatin 	mtx_init(&sc->cmd_lock, NULL,
2610b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
2611b2fc195eSAndrew Gallatin 	mtx_init(&sc->tx_lock, device_get_nameunit(dev),
2612b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
2613b2fc195eSAndrew Gallatin 	sx_init(&sc->driver_lock, device_get_nameunit(dev));
2614b2fc195eSAndrew Gallatin 
2615b2fc195eSAndrew Gallatin 	/* Enable DMA and Memory space access */
2616b2fc195eSAndrew Gallatin 	pci_enable_busmaster(dev);
2617b2fc195eSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2618b2fc195eSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
2619b2fc195eSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2620b2fc195eSAndrew Gallatin 
2621b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
2622b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
2623b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
2624b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
2625b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
2626b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
2627b2fc195eSAndrew Gallatin 		err = ENXIO;
2628b2fc195eSAndrew Gallatin 		goto abort_with_lock;
2629b2fc195eSAndrew Gallatin 	}
2630b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
2631b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
2632b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
2633b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
2634b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
2635b2fc195eSAndrew Gallatin 		err = ENXIO;
2636b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2637b2fc195eSAndrew Gallatin 	}
2638b2fc195eSAndrew Gallatin 
2639b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
2640b2fc195eSAndrew Gallatin 	   lanai SRAM */
26416d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
2642b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
2643b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
26446d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
2645b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
26466d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
26476d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
2648b2fc195eSAndrew Gallatin 	if (err != 0)
2649b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2650b2fc195eSAndrew Gallatin 
2651b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
26526d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
2653b2fc195eSAndrew Gallatin 
2654b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
26556d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
26566d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
2657b2fc195eSAndrew Gallatin 	if (err != 0)
2658b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2659b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
26606d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
2661b2fc195eSAndrew Gallatin 	if (err != 0)
2662b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
2663b2fc195eSAndrew Gallatin 
26646d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->fw_stats_dma,
2665b2fc195eSAndrew Gallatin 			     sizeof (*sc->fw_stats), 64);
2666b2fc195eSAndrew Gallatin 	if (err != 0)
2667b2fc195eSAndrew Gallatin 		goto abort_with_zeropad_dma;
26685e7d8541SAndrew Gallatin 	sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr;
2669b2fc195eSAndrew Gallatin 
2670b2fc195eSAndrew Gallatin 
2671b2fc195eSAndrew Gallatin 	/* allocate interrupt queues */
26725e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);
26735e7d8541SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096);
2674b2fc195eSAndrew Gallatin 	if (err != 0)
26755e7d8541SAndrew Gallatin 		goto abort_with_fw_stats;
26765e7d8541SAndrew Gallatin 	sc->rx_done.entry = sc->rx_done.dma.addr;
26775e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
2678b2fc195eSAndrew Gallatin 	/* Add our ithread  */
2679b2fc195eSAndrew Gallatin 	rid = 0;
2680b2fc195eSAndrew Gallatin 	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0,
2681b2fc195eSAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
2682b2fc195eSAndrew Gallatin 	if (sc->irq_res == NULL) {
2683b2fc195eSAndrew Gallatin 		device_printf(dev, "could not alloc interrupt\n");
26845e7d8541SAndrew Gallatin 		goto abort_with_rx_done;
2685b2fc195eSAndrew Gallatin 	}
2686b2fc195eSAndrew Gallatin 
2687b2fc195eSAndrew Gallatin 	/* load the firmware */
26886d87a65dSAndrew Gallatin 	mxge_select_firmware(sc);
2689b2fc195eSAndrew Gallatin 
26906d87a65dSAndrew Gallatin 	err = mxge_load_firmware(sc);
2691b2fc195eSAndrew Gallatin 	if (err != 0)
2692b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
26935e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
26946d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2695b2fc195eSAndrew Gallatin 	if (err != 0)
2696b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
2697b2fc195eSAndrew Gallatin 
2698b2fc195eSAndrew Gallatin 	/* hook into the network stack */
2699b2fc195eSAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
2700b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
2701aed8e389SAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4;
2702aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
2703b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
27045e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
27056d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
2706b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
2707b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
27086d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
27096d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
27106d87a65dSAndrew Gallatin 	ifp->if_watchdog = mxge_watchdog;
2711b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
2712b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
27136d87a65dSAndrew Gallatin 	ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN;
2714b2fc195eSAndrew Gallatin 
2715b2fc195eSAndrew Gallatin 	/* Initialise the ifmedia structure */
27166d87a65dSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
27176d87a65dSAndrew Gallatin 		     mxge_media_status);
2718b2fc195eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
27196d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
2720b2fc195eSAndrew Gallatin 	return 0;
2721b2fc195eSAndrew Gallatin 
2722b2fc195eSAndrew Gallatin abort_with_irq_res:
2723b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
27245e7d8541SAndrew Gallatin abort_with_rx_done:
27255e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
27265e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
27275e7d8541SAndrew Gallatin abort_with_fw_stats:
27286d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
2729b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
27306d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
2731b2fc195eSAndrew Gallatin abort_with_cmd_dma:
27326d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
2733b2fc195eSAndrew Gallatin abort_with_mem_res:
2734b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
2735b2fc195eSAndrew Gallatin abort_with_lock:
2736b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
2737b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->cmd_lock);
2738b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->tx_lock);
2739b2fc195eSAndrew Gallatin 	sx_destroy(&sc->driver_lock);
2740b2fc195eSAndrew Gallatin 	if_free(ifp);
2741b2fc195eSAndrew Gallatin abort_with_parent_dmat:
2742b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
2743b2fc195eSAndrew Gallatin 
2744b2fc195eSAndrew Gallatin abort_with_nothing:
2745b2fc195eSAndrew Gallatin 	return err;
2746b2fc195eSAndrew Gallatin }
2747b2fc195eSAndrew Gallatin 
2748b2fc195eSAndrew Gallatin static int
27496d87a65dSAndrew Gallatin mxge_detach(device_t dev)
2750b2fc195eSAndrew Gallatin {
27516d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
2752b2fc195eSAndrew Gallatin 
2753b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
2754b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
27556d87a65dSAndrew Gallatin 		mxge_close(sc);
2756b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
2757b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
2758091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
2759b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
27605e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
27615e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
27626d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
27636d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
27646d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
2765b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
2766b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
2767b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->cmd_lock);
2768b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->tx_lock);
2769b2fc195eSAndrew Gallatin 	sx_destroy(&sc->driver_lock);
2770b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
2771b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
2772b2fc195eSAndrew Gallatin 	return 0;
2773b2fc195eSAndrew Gallatin }
2774b2fc195eSAndrew Gallatin 
2775b2fc195eSAndrew Gallatin static int
27766d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
2777b2fc195eSAndrew Gallatin {
2778b2fc195eSAndrew Gallatin 	return 0;
2779b2fc195eSAndrew Gallatin }
2780b2fc195eSAndrew Gallatin 
2781b2fc195eSAndrew Gallatin /*
2782b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
2783b2fc195eSAndrew Gallatin 
2784b2fc195eSAndrew Gallatin   Local Variables:
2785b2fc195eSAndrew Gallatin   c-file-style:"linux"
2786b2fc195eSAndrew Gallatin   tab-width:8
2787b2fc195eSAndrew Gallatin   End:
2788b2fc195eSAndrew Gallatin */
2789