xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 40385a5f331dddcd7eb4a25a971f6148f5de1a68)
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;
87d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
885e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024;
896d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
905e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
916d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
925e7d8541SAndrew Gallatin static int mxge_verbose = 0;
93dce01b9bSAndrew Gallatin static int mxge_ticks;
946d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
956d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
96b2fc195eSAndrew Gallatin 
976d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
986d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
996d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1006d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1016d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
102b2fc195eSAndrew Gallatin 
1036d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
104b2fc195eSAndrew Gallatin {
105b2fc195eSAndrew Gallatin   /* Device interface */
1066d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1076d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1086d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1096d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
110b2fc195eSAndrew Gallatin   {0, 0}
111b2fc195eSAndrew Gallatin };
112b2fc195eSAndrew Gallatin 
1136d87a65dSAndrew Gallatin static driver_t mxge_driver =
114b2fc195eSAndrew Gallatin {
1156d87a65dSAndrew Gallatin   "mxge",
1166d87a65dSAndrew Gallatin   mxge_methods,
1176d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
118b2fc195eSAndrew Gallatin };
119b2fc195eSAndrew Gallatin 
1206d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
121b2fc195eSAndrew Gallatin 
122b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1236d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1246d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
125b2fc195eSAndrew Gallatin 
126b2fc195eSAndrew Gallatin static int
1276d87a65dSAndrew Gallatin mxge_probe(device_t dev)
128b2fc195eSAndrew Gallatin {
1296d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1306d87a65dSAndrew Gallatin       (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) {
131b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
132b2fc195eSAndrew Gallatin 	  return 0;
133b2fc195eSAndrew Gallatin   }
134b2fc195eSAndrew Gallatin   return ENXIO;
135b2fc195eSAndrew Gallatin }
136b2fc195eSAndrew Gallatin 
137b2fc195eSAndrew Gallatin static void
1386d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
139b2fc195eSAndrew Gallatin {
140b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
141b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
142b2fc195eSAndrew Gallatin 	vm_offset_t len;
143b2fc195eSAndrew Gallatin 	int err, action;
144b2fc195eSAndrew Gallatin 
145b2fc195eSAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
146b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
147b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
148b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
149b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
150b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1516d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
152b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
153b2fc195eSAndrew Gallatin 	if (err != 0) {
154b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
155b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
156b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
157b2fc195eSAndrew Gallatin 	} else {
158b2fc195eSAndrew Gallatin 		sc->wc = 1;
159b2fc195eSAndrew Gallatin 	}
160b2fc195eSAndrew Gallatin }
161b2fc195eSAndrew Gallatin 
162b2fc195eSAndrew Gallatin 
163b2fc195eSAndrew Gallatin /* callback to get our DMA address */
164b2fc195eSAndrew Gallatin static void
1656d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
166b2fc195eSAndrew Gallatin 			 int error)
167b2fc195eSAndrew Gallatin {
168b2fc195eSAndrew Gallatin 	if (error == 0) {
169b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
170b2fc195eSAndrew Gallatin 	}
171b2fc195eSAndrew Gallatin }
172b2fc195eSAndrew Gallatin 
173b2fc195eSAndrew Gallatin static int
1746d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
175b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
176b2fc195eSAndrew Gallatin {
177b2fc195eSAndrew Gallatin 	int err;
178b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
179b2fc195eSAndrew Gallatin 
180b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
181b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
182b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
183b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
184b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
185b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
186b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
187b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
188b2fc195eSAndrew Gallatin 				 1,			/* num segs */
189b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
190b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
191b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
192b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
193b2fc195eSAndrew Gallatin 	if (err != 0) {
194b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
195b2fc195eSAndrew Gallatin 		return err;
196b2fc195eSAndrew Gallatin 	}
197b2fc195eSAndrew Gallatin 
198b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
199b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
200b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
201b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
202b2fc195eSAndrew Gallatin 	if (err != 0) {
203b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
204b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
205b2fc195eSAndrew Gallatin 	}
206b2fc195eSAndrew Gallatin 
207b2fc195eSAndrew Gallatin 	/* load the memory */
208b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2096d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
210b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
211b2fc195eSAndrew Gallatin 	if (err != 0) {
212b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
213b2fc195eSAndrew Gallatin 		goto abort_with_mem;
214b2fc195eSAndrew Gallatin 	}
215b2fc195eSAndrew Gallatin 	return 0;
216b2fc195eSAndrew Gallatin 
217b2fc195eSAndrew Gallatin abort_with_mem:
218b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
219b2fc195eSAndrew Gallatin abort_with_dmat:
220b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
221b2fc195eSAndrew Gallatin 	return err;
222b2fc195eSAndrew Gallatin }
223b2fc195eSAndrew Gallatin 
224b2fc195eSAndrew Gallatin 
225b2fc195eSAndrew Gallatin static void
2266d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
227b2fc195eSAndrew Gallatin {
228b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
229b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
230b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
231b2fc195eSAndrew Gallatin }
232b2fc195eSAndrew Gallatin 
233b2fc195eSAndrew Gallatin /*
234b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
235b2fc195eSAndrew Gallatin  * SN=x\0
236b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
237b2fc195eSAndrew Gallatin  * PC=text\0
238b2fc195eSAndrew Gallatin  */
239b2fc195eSAndrew Gallatin 
240b2fc195eSAndrew Gallatin static int
2416d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
242b2fc195eSAndrew Gallatin {
2436d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
244b2fc195eSAndrew Gallatin 
245b2fc195eSAndrew Gallatin 	char *ptr, *limit;
246b2fc195eSAndrew Gallatin 	int i, found_mac;
247b2fc195eSAndrew Gallatin 
248b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2496d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
250b2fc195eSAndrew Gallatin 	found_mac = 0;
251b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
252b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2535e7d8541SAndrew Gallatin 			ptr += 1;
254b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
255b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2565e7d8541SAndrew Gallatin 				ptr += 3;
257b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
258b2fc195eSAndrew Gallatin 					goto abort;
259b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
260b2fc195eSAndrew Gallatin 				found_mac = 1;
261b2fc195eSAndrew Gallatin 			}
2625e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2635e7d8541SAndrew Gallatin 			ptr += 3;
2645e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2655e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2665e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2675e7d8541SAndrew Gallatin 			ptr += 3;
2685e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2695e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
270b2fc195eSAndrew Gallatin 		}
2716d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
272b2fc195eSAndrew Gallatin 	}
273b2fc195eSAndrew Gallatin 
274b2fc195eSAndrew Gallatin 	if (found_mac)
275b2fc195eSAndrew Gallatin 		return 0;
276b2fc195eSAndrew Gallatin 
277b2fc195eSAndrew Gallatin  abort:
278b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
279b2fc195eSAndrew Gallatin 
280b2fc195eSAndrew Gallatin 	return ENXIO;
281b2fc195eSAndrew Gallatin }
282b2fc195eSAndrew Gallatin 
283b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
284b2fc195eSAndrew Gallatin static int
2856d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
286b2fc195eSAndrew Gallatin {
287b2fc195eSAndrew Gallatin 	uint32_t val;
288b2fc195eSAndrew Gallatin 	unsigned long off;
289b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
290b2fc195eSAndrew Gallatin 	uint16_t vendor_id, device_id;
291b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
292b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
293b2fc195eSAndrew Gallatin 
294b2fc195eSAndrew Gallatin 	/* XXXX
295b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
296b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
297b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
298b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
299b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
300b2fc195eSAndrew Gallatin 	*/
301b2fc195eSAndrew Gallatin #if 0
302b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
303b2fc195eSAndrew Gallatin 	   config space */
304b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
305b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
306b2fc195eSAndrew Gallatin 		val |= 0x40;
307b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
308b2fc195eSAndrew Gallatin 		return 0;
309b2fc195eSAndrew Gallatin 	}
310b2fc195eSAndrew Gallatin #endif
311b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
312b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
313b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
314b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
315b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
316b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
317b2fc195eSAndrew Gallatin 	 */
318b2fc195eSAndrew Gallatin 
319b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
320b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
321b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
322b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
323b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
324b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
325b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
326b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
327b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
328b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
329b2fc195eSAndrew Gallatin 
330b2fc195eSAndrew Gallatin 	off =  0xe0000000UL
331b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
332b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
333b2fc195eSAndrew Gallatin 						 + 8 * slot);
334b2fc195eSAndrew Gallatin 
335b2fc195eSAndrew Gallatin 	/* map it into the kernel */
336b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
337b2fc195eSAndrew Gallatin 
338b2fc195eSAndrew Gallatin 
339b2fc195eSAndrew Gallatin 	if (va == NULL) {
340b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
341b2fc195eSAndrew Gallatin 		return EIO;
342b2fc195eSAndrew Gallatin 	}
343b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
344b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
345b2fc195eSAndrew Gallatin 
346b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
347b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
348b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
349b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
350b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
351b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
352b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
353b2fc195eSAndrew Gallatin 		return EIO;
354b2fc195eSAndrew Gallatin 	}
355b2fc195eSAndrew Gallatin 
356b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
357b2fc195eSAndrew Gallatin 	val = *ptr32;
358b2fc195eSAndrew Gallatin 
359b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
360b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
361b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
362b2fc195eSAndrew Gallatin 		return EIO;
363b2fc195eSAndrew Gallatin 	}
364b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
365b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3665e7d8541SAndrew Gallatin 	if (mxge_verbose)
367b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
3685e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
3695e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
370b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
371b2fc195eSAndrew Gallatin 	return 0;
372b2fc195eSAndrew Gallatin }
373b2fc195eSAndrew Gallatin #else
374b2fc195eSAndrew Gallatin static int
3756d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
376b2fc195eSAndrew Gallatin {
377b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
378b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
379b2fc195eSAndrew Gallatin 	return ENXIO;
380b2fc195eSAndrew Gallatin }
381b2fc195eSAndrew Gallatin #endif
382b2fc195eSAndrew Gallatin /*
383b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
384b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
385b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
386b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
387b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
388b2fc195eSAndrew Gallatin  *
389b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
390b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
391b2fc195eSAndrew Gallatin  *
392b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
393b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
394b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
395b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
396b2fc195eSAndrew Gallatin  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
397b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
398b2fc195eSAndrew Gallatin  * firmware image, and set tx.boundary to 4KB.
399b2fc195eSAndrew Gallatin  */
400b2fc195eSAndrew Gallatin 
401b2fc195eSAndrew Gallatin static void
4026d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
403b2fc195eSAndrew Gallatin {
404b2fc195eSAndrew Gallatin 	int err, aligned = 0;
405b2fc195eSAndrew Gallatin 	device_t pdev;
406b2fc195eSAndrew Gallatin 	uint16_t pvend, pdid;
407b2fc195eSAndrew Gallatin 
408d91b1b49SAndrew Gallatin 
409d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
410d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
411d91b1b49SAndrew Gallatin 			aligned = 1;
412d91b1b49SAndrew Gallatin 		else
413d91b1b49SAndrew Gallatin 			aligned = 0;
414d91b1b49SAndrew Gallatin 		if (mxge_verbose)
415d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
416d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
417d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
418d91b1b49SAndrew Gallatin 		goto abort;
419d91b1b49SAndrew Gallatin 	}
420d91b1b49SAndrew Gallatin 
421d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
422d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
423d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
424d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
425d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
426d91b1b49SAndrew Gallatin 			      sc->link_width);
427d91b1b49SAndrew Gallatin 		aligned = 1;
428d91b1b49SAndrew Gallatin 		goto abort;
429d91b1b49SAndrew Gallatin 	}
430d91b1b49SAndrew Gallatin 
431b2fc195eSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
432b2fc195eSAndrew Gallatin 	if (pdev == NULL) {
433b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
434b2fc195eSAndrew Gallatin 		goto abort;
435b2fc195eSAndrew Gallatin 	}
436b2fc195eSAndrew Gallatin 	pvend = pci_read_config(pdev, PCIR_VENDOR, 2);
437b2fc195eSAndrew Gallatin 	pdid = pci_read_config(pdev, PCIR_DEVICE, 2);
438b2fc195eSAndrew Gallatin 
439b2fc195eSAndrew Gallatin 	/* see if we can enable ECRC's on an upstream
440b2fc195eSAndrew Gallatin 	   Nvidia bridge */
4416d87a65dSAndrew Gallatin 	if (mxge_nvidia_ecrc_enable &&
442b2fc195eSAndrew Gallatin 	    (pvend == 0x10de && pdid == 0x005d)) {
4436d87a65dSAndrew Gallatin 		err = mxge_enable_nvidia_ecrc(sc, pdev);
444b2fc195eSAndrew Gallatin 		if (err == 0) {
445b2fc195eSAndrew Gallatin 			aligned = 1;
4465e7d8541SAndrew Gallatin 			if (mxge_verbose)
447b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
4485e7d8541SAndrew Gallatin 					      "Assuming aligned completions"
4495e7d8541SAndrew Gallatin 					      " (ECRC)\n");
450b2fc195eSAndrew Gallatin 		}
451b2fc195eSAndrew Gallatin 	}
452b2fc195eSAndrew Gallatin 	/* see if the upstream bridge is known to
453b2fc195eSAndrew Gallatin 	   provided aligned completions */
454b2fc195eSAndrew Gallatin 	if (/* HT2000 */ (pvend == 0x1166 && pdid == 0x0132) ||
4550fa7f681SAndrew Gallatin 	    /* PLX */    (pvend == 0x10b5 && pdid == 0x8532) ||
4560fa7f681SAndrew Gallatin 	    /* Intel */  (pvend == 0x8086 &&
457d91b1b49SAndrew Gallatin 	      /* E5000 NorthBridge*/((pdid >= 0x25f7 && pdid <= 0x25fa) ||
458d91b1b49SAndrew Gallatin 	      /* E5000 SouthBridge*/ (pdid >= 0x3510 && pdid <= 0x351b)))) {
459d91b1b49SAndrew Gallatin 		aligned = 1;
4605e7d8541SAndrew Gallatin 		if (mxge_verbose)
461b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
4625e7d8541SAndrew Gallatin 				      "Assuming aligned completions "
4635e7d8541SAndrew Gallatin 				      "(0x%x:0x%x)\n", pvend, pdid);
464b2fc195eSAndrew Gallatin 	}
465b2fc195eSAndrew Gallatin 
466b2fc195eSAndrew Gallatin abort:
467b2fc195eSAndrew Gallatin 	if (aligned) {
4686d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
469b2fc195eSAndrew Gallatin 		sc->tx.boundary = 4096;
470b2fc195eSAndrew Gallatin 	} else {
4716d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
472b2fc195eSAndrew Gallatin 		sc->tx.boundary = 2048;
473b2fc195eSAndrew Gallatin 	}
474b2fc195eSAndrew Gallatin }
475b2fc195eSAndrew Gallatin 
476b2fc195eSAndrew Gallatin union qualhack
477b2fc195eSAndrew Gallatin {
478b2fc195eSAndrew Gallatin         const char *ro_char;
479b2fc195eSAndrew Gallatin         char *rw_char;
480b2fc195eSAndrew Gallatin };
481b2fc195eSAndrew Gallatin 
4824da0d523SAndrew Gallatin static int
4834da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
4844da0d523SAndrew Gallatin {
485b824b7d8SAndrew Gallatin 
4864da0d523SAndrew Gallatin 
4874da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
4884da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
4894da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
4904da0d523SAndrew Gallatin 		return EIO;
4914da0d523SAndrew Gallatin 	}
4924da0d523SAndrew Gallatin 
4934da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
4944da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
4954da0d523SAndrew Gallatin 	if (mxge_verbose)
4964da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
4974da0d523SAndrew Gallatin 
498b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
499b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
5004da0d523SAndrew Gallatin 
501b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
502b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
5034da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
5044da0d523SAndrew Gallatin 			      sc->fw_version);
5054da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
5064da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
5074da0d523SAndrew Gallatin 		return EINVAL;
5084da0d523SAndrew Gallatin 	}
5094da0d523SAndrew Gallatin 	return 0;
5104da0d523SAndrew Gallatin 
5114da0d523SAndrew Gallatin }
512b2fc195eSAndrew Gallatin 
513b2fc195eSAndrew Gallatin static int
5146d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
515b2fc195eSAndrew Gallatin {
51633d54970SLuigi Rizzo 	const struct firmware *fw;
517b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
518b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
519b2fc195eSAndrew Gallatin 	const char *fw_data;
520b2fc195eSAndrew Gallatin 	union qualhack hack;
521b2fc195eSAndrew Gallatin 	int status;
5224da0d523SAndrew Gallatin 	unsigned int i;
5234da0d523SAndrew Gallatin 	char dummy;
524b2fc195eSAndrew Gallatin 
525b2fc195eSAndrew Gallatin 
526b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
527b2fc195eSAndrew Gallatin 
528b2fc195eSAndrew Gallatin 	if (fw == NULL) {
529b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
530b2fc195eSAndrew Gallatin 			      sc->fw_name);
531b2fc195eSAndrew Gallatin 		return ENOENT;
532b2fc195eSAndrew Gallatin 	}
533b2fc195eSAndrew Gallatin 	if (fw->datasize > *limit ||
534b2fc195eSAndrew Gallatin 	    fw->datasize < MCP_HEADER_PTR_OFFSET + 4) {
535b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n",
536b2fc195eSAndrew Gallatin 			      sc->fw_name, (int)fw->datasize, (int) *limit);
537b2fc195eSAndrew Gallatin 		status = ENOSPC;
538b2fc195eSAndrew Gallatin 		goto abort_with_fw;
539b2fc195eSAndrew Gallatin 	}
540b2fc195eSAndrew Gallatin 	*limit = fw->datasize;
541b2fc195eSAndrew Gallatin 
542b2fc195eSAndrew Gallatin 	/* check id */
543b2fc195eSAndrew Gallatin 	fw_data = (const char *)fw->data;
544b2fc195eSAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
545b2fc195eSAndrew Gallatin 			     (fw_data + MCP_HEADER_PTR_OFFSET));
546b2fc195eSAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) {
547b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
548b2fc195eSAndrew Gallatin 		status = EIO;
549b2fc195eSAndrew Gallatin 		goto abort_with_fw;
550b2fc195eSAndrew Gallatin 	}
551b2fc195eSAndrew Gallatin 	hdr = (const void*)(fw_data + hdr_offset);
552b2fc195eSAndrew Gallatin 
5534da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
5544da0d523SAndrew Gallatin 	if (status != 0)
5554da0d523SAndrew Gallatin 		goto abort_with_fw;
556b2fc195eSAndrew Gallatin 
557b2fc195eSAndrew Gallatin 	hack.ro_char = fw_data;
558b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
5594da0d523SAndrew Gallatin 	for (i = 0; i < *limit; i += 256) {
5604da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
5614da0d523SAndrew Gallatin 			      hack.rw_char + i,
5624da0d523SAndrew Gallatin 			      min(256U, (unsigned)(*limit - i)));
5634da0d523SAndrew Gallatin 		mb();
5644da0d523SAndrew Gallatin 		dummy = *sc->sram;
5654da0d523SAndrew Gallatin 		mb();
5664da0d523SAndrew Gallatin 	}
567b2fc195eSAndrew Gallatin 
568b2fc195eSAndrew Gallatin 	status = 0;
569b2fc195eSAndrew Gallatin abort_with_fw:
570b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
571b2fc195eSAndrew Gallatin 	return status;
572b2fc195eSAndrew Gallatin }
573b2fc195eSAndrew Gallatin 
574b2fc195eSAndrew Gallatin /*
575b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
576b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
577b2fc195eSAndrew Gallatin  */
578b2fc195eSAndrew Gallatin 
579b2fc195eSAndrew Gallatin static void
5806d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
581b2fc195eSAndrew Gallatin {
582b2fc195eSAndrew Gallatin 	char buf_bytes[72];
583b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
584b2fc195eSAndrew Gallatin 	volatile char *submit;
585b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
586b2fc195eSAndrew Gallatin 	int i;
587b2fc195eSAndrew Gallatin 
588b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
589b2fc195eSAndrew Gallatin 
590b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
591b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
592b2fc195eSAndrew Gallatin 	*confirm = 0;
593b2fc195eSAndrew Gallatin 	mb();
594b2fc195eSAndrew Gallatin 
595b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
596b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
597b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
598b2fc195eSAndrew Gallatin 	*/
599b2fc195eSAndrew Gallatin 
6006d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
6016d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
602b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
603b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
604b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
6056d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
6066d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
607b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
608b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
609b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
610b2fc195eSAndrew Gallatin 
611b2fc195eSAndrew Gallatin 
6120fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
613b2fc195eSAndrew Gallatin 
6146d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
615b2fc195eSAndrew Gallatin 	mb();
616b2fc195eSAndrew Gallatin 	DELAY(1000);
617b2fc195eSAndrew Gallatin 	mb();
618b2fc195eSAndrew Gallatin 	i = 0;
619b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
620b2fc195eSAndrew Gallatin 		DELAY(1000);
621b2fc195eSAndrew Gallatin 		i++;
622b2fc195eSAndrew Gallatin 	}
623b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
624b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
625b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
626b2fc195eSAndrew Gallatin 			      *confirm);
627b2fc195eSAndrew Gallatin 	}
628b2fc195eSAndrew Gallatin 	return;
629b2fc195eSAndrew Gallatin }
630b2fc195eSAndrew Gallatin 
631b2fc195eSAndrew Gallatin static int
6326d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
633b2fc195eSAndrew Gallatin {
634b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
635b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
636b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
6370fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
638b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
639b2fc195eSAndrew Gallatin 	int sleep_total = 0;
640b2fc195eSAndrew Gallatin 
641b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
642b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
643b2fc195eSAndrew Gallatin 
644b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
645b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
646b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
647b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
6486d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
6496d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
650b2fc195eSAndrew Gallatin 
651b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
652b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
653a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
654b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
655b2fc195eSAndrew Gallatin 	mb();
6566d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
657b2fc195eSAndrew Gallatin 
6585e7d8541SAndrew Gallatin 	/* wait up to 20ms */
6595e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
660b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
661b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
662b2fc195eSAndrew Gallatin 		mb();
663b2fc195eSAndrew Gallatin 		if (response->result != 0xffffffff) {
664b2fc195eSAndrew Gallatin 			if (response->result == 0) {
665b2fc195eSAndrew Gallatin 				data->data0 = be32toh(response->data);
666a98d6cd7SAndrew Gallatin 				mtx_unlock(&sc->cmd_mtx);
667b2fc195eSAndrew Gallatin 				return 0;
668b2fc195eSAndrew Gallatin 			} else {
669b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
6706d87a65dSAndrew Gallatin 					      "mxge: command %d "
671b2fc195eSAndrew Gallatin 					      "failed, result = %d\n",
672b2fc195eSAndrew Gallatin 					      cmd, be32toh(response->result));
673a98d6cd7SAndrew Gallatin 				mtx_unlock(&sc->cmd_mtx);
674b2fc195eSAndrew Gallatin 				return ENXIO;
675b2fc195eSAndrew Gallatin 			}
676b2fc195eSAndrew Gallatin 		}
6775e7d8541SAndrew Gallatin 		DELAY(1000);
678b2fc195eSAndrew Gallatin 	}
679a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
6806d87a65dSAndrew Gallatin 	device_printf(sc->dev, "mxge: command %d timed out"
681b2fc195eSAndrew Gallatin 		      "result = %d\n",
682b2fc195eSAndrew Gallatin 		      cmd, be32toh(response->result));
683b2fc195eSAndrew Gallatin 	return EAGAIN;
684b2fc195eSAndrew Gallatin }
685b2fc195eSAndrew Gallatin 
6864da0d523SAndrew Gallatin static int
6874da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
6884da0d523SAndrew Gallatin {
6894da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
6904da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
6914da0d523SAndrew Gallatin 	size_t hdr_offset;
6924da0d523SAndrew Gallatin 	int status;
6934da0d523SAndrew Gallatin 
6944da0d523SAndrew Gallatin 	/* find running firmware header */
6954da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
6964da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
6974da0d523SAndrew Gallatin 
6984da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
6994da0d523SAndrew Gallatin 		device_printf(sc->dev,
7004da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
7014da0d523SAndrew Gallatin 			      (int)hdr_offset);
7024da0d523SAndrew Gallatin 		return EIO;
7034da0d523SAndrew Gallatin 	}
7044da0d523SAndrew Gallatin 
7054da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
7064da0d523SAndrew Gallatin 	 * validate firmware */
7074da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
7084da0d523SAndrew Gallatin 	if (hdr == NULL) {
7094da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
7104da0d523SAndrew Gallatin 		return ENOMEM;
7114da0d523SAndrew Gallatin 	}
7124da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
7134da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
7144da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
7154da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7164da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
717b824b7d8SAndrew Gallatin 
718b824b7d8SAndrew Gallatin 	/*
719b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
720b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
721b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
722b824b7d8SAndrew Gallatin 	 */
723b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
724b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
725b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
726b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
727b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
728b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
729b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
730b824b7d8SAndrew Gallatin 	}
731b824b7d8SAndrew Gallatin 
7324da0d523SAndrew Gallatin 	return status;
7334da0d523SAndrew Gallatin }
7344da0d523SAndrew Gallatin 
735b2fc195eSAndrew Gallatin 
736b2fc195eSAndrew Gallatin static int
7376d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc)
738b2fc195eSAndrew Gallatin {
739b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
740b2fc195eSAndrew Gallatin 	volatile char *submit;
741b2fc195eSAndrew Gallatin 	char buf_bytes[72];
742b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
743b2fc195eSAndrew Gallatin 	int status, i;
744b2fc195eSAndrew Gallatin 
745b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
746b2fc195eSAndrew Gallatin 
747b2fc195eSAndrew Gallatin 	size = sc->sram_size;
7486d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
749b2fc195eSAndrew Gallatin 	if (status) {
7504da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
7514da0d523SAndrew Gallatin 		   it is new enough */
7524da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
7534da0d523SAndrew Gallatin 		if (status) {
7544da0d523SAndrew Gallatin 			device_printf(sc->dev,
7554da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
756b2fc195eSAndrew Gallatin 			return status;
757b2fc195eSAndrew Gallatin 		}
7584da0d523SAndrew Gallatin 		device_printf(sc->dev,
7594da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
7604da0d523SAndrew Gallatin 		if (sc->tx.boundary == 4096) {
7614da0d523SAndrew Gallatin 			device_printf(sc->dev,
7624da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
7634da0d523SAndrew Gallatin 				 ".  For optimal\n");
7644da0d523SAndrew Gallatin 			device_printf(sc->dev,
7654da0d523SAndrew Gallatin 				 "performance consider loading optimized "
7664da0d523SAndrew Gallatin 				 "firmware\n");
7674da0d523SAndrew Gallatin 		}
768d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
769d91b1b49SAndrew Gallatin 		sc->tx.boundary = 2048;
770d91b1b49SAndrew Gallatin 		return 0;
7714da0d523SAndrew Gallatin 	}
772b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
773b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
774b2fc195eSAndrew Gallatin 	*confirm = 0;
775b2fc195eSAndrew Gallatin 	mb();
776b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
777b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
778b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
779b2fc195eSAndrew Gallatin 	*/
780b2fc195eSAndrew Gallatin 
7816d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7826d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
783b2fc195eSAndrew Gallatin 
784b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
785b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
786b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
787b2fc195eSAndrew Gallatin 
788b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
789b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
790b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
791b2fc195eSAndrew Gallatin 	*/
792b2fc195eSAndrew Gallatin 					/* where the code starts*/
7936d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
794b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
795b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
796b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
797b2fc195eSAndrew Gallatin 
7980fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
7996d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
800b2fc195eSAndrew Gallatin 	mb();
801b2fc195eSAndrew Gallatin 	DELAY(1000);
802b2fc195eSAndrew Gallatin 	mb();
803b2fc195eSAndrew Gallatin 	i = 0;
804b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
805b2fc195eSAndrew Gallatin 		DELAY(1000*10);
806b2fc195eSAndrew Gallatin 		i++;
807b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
808b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
809b2fc195eSAndrew Gallatin 	}
810b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
811b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
812b2fc195eSAndrew Gallatin 			confirm, *confirm);
813b2fc195eSAndrew Gallatin 
814b2fc195eSAndrew Gallatin 		return ENXIO;
815b2fc195eSAndrew Gallatin 	}
816b2fc195eSAndrew Gallatin 	return 0;
817b2fc195eSAndrew Gallatin }
818b2fc195eSAndrew Gallatin 
819b2fc195eSAndrew Gallatin static int
8206d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
821b2fc195eSAndrew Gallatin {
8226d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
823b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
824b2fc195eSAndrew Gallatin 	int status;
825b2fc195eSAndrew Gallatin 
826b2fc195eSAndrew Gallatin 
827b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
828b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
829b2fc195eSAndrew Gallatin 
830b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
831b2fc195eSAndrew Gallatin 
8325e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
833b2fc195eSAndrew Gallatin 	return status;
834b2fc195eSAndrew Gallatin }
835b2fc195eSAndrew Gallatin 
836b2fc195eSAndrew Gallatin static int
8376d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
838b2fc195eSAndrew Gallatin {
8396d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
840b2fc195eSAndrew Gallatin 	int status;
841b2fc195eSAndrew Gallatin 
842b2fc195eSAndrew Gallatin 	if (pause)
8435e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
844b2fc195eSAndrew Gallatin 				       &cmd);
845b2fc195eSAndrew Gallatin 	else
8465e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
847b2fc195eSAndrew Gallatin 				       &cmd);
848b2fc195eSAndrew Gallatin 
849b2fc195eSAndrew Gallatin 	if (status) {
850b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
851b2fc195eSAndrew Gallatin 		return ENXIO;
852b2fc195eSAndrew Gallatin 	}
853b2fc195eSAndrew Gallatin 	sc->pause = pause;
854b2fc195eSAndrew Gallatin 	return 0;
855b2fc195eSAndrew Gallatin }
856b2fc195eSAndrew Gallatin 
857b2fc195eSAndrew Gallatin static void
8586d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
859b2fc195eSAndrew Gallatin {
8606d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
861b2fc195eSAndrew Gallatin 	int status;
862b2fc195eSAndrew Gallatin 
863b2fc195eSAndrew Gallatin 	if (promisc)
8645e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
865b2fc195eSAndrew Gallatin 				       &cmd);
866b2fc195eSAndrew Gallatin 	else
8675e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
868b2fc195eSAndrew Gallatin 				       &cmd);
869b2fc195eSAndrew Gallatin 
870b2fc195eSAndrew Gallatin 	if (status) {
871b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
872b2fc195eSAndrew Gallatin 	}
873b2fc195eSAndrew Gallatin }
874b2fc195eSAndrew Gallatin 
8750fa7f681SAndrew Gallatin static void
8760fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
8770fa7f681SAndrew Gallatin {
8780fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
8790fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
8800fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
8810fa7f681SAndrew Gallatin 	int err;
8820fa7f681SAndrew Gallatin 
8830fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
8840fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
8850fa7f681SAndrew Gallatin 		return;
8860fa7f681SAndrew Gallatin 
8870fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
8880fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
8890fa7f681SAndrew Gallatin 	if (err != 0) {
8900fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
8910fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
8920fa7f681SAndrew Gallatin 		return;
8930fa7f681SAndrew Gallatin 	}
8940fa7f681SAndrew Gallatin 
895b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
896b824b7d8SAndrew Gallatin 		return;
8970fa7f681SAndrew Gallatin 
8980fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
8990fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
9000fa7f681SAndrew Gallatin 		return;
9010fa7f681SAndrew Gallatin 
9020fa7f681SAndrew Gallatin 	/* Flush all the filters */
9030fa7f681SAndrew Gallatin 
9040fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
9050fa7f681SAndrew Gallatin 	if (err != 0) {
9060fa7f681SAndrew Gallatin 		device_printf(sc->dev,
9070fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
9080fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
9090fa7f681SAndrew Gallatin 		return;
9100fa7f681SAndrew Gallatin 	}
9110fa7f681SAndrew Gallatin 
9120fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
9130fa7f681SAndrew Gallatin 
9140fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
9150fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
9160fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
9170fa7f681SAndrew Gallatin 			continue;
9180fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
9190fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
9200fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
9210fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
9220fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
9230fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
9240fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
9250fa7f681SAndrew Gallatin 		if (err != 0) {
9260fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
9270fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
9280fa7f681SAndrew Gallatin 			       "%d\t", err);
9290fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
9300fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
9310fa7f681SAndrew Gallatin 			return;
9320fa7f681SAndrew Gallatin 		}
9330fa7f681SAndrew Gallatin 	}
9340fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
9350fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
9360fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
9370fa7f681SAndrew Gallatin 	if (err != 0) {
9380fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
9390fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
9400fa7f681SAndrew Gallatin 	}
9410fa7f681SAndrew Gallatin }
9420fa7f681SAndrew Gallatin 
9430fa7f681SAndrew Gallatin 
944b2fc195eSAndrew Gallatin static int
9456d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc)
946b2fc195eSAndrew Gallatin {
947b2fc195eSAndrew Gallatin 
9486d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
9495e7d8541SAndrew Gallatin 	size_t bytes;
9505e7d8541SAndrew Gallatin 	int status;
951b2fc195eSAndrew Gallatin 
952b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
953b2fc195eSAndrew Gallatin 	   is alive */
954b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
9555e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
956b2fc195eSAndrew Gallatin 	if (status != 0) {
957b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
958b2fc195eSAndrew Gallatin 		return ENXIO;
959b2fc195eSAndrew Gallatin 	}
960b2fc195eSAndrew Gallatin 
961091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
962091feecdSAndrew Gallatin 
963b2fc195eSAndrew Gallatin 	/* Now exchange information about interrupts  */
9645e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\
9655e7d8541SAndrew Gallatin 	memset(sc->rx_done.entry, 0, bytes);
9665e7d8541SAndrew Gallatin 	cmd.data0 = (uint32_t)bytes;
9675e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
9685e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr);
9695e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr);
9705e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd);
971b2fc195eSAndrew Gallatin 
9726d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
9735e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
9745e7d8541SAndrew Gallatin 
9755e7d8541SAndrew Gallatin 
9765e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
9775e7d8541SAndrew Gallatin 
9785e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
9795e7d8541SAndrew Gallatin 	sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
9805e7d8541SAndrew Gallatin 
9815e7d8541SAndrew Gallatin 
9825e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
9836d87a65dSAndrew Gallatin 				&cmd);
9845e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
985b2fc195eSAndrew Gallatin 	if (status != 0) {
986b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
987b2fc195eSAndrew Gallatin 		return status;
988b2fc195eSAndrew Gallatin 	}
989b2fc195eSAndrew Gallatin 
9905e7d8541SAndrew Gallatin 
9915e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
9925e7d8541SAndrew Gallatin 
9935e7d8541SAndrew Gallatin 
9945e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
9955e7d8541SAndrew Gallatin 	sc->read_dma = sc->write_dma = sc->read_write_dma = 0;
9965e7d8541SAndrew Gallatin 
9975e7d8541SAndrew Gallatin 	/* Read DMA */
998a98d6cd7SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr);
999a98d6cd7SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr);
10005e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10000;
10015e7d8541SAndrew Gallatin 
10025e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
10035e7d8541SAndrew Gallatin 	if (status != 0)
10045e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read dma benchmark failed\n");
10055e7d8541SAndrew Gallatin 	else
10065e7d8541SAndrew Gallatin 		sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
10075e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
10085e7d8541SAndrew Gallatin 
10095e7d8541SAndrew Gallatin 	/* Write DMA */
1010a98d6cd7SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr);
1011a98d6cd7SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr);
10125e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x1;
10135e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
10145e7d8541SAndrew Gallatin 	if (status != 0)
10155e7d8541SAndrew Gallatin 		device_printf(sc->dev, "write dma benchmark failed\n");
10165e7d8541SAndrew Gallatin 	else
10175e7d8541SAndrew Gallatin 		sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
10185e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
10195e7d8541SAndrew Gallatin 	/* Read/Write DMA */
1020a98d6cd7SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr);
1021a98d6cd7SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr);
10225e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10001;
10235e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
10245e7d8541SAndrew Gallatin 	if (status != 0)
10255e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read/write dma benchmark failed\n");
10265e7d8541SAndrew Gallatin 	else
10275e7d8541SAndrew Gallatin 		sc->read_write_dma =
10285e7d8541SAndrew Gallatin 			((cmd.data0>>16) * sc->tx.boundary * 2 * 2) /
10295e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
10305e7d8541SAndrew Gallatin 
1031b2fc195eSAndrew Gallatin 	/* reset mcp/driver shared state back to 0 */
10325e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
10335e7d8541SAndrew Gallatin 	sc->rx_done.idx = 0;
10345e7d8541SAndrew Gallatin 	sc->rx_done.cnt = 0;
1035b2fc195eSAndrew Gallatin 	sc->tx.req = 0;
1036b2fc195eSAndrew Gallatin 	sc->tx.done = 0;
10375e7d8541SAndrew Gallatin 	sc->tx.pkt_done = 0;
1038a82c2581SAndrew Gallatin 	sc->tx.wake = 0;
1039a82c2581SAndrew Gallatin 	sc->tx.stall = 0;
1040b2fc195eSAndrew Gallatin 	sc->rx_big.cnt = 0;
1041b2fc195eSAndrew Gallatin 	sc->rx_small.cnt = 0;
1042b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
1043a98d6cd7SAndrew Gallatin 	sc->fw_stats->valid = 0;
1044a98d6cd7SAndrew Gallatin 	sc->fw_stats->send_done_count = 0;
10456d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
10466d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
10476d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
10480fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1049b2fc195eSAndrew Gallatin 	return status;
1050b2fc195eSAndrew Gallatin }
1051b2fc195eSAndrew Gallatin 
1052b2fc195eSAndrew Gallatin static int
10536d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1054b2fc195eSAndrew Gallatin {
10556d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1056b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1057b2fc195eSAndrew Gallatin         int err;
1058b2fc195eSAndrew Gallatin 
1059b2fc195eSAndrew Gallatin         sc = arg1;
1060b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1061b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1062b2fc195eSAndrew Gallatin         if (err != 0) {
1063b2fc195eSAndrew Gallatin                 return err;
1064b2fc195eSAndrew Gallatin         }
1065b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1066b2fc195eSAndrew Gallatin                 return 0;
1067b2fc195eSAndrew Gallatin 
1068b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1069b2fc195eSAndrew Gallatin                 return EINVAL;
1070b2fc195eSAndrew Gallatin 
1071a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
10725e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1073b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
10745e7d8541SAndrew Gallatin 
1075a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1076b2fc195eSAndrew Gallatin         return err;
1077b2fc195eSAndrew Gallatin }
1078b2fc195eSAndrew Gallatin 
1079b2fc195eSAndrew Gallatin static int
10806d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1081b2fc195eSAndrew Gallatin {
10826d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1083b2fc195eSAndrew Gallatin         unsigned int enabled;
1084b2fc195eSAndrew Gallatin         int err;
1085b2fc195eSAndrew Gallatin 
1086b2fc195eSAndrew Gallatin         sc = arg1;
1087b2fc195eSAndrew Gallatin         enabled = sc->pause;
1088b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1089b2fc195eSAndrew Gallatin         if (err != 0) {
1090b2fc195eSAndrew Gallatin                 return err;
1091b2fc195eSAndrew Gallatin         }
1092b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1093b2fc195eSAndrew Gallatin                 return 0;
1094b2fc195eSAndrew Gallatin 
1095a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
10966d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1097a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1098b2fc195eSAndrew Gallatin         return err;
1099b2fc195eSAndrew Gallatin }
1100b2fc195eSAndrew Gallatin 
1101b2fc195eSAndrew Gallatin static int
11026d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1103b2fc195eSAndrew Gallatin {
1104b2fc195eSAndrew Gallatin         int err;
1105b2fc195eSAndrew Gallatin 
1106b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1107b2fc195eSAndrew Gallatin                 return EFAULT;
1108b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1109b2fc195eSAndrew Gallatin         arg1 = NULL;
1110b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1111b2fc195eSAndrew Gallatin 
1112b2fc195eSAndrew Gallatin         return err;
1113b2fc195eSAndrew Gallatin }
1114b2fc195eSAndrew Gallatin 
1115b2fc195eSAndrew Gallatin static void
11166d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1117b2fc195eSAndrew Gallatin {
1118b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1119b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
11205e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
1121b2fc195eSAndrew Gallatin 
1122b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1123b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
1124b2fc195eSAndrew Gallatin 	fw = sc->fw_stats;
1125b2fc195eSAndrew Gallatin 
11265e7d8541SAndrew Gallatin 	/* random information */
11275e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11285e7d8541SAndrew Gallatin 		       "firmware_version",
11295e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
11305e7d8541SAndrew Gallatin 		       0, "firmware version");
11315e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11325e7d8541SAndrew Gallatin 		       "serial_number",
11335e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
11345e7d8541SAndrew Gallatin 		       0, "serial number");
11355e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11365e7d8541SAndrew Gallatin 		       "product_code",
11375e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
11385e7d8541SAndrew Gallatin 		       0, "product_code");
11395e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1140d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1141d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1142d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1143d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11445e7d8541SAndrew Gallatin 		       "tx_boundary",
11455e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.boundary,
11465e7d8541SAndrew Gallatin 		       0, "tx_boundary");
11475e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1148091feecdSAndrew Gallatin 		       "write_combine",
1149091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1150091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1151091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11525e7d8541SAndrew Gallatin 		       "read_dma_MBs",
11535e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
11545e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
11555e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11565e7d8541SAndrew Gallatin 		       "write_dma_MBs",
11575e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
11585e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
11595e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11605e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
11615e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
11625e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
11635e7d8541SAndrew Gallatin 
11645e7d8541SAndrew Gallatin 
11655e7d8541SAndrew Gallatin 	/* performance related tunables */
1166b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1167b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1168b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
11696d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1170b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1171b2fc195eSAndrew Gallatin 
1172b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1173b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1174b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
11756d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1176b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1177b2fc195eSAndrew Gallatin 
1178b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11795e7d8541SAndrew Gallatin 		       "deassert_wait",
11805e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
11815e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1182b2fc195eSAndrew Gallatin 
1183b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1184b2fc195eSAndrew Gallatin 	   Need to swap it */
1185b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1186b2fc195eSAndrew Gallatin 			"link_up",
1187b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
11886d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1189b2fc195eSAndrew Gallatin 			"I", "link up");
1190b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1191b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1192b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
11936d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1194b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1195b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1196b2fc195eSAndrew Gallatin 			"dropped_link_overflow",
1197b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
11986d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1199b2fc195eSAndrew Gallatin 			"I", "dropped_link_overflow");
1200b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1201b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1202b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1203b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
12046d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1205b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1206b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
12070fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
12080fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
12090fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
12100fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
12110fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
12120fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1213b2fc195eSAndrew Gallatin 			"dropped_runt",
1214b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
12156d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1216b2fc195eSAndrew Gallatin 			"I", "dropped_runt");
1217b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1218b2fc195eSAndrew Gallatin 			"dropped_overrun",
1219b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
12206d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1221b2fc195eSAndrew Gallatin 			"I", "dropped_overrun");
1222b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1223b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1224b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1225b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
12266d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1227b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1228b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1229b2fc195eSAndrew Gallatin 			"dropped_no_big_buffer",
1230b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
12316d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1232b2fc195eSAndrew Gallatin 			"I", "dropped_no_big_buffer");
1233b2fc195eSAndrew Gallatin 
1234b2fc195eSAndrew Gallatin 	/* host counters exported for debugging */
1235b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12365e7d8541SAndrew Gallatin 		       "rx_small_cnt",
12375e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_small.cnt,
12385e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
12395e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12405e7d8541SAndrew Gallatin 		       "rx_big_cnt",
12415e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_big.cnt,
12425e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
12435e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1244b2fc195eSAndrew Gallatin 		       "tx_req",
1245b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.req,
1246b2fc195eSAndrew Gallatin 		       0, "tx_req");
1247b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1248b2fc195eSAndrew Gallatin 		       "tx_done",
1249b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.done,
1250b2fc195eSAndrew Gallatin 		       0, "tx_done");
1251b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12525e7d8541SAndrew Gallatin 		       "tx_pkt_done",
12535e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.pkt_done,
12545e7d8541SAndrew Gallatin 		       0, "tx_done");
1255a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1256a82c2581SAndrew Gallatin 		       "tx_stall",
1257a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.stall,
1258a82c2581SAndrew Gallatin 		       0, "tx_stall");
1259a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1260a82c2581SAndrew Gallatin 		       "tx_wake",
1261a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.wake,
1262a82c2581SAndrew Gallatin 		       0, "tx_wake");
12635e7d8541SAndrew Gallatin 
12645e7d8541SAndrew Gallatin 	/* verbose printing? */
1265b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12665e7d8541SAndrew Gallatin 		       "verbose",
12675e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
12685e7d8541SAndrew Gallatin 		       0, "verbose printing");
1269b2fc195eSAndrew Gallatin 
1270b2fc195eSAndrew Gallatin }
1271b2fc195eSAndrew Gallatin 
1272b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1273b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1274b2fc195eSAndrew Gallatin 
1275b2fc195eSAndrew Gallatin static inline void
12766d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx,
1277b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1278b2fc195eSAndrew Gallatin {
1279b2fc195eSAndrew Gallatin         int idx, starting_slot;
1280b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1281b2fc195eSAndrew Gallatin         while (cnt > 1) {
1282b2fc195eSAndrew Gallatin                 cnt--;
1283b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
12846d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1285b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1286b2fc195eSAndrew Gallatin                 mb();
1287b2fc195eSAndrew Gallatin         }
1288b2fc195eSAndrew Gallatin }
1289b2fc195eSAndrew Gallatin 
1290b2fc195eSAndrew Gallatin /*
1291b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1292b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1293b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1294b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1295b2fc195eSAndrew Gallatin  */
1296b2fc195eSAndrew Gallatin 
1297b2fc195eSAndrew Gallatin static inline void
12986d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src,
1299b2fc195eSAndrew Gallatin                   int cnt)
1300b2fc195eSAndrew Gallatin {
1301b2fc195eSAndrew Gallatin         int idx, i;
1302b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1303b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1304b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1305b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
13065e7d8541SAndrew Gallatin 	uint8_t last_flags;
1307b2fc195eSAndrew Gallatin 
1308b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1309b2fc195eSAndrew Gallatin 
13105e7d8541SAndrew Gallatin 	last_flags = src->flags;
13115e7d8541SAndrew Gallatin 	src->flags = 0;
1312b2fc195eSAndrew Gallatin         mb();
1313b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1314b2fc195eSAndrew Gallatin         srcp = src;
1315b2fc195eSAndrew Gallatin 
1316b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1317b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
13186d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1319b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1320b2fc195eSAndrew Gallatin                         srcp += 2;
1321b2fc195eSAndrew Gallatin                         dstp += 2;
1322b2fc195eSAndrew Gallatin                 }
1323b2fc195eSAndrew Gallatin         } else {
1324b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1325b2fc195eSAndrew Gallatin                    that it is submitted below */
13266d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1327b2fc195eSAndrew Gallatin                 i = 0;
1328b2fc195eSAndrew Gallatin         }
1329b2fc195eSAndrew Gallatin         if (i < cnt) {
1330b2fc195eSAndrew Gallatin                 /* submit the first request */
13316d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1332b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1333b2fc195eSAndrew Gallatin         }
1334b2fc195eSAndrew Gallatin 
1335b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
13365e7d8541SAndrew Gallatin         src->flags = last_flags;
1337b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1338b2fc195eSAndrew Gallatin         src_ints+=3;
1339b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1340b2fc195eSAndrew Gallatin         dst_ints+=3;
1341b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1342b2fc195eSAndrew Gallatin         tx->req += cnt;
1343b2fc195eSAndrew Gallatin         mb();
1344b2fc195eSAndrew Gallatin }
1345b2fc195eSAndrew Gallatin 
1346b2fc195eSAndrew Gallatin static inline void
13476d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt)
1348b2fc195eSAndrew Gallatin {
1349b2fc195eSAndrew Gallatin     tx->req += cnt;
1350b2fc195eSAndrew Gallatin     mb();
1351b2fc195eSAndrew Gallatin     while (cnt >= 4) {
13526d87a65dSAndrew Gallatin 	    mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64);
1353b2fc195eSAndrew Gallatin 	    mb();
1354b2fc195eSAndrew Gallatin 	    src += 4;
1355b2fc195eSAndrew Gallatin 	    cnt -= 4;
1356b2fc195eSAndrew Gallatin     }
1357b2fc195eSAndrew Gallatin     if (cnt > 0) {
1358b2fc195eSAndrew Gallatin 	    /* pad it to 64 bytes.  The src is 64 bytes bigger than it
1359b2fc195eSAndrew Gallatin 	       needs to be so that we don't overrun it */
13600fa7f681SAndrew Gallatin 	    mxge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), src, 64);
1361b2fc195eSAndrew Gallatin 	    mb();
1362b2fc195eSAndrew Gallatin     }
1363b2fc195eSAndrew Gallatin }
1364b2fc195eSAndrew Gallatin 
1365b2fc195eSAndrew Gallatin static void
1366aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt)
1367aed8e389SAndrew Gallatin {
1368aed8e389SAndrew Gallatin 	mxge_tx_buf_t *tx;
1369aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1370aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1371aed8e389SAndrew Gallatin 	struct ether_header *eh;
1372aed8e389SAndrew Gallatin 	struct ip *ip;
1373aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1374aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1375aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1376aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1377aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1378aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1379aed8e389SAndrew Gallatin 	static int once;
1380aed8e389SAndrew Gallatin 
1381aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1382aed8e389SAndrew Gallatin 
1383aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1384aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1385aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1386aed8e389SAndrew Gallatin 	 */
1387aed8e389SAndrew Gallatin 
1388aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1389aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1390aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1391aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh)
1392aed8e389SAndrew Gallatin 			    + sizeof (*ip))) {
1393aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1394aed8e389SAndrew Gallatin 			   sc->scratch);
1395aed8e389SAndrew Gallatin 		eh = (struct ether_header *)sc->scratch;
1396aed8e389SAndrew Gallatin 	} else {
1397aed8e389SAndrew Gallatin 		eh = mtod(m, struct ether_header *);
1398aed8e389SAndrew Gallatin 	}
1399aed8e389SAndrew Gallatin 	ip = (struct ip *) (eh + 1);
1400aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2)
1401aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1402aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2)
1403aed8e389SAndrew Gallatin 			   + sizeof (*tcp),  sc->scratch);
1404aed8e389SAndrew Gallatin 		eh = (struct ether_header *) sc->scratch;
1405aed8e389SAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1406aed8e389SAndrew Gallatin 	}
1407aed8e389SAndrew Gallatin 
1408aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1409aed8e389SAndrew Gallatin 	cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2));
1410aed8e389SAndrew Gallatin 
1411aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1412aed8e389SAndrew Gallatin 	cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1413aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1414aed8e389SAndrew Gallatin 
1415aed8e389SAndrew Gallatin 
1416aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1417aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1418aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1419aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1420aed8e389SAndrew Gallatin 
1421aed8e389SAndrew Gallatin 	tx = &sc->tx;
1422aed8e389SAndrew Gallatin 	req = tx->req_list;
1423aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1424aed8e389SAndrew Gallatin 	cnt = 0;
1425aed8e389SAndrew Gallatin 	rdma_count = 0;
1426aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1427aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1428aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1429aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1430aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1431aed8e389SAndrew Gallatin 	 *
1432aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1433aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1434aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1435aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1436aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1437aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1438aed8e389SAndrew Gallatin 	 *
1439aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1440aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1441aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1442aed8e389SAndrew Gallatin 	 */
1443aed8e389SAndrew Gallatin 
1444aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1445aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1446aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1447aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1448aed8e389SAndrew Gallatin 		len = seglen = seg->ds_len;
1449aed8e389SAndrew Gallatin 
1450aed8e389SAndrew Gallatin 		while (len) {
1451aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1452aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1453aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1454aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1455aed8e389SAndrew Gallatin 				/* payload */
1456aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1457aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1458aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1459aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1460aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1461aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1462aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1463aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1464aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1465aed8e389SAndrew Gallatin 				/* header ends */
1466aed8e389SAndrew Gallatin 				rdma_count = -1;
1467aed8e389SAndrew Gallatin 				cum_len_next = 0;
1468aed8e389SAndrew Gallatin 				seglen = -cum_len;
1469aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1470aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1471aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1472aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1473aed8e389SAndrew Gallatin 			    }
1474aed8e389SAndrew Gallatin 
1475aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1476aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1477aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1478aed8e389SAndrew Gallatin 			req->pad = 0;
1479aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1480aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1481aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1482aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1483aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1484aed8e389SAndrew Gallatin 			low += seglen;
1485aed8e389SAndrew Gallatin 			len -= seglen;
1486aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1487aed8e389SAndrew Gallatin 			flags = flags_next;
1488aed8e389SAndrew Gallatin 			req++;
1489aed8e389SAndrew Gallatin 			cnt++;
1490aed8e389SAndrew Gallatin 			rdma_count++;
1491aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1492aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1493aed8e389SAndrew Gallatin 			else
1494aed8e389SAndrew Gallatin 				cksum_offset = 0;
1495aed8e389SAndrew Gallatin 			if (__predict_false(cnt > MXGE_MAX_SEND_DESC))
1496aed8e389SAndrew Gallatin 				goto drop;
1497aed8e389SAndrew Gallatin 		}
1498aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1499aed8e389SAndrew Gallatin 		seg++;
1500aed8e389SAndrew Gallatin 	}
1501aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1502aed8e389SAndrew Gallatin 
1503aed8e389SAndrew Gallatin 	do {
1504aed8e389SAndrew Gallatin 		req--;
1505aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1506aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1507aed8e389SAndrew Gallatin 
1508aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1509aed8e389SAndrew Gallatin 	if (tx->wc_fifo == NULL)
1510aed8e389SAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1511aed8e389SAndrew Gallatin 	else
1512aed8e389SAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1513aed8e389SAndrew Gallatin 	return;
1514aed8e389SAndrew Gallatin 
1515aed8e389SAndrew Gallatin drop:
1516aed8e389SAndrew Gallatin 	m_freem(m);
1517aed8e389SAndrew Gallatin 	sc->ifp->if_oerrors++;
1518aed8e389SAndrew Gallatin 	if (!once) {
1519aed8e389SAndrew Gallatin 		printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n");
1520aed8e389SAndrew Gallatin 		printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list);
1521aed8e389SAndrew Gallatin 		once = 1;
1522aed8e389SAndrew Gallatin 	}
1523aed8e389SAndrew Gallatin 	return;
1524aed8e389SAndrew Gallatin 
1525aed8e389SAndrew Gallatin }
1526aed8e389SAndrew Gallatin 
1527aed8e389SAndrew Gallatin static void
15286d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m)
1529b2fc195eSAndrew Gallatin {
1530b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1531b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1532b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1533b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
15346d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1535b2fc195eSAndrew Gallatin 	struct ether_header *eh;
1536b2fc195eSAndrew Gallatin 	struct ip *ip;
1537aed8e389SAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
1538aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1539aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1540b2fc195eSAndrew Gallatin 
1541b2fc195eSAndrew Gallatin 
1542b2fc195eSAndrew Gallatin 
1543b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1544b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1545b2fc195eSAndrew Gallatin 
1546b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1547b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1548b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1549aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1550b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1551b2fc195eSAndrew Gallatin 	if (err == EFBIG) {
1552b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1553b2fc195eSAndrew Gallatin 		   to defrag */
1554b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1555b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1556b2fc195eSAndrew Gallatin 			goto drop;
1557b2fc195eSAndrew Gallatin 		}
1558b2fc195eSAndrew Gallatin 		m = m_tmp;
1559b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1560b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1561aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1562b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1563b2fc195eSAndrew Gallatin 	}
1564b2fc195eSAndrew Gallatin 	if (err != 0) {
1565aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1566aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1567b2fc195eSAndrew Gallatin 		goto drop;
1568b2fc195eSAndrew Gallatin 	}
1569b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1570b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
15715e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1572b2fc195eSAndrew Gallatin 
1573aed8e389SAndrew Gallatin 
1574aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1575aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
1576aed8e389SAndrew Gallatin 		mxge_encap_tso(sc, m, cnt);
1577aed8e389SAndrew Gallatin 		return;
1578aed8e389SAndrew Gallatin 	}
1579aed8e389SAndrew Gallatin 
1580b2fc195eSAndrew Gallatin 	req = tx->req_list;
1581b2fc195eSAndrew Gallatin 	cksum_offset = 0;
15825e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
15835e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1584b2fc195eSAndrew Gallatin 
1585b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1586b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1587aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
1588aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
1589aed8e389SAndrew Gallatin 		if (__predict_false(m->m_len < sizeof (*eh)
1590aed8e389SAndrew Gallatin 				    + sizeof (*ip))) {
1591aed8e389SAndrew Gallatin 			m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1592aed8e389SAndrew Gallatin 				   sc->scratch);
1593aed8e389SAndrew Gallatin 			eh = (struct ether_header *)sc->scratch;
1594aed8e389SAndrew Gallatin 		} else {
1595b2fc195eSAndrew Gallatin 			eh = mtod(m, struct ether_header *);
1596aed8e389SAndrew Gallatin 		}
1597b2fc195eSAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1598b2fc195eSAndrew Gallatin 		cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1599b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
16005e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1601b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
16025e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1603aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
1604aed8e389SAndrew Gallatin 	} else {
1605aed8e389SAndrew Gallatin 		odd_flag = 0;
1606b2fc195eSAndrew Gallatin 	}
16075e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
16085e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
1609b2fc195eSAndrew Gallatin 
1610b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
1611b2fc195eSAndrew Gallatin 	cum_len = 0;
1612aed8e389SAndrew Gallatin 	seg = tx->seg_list;
16135e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
1614b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1615b2fc195eSAndrew Gallatin 		req->addr_low =
16166d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1617b2fc195eSAndrew Gallatin 		req->addr_high =
16186d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1619b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
1620b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
1621b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
1622b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
1623b2fc195eSAndrew Gallatin 		else
1624b2fc195eSAndrew Gallatin 			cksum_offset = 0;
16255e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
16265e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
16275e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1628aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1629b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
1630b2fc195eSAndrew Gallatin 		seg++;
1631b2fc195eSAndrew Gallatin 		req++;
1632b2fc195eSAndrew Gallatin 		req->flags = 0;
1633b2fc195eSAndrew Gallatin 	}
1634b2fc195eSAndrew Gallatin 	req--;
1635b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
1636b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
1637b2fc195eSAndrew Gallatin 		req++;
1638b2fc195eSAndrew Gallatin 		req->addr_low =
16396d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
1640b2fc195eSAndrew Gallatin 		req->addr_high =
16416d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
1642b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
16435e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
16445e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
16455e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
16465e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1647aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1648b2fc195eSAndrew Gallatin 		cnt++;
1649b2fc195eSAndrew Gallatin 	}
16505e7d8541SAndrew Gallatin 
16515e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
16525e7d8541SAndrew Gallatin #if 0
16535e7d8541SAndrew Gallatin 	/* print what the firmware will see */
16545e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
16555e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
16565e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
16575e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
16585e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
16595e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
16605e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
16615e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
16625e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
16635e7d8541SAndrew Gallatin 	}
16645e7d8541SAndrew Gallatin 	printf("--------------\n");
16655e7d8541SAndrew Gallatin #endif
16665e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1667b2fc195eSAndrew Gallatin 	if (tx->wc_fifo == NULL)
16686d87a65dSAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1669b2fc195eSAndrew Gallatin 	else
16706d87a65dSAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1671b2fc195eSAndrew Gallatin 	return;
1672b2fc195eSAndrew Gallatin 
1673b2fc195eSAndrew Gallatin drop:
1674b2fc195eSAndrew Gallatin 	m_freem(m);
1675b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
1676b2fc195eSAndrew Gallatin 	return;
1677b2fc195eSAndrew Gallatin }
1678b2fc195eSAndrew Gallatin 
1679b2fc195eSAndrew Gallatin 
16806d914a32SAndrew Gallatin 
16816d914a32SAndrew Gallatin 
16826d914a32SAndrew Gallatin static inline void
16836d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc)
1684b2fc195eSAndrew Gallatin {
1685b2fc195eSAndrew Gallatin 	struct mbuf *m;
1686b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1687b2fc195eSAndrew Gallatin 
1688b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
16896d914a32SAndrew Gallatin 	while ((sc->tx.mask - (sc->tx.req - sc->tx.done))
16906d914a32SAndrew Gallatin 	       > MXGE_MAX_SEND_DESC) {
1691b2fc195eSAndrew Gallatin 
16926d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
16936d914a32SAndrew Gallatin 		if (m == NULL) {
16946d914a32SAndrew Gallatin 			return;
16956d914a32SAndrew Gallatin 		}
1696b2fc195eSAndrew Gallatin 		/* let BPF see it */
1697b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
1698b2fc195eSAndrew Gallatin 
1699b2fc195eSAndrew Gallatin 		/* give it to the nic */
17006d87a65dSAndrew Gallatin 		mxge_encap(sc, m);
17016d914a32SAndrew Gallatin 	}
17026d914a32SAndrew Gallatin 	/* ran out of transmit slots */
1703a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
1704b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1705a82c2581SAndrew Gallatin 		sc->tx.stall++;
1706a82c2581SAndrew Gallatin 	}
1707b2fc195eSAndrew Gallatin }
1708b2fc195eSAndrew Gallatin 
1709b2fc195eSAndrew Gallatin static void
17106d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
1711b2fc195eSAndrew Gallatin {
17126d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
1713b2fc195eSAndrew Gallatin 
1714b2fc195eSAndrew Gallatin 
1715a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->tx_mtx);
17166d87a65dSAndrew Gallatin 	mxge_start_locked(sc);
1717a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->tx_mtx);
1718b2fc195eSAndrew Gallatin }
1719b2fc195eSAndrew Gallatin 
17205e7d8541SAndrew Gallatin /*
17215e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
17225e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
17235e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
17245e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
17255e7d8541SAndrew Gallatin  * in a burst
17265e7d8541SAndrew Gallatin  */
17275e7d8541SAndrew Gallatin static inline void
17285e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
17295e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
17305e7d8541SAndrew Gallatin {
17315e7d8541SAndrew Gallatin 	uint32_t low;
17325e7d8541SAndrew Gallatin 
17335e7d8541SAndrew Gallatin 	low = src->addr_low;
17345e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
1735a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
1736a1480dfbSAndrew Gallatin 	mb();
1737a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
17385e7d8541SAndrew Gallatin 	mb();
173940385a5fSAndrew Gallatin 	src->addr_low = low;
17405e7d8541SAndrew Gallatin 	dst->addr_low = low;
17415e7d8541SAndrew Gallatin 	mb();
17425e7d8541SAndrew Gallatin }
17435e7d8541SAndrew Gallatin 
1744b2fc195eSAndrew Gallatin static int
17456d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1746b2fc195eSAndrew Gallatin {
1747b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1748b2fc195eSAndrew Gallatin 	struct mbuf *m;
17496d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_small;
1750b2fc195eSAndrew Gallatin 	int cnt, err;
1751b2fc195eSAndrew Gallatin 
1752b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
1753b2fc195eSAndrew Gallatin 	if (m == NULL) {
1754b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1755b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1756b2fc195eSAndrew Gallatin 		goto done;
1757b2fc195eSAndrew Gallatin 	}
1758b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
1759b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1760b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1761b2fc195eSAndrew Gallatin 	if (err != 0) {
1762b2fc195eSAndrew Gallatin 		m_free(m);
1763b2fc195eSAndrew Gallatin 		goto done;
1764b2fc195eSAndrew Gallatin 	}
1765b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1766b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
17676d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1768b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
17696d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1770b2fc195eSAndrew Gallatin 
1771b2fc195eSAndrew Gallatin done:
1772b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
17735e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
17745e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
17755e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
17765e7d8541SAndrew Gallatin 		else {
1777b2fc195eSAndrew Gallatin 			mb();
17785e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
17795e7d8541SAndrew Gallatin 		}
1780b2fc195eSAndrew Gallatin         }
1781b2fc195eSAndrew Gallatin 	return err;
1782b2fc195eSAndrew Gallatin }
1783b2fc195eSAndrew Gallatin 
1784b2fc195eSAndrew Gallatin static int
17856d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1786b2fc195eSAndrew Gallatin {
1787b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1788b2fc195eSAndrew Gallatin 	struct mbuf *m;
17896d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_big;
1790b2fc195eSAndrew Gallatin 	int cnt, err;
1791b2fc195eSAndrew Gallatin 
1792b2fc195eSAndrew Gallatin 	m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes);
1793b2fc195eSAndrew Gallatin 	if (m == NULL) {
1794b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1795b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1796b2fc195eSAndrew Gallatin 		goto done;
1797b2fc195eSAndrew Gallatin 	}
1798b2fc195eSAndrew Gallatin 	m->m_len = sc->big_bytes;
1799b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1800b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1801b2fc195eSAndrew Gallatin 	if (err != 0) {
1802b2fc195eSAndrew Gallatin 		m_free(m);
1803b2fc195eSAndrew Gallatin 		goto done;
1804b2fc195eSAndrew Gallatin 	}
1805b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1806b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
18076d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1808b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
18096d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1810b2fc195eSAndrew Gallatin 
1811b2fc195eSAndrew Gallatin done:
1812b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
18135e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
18145e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
18155e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
18165e7d8541SAndrew Gallatin 		else {
1817b2fc195eSAndrew Gallatin 			mb();
18185e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
18195e7d8541SAndrew Gallatin 		}
1820b2fc195eSAndrew Gallatin         }
1821b2fc195eSAndrew Gallatin 	return err;
1822b2fc195eSAndrew Gallatin }
1823b2fc195eSAndrew Gallatin 
1824b2fc195eSAndrew Gallatin static inline void
18255e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
18265e7d8541SAndrew Gallatin {
18275e7d8541SAndrew Gallatin 	struct ether_header *eh;
18285e7d8541SAndrew Gallatin 	struct ip *ip;
18295e7d8541SAndrew Gallatin 
18305e7d8541SAndrew Gallatin 	eh = mtod(m, struct ether_header *);
18315e7d8541SAndrew Gallatin 	if (__predict_true(eh->ether_type ==  htons(ETHERTYPE_IP))) {
18325e7d8541SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
18335e7d8541SAndrew Gallatin 		if (__predict_true(ip->ip_p == IPPROTO_TCP ||
18345e7d8541SAndrew Gallatin 				   ip->ip_p == IPPROTO_UDP)) {
18355e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_data = csum;
18365e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_flags = CSUM_DATA_VALID;
18375e7d8541SAndrew Gallatin 		}
18385e7d8541SAndrew Gallatin 	}
18395e7d8541SAndrew Gallatin }
18405e7d8541SAndrew Gallatin 
18415e7d8541SAndrew Gallatin static inline void
18425e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum)
1843b2fc195eSAndrew Gallatin {
1844b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1845b2fc195eSAndrew Gallatin 	struct mbuf *m = 0; 		/* -Wunitialized */
1846b2fc195eSAndrew Gallatin 	struct mbuf *m_prev = 0;	/* -Wunitialized */
1847b2fc195eSAndrew Gallatin 	struct mbuf *m_head = 0;
1848b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
18496d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1850b2fc195eSAndrew Gallatin 	int idx;
1851b2fc195eSAndrew Gallatin 
1852b2fc195eSAndrew Gallatin 
1853b2fc195eSAndrew Gallatin 	rx = &sc->rx_big;
1854b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1855b2fc195eSAndrew Gallatin 	while (len > 0) {
1856b2fc195eSAndrew Gallatin 		idx = rx->cnt & rx->mask;
1857b2fc195eSAndrew Gallatin                 rx->cnt++;
1858b2fc195eSAndrew Gallatin 		/* save a pointer to the received mbuf */
1859b2fc195eSAndrew Gallatin 		m = rx->info[idx].m;
1860b2fc195eSAndrew Gallatin 		/* try to replace the received mbuf */
18616d87a65dSAndrew Gallatin 		if (mxge_get_buf_big(sc, rx->extra_map, idx)) {
1862b2fc195eSAndrew Gallatin 			goto drop;
1863b2fc195eSAndrew Gallatin 		}
1864b2fc195eSAndrew Gallatin 		/* unmap the received buffer */
1865b2fc195eSAndrew Gallatin 		old_map = rx->info[idx].map;
1866b2fc195eSAndrew Gallatin 		bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1867b2fc195eSAndrew Gallatin 		bus_dmamap_unload(rx->dmat, old_map);
1868b2fc195eSAndrew Gallatin 
1869b2fc195eSAndrew Gallatin 		/* swap the bus_dmamap_t's */
1870b2fc195eSAndrew Gallatin 		rx->info[idx].map = rx->extra_map;
1871b2fc195eSAndrew Gallatin 		rx->extra_map = old_map;
1872b2fc195eSAndrew Gallatin 
1873b2fc195eSAndrew Gallatin 		/* chain multiple segments together */
1874b2fc195eSAndrew Gallatin 		if (!m_head) {
1875b2fc195eSAndrew Gallatin 			m_head = m;
1876b2fc195eSAndrew Gallatin 			/* mcp implicitly skips 1st bytes so that
1877b2fc195eSAndrew Gallatin 			 * packet is properly aligned */
18785e7d8541SAndrew Gallatin 			m->m_data += MXGEFW_PAD;
1879b2fc195eSAndrew Gallatin 			m->m_pkthdr.len = len;
18805e7d8541SAndrew Gallatin 			m->m_len = sc->big_bytes - MXGEFW_PAD;
1881b2fc195eSAndrew Gallatin 		} else {
1882b2fc195eSAndrew Gallatin 			m->m_len = sc->big_bytes;
1883b2fc195eSAndrew Gallatin 			m->m_flags &= ~M_PKTHDR;
1884b2fc195eSAndrew Gallatin 			m_prev->m_next = m;
1885b2fc195eSAndrew Gallatin 		}
1886b2fc195eSAndrew Gallatin 		len -= m->m_len;
1887b2fc195eSAndrew Gallatin 		m_prev = m;
1888b2fc195eSAndrew Gallatin 	}
1889b2fc195eSAndrew Gallatin 
1890b2fc195eSAndrew Gallatin 	/* trim trailing garbage from the last mbuf in the chain.  If
1891b2fc195eSAndrew Gallatin 	 * there is any garbage, len will be negative */
1892b2fc195eSAndrew Gallatin 	m->m_len += len;
1893b2fc195eSAndrew Gallatin 
1894b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
18955e7d8541SAndrew Gallatin 	if (sc->csum_flag)
18965e7d8541SAndrew Gallatin 		mxge_rx_csum(m_head, csum);
1897b2fc195eSAndrew Gallatin 
1898b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1899b2fc195eSAndrew Gallatin 	m_head->m_pkthdr.rcvif = ifp;
1900b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1901b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m_head);
1902b2fc195eSAndrew Gallatin 	return;
1903b2fc195eSAndrew Gallatin 
1904b2fc195eSAndrew Gallatin drop:
1905b2fc195eSAndrew Gallatin 	/* drop the frame -- the old mbuf(s) are re-cycled by running
1906b2fc195eSAndrew Gallatin 	   every slot through the allocator */
1907b2fc195eSAndrew Gallatin         if (m_head) {
1908b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1909b2fc195eSAndrew Gallatin                 m_freem(m_head);
1910b2fc195eSAndrew Gallatin         } else {
19115e7d8541SAndrew Gallatin                 len -= (sc->big_bytes + MXGEFW_PAD);
1912b2fc195eSAndrew Gallatin         }
1913b2fc195eSAndrew Gallatin         while ((int)len > 0) {
1914b2fc195eSAndrew Gallatin                 idx = rx->cnt & rx->mask;
1915b2fc195eSAndrew Gallatin                 rx->cnt++;
1916b2fc195eSAndrew Gallatin                 m = rx->info[idx].m;
19176d87a65dSAndrew Gallatin                 if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) {
1918b2fc195eSAndrew Gallatin 			m_freem(m);
1919b2fc195eSAndrew Gallatin 			/* unmap the received buffer */
1920b2fc195eSAndrew Gallatin 			old_map = rx->info[idx].map;
1921b2fc195eSAndrew Gallatin 			bus_dmamap_sync(rx->dmat, old_map,
1922b2fc195eSAndrew Gallatin 					BUS_DMASYNC_POSTREAD);
1923b2fc195eSAndrew Gallatin 			bus_dmamap_unload(rx->dmat, old_map);
1924b2fc195eSAndrew Gallatin 
1925b2fc195eSAndrew Gallatin 			/* swap the bus_dmamap_t's */
1926b2fc195eSAndrew Gallatin 			rx->info[idx].map = rx->extra_map;
1927b2fc195eSAndrew Gallatin 			rx->extra_map = old_map;
1928b2fc195eSAndrew Gallatin 		}
1929b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1930b2fc195eSAndrew Gallatin         }
1931b2fc195eSAndrew Gallatin 
1932b2fc195eSAndrew Gallatin 	ifp->if_ierrors++;
1933b2fc195eSAndrew Gallatin 
1934b2fc195eSAndrew Gallatin }
1935b2fc195eSAndrew Gallatin 
1936b2fc195eSAndrew Gallatin static inline void
19375e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum)
1938b2fc195eSAndrew Gallatin {
1939b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1940b2fc195eSAndrew Gallatin 	struct mbuf *m;
19416d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1942b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
1943b2fc195eSAndrew Gallatin 	int idx;
1944b2fc195eSAndrew Gallatin 
1945b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1946b2fc195eSAndrew Gallatin 	rx = &sc->rx_small;
1947b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
1948b2fc195eSAndrew Gallatin 	rx->cnt++;
1949b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
1950b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
1951b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
19526d87a65dSAndrew Gallatin 	if (mxge_get_buf_small(sc, rx->extra_map, idx)) {
1953b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
1954b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
1955b2fc195eSAndrew Gallatin 		return;
1956b2fc195eSAndrew Gallatin 	}
1957b2fc195eSAndrew Gallatin 
1958b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
1959b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
1960b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1961b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
1962b2fc195eSAndrew Gallatin 
1963b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
1964b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
1965b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
1966b2fc195eSAndrew Gallatin 
1967b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
1968b2fc195eSAndrew Gallatin 	 * aligned */
19695e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
1970b2fc195eSAndrew Gallatin 
1971b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
19725e7d8541SAndrew Gallatin 	if (sc->csum_flag)
19735e7d8541SAndrew Gallatin 		mxge_rx_csum(m, csum);
1974b2fc195eSAndrew Gallatin 
1975b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1976b2fc195eSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
1977b2fc195eSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
1978b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1979b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
1980b2fc195eSAndrew Gallatin }
1981b2fc195eSAndrew Gallatin 
1982b2fc195eSAndrew Gallatin static inline void
19835e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc)
19845e7d8541SAndrew Gallatin {
19855e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
19865e7d8541SAndrew Gallatin 	int limit = 0;
19875e7d8541SAndrew Gallatin 	uint16_t length;
19885e7d8541SAndrew Gallatin 	uint16_t checksum;
19895e7d8541SAndrew Gallatin 
19905e7d8541SAndrew Gallatin 
19915e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
19925e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
19935e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
19945e7d8541SAndrew Gallatin 		checksum = ntohs(rx_done->entry[rx_done->idx].checksum);
1995b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
19965e7d8541SAndrew Gallatin 			mxge_rx_done_small(sc, length, checksum);
19975e7d8541SAndrew Gallatin 		else
19985e7d8541SAndrew Gallatin 			mxge_rx_done_big(sc, length, checksum);
19995e7d8541SAndrew Gallatin 		rx_done->cnt++;
20005e7d8541SAndrew Gallatin 		rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1);
20015e7d8541SAndrew Gallatin 
20025e7d8541SAndrew Gallatin 		/* limit potential for livelock */
20035e7d8541SAndrew Gallatin 		if (__predict_false(++limit > 2 * mxge_max_intr_slots))
20045e7d8541SAndrew Gallatin 			break;
20055e7d8541SAndrew Gallatin 
20065e7d8541SAndrew Gallatin 	}
20075e7d8541SAndrew Gallatin }
20085e7d8541SAndrew Gallatin 
20095e7d8541SAndrew Gallatin 
20105e7d8541SAndrew Gallatin static inline void
20116d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx)
2012b2fc195eSAndrew Gallatin {
2013b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20146d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
2015b2fc195eSAndrew Gallatin 	struct mbuf *m;
2016b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
20175e7d8541SAndrew Gallatin 	int idx, limit;
2018b2fc195eSAndrew Gallatin 
20195e7d8541SAndrew Gallatin 	limit = 0;
2020b2fc195eSAndrew Gallatin 	tx = &sc->tx;
2021b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20225e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2023b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2024b2fc195eSAndrew Gallatin 		tx->done++;
2025b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2026b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2027b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2028b2fc195eSAndrew Gallatin 		if (m != NULL) {
2029b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2030b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2031b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2032b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2033b2fc195eSAndrew Gallatin 			m_freem(m);
2034b2fc195eSAndrew Gallatin 		}
20355e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
20365e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
20375e7d8541SAndrew Gallatin 			tx->pkt_done++;
20385e7d8541SAndrew Gallatin 		}
20395e7d8541SAndrew Gallatin 		/* limit potential for livelock by only handling
20405e7d8541SAndrew Gallatin 		   2 full tx rings per call */
20415e7d8541SAndrew Gallatin 		if (__predict_false(++limit >  2 * tx->mask))
20425e7d8541SAndrew Gallatin 			break;
2043b2fc195eSAndrew Gallatin 	}
2044b2fc195eSAndrew Gallatin 
2045b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2046b2fc195eSAndrew Gallatin            its OK to send packets */
2047b2fc195eSAndrew Gallatin 
2048b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2049b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2050a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->tx_mtx);
2051b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2052a82c2581SAndrew Gallatin 		sc->tx.wake++;
20536d87a65dSAndrew Gallatin 		mxge_start_locked(sc);
2054a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->tx_mtx);
2055b2fc195eSAndrew Gallatin 	}
2056b2fc195eSAndrew Gallatin }
2057b2fc195eSAndrew Gallatin 
2058b2fc195eSAndrew Gallatin static void
20596d87a65dSAndrew Gallatin mxge_intr(void *arg)
2060b2fc195eSAndrew Gallatin {
20616d87a65dSAndrew Gallatin 	mxge_softc_t *sc = arg;
20625e7d8541SAndrew Gallatin 	mcp_irq_data_t *stats = sc->fw_stats;
20635e7d8541SAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
20645e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
20655e7d8541SAndrew Gallatin 	uint32_t send_done_count;
20665e7d8541SAndrew Gallatin 	uint8_t valid;
2067b2fc195eSAndrew Gallatin 
2068b2fc195eSAndrew Gallatin 
20695e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
20705e7d8541SAndrew Gallatin 	if (!stats->valid) {
20715e7d8541SAndrew Gallatin 		return;
2072b2fc195eSAndrew Gallatin 	}
20735e7d8541SAndrew Gallatin 	valid = stats->valid;
2074b2fc195eSAndrew Gallatin 
2075dc8731d4SAndrew Gallatin 	if (!sc->msi_enabled) {
20765e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
20775e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
20785e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
20795e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
20805e7d8541SAndrew Gallatin 			stats->valid = 0;
2081dc8731d4SAndrew Gallatin 	} else {
2082dc8731d4SAndrew Gallatin 		stats->valid = 0;
2083dc8731d4SAndrew Gallatin 	}
2084dc8731d4SAndrew Gallatin 
2085dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
20865e7d8541SAndrew Gallatin 	do {
20875e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
20885e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
20895e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
20905e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
20915e7d8541SAndrew Gallatin 			mxge_tx_done(sc, (int)send_done_count);
20925e7d8541SAndrew Gallatin 			mxge_clean_rx_done(sc);
20935e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2094b2fc195eSAndrew Gallatin 		}
20955e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2096b2fc195eSAndrew Gallatin 
20975e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
20985e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
20995e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2100b2fc195eSAndrew Gallatin 			if (sc->link_state) {
21015e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
21025e7d8541SAndrew Gallatin 				if (mxge_verbose)
21035e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2104b2fc195eSAndrew Gallatin 			} else {
21055e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
21065e7d8541SAndrew Gallatin 				if (mxge_verbose)
21075e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2108b2fc195eSAndrew Gallatin 			}
2109b2fc195eSAndrew Gallatin 		}
2110b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
2111b2fc195eSAndrew Gallatin 		    be32toh(sc->fw_stats->rdma_tags_available)) {
2112b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
2113b2fc195eSAndrew Gallatin 				be32toh(sc->fw_stats->rdma_tags_available);
21145e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
21155e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
21165e7d8541SAndrew Gallatin 		}
21175e7d8541SAndrew Gallatin 		sc->down_cnt += stats->link_down;
2118b2fc195eSAndrew Gallatin 	}
2119b2fc195eSAndrew Gallatin 
21205e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
21215e7d8541SAndrew Gallatin 	if (valid & 0x1)
21225e7d8541SAndrew Gallatin 	    *sc->irq_claim = be32toh(3);
21235e7d8541SAndrew Gallatin 	*(sc->irq_claim + 1) = be32toh(3);
2124b2fc195eSAndrew Gallatin }
2125b2fc195eSAndrew Gallatin 
2126b2fc195eSAndrew Gallatin static void
21276d87a65dSAndrew Gallatin mxge_init(void *arg)
2128b2fc195eSAndrew Gallatin {
2129b2fc195eSAndrew Gallatin }
2130b2fc195eSAndrew Gallatin 
2131b2fc195eSAndrew Gallatin 
2132b2fc195eSAndrew Gallatin 
2133b2fc195eSAndrew Gallatin static void
21346d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2135b2fc195eSAndrew Gallatin {
2136b2fc195eSAndrew Gallatin 	int i;
2137b2fc195eSAndrew Gallatin 
2138b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2139b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2140b2fc195eSAndrew Gallatin 			continue;
2141b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2142b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2143b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2144b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2145b2fc195eSAndrew Gallatin 	}
2146b2fc195eSAndrew Gallatin 
2147b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2148b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2149b2fc195eSAndrew Gallatin 			continue;
2150b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2151b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2152b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2153b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2154b2fc195eSAndrew Gallatin 	}
2155b2fc195eSAndrew Gallatin 
2156b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2157dce01b9bSAndrew Gallatin 		sc->tx.info[i].flag = 0;
2158b2fc195eSAndrew Gallatin 		if (sc->tx.info[i].m == NULL)
2159b2fc195eSAndrew Gallatin 			continue;
2160b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->tx.dmat,
2161b2fc195eSAndrew Gallatin 				  sc->tx.info[i].map);
2162b2fc195eSAndrew Gallatin 		m_freem(sc->tx.info[i].m);
2163b2fc195eSAndrew Gallatin 		sc->tx.info[i].m = NULL;
2164b2fc195eSAndrew Gallatin 	}
2165b2fc195eSAndrew Gallatin }
2166b2fc195eSAndrew Gallatin 
2167b2fc195eSAndrew Gallatin static void
21686d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2169b2fc195eSAndrew Gallatin {
2170b2fc195eSAndrew Gallatin 	int i;
2171b2fc195eSAndrew Gallatin 
2172aed8e389SAndrew Gallatin 	if (sc->tx.req_bytes != NULL)
2173b2fc195eSAndrew Gallatin 		free(sc->tx.req_bytes, M_DEVBUF);
2174aed8e389SAndrew Gallatin 	if (sc->tx.seg_list != NULL)
2175aed8e389SAndrew Gallatin 		free(sc->tx.seg_list, M_DEVBUF);
2176b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow != NULL)
2177b2fc195eSAndrew Gallatin 		free(sc->rx_small.shadow, M_DEVBUF);
2178b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow != NULL)
2179b2fc195eSAndrew Gallatin 		free(sc->rx_big.shadow, M_DEVBUF);
2180b2fc195eSAndrew Gallatin 	if (sc->tx.info != NULL) {
2181c2657176SAndrew Gallatin 		if (sc->tx.dmat != NULL) {
2182b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->tx.mask; i++) {
2183b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->tx.dmat,
2184b2fc195eSAndrew Gallatin 						   sc->tx.info[i].map);
2185b2fc195eSAndrew Gallatin 			}
2186c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->tx.dmat);
2187c2657176SAndrew Gallatin 		}
2188b2fc195eSAndrew Gallatin 		free(sc->tx.info, M_DEVBUF);
2189b2fc195eSAndrew Gallatin 	}
2190b2fc195eSAndrew Gallatin 	if (sc->rx_small.info != NULL) {
2191c2657176SAndrew Gallatin 		if (sc->rx_small.dmat != NULL) {
2192b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_small.mask; i++) {
2193b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_small.dmat,
2194b2fc195eSAndrew Gallatin 						   sc->rx_small.info[i].map);
2195b2fc195eSAndrew Gallatin 			}
2196c2657176SAndrew Gallatin 			bus_dmamap_destroy(sc->rx_small.dmat,
2197c2657176SAndrew Gallatin 					   sc->rx_small.extra_map);
2198c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_small.dmat);
2199c2657176SAndrew Gallatin 		}
2200b2fc195eSAndrew Gallatin 		free(sc->rx_small.info, M_DEVBUF);
2201b2fc195eSAndrew Gallatin 	}
2202b2fc195eSAndrew Gallatin 	if (sc->rx_big.info != NULL) {
2203c2657176SAndrew Gallatin 		if (sc->rx_big.dmat != NULL) {
2204b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_big.mask; i++) {
2205b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_big.dmat,
2206b2fc195eSAndrew Gallatin 						   sc->rx_big.info[i].map);
2207b2fc195eSAndrew Gallatin 			}
2208b2fc195eSAndrew Gallatin 			bus_dmamap_destroy(sc->rx_big.dmat,
2209b2fc195eSAndrew Gallatin 					   sc->rx_big.extra_map);
2210b2fc195eSAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_big.dmat);
2211b2fc195eSAndrew Gallatin 		}
2212c2657176SAndrew Gallatin 		free(sc->rx_big.info, M_DEVBUF);
2213c2657176SAndrew Gallatin 	}
2214c2657176SAndrew Gallatin }
2215b2fc195eSAndrew Gallatin 
2216b2fc195eSAndrew Gallatin static int
22176d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
2218b2fc195eSAndrew Gallatin {
22196d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2220b2fc195eSAndrew Gallatin 	int tx_ring_size, rx_ring_size;
2221b2fc195eSAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
2222b2fc195eSAndrew Gallatin 	int i, err;
2223b2fc195eSAndrew Gallatin 	unsigned long bytes;
2224b2fc195eSAndrew Gallatin 
2225b2fc195eSAndrew Gallatin 	/* get ring sizes */
22265e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
2227b2fc195eSAndrew Gallatin 	tx_ring_size = cmd.data0;
22285e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
2229b2fc195eSAndrew Gallatin 	if (err != 0) {
2230b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Cannot determine ring sizes\n");
2231b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2232b2fc195eSAndrew Gallatin 	}
2233b2fc195eSAndrew Gallatin 
2234b2fc195eSAndrew Gallatin 	rx_ring_size = cmd.data0;
2235b2fc195eSAndrew Gallatin 
2236b2fc195eSAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
2237b2fc195eSAndrew Gallatin 	rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t);
223876bb9c5eSAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
2239a82c2581SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
224076bb9c5eSAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
2241b2fc195eSAndrew Gallatin 
2242b2fc195eSAndrew Gallatin 	sc->tx.mask = tx_ring_entries - 1;
2243b2fc195eSAndrew Gallatin 	sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1;
2244b2fc195eSAndrew Gallatin 
2245b2fc195eSAndrew Gallatin 	err = ENOMEM;
2246b2fc195eSAndrew Gallatin 
2247b2fc195eSAndrew Gallatin 	/* allocate the tx request copy block */
2248b2fc195eSAndrew Gallatin 	bytes = 8 +
22495e7d8541SAndrew Gallatin 		sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4);
2250b2fc195eSAndrew Gallatin 	sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
2251b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes == NULL)
2252b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2253b2fc195eSAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
2254b2fc195eSAndrew Gallatin 	sc->tx.req_list = (mcp_kreq_ether_send_t *)
2255b2fc195eSAndrew Gallatin 		((unsigned long)(sc->tx.req_bytes + 7) & ~7UL);
2256b2fc195eSAndrew Gallatin 
2257aed8e389SAndrew Gallatin 	/* allocate the tx busdma segment list */
2258aed8e389SAndrew Gallatin 	bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC;
2259aed8e389SAndrew Gallatin 	sc->tx.seg_list = (bus_dma_segment_t *)
2260aed8e389SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
2261aed8e389SAndrew Gallatin 	if (sc->tx.seg_list == NULL)
2262aed8e389SAndrew Gallatin 		goto abort_with_alloc;
2263aed8e389SAndrew Gallatin 
2264b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
2265b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow);
2266b2fc195eSAndrew Gallatin 	sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2267b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow == NULL)
2268b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2269b2fc195eSAndrew Gallatin 
2270b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow);
2271b2fc195eSAndrew Gallatin 	sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2272b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow == NULL)
2273b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2274b2fc195eSAndrew Gallatin 
2275b2fc195eSAndrew Gallatin 	/* allocate the host info rings */
2276b2fc195eSAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*sc->tx.info);
2277b2fc195eSAndrew Gallatin 	sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2278b2fc195eSAndrew Gallatin 	if (sc->tx.info == NULL)
2279b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2280b2fc195eSAndrew Gallatin 
2281b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.info);
2282b2fc195eSAndrew Gallatin 	sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2283b2fc195eSAndrew Gallatin 	if (sc->rx_small.info == NULL)
2284b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2285b2fc195eSAndrew Gallatin 
2286b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.info);
2287b2fc195eSAndrew Gallatin 	sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2288b2fc195eSAndrew Gallatin 	if (sc->rx_big.info == NULL)
2289b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2290b2fc195eSAndrew Gallatin 
2291b2fc195eSAndrew Gallatin 	/* allocate the busdma resources */
2292b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2293b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2294b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* boundary */
2295b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2296b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2297b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2298aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
2299aed8e389SAndrew Gallatin 				 MXGE_MAX_SEND_DESC/2,	/* num segs */
2300b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* maxsegsize */
2301b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2302b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2303b2fc195eSAndrew Gallatin 				 &sc->tx.dmat);		/* tag */
2304b2fc195eSAndrew Gallatin 
2305b2fc195eSAndrew Gallatin 	if (err != 0) {
2306b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
2307b2fc195eSAndrew Gallatin 			      err);
2308b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2309b2fc195eSAndrew Gallatin 	}
2310b2fc195eSAndrew Gallatin 
2311b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2312b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2313b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2314b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2315b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2316b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2317b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2318b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2319b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2320b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2321b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2322b2fc195eSAndrew Gallatin 				 &sc->rx_small.dmat);	/* tag */
2323b2fc195eSAndrew Gallatin 	if (err != 0) {
2324b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2325b2fc195eSAndrew Gallatin 			      err);
2326b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2327b2fc195eSAndrew Gallatin 	}
2328b2fc195eSAndrew Gallatin 
2329b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2330b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2331b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2332b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2333b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2334b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2335b2fc195eSAndrew Gallatin 				 4096,			/* maxsize */
2336b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2337b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2338b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2339b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2340b2fc195eSAndrew Gallatin 				 &sc->rx_big.dmat);	/* tag */
2341b2fc195eSAndrew Gallatin 	if (err != 0) {
2342b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2343b2fc195eSAndrew Gallatin 			      err);
2344b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2345b2fc195eSAndrew Gallatin 	}
2346b2fc195eSAndrew Gallatin 
2347b2fc195eSAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
2348b2fc195eSAndrew Gallatin 	   in each ring */
2349b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2350b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->tx.dmat, 0,
2351b2fc195eSAndrew Gallatin 					&sc->tx.info[i].map);
2352b2fc195eSAndrew Gallatin 		if (err != 0) {
2353b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
2354b2fc195eSAndrew Gallatin 			      err);
2355b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2356b2fc195eSAndrew Gallatin 		}
2357b2fc195eSAndrew Gallatin 	}
2358b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2359b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_small.dmat, 0,
2360b2fc195eSAndrew Gallatin 					&sc->rx_small.info[i].map);
2361b2fc195eSAndrew Gallatin 		if (err != 0) {
2362b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2363b2fc195eSAndrew Gallatin 				      err);
2364b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2365b2fc195eSAndrew Gallatin 		}
2366b2fc195eSAndrew Gallatin 	}
2367b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_small.dmat, 0,
2368b2fc195eSAndrew Gallatin 				&sc->rx_small.extra_map);
2369b2fc195eSAndrew Gallatin 	if (err != 0) {
2370b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2371b2fc195eSAndrew Gallatin 			      err);
2372b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2373b2fc195eSAndrew Gallatin 	}
2374b2fc195eSAndrew Gallatin 
2375b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2376b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_big.dmat, 0,
2377b2fc195eSAndrew Gallatin 					&sc->rx_big.info[i].map);
2378b2fc195eSAndrew Gallatin 		if (err != 0) {
2379b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2380b2fc195eSAndrew Gallatin 			      err);
2381b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2382b2fc195eSAndrew Gallatin 		}
2383b2fc195eSAndrew Gallatin 	}
2384b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_big.dmat, 0,
2385b2fc195eSAndrew Gallatin 				&sc->rx_big.extra_map);
2386b2fc195eSAndrew Gallatin 	if (err != 0) {
2387b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2388b2fc195eSAndrew Gallatin 			      err);
2389b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2390b2fc195eSAndrew Gallatin 	}
2391b2fc195eSAndrew Gallatin 	return 0;
2392b2fc195eSAndrew Gallatin 
2393b2fc195eSAndrew Gallatin abort_with_alloc:
23946d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2395b2fc195eSAndrew Gallatin 
2396b2fc195eSAndrew Gallatin abort_with_nothing:
2397b2fc195eSAndrew Gallatin 	return err;
2398b2fc195eSAndrew Gallatin }
2399b2fc195eSAndrew Gallatin 
2400b2fc195eSAndrew Gallatin static int
24016d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc)
2402b2fc195eSAndrew Gallatin {
24036d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2404b2fc195eSAndrew Gallatin 	int i, err;
2405b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
24060fa7f681SAndrew Gallatin 	bus_addr_t bus;
2407b2fc195eSAndrew Gallatin 
2408b2fc195eSAndrew Gallatin 
24097d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
24107d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
24117d542e2dSAndrew Gallatin 
24126d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2413b2fc195eSAndrew Gallatin 	if (err != 0) {
2414b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
2415b2fc195eSAndrew Gallatin 		return EIO;
2416b2fc195eSAndrew Gallatin 	}
2417a98d6cd7SAndrew Gallatin 	bzero(sc->rx_done.entry,
2418a98d6cd7SAndrew Gallatin 	      mxge_max_intr_slots * sizeof(*sc->rx_done.entry));
2419b2fc195eSAndrew Gallatin 
2420b2fc195eSAndrew Gallatin 	if (MCLBYTES >=
24215e7d8541SAndrew Gallatin 	    sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD)
2422b2fc195eSAndrew Gallatin 		sc->big_bytes = MCLBYTES;
2423b2fc195eSAndrew Gallatin 	else
2424b2fc195eSAndrew Gallatin 		sc->big_bytes = MJUMPAGESIZE;
2425b2fc195eSAndrew Gallatin 
2426b2fc195eSAndrew Gallatin 
2427b2fc195eSAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
2428b2fc195eSAndrew Gallatin 
24295e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
2430b2fc195eSAndrew Gallatin 	sc->tx.lanai =
2431b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
24326d87a65dSAndrew Gallatin 	err |= mxge_send_cmd(sc,
24335e7d8541SAndrew Gallatin 				 MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
2434b2fc195eSAndrew Gallatin 	sc->rx_small.lanai =
2435b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
24365e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
2437b2fc195eSAndrew Gallatin 	sc->rx_big.lanai =
2438b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
2439b2fc195eSAndrew Gallatin 
2440b2fc195eSAndrew Gallatin 	if (err != 0) {
2441b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
2442b2fc195eSAndrew Gallatin 			      "failed to get ring sizes or locations\n");
2443a98d6cd7SAndrew Gallatin 		return EIO;
2444b2fc195eSAndrew Gallatin 	}
2445b2fc195eSAndrew Gallatin 
2446b2fc195eSAndrew Gallatin 	if (sc->wc) {
24470fa7f681SAndrew Gallatin 		sc->tx.wc_fifo = sc->sram + MXGEFW_ETH_SEND_4;
24480fa7f681SAndrew Gallatin 		sc->rx_small.wc_fifo = sc->sram + MXGEFW_ETH_RECV_SMALL;
24490fa7f681SAndrew Gallatin 		sc->rx_big.wc_fifo = sc->sram + MXGEFW_ETH_RECV_BIG;
2450b2fc195eSAndrew Gallatin 	} else {
2451b2fc195eSAndrew Gallatin 		sc->tx.wc_fifo = 0;
2452b2fc195eSAndrew Gallatin 		sc->rx_small.wc_fifo = 0;
2453b2fc195eSAndrew Gallatin 		sc->rx_big.wc_fifo = 0;
2454b2fc195eSAndrew Gallatin 	}
2455b2fc195eSAndrew Gallatin 
2456b2fc195eSAndrew Gallatin 
2457b2fc195eSAndrew Gallatin 	/* stock receive rings */
2458b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2459b2fc195eSAndrew Gallatin 		map = sc->rx_small.info[i].map;
24606d87a65dSAndrew Gallatin 		err = mxge_get_buf_small(sc, map, i);
2461b2fc195eSAndrew Gallatin 		if (err) {
2462b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
2463b2fc195eSAndrew Gallatin 				      i, sc->rx_small.mask + 1);
2464b2fc195eSAndrew Gallatin 			goto abort;
2465b2fc195eSAndrew Gallatin 		}
2466b2fc195eSAndrew Gallatin 	}
2467b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2468b2fc195eSAndrew Gallatin 		map = sc->rx_big.info[i].map;
24696d87a65dSAndrew Gallatin 		err = mxge_get_buf_big(sc, map, i);
2470b2fc195eSAndrew Gallatin 		if (err) {
2471b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
2472b2fc195eSAndrew Gallatin 				      i, sc->rx_big.mask + 1);
2473b2fc195eSAndrew Gallatin 			goto abort;
2474b2fc195eSAndrew Gallatin 		}
2475b2fc195eSAndrew Gallatin 	}
2476b2fc195eSAndrew Gallatin 
2477b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
2478b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
2479b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
2480b2fc195eSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN;
24815e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
2482b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
24835e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
2484b2fc195eSAndrew Gallatin 			     &cmd);
2485b2fc195eSAndrew Gallatin 	cmd.data0 = sc->big_bytes;
24865e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
24870fa7f681SAndrew Gallatin 
24880fa7f681SAndrew Gallatin 	if (err != 0) {
24890fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
24900fa7f681SAndrew Gallatin 		goto abort;
24910fa7f681SAndrew Gallatin 	}
24920fa7f681SAndrew Gallatin 
2493b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
24946d87a65dSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr);
24956d87a65dSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr);
24960fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
24970fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
24980fa7f681SAndrew Gallatin 
24990fa7f681SAndrew Gallatin 	if (err != 0) {
25000fa7f681SAndrew Gallatin 		bus = sc->fw_stats_dma.bus_addr;
25010fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
25020fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
25030fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
25040fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
25050fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
25060fa7f681SAndrew Gallatin 				    &cmd);
25070fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
25080fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
25090fa7f681SAndrew Gallatin 	} else {
25100fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
25110fa7f681SAndrew Gallatin 	}
2512b2fc195eSAndrew Gallatin 
2513b2fc195eSAndrew Gallatin 	if (err != 0) {
2514b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
2515b2fc195eSAndrew Gallatin 		goto abort;
2516b2fc195eSAndrew Gallatin 	}
2517b2fc195eSAndrew Gallatin 
2518b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
25195e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
2520b2fc195eSAndrew Gallatin 	if (err) {
2521b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
2522b2fc195eSAndrew Gallatin 		goto abort;
2523b2fc195eSAndrew Gallatin 	}
2524b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
2525b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2526b2fc195eSAndrew Gallatin 
2527b2fc195eSAndrew Gallatin 	return 0;
2528b2fc195eSAndrew Gallatin 
2529b2fc195eSAndrew Gallatin 
2530b2fc195eSAndrew Gallatin abort:
25316d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2532a98d6cd7SAndrew Gallatin 
2533b2fc195eSAndrew Gallatin 	return err;
2534b2fc195eSAndrew Gallatin }
2535b2fc195eSAndrew Gallatin 
2536b2fc195eSAndrew Gallatin static int
25376d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
2538b2fc195eSAndrew Gallatin {
25396d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2540b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
2541b2fc195eSAndrew Gallatin 
2542b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2543b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
2544b2fc195eSAndrew Gallatin 	mb();
25455e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
2546b2fc195eSAndrew Gallatin 	if (err) {
2547b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
2548b2fc195eSAndrew Gallatin 	}
2549b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2550b2fc195eSAndrew Gallatin 		/* wait for down irq */
2551dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
2552b2fc195eSAndrew Gallatin 	}
2553b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2554b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
2555b2fc195eSAndrew Gallatin 	}
2556a98d6cd7SAndrew Gallatin 
25576d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2558a98d6cd7SAndrew Gallatin 
2559b2fc195eSAndrew Gallatin 	return 0;
2560b2fc195eSAndrew Gallatin }
2561b2fc195eSAndrew Gallatin 
2562dce01b9bSAndrew Gallatin static void
2563dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
2564dce01b9bSAndrew Gallatin {
2565dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2566dce01b9bSAndrew Gallatin 	int reg;
2567dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
2568dce01b9bSAndrew Gallatin 
2569dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
2570dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
2571dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
2572dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
2573dce01b9bSAndrew Gallatin 
2574dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
2575dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
2576dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
2577dce01b9bSAndrew Gallatin 	}
2578dce01b9bSAndrew Gallatin 
2579dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
2580dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
2581dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2582dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
2583dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2584dce01b9bSAndrew Gallatin }
2585dce01b9bSAndrew Gallatin 
2586dce01b9bSAndrew Gallatin static uint32_t
2587dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
2588dce01b9bSAndrew Gallatin {
2589dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2590dce01b9bSAndrew Gallatin 	uint32_t vs;
2591dce01b9bSAndrew Gallatin 
2592dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
2593dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
2594dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
2595dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
2596dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
2597dce01b9bSAndrew Gallatin 	}
2598dce01b9bSAndrew Gallatin 	/* enable read32 mode */
2599dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
2600dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
2601dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
2602dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
2603dce01b9bSAndrew Gallatin }
2604dce01b9bSAndrew Gallatin 
2605dce01b9bSAndrew Gallatin static void
2606dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
2607dce01b9bSAndrew Gallatin {
2608dce01b9bSAndrew Gallatin 	int err;
2609dce01b9bSAndrew Gallatin 	uint32_t reboot;
2610dce01b9bSAndrew Gallatin 	uint16_t cmd;
2611dce01b9bSAndrew Gallatin 
2612dce01b9bSAndrew Gallatin 	err = ENXIO;
2613dce01b9bSAndrew Gallatin 
2614dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
2615dce01b9bSAndrew Gallatin 
2616dce01b9bSAndrew Gallatin 	/*
2617dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
2618dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
2619dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
2620dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
2621dce01b9bSAndrew Gallatin 	 * again
2622dce01b9bSAndrew Gallatin 	 */
2623dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2624dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
2625dce01b9bSAndrew Gallatin 		/*
2626dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
2627dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
2628dce01b9bSAndrew Gallatin 		 * back, then give up
2629dce01b9bSAndrew Gallatin 		 */
2630dce01b9bSAndrew Gallatin 		DELAY(1000*100);
2631dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2632dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
2633dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
2634dce01b9bSAndrew Gallatin 			goto abort;
2635dce01b9bSAndrew Gallatin 		}
2636dce01b9bSAndrew Gallatin 	}
2637dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
2638dce01b9bSAndrew Gallatin 		/* print the reboot status */
2639dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
2640dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
2641dce01b9bSAndrew Gallatin 			      reboot);
2642dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
2643dce01b9bSAndrew Gallatin 
2644dce01b9bSAndrew Gallatin 		/* XXXX waiting for pci_cfg_restore() to be exported */
2645dce01b9bSAndrew Gallatin 		goto abort; /* just abort for now */
2646dce01b9bSAndrew Gallatin 
2647dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
2648dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
2649dce01b9bSAndrew Gallatin 	} else {
2650dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC did not reboot, ring state:\n");
2651dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "tx.req=%d tx.done=%d\n",
2652dce01b9bSAndrew Gallatin 			      sc->tx.req, sc->tx.done);
2653dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
2654dce01b9bSAndrew Gallatin 			      sc->tx.pkt_done,
2655dce01b9bSAndrew Gallatin 			      be32toh(sc->fw_stats->send_done_count));
2656dce01b9bSAndrew Gallatin 	}
2657dce01b9bSAndrew Gallatin 
2658dce01b9bSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
2659dce01b9bSAndrew Gallatin 		mxge_close(sc);
2660dce01b9bSAndrew Gallatin 		err = mxge_open(sc);
2661dce01b9bSAndrew Gallatin 	}
2662dce01b9bSAndrew Gallatin 
2663dce01b9bSAndrew Gallatin abort:
2664dce01b9bSAndrew Gallatin 	/*
2665dce01b9bSAndrew Gallatin 	 * stop the watchdog if the nic is dead, to avoid spamming the
2666dce01b9bSAndrew Gallatin 	 * console
2667dce01b9bSAndrew Gallatin 	 */
2668dce01b9bSAndrew Gallatin 	if (err != 0) {
2669dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
2670dce01b9bSAndrew Gallatin 	}
2671dce01b9bSAndrew Gallatin }
2672dce01b9bSAndrew Gallatin 
2673dce01b9bSAndrew Gallatin static void
2674dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
2675dce01b9bSAndrew Gallatin {
2676dce01b9bSAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
2677dce01b9bSAndrew Gallatin 
2678dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
2679dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
2680dce01b9bSAndrew Gallatin 	if (tx->req != tx->done &&
2681dce01b9bSAndrew Gallatin 	    tx->watchdog_req != tx->watchdog_done &&
2682dce01b9bSAndrew Gallatin 	    tx->done == tx->watchdog_done)
2683dce01b9bSAndrew Gallatin 		mxge_watchdog_reset(sc);
2684dce01b9bSAndrew Gallatin 
2685dce01b9bSAndrew Gallatin 	tx->watchdog_req = tx->req;
2686dce01b9bSAndrew Gallatin 	tx->watchdog_done = tx->done;
2687dce01b9bSAndrew Gallatin }
2688dce01b9bSAndrew Gallatin 
2689dce01b9bSAndrew Gallatin static void
2690dce01b9bSAndrew Gallatin mxge_tick(void *arg)
2691dce01b9bSAndrew Gallatin {
2692dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
2693dce01b9bSAndrew Gallatin 
2694dce01b9bSAndrew Gallatin 
2695dce01b9bSAndrew Gallatin 	/* Synchronize with possible callout reset/stop. */
2696dce01b9bSAndrew Gallatin 	if (callout_pending(&sc->co_hdl) ||
2697dce01b9bSAndrew Gallatin 	    !callout_active(&sc->co_hdl)) {
2698dce01b9bSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2699dce01b9bSAndrew Gallatin 		return;
2700dce01b9bSAndrew Gallatin 	}
2701dce01b9bSAndrew Gallatin 
2702dce01b9bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
2703dce01b9bSAndrew Gallatin 	mxge_watchdog(sc);
2704dce01b9bSAndrew Gallatin }
2705b2fc195eSAndrew Gallatin 
2706b2fc195eSAndrew Gallatin static int
27076d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
2708b2fc195eSAndrew Gallatin {
2709b2fc195eSAndrew Gallatin 	return EINVAL;
2710b2fc195eSAndrew Gallatin }
2711b2fc195eSAndrew Gallatin 
2712b2fc195eSAndrew Gallatin static int
27136d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
2714b2fc195eSAndrew Gallatin {
2715b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
2716b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
2717b2fc195eSAndrew Gallatin 	int err = 0;
2718b2fc195eSAndrew Gallatin 
2719b2fc195eSAndrew Gallatin 
2720b2fc195eSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN;
27216d87a65dSAndrew Gallatin 	if ((real_mtu > MXGE_MAX_ETHER_MTU) ||
2722b2fc195eSAndrew Gallatin 	    real_mtu < 60)
2723b2fc195eSAndrew Gallatin 		return EINVAL;
2724a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
2725b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
2726b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
2727b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
2728dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
27296d87a65dSAndrew Gallatin 		mxge_close(sc);
27306d87a65dSAndrew Gallatin 		err = mxge_open(sc);
2731b2fc195eSAndrew Gallatin 		if (err != 0) {
2732b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
27336d87a65dSAndrew Gallatin 			mxge_close(sc);
27346d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
2735b2fc195eSAndrew Gallatin 		}
2736dce01b9bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
2737b2fc195eSAndrew Gallatin 	}
2738a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
2739b2fc195eSAndrew Gallatin 	return err;
2740b2fc195eSAndrew Gallatin }
2741b2fc195eSAndrew Gallatin 
2742b2fc195eSAndrew Gallatin static void
27436d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
2744b2fc195eSAndrew Gallatin {
27456d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2746b2fc195eSAndrew Gallatin 
2747b2fc195eSAndrew Gallatin 
2748b2fc195eSAndrew Gallatin 	if (sc == NULL)
2749b2fc195eSAndrew Gallatin 		return;
2750b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
2751b2fc195eSAndrew Gallatin 	ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0;
2752b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
2753b2fc195eSAndrew Gallatin 	ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0;
2754b2fc195eSAndrew Gallatin }
2755b2fc195eSAndrew Gallatin 
2756b2fc195eSAndrew Gallatin static int
27576d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
2758b2fc195eSAndrew Gallatin {
27596d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2760b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
2761b2fc195eSAndrew Gallatin 	int err, mask;
2762b2fc195eSAndrew Gallatin 
2763b2fc195eSAndrew Gallatin 	err = 0;
2764b2fc195eSAndrew Gallatin 	switch (command) {
2765b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
2766b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
2767b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
2768b2fc195eSAndrew Gallatin 		break;
2769b2fc195eSAndrew Gallatin 
2770b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
27716d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
2772b2fc195eSAndrew Gallatin 		break;
2773b2fc195eSAndrew Gallatin 
2774b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
2775a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
2776b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
2777dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
27786d87a65dSAndrew Gallatin 				err = mxge_open(sc);
2779dce01b9bSAndrew Gallatin 				callout_reset(&sc->co_hdl, mxge_ticks,
2780dce01b9bSAndrew Gallatin 					      mxge_tick, sc);
2781dce01b9bSAndrew Gallatin 			} else {
27820fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
27830fa7f681SAndrew Gallatin 				   flag chages */
27840fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
27850fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
27860fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
27870fa7f681SAndrew Gallatin 			}
2788b2fc195eSAndrew Gallatin 		} else {
2789dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
27906d87a65dSAndrew Gallatin 				mxge_close(sc);
2791dce01b9bSAndrew Gallatin 				callout_stop(&sc->co_hdl);
2792dce01b9bSAndrew Gallatin 			}
2793b2fc195eSAndrew Gallatin 		}
2794a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2795b2fc195eSAndrew Gallatin 		break;
2796b2fc195eSAndrew Gallatin 
2797b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
2798b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
2799a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
28000fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
2801a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2802b2fc195eSAndrew Gallatin 		break;
2803b2fc195eSAndrew Gallatin 
2804b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
2805a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
2806b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
2807b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
2808b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
2809aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
2810aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
2811aed8e389SAndrew Gallatin 						      | CSUM_TSO);
2812b2fc195eSAndrew Gallatin 			} else {
2813b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
2814b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
2815b2fc195eSAndrew Gallatin 			}
2816b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
2817b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
2818b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
28195e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
2820b2fc195eSAndrew Gallatin 			} else {
2821b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
28225e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
2823b2fc195eSAndrew Gallatin 			}
2824b2fc195eSAndrew Gallatin 		}
2825aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
2826aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
2827aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
2828aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
2829aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
2830aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
2831aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
2832aed8e389SAndrew Gallatin 			} else {
2833aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
2834aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
2835aed8e389SAndrew Gallatin 				err = EINVAL;
2836aed8e389SAndrew Gallatin 			}
2837aed8e389SAndrew Gallatin 		}
2838a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2839b2fc195eSAndrew Gallatin 		break;
2840b2fc195eSAndrew Gallatin 
2841b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
2842b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
2843b2fc195eSAndrew Gallatin 				    &sc->media, command);
2844b2fc195eSAndrew Gallatin                 break;
2845b2fc195eSAndrew Gallatin 
2846b2fc195eSAndrew Gallatin 	default:
2847b2fc195eSAndrew Gallatin 		err = ENOTTY;
2848b2fc195eSAndrew Gallatin         }
2849b2fc195eSAndrew Gallatin 	return err;
2850b2fc195eSAndrew Gallatin }
2851b2fc195eSAndrew Gallatin 
2852b2fc195eSAndrew Gallatin static void
28536d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
2854b2fc195eSAndrew Gallatin {
2855b2fc195eSAndrew Gallatin 
28566d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
28576d87a65dSAndrew Gallatin 			  &mxge_flow_control);
28586d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
28596d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
28606d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
28616d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
2862d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
2863d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
28645e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
28655e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
28665e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
28675e7d8541SAndrew Gallatin 			  &mxge_verbose);
2868dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
2869b2fc195eSAndrew Gallatin 
28705e7d8541SAndrew Gallatin 	if (bootverbose)
28715e7d8541SAndrew Gallatin 		mxge_verbose = 1;
28726d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
28736d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
2874dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
2875dce01b9bSAndrew Gallatin 		mxge_ticks = hz;
28766d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
2877b2fc195eSAndrew Gallatin }
2878b2fc195eSAndrew Gallatin 
2879b2fc195eSAndrew Gallatin static int
28806d87a65dSAndrew Gallatin mxge_attach(device_t dev)
2881b2fc195eSAndrew Gallatin {
28826d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
2883b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2884b2fc195eSAndrew Gallatin 	size_t bytes;
2885dce01b9bSAndrew Gallatin 	int count, rid, err;
2886b2fc195eSAndrew Gallatin 
2887b2fc195eSAndrew Gallatin 	sc->dev = dev;
28886d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
2889b2fc195eSAndrew Gallatin 
2890b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
2891b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2892b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2893b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2894b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2895b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2896aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
28975e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
2898b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2899b2fc195eSAndrew Gallatin 				 0,			/* flags */
2900b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2901b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
2902b2fc195eSAndrew Gallatin 
2903b2fc195eSAndrew Gallatin 	if (err != 0) {
2904b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
2905b2fc195eSAndrew Gallatin 			      err);
2906b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2907b2fc195eSAndrew Gallatin 	}
2908b2fc195eSAndrew Gallatin 
2909b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
2910b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
2911b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
2912b2fc195eSAndrew Gallatin 		err = ENOSPC;
2913b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
2914b2fc195eSAndrew Gallatin 	}
2915a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
2916a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
2917a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
2918a98d6cd7SAndrew Gallatin 	snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx",
2919a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
2920a98d6cd7SAndrew Gallatin 	mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF);
2921a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
2922a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
2923a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
2924b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
2925b2fc195eSAndrew Gallatin 
2926dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
2927d91b1b49SAndrew Gallatin 
2928dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
2929b2fc195eSAndrew Gallatin 
2930b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
2931b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
2932b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
2933b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
2934b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
2935b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
2936b2fc195eSAndrew Gallatin 		err = ENXIO;
2937b2fc195eSAndrew Gallatin 		goto abort_with_lock;
2938b2fc195eSAndrew Gallatin 	}
2939b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
2940b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
2941b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
2942b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
2943b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
2944b2fc195eSAndrew Gallatin 		err = ENXIO;
2945b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2946b2fc195eSAndrew Gallatin 	}
2947b2fc195eSAndrew Gallatin 
2948b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
2949b2fc195eSAndrew Gallatin 	   lanai SRAM */
29506d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
2951b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
2952b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
29536d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
2954b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
29556d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
29566d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
2957b2fc195eSAndrew Gallatin 	if (err != 0)
2958b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2959b2fc195eSAndrew Gallatin 
2960b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
29616d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
2962b2fc195eSAndrew Gallatin 
2963b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
29646d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
29656d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
2966b2fc195eSAndrew Gallatin 	if (err != 0)
2967b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2968b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
29696d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
2970b2fc195eSAndrew Gallatin 	if (err != 0)
2971b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
2972b2fc195eSAndrew Gallatin 
29736d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->fw_stats_dma,
2974b2fc195eSAndrew Gallatin 			     sizeof (*sc->fw_stats), 64);
2975b2fc195eSAndrew Gallatin 	if (err != 0)
2976b2fc195eSAndrew Gallatin 		goto abort_with_zeropad_dma;
29775e7d8541SAndrew Gallatin 	sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr;
2978b2fc195eSAndrew Gallatin 
2979a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
2980a98d6cd7SAndrew Gallatin 	if (err != 0)
2981a98d6cd7SAndrew Gallatin 		goto abort_with_fw_stats;
2982b2fc195eSAndrew Gallatin 
2983b2fc195eSAndrew Gallatin 	/* allocate interrupt queues */
29845e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);
29855e7d8541SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096);
2986b2fc195eSAndrew Gallatin 	if (err != 0)
2987a98d6cd7SAndrew Gallatin 		goto abort_with_dmabench;
29885e7d8541SAndrew Gallatin 	sc->rx_done.entry = sc->rx_done.dma.addr;
29895e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
2990dc8731d4SAndrew Gallatin 
2991b2fc195eSAndrew Gallatin 	/* Add our ithread  */
2992dc8731d4SAndrew Gallatin 	count = pci_msi_count(dev);
2993dc8731d4SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(dev, &count) == 0) {
2994dc8731d4SAndrew Gallatin 		rid = 1;
2995dc8731d4SAndrew Gallatin 		sc->msi_enabled = 1;
2996dc8731d4SAndrew Gallatin 	} else {
2997b2fc195eSAndrew Gallatin 		rid = 0;
2998dc8731d4SAndrew Gallatin 	}
2999b2fc195eSAndrew Gallatin 	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0,
3000b2fc195eSAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
3001b2fc195eSAndrew Gallatin 	if (sc->irq_res == NULL) {
3002b2fc195eSAndrew Gallatin 		device_printf(dev, "could not alloc interrupt\n");
30035e7d8541SAndrew Gallatin 		goto abort_with_rx_done;
3004b2fc195eSAndrew Gallatin 	}
3005d91b1b49SAndrew Gallatin 	if (mxge_verbose)
3006dc8731d4SAndrew Gallatin 		device_printf(dev, "using %s irq %ld\n",
3007dc8731d4SAndrew Gallatin 			      sc->msi_enabled ? "MSI" : "INTx",
3008dc8731d4SAndrew Gallatin 			      rman_get_start(sc->irq_res));
3009b2fc195eSAndrew Gallatin 	/* load the firmware */
30106d87a65dSAndrew Gallatin 	mxge_select_firmware(sc);
3011b2fc195eSAndrew Gallatin 
30126d87a65dSAndrew Gallatin 	err = mxge_load_firmware(sc);
3013b2fc195eSAndrew Gallatin 	if (err != 0)
3014b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
30155e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
30166d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
3017b2fc195eSAndrew Gallatin 	if (err != 0)
3018b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
3019b2fc195eSAndrew Gallatin 
3020a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
3021a98d6cd7SAndrew Gallatin 	if (err != 0) {
3022a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
3023a98d6cd7SAndrew Gallatin 		goto abort_with_irq_res;
3024a98d6cd7SAndrew Gallatin 	}
3025a98d6cd7SAndrew Gallatin 
3026a98d6cd7SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
3027a98d6cd7SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
3028ef544f63SPaolo Pisati 			     NULL, mxge_intr, sc, &sc->ih);
3029a98d6cd7SAndrew Gallatin 	if (err != 0) {
3030a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
3031a98d6cd7SAndrew Gallatin 	}
3032b2fc195eSAndrew Gallatin 	/* hook into the network stack */
3033b2fc195eSAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
3034b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
3035a82c2581SAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
3036a82c2581SAndrew Gallatin 		IFCAP_JUMBO_MTU;
3037aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
3038b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
30395e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
30406d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
3041b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
3042b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
30436d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
30446d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
3045b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
3046b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
30476d87a65dSAndrew Gallatin 	ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN;
3048b2fc195eSAndrew Gallatin 
3049b2fc195eSAndrew Gallatin 	/* Initialise the ifmedia structure */
30506d87a65dSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
30516d87a65dSAndrew Gallatin 		     mxge_media_status);
3052b2fc195eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
30536d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
3054b2fc195eSAndrew Gallatin 	return 0;
3055b2fc195eSAndrew Gallatin 
3056a98d6cd7SAndrew Gallatin abort_with_rings:
3057a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3058b2fc195eSAndrew Gallatin abort_with_irq_res:
3059dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3060dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3061dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3062dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
30635e7d8541SAndrew Gallatin abort_with_rx_done:
30645e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
30655e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
3066a98d6cd7SAndrew Gallatin abort_with_dmabench:
3067a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
30685e7d8541SAndrew Gallatin abort_with_fw_stats:
30696d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3070b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
30716d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
3072b2fc195eSAndrew Gallatin abort_with_cmd_dma:
30736d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3074b2fc195eSAndrew Gallatin abort_with_mem_res:
3075b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3076b2fc195eSAndrew Gallatin abort_with_lock:
3077b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3078a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3079a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3080a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3081b2fc195eSAndrew Gallatin 	if_free(ifp);
3082b2fc195eSAndrew Gallatin abort_with_parent_dmat:
3083b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3084b2fc195eSAndrew Gallatin 
3085b2fc195eSAndrew Gallatin abort_with_nothing:
3086b2fc195eSAndrew Gallatin 	return err;
3087b2fc195eSAndrew Gallatin }
3088b2fc195eSAndrew Gallatin 
3089b2fc195eSAndrew Gallatin static int
30906d87a65dSAndrew Gallatin mxge_detach(device_t dev)
3091b2fc195eSAndrew Gallatin {
30926d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
3093b2fc195eSAndrew Gallatin 
3094a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3095b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
30966d87a65dSAndrew Gallatin 		mxge_close(sc);
3097dce01b9bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3098a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3099b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
3100dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
3101091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
3102a98d6cd7SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
3103a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3104dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3105dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3106dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3107dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
3108dc8731d4SAndrew Gallatin 
31095e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
31105e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
31116d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3112a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
31136d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
31146d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3115b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3116b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3117a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3118a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3119a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3120b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
3121b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3122b2fc195eSAndrew Gallatin 	return 0;
3123b2fc195eSAndrew Gallatin }
3124b2fc195eSAndrew Gallatin 
3125b2fc195eSAndrew Gallatin static int
31266d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
3127b2fc195eSAndrew Gallatin {
3128b2fc195eSAndrew Gallatin 	return 0;
3129b2fc195eSAndrew Gallatin }
3130b2fc195eSAndrew Gallatin 
3131b2fc195eSAndrew Gallatin /*
3132b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
3133b2fc195eSAndrew Gallatin 
3134b2fc195eSAndrew Gallatin   Local Variables:
3135b2fc195eSAndrew Gallatin   c-file-style:"linux"
3136b2fc195eSAndrew Gallatin   tab-width:8
3137b2fc195eSAndrew Gallatin   End:
3138b2fc195eSAndrew Gallatin */
3139