xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 4e7f640dfbe1f666c3857534899ee168776fbe67)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
3b2fc195eSAndrew Gallatin Copyright (c) 2006, Myricom Inc.
4b2fc195eSAndrew Gallatin All rights reserved.
5b2fc195eSAndrew Gallatin 
6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
8b2fc195eSAndrew Gallatin 
9b2fc195eSAndrew Gallatin  1. Redistributions of source code must retain the above copyright notice,
10b2fc195eSAndrew Gallatin     this list of conditions and the following disclaimer.
11b2fc195eSAndrew Gallatin 
12b2fc195eSAndrew Gallatin  2. Redistributions in binary form must reproduce the above copyright
13b2fc195eSAndrew Gallatin     notice, this list of conditions and the following disclaimer in the
14b2fc195eSAndrew Gallatin     documentation and/or other materials provided with the distribution.
15b2fc195eSAndrew Gallatin 
16b2fc195eSAndrew Gallatin  3. Neither the name of the Myricom Inc, nor the names of its
17b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
18b2fc195eSAndrew Gallatin     this software without specific prior written permission.
19b2fc195eSAndrew Gallatin 
20b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
31b2fc195eSAndrew Gallatin 
32b2fc195eSAndrew Gallatin ***************************************************************************/
33b2fc195eSAndrew Gallatin 
34b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
35b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
36b2fc195eSAndrew Gallatin 
37b2fc195eSAndrew Gallatin #include <sys/param.h>
38b2fc195eSAndrew Gallatin #include <sys/systm.h>
39b2fc195eSAndrew Gallatin #include <sys/linker.h>
40b2fc195eSAndrew Gallatin #include <sys/firmware.h>
41b2fc195eSAndrew Gallatin #include <sys/endian.h>
42b2fc195eSAndrew Gallatin #include <sys/sockio.h>
43b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
44b2fc195eSAndrew Gallatin #include <sys/malloc.h>
45b2fc195eSAndrew Gallatin #include <sys/kdb.h>
46b2fc195eSAndrew Gallatin #include <sys/kernel.h>
474e7f640dSJohn Baldwin #include <sys/lock.h>
48b2fc195eSAndrew Gallatin #include <sys/module.h>
49b2fc195eSAndrew Gallatin #include <sys/memrange.h>
50b2fc195eSAndrew Gallatin #include <sys/socket.h>
51b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
52b2fc195eSAndrew Gallatin #include <sys/sx.h>
53b2fc195eSAndrew Gallatin 
54b2fc195eSAndrew Gallatin #include <net/if.h>
55b2fc195eSAndrew Gallatin #include <net/if_arp.h>
56b2fc195eSAndrew Gallatin #include <net/ethernet.h>
57b2fc195eSAndrew Gallatin #include <net/if_dl.h>
58b2fc195eSAndrew Gallatin #include <net/if_media.h>
59b2fc195eSAndrew Gallatin 
60b2fc195eSAndrew Gallatin #include <net/bpf.h>
61b2fc195eSAndrew Gallatin 
62b2fc195eSAndrew Gallatin #include <net/if_types.h>
63b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
64b2fc195eSAndrew Gallatin #include <net/zlib.h>
65b2fc195eSAndrew Gallatin 
66b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
67b2fc195eSAndrew Gallatin #include <netinet/in.h>
68b2fc195eSAndrew Gallatin #include <netinet/ip.h>
69aed8e389SAndrew Gallatin #include <netinet/tcp.h>
70b2fc195eSAndrew Gallatin 
71b2fc195eSAndrew Gallatin #include <machine/bus.h>
72b2fc195eSAndrew Gallatin #include <machine/resource.h>
73b2fc195eSAndrew Gallatin #include <sys/bus.h>
74b2fc195eSAndrew Gallatin #include <sys/rman.h>
75b2fc195eSAndrew Gallatin 
76b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
77b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
78b2fc195eSAndrew Gallatin 
79b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
80b2fc195eSAndrew Gallatin #include <vm/pmap.h>
81b2fc195eSAndrew Gallatin 
826d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
836d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
846d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
85b2fc195eSAndrew Gallatin 
86b2fc195eSAndrew Gallatin /* tunable params */
876d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
88d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
895e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024;
906d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
915e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
926d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
935e7d8541SAndrew Gallatin static int mxge_verbose = 0;
94dce01b9bSAndrew Gallatin static int mxge_ticks;
956d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
966d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
97b2fc195eSAndrew Gallatin 
986d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
996d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1006d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1016d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1026d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
103b2fc195eSAndrew Gallatin 
1046d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
105b2fc195eSAndrew Gallatin {
106b2fc195eSAndrew Gallatin   /* Device interface */
1076d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1086d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1096d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1106d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
111b2fc195eSAndrew Gallatin   {0, 0}
112b2fc195eSAndrew Gallatin };
113b2fc195eSAndrew Gallatin 
1146d87a65dSAndrew Gallatin static driver_t mxge_driver =
115b2fc195eSAndrew Gallatin {
1166d87a65dSAndrew Gallatin   "mxge",
1176d87a65dSAndrew Gallatin   mxge_methods,
1186d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
119b2fc195eSAndrew Gallatin };
120b2fc195eSAndrew Gallatin 
1216d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
122b2fc195eSAndrew Gallatin 
123b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1246d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1256d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
126b2fc195eSAndrew Gallatin 
127b2fc195eSAndrew Gallatin static int
1286d87a65dSAndrew Gallatin mxge_probe(device_t dev)
129b2fc195eSAndrew Gallatin {
1306d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1316d87a65dSAndrew Gallatin       (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) {
132b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
133b2fc195eSAndrew Gallatin 	  return 0;
134b2fc195eSAndrew Gallatin   }
135b2fc195eSAndrew Gallatin   return ENXIO;
136b2fc195eSAndrew Gallatin }
137b2fc195eSAndrew Gallatin 
138b2fc195eSAndrew Gallatin static void
1396d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
140b2fc195eSAndrew Gallatin {
141b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
142b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
143b2fc195eSAndrew Gallatin 	vm_offset_t len;
144b2fc195eSAndrew Gallatin 	int err, action;
145b2fc195eSAndrew Gallatin 
146b2fc195eSAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
147b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
148b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
149b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
150b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
151b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1526d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
153b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
154b2fc195eSAndrew Gallatin 	if (err != 0) {
155b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
156b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
157b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
158b2fc195eSAndrew Gallatin 	} else {
159b2fc195eSAndrew Gallatin 		sc->wc = 1;
160b2fc195eSAndrew Gallatin 	}
161b2fc195eSAndrew Gallatin }
162b2fc195eSAndrew Gallatin 
163b2fc195eSAndrew Gallatin 
164b2fc195eSAndrew Gallatin /* callback to get our DMA address */
165b2fc195eSAndrew Gallatin static void
1666d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
167b2fc195eSAndrew Gallatin 			 int error)
168b2fc195eSAndrew Gallatin {
169b2fc195eSAndrew Gallatin 	if (error == 0) {
170b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
171b2fc195eSAndrew Gallatin 	}
172b2fc195eSAndrew Gallatin }
173b2fc195eSAndrew Gallatin 
174b2fc195eSAndrew Gallatin static int
1756d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
176b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
177b2fc195eSAndrew Gallatin {
178b2fc195eSAndrew Gallatin 	int err;
179b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
180b2fc195eSAndrew Gallatin 
181b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
182b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
183b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
184b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
185b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
186b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
187b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
188b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
189b2fc195eSAndrew Gallatin 				 1,			/* num segs */
190b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
191b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
192b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
193b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
194b2fc195eSAndrew Gallatin 	if (err != 0) {
195b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
196b2fc195eSAndrew Gallatin 		return err;
197b2fc195eSAndrew Gallatin 	}
198b2fc195eSAndrew Gallatin 
199b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
200b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
201b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
202b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
203b2fc195eSAndrew Gallatin 	if (err != 0) {
204b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
205b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
206b2fc195eSAndrew Gallatin 	}
207b2fc195eSAndrew Gallatin 
208b2fc195eSAndrew Gallatin 	/* load the memory */
209b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2106d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
211b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
212b2fc195eSAndrew Gallatin 	if (err != 0) {
213b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
214b2fc195eSAndrew Gallatin 		goto abort_with_mem;
215b2fc195eSAndrew Gallatin 	}
216b2fc195eSAndrew Gallatin 	return 0;
217b2fc195eSAndrew Gallatin 
218b2fc195eSAndrew Gallatin abort_with_mem:
219b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
220b2fc195eSAndrew Gallatin abort_with_dmat:
221b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
222b2fc195eSAndrew Gallatin 	return err;
223b2fc195eSAndrew Gallatin }
224b2fc195eSAndrew Gallatin 
225b2fc195eSAndrew Gallatin 
226b2fc195eSAndrew Gallatin static void
2276d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
228b2fc195eSAndrew Gallatin {
229b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
230b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
231b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
232b2fc195eSAndrew Gallatin }
233b2fc195eSAndrew Gallatin 
234b2fc195eSAndrew Gallatin /*
235b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
236b2fc195eSAndrew Gallatin  * SN=x\0
237b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
238b2fc195eSAndrew Gallatin  * PC=text\0
239b2fc195eSAndrew Gallatin  */
240b2fc195eSAndrew Gallatin 
241b2fc195eSAndrew Gallatin static int
2426d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
243b2fc195eSAndrew Gallatin {
2446d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
245b2fc195eSAndrew Gallatin 
246b2fc195eSAndrew Gallatin 	char *ptr, *limit;
247b2fc195eSAndrew Gallatin 	int i, found_mac;
248b2fc195eSAndrew Gallatin 
249b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2506d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
251b2fc195eSAndrew Gallatin 	found_mac = 0;
252b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
253b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2545e7d8541SAndrew Gallatin 			ptr += 1;
255b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
256b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2575e7d8541SAndrew Gallatin 				ptr += 3;
258b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
259b2fc195eSAndrew Gallatin 					goto abort;
260b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
261b2fc195eSAndrew Gallatin 				found_mac = 1;
262b2fc195eSAndrew Gallatin 			}
2635e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2645e7d8541SAndrew Gallatin 			ptr += 3;
2655e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2665e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2675e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2685e7d8541SAndrew Gallatin 			ptr += 3;
2695e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2705e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
271b2fc195eSAndrew Gallatin 		}
2726d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
273b2fc195eSAndrew Gallatin 	}
274b2fc195eSAndrew Gallatin 
275b2fc195eSAndrew Gallatin 	if (found_mac)
276b2fc195eSAndrew Gallatin 		return 0;
277b2fc195eSAndrew Gallatin 
278b2fc195eSAndrew Gallatin  abort:
279b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
280b2fc195eSAndrew Gallatin 
281b2fc195eSAndrew Gallatin 	return ENXIO;
282b2fc195eSAndrew Gallatin }
283b2fc195eSAndrew Gallatin 
284b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
285b2fc195eSAndrew Gallatin static int
2866d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
287b2fc195eSAndrew Gallatin {
288b2fc195eSAndrew Gallatin 	uint32_t val;
289b2fc195eSAndrew Gallatin 	unsigned long off;
290b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
291b2fc195eSAndrew Gallatin 	uint16_t vendor_id, device_id;
292b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
293b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
294b2fc195eSAndrew Gallatin 
295b2fc195eSAndrew Gallatin 	/* XXXX
296b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
297b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
298b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
299b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
300b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
301b2fc195eSAndrew Gallatin 	*/
302b2fc195eSAndrew Gallatin #if 0
303b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
304b2fc195eSAndrew Gallatin 	   config space */
305b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
306b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
307b2fc195eSAndrew Gallatin 		val |= 0x40;
308b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
309b2fc195eSAndrew Gallatin 		return 0;
310b2fc195eSAndrew Gallatin 	}
311b2fc195eSAndrew Gallatin #endif
312b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
313b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
314b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
315b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
316b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
317b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
318b2fc195eSAndrew Gallatin 	 */
319b2fc195eSAndrew Gallatin 
320b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
321b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
322b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
323b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
324b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
325b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
326b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
327b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
328b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
329b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
330b2fc195eSAndrew Gallatin 
331b2fc195eSAndrew Gallatin 	off =  0xe0000000UL
332b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
333b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
334b2fc195eSAndrew Gallatin 						 + 8 * slot);
335b2fc195eSAndrew Gallatin 
336b2fc195eSAndrew Gallatin 	/* map it into the kernel */
337b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
338b2fc195eSAndrew Gallatin 
339b2fc195eSAndrew Gallatin 
340b2fc195eSAndrew Gallatin 	if (va == NULL) {
341b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
342b2fc195eSAndrew Gallatin 		return EIO;
343b2fc195eSAndrew Gallatin 	}
344b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
345b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
346b2fc195eSAndrew Gallatin 
347b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
348b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
349b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
350b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
351b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
352b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
353b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
354b2fc195eSAndrew Gallatin 		return EIO;
355b2fc195eSAndrew Gallatin 	}
356b2fc195eSAndrew Gallatin 
357b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
358b2fc195eSAndrew Gallatin 	val = *ptr32;
359b2fc195eSAndrew Gallatin 
360b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
361b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
362b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
363b2fc195eSAndrew Gallatin 		return EIO;
364b2fc195eSAndrew Gallatin 	}
365b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
366b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3675e7d8541SAndrew Gallatin 	if (mxge_verbose)
368b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
3695e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
3705e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
371b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
372b2fc195eSAndrew Gallatin 	return 0;
373b2fc195eSAndrew Gallatin }
374b2fc195eSAndrew Gallatin #else
375b2fc195eSAndrew Gallatin static int
3766d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
377b2fc195eSAndrew Gallatin {
378b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
379b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
380b2fc195eSAndrew Gallatin 	return ENXIO;
381b2fc195eSAndrew Gallatin }
382b2fc195eSAndrew Gallatin #endif
383b2fc195eSAndrew Gallatin /*
384b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
385b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
386b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
387b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
388b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
389b2fc195eSAndrew Gallatin  *
390b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
391b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
392b2fc195eSAndrew Gallatin  *
393b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
394b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
395b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
396b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
397b2fc195eSAndrew Gallatin  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
398b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
399b2fc195eSAndrew Gallatin  * firmware image, and set tx.boundary to 4KB.
400b2fc195eSAndrew Gallatin  */
401b2fc195eSAndrew Gallatin 
402b2fc195eSAndrew Gallatin static void
4036d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
404b2fc195eSAndrew Gallatin {
405b2fc195eSAndrew Gallatin 	int err, aligned = 0;
406b2fc195eSAndrew Gallatin 	device_t pdev;
407b2fc195eSAndrew Gallatin 	uint16_t pvend, pdid;
408b2fc195eSAndrew Gallatin 
409d91b1b49SAndrew Gallatin 
410d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
411d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
412d91b1b49SAndrew Gallatin 			aligned = 1;
413d91b1b49SAndrew Gallatin 		else
414d91b1b49SAndrew Gallatin 			aligned = 0;
415d91b1b49SAndrew Gallatin 		if (mxge_verbose)
416d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
417d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
418d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
419d91b1b49SAndrew Gallatin 		goto abort;
420d91b1b49SAndrew Gallatin 	}
421d91b1b49SAndrew Gallatin 
422d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
423d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
424d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
425d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
426d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
427d91b1b49SAndrew Gallatin 			      sc->link_width);
428d91b1b49SAndrew Gallatin 		aligned = 1;
429d91b1b49SAndrew Gallatin 		goto abort;
430d91b1b49SAndrew Gallatin 	}
431d91b1b49SAndrew Gallatin 
432b2fc195eSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
433b2fc195eSAndrew Gallatin 	if (pdev == NULL) {
434b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
435b2fc195eSAndrew Gallatin 		goto abort;
436b2fc195eSAndrew Gallatin 	}
437b2fc195eSAndrew Gallatin 	pvend = pci_read_config(pdev, PCIR_VENDOR, 2);
438b2fc195eSAndrew Gallatin 	pdid = pci_read_config(pdev, PCIR_DEVICE, 2);
439b2fc195eSAndrew Gallatin 
440b2fc195eSAndrew Gallatin 	/* see if we can enable ECRC's on an upstream
441b2fc195eSAndrew Gallatin 	   Nvidia bridge */
4426d87a65dSAndrew Gallatin 	if (mxge_nvidia_ecrc_enable &&
443b2fc195eSAndrew Gallatin 	    (pvend == 0x10de && pdid == 0x005d)) {
4446d87a65dSAndrew Gallatin 		err = mxge_enable_nvidia_ecrc(sc, pdev);
445b2fc195eSAndrew Gallatin 		if (err == 0) {
446b2fc195eSAndrew Gallatin 			aligned = 1;
4475e7d8541SAndrew Gallatin 			if (mxge_verbose)
448b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
4495e7d8541SAndrew Gallatin 					      "Assuming aligned completions"
4505e7d8541SAndrew Gallatin 					      " (ECRC)\n");
451b2fc195eSAndrew Gallatin 		}
452b2fc195eSAndrew Gallatin 	}
453b2fc195eSAndrew Gallatin 	/* see if the upstream bridge is known to
454b2fc195eSAndrew Gallatin 	   provided aligned completions */
455b2fc195eSAndrew Gallatin 	if (/* HT2000 */ (pvend == 0x1166 && pdid == 0x0132) ||
4560fa7f681SAndrew Gallatin 	    /* PLX */    (pvend == 0x10b5 && pdid == 0x8532) ||
4570fa7f681SAndrew Gallatin 	    /* Intel */  (pvend == 0x8086 &&
458d91b1b49SAndrew Gallatin 	      /* E5000 NorthBridge*/((pdid >= 0x25f7 && pdid <= 0x25fa) ||
459d91b1b49SAndrew Gallatin 	      /* E5000 SouthBridge*/ (pdid >= 0x3510 && pdid <= 0x351b)))) {
460d91b1b49SAndrew Gallatin 		aligned = 1;
4615e7d8541SAndrew Gallatin 		if (mxge_verbose)
462b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
4635e7d8541SAndrew Gallatin 				      "Assuming aligned completions "
4645e7d8541SAndrew Gallatin 				      "(0x%x:0x%x)\n", pvend, pdid);
465b2fc195eSAndrew Gallatin 	}
466b2fc195eSAndrew Gallatin 
467b2fc195eSAndrew Gallatin abort:
468b2fc195eSAndrew Gallatin 	if (aligned) {
4696d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
470b2fc195eSAndrew Gallatin 		sc->tx.boundary = 4096;
471b2fc195eSAndrew Gallatin 	} else {
4726d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
473b2fc195eSAndrew Gallatin 		sc->tx.boundary = 2048;
474b2fc195eSAndrew Gallatin 	}
475b2fc195eSAndrew Gallatin }
476b2fc195eSAndrew Gallatin 
477b2fc195eSAndrew Gallatin union qualhack
478b2fc195eSAndrew Gallatin {
479b2fc195eSAndrew Gallatin         const char *ro_char;
480b2fc195eSAndrew Gallatin         char *rw_char;
481b2fc195eSAndrew Gallatin };
482b2fc195eSAndrew Gallatin 
4834da0d523SAndrew Gallatin static int
4844da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
4854da0d523SAndrew Gallatin {
486b824b7d8SAndrew Gallatin 
4874da0d523SAndrew Gallatin 
4884da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
4894da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
4904da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
4914da0d523SAndrew Gallatin 		return EIO;
4924da0d523SAndrew Gallatin 	}
4934da0d523SAndrew Gallatin 
4944da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
4954da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
4964da0d523SAndrew Gallatin 	if (mxge_verbose)
4974da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
4984da0d523SAndrew Gallatin 
499b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
500b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
5014da0d523SAndrew Gallatin 
502b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
503b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
5044da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
5054da0d523SAndrew Gallatin 			      sc->fw_version);
5064da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
5074da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
5084da0d523SAndrew Gallatin 		return EINVAL;
5094da0d523SAndrew Gallatin 	}
5104da0d523SAndrew Gallatin 	return 0;
5114da0d523SAndrew Gallatin 
5124da0d523SAndrew Gallatin }
513b2fc195eSAndrew Gallatin 
514b2fc195eSAndrew Gallatin static int
5156d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
516b2fc195eSAndrew Gallatin {
51733d54970SLuigi Rizzo 	const struct firmware *fw;
518b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
519b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
520b2fc195eSAndrew Gallatin 	const char *fw_data;
521b2fc195eSAndrew Gallatin 	union qualhack hack;
522b2fc195eSAndrew Gallatin 	int status;
5234da0d523SAndrew Gallatin 	unsigned int i;
5244da0d523SAndrew Gallatin 	char dummy;
525b2fc195eSAndrew Gallatin 
526b2fc195eSAndrew Gallatin 
527b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
528b2fc195eSAndrew Gallatin 
529b2fc195eSAndrew Gallatin 	if (fw == NULL) {
530b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
531b2fc195eSAndrew Gallatin 			      sc->fw_name);
532b2fc195eSAndrew Gallatin 		return ENOENT;
533b2fc195eSAndrew Gallatin 	}
534b2fc195eSAndrew Gallatin 	if (fw->datasize > *limit ||
535b2fc195eSAndrew Gallatin 	    fw->datasize < MCP_HEADER_PTR_OFFSET + 4) {
536b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n",
537b2fc195eSAndrew Gallatin 			      sc->fw_name, (int)fw->datasize, (int) *limit);
538b2fc195eSAndrew Gallatin 		status = ENOSPC;
539b2fc195eSAndrew Gallatin 		goto abort_with_fw;
540b2fc195eSAndrew Gallatin 	}
541b2fc195eSAndrew Gallatin 	*limit = fw->datasize;
542b2fc195eSAndrew Gallatin 
543b2fc195eSAndrew Gallatin 	/* check id */
544b2fc195eSAndrew Gallatin 	fw_data = (const char *)fw->data;
545b2fc195eSAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
546b2fc195eSAndrew Gallatin 			     (fw_data + MCP_HEADER_PTR_OFFSET));
547b2fc195eSAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) {
548b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
549b2fc195eSAndrew Gallatin 		status = EIO;
550b2fc195eSAndrew Gallatin 		goto abort_with_fw;
551b2fc195eSAndrew Gallatin 	}
552b2fc195eSAndrew Gallatin 	hdr = (const void*)(fw_data + hdr_offset);
553b2fc195eSAndrew Gallatin 
5544da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
5554da0d523SAndrew Gallatin 	if (status != 0)
5564da0d523SAndrew Gallatin 		goto abort_with_fw;
557b2fc195eSAndrew Gallatin 
558b2fc195eSAndrew Gallatin 	hack.ro_char = fw_data;
559b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
5604da0d523SAndrew Gallatin 	for (i = 0; i < *limit; i += 256) {
5614da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
5624da0d523SAndrew Gallatin 			      hack.rw_char + i,
5634da0d523SAndrew Gallatin 			      min(256U, (unsigned)(*limit - i)));
5644da0d523SAndrew Gallatin 		mb();
5654da0d523SAndrew Gallatin 		dummy = *sc->sram;
5664da0d523SAndrew Gallatin 		mb();
5674da0d523SAndrew Gallatin 	}
568b2fc195eSAndrew Gallatin 
569b2fc195eSAndrew Gallatin 	status = 0;
570b2fc195eSAndrew Gallatin abort_with_fw:
571b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
572b2fc195eSAndrew Gallatin 	return status;
573b2fc195eSAndrew Gallatin }
574b2fc195eSAndrew Gallatin 
575b2fc195eSAndrew Gallatin /*
576b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
577b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
578b2fc195eSAndrew Gallatin  */
579b2fc195eSAndrew Gallatin 
580b2fc195eSAndrew Gallatin static void
5816d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
582b2fc195eSAndrew Gallatin {
583b2fc195eSAndrew Gallatin 	char buf_bytes[72];
584b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
585b2fc195eSAndrew Gallatin 	volatile char *submit;
586b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
587b2fc195eSAndrew Gallatin 	int i;
588b2fc195eSAndrew Gallatin 
589b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
590b2fc195eSAndrew Gallatin 
591b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
592b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
593b2fc195eSAndrew Gallatin 	*confirm = 0;
594b2fc195eSAndrew Gallatin 	mb();
595b2fc195eSAndrew Gallatin 
596b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
597b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
598b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
599b2fc195eSAndrew Gallatin 	*/
600b2fc195eSAndrew Gallatin 
6016d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
6026d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
603b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
604b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
605b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
6066d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
6076d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
608b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
609b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
610b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
611b2fc195eSAndrew Gallatin 
612b2fc195eSAndrew Gallatin 
6130fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
614b2fc195eSAndrew Gallatin 
6156d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
616b2fc195eSAndrew Gallatin 	mb();
617b2fc195eSAndrew Gallatin 	DELAY(1000);
618b2fc195eSAndrew Gallatin 	mb();
619b2fc195eSAndrew Gallatin 	i = 0;
620b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
621b2fc195eSAndrew Gallatin 		DELAY(1000);
622b2fc195eSAndrew Gallatin 		i++;
623b2fc195eSAndrew Gallatin 	}
624b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
625b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
626b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
627b2fc195eSAndrew Gallatin 			      *confirm);
628b2fc195eSAndrew Gallatin 	}
629b2fc195eSAndrew Gallatin 	return;
630b2fc195eSAndrew Gallatin }
631b2fc195eSAndrew Gallatin 
632b2fc195eSAndrew Gallatin static int
6336d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
634b2fc195eSAndrew Gallatin {
635b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
636b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
637b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
6380fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
639b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
640b2fc195eSAndrew Gallatin 	int sleep_total = 0;
641b2fc195eSAndrew Gallatin 
642b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
643b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
644b2fc195eSAndrew Gallatin 
645b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
646b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
647b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
648b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
6496d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
6506d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
651b2fc195eSAndrew Gallatin 
652b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
653b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
654a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
655b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
656b2fc195eSAndrew Gallatin 	mb();
6576d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
658b2fc195eSAndrew Gallatin 
6595e7d8541SAndrew Gallatin 	/* wait up to 20ms */
6605e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
661b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
662b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
663b2fc195eSAndrew Gallatin 		mb();
664b2fc195eSAndrew Gallatin 		if (response->result != 0xffffffff) {
665b2fc195eSAndrew Gallatin 			if (response->result == 0) {
666b2fc195eSAndrew Gallatin 				data->data0 = be32toh(response->data);
667a98d6cd7SAndrew Gallatin 				mtx_unlock(&sc->cmd_mtx);
668b2fc195eSAndrew Gallatin 				return 0;
669b2fc195eSAndrew Gallatin 			} else {
670b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
6716d87a65dSAndrew Gallatin 					      "mxge: command %d "
672b2fc195eSAndrew Gallatin 					      "failed, result = %d\n",
673b2fc195eSAndrew Gallatin 					      cmd, be32toh(response->result));
674a98d6cd7SAndrew Gallatin 				mtx_unlock(&sc->cmd_mtx);
675b2fc195eSAndrew Gallatin 				return ENXIO;
676b2fc195eSAndrew Gallatin 			}
677b2fc195eSAndrew Gallatin 		}
6785e7d8541SAndrew Gallatin 		DELAY(1000);
679b2fc195eSAndrew Gallatin 	}
680a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
6816d87a65dSAndrew Gallatin 	device_printf(sc->dev, "mxge: command %d timed out"
682b2fc195eSAndrew Gallatin 		      "result = %d\n",
683b2fc195eSAndrew Gallatin 		      cmd, be32toh(response->result));
684b2fc195eSAndrew Gallatin 	return EAGAIN;
685b2fc195eSAndrew Gallatin }
686b2fc195eSAndrew Gallatin 
6874da0d523SAndrew Gallatin static int
6884da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
6894da0d523SAndrew Gallatin {
6904da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
6914da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
6924da0d523SAndrew Gallatin 	size_t hdr_offset;
6934da0d523SAndrew Gallatin 	int status;
6944da0d523SAndrew Gallatin 
6954da0d523SAndrew Gallatin 	/* find running firmware header */
6964da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
6974da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
6984da0d523SAndrew Gallatin 
6994da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
7004da0d523SAndrew Gallatin 		device_printf(sc->dev,
7014da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
7024da0d523SAndrew Gallatin 			      (int)hdr_offset);
7034da0d523SAndrew Gallatin 		return EIO;
7044da0d523SAndrew Gallatin 	}
7054da0d523SAndrew Gallatin 
7064da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
7074da0d523SAndrew Gallatin 	 * validate firmware */
7084da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
7094da0d523SAndrew Gallatin 	if (hdr == NULL) {
7104da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
7114da0d523SAndrew Gallatin 		return ENOMEM;
7124da0d523SAndrew Gallatin 	}
7134da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
7144da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
7154da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
7164da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7174da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
718b824b7d8SAndrew Gallatin 
719b824b7d8SAndrew Gallatin 	/*
720b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
721b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
722b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
723b824b7d8SAndrew Gallatin 	 */
724b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
725b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
726b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
727b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
728b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
729b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
730b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
731b824b7d8SAndrew Gallatin 	}
732b824b7d8SAndrew Gallatin 
7334da0d523SAndrew Gallatin 	return status;
7344da0d523SAndrew Gallatin }
7354da0d523SAndrew Gallatin 
736b2fc195eSAndrew Gallatin 
737b2fc195eSAndrew Gallatin static int
7386d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc)
739b2fc195eSAndrew Gallatin {
740b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
741b2fc195eSAndrew Gallatin 	volatile char *submit;
742b2fc195eSAndrew Gallatin 	char buf_bytes[72];
743b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
744b2fc195eSAndrew Gallatin 	int status, i;
745b2fc195eSAndrew Gallatin 
746b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
747b2fc195eSAndrew Gallatin 
748b2fc195eSAndrew Gallatin 	size = sc->sram_size;
7496d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
750b2fc195eSAndrew Gallatin 	if (status) {
7514da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
7524da0d523SAndrew Gallatin 		   it is new enough */
7534da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
7544da0d523SAndrew Gallatin 		if (status) {
7554da0d523SAndrew Gallatin 			device_printf(sc->dev,
7564da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
757b2fc195eSAndrew Gallatin 			return status;
758b2fc195eSAndrew Gallatin 		}
7594da0d523SAndrew Gallatin 		device_printf(sc->dev,
7604da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
7614da0d523SAndrew Gallatin 		if (sc->tx.boundary == 4096) {
7624da0d523SAndrew Gallatin 			device_printf(sc->dev,
7634da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
7644da0d523SAndrew Gallatin 				 ".  For optimal\n");
7654da0d523SAndrew Gallatin 			device_printf(sc->dev,
7664da0d523SAndrew Gallatin 				 "performance consider loading optimized "
7674da0d523SAndrew Gallatin 				 "firmware\n");
7684da0d523SAndrew Gallatin 		}
769d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
770d91b1b49SAndrew Gallatin 		sc->tx.boundary = 2048;
771d91b1b49SAndrew Gallatin 		return 0;
7724da0d523SAndrew Gallatin 	}
773b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
774b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
775b2fc195eSAndrew Gallatin 	*confirm = 0;
776b2fc195eSAndrew Gallatin 	mb();
777b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
778b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
779b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
780b2fc195eSAndrew Gallatin 	*/
781b2fc195eSAndrew Gallatin 
7826d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7836d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
784b2fc195eSAndrew Gallatin 
785b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
786b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
787b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
788b2fc195eSAndrew Gallatin 
789b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
790b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
791b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
792b2fc195eSAndrew Gallatin 	*/
793b2fc195eSAndrew Gallatin 					/* where the code starts*/
7946d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
795b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
796b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
797b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
798b2fc195eSAndrew Gallatin 
7990fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
8006d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
801b2fc195eSAndrew Gallatin 	mb();
802b2fc195eSAndrew Gallatin 	DELAY(1000);
803b2fc195eSAndrew Gallatin 	mb();
804b2fc195eSAndrew Gallatin 	i = 0;
805b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
806b2fc195eSAndrew Gallatin 		DELAY(1000*10);
807b2fc195eSAndrew Gallatin 		i++;
808b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
809b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
810b2fc195eSAndrew Gallatin 	}
811b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
812b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
813b2fc195eSAndrew Gallatin 			confirm, *confirm);
814b2fc195eSAndrew Gallatin 
815b2fc195eSAndrew Gallatin 		return ENXIO;
816b2fc195eSAndrew Gallatin 	}
817b2fc195eSAndrew Gallatin 	return 0;
818b2fc195eSAndrew Gallatin }
819b2fc195eSAndrew Gallatin 
820b2fc195eSAndrew Gallatin static int
8216d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
822b2fc195eSAndrew Gallatin {
8236d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
824b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
825b2fc195eSAndrew Gallatin 	int status;
826b2fc195eSAndrew Gallatin 
827b2fc195eSAndrew Gallatin 
828b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
829b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
830b2fc195eSAndrew Gallatin 
831b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
832b2fc195eSAndrew Gallatin 
8335e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
834b2fc195eSAndrew Gallatin 	return status;
835b2fc195eSAndrew Gallatin }
836b2fc195eSAndrew Gallatin 
837b2fc195eSAndrew Gallatin static int
8386d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
839b2fc195eSAndrew Gallatin {
8406d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
841b2fc195eSAndrew Gallatin 	int status;
842b2fc195eSAndrew Gallatin 
843b2fc195eSAndrew Gallatin 	if (pause)
8445e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
845b2fc195eSAndrew Gallatin 				       &cmd);
846b2fc195eSAndrew Gallatin 	else
8475e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
848b2fc195eSAndrew Gallatin 				       &cmd);
849b2fc195eSAndrew Gallatin 
850b2fc195eSAndrew Gallatin 	if (status) {
851b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
852b2fc195eSAndrew Gallatin 		return ENXIO;
853b2fc195eSAndrew Gallatin 	}
854b2fc195eSAndrew Gallatin 	sc->pause = pause;
855b2fc195eSAndrew Gallatin 	return 0;
856b2fc195eSAndrew Gallatin }
857b2fc195eSAndrew Gallatin 
858b2fc195eSAndrew Gallatin static void
8596d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
860b2fc195eSAndrew Gallatin {
8616d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
862b2fc195eSAndrew Gallatin 	int status;
863b2fc195eSAndrew Gallatin 
864b2fc195eSAndrew Gallatin 	if (promisc)
8655e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
866b2fc195eSAndrew Gallatin 				       &cmd);
867b2fc195eSAndrew Gallatin 	else
8685e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
869b2fc195eSAndrew Gallatin 				       &cmd);
870b2fc195eSAndrew Gallatin 
871b2fc195eSAndrew Gallatin 	if (status) {
872b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
873b2fc195eSAndrew Gallatin 	}
874b2fc195eSAndrew Gallatin }
875b2fc195eSAndrew Gallatin 
8760fa7f681SAndrew Gallatin static void
8770fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
8780fa7f681SAndrew Gallatin {
8790fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
8800fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
8810fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
8820fa7f681SAndrew Gallatin 	int err;
8830fa7f681SAndrew Gallatin 
8840fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
8850fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
8860fa7f681SAndrew Gallatin 		return;
8870fa7f681SAndrew Gallatin 
8880fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
8890fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
8900fa7f681SAndrew Gallatin 	if (err != 0) {
8910fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
8920fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
8930fa7f681SAndrew Gallatin 		return;
8940fa7f681SAndrew Gallatin 	}
8950fa7f681SAndrew Gallatin 
896b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
897b824b7d8SAndrew Gallatin 		return;
8980fa7f681SAndrew Gallatin 
8990fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
9000fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
9010fa7f681SAndrew Gallatin 		return;
9020fa7f681SAndrew Gallatin 
9030fa7f681SAndrew Gallatin 	/* Flush all the filters */
9040fa7f681SAndrew Gallatin 
9050fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
9060fa7f681SAndrew Gallatin 	if (err != 0) {
9070fa7f681SAndrew Gallatin 		device_printf(sc->dev,
9080fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
9090fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
9100fa7f681SAndrew Gallatin 		return;
9110fa7f681SAndrew Gallatin 	}
9120fa7f681SAndrew Gallatin 
9130fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
9140fa7f681SAndrew Gallatin 
9150fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
9160fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
9170fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
9180fa7f681SAndrew Gallatin 			continue;
9190fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
9200fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
9210fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
9220fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
9230fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
9240fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
9250fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
9260fa7f681SAndrew Gallatin 		if (err != 0) {
9270fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
9280fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
9290fa7f681SAndrew Gallatin 			       "%d\t", err);
9300fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
9310fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
9320fa7f681SAndrew Gallatin 			return;
9330fa7f681SAndrew Gallatin 		}
9340fa7f681SAndrew Gallatin 	}
9350fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
9360fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
9370fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
9380fa7f681SAndrew Gallatin 	if (err != 0) {
9390fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
9400fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
9410fa7f681SAndrew Gallatin 	}
9420fa7f681SAndrew Gallatin }
9430fa7f681SAndrew Gallatin 
9440fa7f681SAndrew Gallatin 
945b2fc195eSAndrew Gallatin static int
9466d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc)
947b2fc195eSAndrew Gallatin {
948b2fc195eSAndrew Gallatin 
9496d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
9505e7d8541SAndrew Gallatin 	size_t bytes;
9515e7d8541SAndrew Gallatin 	int status;
952b2fc195eSAndrew Gallatin 
953b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
954b2fc195eSAndrew Gallatin 	   is alive */
955b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
9565e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
957b2fc195eSAndrew Gallatin 	if (status != 0) {
958b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
959b2fc195eSAndrew Gallatin 		return ENXIO;
960b2fc195eSAndrew Gallatin 	}
961b2fc195eSAndrew Gallatin 
962091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
963091feecdSAndrew Gallatin 
964b2fc195eSAndrew Gallatin 	/* Now exchange information about interrupts  */
9655e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\
9665e7d8541SAndrew Gallatin 	memset(sc->rx_done.entry, 0, bytes);
9675e7d8541SAndrew Gallatin 	cmd.data0 = (uint32_t)bytes;
9685e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
9695e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr);
9705e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr);
9715e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd);
972b2fc195eSAndrew Gallatin 
9736d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
9745e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
9755e7d8541SAndrew Gallatin 
9765e7d8541SAndrew Gallatin 
9775e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
9785e7d8541SAndrew Gallatin 
9795e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
9805e7d8541SAndrew Gallatin 	sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
9815e7d8541SAndrew Gallatin 
9825e7d8541SAndrew Gallatin 
9835e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
9846d87a65dSAndrew Gallatin 				&cmd);
9855e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
986b2fc195eSAndrew Gallatin 	if (status != 0) {
987b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
988b2fc195eSAndrew Gallatin 		return status;
989b2fc195eSAndrew Gallatin 	}
990b2fc195eSAndrew Gallatin 
9915e7d8541SAndrew Gallatin 
9925e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
9935e7d8541SAndrew Gallatin 
9945e7d8541SAndrew Gallatin 
9955e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
9965e7d8541SAndrew Gallatin 	sc->read_dma = sc->write_dma = sc->read_write_dma = 0;
9975e7d8541SAndrew Gallatin 
9985e7d8541SAndrew Gallatin 	/* Read DMA */
999a98d6cd7SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr);
1000a98d6cd7SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr);
10015e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10000;
10025e7d8541SAndrew Gallatin 
10035e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
10045e7d8541SAndrew Gallatin 	if (status != 0)
10055e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read dma benchmark failed\n");
10065e7d8541SAndrew Gallatin 	else
10075e7d8541SAndrew Gallatin 		sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
10085e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
10095e7d8541SAndrew Gallatin 
10105e7d8541SAndrew Gallatin 	/* Write DMA */
1011a98d6cd7SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr);
1012a98d6cd7SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr);
10135e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x1;
10145e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
10155e7d8541SAndrew Gallatin 	if (status != 0)
10165e7d8541SAndrew Gallatin 		device_printf(sc->dev, "write dma benchmark failed\n");
10175e7d8541SAndrew Gallatin 	else
10185e7d8541SAndrew Gallatin 		sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
10195e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
10205e7d8541SAndrew Gallatin 	/* Read/Write DMA */
1021a98d6cd7SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr);
1022a98d6cd7SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr);
10235e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10001;
10245e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
10255e7d8541SAndrew Gallatin 	if (status != 0)
10265e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read/write dma benchmark failed\n");
10275e7d8541SAndrew Gallatin 	else
10285e7d8541SAndrew Gallatin 		sc->read_write_dma =
10295e7d8541SAndrew Gallatin 			((cmd.data0>>16) * sc->tx.boundary * 2 * 2) /
10305e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
10315e7d8541SAndrew Gallatin 
1032b2fc195eSAndrew Gallatin 	/* reset mcp/driver shared state back to 0 */
10335e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
10345e7d8541SAndrew Gallatin 	sc->rx_done.idx = 0;
10355e7d8541SAndrew Gallatin 	sc->rx_done.cnt = 0;
1036b2fc195eSAndrew Gallatin 	sc->tx.req = 0;
1037b2fc195eSAndrew Gallatin 	sc->tx.done = 0;
10385e7d8541SAndrew Gallatin 	sc->tx.pkt_done = 0;
1039a82c2581SAndrew Gallatin 	sc->tx.wake = 0;
1040a82c2581SAndrew Gallatin 	sc->tx.stall = 0;
1041b2fc195eSAndrew Gallatin 	sc->rx_big.cnt = 0;
1042b2fc195eSAndrew Gallatin 	sc->rx_small.cnt = 0;
1043b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
1044a98d6cd7SAndrew Gallatin 	sc->fw_stats->valid = 0;
1045a98d6cd7SAndrew Gallatin 	sc->fw_stats->send_done_count = 0;
10466d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
10476d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
10486d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
10490fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1050b2fc195eSAndrew Gallatin 	return status;
1051b2fc195eSAndrew Gallatin }
1052b2fc195eSAndrew Gallatin 
1053b2fc195eSAndrew Gallatin static int
10546d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1055b2fc195eSAndrew Gallatin {
10566d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1057b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1058b2fc195eSAndrew Gallatin         int err;
1059b2fc195eSAndrew Gallatin 
1060b2fc195eSAndrew Gallatin         sc = arg1;
1061b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1062b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1063b2fc195eSAndrew Gallatin         if (err != 0) {
1064b2fc195eSAndrew Gallatin                 return err;
1065b2fc195eSAndrew Gallatin         }
1066b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1067b2fc195eSAndrew Gallatin                 return 0;
1068b2fc195eSAndrew Gallatin 
1069b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1070b2fc195eSAndrew Gallatin                 return EINVAL;
1071b2fc195eSAndrew Gallatin 
1072a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
10735e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1074b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
10755e7d8541SAndrew Gallatin 
1076a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1077b2fc195eSAndrew Gallatin         return err;
1078b2fc195eSAndrew Gallatin }
1079b2fc195eSAndrew Gallatin 
1080b2fc195eSAndrew Gallatin static int
10816d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1082b2fc195eSAndrew Gallatin {
10836d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1084b2fc195eSAndrew Gallatin         unsigned int enabled;
1085b2fc195eSAndrew Gallatin         int err;
1086b2fc195eSAndrew Gallatin 
1087b2fc195eSAndrew Gallatin         sc = arg1;
1088b2fc195eSAndrew Gallatin         enabled = sc->pause;
1089b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1090b2fc195eSAndrew Gallatin         if (err != 0) {
1091b2fc195eSAndrew Gallatin                 return err;
1092b2fc195eSAndrew Gallatin         }
1093b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1094b2fc195eSAndrew Gallatin                 return 0;
1095b2fc195eSAndrew Gallatin 
1096a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
10976d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1098a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1099b2fc195eSAndrew Gallatin         return err;
1100b2fc195eSAndrew Gallatin }
1101b2fc195eSAndrew Gallatin 
1102b2fc195eSAndrew Gallatin static int
11036d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1104b2fc195eSAndrew Gallatin {
1105b2fc195eSAndrew Gallatin         int err;
1106b2fc195eSAndrew Gallatin 
1107b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1108b2fc195eSAndrew Gallatin                 return EFAULT;
1109b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1110b2fc195eSAndrew Gallatin         arg1 = NULL;
1111b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1112b2fc195eSAndrew Gallatin 
1113b2fc195eSAndrew Gallatin         return err;
1114b2fc195eSAndrew Gallatin }
1115b2fc195eSAndrew Gallatin 
1116b2fc195eSAndrew Gallatin static void
11176d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1118b2fc195eSAndrew Gallatin {
1119b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1120b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
11215e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
1122b2fc195eSAndrew Gallatin 
1123b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1124b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
1125b2fc195eSAndrew Gallatin 	fw = sc->fw_stats;
1126b2fc195eSAndrew Gallatin 
11275e7d8541SAndrew Gallatin 	/* random information */
11285e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11295e7d8541SAndrew Gallatin 		       "firmware_version",
11305e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
11315e7d8541SAndrew Gallatin 		       0, "firmware version");
11325e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11335e7d8541SAndrew Gallatin 		       "serial_number",
11345e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
11355e7d8541SAndrew Gallatin 		       0, "serial number");
11365e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11375e7d8541SAndrew Gallatin 		       "product_code",
11385e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
11395e7d8541SAndrew Gallatin 		       0, "product_code");
11405e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1141d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1142d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1143d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1144d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11455e7d8541SAndrew Gallatin 		       "tx_boundary",
11465e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.boundary,
11475e7d8541SAndrew Gallatin 		       0, "tx_boundary");
11485e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1149091feecdSAndrew Gallatin 		       "write_combine",
1150091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1151091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1152091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11535e7d8541SAndrew Gallatin 		       "read_dma_MBs",
11545e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
11555e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
11565e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11575e7d8541SAndrew Gallatin 		       "write_dma_MBs",
11585e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
11595e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
11605e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11615e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
11625e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
11635e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
11645e7d8541SAndrew Gallatin 
11655e7d8541SAndrew Gallatin 
11665e7d8541SAndrew Gallatin 	/* performance related tunables */
1167b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1168b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1169b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
11706d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1171b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1172b2fc195eSAndrew Gallatin 
1173b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1174b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1175b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
11766d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1177b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1178b2fc195eSAndrew Gallatin 
1179b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11805e7d8541SAndrew Gallatin 		       "deassert_wait",
11815e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
11825e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1183b2fc195eSAndrew Gallatin 
1184b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1185b2fc195eSAndrew Gallatin 	   Need to swap it */
1186b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1187b2fc195eSAndrew Gallatin 			"link_up",
1188b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
11896d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1190b2fc195eSAndrew Gallatin 			"I", "link up");
1191b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1192b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1193b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
11946d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1195b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1196b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1197b2fc195eSAndrew Gallatin 			"dropped_link_overflow",
1198b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
11996d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1200b2fc195eSAndrew Gallatin 			"I", "dropped_link_overflow");
1201b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1202b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1203b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1204b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
12056d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1206b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1207b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
12080fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
12090fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
12100fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
12110fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
12120fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
12130fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1214b2fc195eSAndrew Gallatin 			"dropped_runt",
1215b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
12166d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1217b2fc195eSAndrew Gallatin 			"I", "dropped_runt");
1218b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1219b2fc195eSAndrew Gallatin 			"dropped_overrun",
1220b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
12216d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1222b2fc195eSAndrew Gallatin 			"I", "dropped_overrun");
1223b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1224b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1225b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1226b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
12276d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1228b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1229b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1230b2fc195eSAndrew Gallatin 			"dropped_no_big_buffer",
1231b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
12326d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1233b2fc195eSAndrew Gallatin 			"I", "dropped_no_big_buffer");
1234b2fc195eSAndrew Gallatin 
1235b2fc195eSAndrew Gallatin 	/* host counters exported for debugging */
1236b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12375e7d8541SAndrew Gallatin 		       "rx_small_cnt",
12385e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_small.cnt,
12395e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
12405e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12415e7d8541SAndrew Gallatin 		       "rx_big_cnt",
12425e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_big.cnt,
12435e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
12445e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1245b2fc195eSAndrew Gallatin 		       "tx_req",
1246b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.req,
1247b2fc195eSAndrew Gallatin 		       0, "tx_req");
1248b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1249b2fc195eSAndrew Gallatin 		       "tx_done",
1250b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.done,
1251b2fc195eSAndrew Gallatin 		       0, "tx_done");
1252b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12535e7d8541SAndrew Gallatin 		       "tx_pkt_done",
12545e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.pkt_done,
12555e7d8541SAndrew Gallatin 		       0, "tx_done");
1256a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1257a82c2581SAndrew Gallatin 		       "tx_stall",
1258a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.stall,
1259a82c2581SAndrew Gallatin 		       0, "tx_stall");
1260a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1261a82c2581SAndrew Gallatin 		       "tx_wake",
1262a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.wake,
1263a82c2581SAndrew Gallatin 		       0, "tx_wake");
12645e7d8541SAndrew Gallatin 
12655e7d8541SAndrew Gallatin 	/* verbose printing? */
1266b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12675e7d8541SAndrew Gallatin 		       "verbose",
12685e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
12695e7d8541SAndrew Gallatin 		       0, "verbose printing");
1270b2fc195eSAndrew Gallatin 
1271b2fc195eSAndrew Gallatin }
1272b2fc195eSAndrew Gallatin 
1273b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1274b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1275b2fc195eSAndrew Gallatin 
1276b2fc195eSAndrew Gallatin static inline void
12776d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx,
1278b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1279b2fc195eSAndrew Gallatin {
1280b2fc195eSAndrew Gallatin         int idx, starting_slot;
1281b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1282b2fc195eSAndrew Gallatin         while (cnt > 1) {
1283b2fc195eSAndrew Gallatin                 cnt--;
1284b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
12856d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1286b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1287b2fc195eSAndrew Gallatin                 mb();
1288b2fc195eSAndrew Gallatin         }
1289b2fc195eSAndrew Gallatin }
1290b2fc195eSAndrew Gallatin 
1291b2fc195eSAndrew Gallatin /*
1292b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1293b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1294b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1295b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1296b2fc195eSAndrew Gallatin  */
1297b2fc195eSAndrew Gallatin 
1298b2fc195eSAndrew Gallatin static inline void
12996d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src,
1300b2fc195eSAndrew Gallatin                   int cnt)
1301b2fc195eSAndrew Gallatin {
1302b2fc195eSAndrew Gallatin         int idx, i;
1303b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1304b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1305b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1306b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
13075e7d8541SAndrew Gallatin 	uint8_t last_flags;
1308b2fc195eSAndrew Gallatin 
1309b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1310b2fc195eSAndrew Gallatin 
13115e7d8541SAndrew Gallatin 	last_flags = src->flags;
13125e7d8541SAndrew Gallatin 	src->flags = 0;
1313b2fc195eSAndrew Gallatin         mb();
1314b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1315b2fc195eSAndrew Gallatin         srcp = src;
1316b2fc195eSAndrew Gallatin 
1317b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1318b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
13196d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1320b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1321b2fc195eSAndrew Gallatin                         srcp += 2;
1322b2fc195eSAndrew Gallatin                         dstp += 2;
1323b2fc195eSAndrew Gallatin                 }
1324b2fc195eSAndrew Gallatin         } else {
1325b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1326b2fc195eSAndrew Gallatin                    that it is submitted below */
13276d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1328b2fc195eSAndrew Gallatin                 i = 0;
1329b2fc195eSAndrew Gallatin         }
1330b2fc195eSAndrew Gallatin         if (i < cnt) {
1331b2fc195eSAndrew Gallatin                 /* submit the first request */
13326d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1333b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1334b2fc195eSAndrew Gallatin         }
1335b2fc195eSAndrew Gallatin 
1336b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
13375e7d8541SAndrew Gallatin         src->flags = last_flags;
1338b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1339b2fc195eSAndrew Gallatin         src_ints+=3;
1340b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1341b2fc195eSAndrew Gallatin         dst_ints+=3;
1342b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1343b2fc195eSAndrew Gallatin         tx->req += cnt;
1344b2fc195eSAndrew Gallatin         mb();
1345b2fc195eSAndrew Gallatin }
1346b2fc195eSAndrew Gallatin 
1347b2fc195eSAndrew Gallatin static inline void
13486d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt)
1349b2fc195eSAndrew Gallatin {
1350b2fc195eSAndrew Gallatin     tx->req += cnt;
1351b2fc195eSAndrew Gallatin     mb();
1352b2fc195eSAndrew Gallatin     while (cnt >= 4) {
13536d87a65dSAndrew Gallatin 	    mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64);
1354b2fc195eSAndrew Gallatin 	    mb();
1355b2fc195eSAndrew Gallatin 	    src += 4;
1356b2fc195eSAndrew Gallatin 	    cnt -= 4;
1357b2fc195eSAndrew Gallatin     }
1358b2fc195eSAndrew Gallatin     if (cnt > 0) {
1359b2fc195eSAndrew Gallatin 	    /* pad it to 64 bytes.  The src is 64 bytes bigger than it
1360b2fc195eSAndrew Gallatin 	       needs to be so that we don't overrun it */
13610fa7f681SAndrew Gallatin 	    mxge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), src, 64);
1362b2fc195eSAndrew Gallatin 	    mb();
1363b2fc195eSAndrew Gallatin     }
1364b2fc195eSAndrew Gallatin }
1365b2fc195eSAndrew Gallatin 
1366b2fc195eSAndrew Gallatin static void
1367aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt)
1368aed8e389SAndrew Gallatin {
1369aed8e389SAndrew Gallatin 	mxge_tx_buf_t *tx;
1370aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1371aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1372aed8e389SAndrew Gallatin 	struct ether_header *eh;
1373aed8e389SAndrew Gallatin 	struct ip *ip;
1374aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1375aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1376aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1377aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1378aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1379aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1380aed8e389SAndrew Gallatin 	static int once;
1381aed8e389SAndrew Gallatin 
1382aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1383aed8e389SAndrew Gallatin 
1384aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1385aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1386aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1387aed8e389SAndrew Gallatin 	 */
1388aed8e389SAndrew Gallatin 
1389aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1390aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1391aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1392aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh)
1393aed8e389SAndrew Gallatin 			    + sizeof (*ip))) {
1394aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1395aed8e389SAndrew Gallatin 			   sc->scratch);
1396aed8e389SAndrew Gallatin 		eh = (struct ether_header *)sc->scratch;
1397aed8e389SAndrew Gallatin 	} else {
1398aed8e389SAndrew Gallatin 		eh = mtod(m, struct ether_header *);
1399aed8e389SAndrew Gallatin 	}
1400aed8e389SAndrew Gallatin 	ip = (struct ip *) (eh + 1);
1401aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2)
1402aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1403aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2)
1404aed8e389SAndrew Gallatin 			   + sizeof (*tcp),  sc->scratch);
1405aed8e389SAndrew Gallatin 		eh = (struct ether_header *) sc->scratch;
1406aed8e389SAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1407aed8e389SAndrew Gallatin 	}
1408aed8e389SAndrew Gallatin 
1409aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1410aed8e389SAndrew Gallatin 	cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2));
1411aed8e389SAndrew Gallatin 
1412aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1413aed8e389SAndrew Gallatin 	cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1414aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1415aed8e389SAndrew Gallatin 
1416aed8e389SAndrew Gallatin 
1417aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1418aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1419aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1420aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1421aed8e389SAndrew Gallatin 
1422aed8e389SAndrew Gallatin 	tx = &sc->tx;
1423aed8e389SAndrew Gallatin 	req = tx->req_list;
1424aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1425aed8e389SAndrew Gallatin 	cnt = 0;
1426aed8e389SAndrew Gallatin 	rdma_count = 0;
1427aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1428aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1429aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1430aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1431aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1432aed8e389SAndrew Gallatin 	 *
1433aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1434aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1435aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1436aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1437aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1438aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1439aed8e389SAndrew Gallatin 	 *
1440aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1441aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1442aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1443aed8e389SAndrew Gallatin 	 */
1444aed8e389SAndrew Gallatin 
1445aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1446aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1447aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1448aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1449aed8e389SAndrew Gallatin 		len = seglen = seg->ds_len;
1450aed8e389SAndrew Gallatin 
1451aed8e389SAndrew Gallatin 		while (len) {
1452aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1453aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1454aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1455aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1456aed8e389SAndrew Gallatin 				/* payload */
1457aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1458aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1459aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1460aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1461aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1462aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1463aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1464aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1465aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1466aed8e389SAndrew Gallatin 				/* header ends */
1467aed8e389SAndrew Gallatin 				rdma_count = -1;
1468aed8e389SAndrew Gallatin 				cum_len_next = 0;
1469aed8e389SAndrew Gallatin 				seglen = -cum_len;
1470aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1471aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1472aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1473aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1474aed8e389SAndrew Gallatin 			    }
1475aed8e389SAndrew Gallatin 
1476aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1477aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1478aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1479aed8e389SAndrew Gallatin 			req->pad = 0;
1480aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1481aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1482aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1483aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1484aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1485aed8e389SAndrew Gallatin 			low += seglen;
1486aed8e389SAndrew Gallatin 			len -= seglen;
1487aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1488aed8e389SAndrew Gallatin 			flags = flags_next;
1489aed8e389SAndrew Gallatin 			req++;
1490aed8e389SAndrew Gallatin 			cnt++;
1491aed8e389SAndrew Gallatin 			rdma_count++;
1492aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1493aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1494aed8e389SAndrew Gallatin 			else
1495aed8e389SAndrew Gallatin 				cksum_offset = 0;
1496aed8e389SAndrew Gallatin 			if (__predict_false(cnt > MXGE_MAX_SEND_DESC))
1497aed8e389SAndrew Gallatin 				goto drop;
1498aed8e389SAndrew Gallatin 		}
1499aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1500aed8e389SAndrew Gallatin 		seg++;
1501aed8e389SAndrew Gallatin 	}
1502aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1503aed8e389SAndrew Gallatin 
1504aed8e389SAndrew Gallatin 	do {
1505aed8e389SAndrew Gallatin 		req--;
1506aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1507aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1508aed8e389SAndrew Gallatin 
1509aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1510aed8e389SAndrew Gallatin 	if (tx->wc_fifo == NULL)
1511aed8e389SAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1512aed8e389SAndrew Gallatin 	else
1513aed8e389SAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1514aed8e389SAndrew Gallatin 	return;
1515aed8e389SAndrew Gallatin 
1516aed8e389SAndrew Gallatin drop:
1517aed8e389SAndrew Gallatin 	m_freem(m);
1518aed8e389SAndrew Gallatin 	sc->ifp->if_oerrors++;
1519aed8e389SAndrew Gallatin 	if (!once) {
1520aed8e389SAndrew Gallatin 		printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n");
1521aed8e389SAndrew Gallatin 		printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list);
1522aed8e389SAndrew Gallatin 		once = 1;
1523aed8e389SAndrew Gallatin 	}
1524aed8e389SAndrew Gallatin 	return;
1525aed8e389SAndrew Gallatin 
1526aed8e389SAndrew Gallatin }
1527aed8e389SAndrew Gallatin 
1528aed8e389SAndrew Gallatin static void
15296d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m)
1530b2fc195eSAndrew Gallatin {
1531b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1532b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1533b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1534b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
15356d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1536b2fc195eSAndrew Gallatin 	struct ether_header *eh;
1537b2fc195eSAndrew Gallatin 	struct ip *ip;
1538aed8e389SAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
1539aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1540aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1541b2fc195eSAndrew Gallatin 
1542b2fc195eSAndrew Gallatin 
1543b2fc195eSAndrew Gallatin 
1544b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1545b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1546b2fc195eSAndrew Gallatin 
1547b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1548b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1549b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1550aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1551b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1552b2fc195eSAndrew Gallatin 	if (err == EFBIG) {
1553b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1554b2fc195eSAndrew Gallatin 		   to defrag */
1555b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1556b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1557b2fc195eSAndrew Gallatin 			goto drop;
1558b2fc195eSAndrew Gallatin 		}
1559b2fc195eSAndrew Gallatin 		m = m_tmp;
1560b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1561b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1562aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1563b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1564b2fc195eSAndrew Gallatin 	}
1565b2fc195eSAndrew Gallatin 	if (err != 0) {
1566aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1567aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1568b2fc195eSAndrew Gallatin 		goto drop;
1569b2fc195eSAndrew Gallatin 	}
1570b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1571b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
15725e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1573b2fc195eSAndrew Gallatin 
1574aed8e389SAndrew Gallatin 
1575aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1576aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
1577aed8e389SAndrew Gallatin 		mxge_encap_tso(sc, m, cnt);
1578aed8e389SAndrew Gallatin 		return;
1579aed8e389SAndrew Gallatin 	}
1580aed8e389SAndrew Gallatin 
1581b2fc195eSAndrew Gallatin 	req = tx->req_list;
1582b2fc195eSAndrew Gallatin 	cksum_offset = 0;
15835e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
15845e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1585b2fc195eSAndrew Gallatin 
1586b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1587b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1588aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
1589aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
1590aed8e389SAndrew Gallatin 		if (__predict_false(m->m_len < sizeof (*eh)
1591aed8e389SAndrew Gallatin 				    + sizeof (*ip))) {
1592aed8e389SAndrew Gallatin 			m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1593aed8e389SAndrew Gallatin 				   sc->scratch);
1594aed8e389SAndrew Gallatin 			eh = (struct ether_header *)sc->scratch;
1595aed8e389SAndrew Gallatin 		} else {
1596b2fc195eSAndrew Gallatin 			eh = mtod(m, struct ether_header *);
1597aed8e389SAndrew Gallatin 		}
1598b2fc195eSAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1599b2fc195eSAndrew Gallatin 		cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1600b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
16015e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1602b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
16035e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1604aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
1605aed8e389SAndrew Gallatin 	} else {
1606aed8e389SAndrew Gallatin 		odd_flag = 0;
1607b2fc195eSAndrew Gallatin 	}
16085e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
16095e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
1610b2fc195eSAndrew Gallatin 
1611b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
1612b2fc195eSAndrew Gallatin 	cum_len = 0;
1613aed8e389SAndrew Gallatin 	seg = tx->seg_list;
16145e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
1615b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1616b2fc195eSAndrew Gallatin 		req->addr_low =
16176d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1618b2fc195eSAndrew Gallatin 		req->addr_high =
16196d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1620b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
1621b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
1622b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
1623b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
1624b2fc195eSAndrew Gallatin 		else
1625b2fc195eSAndrew Gallatin 			cksum_offset = 0;
16265e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
16275e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
16285e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1629aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1630b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
1631b2fc195eSAndrew Gallatin 		seg++;
1632b2fc195eSAndrew Gallatin 		req++;
1633b2fc195eSAndrew Gallatin 		req->flags = 0;
1634b2fc195eSAndrew Gallatin 	}
1635b2fc195eSAndrew Gallatin 	req--;
1636b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
1637b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
1638b2fc195eSAndrew Gallatin 		req++;
1639b2fc195eSAndrew Gallatin 		req->addr_low =
16406d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
1641b2fc195eSAndrew Gallatin 		req->addr_high =
16426d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
1643b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
16445e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
16455e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
16465e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
16475e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1648aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1649b2fc195eSAndrew Gallatin 		cnt++;
1650b2fc195eSAndrew Gallatin 	}
16515e7d8541SAndrew Gallatin 
16525e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
16535e7d8541SAndrew Gallatin #if 0
16545e7d8541SAndrew Gallatin 	/* print what the firmware will see */
16555e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
16565e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
16575e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
16585e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
16595e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
16605e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
16615e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
16625e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
16635e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
16645e7d8541SAndrew Gallatin 	}
16655e7d8541SAndrew Gallatin 	printf("--------------\n");
16665e7d8541SAndrew Gallatin #endif
16675e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1668b2fc195eSAndrew Gallatin 	if (tx->wc_fifo == NULL)
16696d87a65dSAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1670b2fc195eSAndrew Gallatin 	else
16716d87a65dSAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1672b2fc195eSAndrew Gallatin 	return;
1673b2fc195eSAndrew Gallatin 
1674b2fc195eSAndrew Gallatin drop:
1675b2fc195eSAndrew Gallatin 	m_freem(m);
1676b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
1677b2fc195eSAndrew Gallatin 	return;
1678b2fc195eSAndrew Gallatin }
1679b2fc195eSAndrew Gallatin 
1680b2fc195eSAndrew Gallatin 
16816d914a32SAndrew Gallatin 
16826d914a32SAndrew Gallatin 
16836d914a32SAndrew Gallatin static inline void
16846d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc)
1685b2fc195eSAndrew Gallatin {
1686b2fc195eSAndrew Gallatin 	struct mbuf *m;
1687b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1688b2fc195eSAndrew Gallatin 
1689b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
16906d914a32SAndrew Gallatin 	while ((sc->tx.mask - (sc->tx.req - sc->tx.done))
16916d914a32SAndrew Gallatin 	       > MXGE_MAX_SEND_DESC) {
1692b2fc195eSAndrew Gallatin 
16936d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
16946d914a32SAndrew Gallatin 		if (m == NULL) {
16956d914a32SAndrew Gallatin 			return;
16966d914a32SAndrew Gallatin 		}
1697b2fc195eSAndrew Gallatin 		/* let BPF see it */
1698b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
1699b2fc195eSAndrew Gallatin 
1700b2fc195eSAndrew Gallatin 		/* give it to the nic */
17016d87a65dSAndrew Gallatin 		mxge_encap(sc, m);
17026d914a32SAndrew Gallatin 	}
17036d914a32SAndrew Gallatin 	/* ran out of transmit slots */
1704a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
1705b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1706a82c2581SAndrew Gallatin 		sc->tx.stall++;
1707a82c2581SAndrew Gallatin 	}
1708b2fc195eSAndrew Gallatin }
1709b2fc195eSAndrew Gallatin 
1710b2fc195eSAndrew Gallatin static void
17116d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
1712b2fc195eSAndrew Gallatin {
17136d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
1714b2fc195eSAndrew Gallatin 
1715b2fc195eSAndrew Gallatin 
1716a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->tx_mtx);
17176d87a65dSAndrew Gallatin 	mxge_start_locked(sc);
1718a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->tx_mtx);
1719b2fc195eSAndrew Gallatin }
1720b2fc195eSAndrew Gallatin 
17215e7d8541SAndrew Gallatin /*
17225e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
17235e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
17245e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
17255e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
17265e7d8541SAndrew Gallatin  * in a burst
17275e7d8541SAndrew Gallatin  */
17285e7d8541SAndrew Gallatin static inline void
17295e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
17305e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
17315e7d8541SAndrew Gallatin {
17325e7d8541SAndrew Gallatin 	uint32_t low;
17335e7d8541SAndrew Gallatin 
17345e7d8541SAndrew Gallatin 	low = src->addr_low;
17355e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
1736a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
1737a1480dfbSAndrew Gallatin 	mb();
1738a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
17395e7d8541SAndrew Gallatin 	mb();
174040385a5fSAndrew Gallatin 	src->addr_low = low;
17415e7d8541SAndrew Gallatin 	dst->addr_low = low;
17425e7d8541SAndrew Gallatin 	mb();
17435e7d8541SAndrew Gallatin }
17445e7d8541SAndrew Gallatin 
1745b2fc195eSAndrew Gallatin static int
17466d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1747b2fc195eSAndrew Gallatin {
1748b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1749b2fc195eSAndrew Gallatin 	struct mbuf *m;
17506d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_small;
1751b2fc195eSAndrew Gallatin 	int cnt, err;
1752b2fc195eSAndrew Gallatin 
1753b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
1754b2fc195eSAndrew Gallatin 	if (m == NULL) {
1755b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1756b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1757b2fc195eSAndrew Gallatin 		goto done;
1758b2fc195eSAndrew Gallatin 	}
1759b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
1760b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1761b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1762b2fc195eSAndrew Gallatin 	if (err != 0) {
1763b2fc195eSAndrew Gallatin 		m_free(m);
1764b2fc195eSAndrew Gallatin 		goto done;
1765b2fc195eSAndrew Gallatin 	}
1766b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1767b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
17686d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1769b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
17706d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1771b2fc195eSAndrew Gallatin 
1772b2fc195eSAndrew Gallatin done:
1773b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
17745e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
17755e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
17765e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
17775e7d8541SAndrew Gallatin 		else {
1778b2fc195eSAndrew Gallatin 			mb();
17795e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
17805e7d8541SAndrew Gallatin 		}
1781b2fc195eSAndrew Gallatin         }
1782b2fc195eSAndrew Gallatin 	return err;
1783b2fc195eSAndrew Gallatin }
1784b2fc195eSAndrew Gallatin 
1785b2fc195eSAndrew Gallatin static int
17866d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1787b2fc195eSAndrew Gallatin {
1788b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1789b2fc195eSAndrew Gallatin 	struct mbuf *m;
17906d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_big;
1791b2fc195eSAndrew Gallatin 	int cnt, err;
1792b2fc195eSAndrew Gallatin 
1793b2fc195eSAndrew Gallatin 	m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes);
1794b2fc195eSAndrew Gallatin 	if (m == NULL) {
1795b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1796b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1797b2fc195eSAndrew Gallatin 		goto done;
1798b2fc195eSAndrew Gallatin 	}
1799b2fc195eSAndrew Gallatin 	m->m_len = sc->big_bytes;
1800b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1801b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1802b2fc195eSAndrew Gallatin 	if (err != 0) {
1803b2fc195eSAndrew Gallatin 		m_free(m);
1804b2fc195eSAndrew Gallatin 		goto done;
1805b2fc195eSAndrew Gallatin 	}
1806b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1807b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
18086d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1809b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
18106d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1811b2fc195eSAndrew Gallatin 
1812b2fc195eSAndrew Gallatin done:
1813b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
18145e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
18155e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
18165e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
18175e7d8541SAndrew Gallatin 		else {
1818b2fc195eSAndrew Gallatin 			mb();
18195e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
18205e7d8541SAndrew Gallatin 		}
1821b2fc195eSAndrew Gallatin         }
1822b2fc195eSAndrew Gallatin 	return err;
1823b2fc195eSAndrew Gallatin }
1824b2fc195eSAndrew Gallatin 
1825b2fc195eSAndrew Gallatin static inline void
18265e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
18275e7d8541SAndrew Gallatin {
18285e7d8541SAndrew Gallatin 	struct ether_header *eh;
18295e7d8541SAndrew Gallatin 	struct ip *ip;
18305e7d8541SAndrew Gallatin 
18315e7d8541SAndrew Gallatin 	eh = mtod(m, struct ether_header *);
18325e7d8541SAndrew Gallatin 	if (__predict_true(eh->ether_type ==  htons(ETHERTYPE_IP))) {
18335e7d8541SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
18345e7d8541SAndrew Gallatin 		if (__predict_true(ip->ip_p == IPPROTO_TCP ||
18355e7d8541SAndrew Gallatin 				   ip->ip_p == IPPROTO_UDP)) {
18365e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_data = csum;
18375e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_flags = CSUM_DATA_VALID;
18385e7d8541SAndrew Gallatin 		}
18395e7d8541SAndrew Gallatin 	}
18405e7d8541SAndrew Gallatin }
18415e7d8541SAndrew Gallatin 
18425e7d8541SAndrew Gallatin static inline void
18435e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum)
1844b2fc195eSAndrew Gallatin {
1845b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1846b2fc195eSAndrew Gallatin 	struct mbuf *m = 0; 		/* -Wunitialized */
1847b2fc195eSAndrew Gallatin 	struct mbuf *m_prev = 0;	/* -Wunitialized */
1848b2fc195eSAndrew Gallatin 	struct mbuf *m_head = 0;
1849b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
18506d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1851b2fc195eSAndrew Gallatin 	int idx;
1852b2fc195eSAndrew Gallatin 
1853b2fc195eSAndrew Gallatin 
1854b2fc195eSAndrew Gallatin 	rx = &sc->rx_big;
1855b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1856b2fc195eSAndrew Gallatin 	while (len > 0) {
1857b2fc195eSAndrew Gallatin 		idx = rx->cnt & rx->mask;
1858b2fc195eSAndrew Gallatin                 rx->cnt++;
1859b2fc195eSAndrew Gallatin 		/* save a pointer to the received mbuf */
1860b2fc195eSAndrew Gallatin 		m = rx->info[idx].m;
1861b2fc195eSAndrew Gallatin 		/* try to replace the received mbuf */
18626d87a65dSAndrew Gallatin 		if (mxge_get_buf_big(sc, rx->extra_map, idx)) {
1863b2fc195eSAndrew Gallatin 			goto drop;
1864b2fc195eSAndrew Gallatin 		}
1865b2fc195eSAndrew Gallatin 		/* unmap the received buffer */
1866b2fc195eSAndrew Gallatin 		old_map = rx->info[idx].map;
1867b2fc195eSAndrew Gallatin 		bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1868b2fc195eSAndrew Gallatin 		bus_dmamap_unload(rx->dmat, old_map);
1869b2fc195eSAndrew Gallatin 
1870b2fc195eSAndrew Gallatin 		/* swap the bus_dmamap_t's */
1871b2fc195eSAndrew Gallatin 		rx->info[idx].map = rx->extra_map;
1872b2fc195eSAndrew Gallatin 		rx->extra_map = old_map;
1873b2fc195eSAndrew Gallatin 
1874b2fc195eSAndrew Gallatin 		/* chain multiple segments together */
1875b2fc195eSAndrew Gallatin 		if (!m_head) {
1876b2fc195eSAndrew Gallatin 			m_head = m;
1877b2fc195eSAndrew Gallatin 			/* mcp implicitly skips 1st bytes so that
1878b2fc195eSAndrew Gallatin 			 * packet is properly aligned */
18795e7d8541SAndrew Gallatin 			m->m_data += MXGEFW_PAD;
1880b2fc195eSAndrew Gallatin 			m->m_pkthdr.len = len;
18815e7d8541SAndrew Gallatin 			m->m_len = sc->big_bytes - MXGEFW_PAD;
1882b2fc195eSAndrew Gallatin 		} else {
1883b2fc195eSAndrew Gallatin 			m->m_len = sc->big_bytes;
1884b2fc195eSAndrew Gallatin 			m->m_flags &= ~M_PKTHDR;
1885b2fc195eSAndrew Gallatin 			m_prev->m_next = m;
1886b2fc195eSAndrew Gallatin 		}
1887b2fc195eSAndrew Gallatin 		len -= m->m_len;
1888b2fc195eSAndrew Gallatin 		m_prev = m;
1889b2fc195eSAndrew Gallatin 	}
1890b2fc195eSAndrew Gallatin 
1891b2fc195eSAndrew Gallatin 	/* trim trailing garbage from the last mbuf in the chain.  If
1892b2fc195eSAndrew Gallatin 	 * there is any garbage, len will be negative */
1893b2fc195eSAndrew Gallatin 	m->m_len += len;
1894b2fc195eSAndrew Gallatin 
1895b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
18965e7d8541SAndrew Gallatin 	if (sc->csum_flag)
18975e7d8541SAndrew Gallatin 		mxge_rx_csum(m_head, csum);
1898b2fc195eSAndrew Gallatin 
1899b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1900b2fc195eSAndrew Gallatin 	m_head->m_pkthdr.rcvif = ifp;
1901b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1902b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m_head);
1903b2fc195eSAndrew Gallatin 	return;
1904b2fc195eSAndrew Gallatin 
1905b2fc195eSAndrew Gallatin drop:
1906b2fc195eSAndrew Gallatin 	/* drop the frame -- the old mbuf(s) are re-cycled by running
1907b2fc195eSAndrew Gallatin 	   every slot through the allocator */
1908b2fc195eSAndrew Gallatin         if (m_head) {
1909b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1910b2fc195eSAndrew Gallatin                 m_freem(m_head);
1911b2fc195eSAndrew Gallatin         } else {
19125e7d8541SAndrew Gallatin                 len -= (sc->big_bytes + MXGEFW_PAD);
1913b2fc195eSAndrew Gallatin         }
1914b2fc195eSAndrew Gallatin         while ((int)len > 0) {
1915b2fc195eSAndrew Gallatin                 idx = rx->cnt & rx->mask;
1916b2fc195eSAndrew Gallatin                 rx->cnt++;
1917b2fc195eSAndrew Gallatin                 m = rx->info[idx].m;
19186d87a65dSAndrew Gallatin                 if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) {
1919b2fc195eSAndrew Gallatin 			m_freem(m);
1920b2fc195eSAndrew Gallatin 			/* unmap the received buffer */
1921b2fc195eSAndrew Gallatin 			old_map = rx->info[idx].map;
1922b2fc195eSAndrew Gallatin 			bus_dmamap_sync(rx->dmat, old_map,
1923b2fc195eSAndrew Gallatin 					BUS_DMASYNC_POSTREAD);
1924b2fc195eSAndrew Gallatin 			bus_dmamap_unload(rx->dmat, old_map);
1925b2fc195eSAndrew Gallatin 
1926b2fc195eSAndrew Gallatin 			/* swap the bus_dmamap_t's */
1927b2fc195eSAndrew Gallatin 			rx->info[idx].map = rx->extra_map;
1928b2fc195eSAndrew Gallatin 			rx->extra_map = old_map;
1929b2fc195eSAndrew Gallatin 		}
1930b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1931b2fc195eSAndrew Gallatin         }
1932b2fc195eSAndrew Gallatin 
1933b2fc195eSAndrew Gallatin 	ifp->if_ierrors++;
1934b2fc195eSAndrew Gallatin 
1935b2fc195eSAndrew Gallatin }
1936b2fc195eSAndrew Gallatin 
1937b2fc195eSAndrew Gallatin static inline void
19385e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum)
1939b2fc195eSAndrew Gallatin {
1940b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1941b2fc195eSAndrew Gallatin 	struct mbuf *m;
19426d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1943b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
1944b2fc195eSAndrew Gallatin 	int idx;
1945b2fc195eSAndrew Gallatin 
1946b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1947b2fc195eSAndrew Gallatin 	rx = &sc->rx_small;
1948b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
1949b2fc195eSAndrew Gallatin 	rx->cnt++;
1950b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
1951b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
1952b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
19536d87a65dSAndrew Gallatin 	if (mxge_get_buf_small(sc, rx->extra_map, idx)) {
1954b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
1955b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
1956b2fc195eSAndrew Gallatin 		return;
1957b2fc195eSAndrew Gallatin 	}
1958b2fc195eSAndrew Gallatin 
1959b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
1960b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
1961b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1962b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
1963b2fc195eSAndrew Gallatin 
1964b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
1965b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
1966b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
1967b2fc195eSAndrew Gallatin 
1968b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
1969b2fc195eSAndrew Gallatin 	 * aligned */
19705e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
1971b2fc195eSAndrew Gallatin 
1972b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
19735e7d8541SAndrew Gallatin 	if (sc->csum_flag)
19745e7d8541SAndrew Gallatin 		mxge_rx_csum(m, csum);
1975b2fc195eSAndrew Gallatin 
1976b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1977b2fc195eSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
1978b2fc195eSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
1979b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1980b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
1981b2fc195eSAndrew Gallatin }
1982b2fc195eSAndrew Gallatin 
1983b2fc195eSAndrew Gallatin static inline void
19845e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc)
19855e7d8541SAndrew Gallatin {
19865e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
19875e7d8541SAndrew Gallatin 	int limit = 0;
19885e7d8541SAndrew Gallatin 	uint16_t length;
19895e7d8541SAndrew Gallatin 	uint16_t checksum;
19905e7d8541SAndrew Gallatin 
19915e7d8541SAndrew Gallatin 
19925e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
19935e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
19945e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
19955e7d8541SAndrew Gallatin 		checksum = ntohs(rx_done->entry[rx_done->idx].checksum);
1996b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
19975e7d8541SAndrew Gallatin 			mxge_rx_done_small(sc, length, checksum);
19985e7d8541SAndrew Gallatin 		else
19995e7d8541SAndrew Gallatin 			mxge_rx_done_big(sc, length, checksum);
20005e7d8541SAndrew Gallatin 		rx_done->cnt++;
20015e7d8541SAndrew Gallatin 		rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1);
20025e7d8541SAndrew Gallatin 
20035e7d8541SAndrew Gallatin 		/* limit potential for livelock */
20045e7d8541SAndrew Gallatin 		if (__predict_false(++limit > 2 * mxge_max_intr_slots))
20055e7d8541SAndrew Gallatin 			break;
20065e7d8541SAndrew Gallatin 
20075e7d8541SAndrew Gallatin 	}
20085e7d8541SAndrew Gallatin }
20095e7d8541SAndrew Gallatin 
20105e7d8541SAndrew Gallatin 
20115e7d8541SAndrew Gallatin static inline void
20126d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx)
2013b2fc195eSAndrew Gallatin {
2014b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20156d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
2016b2fc195eSAndrew Gallatin 	struct mbuf *m;
2017b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
20185e7d8541SAndrew Gallatin 	int idx, limit;
2019b2fc195eSAndrew Gallatin 
20205e7d8541SAndrew Gallatin 	limit = 0;
2021b2fc195eSAndrew Gallatin 	tx = &sc->tx;
2022b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20235e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2024b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2025b2fc195eSAndrew Gallatin 		tx->done++;
2026b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2027b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2028b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2029b2fc195eSAndrew Gallatin 		if (m != NULL) {
2030b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2031b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2032b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2033b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2034b2fc195eSAndrew Gallatin 			m_freem(m);
2035b2fc195eSAndrew Gallatin 		}
20365e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
20375e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
20385e7d8541SAndrew Gallatin 			tx->pkt_done++;
20395e7d8541SAndrew Gallatin 		}
20405e7d8541SAndrew Gallatin 		/* limit potential for livelock by only handling
20415e7d8541SAndrew Gallatin 		   2 full tx rings per call */
20425e7d8541SAndrew Gallatin 		if (__predict_false(++limit >  2 * tx->mask))
20435e7d8541SAndrew Gallatin 			break;
2044b2fc195eSAndrew Gallatin 	}
2045b2fc195eSAndrew Gallatin 
2046b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2047b2fc195eSAndrew Gallatin            its OK to send packets */
2048b2fc195eSAndrew Gallatin 
2049b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2050b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2051a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->tx_mtx);
2052b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2053a82c2581SAndrew Gallatin 		sc->tx.wake++;
20546d87a65dSAndrew Gallatin 		mxge_start_locked(sc);
2055a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->tx_mtx);
2056b2fc195eSAndrew Gallatin 	}
2057b2fc195eSAndrew Gallatin }
2058b2fc195eSAndrew Gallatin 
2059b2fc195eSAndrew Gallatin static void
20606d87a65dSAndrew Gallatin mxge_intr(void *arg)
2061b2fc195eSAndrew Gallatin {
20626d87a65dSAndrew Gallatin 	mxge_softc_t *sc = arg;
20635e7d8541SAndrew Gallatin 	mcp_irq_data_t *stats = sc->fw_stats;
20645e7d8541SAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
20655e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
20665e7d8541SAndrew Gallatin 	uint32_t send_done_count;
20675e7d8541SAndrew Gallatin 	uint8_t valid;
2068b2fc195eSAndrew Gallatin 
2069b2fc195eSAndrew Gallatin 
20705e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
20715e7d8541SAndrew Gallatin 	if (!stats->valid) {
20725e7d8541SAndrew Gallatin 		return;
2073b2fc195eSAndrew Gallatin 	}
20745e7d8541SAndrew Gallatin 	valid = stats->valid;
2075b2fc195eSAndrew Gallatin 
2076dc8731d4SAndrew Gallatin 	if (!sc->msi_enabled) {
20775e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
20785e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
20795e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
20805e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
20815e7d8541SAndrew Gallatin 			stats->valid = 0;
2082dc8731d4SAndrew Gallatin 	} else {
2083dc8731d4SAndrew Gallatin 		stats->valid = 0;
2084dc8731d4SAndrew Gallatin 	}
2085dc8731d4SAndrew Gallatin 
2086dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
20875e7d8541SAndrew Gallatin 	do {
20885e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
20895e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
20905e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
20915e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
20925e7d8541SAndrew Gallatin 			mxge_tx_done(sc, (int)send_done_count);
20935e7d8541SAndrew Gallatin 			mxge_clean_rx_done(sc);
20945e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2095b2fc195eSAndrew Gallatin 		}
20965e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2097b2fc195eSAndrew Gallatin 
20985e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
20995e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
21005e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2101b2fc195eSAndrew Gallatin 			if (sc->link_state) {
21025e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
21035e7d8541SAndrew Gallatin 				if (mxge_verbose)
21045e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2105b2fc195eSAndrew Gallatin 			} else {
21065e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
21075e7d8541SAndrew Gallatin 				if (mxge_verbose)
21085e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2109b2fc195eSAndrew Gallatin 			}
2110b2fc195eSAndrew Gallatin 		}
2111b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
2112b2fc195eSAndrew Gallatin 		    be32toh(sc->fw_stats->rdma_tags_available)) {
2113b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
2114b2fc195eSAndrew Gallatin 				be32toh(sc->fw_stats->rdma_tags_available);
21155e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
21165e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
21175e7d8541SAndrew Gallatin 		}
21185e7d8541SAndrew Gallatin 		sc->down_cnt += stats->link_down;
2119b2fc195eSAndrew Gallatin 	}
2120b2fc195eSAndrew Gallatin 
21215e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
21225e7d8541SAndrew Gallatin 	if (valid & 0x1)
21235e7d8541SAndrew Gallatin 	    *sc->irq_claim = be32toh(3);
21245e7d8541SAndrew Gallatin 	*(sc->irq_claim + 1) = be32toh(3);
2125b2fc195eSAndrew Gallatin }
2126b2fc195eSAndrew Gallatin 
2127b2fc195eSAndrew Gallatin static void
21286d87a65dSAndrew Gallatin mxge_init(void *arg)
2129b2fc195eSAndrew Gallatin {
2130b2fc195eSAndrew Gallatin }
2131b2fc195eSAndrew Gallatin 
2132b2fc195eSAndrew Gallatin 
2133b2fc195eSAndrew Gallatin 
2134b2fc195eSAndrew Gallatin static void
21356d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2136b2fc195eSAndrew Gallatin {
2137b2fc195eSAndrew Gallatin 	int i;
2138b2fc195eSAndrew Gallatin 
2139b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2140b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2141b2fc195eSAndrew Gallatin 			continue;
2142b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2143b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2144b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2145b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2146b2fc195eSAndrew Gallatin 	}
2147b2fc195eSAndrew Gallatin 
2148b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2149b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2150b2fc195eSAndrew Gallatin 			continue;
2151b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2152b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2153b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2154b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2155b2fc195eSAndrew Gallatin 	}
2156b2fc195eSAndrew Gallatin 
2157b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2158dce01b9bSAndrew Gallatin 		sc->tx.info[i].flag = 0;
2159b2fc195eSAndrew Gallatin 		if (sc->tx.info[i].m == NULL)
2160b2fc195eSAndrew Gallatin 			continue;
2161b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->tx.dmat,
2162b2fc195eSAndrew Gallatin 				  sc->tx.info[i].map);
2163b2fc195eSAndrew Gallatin 		m_freem(sc->tx.info[i].m);
2164b2fc195eSAndrew Gallatin 		sc->tx.info[i].m = NULL;
2165b2fc195eSAndrew Gallatin 	}
2166b2fc195eSAndrew Gallatin }
2167b2fc195eSAndrew Gallatin 
2168b2fc195eSAndrew Gallatin static void
21696d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2170b2fc195eSAndrew Gallatin {
2171b2fc195eSAndrew Gallatin 	int i;
2172b2fc195eSAndrew Gallatin 
2173aed8e389SAndrew Gallatin 	if (sc->tx.req_bytes != NULL)
2174b2fc195eSAndrew Gallatin 		free(sc->tx.req_bytes, M_DEVBUF);
2175aed8e389SAndrew Gallatin 	if (sc->tx.seg_list != NULL)
2176aed8e389SAndrew Gallatin 		free(sc->tx.seg_list, M_DEVBUF);
2177b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow != NULL)
2178b2fc195eSAndrew Gallatin 		free(sc->rx_small.shadow, M_DEVBUF);
2179b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow != NULL)
2180b2fc195eSAndrew Gallatin 		free(sc->rx_big.shadow, M_DEVBUF);
2181b2fc195eSAndrew Gallatin 	if (sc->tx.info != NULL) {
2182c2657176SAndrew Gallatin 		if (sc->tx.dmat != NULL) {
2183b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->tx.mask; i++) {
2184b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->tx.dmat,
2185b2fc195eSAndrew Gallatin 						   sc->tx.info[i].map);
2186b2fc195eSAndrew Gallatin 			}
2187c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->tx.dmat);
2188c2657176SAndrew Gallatin 		}
2189b2fc195eSAndrew Gallatin 		free(sc->tx.info, M_DEVBUF);
2190b2fc195eSAndrew Gallatin 	}
2191b2fc195eSAndrew Gallatin 	if (sc->rx_small.info != NULL) {
2192c2657176SAndrew Gallatin 		if (sc->rx_small.dmat != NULL) {
2193b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_small.mask; i++) {
2194b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_small.dmat,
2195b2fc195eSAndrew Gallatin 						   sc->rx_small.info[i].map);
2196b2fc195eSAndrew Gallatin 			}
2197c2657176SAndrew Gallatin 			bus_dmamap_destroy(sc->rx_small.dmat,
2198c2657176SAndrew Gallatin 					   sc->rx_small.extra_map);
2199c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_small.dmat);
2200c2657176SAndrew Gallatin 		}
2201b2fc195eSAndrew Gallatin 		free(sc->rx_small.info, M_DEVBUF);
2202b2fc195eSAndrew Gallatin 	}
2203b2fc195eSAndrew Gallatin 	if (sc->rx_big.info != NULL) {
2204c2657176SAndrew Gallatin 		if (sc->rx_big.dmat != NULL) {
2205b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_big.mask; i++) {
2206b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_big.dmat,
2207b2fc195eSAndrew Gallatin 						   sc->rx_big.info[i].map);
2208b2fc195eSAndrew Gallatin 			}
2209b2fc195eSAndrew Gallatin 			bus_dmamap_destroy(sc->rx_big.dmat,
2210b2fc195eSAndrew Gallatin 					   sc->rx_big.extra_map);
2211b2fc195eSAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_big.dmat);
2212b2fc195eSAndrew Gallatin 		}
2213c2657176SAndrew Gallatin 		free(sc->rx_big.info, M_DEVBUF);
2214c2657176SAndrew Gallatin 	}
2215c2657176SAndrew Gallatin }
2216b2fc195eSAndrew Gallatin 
2217b2fc195eSAndrew Gallatin static int
22186d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
2219b2fc195eSAndrew Gallatin {
22206d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2221b2fc195eSAndrew Gallatin 	int tx_ring_size, rx_ring_size;
2222b2fc195eSAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
2223b2fc195eSAndrew Gallatin 	int i, err;
2224b2fc195eSAndrew Gallatin 	unsigned long bytes;
2225b2fc195eSAndrew Gallatin 
2226b2fc195eSAndrew Gallatin 	/* get ring sizes */
22275e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
2228b2fc195eSAndrew Gallatin 	tx_ring_size = cmd.data0;
22295e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
2230b2fc195eSAndrew Gallatin 	if (err != 0) {
2231b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Cannot determine ring sizes\n");
2232b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2233b2fc195eSAndrew Gallatin 	}
2234b2fc195eSAndrew Gallatin 
2235b2fc195eSAndrew Gallatin 	rx_ring_size = cmd.data0;
2236b2fc195eSAndrew Gallatin 
2237b2fc195eSAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
2238b2fc195eSAndrew Gallatin 	rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t);
223976bb9c5eSAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
2240a82c2581SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
224176bb9c5eSAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
2242b2fc195eSAndrew Gallatin 
2243b2fc195eSAndrew Gallatin 	sc->tx.mask = tx_ring_entries - 1;
2244b2fc195eSAndrew Gallatin 	sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1;
2245b2fc195eSAndrew Gallatin 
2246b2fc195eSAndrew Gallatin 	err = ENOMEM;
2247b2fc195eSAndrew Gallatin 
2248b2fc195eSAndrew Gallatin 	/* allocate the tx request copy block */
2249b2fc195eSAndrew Gallatin 	bytes = 8 +
22505e7d8541SAndrew Gallatin 		sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4);
2251b2fc195eSAndrew Gallatin 	sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
2252b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes == NULL)
2253b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2254b2fc195eSAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
2255b2fc195eSAndrew Gallatin 	sc->tx.req_list = (mcp_kreq_ether_send_t *)
2256b2fc195eSAndrew Gallatin 		((unsigned long)(sc->tx.req_bytes + 7) & ~7UL);
2257b2fc195eSAndrew Gallatin 
2258aed8e389SAndrew Gallatin 	/* allocate the tx busdma segment list */
2259aed8e389SAndrew Gallatin 	bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC;
2260aed8e389SAndrew Gallatin 	sc->tx.seg_list = (bus_dma_segment_t *)
2261aed8e389SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
2262aed8e389SAndrew Gallatin 	if (sc->tx.seg_list == NULL)
2263aed8e389SAndrew Gallatin 		goto abort_with_alloc;
2264aed8e389SAndrew Gallatin 
2265b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
2266b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow);
2267b2fc195eSAndrew Gallatin 	sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2268b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow == NULL)
2269b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2270b2fc195eSAndrew Gallatin 
2271b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow);
2272b2fc195eSAndrew Gallatin 	sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2273b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow == NULL)
2274b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2275b2fc195eSAndrew Gallatin 
2276b2fc195eSAndrew Gallatin 	/* allocate the host info rings */
2277b2fc195eSAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*sc->tx.info);
2278b2fc195eSAndrew Gallatin 	sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2279b2fc195eSAndrew Gallatin 	if (sc->tx.info == NULL)
2280b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2281b2fc195eSAndrew Gallatin 
2282b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.info);
2283b2fc195eSAndrew Gallatin 	sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2284b2fc195eSAndrew Gallatin 	if (sc->rx_small.info == NULL)
2285b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2286b2fc195eSAndrew Gallatin 
2287b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.info);
2288b2fc195eSAndrew Gallatin 	sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2289b2fc195eSAndrew Gallatin 	if (sc->rx_big.info == NULL)
2290b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2291b2fc195eSAndrew Gallatin 
2292b2fc195eSAndrew Gallatin 	/* allocate the busdma resources */
2293b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2294b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2295b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* boundary */
2296b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2297b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2298b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2299aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
2300aed8e389SAndrew Gallatin 				 MXGE_MAX_SEND_DESC/2,	/* num segs */
2301b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* maxsegsize */
2302b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2303b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2304b2fc195eSAndrew Gallatin 				 &sc->tx.dmat);		/* tag */
2305b2fc195eSAndrew Gallatin 
2306b2fc195eSAndrew Gallatin 	if (err != 0) {
2307b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
2308b2fc195eSAndrew Gallatin 			      err);
2309b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2310b2fc195eSAndrew Gallatin 	}
2311b2fc195eSAndrew Gallatin 
2312b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2313b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2314b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2315b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2316b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2317b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2318b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2319b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2320b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2321b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2322b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2323b2fc195eSAndrew Gallatin 				 &sc->rx_small.dmat);	/* tag */
2324b2fc195eSAndrew Gallatin 	if (err != 0) {
2325b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2326b2fc195eSAndrew Gallatin 			      err);
2327b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2328b2fc195eSAndrew Gallatin 	}
2329b2fc195eSAndrew Gallatin 
2330b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2331b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2332b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2333b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2334b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2335b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2336b2fc195eSAndrew Gallatin 				 4096,			/* maxsize */
2337b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2338b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2339b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2340b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2341b2fc195eSAndrew Gallatin 				 &sc->rx_big.dmat);	/* tag */
2342b2fc195eSAndrew Gallatin 	if (err != 0) {
2343b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2344b2fc195eSAndrew Gallatin 			      err);
2345b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2346b2fc195eSAndrew Gallatin 	}
2347b2fc195eSAndrew Gallatin 
2348b2fc195eSAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
2349b2fc195eSAndrew Gallatin 	   in each ring */
2350b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2351b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->tx.dmat, 0,
2352b2fc195eSAndrew Gallatin 					&sc->tx.info[i].map);
2353b2fc195eSAndrew Gallatin 		if (err != 0) {
2354b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
2355b2fc195eSAndrew Gallatin 			      err);
2356b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2357b2fc195eSAndrew Gallatin 		}
2358b2fc195eSAndrew Gallatin 	}
2359b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2360b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_small.dmat, 0,
2361b2fc195eSAndrew Gallatin 					&sc->rx_small.info[i].map);
2362b2fc195eSAndrew Gallatin 		if (err != 0) {
2363b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2364b2fc195eSAndrew Gallatin 				      err);
2365b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2366b2fc195eSAndrew Gallatin 		}
2367b2fc195eSAndrew Gallatin 	}
2368b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_small.dmat, 0,
2369b2fc195eSAndrew Gallatin 				&sc->rx_small.extra_map);
2370b2fc195eSAndrew Gallatin 	if (err != 0) {
2371b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2372b2fc195eSAndrew Gallatin 			      err);
2373b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2374b2fc195eSAndrew Gallatin 	}
2375b2fc195eSAndrew Gallatin 
2376b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2377b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_big.dmat, 0,
2378b2fc195eSAndrew Gallatin 					&sc->rx_big.info[i].map);
2379b2fc195eSAndrew Gallatin 		if (err != 0) {
2380b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2381b2fc195eSAndrew Gallatin 			      err);
2382b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2383b2fc195eSAndrew Gallatin 		}
2384b2fc195eSAndrew Gallatin 	}
2385b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_big.dmat, 0,
2386b2fc195eSAndrew Gallatin 				&sc->rx_big.extra_map);
2387b2fc195eSAndrew Gallatin 	if (err != 0) {
2388b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2389b2fc195eSAndrew Gallatin 			      err);
2390b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2391b2fc195eSAndrew Gallatin 	}
2392b2fc195eSAndrew Gallatin 	return 0;
2393b2fc195eSAndrew Gallatin 
2394b2fc195eSAndrew Gallatin abort_with_alloc:
23956d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2396b2fc195eSAndrew Gallatin 
2397b2fc195eSAndrew Gallatin abort_with_nothing:
2398b2fc195eSAndrew Gallatin 	return err;
2399b2fc195eSAndrew Gallatin }
2400b2fc195eSAndrew Gallatin 
2401b2fc195eSAndrew Gallatin static int
24026d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc)
2403b2fc195eSAndrew Gallatin {
24046d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2405b2fc195eSAndrew Gallatin 	int i, err;
2406b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
24070fa7f681SAndrew Gallatin 	bus_addr_t bus;
2408b2fc195eSAndrew Gallatin 
2409b2fc195eSAndrew Gallatin 
24107d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
24117d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
24127d542e2dSAndrew Gallatin 
24136d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2414b2fc195eSAndrew Gallatin 	if (err != 0) {
2415b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
2416b2fc195eSAndrew Gallatin 		return EIO;
2417b2fc195eSAndrew Gallatin 	}
2418a98d6cd7SAndrew Gallatin 	bzero(sc->rx_done.entry,
2419a98d6cd7SAndrew Gallatin 	      mxge_max_intr_slots * sizeof(*sc->rx_done.entry));
2420b2fc195eSAndrew Gallatin 
2421b2fc195eSAndrew Gallatin 	if (MCLBYTES >=
24225e7d8541SAndrew Gallatin 	    sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD)
2423b2fc195eSAndrew Gallatin 		sc->big_bytes = MCLBYTES;
2424b2fc195eSAndrew Gallatin 	else
2425b2fc195eSAndrew Gallatin 		sc->big_bytes = MJUMPAGESIZE;
2426b2fc195eSAndrew Gallatin 
2427b2fc195eSAndrew Gallatin 
2428b2fc195eSAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
2429b2fc195eSAndrew Gallatin 
24305e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
2431b2fc195eSAndrew Gallatin 	sc->tx.lanai =
2432b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
24336d87a65dSAndrew Gallatin 	err |= mxge_send_cmd(sc,
24345e7d8541SAndrew Gallatin 				 MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
2435b2fc195eSAndrew Gallatin 	sc->rx_small.lanai =
2436b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
24375e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
2438b2fc195eSAndrew Gallatin 	sc->rx_big.lanai =
2439b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
2440b2fc195eSAndrew Gallatin 
2441b2fc195eSAndrew Gallatin 	if (err != 0) {
2442b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
2443b2fc195eSAndrew Gallatin 			      "failed to get ring sizes or locations\n");
2444a98d6cd7SAndrew Gallatin 		return EIO;
2445b2fc195eSAndrew Gallatin 	}
2446b2fc195eSAndrew Gallatin 
2447b2fc195eSAndrew Gallatin 	if (sc->wc) {
24480fa7f681SAndrew Gallatin 		sc->tx.wc_fifo = sc->sram + MXGEFW_ETH_SEND_4;
24490fa7f681SAndrew Gallatin 		sc->rx_small.wc_fifo = sc->sram + MXGEFW_ETH_RECV_SMALL;
24500fa7f681SAndrew Gallatin 		sc->rx_big.wc_fifo = sc->sram + MXGEFW_ETH_RECV_BIG;
2451b2fc195eSAndrew Gallatin 	} else {
2452b2fc195eSAndrew Gallatin 		sc->tx.wc_fifo = 0;
2453b2fc195eSAndrew Gallatin 		sc->rx_small.wc_fifo = 0;
2454b2fc195eSAndrew Gallatin 		sc->rx_big.wc_fifo = 0;
2455b2fc195eSAndrew Gallatin 	}
2456b2fc195eSAndrew Gallatin 
2457b2fc195eSAndrew Gallatin 
2458b2fc195eSAndrew Gallatin 	/* stock receive rings */
2459b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2460b2fc195eSAndrew Gallatin 		map = sc->rx_small.info[i].map;
24616d87a65dSAndrew Gallatin 		err = mxge_get_buf_small(sc, map, i);
2462b2fc195eSAndrew Gallatin 		if (err) {
2463b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
2464b2fc195eSAndrew Gallatin 				      i, sc->rx_small.mask + 1);
2465b2fc195eSAndrew Gallatin 			goto abort;
2466b2fc195eSAndrew Gallatin 		}
2467b2fc195eSAndrew Gallatin 	}
2468b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2469b2fc195eSAndrew Gallatin 		map = sc->rx_big.info[i].map;
24706d87a65dSAndrew Gallatin 		err = mxge_get_buf_big(sc, map, i);
2471b2fc195eSAndrew Gallatin 		if (err) {
2472b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
2473b2fc195eSAndrew Gallatin 				      i, sc->rx_big.mask + 1);
2474b2fc195eSAndrew Gallatin 			goto abort;
2475b2fc195eSAndrew Gallatin 		}
2476b2fc195eSAndrew Gallatin 	}
2477b2fc195eSAndrew Gallatin 
2478b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
2479b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
2480b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
2481b2fc195eSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN;
24825e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
2483b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
24845e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
2485b2fc195eSAndrew Gallatin 			     &cmd);
2486b2fc195eSAndrew Gallatin 	cmd.data0 = sc->big_bytes;
24875e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
24880fa7f681SAndrew Gallatin 
24890fa7f681SAndrew Gallatin 	if (err != 0) {
24900fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
24910fa7f681SAndrew Gallatin 		goto abort;
24920fa7f681SAndrew Gallatin 	}
24930fa7f681SAndrew Gallatin 
2494b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
24956d87a65dSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr);
24966d87a65dSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr);
24970fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
24980fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
24990fa7f681SAndrew Gallatin 
25000fa7f681SAndrew Gallatin 	if (err != 0) {
25010fa7f681SAndrew Gallatin 		bus = sc->fw_stats_dma.bus_addr;
25020fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
25030fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
25040fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
25050fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
25060fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
25070fa7f681SAndrew Gallatin 				    &cmd);
25080fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
25090fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
25100fa7f681SAndrew Gallatin 	} else {
25110fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
25120fa7f681SAndrew Gallatin 	}
2513b2fc195eSAndrew Gallatin 
2514b2fc195eSAndrew Gallatin 	if (err != 0) {
2515b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
2516b2fc195eSAndrew Gallatin 		goto abort;
2517b2fc195eSAndrew Gallatin 	}
2518b2fc195eSAndrew Gallatin 
2519b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
25205e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
2521b2fc195eSAndrew Gallatin 	if (err) {
2522b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
2523b2fc195eSAndrew Gallatin 		goto abort;
2524b2fc195eSAndrew Gallatin 	}
2525b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
2526b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2527b2fc195eSAndrew Gallatin 
2528b2fc195eSAndrew Gallatin 	return 0;
2529b2fc195eSAndrew Gallatin 
2530b2fc195eSAndrew Gallatin 
2531b2fc195eSAndrew Gallatin abort:
25326d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2533a98d6cd7SAndrew Gallatin 
2534b2fc195eSAndrew Gallatin 	return err;
2535b2fc195eSAndrew Gallatin }
2536b2fc195eSAndrew Gallatin 
2537b2fc195eSAndrew Gallatin static int
25386d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
2539b2fc195eSAndrew Gallatin {
25406d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2541b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
2542b2fc195eSAndrew Gallatin 
2543b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2544b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
2545b2fc195eSAndrew Gallatin 	mb();
25465e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
2547b2fc195eSAndrew Gallatin 	if (err) {
2548b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
2549b2fc195eSAndrew Gallatin 	}
2550b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2551b2fc195eSAndrew Gallatin 		/* wait for down irq */
2552dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
2553b2fc195eSAndrew Gallatin 	}
2554b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2555b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
2556b2fc195eSAndrew Gallatin 	}
2557a98d6cd7SAndrew Gallatin 
25586d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2559a98d6cd7SAndrew Gallatin 
2560b2fc195eSAndrew Gallatin 	return 0;
2561b2fc195eSAndrew Gallatin }
2562b2fc195eSAndrew Gallatin 
2563dce01b9bSAndrew Gallatin static void
2564dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
2565dce01b9bSAndrew Gallatin {
2566dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2567dce01b9bSAndrew Gallatin 	int reg;
2568dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
2569dce01b9bSAndrew Gallatin 
2570dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
2571dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
2572dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
2573dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
2574dce01b9bSAndrew Gallatin 
2575dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
2576dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
2577dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
2578dce01b9bSAndrew Gallatin 	}
2579dce01b9bSAndrew Gallatin 
2580dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
2581dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
2582dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2583dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
2584dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2585dce01b9bSAndrew Gallatin }
2586dce01b9bSAndrew Gallatin 
2587dce01b9bSAndrew Gallatin static uint32_t
2588dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
2589dce01b9bSAndrew Gallatin {
2590dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2591dce01b9bSAndrew Gallatin 	uint32_t vs;
2592dce01b9bSAndrew Gallatin 
2593dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
2594dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
2595dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
2596dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
2597dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
2598dce01b9bSAndrew Gallatin 	}
2599dce01b9bSAndrew Gallatin 	/* enable read32 mode */
2600dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
2601dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
2602dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
2603dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
2604dce01b9bSAndrew Gallatin }
2605dce01b9bSAndrew Gallatin 
2606dce01b9bSAndrew Gallatin static void
2607dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
2608dce01b9bSAndrew Gallatin {
2609dce01b9bSAndrew Gallatin 	int err;
2610dce01b9bSAndrew Gallatin 	uint32_t reboot;
2611dce01b9bSAndrew Gallatin 	uint16_t cmd;
2612dce01b9bSAndrew Gallatin 
2613dce01b9bSAndrew Gallatin 	err = ENXIO;
2614dce01b9bSAndrew Gallatin 
2615dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
2616dce01b9bSAndrew Gallatin 
2617dce01b9bSAndrew Gallatin 	/*
2618dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
2619dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
2620dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
2621dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
2622dce01b9bSAndrew Gallatin 	 * again
2623dce01b9bSAndrew Gallatin 	 */
2624dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2625dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
2626dce01b9bSAndrew Gallatin 		/*
2627dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
2628dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
2629dce01b9bSAndrew Gallatin 		 * back, then give up
2630dce01b9bSAndrew Gallatin 		 */
2631dce01b9bSAndrew Gallatin 		DELAY(1000*100);
2632dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2633dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
2634dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
2635dce01b9bSAndrew Gallatin 			goto abort;
2636dce01b9bSAndrew Gallatin 		}
2637dce01b9bSAndrew Gallatin 	}
2638dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
2639dce01b9bSAndrew Gallatin 		/* print the reboot status */
2640dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
2641dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
2642dce01b9bSAndrew Gallatin 			      reboot);
2643dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
2644dce01b9bSAndrew Gallatin 
2645dce01b9bSAndrew Gallatin 		/* XXXX waiting for pci_cfg_restore() to be exported */
2646dce01b9bSAndrew Gallatin 		goto abort; /* just abort for now */
2647dce01b9bSAndrew Gallatin 
2648dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
2649dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
2650dce01b9bSAndrew Gallatin 	} else {
2651dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC did not reboot, ring state:\n");
2652dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "tx.req=%d tx.done=%d\n",
2653dce01b9bSAndrew Gallatin 			      sc->tx.req, sc->tx.done);
2654dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
2655dce01b9bSAndrew Gallatin 			      sc->tx.pkt_done,
2656dce01b9bSAndrew Gallatin 			      be32toh(sc->fw_stats->send_done_count));
2657dce01b9bSAndrew Gallatin 	}
2658dce01b9bSAndrew Gallatin 
2659dce01b9bSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
2660dce01b9bSAndrew Gallatin 		mxge_close(sc);
2661dce01b9bSAndrew Gallatin 		err = mxge_open(sc);
2662dce01b9bSAndrew Gallatin 	}
2663dce01b9bSAndrew Gallatin 
2664dce01b9bSAndrew Gallatin abort:
2665dce01b9bSAndrew Gallatin 	/*
2666dce01b9bSAndrew Gallatin 	 * stop the watchdog if the nic is dead, to avoid spamming the
2667dce01b9bSAndrew Gallatin 	 * console
2668dce01b9bSAndrew Gallatin 	 */
2669dce01b9bSAndrew Gallatin 	if (err != 0) {
2670dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
2671dce01b9bSAndrew Gallatin 	}
2672dce01b9bSAndrew Gallatin }
2673dce01b9bSAndrew Gallatin 
2674dce01b9bSAndrew Gallatin static void
2675dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
2676dce01b9bSAndrew Gallatin {
2677dce01b9bSAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
2678dce01b9bSAndrew Gallatin 
2679dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
2680dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
2681dce01b9bSAndrew Gallatin 	if (tx->req != tx->done &&
2682dce01b9bSAndrew Gallatin 	    tx->watchdog_req != tx->watchdog_done &&
2683dce01b9bSAndrew Gallatin 	    tx->done == tx->watchdog_done)
2684dce01b9bSAndrew Gallatin 		mxge_watchdog_reset(sc);
2685dce01b9bSAndrew Gallatin 
2686dce01b9bSAndrew Gallatin 	tx->watchdog_req = tx->req;
2687dce01b9bSAndrew Gallatin 	tx->watchdog_done = tx->done;
2688dce01b9bSAndrew Gallatin }
2689dce01b9bSAndrew Gallatin 
2690dce01b9bSAndrew Gallatin static void
2691dce01b9bSAndrew Gallatin mxge_tick(void *arg)
2692dce01b9bSAndrew Gallatin {
2693dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
2694dce01b9bSAndrew Gallatin 
2695dce01b9bSAndrew Gallatin 
2696dce01b9bSAndrew Gallatin 	/* Synchronize with possible callout reset/stop. */
2697dce01b9bSAndrew Gallatin 	if (callout_pending(&sc->co_hdl) ||
2698dce01b9bSAndrew Gallatin 	    !callout_active(&sc->co_hdl)) {
2699dce01b9bSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2700dce01b9bSAndrew Gallatin 		return;
2701dce01b9bSAndrew Gallatin 	}
2702dce01b9bSAndrew Gallatin 
2703dce01b9bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
2704dce01b9bSAndrew Gallatin 	mxge_watchdog(sc);
2705dce01b9bSAndrew Gallatin }
2706b2fc195eSAndrew Gallatin 
2707b2fc195eSAndrew Gallatin static int
27086d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
2709b2fc195eSAndrew Gallatin {
2710b2fc195eSAndrew Gallatin 	return EINVAL;
2711b2fc195eSAndrew Gallatin }
2712b2fc195eSAndrew Gallatin 
2713b2fc195eSAndrew Gallatin static int
27146d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
2715b2fc195eSAndrew Gallatin {
2716b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
2717b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
2718b2fc195eSAndrew Gallatin 	int err = 0;
2719b2fc195eSAndrew Gallatin 
2720b2fc195eSAndrew Gallatin 
2721b2fc195eSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN;
27226d87a65dSAndrew Gallatin 	if ((real_mtu > MXGE_MAX_ETHER_MTU) ||
2723b2fc195eSAndrew Gallatin 	    real_mtu < 60)
2724b2fc195eSAndrew Gallatin 		return EINVAL;
2725a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
2726b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
2727b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
2728b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
2729dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
27306d87a65dSAndrew Gallatin 		mxge_close(sc);
27316d87a65dSAndrew Gallatin 		err = mxge_open(sc);
2732b2fc195eSAndrew Gallatin 		if (err != 0) {
2733b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
27346d87a65dSAndrew Gallatin 			mxge_close(sc);
27356d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
2736b2fc195eSAndrew Gallatin 		}
2737dce01b9bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
2738b2fc195eSAndrew Gallatin 	}
2739a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
2740b2fc195eSAndrew Gallatin 	return err;
2741b2fc195eSAndrew Gallatin }
2742b2fc195eSAndrew Gallatin 
2743b2fc195eSAndrew Gallatin static void
27446d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
2745b2fc195eSAndrew Gallatin {
27466d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2747b2fc195eSAndrew Gallatin 
2748b2fc195eSAndrew Gallatin 
2749b2fc195eSAndrew Gallatin 	if (sc == NULL)
2750b2fc195eSAndrew Gallatin 		return;
2751b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
2752b2fc195eSAndrew Gallatin 	ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0;
2753b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
2754b2fc195eSAndrew Gallatin 	ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0;
2755b2fc195eSAndrew Gallatin }
2756b2fc195eSAndrew Gallatin 
2757b2fc195eSAndrew Gallatin static int
27586d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
2759b2fc195eSAndrew Gallatin {
27606d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2761b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
2762b2fc195eSAndrew Gallatin 	int err, mask;
2763b2fc195eSAndrew Gallatin 
2764b2fc195eSAndrew Gallatin 	err = 0;
2765b2fc195eSAndrew Gallatin 	switch (command) {
2766b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
2767b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
2768b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
2769b2fc195eSAndrew Gallatin 		break;
2770b2fc195eSAndrew Gallatin 
2771b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
27726d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
2773b2fc195eSAndrew Gallatin 		break;
2774b2fc195eSAndrew Gallatin 
2775b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
2776a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
2777b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
2778dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
27796d87a65dSAndrew Gallatin 				err = mxge_open(sc);
2780dce01b9bSAndrew Gallatin 				callout_reset(&sc->co_hdl, mxge_ticks,
2781dce01b9bSAndrew Gallatin 					      mxge_tick, sc);
2782dce01b9bSAndrew Gallatin 			} else {
27830fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
27840fa7f681SAndrew Gallatin 				   flag chages */
27850fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
27860fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
27870fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
27880fa7f681SAndrew Gallatin 			}
2789b2fc195eSAndrew Gallatin 		} else {
2790dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
27916d87a65dSAndrew Gallatin 				mxge_close(sc);
2792dce01b9bSAndrew Gallatin 				callout_stop(&sc->co_hdl);
2793dce01b9bSAndrew Gallatin 			}
2794b2fc195eSAndrew Gallatin 		}
2795a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2796b2fc195eSAndrew Gallatin 		break;
2797b2fc195eSAndrew Gallatin 
2798b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
2799b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
2800a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
28010fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
2802a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2803b2fc195eSAndrew Gallatin 		break;
2804b2fc195eSAndrew Gallatin 
2805b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
2806a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
2807b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
2808b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
2809b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
2810aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
2811aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
2812aed8e389SAndrew Gallatin 						      | CSUM_TSO);
2813b2fc195eSAndrew Gallatin 			} else {
2814b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
2815b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
2816b2fc195eSAndrew Gallatin 			}
2817b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
2818b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
2819b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
28205e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
2821b2fc195eSAndrew Gallatin 			} else {
2822b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
28235e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
2824b2fc195eSAndrew Gallatin 			}
2825b2fc195eSAndrew Gallatin 		}
2826aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
2827aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
2828aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
2829aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
2830aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
2831aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
2832aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
2833aed8e389SAndrew Gallatin 			} else {
2834aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
2835aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
2836aed8e389SAndrew Gallatin 				err = EINVAL;
2837aed8e389SAndrew Gallatin 			}
2838aed8e389SAndrew Gallatin 		}
2839a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2840b2fc195eSAndrew Gallatin 		break;
2841b2fc195eSAndrew Gallatin 
2842b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
2843b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
2844b2fc195eSAndrew Gallatin 				    &sc->media, command);
2845b2fc195eSAndrew Gallatin                 break;
2846b2fc195eSAndrew Gallatin 
2847b2fc195eSAndrew Gallatin 	default:
2848b2fc195eSAndrew Gallatin 		err = ENOTTY;
2849b2fc195eSAndrew Gallatin         }
2850b2fc195eSAndrew Gallatin 	return err;
2851b2fc195eSAndrew Gallatin }
2852b2fc195eSAndrew Gallatin 
2853b2fc195eSAndrew Gallatin static void
28546d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
2855b2fc195eSAndrew Gallatin {
2856b2fc195eSAndrew Gallatin 
28576d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
28586d87a65dSAndrew Gallatin 			  &mxge_flow_control);
28596d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
28606d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
28616d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
28626d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
2863d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
2864d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
28655e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
28665e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
28675e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
28685e7d8541SAndrew Gallatin 			  &mxge_verbose);
2869dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
2870b2fc195eSAndrew Gallatin 
28715e7d8541SAndrew Gallatin 	if (bootverbose)
28725e7d8541SAndrew Gallatin 		mxge_verbose = 1;
28736d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
28746d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
2875dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
2876dce01b9bSAndrew Gallatin 		mxge_ticks = hz;
28776d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
2878b2fc195eSAndrew Gallatin }
2879b2fc195eSAndrew Gallatin 
2880b2fc195eSAndrew Gallatin static int
28816d87a65dSAndrew Gallatin mxge_attach(device_t dev)
2882b2fc195eSAndrew Gallatin {
28836d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
2884b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2885b2fc195eSAndrew Gallatin 	size_t bytes;
2886dce01b9bSAndrew Gallatin 	int count, rid, err;
2887b2fc195eSAndrew Gallatin 
2888b2fc195eSAndrew Gallatin 	sc->dev = dev;
28896d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
2890b2fc195eSAndrew Gallatin 
2891b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
2892b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2893b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2894b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2895b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2896b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2897aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
28985e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
2899b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2900b2fc195eSAndrew Gallatin 				 0,			/* flags */
2901b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2902b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
2903b2fc195eSAndrew Gallatin 
2904b2fc195eSAndrew Gallatin 	if (err != 0) {
2905b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
2906b2fc195eSAndrew Gallatin 			      err);
2907b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2908b2fc195eSAndrew Gallatin 	}
2909b2fc195eSAndrew Gallatin 
2910b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
2911b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
2912b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
2913b2fc195eSAndrew Gallatin 		err = ENOSPC;
2914b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
2915b2fc195eSAndrew Gallatin 	}
2916a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
2917a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
2918a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
2919a98d6cd7SAndrew Gallatin 	snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx",
2920a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
2921a98d6cd7SAndrew Gallatin 	mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF);
2922a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
2923a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
2924a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
2925b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
2926b2fc195eSAndrew Gallatin 
2927dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
2928d91b1b49SAndrew Gallatin 
2929dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
2930b2fc195eSAndrew Gallatin 
2931b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
2932b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
2933b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
2934b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
2935b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
2936b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
2937b2fc195eSAndrew Gallatin 		err = ENXIO;
2938b2fc195eSAndrew Gallatin 		goto abort_with_lock;
2939b2fc195eSAndrew Gallatin 	}
2940b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
2941b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
2942b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
2943b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
2944b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
2945b2fc195eSAndrew Gallatin 		err = ENXIO;
2946b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2947b2fc195eSAndrew Gallatin 	}
2948b2fc195eSAndrew Gallatin 
2949b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
2950b2fc195eSAndrew Gallatin 	   lanai SRAM */
29516d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
2952b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
2953b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
29546d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
2955b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
29566d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
29576d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
2958b2fc195eSAndrew Gallatin 	if (err != 0)
2959b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2960b2fc195eSAndrew Gallatin 
2961b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
29626d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
2963b2fc195eSAndrew Gallatin 
2964b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
29656d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
29666d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
2967b2fc195eSAndrew Gallatin 	if (err != 0)
2968b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2969b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
29706d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
2971b2fc195eSAndrew Gallatin 	if (err != 0)
2972b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
2973b2fc195eSAndrew Gallatin 
29746d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->fw_stats_dma,
2975b2fc195eSAndrew Gallatin 			     sizeof (*sc->fw_stats), 64);
2976b2fc195eSAndrew Gallatin 	if (err != 0)
2977b2fc195eSAndrew Gallatin 		goto abort_with_zeropad_dma;
29785e7d8541SAndrew Gallatin 	sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr;
2979b2fc195eSAndrew Gallatin 
2980a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
2981a98d6cd7SAndrew Gallatin 	if (err != 0)
2982a98d6cd7SAndrew Gallatin 		goto abort_with_fw_stats;
2983b2fc195eSAndrew Gallatin 
2984b2fc195eSAndrew Gallatin 	/* allocate interrupt queues */
29855e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);
29865e7d8541SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096);
2987b2fc195eSAndrew Gallatin 	if (err != 0)
2988a98d6cd7SAndrew Gallatin 		goto abort_with_dmabench;
29895e7d8541SAndrew Gallatin 	sc->rx_done.entry = sc->rx_done.dma.addr;
29905e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
2991dc8731d4SAndrew Gallatin 
2992b2fc195eSAndrew Gallatin 	/* Add our ithread  */
2993dc8731d4SAndrew Gallatin 	count = pci_msi_count(dev);
2994dc8731d4SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(dev, &count) == 0) {
2995dc8731d4SAndrew Gallatin 		rid = 1;
2996dc8731d4SAndrew Gallatin 		sc->msi_enabled = 1;
2997dc8731d4SAndrew Gallatin 	} else {
2998b2fc195eSAndrew Gallatin 		rid = 0;
2999dc8731d4SAndrew Gallatin 	}
3000b2fc195eSAndrew Gallatin 	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0,
3001b2fc195eSAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
3002b2fc195eSAndrew Gallatin 	if (sc->irq_res == NULL) {
3003b2fc195eSAndrew Gallatin 		device_printf(dev, "could not alloc interrupt\n");
30045e7d8541SAndrew Gallatin 		goto abort_with_rx_done;
3005b2fc195eSAndrew Gallatin 	}
3006d91b1b49SAndrew Gallatin 	if (mxge_verbose)
3007dc8731d4SAndrew Gallatin 		device_printf(dev, "using %s irq %ld\n",
3008dc8731d4SAndrew Gallatin 			      sc->msi_enabled ? "MSI" : "INTx",
3009dc8731d4SAndrew Gallatin 			      rman_get_start(sc->irq_res));
3010b2fc195eSAndrew Gallatin 	/* load the firmware */
30116d87a65dSAndrew Gallatin 	mxge_select_firmware(sc);
3012b2fc195eSAndrew Gallatin 
30136d87a65dSAndrew Gallatin 	err = mxge_load_firmware(sc);
3014b2fc195eSAndrew Gallatin 	if (err != 0)
3015b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
30165e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
30176d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
3018b2fc195eSAndrew Gallatin 	if (err != 0)
3019b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
3020b2fc195eSAndrew Gallatin 
3021a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
3022a98d6cd7SAndrew Gallatin 	if (err != 0) {
3023a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
3024a98d6cd7SAndrew Gallatin 		goto abort_with_irq_res;
3025a98d6cd7SAndrew Gallatin 	}
3026a98d6cd7SAndrew Gallatin 
3027a98d6cd7SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
3028a98d6cd7SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
3029ef544f63SPaolo Pisati 			     NULL, mxge_intr, sc, &sc->ih);
3030a98d6cd7SAndrew Gallatin 	if (err != 0) {
3031a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
3032a98d6cd7SAndrew Gallatin 	}
3033b2fc195eSAndrew Gallatin 	/* hook into the network stack */
3034b2fc195eSAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
3035b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
3036a82c2581SAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
3037a82c2581SAndrew Gallatin 		IFCAP_JUMBO_MTU;
3038aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
3039b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
30405e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
30416d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
3042b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
3043b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
30446d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
30456d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
3046b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
3047b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
30486d87a65dSAndrew Gallatin 	ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN;
3049b2fc195eSAndrew Gallatin 
3050b2fc195eSAndrew Gallatin 	/* Initialise the ifmedia structure */
30516d87a65dSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
30526d87a65dSAndrew Gallatin 		     mxge_media_status);
3053b2fc195eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
30546d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
3055b2fc195eSAndrew Gallatin 	return 0;
3056b2fc195eSAndrew Gallatin 
3057a98d6cd7SAndrew Gallatin abort_with_rings:
3058a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3059b2fc195eSAndrew Gallatin abort_with_irq_res:
3060dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3061dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3062dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3063dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
30645e7d8541SAndrew Gallatin abort_with_rx_done:
30655e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
30665e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
3067a98d6cd7SAndrew Gallatin abort_with_dmabench:
3068a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
30695e7d8541SAndrew Gallatin abort_with_fw_stats:
30706d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3071b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
30726d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
3073b2fc195eSAndrew Gallatin abort_with_cmd_dma:
30746d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3075b2fc195eSAndrew Gallatin abort_with_mem_res:
3076b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3077b2fc195eSAndrew Gallatin abort_with_lock:
3078b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3079a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3080a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3081a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3082b2fc195eSAndrew Gallatin 	if_free(ifp);
3083b2fc195eSAndrew Gallatin abort_with_parent_dmat:
3084b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3085b2fc195eSAndrew Gallatin 
3086b2fc195eSAndrew Gallatin abort_with_nothing:
3087b2fc195eSAndrew Gallatin 	return err;
3088b2fc195eSAndrew Gallatin }
3089b2fc195eSAndrew Gallatin 
3090b2fc195eSAndrew Gallatin static int
30916d87a65dSAndrew Gallatin mxge_detach(device_t dev)
3092b2fc195eSAndrew Gallatin {
30936d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
3094b2fc195eSAndrew Gallatin 
3095a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3096b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
30976d87a65dSAndrew Gallatin 		mxge_close(sc);
3098dce01b9bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3099a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3100b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
3101dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
3102091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
3103a98d6cd7SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
3104a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3105dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3106dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3107dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3108dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
3109dc8731d4SAndrew Gallatin 
31105e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
31115e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
31126d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3113a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
31146d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
31156d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3116b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3117b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3118a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3119a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3120a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3121b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
3122b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3123b2fc195eSAndrew Gallatin 	return 0;
3124b2fc195eSAndrew Gallatin }
3125b2fc195eSAndrew Gallatin 
3126b2fc195eSAndrew Gallatin static int
31276d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
3128b2fc195eSAndrew Gallatin {
3129b2fc195eSAndrew Gallatin 	return 0;
3130b2fc195eSAndrew Gallatin }
3131b2fc195eSAndrew Gallatin 
3132b2fc195eSAndrew Gallatin /*
3133b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
3134b2fc195eSAndrew Gallatin 
3135b2fc195eSAndrew Gallatin   Local Variables:
3136b2fc195eSAndrew Gallatin   c-file-style:"linux"
3137b2fc195eSAndrew Gallatin   tab-width:8
3138b2fc195eSAndrew Gallatin   End:
3139b2fc195eSAndrew Gallatin */
3140