xref: /freebsd/sys/dev/mxge/if_mxge.c (revision d91b1b49760392937895deb4a511b33f218710fb)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
3b2fc195eSAndrew Gallatin Copyright (c) 2006, Myricom Inc.
4b2fc195eSAndrew Gallatin All rights reserved.
5b2fc195eSAndrew Gallatin 
6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
8b2fc195eSAndrew Gallatin 
9b2fc195eSAndrew Gallatin  1. Redistributions of source code must retain the above copyright notice,
10b2fc195eSAndrew Gallatin     this list of conditions and the following disclaimer.
11b2fc195eSAndrew Gallatin 
12b2fc195eSAndrew Gallatin  2. Redistributions in binary form must reproduce the above copyright
13b2fc195eSAndrew Gallatin     notice, this list of conditions and the following disclaimer in the
14b2fc195eSAndrew Gallatin     documentation and/or other materials provided with the distribution.
15b2fc195eSAndrew Gallatin 
16b2fc195eSAndrew Gallatin  3. Neither the name of the Myricom Inc, nor the names of its
17b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
18b2fc195eSAndrew Gallatin     this software without specific prior written permission.
19b2fc195eSAndrew Gallatin 
20b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
31b2fc195eSAndrew Gallatin 
32b2fc195eSAndrew Gallatin ***************************************************************************/
33b2fc195eSAndrew Gallatin 
34b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
35b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
36b2fc195eSAndrew Gallatin 
37b2fc195eSAndrew Gallatin #include <sys/param.h>
38b2fc195eSAndrew Gallatin #include <sys/systm.h>
39b2fc195eSAndrew Gallatin #include <sys/linker.h>
40b2fc195eSAndrew Gallatin #include <sys/firmware.h>
41b2fc195eSAndrew Gallatin #include <sys/endian.h>
42b2fc195eSAndrew Gallatin #include <sys/sockio.h>
43b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
44b2fc195eSAndrew Gallatin #include <sys/malloc.h>
45b2fc195eSAndrew Gallatin #include <sys/kdb.h>
46b2fc195eSAndrew Gallatin #include <sys/kernel.h>
47b2fc195eSAndrew Gallatin #include <sys/module.h>
48b2fc195eSAndrew Gallatin #include <sys/memrange.h>
49b2fc195eSAndrew Gallatin #include <sys/socket.h>
50b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
51b2fc195eSAndrew Gallatin #include <sys/sx.h>
52b2fc195eSAndrew Gallatin 
53b2fc195eSAndrew Gallatin #include <net/if.h>
54b2fc195eSAndrew Gallatin #include <net/if_arp.h>
55b2fc195eSAndrew Gallatin #include <net/ethernet.h>
56b2fc195eSAndrew Gallatin #include <net/if_dl.h>
57b2fc195eSAndrew Gallatin #include <net/if_media.h>
58b2fc195eSAndrew Gallatin 
59b2fc195eSAndrew Gallatin #include <net/bpf.h>
60b2fc195eSAndrew Gallatin 
61b2fc195eSAndrew Gallatin #include <net/if_types.h>
62b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
63b2fc195eSAndrew Gallatin #include <net/zlib.h>
64b2fc195eSAndrew Gallatin 
65b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
66b2fc195eSAndrew Gallatin #include <netinet/in.h>
67b2fc195eSAndrew Gallatin #include <netinet/ip.h>
68aed8e389SAndrew Gallatin #include <netinet/tcp.h>
69b2fc195eSAndrew Gallatin 
70b2fc195eSAndrew Gallatin #include <machine/bus.h>
71b2fc195eSAndrew Gallatin #include <machine/resource.h>
72b2fc195eSAndrew Gallatin #include <sys/bus.h>
73b2fc195eSAndrew Gallatin #include <sys/rman.h>
74b2fc195eSAndrew Gallatin 
75b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
76b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
77b2fc195eSAndrew Gallatin 
78b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
79b2fc195eSAndrew Gallatin #include <vm/pmap.h>
80b2fc195eSAndrew Gallatin 
816d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
826d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
836d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
84b2fc195eSAndrew Gallatin 
85b2fc195eSAndrew Gallatin /* tunable params */
866d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
87d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
885e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024;
896d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
905e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
916d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
925e7d8541SAndrew Gallatin static int mxge_verbose = 0;
936d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
946d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
95b2fc195eSAndrew Gallatin 
966d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
976d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
986d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
996d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1006d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
101b2fc195eSAndrew Gallatin 
1026d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
103b2fc195eSAndrew Gallatin {
104b2fc195eSAndrew Gallatin   /* Device interface */
1056d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1066d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1076d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1086d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
109b2fc195eSAndrew Gallatin   {0, 0}
110b2fc195eSAndrew Gallatin };
111b2fc195eSAndrew Gallatin 
1126d87a65dSAndrew Gallatin static driver_t mxge_driver =
113b2fc195eSAndrew Gallatin {
1146d87a65dSAndrew Gallatin   "mxge",
1156d87a65dSAndrew Gallatin   mxge_methods,
1166d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
117b2fc195eSAndrew Gallatin };
118b2fc195eSAndrew Gallatin 
1196d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
120b2fc195eSAndrew Gallatin 
121b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1226d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1236d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
124b2fc195eSAndrew Gallatin 
125b2fc195eSAndrew Gallatin static int
1266d87a65dSAndrew Gallatin mxge_probe(device_t dev)
127b2fc195eSAndrew Gallatin {
1286d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1296d87a65dSAndrew Gallatin       (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) {
130b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
131b2fc195eSAndrew Gallatin 	  return 0;
132b2fc195eSAndrew Gallatin   }
133b2fc195eSAndrew Gallatin   return ENXIO;
134b2fc195eSAndrew Gallatin }
135b2fc195eSAndrew Gallatin 
136b2fc195eSAndrew Gallatin static void
1376d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
138b2fc195eSAndrew Gallatin {
139b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
140b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
141b2fc195eSAndrew Gallatin 	vm_offset_t len;
142b2fc195eSAndrew Gallatin 	int err, action;
143b2fc195eSAndrew Gallatin 
144b2fc195eSAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
145b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
146b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
147b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
148b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
149b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1506d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
151b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
152b2fc195eSAndrew Gallatin 	if (err != 0) {
153b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
154b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
155b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
156b2fc195eSAndrew Gallatin 	} else {
157b2fc195eSAndrew Gallatin 		sc->wc = 1;
158b2fc195eSAndrew Gallatin 	}
159b2fc195eSAndrew Gallatin }
160b2fc195eSAndrew Gallatin 
161b2fc195eSAndrew Gallatin 
162b2fc195eSAndrew Gallatin /* callback to get our DMA address */
163b2fc195eSAndrew Gallatin static void
1646d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
165b2fc195eSAndrew Gallatin 			 int error)
166b2fc195eSAndrew Gallatin {
167b2fc195eSAndrew Gallatin 	if (error == 0) {
168b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
169b2fc195eSAndrew Gallatin 	}
170b2fc195eSAndrew Gallatin }
171b2fc195eSAndrew Gallatin 
172b2fc195eSAndrew Gallatin static int
1736d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
174b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
175b2fc195eSAndrew Gallatin {
176b2fc195eSAndrew Gallatin 	int err;
177b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
178b2fc195eSAndrew Gallatin 
179b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
180b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
181b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
182b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
183b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
184b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
185b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
186b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
187b2fc195eSAndrew Gallatin 				 1,			/* num segs */
188b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
189b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
190b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
191b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
192b2fc195eSAndrew Gallatin 	if (err != 0) {
193b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
194b2fc195eSAndrew Gallatin 		return err;
195b2fc195eSAndrew Gallatin 	}
196b2fc195eSAndrew Gallatin 
197b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
198b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
199b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
200b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
201b2fc195eSAndrew Gallatin 	if (err != 0) {
202b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
203b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
204b2fc195eSAndrew Gallatin 	}
205b2fc195eSAndrew Gallatin 
206b2fc195eSAndrew Gallatin 	/* load the memory */
207b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2086d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
209b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
210b2fc195eSAndrew Gallatin 	if (err != 0) {
211b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
212b2fc195eSAndrew Gallatin 		goto abort_with_mem;
213b2fc195eSAndrew Gallatin 	}
214b2fc195eSAndrew Gallatin 	return 0;
215b2fc195eSAndrew Gallatin 
216b2fc195eSAndrew Gallatin abort_with_mem:
217b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
218b2fc195eSAndrew Gallatin abort_with_dmat:
219b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
220b2fc195eSAndrew Gallatin 	return err;
221b2fc195eSAndrew Gallatin }
222b2fc195eSAndrew Gallatin 
223b2fc195eSAndrew Gallatin 
224b2fc195eSAndrew Gallatin static void
2256d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
226b2fc195eSAndrew Gallatin {
227b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
228b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
229b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
230b2fc195eSAndrew Gallatin }
231b2fc195eSAndrew Gallatin 
232b2fc195eSAndrew Gallatin /*
233b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
234b2fc195eSAndrew Gallatin  * SN=x\0
235b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
236b2fc195eSAndrew Gallatin  * PC=text\0
237b2fc195eSAndrew Gallatin  */
238b2fc195eSAndrew Gallatin 
239b2fc195eSAndrew Gallatin static int
2406d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
241b2fc195eSAndrew Gallatin {
2426d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
243b2fc195eSAndrew Gallatin 
244b2fc195eSAndrew Gallatin 	char *ptr, *limit;
245b2fc195eSAndrew Gallatin 	int i, found_mac;
246b2fc195eSAndrew Gallatin 
247b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2486d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
249b2fc195eSAndrew Gallatin 	found_mac = 0;
250b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
251b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2525e7d8541SAndrew Gallatin 			ptr += 1;
253b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
254b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2555e7d8541SAndrew Gallatin 				ptr += 3;
256b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
257b2fc195eSAndrew Gallatin 					goto abort;
258b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
259b2fc195eSAndrew Gallatin 				found_mac = 1;
260b2fc195eSAndrew Gallatin 			}
2615e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2625e7d8541SAndrew Gallatin 			ptr += 3;
2635e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2645e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2655e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2665e7d8541SAndrew Gallatin 			ptr += 3;
2675e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2685e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
269b2fc195eSAndrew Gallatin 		}
2706d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
271b2fc195eSAndrew Gallatin 	}
272b2fc195eSAndrew Gallatin 
273b2fc195eSAndrew Gallatin 	if (found_mac)
274b2fc195eSAndrew Gallatin 		return 0;
275b2fc195eSAndrew Gallatin 
276b2fc195eSAndrew Gallatin  abort:
277b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
278b2fc195eSAndrew Gallatin 
279b2fc195eSAndrew Gallatin 	return ENXIO;
280b2fc195eSAndrew Gallatin }
281b2fc195eSAndrew Gallatin 
282b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
283b2fc195eSAndrew Gallatin static int
2846d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
285b2fc195eSAndrew Gallatin {
286b2fc195eSAndrew Gallatin 	uint32_t val;
287b2fc195eSAndrew Gallatin 	unsigned long off;
288b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
289b2fc195eSAndrew Gallatin 	uint16_t vendor_id, device_id;
290b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
291b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
292b2fc195eSAndrew Gallatin 
293b2fc195eSAndrew Gallatin 	/* XXXX
294b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
295b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
296b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
297b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
298b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
299b2fc195eSAndrew Gallatin 	*/
300b2fc195eSAndrew Gallatin #if 0
301b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
302b2fc195eSAndrew Gallatin 	   config space */
303b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
304b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
305b2fc195eSAndrew Gallatin 		val |= 0x40;
306b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
307b2fc195eSAndrew Gallatin 		return 0;
308b2fc195eSAndrew Gallatin 	}
309b2fc195eSAndrew Gallatin #endif
310b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
311b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
312b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
313b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
314b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
315b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
316b2fc195eSAndrew Gallatin 	 */
317b2fc195eSAndrew Gallatin 
318b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
319b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
320b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
321b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
322b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
323b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
324b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
325b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
326b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
327b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
328b2fc195eSAndrew Gallatin 
329b2fc195eSAndrew Gallatin 	off =  0xe0000000UL
330b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
331b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
332b2fc195eSAndrew Gallatin 						 + 8 * slot);
333b2fc195eSAndrew Gallatin 
334b2fc195eSAndrew Gallatin 	/* map it into the kernel */
335b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
336b2fc195eSAndrew Gallatin 
337b2fc195eSAndrew Gallatin 
338b2fc195eSAndrew Gallatin 	if (va == NULL) {
339b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
340b2fc195eSAndrew Gallatin 		return EIO;
341b2fc195eSAndrew Gallatin 	}
342b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
343b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
344b2fc195eSAndrew Gallatin 
345b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
346b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
347b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
348b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
349b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
350b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
351b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
352b2fc195eSAndrew Gallatin 		return EIO;
353b2fc195eSAndrew Gallatin 	}
354b2fc195eSAndrew Gallatin 
355b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
356b2fc195eSAndrew Gallatin 	val = *ptr32;
357b2fc195eSAndrew Gallatin 
358b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
359b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
360b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
361b2fc195eSAndrew Gallatin 		return EIO;
362b2fc195eSAndrew Gallatin 	}
363b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
364b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3655e7d8541SAndrew Gallatin 	if (mxge_verbose)
366b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
3675e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
3685e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
369b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
370b2fc195eSAndrew Gallatin 	return 0;
371b2fc195eSAndrew Gallatin }
372b2fc195eSAndrew Gallatin #else
373b2fc195eSAndrew Gallatin static int
3746d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
375b2fc195eSAndrew Gallatin {
376b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
377b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
378b2fc195eSAndrew Gallatin 	return ENXIO;
379b2fc195eSAndrew Gallatin }
380b2fc195eSAndrew Gallatin #endif
381b2fc195eSAndrew Gallatin /*
382b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
383b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
384b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
385b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
386b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
387b2fc195eSAndrew Gallatin  *
388b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
389b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
390b2fc195eSAndrew Gallatin  *
391b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
392b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
393b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
394b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
395b2fc195eSAndrew Gallatin  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
396b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
397b2fc195eSAndrew Gallatin  * firmware image, and set tx.boundary to 4KB.
398b2fc195eSAndrew Gallatin  */
399b2fc195eSAndrew Gallatin 
400b2fc195eSAndrew Gallatin static void
4016d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
402b2fc195eSAndrew Gallatin {
403b2fc195eSAndrew Gallatin 	int err, aligned = 0;
404b2fc195eSAndrew Gallatin 	device_t pdev;
405b2fc195eSAndrew Gallatin 	uint16_t pvend, pdid;
406b2fc195eSAndrew Gallatin 
407d91b1b49SAndrew Gallatin 
408d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
409d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
410d91b1b49SAndrew Gallatin 			aligned = 1;
411d91b1b49SAndrew Gallatin 		else
412d91b1b49SAndrew Gallatin 			aligned = 0;
413d91b1b49SAndrew Gallatin 		if (mxge_verbose)
414d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
415d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
416d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
417d91b1b49SAndrew Gallatin 		goto abort;
418d91b1b49SAndrew Gallatin 	}
419d91b1b49SAndrew Gallatin 
420d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
421d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
422d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
423d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
424d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
425d91b1b49SAndrew Gallatin 			      sc->link_width);
426d91b1b49SAndrew Gallatin 		aligned = 1;
427d91b1b49SAndrew Gallatin 		goto abort;
428d91b1b49SAndrew Gallatin 	}
429d91b1b49SAndrew Gallatin 
430b2fc195eSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
431b2fc195eSAndrew Gallatin 	if (pdev == NULL) {
432b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
433b2fc195eSAndrew Gallatin 		goto abort;
434b2fc195eSAndrew Gallatin 	}
435b2fc195eSAndrew Gallatin 	pvend = pci_read_config(pdev, PCIR_VENDOR, 2);
436b2fc195eSAndrew Gallatin 	pdid = pci_read_config(pdev, PCIR_DEVICE, 2);
437b2fc195eSAndrew Gallatin 
438b2fc195eSAndrew Gallatin 	/* see if we can enable ECRC's on an upstream
439b2fc195eSAndrew Gallatin 	   Nvidia bridge */
4406d87a65dSAndrew Gallatin 	if (mxge_nvidia_ecrc_enable &&
441b2fc195eSAndrew Gallatin 	    (pvend == 0x10de && pdid == 0x005d)) {
4426d87a65dSAndrew Gallatin 		err = mxge_enable_nvidia_ecrc(sc, pdev);
443b2fc195eSAndrew Gallatin 		if (err == 0) {
444b2fc195eSAndrew Gallatin 			aligned = 1;
4455e7d8541SAndrew Gallatin 			if (mxge_verbose)
446b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
4475e7d8541SAndrew Gallatin 					      "Assuming aligned completions"
4485e7d8541SAndrew Gallatin 					      " (ECRC)\n");
449b2fc195eSAndrew Gallatin 		}
450b2fc195eSAndrew Gallatin 	}
451b2fc195eSAndrew Gallatin 	/* see if the upstream bridge is known to
452b2fc195eSAndrew Gallatin 	   provided aligned completions */
453b2fc195eSAndrew Gallatin 	if (/* HT2000 */ (pvend == 0x1166 && pdid == 0x0132) ||
4540fa7f681SAndrew Gallatin 	    /* PLX */    (pvend == 0x10b5 && pdid == 0x8532) ||
4550fa7f681SAndrew Gallatin 	    /* Intel */  (pvend == 0x8086 &&
456d91b1b49SAndrew Gallatin 	      /* E5000 NorthBridge*/((pdid >= 0x25f7 && pdid <= 0x25fa) ||
457d91b1b49SAndrew Gallatin 	      /* E5000 SouthBridge*/ (pdid >= 0x3510 && pdid <= 0x351b)))) {
458d91b1b49SAndrew Gallatin 		aligned = 1;
4595e7d8541SAndrew Gallatin 		if (mxge_verbose)
460b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
4615e7d8541SAndrew Gallatin 				      "Assuming aligned completions "
4625e7d8541SAndrew Gallatin 				      "(0x%x:0x%x)\n", pvend, pdid);
463b2fc195eSAndrew Gallatin 	}
464b2fc195eSAndrew Gallatin 
465b2fc195eSAndrew Gallatin abort:
466b2fc195eSAndrew Gallatin 	if (aligned) {
4676d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
468b2fc195eSAndrew Gallatin 		sc->tx.boundary = 4096;
469b2fc195eSAndrew Gallatin 	} else {
4706d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
471b2fc195eSAndrew Gallatin 		sc->tx.boundary = 2048;
472b2fc195eSAndrew Gallatin 	}
473b2fc195eSAndrew Gallatin }
474b2fc195eSAndrew Gallatin 
475b2fc195eSAndrew Gallatin union qualhack
476b2fc195eSAndrew Gallatin {
477b2fc195eSAndrew Gallatin         const char *ro_char;
478b2fc195eSAndrew Gallatin         char *rw_char;
479b2fc195eSAndrew Gallatin };
480b2fc195eSAndrew Gallatin 
4814da0d523SAndrew Gallatin static int
4824da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
4834da0d523SAndrew Gallatin {
4844da0d523SAndrew Gallatin 	int major, minor;
4854da0d523SAndrew Gallatin 
4864da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
4874da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
4884da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
4894da0d523SAndrew Gallatin 		return EIO;
4904da0d523SAndrew Gallatin 	}
4914da0d523SAndrew Gallatin 
4924da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
4934da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
4944da0d523SAndrew Gallatin 	if (mxge_verbose)
4954da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
4964da0d523SAndrew Gallatin 
4974da0d523SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d", &major, &minor);
4984da0d523SAndrew Gallatin 
4994da0d523SAndrew Gallatin 	if (!(major == MXGEFW_VERSION_MAJOR
5004da0d523SAndrew Gallatin 	      && minor == MXGEFW_VERSION_MINOR)) {
5014da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
5024da0d523SAndrew Gallatin 			      sc->fw_version);
5034da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
5044da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
5054da0d523SAndrew Gallatin 		return EINVAL;
5064da0d523SAndrew Gallatin 	}
5074da0d523SAndrew Gallatin 	return 0;
5084da0d523SAndrew Gallatin 
5094da0d523SAndrew Gallatin }
510b2fc195eSAndrew Gallatin 
511b2fc195eSAndrew Gallatin static int
5126d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
513b2fc195eSAndrew Gallatin {
514b2fc195eSAndrew Gallatin 	struct firmware *fw;
515b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
516b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
517b2fc195eSAndrew Gallatin 	const char *fw_data;
518b2fc195eSAndrew Gallatin 	union qualhack hack;
519b2fc195eSAndrew Gallatin 	int status;
5204da0d523SAndrew Gallatin 	unsigned int i;
5214da0d523SAndrew Gallatin 	char dummy;
522b2fc195eSAndrew Gallatin 
523b2fc195eSAndrew Gallatin 
524b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
525b2fc195eSAndrew Gallatin 
526b2fc195eSAndrew Gallatin 	if (fw == NULL) {
527b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
528b2fc195eSAndrew Gallatin 			      sc->fw_name);
529b2fc195eSAndrew Gallatin 		return ENOENT;
530b2fc195eSAndrew Gallatin 	}
531b2fc195eSAndrew Gallatin 	if (fw->datasize > *limit ||
532b2fc195eSAndrew Gallatin 	    fw->datasize < MCP_HEADER_PTR_OFFSET + 4) {
533b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n",
534b2fc195eSAndrew Gallatin 			      sc->fw_name, (int)fw->datasize, (int) *limit);
535b2fc195eSAndrew Gallatin 		status = ENOSPC;
536b2fc195eSAndrew Gallatin 		goto abort_with_fw;
537b2fc195eSAndrew Gallatin 	}
538b2fc195eSAndrew Gallatin 	*limit = fw->datasize;
539b2fc195eSAndrew Gallatin 
540b2fc195eSAndrew Gallatin 	/* check id */
541b2fc195eSAndrew Gallatin 	fw_data = (const char *)fw->data;
542b2fc195eSAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
543b2fc195eSAndrew Gallatin 			     (fw_data + MCP_HEADER_PTR_OFFSET));
544b2fc195eSAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) {
545b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
546b2fc195eSAndrew Gallatin 		status = EIO;
547b2fc195eSAndrew Gallatin 		goto abort_with_fw;
548b2fc195eSAndrew Gallatin 	}
549b2fc195eSAndrew Gallatin 	hdr = (const void*)(fw_data + hdr_offset);
550b2fc195eSAndrew Gallatin 
5514da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
5524da0d523SAndrew Gallatin 	if (status != 0)
5534da0d523SAndrew Gallatin 		goto abort_with_fw;
554b2fc195eSAndrew Gallatin 
555b2fc195eSAndrew Gallatin 	hack.ro_char = fw_data;
556b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
5574da0d523SAndrew Gallatin 	for (i = 0; i < *limit; i += 256) {
5584da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
5594da0d523SAndrew Gallatin 			      hack.rw_char + i,
5604da0d523SAndrew Gallatin 			      min(256U, (unsigned)(*limit - i)));
5614da0d523SAndrew Gallatin 		mb();
5624da0d523SAndrew Gallatin 		dummy = *sc->sram;
5634da0d523SAndrew Gallatin 		mb();
5644da0d523SAndrew Gallatin 	}
565b2fc195eSAndrew Gallatin 
566b2fc195eSAndrew Gallatin 	status = 0;
567b2fc195eSAndrew Gallatin abort_with_fw:
568b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
569b2fc195eSAndrew Gallatin 	return status;
570b2fc195eSAndrew Gallatin }
571b2fc195eSAndrew Gallatin 
572b2fc195eSAndrew Gallatin /*
573b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
574b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
575b2fc195eSAndrew Gallatin  */
576b2fc195eSAndrew Gallatin 
577b2fc195eSAndrew Gallatin static void
5786d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
579b2fc195eSAndrew Gallatin {
580b2fc195eSAndrew Gallatin 	char buf_bytes[72];
581b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
582b2fc195eSAndrew Gallatin 	volatile char *submit;
583b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
584b2fc195eSAndrew Gallatin 	int i;
585b2fc195eSAndrew Gallatin 
586b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
587b2fc195eSAndrew Gallatin 
588b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
589b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
590b2fc195eSAndrew Gallatin 	*confirm = 0;
591b2fc195eSAndrew Gallatin 	mb();
592b2fc195eSAndrew Gallatin 
593b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
594b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
595b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
596b2fc195eSAndrew Gallatin 	*/
597b2fc195eSAndrew Gallatin 
5986d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
5996d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
600b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
601b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
602b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
6036d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
6046d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
605b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
606b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
607b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
608b2fc195eSAndrew Gallatin 
609b2fc195eSAndrew Gallatin 
6100fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
611b2fc195eSAndrew Gallatin 
6126d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
613b2fc195eSAndrew Gallatin 	mb();
614b2fc195eSAndrew Gallatin 	DELAY(1000);
615b2fc195eSAndrew Gallatin 	mb();
616b2fc195eSAndrew Gallatin 	i = 0;
617b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
618b2fc195eSAndrew Gallatin 		DELAY(1000);
619b2fc195eSAndrew Gallatin 		i++;
620b2fc195eSAndrew Gallatin 	}
621b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
622b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
623b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
624b2fc195eSAndrew Gallatin 			      *confirm);
625b2fc195eSAndrew Gallatin 	}
626b2fc195eSAndrew Gallatin 	return;
627b2fc195eSAndrew Gallatin }
628b2fc195eSAndrew Gallatin 
629b2fc195eSAndrew Gallatin static int
6306d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
631b2fc195eSAndrew Gallatin {
632b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
633b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
634b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
6350fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
636b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
637b2fc195eSAndrew Gallatin 	int sleep_total = 0;
638b2fc195eSAndrew Gallatin 
639b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
640b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
641b2fc195eSAndrew Gallatin 
642b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
643b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
644b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
645b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
6466d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
6476d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
648b2fc195eSAndrew Gallatin 
649b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
650b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
651b2fc195eSAndrew Gallatin 	mtx_lock(&sc->cmd_lock);
652b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
653b2fc195eSAndrew Gallatin 	mb();
6546d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
655b2fc195eSAndrew Gallatin 
6565e7d8541SAndrew Gallatin 	/* wait up to 20ms */
6575e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
658b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
659b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
660b2fc195eSAndrew Gallatin 		mb();
661b2fc195eSAndrew Gallatin 		if (response->result != 0xffffffff) {
662b2fc195eSAndrew Gallatin 			if (response->result == 0) {
663b2fc195eSAndrew Gallatin 				data->data0 = be32toh(response->data);
664b2fc195eSAndrew Gallatin 				mtx_unlock(&sc->cmd_lock);
665b2fc195eSAndrew Gallatin 				return 0;
666b2fc195eSAndrew Gallatin 			} else {
667b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
6686d87a65dSAndrew Gallatin 					      "mxge: command %d "
669b2fc195eSAndrew Gallatin 					      "failed, result = %d\n",
670b2fc195eSAndrew Gallatin 					      cmd, be32toh(response->result));
671b2fc195eSAndrew Gallatin 				mtx_unlock(&sc->cmd_lock);
672b2fc195eSAndrew Gallatin 				return ENXIO;
673b2fc195eSAndrew Gallatin 			}
674b2fc195eSAndrew Gallatin 		}
6755e7d8541SAndrew Gallatin 		DELAY(1000);
676b2fc195eSAndrew Gallatin 	}
677b2fc195eSAndrew Gallatin 	mtx_unlock(&sc->cmd_lock);
6786d87a65dSAndrew Gallatin 	device_printf(sc->dev, "mxge: command %d timed out"
679b2fc195eSAndrew Gallatin 		      "result = %d\n",
680b2fc195eSAndrew Gallatin 		      cmd, be32toh(response->result));
681b2fc195eSAndrew Gallatin 	return EAGAIN;
682b2fc195eSAndrew Gallatin }
683b2fc195eSAndrew Gallatin 
6844da0d523SAndrew Gallatin static int
6854da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
6864da0d523SAndrew Gallatin {
6874da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
6884da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
6894da0d523SAndrew Gallatin 	size_t hdr_offset;
6904da0d523SAndrew Gallatin 	int status;
6914da0d523SAndrew Gallatin 
6924da0d523SAndrew Gallatin 	/* find running firmware header */
6934da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
6944da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
6954da0d523SAndrew Gallatin 
6964da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
6974da0d523SAndrew Gallatin 		device_printf(sc->dev,
6984da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
6994da0d523SAndrew Gallatin 			      (int)hdr_offset);
7004da0d523SAndrew Gallatin 		return EIO;
7014da0d523SAndrew Gallatin 	}
7024da0d523SAndrew Gallatin 
7034da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
7044da0d523SAndrew Gallatin 	 * validate firmware */
7054da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
7064da0d523SAndrew Gallatin 	if (hdr == NULL) {
7074da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
7084da0d523SAndrew Gallatin 		return ENOMEM;
7094da0d523SAndrew Gallatin 	}
7104da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
7114da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
7124da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
7134da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7144da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
7154da0d523SAndrew Gallatin 	return status;
7164da0d523SAndrew Gallatin }
7174da0d523SAndrew Gallatin 
718b2fc195eSAndrew Gallatin 
719b2fc195eSAndrew Gallatin static int
7206d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc)
721b2fc195eSAndrew Gallatin {
722b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
723b2fc195eSAndrew Gallatin 	volatile char *submit;
724b2fc195eSAndrew Gallatin 	char buf_bytes[72];
725b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
726b2fc195eSAndrew Gallatin 	int status, i;
727b2fc195eSAndrew Gallatin 
728b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
729b2fc195eSAndrew Gallatin 
730b2fc195eSAndrew Gallatin 	size = sc->sram_size;
7316d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
732b2fc195eSAndrew Gallatin 	if (status) {
7334da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
7344da0d523SAndrew Gallatin 		   it is new enough */
7354da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
7364da0d523SAndrew Gallatin 		if (status) {
7374da0d523SAndrew Gallatin 			device_printf(sc->dev,
7384da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
739b2fc195eSAndrew Gallatin 			return status;
740b2fc195eSAndrew Gallatin 		}
7414da0d523SAndrew Gallatin 		device_printf(sc->dev,
7424da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
7434da0d523SAndrew Gallatin 		if (sc->tx.boundary == 4096) {
7444da0d523SAndrew Gallatin 			device_printf(sc->dev,
7454da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
7464da0d523SAndrew Gallatin 				 ".  For optimal\n");
7474da0d523SAndrew Gallatin 			device_printf(sc->dev,
7484da0d523SAndrew Gallatin 				 "performance consider loading optimized "
7494da0d523SAndrew Gallatin 				 "firmware\n");
7504da0d523SAndrew Gallatin 		}
751d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
752d91b1b49SAndrew Gallatin 		sc->tx.boundary = 2048;
753d91b1b49SAndrew Gallatin 		return 0;
7544da0d523SAndrew Gallatin 	}
755b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
756b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
757b2fc195eSAndrew Gallatin 	*confirm = 0;
758b2fc195eSAndrew Gallatin 	mb();
759b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
760b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
761b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
762b2fc195eSAndrew Gallatin 	*/
763b2fc195eSAndrew Gallatin 
7646d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7656d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
766b2fc195eSAndrew Gallatin 
767b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
768b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
769b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
770b2fc195eSAndrew Gallatin 
771b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
772b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
773b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
774b2fc195eSAndrew Gallatin 	*/
775b2fc195eSAndrew Gallatin 					/* where the code starts*/
7766d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
777b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
778b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
779b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
780b2fc195eSAndrew Gallatin 
7810fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
7826d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
783b2fc195eSAndrew Gallatin 	mb();
784b2fc195eSAndrew Gallatin 	DELAY(1000);
785b2fc195eSAndrew Gallatin 	mb();
786b2fc195eSAndrew Gallatin 	i = 0;
787b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
788b2fc195eSAndrew Gallatin 		DELAY(1000*10);
789b2fc195eSAndrew Gallatin 		i++;
790b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
791b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
792b2fc195eSAndrew Gallatin 	}
793b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
794b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
795b2fc195eSAndrew Gallatin 			confirm, *confirm);
796b2fc195eSAndrew Gallatin 
797b2fc195eSAndrew Gallatin 		return ENXIO;
798b2fc195eSAndrew Gallatin 	}
799b2fc195eSAndrew Gallatin 	return 0;
800b2fc195eSAndrew Gallatin }
801b2fc195eSAndrew Gallatin 
802b2fc195eSAndrew Gallatin static int
8036d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
804b2fc195eSAndrew Gallatin {
8056d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
806b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
807b2fc195eSAndrew Gallatin 	int status;
808b2fc195eSAndrew Gallatin 
809b2fc195eSAndrew Gallatin 
810b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
811b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
812b2fc195eSAndrew Gallatin 
813b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
814b2fc195eSAndrew Gallatin 
8155e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
816b2fc195eSAndrew Gallatin 	return status;
817b2fc195eSAndrew Gallatin }
818b2fc195eSAndrew Gallatin 
819b2fc195eSAndrew Gallatin static int
8206d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
821b2fc195eSAndrew Gallatin {
8226d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
823b2fc195eSAndrew Gallatin 	int status;
824b2fc195eSAndrew Gallatin 
825b2fc195eSAndrew Gallatin 	if (pause)
8265e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
827b2fc195eSAndrew Gallatin 				       &cmd);
828b2fc195eSAndrew Gallatin 	else
8295e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
830b2fc195eSAndrew Gallatin 				       &cmd);
831b2fc195eSAndrew Gallatin 
832b2fc195eSAndrew Gallatin 	if (status) {
833b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
834b2fc195eSAndrew Gallatin 		return ENXIO;
835b2fc195eSAndrew Gallatin 	}
836b2fc195eSAndrew Gallatin 	sc->pause = pause;
837b2fc195eSAndrew Gallatin 	return 0;
838b2fc195eSAndrew Gallatin }
839b2fc195eSAndrew Gallatin 
840b2fc195eSAndrew Gallatin static void
8416d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
842b2fc195eSAndrew Gallatin {
8436d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
844b2fc195eSAndrew Gallatin 	int status;
845b2fc195eSAndrew Gallatin 
846b2fc195eSAndrew Gallatin 	if (promisc)
8475e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
848b2fc195eSAndrew Gallatin 				       &cmd);
849b2fc195eSAndrew Gallatin 	else
8505e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
851b2fc195eSAndrew Gallatin 				       &cmd);
852b2fc195eSAndrew Gallatin 
853b2fc195eSAndrew Gallatin 	if (status) {
854b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
855b2fc195eSAndrew Gallatin 	}
856b2fc195eSAndrew Gallatin }
857b2fc195eSAndrew Gallatin 
8580fa7f681SAndrew Gallatin static void
8590fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
8600fa7f681SAndrew Gallatin {
8610fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
8620fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
8630fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
8640fa7f681SAndrew Gallatin 	int err;
8650fa7f681SAndrew Gallatin 
8660fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
8670fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
8680fa7f681SAndrew Gallatin 		return;
8690fa7f681SAndrew Gallatin 
8700fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
8710fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
8720fa7f681SAndrew Gallatin 	if (err != 0) {
8730fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
8740fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
8750fa7f681SAndrew Gallatin 		return;
8760fa7f681SAndrew Gallatin 	}
8770fa7f681SAndrew Gallatin 
8780fa7f681SAndrew Gallatin 
8790fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
8800fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
8810fa7f681SAndrew Gallatin 		return;
8820fa7f681SAndrew Gallatin 
8830fa7f681SAndrew Gallatin 	/* Flush all the filters */
8840fa7f681SAndrew Gallatin 
8850fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
8860fa7f681SAndrew Gallatin 	if (err != 0) {
8870fa7f681SAndrew Gallatin 		device_printf(sc->dev,
8880fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
8890fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
8900fa7f681SAndrew Gallatin 		return;
8910fa7f681SAndrew Gallatin 	}
8920fa7f681SAndrew Gallatin 
8930fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
8940fa7f681SAndrew Gallatin 
8950fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
8960fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
8970fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
8980fa7f681SAndrew Gallatin 			continue;
8990fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
9000fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
9010fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
9020fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
9030fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
9040fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
9050fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
9060fa7f681SAndrew Gallatin 		if (err != 0) {
9070fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
9080fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
9090fa7f681SAndrew Gallatin 			       "%d\t", err);
9100fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
9110fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
9120fa7f681SAndrew Gallatin 			return;
9130fa7f681SAndrew Gallatin 		}
9140fa7f681SAndrew Gallatin 	}
9150fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
9160fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
9170fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
9180fa7f681SAndrew Gallatin 	if (err != 0) {
9190fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
9200fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
9210fa7f681SAndrew Gallatin 	}
9220fa7f681SAndrew Gallatin }
9230fa7f681SAndrew Gallatin 
9240fa7f681SAndrew Gallatin 
925b2fc195eSAndrew Gallatin static int
9266d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc)
927b2fc195eSAndrew Gallatin {
928b2fc195eSAndrew Gallatin 
9296d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
9305e7d8541SAndrew Gallatin 	mxge_dma_t dmabench_dma;
9315e7d8541SAndrew Gallatin 	size_t bytes;
9325e7d8541SAndrew Gallatin 	int status;
933b2fc195eSAndrew Gallatin 
934b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
935b2fc195eSAndrew Gallatin 	   is alive */
936b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
9375e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
938b2fc195eSAndrew Gallatin 	if (status != 0) {
939b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
940b2fc195eSAndrew Gallatin 		return ENXIO;
941b2fc195eSAndrew Gallatin 	}
942b2fc195eSAndrew Gallatin 
943091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
944091feecdSAndrew Gallatin 
945b2fc195eSAndrew Gallatin 	/* Now exchange information about interrupts  */
9465e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\
9475e7d8541SAndrew Gallatin 	memset(sc->rx_done.entry, 0, bytes);
9485e7d8541SAndrew Gallatin 	cmd.data0 = (uint32_t)bytes;
9495e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
9505e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr);
9515e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr);
9525e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd);
953b2fc195eSAndrew Gallatin 
9546d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
9555e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
9565e7d8541SAndrew Gallatin 
9575e7d8541SAndrew Gallatin 
9585e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
9595e7d8541SAndrew Gallatin 
9605e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
9615e7d8541SAndrew Gallatin 	sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
9625e7d8541SAndrew Gallatin 
9635e7d8541SAndrew Gallatin 
9645e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
9656d87a65dSAndrew Gallatin 				&cmd);
9665e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
967b2fc195eSAndrew Gallatin 	if (status != 0) {
968b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
969b2fc195eSAndrew Gallatin 		return status;
970b2fc195eSAndrew Gallatin 	}
971b2fc195eSAndrew Gallatin 
9725e7d8541SAndrew Gallatin 
9735e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
9745e7d8541SAndrew Gallatin 
9755e7d8541SAndrew Gallatin 
9765e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
9775e7d8541SAndrew Gallatin 	sc->read_dma = sc->write_dma = sc->read_write_dma = 0;
9785e7d8541SAndrew Gallatin 	status = mxge_dma_alloc(sc, &dmabench_dma, 4096, 4096);
9795e7d8541SAndrew Gallatin 	if (status)
9805e7d8541SAndrew Gallatin 		goto dmabench_fail;
9815e7d8541SAndrew Gallatin 
9825e7d8541SAndrew Gallatin 	/* Read DMA */
9835e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
9845e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
9855e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10000;
9865e7d8541SAndrew Gallatin 
9875e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
9885e7d8541SAndrew Gallatin 	if (status != 0)
9895e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read dma benchmark failed\n");
9905e7d8541SAndrew Gallatin 	else
9915e7d8541SAndrew Gallatin 		sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
9925e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
9935e7d8541SAndrew Gallatin 
9945e7d8541SAndrew Gallatin 	/* Write DMA */
9955e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
9965e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
9975e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x1;
9985e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
9995e7d8541SAndrew Gallatin 	if (status != 0)
10005e7d8541SAndrew Gallatin 		device_printf(sc->dev, "write dma benchmark failed\n");
10015e7d8541SAndrew Gallatin 	else
10025e7d8541SAndrew Gallatin 		sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
10035e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
10045e7d8541SAndrew Gallatin 	/* Read/Write DMA */
10055e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
10065e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
10075e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10001;
10085e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
10095e7d8541SAndrew Gallatin 	if (status != 0)
10105e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read/write dma benchmark failed\n");
10115e7d8541SAndrew Gallatin 	else
10125e7d8541SAndrew Gallatin 		sc->read_write_dma =
10135e7d8541SAndrew Gallatin 			((cmd.data0>>16) * sc->tx.boundary * 2 * 2) /
10145e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
10155e7d8541SAndrew Gallatin 
10165e7d8541SAndrew Gallatin 	mxge_dma_free(&dmabench_dma);
10175e7d8541SAndrew Gallatin 
10185e7d8541SAndrew Gallatin dmabench_fail:
1019b2fc195eSAndrew Gallatin 	/* reset mcp/driver shared state back to 0 */
10205e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
10215e7d8541SAndrew Gallatin 	sc->rx_done.idx = 0;
10225e7d8541SAndrew Gallatin 	sc->rx_done.cnt = 0;
1023b2fc195eSAndrew Gallatin 	sc->tx.req = 0;
1024b2fc195eSAndrew Gallatin 	sc->tx.done = 0;
10255e7d8541SAndrew Gallatin 	sc->tx.pkt_done = 0;
1026b2fc195eSAndrew Gallatin 	sc->rx_big.cnt = 0;
1027b2fc195eSAndrew Gallatin 	sc->rx_small.cnt = 0;
1028b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
10296d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
10306d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
10316d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
10320fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1033b2fc195eSAndrew Gallatin 	return status;
1034b2fc195eSAndrew Gallatin }
1035b2fc195eSAndrew Gallatin 
1036b2fc195eSAndrew Gallatin static int
10376d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1038b2fc195eSAndrew Gallatin {
10396d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1040b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1041b2fc195eSAndrew Gallatin         int err;
1042b2fc195eSAndrew Gallatin 
1043b2fc195eSAndrew Gallatin         sc = arg1;
1044b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1045b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1046b2fc195eSAndrew Gallatin         if (err != 0) {
1047b2fc195eSAndrew Gallatin                 return err;
1048b2fc195eSAndrew Gallatin         }
1049b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1050b2fc195eSAndrew Gallatin                 return 0;
1051b2fc195eSAndrew Gallatin 
1052b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1053b2fc195eSAndrew Gallatin                 return EINVAL;
1054b2fc195eSAndrew Gallatin 
1055b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
10565e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1057b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
10585e7d8541SAndrew Gallatin 
1059b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
1060b2fc195eSAndrew Gallatin         return err;
1061b2fc195eSAndrew Gallatin }
1062b2fc195eSAndrew Gallatin 
1063b2fc195eSAndrew Gallatin static int
10646d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1065b2fc195eSAndrew Gallatin {
10666d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1067b2fc195eSAndrew Gallatin         unsigned int enabled;
1068b2fc195eSAndrew Gallatin         int err;
1069b2fc195eSAndrew Gallatin 
1070b2fc195eSAndrew Gallatin         sc = arg1;
1071b2fc195eSAndrew Gallatin         enabled = sc->pause;
1072b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1073b2fc195eSAndrew Gallatin         if (err != 0) {
1074b2fc195eSAndrew Gallatin                 return err;
1075b2fc195eSAndrew Gallatin         }
1076b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1077b2fc195eSAndrew Gallatin                 return 0;
1078b2fc195eSAndrew Gallatin 
1079b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
10806d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1081b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
1082b2fc195eSAndrew Gallatin         return err;
1083b2fc195eSAndrew Gallatin }
1084b2fc195eSAndrew Gallatin 
1085b2fc195eSAndrew Gallatin static int
10866d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1087b2fc195eSAndrew Gallatin {
1088b2fc195eSAndrew Gallatin         int err;
1089b2fc195eSAndrew Gallatin 
1090b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1091b2fc195eSAndrew Gallatin                 return EFAULT;
1092b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1093b2fc195eSAndrew Gallatin         arg1 = NULL;
1094b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1095b2fc195eSAndrew Gallatin 
1096b2fc195eSAndrew Gallatin         return err;
1097b2fc195eSAndrew Gallatin }
1098b2fc195eSAndrew Gallatin 
1099b2fc195eSAndrew Gallatin static void
11006d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1101b2fc195eSAndrew Gallatin {
1102b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1103b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
11045e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
1105b2fc195eSAndrew Gallatin 
1106b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1107b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
1108b2fc195eSAndrew Gallatin 	fw = sc->fw_stats;
1109b2fc195eSAndrew Gallatin 
11105e7d8541SAndrew Gallatin 	/* random information */
11115e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11125e7d8541SAndrew Gallatin 		       "firmware_version",
11135e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
11145e7d8541SAndrew Gallatin 		       0, "firmware version");
11155e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11165e7d8541SAndrew Gallatin 		       "serial_number",
11175e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
11185e7d8541SAndrew Gallatin 		       0, "serial number");
11195e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
11205e7d8541SAndrew Gallatin 		       "product_code",
11215e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
11225e7d8541SAndrew Gallatin 		       0, "product_code");
11235e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1124d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1125d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1126d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1127d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11285e7d8541SAndrew Gallatin 		       "tx_boundary",
11295e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.boundary,
11305e7d8541SAndrew Gallatin 		       0, "tx_boundary");
11315e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1132091feecdSAndrew Gallatin 		       "write_combine",
1133091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1134091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1135091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11365e7d8541SAndrew Gallatin 		       "read_dma_MBs",
11375e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
11385e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
11395e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11405e7d8541SAndrew Gallatin 		       "write_dma_MBs",
11415e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
11425e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
11435e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11445e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
11455e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
11465e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
11475e7d8541SAndrew Gallatin 
11485e7d8541SAndrew Gallatin 
11495e7d8541SAndrew Gallatin 	/* performance related tunables */
1150b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1151b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1152b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
11536d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1154b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1155b2fc195eSAndrew Gallatin 
1156b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1157b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1158b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
11596d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1160b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1161b2fc195eSAndrew Gallatin 
1162b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11635e7d8541SAndrew Gallatin 		       "deassert_wait",
11645e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
11655e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1166b2fc195eSAndrew Gallatin 
1167b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1168b2fc195eSAndrew Gallatin 	   Need to swap it */
1169b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1170b2fc195eSAndrew Gallatin 			"link_up",
1171b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
11726d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1173b2fc195eSAndrew Gallatin 			"I", "link up");
1174b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1175b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1176b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
11776d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1178b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1179b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1180b2fc195eSAndrew Gallatin 			"dropped_link_overflow",
1181b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
11826d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1183b2fc195eSAndrew Gallatin 			"I", "dropped_link_overflow");
1184b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1185b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1186b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1187b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
11886d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1189b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1190b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
11910fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
11920fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
11930fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
11940fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
11950fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
11960fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1197b2fc195eSAndrew Gallatin 			"dropped_runt",
1198b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
11996d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1200b2fc195eSAndrew Gallatin 			"I", "dropped_runt");
1201b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1202b2fc195eSAndrew Gallatin 			"dropped_overrun",
1203b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
12046d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1205b2fc195eSAndrew Gallatin 			"I", "dropped_overrun");
1206b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1207b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1208b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1209b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
12106d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1211b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1212b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1213b2fc195eSAndrew Gallatin 			"dropped_no_big_buffer",
1214b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
12156d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1216b2fc195eSAndrew Gallatin 			"I", "dropped_no_big_buffer");
1217b2fc195eSAndrew Gallatin 
1218b2fc195eSAndrew Gallatin 	/* host counters exported for debugging */
1219b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12205e7d8541SAndrew Gallatin 		       "rx_small_cnt",
12215e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_small.cnt,
12225e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
12235e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12245e7d8541SAndrew Gallatin 		       "rx_big_cnt",
12255e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_big.cnt,
12265e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
12275e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1228b2fc195eSAndrew Gallatin 		       "tx_req",
1229b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.req,
1230b2fc195eSAndrew Gallatin 		       0, "tx_req");
1231b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1232b2fc195eSAndrew Gallatin 		       "tx_done",
1233b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.done,
1234b2fc195eSAndrew Gallatin 		       0, "tx_done");
1235b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12365e7d8541SAndrew Gallatin 		       "tx_pkt_done",
12375e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.pkt_done,
12385e7d8541SAndrew Gallatin 		       0, "tx_done");
12395e7d8541SAndrew Gallatin 
12405e7d8541SAndrew Gallatin 	/* verbose printing? */
1241b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12425e7d8541SAndrew Gallatin 		       "verbose",
12435e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
12445e7d8541SAndrew Gallatin 		       0, "verbose printing");
1245b2fc195eSAndrew Gallatin 
1246b2fc195eSAndrew Gallatin }
1247b2fc195eSAndrew Gallatin 
1248b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1249b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1250b2fc195eSAndrew Gallatin 
1251b2fc195eSAndrew Gallatin static inline void
12526d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx,
1253b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1254b2fc195eSAndrew Gallatin {
1255b2fc195eSAndrew Gallatin         int idx, starting_slot;
1256b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1257b2fc195eSAndrew Gallatin         while (cnt > 1) {
1258b2fc195eSAndrew Gallatin                 cnt--;
1259b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
12606d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1261b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1262b2fc195eSAndrew Gallatin                 mb();
1263b2fc195eSAndrew Gallatin         }
1264b2fc195eSAndrew Gallatin }
1265b2fc195eSAndrew Gallatin 
1266b2fc195eSAndrew Gallatin /*
1267b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1268b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1269b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1270b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1271b2fc195eSAndrew Gallatin  */
1272b2fc195eSAndrew Gallatin 
1273b2fc195eSAndrew Gallatin static inline void
12746d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src,
1275b2fc195eSAndrew Gallatin                   int cnt)
1276b2fc195eSAndrew Gallatin {
1277b2fc195eSAndrew Gallatin         int idx, i;
1278b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1279b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1280b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1281b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
12825e7d8541SAndrew Gallatin 	uint8_t last_flags;
1283b2fc195eSAndrew Gallatin 
1284b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1285b2fc195eSAndrew Gallatin 
12865e7d8541SAndrew Gallatin 	last_flags = src->flags;
12875e7d8541SAndrew Gallatin 	src->flags = 0;
1288b2fc195eSAndrew Gallatin         mb();
1289b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1290b2fc195eSAndrew Gallatin         srcp = src;
1291b2fc195eSAndrew Gallatin 
1292b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1293b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
12946d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1295b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1296b2fc195eSAndrew Gallatin                         srcp += 2;
1297b2fc195eSAndrew Gallatin                         dstp += 2;
1298b2fc195eSAndrew Gallatin                 }
1299b2fc195eSAndrew Gallatin         } else {
1300b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1301b2fc195eSAndrew Gallatin                    that it is submitted below */
13026d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1303b2fc195eSAndrew Gallatin                 i = 0;
1304b2fc195eSAndrew Gallatin         }
1305b2fc195eSAndrew Gallatin         if (i < cnt) {
1306b2fc195eSAndrew Gallatin                 /* submit the first request */
13076d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1308b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1309b2fc195eSAndrew Gallatin         }
1310b2fc195eSAndrew Gallatin 
1311b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
13125e7d8541SAndrew Gallatin         src->flags = last_flags;
1313b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1314b2fc195eSAndrew Gallatin         src_ints+=3;
1315b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1316b2fc195eSAndrew Gallatin         dst_ints+=3;
1317b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1318b2fc195eSAndrew Gallatin         tx->req += cnt;
1319b2fc195eSAndrew Gallatin         mb();
1320b2fc195eSAndrew Gallatin }
1321b2fc195eSAndrew Gallatin 
1322b2fc195eSAndrew Gallatin static inline void
13236d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt)
1324b2fc195eSAndrew Gallatin {
1325b2fc195eSAndrew Gallatin     tx->req += cnt;
1326b2fc195eSAndrew Gallatin     mb();
1327b2fc195eSAndrew Gallatin     while (cnt >= 4) {
13286d87a65dSAndrew Gallatin 	    mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64);
1329b2fc195eSAndrew Gallatin 	    mb();
1330b2fc195eSAndrew Gallatin 	    src += 4;
1331b2fc195eSAndrew Gallatin 	    cnt -= 4;
1332b2fc195eSAndrew Gallatin     }
1333b2fc195eSAndrew Gallatin     if (cnt > 0) {
1334b2fc195eSAndrew Gallatin 	    /* pad it to 64 bytes.  The src is 64 bytes bigger than it
1335b2fc195eSAndrew Gallatin 	       needs to be so that we don't overrun it */
13360fa7f681SAndrew Gallatin 	    mxge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), src, 64);
1337b2fc195eSAndrew Gallatin 	    mb();
1338b2fc195eSAndrew Gallatin     }
1339b2fc195eSAndrew Gallatin }
1340b2fc195eSAndrew Gallatin 
1341b2fc195eSAndrew Gallatin static void
1342aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt)
1343aed8e389SAndrew Gallatin {
1344aed8e389SAndrew Gallatin 	mxge_tx_buf_t *tx;
1345aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1346aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1347aed8e389SAndrew Gallatin 	struct ether_header *eh;
1348aed8e389SAndrew Gallatin 	struct ip *ip;
1349aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1350aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1351aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1352aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1353aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1354aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1355aed8e389SAndrew Gallatin 	static int once;
1356aed8e389SAndrew Gallatin 
1357aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1358aed8e389SAndrew Gallatin 
1359aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1360aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1361aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1362aed8e389SAndrew Gallatin 	 */
1363aed8e389SAndrew Gallatin 
1364aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1365aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1366aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1367aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh)
1368aed8e389SAndrew Gallatin 			    + sizeof (*ip))) {
1369aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1370aed8e389SAndrew Gallatin 			   sc->scratch);
1371aed8e389SAndrew Gallatin 		eh = (struct ether_header *)sc->scratch;
1372aed8e389SAndrew Gallatin 	} else {
1373aed8e389SAndrew Gallatin 		eh = mtod(m, struct ether_header *);
1374aed8e389SAndrew Gallatin 	}
1375aed8e389SAndrew Gallatin 	ip = (struct ip *) (eh + 1);
1376aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2)
1377aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1378aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2)
1379aed8e389SAndrew Gallatin 			   + sizeof (*tcp),  sc->scratch);
1380aed8e389SAndrew Gallatin 		eh = (struct ether_header *) sc->scratch;
1381aed8e389SAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1382aed8e389SAndrew Gallatin 	}
1383aed8e389SAndrew Gallatin 
1384aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1385aed8e389SAndrew Gallatin 	cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2));
1386aed8e389SAndrew Gallatin 
1387aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1388aed8e389SAndrew Gallatin 	cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1389aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1390aed8e389SAndrew Gallatin 
1391aed8e389SAndrew Gallatin 
1392aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1393aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1394aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1395aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1396aed8e389SAndrew Gallatin 
1397aed8e389SAndrew Gallatin 	tx = &sc->tx;
1398aed8e389SAndrew Gallatin 	req = tx->req_list;
1399aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1400aed8e389SAndrew Gallatin 	cnt = 0;
1401aed8e389SAndrew Gallatin 	rdma_count = 0;
1402aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1403aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1404aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1405aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1406aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1407aed8e389SAndrew Gallatin 	 *
1408aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1409aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1410aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1411aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1412aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1413aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1414aed8e389SAndrew Gallatin 	 *
1415aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1416aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1417aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1418aed8e389SAndrew Gallatin 	 */
1419aed8e389SAndrew Gallatin 
1420aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1421aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1422aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1423aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1424aed8e389SAndrew Gallatin 		len = seglen = seg->ds_len;
1425aed8e389SAndrew Gallatin 
1426aed8e389SAndrew Gallatin 		while (len) {
1427aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1428aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1429aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1430aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1431aed8e389SAndrew Gallatin 				/* payload */
1432aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1433aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1434aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1435aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1436aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1437aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1438aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1439aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1440aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1441aed8e389SAndrew Gallatin 				/* header ends */
1442aed8e389SAndrew Gallatin 				rdma_count = -1;
1443aed8e389SAndrew Gallatin 				cum_len_next = 0;
1444aed8e389SAndrew Gallatin 				seglen = -cum_len;
1445aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1446aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1447aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1448aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1449aed8e389SAndrew Gallatin 			    }
1450aed8e389SAndrew Gallatin 
1451aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1452aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1453aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1454aed8e389SAndrew Gallatin 			req->pad = 0;
1455aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1456aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1457aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1458aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1459aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1460aed8e389SAndrew Gallatin 			low += seglen;
1461aed8e389SAndrew Gallatin 			len -= seglen;
1462aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1463aed8e389SAndrew Gallatin 			flags = flags_next;
1464aed8e389SAndrew Gallatin 			req++;
1465aed8e389SAndrew Gallatin 			cnt++;
1466aed8e389SAndrew Gallatin 			rdma_count++;
1467aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1468aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1469aed8e389SAndrew Gallatin 			else
1470aed8e389SAndrew Gallatin 				cksum_offset = 0;
1471aed8e389SAndrew Gallatin 			if (__predict_false(cnt > MXGE_MAX_SEND_DESC))
1472aed8e389SAndrew Gallatin 				goto drop;
1473aed8e389SAndrew Gallatin 		}
1474aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1475aed8e389SAndrew Gallatin 		seg++;
1476aed8e389SAndrew Gallatin 	}
1477aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1478aed8e389SAndrew Gallatin 
1479aed8e389SAndrew Gallatin 	do {
1480aed8e389SAndrew Gallatin 		req--;
1481aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1482aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1483aed8e389SAndrew Gallatin 
1484aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1485aed8e389SAndrew Gallatin 	if (tx->wc_fifo == NULL)
1486aed8e389SAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1487aed8e389SAndrew Gallatin 	else
1488aed8e389SAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1489aed8e389SAndrew Gallatin 	return;
1490aed8e389SAndrew Gallatin 
1491aed8e389SAndrew Gallatin drop:
1492aed8e389SAndrew Gallatin 	m_freem(m);
1493aed8e389SAndrew Gallatin 	sc->ifp->if_oerrors++;
1494aed8e389SAndrew Gallatin 	if (!once) {
1495aed8e389SAndrew Gallatin 		printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n");
1496aed8e389SAndrew Gallatin 		printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list);
1497aed8e389SAndrew Gallatin 		once = 1;
1498aed8e389SAndrew Gallatin 	}
1499aed8e389SAndrew Gallatin 	return;
1500aed8e389SAndrew Gallatin 
1501aed8e389SAndrew Gallatin }
1502aed8e389SAndrew Gallatin 
1503aed8e389SAndrew Gallatin static void
15046d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m)
1505b2fc195eSAndrew Gallatin {
1506b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1507b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1508b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1509b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
15106d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1511b2fc195eSAndrew Gallatin 	struct ether_header *eh;
1512b2fc195eSAndrew Gallatin 	struct ip *ip;
1513aed8e389SAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
1514aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1515aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1516b2fc195eSAndrew Gallatin 
1517b2fc195eSAndrew Gallatin 
1518b2fc195eSAndrew Gallatin 
1519b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1520b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1521b2fc195eSAndrew Gallatin 
1522b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1523b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1524b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1525aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1526b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1527b2fc195eSAndrew Gallatin 	if (err == EFBIG) {
1528b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1529b2fc195eSAndrew Gallatin 		   to defrag */
1530b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1531b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1532b2fc195eSAndrew Gallatin 			goto drop;
1533b2fc195eSAndrew Gallatin 		}
1534b2fc195eSAndrew Gallatin 		m = m_tmp;
1535b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1536b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1537aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1538b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1539b2fc195eSAndrew Gallatin 	}
1540b2fc195eSAndrew Gallatin 	if (err != 0) {
1541aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1542aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1543b2fc195eSAndrew Gallatin 		goto drop;
1544b2fc195eSAndrew Gallatin 	}
1545b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1546b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
15475e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1548b2fc195eSAndrew Gallatin 
1549aed8e389SAndrew Gallatin 
1550aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1551aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
1552aed8e389SAndrew Gallatin 		mxge_encap_tso(sc, m, cnt);
1553aed8e389SAndrew Gallatin 		return;
1554aed8e389SAndrew Gallatin 	}
1555aed8e389SAndrew Gallatin 
1556b2fc195eSAndrew Gallatin 	req = tx->req_list;
1557b2fc195eSAndrew Gallatin 	cksum_offset = 0;
15585e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
15595e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1560b2fc195eSAndrew Gallatin 
1561b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1562b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1563aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
1564aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
1565aed8e389SAndrew Gallatin 		if (__predict_false(m->m_len < sizeof (*eh)
1566aed8e389SAndrew Gallatin 				    + sizeof (*ip))) {
1567aed8e389SAndrew Gallatin 			m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1568aed8e389SAndrew Gallatin 				   sc->scratch);
1569aed8e389SAndrew Gallatin 			eh = (struct ether_header *)sc->scratch;
1570aed8e389SAndrew Gallatin 		} else {
1571b2fc195eSAndrew Gallatin 			eh = mtod(m, struct ether_header *);
1572aed8e389SAndrew Gallatin 		}
1573b2fc195eSAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1574b2fc195eSAndrew Gallatin 		cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1575b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
15765e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1577b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
15785e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1579aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
1580aed8e389SAndrew Gallatin 	} else {
1581aed8e389SAndrew Gallatin 		odd_flag = 0;
1582b2fc195eSAndrew Gallatin 	}
15835e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
15845e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
1585b2fc195eSAndrew Gallatin 
1586b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
1587b2fc195eSAndrew Gallatin 	cum_len = 0;
1588aed8e389SAndrew Gallatin 	seg = tx->seg_list;
15895e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
1590b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1591b2fc195eSAndrew Gallatin 		req->addr_low =
15926d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1593b2fc195eSAndrew Gallatin 		req->addr_high =
15946d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1595b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
1596b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
1597b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
1598b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
1599b2fc195eSAndrew Gallatin 		else
1600b2fc195eSAndrew Gallatin 			cksum_offset = 0;
16015e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
16025e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
16035e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1604aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1605b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
1606b2fc195eSAndrew Gallatin 		seg++;
1607b2fc195eSAndrew Gallatin 		req++;
1608b2fc195eSAndrew Gallatin 		req->flags = 0;
1609b2fc195eSAndrew Gallatin 	}
1610b2fc195eSAndrew Gallatin 	req--;
1611b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
1612b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
1613b2fc195eSAndrew Gallatin 		req++;
1614b2fc195eSAndrew Gallatin 		req->addr_low =
16156d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
1616b2fc195eSAndrew Gallatin 		req->addr_high =
16176d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
1618b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
16195e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
16205e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
16215e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
16225e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1623aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1624b2fc195eSAndrew Gallatin 		cnt++;
1625b2fc195eSAndrew Gallatin 	}
16265e7d8541SAndrew Gallatin 
16275e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
16285e7d8541SAndrew Gallatin #if 0
16295e7d8541SAndrew Gallatin 	/* print what the firmware will see */
16305e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
16315e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
16325e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
16335e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
16345e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
16355e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
16365e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
16375e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
16385e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
16395e7d8541SAndrew Gallatin 	}
16405e7d8541SAndrew Gallatin 	printf("--------------\n");
16415e7d8541SAndrew Gallatin #endif
16425e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1643b2fc195eSAndrew Gallatin 	if (tx->wc_fifo == NULL)
16446d87a65dSAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1645b2fc195eSAndrew Gallatin 	else
16466d87a65dSAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1647b2fc195eSAndrew Gallatin 	return;
1648b2fc195eSAndrew Gallatin 
1649b2fc195eSAndrew Gallatin drop:
1650b2fc195eSAndrew Gallatin 	m_freem(m);
1651b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
1652b2fc195eSAndrew Gallatin 	return;
1653b2fc195eSAndrew Gallatin }
1654b2fc195eSAndrew Gallatin 
1655b2fc195eSAndrew Gallatin 
16566d914a32SAndrew Gallatin 
16576d914a32SAndrew Gallatin 
16586d914a32SAndrew Gallatin static inline void
16596d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc)
1660b2fc195eSAndrew Gallatin {
1661b2fc195eSAndrew Gallatin 	struct mbuf *m;
1662b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1663b2fc195eSAndrew Gallatin 
1664b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
16656d914a32SAndrew Gallatin 	while ((sc->tx.mask - (sc->tx.req - sc->tx.done))
16666d914a32SAndrew Gallatin 	       > MXGE_MAX_SEND_DESC) {
1667b2fc195eSAndrew Gallatin 
16686d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
16696d914a32SAndrew Gallatin 		if (m == NULL) {
16706d914a32SAndrew Gallatin 			return;
16716d914a32SAndrew Gallatin 		}
1672b2fc195eSAndrew Gallatin 		/* let BPF see it */
1673b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
1674b2fc195eSAndrew Gallatin 
1675b2fc195eSAndrew Gallatin 		/* give it to the nic */
16766d87a65dSAndrew Gallatin 		mxge_encap(sc, m);
16776d914a32SAndrew Gallatin 	}
16786d914a32SAndrew Gallatin 	/* ran out of transmit slots */
1679b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1680b2fc195eSAndrew Gallatin }
1681b2fc195eSAndrew Gallatin 
1682b2fc195eSAndrew Gallatin static void
16836d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
1684b2fc195eSAndrew Gallatin {
16856d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
1686b2fc195eSAndrew Gallatin 
1687b2fc195eSAndrew Gallatin 
1688b2fc195eSAndrew Gallatin 	mtx_lock(&sc->tx_lock);
16896d87a65dSAndrew Gallatin 	mxge_start_locked(sc);
1690b2fc195eSAndrew Gallatin 	mtx_unlock(&sc->tx_lock);
1691b2fc195eSAndrew Gallatin }
1692b2fc195eSAndrew Gallatin 
16935e7d8541SAndrew Gallatin /*
16945e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
16955e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
16965e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
16975e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
16985e7d8541SAndrew Gallatin  * in a burst
16995e7d8541SAndrew Gallatin  */
17005e7d8541SAndrew Gallatin static inline void
17015e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
17025e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
17035e7d8541SAndrew Gallatin {
17045e7d8541SAndrew Gallatin 	uint32_t low;
17055e7d8541SAndrew Gallatin 
17065e7d8541SAndrew Gallatin 	low = src->addr_low;
17075e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
17085e7d8541SAndrew Gallatin 	mxge_pio_copy(dst, src, 8 * sizeof (*src));
17095e7d8541SAndrew Gallatin 	mb();
17105e7d8541SAndrew Gallatin 	dst->addr_low = low;
17115e7d8541SAndrew Gallatin 	mb();
17125e7d8541SAndrew Gallatin }
17135e7d8541SAndrew Gallatin 
1714b2fc195eSAndrew Gallatin static int
17156d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1716b2fc195eSAndrew Gallatin {
1717b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1718b2fc195eSAndrew Gallatin 	struct mbuf *m;
17196d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_small;
1720b2fc195eSAndrew Gallatin 	int cnt, err;
1721b2fc195eSAndrew Gallatin 
1722b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
1723b2fc195eSAndrew Gallatin 	if (m == NULL) {
1724b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1725b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1726b2fc195eSAndrew Gallatin 		goto done;
1727b2fc195eSAndrew Gallatin 	}
1728b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
1729b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1730b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1731b2fc195eSAndrew Gallatin 	if (err != 0) {
1732b2fc195eSAndrew Gallatin 		m_free(m);
1733b2fc195eSAndrew Gallatin 		goto done;
1734b2fc195eSAndrew Gallatin 	}
1735b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1736b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
17376d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1738b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
17396d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1740b2fc195eSAndrew Gallatin 
1741b2fc195eSAndrew Gallatin done:
1742b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
17435e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
17445e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
17455e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
17465e7d8541SAndrew Gallatin 		else {
1747b2fc195eSAndrew Gallatin 			mb();
17485e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
17495e7d8541SAndrew Gallatin 		}
1750b2fc195eSAndrew Gallatin         }
1751b2fc195eSAndrew Gallatin 	return err;
1752b2fc195eSAndrew Gallatin }
1753b2fc195eSAndrew Gallatin 
1754b2fc195eSAndrew Gallatin static int
17556d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1756b2fc195eSAndrew Gallatin {
1757b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1758b2fc195eSAndrew Gallatin 	struct mbuf *m;
17596d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_big;
1760b2fc195eSAndrew Gallatin 	int cnt, err;
1761b2fc195eSAndrew Gallatin 
1762b2fc195eSAndrew Gallatin 	m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes);
1763b2fc195eSAndrew Gallatin 	if (m == NULL) {
1764b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1765b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1766b2fc195eSAndrew Gallatin 		goto done;
1767b2fc195eSAndrew Gallatin 	}
1768b2fc195eSAndrew Gallatin 	m->m_len = sc->big_bytes;
1769b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1770b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1771b2fc195eSAndrew Gallatin 	if (err != 0) {
1772b2fc195eSAndrew Gallatin 		m_free(m);
1773b2fc195eSAndrew Gallatin 		goto done;
1774b2fc195eSAndrew Gallatin 	}
1775b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1776b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
17776d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1778b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
17796d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1780b2fc195eSAndrew Gallatin 
1781b2fc195eSAndrew Gallatin done:
1782b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
17835e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
17845e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
17855e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
17865e7d8541SAndrew Gallatin 		else {
1787b2fc195eSAndrew Gallatin 			mb();
17885e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
17895e7d8541SAndrew Gallatin 		}
1790b2fc195eSAndrew Gallatin         }
1791b2fc195eSAndrew Gallatin 	return err;
1792b2fc195eSAndrew Gallatin }
1793b2fc195eSAndrew Gallatin 
1794b2fc195eSAndrew Gallatin static inline void
17955e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
17965e7d8541SAndrew Gallatin {
17975e7d8541SAndrew Gallatin 	struct ether_header *eh;
17985e7d8541SAndrew Gallatin 	struct ip *ip;
17995e7d8541SAndrew Gallatin 
18005e7d8541SAndrew Gallatin 	eh = mtod(m, struct ether_header *);
18015e7d8541SAndrew Gallatin 	if (__predict_true(eh->ether_type ==  htons(ETHERTYPE_IP))) {
18025e7d8541SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
18035e7d8541SAndrew Gallatin 		if (__predict_true(ip->ip_p == IPPROTO_TCP ||
18045e7d8541SAndrew Gallatin 				   ip->ip_p == IPPROTO_UDP)) {
18055e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_data = csum;
18065e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_flags = CSUM_DATA_VALID;
18075e7d8541SAndrew Gallatin 		}
18085e7d8541SAndrew Gallatin 	}
18095e7d8541SAndrew Gallatin }
18105e7d8541SAndrew Gallatin 
18115e7d8541SAndrew Gallatin static inline void
18125e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum)
1813b2fc195eSAndrew Gallatin {
1814b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1815b2fc195eSAndrew Gallatin 	struct mbuf *m = 0; 		/* -Wunitialized */
1816b2fc195eSAndrew Gallatin 	struct mbuf *m_prev = 0;	/* -Wunitialized */
1817b2fc195eSAndrew Gallatin 	struct mbuf *m_head = 0;
1818b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
18196d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1820b2fc195eSAndrew Gallatin 	int idx;
1821b2fc195eSAndrew Gallatin 
1822b2fc195eSAndrew Gallatin 
1823b2fc195eSAndrew Gallatin 	rx = &sc->rx_big;
1824b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1825b2fc195eSAndrew Gallatin 	while (len > 0) {
1826b2fc195eSAndrew Gallatin 		idx = rx->cnt & rx->mask;
1827b2fc195eSAndrew Gallatin                 rx->cnt++;
1828b2fc195eSAndrew Gallatin 		/* save a pointer to the received mbuf */
1829b2fc195eSAndrew Gallatin 		m = rx->info[idx].m;
1830b2fc195eSAndrew Gallatin 		/* try to replace the received mbuf */
18316d87a65dSAndrew Gallatin 		if (mxge_get_buf_big(sc, rx->extra_map, idx)) {
1832b2fc195eSAndrew Gallatin 			goto drop;
1833b2fc195eSAndrew Gallatin 		}
1834b2fc195eSAndrew Gallatin 		/* unmap the received buffer */
1835b2fc195eSAndrew Gallatin 		old_map = rx->info[idx].map;
1836b2fc195eSAndrew Gallatin 		bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1837b2fc195eSAndrew Gallatin 		bus_dmamap_unload(rx->dmat, old_map);
1838b2fc195eSAndrew Gallatin 
1839b2fc195eSAndrew Gallatin 		/* swap the bus_dmamap_t's */
1840b2fc195eSAndrew Gallatin 		rx->info[idx].map = rx->extra_map;
1841b2fc195eSAndrew Gallatin 		rx->extra_map = old_map;
1842b2fc195eSAndrew Gallatin 
1843b2fc195eSAndrew Gallatin 		/* chain multiple segments together */
1844b2fc195eSAndrew Gallatin 		if (!m_head) {
1845b2fc195eSAndrew Gallatin 			m_head = m;
1846b2fc195eSAndrew Gallatin 			/* mcp implicitly skips 1st bytes so that
1847b2fc195eSAndrew Gallatin 			 * packet is properly aligned */
18485e7d8541SAndrew Gallatin 			m->m_data += MXGEFW_PAD;
1849b2fc195eSAndrew Gallatin 			m->m_pkthdr.len = len;
18505e7d8541SAndrew Gallatin 			m->m_len = sc->big_bytes - MXGEFW_PAD;
1851b2fc195eSAndrew Gallatin 		} else {
1852b2fc195eSAndrew Gallatin 			m->m_len = sc->big_bytes;
1853b2fc195eSAndrew Gallatin 			m->m_flags &= ~M_PKTHDR;
1854b2fc195eSAndrew Gallatin 			m_prev->m_next = m;
1855b2fc195eSAndrew Gallatin 		}
1856b2fc195eSAndrew Gallatin 		len -= m->m_len;
1857b2fc195eSAndrew Gallatin 		m_prev = m;
1858b2fc195eSAndrew Gallatin 	}
1859b2fc195eSAndrew Gallatin 
1860b2fc195eSAndrew Gallatin 	/* trim trailing garbage from the last mbuf in the chain.  If
1861b2fc195eSAndrew Gallatin 	 * there is any garbage, len will be negative */
1862b2fc195eSAndrew Gallatin 	m->m_len += len;
1863b2fc195eSAndrew Gallatin 
1864b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
18655e7d8541SAndrew Gallatin 	if (sc->csum_flag)
18665e7d8541SAndrew Gallatin 		mxge_rx_csum(m_head, csum);
1867b2fc195eSAndrew Gallatin 
1868b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1869b2fc195eSAndrew Gallatin 	m_head->m_pkthdr.rcvif = ifp;
1870b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1871b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m_head);
1872b2fc195eSAndrew Gallatin 	return;
1873b2fc195eSAndrew Gallatin 
1874b2fc195eSAndrew Gallatin drop:
1875b2fc195eSAndrew Gallatin 	/* drop the frame -- the old mbuf(s) are re-cycled by running
1876b2fc195eSAndrew Gallatin 	   every slot through the allocator */
1877b2fc195eSAndrew Gallatin         if (m_head) {
1878b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1879b2fc195eSAndrew Gallatin                 m_freem(m_head);
1880b2fc195eSAndrew Gallatin         } else {
18815e7d8541SAndrew Gallatin                 len -= (sc->big_bytes + MXGEFW_PAD);
1882b2fc195eSAndrew Gallatin         }
1883b2fc195eSAndrew Gallatin         while ((int)len > 0) {
1884b2fc195eSAndrew Gallatin                 idx = rx->cnt & rx->mask;
1885b2fc195eSAndrew Gallatin                 rx->cnt++;
1886b2fc195eSAndrew Gallatin                 m = rx->info[idx].m;
18876d87a65dSAndrew Gallatin                 if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) {
1888b2fc195eSAndrew Gallatin 			m_freem(m);
1889b2fc195eSAndrew Gallatin 			/* unmap the received buffer */
1890b2fc195eSAndrew Gallatin 			old_map = rx->info[idx].map;
1891b2fc195eSAndrew Gallatin 			bus_dmamap_sync(rx->dmat, old_map,
1892b2fc195eSAndrew Gallatin 					BUS_DMASYNC_POSTREAD);
1893b2fc195eSAndrew Gallatin 			bus_dmamap_unload(rx->dmat, old_map);
1894b2fc195eSAndrew Gallatin 
1895b2fc195eSAndrew Gallatin 			/* swap the bus_dmamap_t's */
1896b2fc195eSAndrew Gallatin 			rx->info[idx].map = rx->extra_map;
1897b2fc195eSAndrew Gallatin 			rx->extra_map = old_map;
1898b2fc195eSAndrew Gallatin 		}
1899b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1900b2fc195eSAndrew Gallatin         }
1901b2fc195eSAndrew Gallatin 
1902b2fc195eSAndrew Gallatin 	ifp->if_ierrors++;
1903b2fc195eSAndrew Gallatin 
1904b2fc195eSAndrew Gallatin }
1905b2fc195eSAndrew Gallatin 
1906b2fc195eSAndrew Gallatin static inline void
19075e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum)
1908b2fc195eSAndrew Gallatin {
1909b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1910b2fc195eSAndrew Gallatin 	struct mbuf *m;
19116d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1912b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
1913b2fc195eSAndrew Gallatin 	int idx;
1914b2fc195eSAndrew Gallatin 
1915b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1916b2fc195eSAndrew Gallatin 	rx = &sc->rx_small;
1917b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
1918b2fc195eSAndrew Gallatin 	rx->cnt++;
1919b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
1920b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
1921b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
19226d87a65dSAndrew Gallatin 	if (mxge_get_buf_small(sc, rx->extra_map, idx)) {
1923b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
1924b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
1925b2fc195eSAndrew Gallatin 		return;
1926b2fc195eSAndrew Gallatin 	}
1927b2fc195eSAndrew Gallatin 
1928b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
1929b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
1930b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1931b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
1932b2fc195eSAndrew Gallatin 
1933b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
1934b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
1935b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
1936b2fc195eSAndrew Gallatin 
1937b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
1938b2fc195eSAndrew Gallatin 	 * aligned */
19395e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
1940b2fc195eSAndrew Gallatin 
1941b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
19425e7d8541SAndrew Gallatin 	if (sc->csum_flag)
19435e7d8541SAndrew Gallatin 		mxge_rx_csum(m, csum);
1944b2fc195eSAndrew Gallatin 
1945b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1946b2fc195eSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
1947b2fc195eSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
1948b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1949b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
1950b2fc195eSAndrew Gallatin }
1951b2fc195eSAndrew Gallatin 
1952b2fc195eSAndrew Gallatin static inline void
19535e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc)
19545e7d8541SAndrew Gallatin {
19555e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
19565e7d8541SAndrew Gallatin 	int limit = 0;
19575e7d8541SAndrew Gallatin 	uint16_t length;
19585e7d8541SAndrew Gallatin 	uint16_t checksum;
19595e7d8541SAndrew Gallatin 
19605e7d8541SAndrew Gallatin 
19615e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
19625e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
19635e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
19645e7d8541SAndrew Gallatin 		checksum = ntohs(rx_done->entry[rx_done->idx].checksum);
1965b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
19665e7d8541SAndrew Gallatin 			mxge_rx_done_small(sc, length, checksum);
19675e7d8541SAndrew Gallatin 		else
19685e7d8541SAndrew Gallatin 			mxge_rx_done_big(sc, length, checksum);
19695e7d8541SAndrew Gallatin 		rx_done->cnt++;
19705e7d8541SAndrew Gallatin 		rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1);
19715e7d8541SAndrew Gallatin 
19725e7d8541SAndrew Gallatin 		/* limit potential for livelock */
19735e7d8541SAndrew Gallatin 		if (__predict_false(++limit > 2 * mxge_max_intr_slots))
19745e7d8541SAndrew Gallatin 			break;
19755e7d8541SAndrew Gallatin 
19765e7d8541SAndrew Gallatin 	}
19775e7d8541SAndrew Gallatin }
19785e7d8541SAndrew Gallatin 
19795e7d8541SAndrew Gallatin 
19805e7d8541SAndrew Gallatin static inline void
19816d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx)
1982b2fc195eSAndrew Gallatin {
1983b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
19846d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1985b2fc195eSAndrew Gallatin 	struct mbuf *m;
1986b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
19875e7d8541SAndrew Gallatin 	int idx, limit;
1988b2fc195eSAndrew Gallatin 
19895e7d8541SAndrew Gallatin 	limit = 0;
1990b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1991b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
19925e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
1993b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
1994b2fc195eSAndrew Gallatin 		tx->done++;
1995b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
1996b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
1997b2fc195eSAndrew Gallatin 		   segment per-mbuf */
1998b2fc195eSAndrew Gallatin 		if (m != NULL) {
1999b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2000b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2001b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2002b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2003b2fc195eSAndrew Gallatin 			m_freem(m);
2004b2fc195eSAndrew Gallatin 		}
20055e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
20065e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
20075e7d8541SAndrew Gallatin 			tx->pkt_done++;
20085e7d8541SAndrew Gallatin 		}
20095e7d8541SAndrew Gallatin 		/* limit potential for livelock by only handling
20105e7d8541SAndrew Gallatin 		   2 full tx rings per call */
20115e7d8541SAndrew Gallatin 		if (__predict_false(++limit >  2 * tx->mask))
20125e7d8541SAndrew Gallatin 			break;
2013b2fc195eSAndrew Gallatin 	}
2014b2fc195eSAndrew Gallatin 
2015b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2016b2fc195eSAndrew Gallatin            its OK to send packets */
2017b2fc195eSAndrew Gallatin 
2018b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2019b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2020b2fc195eSAndrew Gallatin 		mtx_lock(&sc->tx_lock);
2021b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
20226d87a65dSAndrew Gallatin 		mxge_start_locked(sc);
2023b2fc195eSAndrew Gallatin 		mtx_unlock(&sc->tx_lock);
2024b2fc195eSAndrew Gallatin 	}
2025b2fc195eSAndrew Gallatin }
2026b2fc195eSAndrew Gallatin 
2027b2fc195eSAndrew Gallatin static void
20286d87a65dSAndrew Gallatin mxge_intr(void *arg)
2029b2fc195eSAndrew Gallatin {
20306d87a65dSAndrew Gallatin 	mxge_softc_t *sc = arg;
20315e7d8541SAndrew Gallatin 	mcp_irq_data_t *stats = sc->fw_stats;
20325e7d8541SAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
20335e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
20345e7d8541SAndrew Gallatin 	uint32_t send_done_count;
20355e7d8541SAndrew Gallatin 	uint8_t valid;
2036b2fc195eSAndrew Gallatin 
2037b2fc195eSAndrew Gallatin 
20385e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
20395e7d8541SAndrew Gallatin 	if (!stats->valid) {
20405e7d8541SAndrew Gallatin 		return;
2041b2fc195eSAndrew Gallatin 	}
20425e7d8541SAndrew Gallatin 	valid = stats->valid;
2043b2fc195eSAndrew Gallatin 
2044dc8731d4SAndrew Gallatin 	if (!sc->msi_enabled) {
20455e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
20465e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
20475e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
20485e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
20495e7d8541SAndrew Gallatin 			stats->valid = 0;
2050dc8731d4SAndrew Gallatin 	} else {
2051dc8731d4SAndrew Gallatin 		stats->valid = 0;
2052dc8731d4SAndrew Gallatin 	}
2053dc8731d4SAndrew Gallatin 
2054dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
20555e7d8541SAndrew Gallatin 	do {
20565e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
20575e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
20585e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
20595e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
20605e7d8541SAndrew Gallatin 			mxge_tx_done(sc, (int)send_done_count);
20615e7d8541SAndrew Gallatin 			mxge_clean_rx_done(sc);
20625e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2063b2fc195eSAndrew Gallatin 		}
20645e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2065b2fc195eSAndrew Gallatin 
20665e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
20675e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
20685e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2069b2fc195eSAndrew Gallatin 			if (sc->link_state) {
20705e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
20715e7d8541SAndrew Gallatin 				if (mxge_verbose)
20725e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2073b2fc195eSAndrew Gallatin 			} else {
20745e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
20755e7d8541SAndrew Gallatin 				if (mxge_verbose)
20765e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2077b2fc195eSAndrew Gallatin 			}
2078b2fc195eSAndrew Gallatin 		}
2079b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
2080b2fc195eSAndrew Gallatin 		    be32toh(sc->fw_stats->rdma_tags_available)) {
2081b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
2082b2fc195eSAndrew Gallatin 				be32toh(sc->fw_stats->rdma_tags_available);
20835e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
20845e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
20855e7d8541SAndrew Gallatin 		}
20865e7d8541SAndrew Gallatin 		sc->down_cnt += stats->link_down;
2087b2fc195eSAndrew Gallatin 	}
2088b2fc195eSAndrew Gallatin 
20895e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
20905e7d8541SAndrew Gallatin 	if (valid & 0x1)
20915e7d8541SAndrew Gallatin 	    *sc->irq_claim = be32toh(3);
20925e7d8541SAndrew Gallatin 	*(sc->irq_claim + 1) = be32toh(3);
2093b2fc195eSAndrew Gallatin }
2094b2fc195eSAndrew Gallatin 
2095b2fc195eSAndrew Gallatin static void
20966d87a65dSAndrew Gallatin mxge_watchdog(struct ifnet *ifp)
2097b2fc195eSAndrew Gallatin {
2098b2fc195eSAndrew Gallatin 	printf("%s called\n", __FUNCTION__);
2099b2fc195eSAndrew Gallatin }
2100b2fc195eSAndrew Gallatin 
2101b2fc195eSAndrew Gallatin static void
21026d87a65dSAndrew Gallatin mxge_init(void *arg)
2103b2fc195eSAndrew Gallatin {
2104b2fc195eSAndrew Gallatin }
2105b2fc195eSAndrew Gallatin 
2106b2fc195eSAndrew Gallatin 
2107b2fc195eSAndrew Gallatin 
2108b2fc195eSAndrew Gallatin static void
21096d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2110b2fc195eSAndrew Gallatin {
2111b2fc195eSAndrew Gallatin 	int i;
2112b2fc195eSAndrew Gallatin 
2113b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2114b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2115b2fc195eSAndrew Gallatin 			continue;
2116b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2117b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2118b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2119b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2120b2fc195eSAndrew Gallatin 	}
2121b2fc195eSAndrew Gallatin 
2122b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2123b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2124b2fc195eSAndrew Gallatin 			continue;
2125b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2126b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2127b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2128b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2129b2fc195eSAndrew Gallatin 	}
2130b2fc195eSAndrew Gallatin 
2131b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2132b2fc195eSAndrew Gallatin 		if (sc->tx.info[i].m == NULL)
2133b2fc195eSAndrew Gallatin 			continue;
2134b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->tx.dmat,
2135b2fc195eSAndrew Gallatin 				  sc->tx.info[i].map);
2136b2fc195eSAndrew Gallatin 		m_freem(sc->tx.info[i].m);
2137b2fc195eSAndrew Gallatin 		sc->tx.info[i].m = NULL;
2138b2fc195eSAndrew Gallatin 	}
2139b2fc195eSAndrew Gallatin }
2140b2fc195eSAndrew Gallatin 
2141b2fc195eSAndrew Gallatin static void
21426d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2143b2fc195eSAndrew Gallatin {
2144b2fc195eSAndrew Gallatin 	int i;
2145b2fc195eSAndrew Gallatin 
2146aed8e389SAndrew Gallatin 	if (sc->tx.req_bytes != NULL)
2147b2fc195eSAndrew Gallatin 		free(sc->tx.req_bytes, M_DEVBUF);
2148aed8e389SAndrew Gallatin 	if (sc->tx.seg_list != NULL)
2149aed8e389SAndrew Gallatin 		free(sc->tx.seg_list, M_DEVBUF);
2150b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow != NULL)
2151b2fc195eSAndrew Gallatin 		free(sc->rx_small.shadow, M_DEVBUF);
2152b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow != NULL)
2153b2fc195eSAndrew Gallatin 		free(sc->rx_big.shadow, M_DEVBUF);
2154b2fc195eSAndrew Gallatin 	if (sc->tx.info != NULL) {
2155b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->tx.mask; i++) {
2156b2fc195eSAndrew Gallatin 			if (sc->tx.info[i].map != NULL)
2157b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->tx.dmat,
2158b2fc195eSAndrew Gallatin 						   sc->tx.info[i].map);
2159b2fc195eSAndrew Gallatin 		}
2160b2fc195eSAndrew Gallatin 		free(sc->tx.info, M_DEVBUF);
2161b2fc195eSAndrew Gallatin 	}
2162b2fc195eSAndrew Gallatin 	if (sc->rx_small.info != NULL) {
2163b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->rx_small.mask; i++) {
2164b2fc195eSAndrew Gallatin 			if (sc->rx_small.info[i].map != NULL)
2165b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_small.dmat,
2166b2fc195eSAndrew Gallatin 						   sc->rx_small.info[i].map);
2167b2fc195eSAndrew Gallatin 		}
2168b2fc195eSAndrew Gallatin 		free(sc->rx_small.info, M_DEVBUF);
2169b2fc195eSAndrew Gallatin 	}
2170b2fc195eSAndrew Gallatin 	if (sc->rx_big.info != NULL) {
2171b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->rx_big.mask; i++) {
2172b2fc195eSAndrew Gallatin 			if (sc->rx_big.info[i].map != NULL)
2173b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_big.dmat,
2174b2fc195eSAndrew Gallatin 						   sc->rx_big.info[i].map);
2175b2fc195eSAndrew Gallatin 		}
2176b2fc195eSAndrew Gallatin 		free(sc->rx_big.info, M_DEVBUF);
2177b2fc195eSAndrew Gallatin 	}
2178b2fc195eSAndrew Gallatin 	if (sc->rx_big.extra_map != NULL)
2179b2fc195eSAndrew Gallatin 		bus_dmamap_destroy(sc->rx_big.dmat,
2180b2fc195eSAndrew Gallatin 				   sc->rx_big.extra_map);
2181b2fc195eSAndrew Gallatin 	if (sc->rx_small.extra_map != NULL)
2182b2fc195eSAndrew Gallatin 		bus_dmamap_destroy(sc->rx_small.dmat,
2183b2fc195eSAndrew Gallatin 				   sc->rx_small.extra_map);
2184b2fc195eSAndrew Gallatin 	if (sc->tx.dmat != NULL)
2185b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->tx.dmat);
2186b2fc195eSAndrew Gallatin 	if (sc->rx_small.dmat != NULL)
2187b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->rx_small.dmat);
2188b2fc195eSAndrew Gallatin 	if (sc->rx_big.dmat != NULL)
2189b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->rx_big.dmat);
2190b2fc195eSAndrew Gallatin }
2191b2fc195eSAndrew Gallatin 
2192b2fc195eSAndrew Gallatin static int
21936d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
2194b2fc195eSAndrew Gallatin {
21956d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2196b2fc195eSAndrew Gallatin 	int tx_ring_size, rx_ring_size;
2197b2fc195eSAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
2198b2fc195eSAndrew Gallatin 	int i, err;
2199b2fc195eSAndrew Gallatin 	unsigned long bytes;
2200b2fc195eSAndrew Gallatin 
2201b2fc195eSAndrew Gallatin 	/* get ring sizes */
22025e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
2203b2fc195eSAndrew Gallatin 	tx_ring_size = cmd.data0;
22045e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
2205b2fc195eSAndrew Gallatin 	if (err != 0) {
2206b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Cannot determine ring sizes\n");
2207b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2208b2fc195eSAndrew Gallatin 	}
2209b2fc195eSAndrew Gallatin 
2210b2fc195eSAndrew Gallatin 	rx_ring_size = cmd.data0;
2211b2fc195eSAndrew Gallatin 
2212b2fc195eSAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
2213b2fc195eSAndrew Gallatin 	rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t);
2214b2fc195eSAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
221576bb9c5eSAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
221676bb9c5eSAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
2217b2fc195eSAndrew Gallatin 
2218b2fc195eSAndrew Gallatin 	sc->tx.mask = tx_ring_entries - 1;
2219b2fc195eSAndrew Gallatin 	sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1;
2220b2fc195eSAndrew Gallatin 
2221b2fc195eSAndrew Gallatin 	err = ENOMEM;
2222b2fc195eSAndrew Gallatin 
2223b2fc195eSAndrew Gallatin 	/* allocate the tx request copy block */
2224b2fc195eSAndrew Gallatin 	bytes = 8 +
22255e7d8541SAndrew Gallatin 		sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4);
2226b2fc195eSAndrew Gallatin 	sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
2227b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes == NULL)
2228b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2229b2fc195eSAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
2230b2fc195eSAndrew Gallatin 	sc->tx.req_list = (mcp_kreq_ether_send_t *)
2231b2fc195eSAndrew Gallatin 		((unsigned long)(sc->tx.req_bytes + 7) & ~7UL);
2232b2fc195eSAndrew Gallatin 
2233aed8e389SAndrew Gallatin 	/* allocate the tx busdma segment list */
2234aed8e389SAndrew Gallatin 	bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC;
2235aed8e389SAndrew Gallatin 	sc->tx.seg_list = (bus_dma_segment_t *)
2236aed8e389SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
2237aed8e389SAndrew Gallatin 	if (sc->tx.seg_list == NULL)
2238aed8e389SAndrew Gallatin 		goto abort_with_alloc;
2239aed8e389SAndrew Gallatin 
2240b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
2241b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow);
2242b2fc195eSAndrew Gallatin 	sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2243b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow == NULL)
2244b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2245b2fc195eSAndrew Gallatin 
2246b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow);
2247b2fc195eSAndrew Gallatin 	sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2248b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow == NULL)
2249b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2250b2fc195eSAndrew Gallatin 
2251b2fc195eSAndrew Gallatin 	/* allocate the host info rings */
2252b2fc195eSAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*sc->tx.info);
2253b2fc195eSAndrew Gallatin 	sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2254b2fc195eSAndrew Gallatin 	if (sc->tx.info == NULL)
2255b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2256b2fc195eSAndrew Gallatin 
2257b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.info);
2258b2fc195eSAndrew Gallatin 	sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2259b2fc195eSAndrew Gallatin 	if (sc->rx_small.info == NULL)
2260b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2261b2fc195eSAndrew Gallatin 
2262b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.info);
2263b2fc195eSAndrew Gallatin 	sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2264b2fc195eSAndrew Gallatin 	if (sc->rx_big.info == NULL)
2265b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2266b2fc195eSAndrew Gallatin 
2267b2fc195eSAndrew Gallatin 	/* allocate the busdma resources */
2268b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2269b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2270b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* boundary */
2271b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2272b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2273b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2274aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
2275aed8e389SAndrew Gallatin 				 MXGE_MAX_SEND_DESC/2,	/* num segs */
2276b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* maxsegsize */
2277b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2278b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2279b2fc195eSAndrew Gallatin 				 &sc->tx.dmat);		/* tag */
2280b2fc195eSAndrew Gallatin 
2281b2fc195eSAndrew Gallatin 	if (err != 0) {
2282b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
2283b2fc195eSAndrew Gallatin 			      err);
2284b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2285b2fc195eSAndrew Gallatin 	}
2286b2fc195eSAndrew Gallatin 
2287b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2288b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2289b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2290b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2291b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2292b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2293b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2294b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2295b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2296b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2297b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2298b2fc195eSAndrew Gallatin 				 &sc->rx_small.dmat);	/* tag */
2299b2fc195eSAndrew Gallatin 	if (err != 0) {
2300b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2301b2fc195eSAndrew Gallatin 			      err);
2302b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2303b2fc195eSAndrew Gallatin 	}
2304b2fc195eSAndrew Gallatin 
2305b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2306b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2307b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2308b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2309b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2310b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2311b2fc195eSAndrew Gallatin 				 4096,			/* maxsize */
2312b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2313b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2314b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2315b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2316b2fc195eSAndrew Gallatin 				 &sc->rx_big.dmat);	/* tag */
2317b2fc195eSAndrew Gallatin 	if (err != 0) {
2318b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2319b2fc195eSAndrew Gallatin 			      err);
2320b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2321b2fc195eSAndrew Gallatin 	}
2322b2fc195eSAndrew Gallatin 
2323b2fc195eSAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
2324b2fc195eSAndrew Gallatin 	   in each ring */
2325b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2326b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->tx.dmat, 0,
2327b2fc195eSAndrew Gallatin 					&sc->tx.info[i].map);
2328b2fc195eSAndrew Gallatin 		if (err != 0) {
2329b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
2330b2fc195eSAndrew Gallatin 			      err);
2331b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2332b2fc195eSAndrew Gallatin 		}
2333b2fc195eSAndrew Gallatin 	}
2334b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2335b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_small.dmat, 0,
2336b2fc195eSAndrew Gallatin 					&sc->rx_small.info[i].map);
2337b2fc195eSAndrew Gallatin 		if (err != 0) {
2338b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2339b2fc195eSAndrew Gallatin 				      err);
2340b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2341b2fc195eSAndrew Gallatin 		}
2342b2fc195eSAndrew Gallatin 	}
2343b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_small.dmat, 0,
2344b2fc195eSAndrew Gallatin 				&sc->rx_small.extra_map);
2345b2fc195eSAndrew Gallatin 	if (err != 0) {
2346b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2347b2fc195eSAndrew Gallatin 			      err);
2348b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2349b2fc195eSAndrew Gallatin 	}
2350b2fc195eSAndrew Gallatin 
2351b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2352b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_big.dmat, 0,
2353b2fc195eSAndrew Gallatin 					&sc->rx_big.info[i].map);
2354b2fc195eSAndrew Gallatin 		if (err != 0) {
2355b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2356b2fc195eSAndrew Gallatin 			      err);
2357b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2358b2fc195eSAndrew Gallatin 		}
2359b2fc195eSAndrew Gallatin 	}
2360b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_big.dmat, 0,
2361b2fc195eSAndrew Gallatin 				&sc->rx_big.extra_map);
2362b2fc195eSAndrew Gallatin 	if (err != 0) {
2363b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2364b2fc195eSAndrew Gallatin 			      err);
2365b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2366b2fc195eSAndrew Gallatin 	}
2367b2fc195eSAndrew Gallatin 	return 0;
2368b2fc195eSAndrew Gallatin 
2369b2fc195eSAndrew Gallatin abort_with_alloc:
23706d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2371b2fc195eSAndrew Gallatin 
2372b2fc195eSAndrew Gallatin abort_with_nothing:
2373b2fc195eSAndrew Gallatin 	return err;
2374b2fc195eSAndrew Gallatin }
2375b2fc195eSAndrew Gallatin 
2376b2fc195eSAndrew Gallatin static int
23776d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc)
2378b2fc195eSAndrew Gallatin {
23796d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2380b2fc195eSAndrew Gallatin 	int i, err;
2381b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
23820fa7f681SAndrew Gallatin 	bus_addr_t bus;
2383b2fc195eSAndrew Gallatin 
2384b2fc195eSAndrew Gallatin 
23857d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
23867d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
23877d542e2dSAndrew Gallatin 
23886d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2389b2fc195eSAndrew Gallatin 	if (err != 0) {
2390b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
2391b2fc195eSAndrew Gallatin 		return EIO;
2392b2fc195eSAndrew Gallatin 	}
2393b2fc195eSAndrew Gallatin 
2394b2fc195eSAndrew Gallatin 	if (MCLBYTES >=
23955e7d8541SAndrew Gallatin 	    sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD)
2396b2fc195eSAndrew Gallatin 		sc->big_bytes = MCLBYTES;
2397b2fc195eSAndrew Gallatin 	else
2398b2fc195eSAndrew Gallatin 		sc->big_bytes = MJUMPAGESIZE;
2399b2fc195eSAndrew Gallatin 
24006d87a65dSAndrew Gallatin 	err = mxge_alloc_rings(sc);
2401b2fc195eSAndrew Gallatin 	if (err != 0) {
2402b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
2403b2fc195eSAndrew Gallatin 		return err;
2404b2fc195eSAndrew Gallatin 	}
2405b2fc195eSAndrew Gallatin 
2406b2fc195eSAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
2407b2fc195eSAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
24086d87a65dSAndrew Gallatin 			     mxge_intr, sc, &sc->ih);
2409b2fc195eSAndrew Gallatin 	if (err != 0) {
2410b2fc195eSAndrew Gallatin 		goto abort_with_rings;
2411b2fc195eSAndrew Gallatin 	}
2412b2fc195eSAndrew Gallatin 
2413b2fc195eSAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
2414b2fc195eSAndrew Gallatin 
24155e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
2416b2fc195eSAndrew Gallatin 	sc->tx.lanai =
2417b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
24186d87a65dSAndrew Gallatin 	err |= mxge_send_cmd(sc,
24195e7d8541SAndrew Gallatin 				 MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
2420b2fc195eSAndrew Gallatin 	sc->rx_small.lanai =
2421b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
24225e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
2423b2fc195eSAndrew Gallatin 	sc->rx_big.lanai =
2424b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
2425b2fc195eSAndrew Gallatin 
2426b2fc195eSAndrew Gallatin 	if (err != 0) {
2427b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
2428b2fc195eSAndrew Gallatin 			      "failed to get ring sizes or locations\n");
2429b2fc195eSAndrew Gallatin 		err = EIO;
2430b2fc195eSAndrew Gallatin 		goto abort_with_irq;
2431b2fc195eSAndrew Gallatin 	}
2432b2fc195eSAndrew Gallatin 
2433b2fc195eSAndrew Gallatin 	if (sc->wc) {
24340fa7f681SAndrew Gallatin 		sc->tx.wc_fifo = sc->sram + MXGEFW_ETH_SEND_4;
24350fa7f681SAndrew Gallatin 		sc->rx_small.wc_fifo = sc->sram + MXGEFW_ETH_RECV_SMALL;
24360fa7f681SAndrew Gallatin 		sc->rx_big.wc_fifo = sc->sram + MXGEFW_ETH_RECV_BIG;
2437b2fc195eSAndrew Gallatin 	} else {
2438b2fc195eSAndrew Gallatin 		sc->tx.wc_fifo = 0;
2439b2fc195eSAndrew Gallatin 		sc->rx_small.wc_fifo = 0;
2440b2fc195eSAndrew Gallatin 		sc->rx_big.wc_fifo = 0;
2441b2fc195eSAndrew Gallatin 	}
2442b2fc195eSAndrew Gallatin 
2443b2fc195eSAndrew Gallatin 
2444b2fc195eSAndrew Gallatin 	/* stock receive rings */
2445b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2446b2fc195eSAndrew Gallatin 		map = sc->rx_small.info[i].map;
24476d87a65dSAndrew Gallatin 		err = mxge_get_buf_small(sc, map, i);
2448b2fc195eSAndrew Gallatin 		if (err) {
2449b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
2450b2fc195eSAndrew Gallatin 				      i, sc->rx_small.mask + 1);
2451b2fc195eSAndrew Gallatin 			goto abort;
2452b2fc195eSAndrew Gallatin 		}
2453b2fc195eSAndrew Gallatin 	}
2454b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2455b2fc195eSAndrew Gallatin 		map = sc->rx_big.info[i].map;
24566d87a65dSAndrew Gallatin 		err = mxge_get_buf_big(sc, map, i);
2457b2fc195eSAndrew Gallatin 		if (err) {
2458b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
2459b2fc195eSAndrew Gallatin 				      i, sc->rx_big.mask + 1);
2460b2fc195eSAndrew Gallatin 			goto abort;
2461b2fc195eSAndrew Gallatin 		}
2462b2fc195eSAndrew Gallatin 	}
2463b2fc195eSAndrew Gallatin 
2464b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
2465b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
2466b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
2467b2fc195eSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN;
24685e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
2469b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
24705e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
2471b2fc195eSAndrew Gallatin 			     &cmd);
2472b2fc195eSAndrew Gallatin 	cmd.data0 = sc->big_bytes;
24735e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
24740fa7f681SAndrew Gallatin 
24750fa7f681SAndrew Gallatin 	if (err != 0) {
24760fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
24770fa7f681SAndrew Gallatin 		goto abort;
24780fa7f681SAndrew Gallatin 	}
24790fa7f681SAndrew Gallatin 
2480b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
24816d87a65dSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr);
24826d87a65dSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr);
24830fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
24840fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
24850fa7f681SAndrew Gallatin 
24860fa7f681SAndrew Gallatin 	if (err != 0) {
24870fa7f681SAndrew Gallatin 		bus = sc->fw_stats_dma.bus_addr;
24880fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
24890fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
24900fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
24910fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
24920fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
24930fa7f681SAndrew Gallatin 				    &cmd);
24940fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
24950fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
24960fa7f681SAndrew Gallatin 	} else {
24970fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
24980fa7f681SAndrew Gallatin 	}
2499b2fc195eSAndrew Gallatin 
2500b2fc195eSAndrew Gallatin 	if (err != 0) {
2501b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
2502b2fc195eSAndrew Gallatin 		goto abort;
2503b2fc195eSAndrew Gallatin 	}
2504b2fc195eSAndrew Gallatin 
2505b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
25065e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
2507b2fc195eSAndrew Gallatin 	if (err) {
2508b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
2509b2fc195eSAndrew Gallatin 		goto abort;
2510b2fc195eSAndrew Gallatin 	}
2511b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
2512b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2513b2fc195eSAndrew Gallatin 
2514b2fc195eSAndrew Gallatin 	return 0;
2515b2fc195eSAndrew Gallatin 
2516b2fc195eSAndrew Gallatin 
2517b2fc195eSAndrew Gallatin abort:
25186d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2519b2fc195eSAndrew Gallatin abort_with_irq:
2520b2fc195eSAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
2521b2fc195eSAndrew Gallatin abort_with_rings:
25226d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2523b2fc195eSAndrew Gallatin 	return err;
2524b2fc195eSAndrew Gallatin }
2525b2fc195eSAndrew Gallatin 
2526b2fc195eSAndrew Gallatin static int
25276d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
2528b2fc195eSAndrew Gallatin {
25296d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2530b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
2531b2fc195eSAndrew Gallatin 
2532b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2533b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
2534b2fc195eSAndrew Gallatin 	mb();
25355e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
2536b2fc195eSAndrew Gallatin 	if (err) {
2537b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
2538b2fc195eSAndrew Gallatin 	}
2539b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2540b2fc195eSAndrew Gallatin 		/* wait for down irq */
25416d87a65dSAndrew Gallatin 		(void)tsleep(&sc->down_cnt, PWAIT, "down mxge", hz);
2542b2fc195eSAndrew Gallatin 	}
2543b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2544b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
2545b2fc195eSAndrew Gallatin 	}
2546b2fc195eSAndrew Gallatin 	if (sc->ih != NULL)
2547b2fc195eSAndrew Gallatin 		bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
25486d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
25496d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2550b2fc195eSAndrew Gallatin 	return 0;
2551b2fc195eSAndrew Gallatin }
2552b2fc195eSAndrew Gallatin 
2553b2fc195eSAndrew Gallatin 
2554b2fc195eSAndrew Gallatin static int
25556d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
2556b2fc195eSAndrew Gallatin {
2557b2fc195eSAndrew Gallatin 	return EINVAL;
2558b2fc195eSAndrew Gallatin }
2559b2fc195eSAndrew Gallatin 
2560b2fc195eSAndrew Gallatin static int
25616d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
2562b2fc195eSAndrew Gallatin {
2563b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
2564b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
2565b2fc195eSAndrew Gallatin 	int err = 0;
2566b2fc195eSAndrew Gallatin 
2567b2fc195eSAndrew Gallatin 
2568b2fc195eSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN;
25696d87a65dSAndrew Gallatin 	if ((real_mtu > MXGE_MAX_ETHER_MTU) ||
2570b2fc195eSAndrew Gallatin 	    real_mtu < 60)
2571b2fc195eSAndrew Gallatin 		return EINVAL;
2572b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
2573b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
2574b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
2575b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
25766d87a65dSAndrew Gallatin 		mxge_close(sc);
25776d87a65dSAndrew Gallatin 		err = mxge_open(sc);
2578b2fc195eSAndrew Gallatin 		if (err != 0) {
2579b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
25806d87a65dSAndrew Gallatin 			mxge_close(sc);
25816d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
2582b2fc195eSAndrew Gallatin 		}
2583b2fc195eSAndrew Gallatin 	}
2584b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
2585b2fc195eSAndrew Gallatin 	return err;
2586b2fc195eSAndrew Gallatin }
2587b2fc195eSAndrew Gallatin 
2588b2fc195eSAndrew Gallatin static void
25896d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
2590b2fc195eSAndrew Gallatin {
25916d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2592b2fc195eSAndrew Gallatin 
2593b2fc195eSAndrew Gallatin 
2594b2fc195eSAndrew Gallatin 	if (sc == NULL)
2595b2fc195eSAndrew Gallatin 		return;
2596b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
2597b2fc195eSAndrew Gallatin 	ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0;
2598b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
2599b2fc195eSAndrew Gallatin 	ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0;
2600b2fc195eSAndrew Gallatin }
2601b2fc195eSAndrew Gallatin 
2602b2fc195eSAndrew Gallatin static int
26036d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
2604b2fc195eSAndrew Gallatin {
26056d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2606b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
2607b2fc195eSAndrew Gallatin 	int err, mask;
2608b2fc195eSAndrew Gallatin 
2609b2fc195eSAndrew Gallatin 	err = 0;
2610b2fc195eSAndrew Gallatin 	switch (command) {
2611b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
2612b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
2613b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
2614b2fc195eSAndrew Gallatin 		break;
2615b2fc195eSAndrew Gallatin 
2616b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
26176d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
2618b2fc195eSAndrew Gallatin 		break;
2619b2fc195eSAndrew Gallatin 
2620b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
2621b2fc195eSAndrew Gallatin 		sx_xlock(&sc->driver_lock);
2622b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
2623b2fc195eSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
26246d87a65dSAndrew Gallatin 				err = mxge_open(sc);
26250fa7f681SAndrew Gallatin 			else {
26260fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
26270fa7f681SAndrew Gallatin 				   flag chages */
26280fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
26290fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
26300fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
26310fa7f681SAndrew Gallatin 			}
2632b2fc195eSAndrew Gallatin 		} else {
2633b2fc195eSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
26346d87a65dSAndrew Gallatin 				mxge_close(sc);
2635b2fc195eSAndrew Gallatin 		}
2636b2fc195eSAndrew Gallatin 		sx_xunlock(&sc->driver_lock);
2637b2fc195eSAndrew Gallatin 		break;
2638b2fc195eSAndrew Gallatin 
2639b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
2640b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
26410fa7f681SAndrew Gallatin 		sx_xlock(&sc->driver_lock);
26420fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
26430fa7f681SAndrew Gallatin 		sx_xunlock(&sc->driver_lock);
2644b2fc195eSAndrew Gallatin 		break;
2645b2fc195eSAndrew Gallatin 
2646b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
2647b2fc195eSAndrew Gallatin 		sx_xlock(&sc->driver_lock);
2648b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
2649b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
2650b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
2651aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
2652aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
2653aed8e389SAndrew Gallatin 						      | CSUM_TSO);
2654b2fc195eSAndrew Gallatin 			} else {
2655b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
2656b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
2657b2fc195eSAndrew Gallatin 			}
2658b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
2659b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
2660b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
26615e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
2662b2fc195eSAndrew Gallatin 			} else {
2663b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
26645e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
2665b2fc195eSAndrew Gallatin 			}
2666b2fc195eSAndrew Gallatin 		}
2667aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
2668aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
2669aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
2670aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
2671aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
2672aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
2673aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
2674aed8e389SAndrew Gallatin 			} else {
2675aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
2676aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
2677aed8e389SAndrew Gallatin 				err = EINVAL;
2678aed8e389SAndrew Gallatin 			}
2679aed8e389SAndrew Gallatin 		}
2680b2fc195eSAndrew Gallatin 		sx_xunlock(&sc->driver_lock);
2681b2fc195eSAndrew Gallatin 		break;
2682b2fc195eSAndrew Gallatin 
2683b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
2684b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
2685b2fc195eSAndrew Gallatin 				    &sc->media, command);
2686b2fc195eSAndrew Gallatin                 break;
2687b2fc195eSAndrew Gallatin 
2688b2fc195eSAndrew Gallatin 	default:
2689b2fc195eSAndrew Gallatin 		err = ENOTTY;
2690b2fc195eSAndrew Gallatin         }
2691b2fc195eSAndrew Gallatin 	return err;
2692b2fc195eSAndrew Gallatin }
2693b2fc195eSAndrew Gallatin 
2694b2fc195eSAndrew Gallatin static void
26956d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
2696b2fc195eSAndrew Gallatin {
2697b2fc195eSAndrew Gallatin 
26986d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
26996d87a65dSAndrew Gallatin 			  &mxge_flow_control);
27006d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
27016d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
27026d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
27036d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
2704d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
2705d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
27065e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
27075e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
27085e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
27095e7d8541SAndrew Gallatin 			  &mxge_verbose);
2710b2fc195eSAndrew Gallatin 
27115e7d8541SAndrew Gallatin 	if (bootverbose)
27125e7d8541SAndrew Gallatin 		mxge_verbose = 1;
27136d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
27146d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
27156d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
2716b2fc195eSAndrew Gallatin }
2717b2fc195eSAndrew Gallatin 
2718b2fc195eSAndrew Gallatin static int
27196d87a65dSAndrew Gallatin mxge_attach(device_t dev)
2720b2fc195eSAndrew Gallatin {
27216d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
2722b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2723b2fc195eSAndrew Gallatin 	size_t bytes;
2724d91b1b49SAndrew Gallatin 	int count, rid, err, reg;
2725d91b1b49SAndrew Gallatin 	uint16_t cmd, pectl, lnk;
2726b2fc195eSAndrew Gallatin 
2727b2fc195eSAndrew Gallatin 	sc->dev = dev;
27286d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
2729b2fc195eSAndrew Gallatin 
2730b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
2731b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2732b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2733b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2734b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2735b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2736aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
27375e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
2738b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2739b2fc195eSAndrew Gallatin 				 0,			/* flags */
2740b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2741b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
2742b2fc195eSAndrew Gallatin 
2743b2fc195eSAndrew Gallatin 	if (err != 0) {
2744b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
2745b2fc195eSAndrew Gallatin 			      err);
2746b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2747b2fc195eSAndrew Gallatin 	}
2748b2fc195eSAndrew Gallatin 
2749b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
2750b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
2751b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
2752b2fc195eSAndrew Gallatin 		err = ENOSPC;
2753b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
2754b2fc195eSAndrew Gallatin 	}
2755b2fc195eSAndrew Gallatin 	mtx_init(&sc->cmd_lock, NULL,
2756b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
2757b2fc195eSAndrew Gallatin 	mtx_init(&sc->tx_lock, device_get_nameunit(dev),
2758b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
2759b2fc195eSAndrew Gallatin 	sx_init(&sc->driver_lock, device_get_nameunit(dev));
2760b2fc195eSAndrew Gallatin 
2761d91b1b49SAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
2762d91b1b49SAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
2763d91b1b49SAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
2764d91b1b49SAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
2765d91b1b49SAndrew Gallatin 
2766d91b1b49SAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
2767d91b1b49SAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
2768d91b1b49SAndrew Gallatin 		pci_write_config(dev, reg + 0x8, 2, pectl);
2769d91b1b49SAndrew Gallatin 	}
2770d91b1b49SAndrew Gallatin 
2771b2fc195eSAndrew Gallatin 	/* Enable DMA and Memory space access */
2772b2fc195eSAndrew Gallatin 	pci_enable_busmaster(dev);
2773b2fc195eSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2774b2fc195eSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
2775b2fc195eSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2776b2fc195eSAndrew Gallatin 
2777b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
2778b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
2779b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
2780b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
2781b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
2782b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
2783b2fc195eSAndrew Gallatin 		err = ENXIO;
2784b2fc195eSAndrew Gallatin 		goto abort_with_lock;
2785b2fc195eSAndrew Gallatin 	}
2786b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
2787b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
2788b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
2789b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
2790b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
2791b2fc195eSAndrew Gallatin 		err = ENXIO;
2792b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2793b2fc195eSAndrew Gallatin 	}
2794b2fc195eSAndrew Gallatin 
2795b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
2796b2fc195eSAndrew Gallatin 	   lanai SRAM */
27976d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
2798b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
2799b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
28006d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
2801b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
28026d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
28036d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
2804b2fc195eSAndrew Gallatin 	if (err != 0)
2805b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2806b2fc195eSAndrew Gallatin 
2807b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
28086d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
2809b2fc195eSAndrew Gallatin 
2810b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
28116d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
28126d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
2813b2fc195eSAndrew Gallatin 	if (err != 0)
2814b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2815b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
28166d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
2817b2fc195eSAndrew Gallatin 	if (err != 0)
2818b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
2819b2fc195eSAndrew Gallatin 
28206d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->fw_stats_dma,
2821b2fc195eSAndrew Gallatin 			     sizeof (*sc->fw_stats), 64);
2822b2fc195eSAndrew Gallatin 	if (err != 0)
2823b2fc195eSAndrew Gallatin 		goto abort_with_zeropad_dma;
28245e7d8541SAndrew Gallatin 	sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr;
2825b2fc195eSAndrew Gallatin 
2826b2fc195eSAndrew Gallatin 
2827b2fc195eSAndrew Gallatin 	/* allocate interrupt queues */
28285e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);
28295e7d8541SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096);
2830b2fc195eSAndrew Gallatin 	if (err != 0)
28315e7d8541SAndrew Gallatin 		goto abort_with_fw_stats;
28325e7d8541SAndrew Gallatin 	sc->rx_done.entry = sc->rx_done.dma.addr;
28335e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
2834dc8731d4SAndrew Gallatin 
2835b2fc195eSAndrew Gallatin 	/* Add our ithread  */
2836dc8731d4SAndrew Gallatin 	count = pci_msi_count(dev);
2837dc8731d4SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(dev, &count) == 0) {
2838dc8731d4SAndrew Gallatin 		rid = 1;
2839dc8731d4SAndrew Gallatin 		sc->msi_enabled = 1;
2840dc8731d4SAndrew Gallatin 	} else {
2841b2fc195eSAndrew Gallatin 		rid = 0;
2842dc8731d4SAndrew Gallatin 	}
2843b2fc195eSAndrew Gallatin 	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0,
2844b2fc195eSAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
2845b2fc195eSAndrew Gallatin 	if (sc->irq_res == NULL) {
2846b2fc195eSAndrew Gallatin 		device_printf(dev, "could not alloc interrupt\n");
28475e7d8541SAndrew Gallatin 		goto abort_with_rx_done;
2848b2fc195eSAndrew Gallatin 	}
2849d91b1b49SAndrew Gallatin 	if (mxge_verbose)
2850dc8731d4SAndrew Gallatin 		device_printf(dev, "using %s irq %ld\n",
2851dc8731d4SAndrew Gallatin 			      sc->msi_enabled ? "MSI" : "INTx",
2852dc8731d4SAndrew Gallatin 			      rman_get_start(sc->irq_res));
2853b2fc195eSAndrew Gallatin 	/* load the firmware */
28546d87a65dSAndrew Gallatin 	mxge_select_firmware(sc);
2855b2fc195eSAndrew Gallatin 
28566d87a65dSAndrew Gallatin 	err = mxge_load_firmware(sc);
2857b2fc195eSAndrew Gallatin 	if (err != 0)
2858b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
28595e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
28606d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2861b2fc195eSAndrew Gallatin 	if (err != 0)
2862b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
2863b2fc195eSAndrew Gallatin 
2864b2fc195eSAndrew Gallatin 	/* hook into the network stack */
2865b2fc195eSAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
2866b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
2867aed8e389SAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4;
2868aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
2869b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
28705e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
28716d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
2872b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
2873b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
28746d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
28756d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
28766d87a65dSAndrew Gallatin 	ifp->if_watchdog = mxge_watchdog;
2877b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
2878b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
28796d87a65dSAndrew Gallatin 	ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN;
2880b2fc195eSAndrew Gallatin 
2881b2fc195eSAndrew Gallatin 	/* Initialise the ifmedia structure */
28826d87a65dSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
28836d87a65dSAndrew Gallatin 		     mxge_media_status);
2884b2fc195eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
28856d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
2886b2fc195eSAndrew Gallatin 	return 0;
2887b2fc195eSAndrew Gallatin 
2888b2fc195eSAndrew Gallatin abort_with_irq_res:
2889dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
2890dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
2891dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
2892dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
28935e7d8541SAndrew Gallatin abort_with_rx_done:
28945e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
28955e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
28965e7d8541SAndrew Gallatin abort_with_fw_stats:
28976d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
2898b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
28996d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
2900b2fc195eSAndrew Gallatin abort_with_cmd_dma:
29016d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
2902b2fc195eSAndrew Gallatin abort_with_mem_res:
2903b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
2904b2fc195eSAndrew Gallatin abort_with_lock:
2905b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
2906b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->cmd_lock);
2907b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->tx_lock);
2908b2fc195eSAndrew Gallatin 	sx_destroy(&sc->driver_lock);
2909b2fc195eSAndrew Gallatin 	if_free(ifp);
2910b2fc195eSAndrew Gallatin abort_with_parent_dmat:
2911b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
2912b2fc195eSAndrew Gallatin 
2913b2fc195eSAndrew Gallatin abort_with_nothing:
2914b2fc195eSAndrew Gallatin 	return err;
2915b2fc195eSAndrew Gallatin }
2916b2fc195eSAndrew Gallatin 
2917b2fc195eSAndrew Gallatin static int
29186d87a65dSAndrew Gallatin mxge_detach(device_t dev)
2919b2fc195eSAndrew Gallatin {
29206d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
2921b2fc195eSAndrew Gallatin 
2922b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
2923b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
29246d87a65dSAndrew Gallatin 		mxge_close(sc);
2925b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
2926b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
2927091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
2928dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
2929dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
2930dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
2931dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
2932dc8731d4SAndrew Gallatin 
29335e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
29345e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
29356d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
29366d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
29376d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
2938b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
2939b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
2940b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->cmd_lock);
2941b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->tx_lock);
2942b2fc195eSAndrew Gallatin 	sx_destroy(&sc->driver_lock);
2943b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
2944b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
2945b2fc195eSAndrew Gallatin 	return 0;
2946b2fc195eSAndrew Gallatin }
2947b2fc195eSAndrew Gallatin 
2948b2fc195eSAndrew Gallatin static int
29496d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
2950b2fc195eSAndrew Gallatin {
2951b2fc195eSAndrew Gallatin 	return 0;
2952b2fc195eSAndrew Gallatin }
2953b2fc195eSAndrew Gallatin 
2954b2fc195eSAndrew Gallatin /*
2955b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
2956b2fc195eSAndrew Gallatin 
2957b2fc195eSAndrew Gallatin   Local Variables:
2958b2fc195eSAndrew Gallatin   c-file-style:"linux"
2959b2fc195eSAndrew Gallatin   tab-width:8
2960b2fc195eSAndrew Gallatin   End:
2961b2fc195eSAndrew Gallatin */
2962