xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 091feecd1218175201e5b18b87915ad8d7f52b5b)
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>
68b2fc195eSAndrew Gallatin 
69b2fc195eSAndrew Gallatin #include <machine/bus.h>
70b2fc195eSAndrew Gallatin #include <machine/resource.h>
71b2fc195eSAndrew Gallatin #include <sys/bus.h>
72b2fc195eSAndrew Gallatin #include <sys/rman.h>
73b2fc195eSAndrew Gallatin 
74b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
75b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
76b2fc195eSAndrew Gallatin 
77b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
78b2fc195eSAndrew Gallatin #include <vm/pmap.h>
79b2fc195eSAndrew Gallatin 
806d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
816d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
826d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
83b2fc195eSAndrew Gallatin 
84b2fc195eSAndrew Gallatin /* tunable params */
856d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
865e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024;
876d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
885e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
896d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
905e7d8541SAndrew Gallatin static int mxge_verbose = 0;
916d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
926d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
93b2fc195eSAndrew Gallatin 
946d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
956d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
966d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
976d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
986d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
99b2fc195eSAndrew Gallatin 
1006d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
101b2fc195eSAndrew Gallatin {
102b2fc195eSAndrew Gallatin   /* Device interface */
1036d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1046d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1056d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1066d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
107b2fc195eSAndrew Gallatin   {0, 0}
108b2fc195eSAndrew Gallatin };
109b2fc195eSAndrew Gallatin 
1106d87a65dSAndrew Gallatin static driver_t mxge_driver =
111b2fc195eSAndrew Gallatin {
1126d87a65dSAndrew Gallatin   "mxge",
1136d87a65dSAndrew Gallatin   mxge_methods,
1146d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
115b2fc195eSAndrew Gallatin };
116b2fc195eSAndrew Gallatin 
1176d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
118b2fc195eSAndrew Gallatin 
119b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1206d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1216d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
122b2fc195eSAndrew Gallatin 
123b2fc195eSAndrew Gallatin static int
1246d87a65dSAndrew Gallatin mxge_probe(device_t dev)
125b2fc195eSAndrew Gallatin {
1266d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1276d87a65dSAndrew Gallatin       (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) {
128b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
129b2fc195eSAndrew Gallatin 	  return 0;
130b2fc195eSAndrew Gallatin   }
131b2fc195eSAndrew Gallatin   return ENXIO;
132b2fc195eSAndrew Gallatin }
133b2fc195eSAndrew Gallatin 
134b2fc195eSAndrew Gallatin static void
1356d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
136b2fc195eSAndrew Gallatin {
137b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
138b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
139b2fc195eSAndrew Gallatin 	vm_offset_t len;
140b2fc195eSAndrew Gallatin 	int err, action;
141b2fc195eSAndrew Gallatin 
142b2fc195eSAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
143b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
144b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
145b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
146b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
147b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1486d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
149b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
150b2fc195eSAndrew Gallatin 	if (err != 0) {
151b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
152b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
153b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
154b2fc195eSAndrew Gallatin 	} else {
155b2fc195eSAndrew Gallatin 		sc->wc = 1;
156b2fc195eSAndrew Gallatin 	}
157b2fc195eSAndrew Gallatin }
158b2fc195eSAndrew Gallatin 
159b2fc195eSAndrew Gallatin 
160b2fc195eSAndrew Gallatin /* callback to get our DMA address */
161b2fc195eSAndrew Gallatin static void
1626d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
163b2fc195eSAndrew Gallatin 			 int error)
164b2fc195eSAndrew Gallatin {
165b2fc195eSAndrew Gallatin 	if (error == 0) {
166b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
167b2fc195eSAndrew Gallatin 	}
168b2fc195eSAndrew Gallatin }
169b2fc195eSAndrew Gallatin 
170b2fc195eSAndrew Gallatin static int
1716d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
172b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
173b2fc195eSAndrew Gallatin {
174b2fc195eSAndrew Gallatin 	int err;
175b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
176b2fc195eSAndrew Gallatin 
177b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
178b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
179b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
180b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
181b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
182b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
183b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
184b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
185b2fc195eSAndrew Gallatin 				 1,			/* num segs */
186b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
187b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
188b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
189b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
190b2fc195eSAndrew Gallatin 	if (err != 0) {
191b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
192b2fc195eSAndrew Gallatin 		return err;
193b2fc195eSAndrew Gallatin 	}
194b2fc195eSAndrew Gallatin 
195b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
196b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
197b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
198b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
199b2fc195eSAndrew Gallatin 	if (err != 0) {
200b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
201b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
202b2fc195eSAndrew Gallatin 	}
203b2fc195eSAndrew Gallatin 
204b2fc195eSAndrew Gallatin 	/* load the memory */
205b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2066d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
207b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
208b2fc195eSAndrew Gallatin 	if (err != 0) {
209b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
210b2fc195eSAndrew Gallatin 		goto abort_with_mem;
211b2fc195eSAndrew Gallatin 	}
212b2fc195eSAndrew Gallatin 	return 0;
213b2fc195eSAndrew Gallatin 
214b2fc195eSAndrew Gallatin abort_with_mem:
215b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
216b2fc195eSAndrew Gallatin abort_with_dmat:
217b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
218b2fc195eSAndrew Gallatin 	return err;
219b2fc195eSAndrew Gallatin }
220b2fc195eSAndrew Gallatin 
221b2fc195eSAndrew Gallatin 
222b2fc195eSAndrew Gallatin static void
2236d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
224b2fc195eSAndrew Gallatin {
225b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
226b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
227b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
228b2fc195eSAndrew Gallatin }
229b2fc195eSAndrew Gallatin 
230b2fc195eSAndrew Gallatin /*
231b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
232b2fc195eSAndrew Gallatin  * SN=x\0
233b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
234b2fc195eSAndrew Gallatin  * PC=text\0
235b2fc195eSAndrew Gallatin  */
236b2fc195eSAndrew Gallatin 
237b2fc195eSAndrew Gallatin static int
2386d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
239b2fc195eSAndrew Gallatin {
2406d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
241b2fc195eSAndrew Gallatin 
242b2fc195eSAndrew Gallatin 	char *ptr, *limit;
243b2fc195eSAndrew Gallatin 	int i, found_mac;
244b2fc195eSAndrew Gallatin 
245b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2466d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
247b2fc195eSAndrew Gallatin 	found_mac = 0;
248b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
249b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2505e7d8541SAndrew Gallatin 			ptr += 1;
251b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
252b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2535e7d8541SAndrew Gallatin 				ptr += 3;
254b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
255b2fc195eSAndrew Gallatin 					goto abort;
256b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
257b2fc195eSAndrew Gallatin 				found_mac = 1;
258b2fc195eSAndrew Gallatin 			}
2595e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2605e7d8541SAndrew Gallatin 			ptr += 3;
2615e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2625e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2635e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2645e7d8541SAndrew Gallatin 			ptr += 3;
2655e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2665e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
267b2fc195eSAndrew Gallatin 		}
2686d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
269b2fc195eSAndrew Gallatin 	}
270b2fc195eSAndrew Gallatin 
271b2fc195eSAndrew Gallatin 	if (found_mac)
272b2fc195eSAndrew Gallatin 		return 0;
273b2fc195eSAndrew Gallatin 
274b2fc195eSAndrew Gallatin  abort:
275b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
276b2fc195eSAndrew Gallatin 
277b2fc195eSAndrew Gallatin 	return ENXIO;
278b2fc195eSAndrew Gallatin }
279b2fc195eSAndrew Gallatin 
280b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
281b2fc195eSAndrew Gallatin static int
2826d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
283b2fc195eSAndrew Gallatin {
284b2fc195eSAndrew Gallatin 	uint32_t val;
285b2fc195eSAndrew Gallatin 	unsigned long off;
286b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
287b2fc195eSAndrew Gallatin 	uint16_t vendor_id, device_id;
288b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
289b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
290b2fc195eSAndrew Gallatin 
291b2fc195eSAndrew Gallatin 	/* XXXX
292b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
293b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
294b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
295b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
296b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
297b2fc195eSAndrew Gallatin 	*/
298b2fc195eSAndrew Gallatin #if 0
299b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
300b2fc195eSAndrew Gallatin 	   config space */
301b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
302b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
303b2fc195eSAndrew Gallatin 		val |= 0x40;
304b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
305b2fc195eSAndrew Gallatin 		return 0;
306b2fc195eSAndrew Gallatin 	}
307b2fc195eSAndrew Gallatin #endif
308b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
309b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
310b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
311b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
312b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
313b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
314b2fc195eSAndrew Gallatin 	 */
315b2fc195eSAndrew Gallatin 
316b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
317b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
318b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
319b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
320b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
321b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
322b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
323b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
324b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
325b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
326b2fc195eSAndrew Gallatin 
327b2fc195eSAndrew Gallatin 	off =  0xe0000000UL
328b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
329b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
330b2fc195eSAndrew Gallatin 						 + 8 * slot);
331b2fc195eSAndrew Gallatin 
332b2fc195eSAndrew Gallatin 	/* map it into the kernel */
333b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
334b2fc195eSAndrew Gallatin 
335b2fc195eSAndrew Gallatin 
336b2fc195eSAndrew Gallatin 	if (va == NULL) {
337b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
338b2fc195eSAndrew Gallatin 		return EIO;
339b2fc195eSAndrew Gallatin 	}
340b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
341b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
342b2fc195eSAndrew Gallatin 
343b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
344b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
345b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
346b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
347b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
348b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
349b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
350b2fc195eSAndrew Gallatin 		return EIO;
351b2fc195eSAndrew Gallatin 	}
352b2fc195eSAndrew Gallatin 
353b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
354b2fc195eSAndrew Gallatin 	val = *ptr32;
355b2fc195eSAndrew Gallatin 
356b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
357b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
358b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
359b2fc195eSAndrew Gallatin 		return EIO;
360b2fc195eSAndrew Gallatin 	}
361b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
362b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3635e7d8541SAndrew Gallatin 	if (mxge_verbose)
364b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
3655e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
3665e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
367b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
368b2fc195eSAndrew Gallatin 	return 0;
369b2fc195eSAndrew Gallatin }
370b2fc195eSAndrew Gallatin #else
371b2fc195eSAndrew Gallatin static int
3726d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
373b2fc195eSAndrew Gallatin {
374b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
375b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
376b2fc195eSAndrew Gallatin 	return ENXIO;
377b2fc195eSAndrew Gallatin }
378b2fc195eSAndrew Gallatin #endif
379b2fc195eSAndrew Gallatin /*
380b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
381b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
382b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
383b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
384b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
385b2fc195eSAndrew Gallatin  *
386b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
387b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
388b2fc195eSAndrew Gallatin  *
389b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
390b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
391b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
392b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
393b2fc195eSAndrew Gallatin  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
394b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
395b2fc195eSAndrew Gallatin  * firmware image, and set tx.boundary to 4KB.
396b2fc195eSAndrew Gallatin  */
397b2fc195eSAndrew Gallatin 
398b2fc195eSAndrew Gallatin static void
3996d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
400b2fc195eSAndrew Gallatin {
401b2fc195eSAndrew Gallatin 	int err, aligned = 0;
402b2fc195eSAndrew Gallatin 	device_t pdev;
403b2fc195eSAndrew Gallatin 	uint16_t pvend, pdid;
404b2fc195eSAndrew Gallatin 
405b2fc195eSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
406b2fc195eSAndrew Gallatin 	if (pdev == NULL) {
407b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
408b2fc195eSAndrew Gallatin 		goto abort;
409b2fc195eSAndrew Gallatin 	}
410b2fc195eSAndrew Gallatin 	pvend = pci_read_config(pdev, PCIR_VENDOR, 2);
411b2fc195eSAndrew Gallatin 	pdid = pci_read_config(pdev, PCIR_DEVICE, 2);
412b2fc195eSAndrew Gallatin 
413b2fc195eSAndrew Gallatin 	/* see if we can enable ECRC's on an upstream
414b2fc195eSAndrew Gallatin 	   Nvidia bridge */
4156d87a65dSAndrew Gallatin 	if (mxge_nvidia_ecrc_enable &&
416b2fc195eSAndrew Gallatin 	    (pvend == 0x10de && pdid == 0x005d)) {
4176d87a65dSAndrew Gallatin 		err = mxge_enable_nvidia_ecrc(sc, pdev);
418b2fc195eSAndrew Gallatin 		if (err == 0) {
419b2fc195eSAndrew Gallatin 			aligned = 1;
4205e7d8541SAndrew Gallatin 			if (mxge_verbose)
421b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
4225e7d8541SAndrew Gallatin 					      "Assuming aligned completions"
4235e7d8541SAndrew Gallatin 					      " (ECRC)\n");
424b2fc195eSAndrew Gallatin 		}
425b2fc195eSAndrew Gallatin 	}
426b2fc195eSAndrew Gallatin 	/* see if the upstream bridge is known to
427b2fc195eSAndrew Gallatin 	   provided aligned completions */
428b2fc195eSAndrew Gallatin 	if (/* HT2000  */ (pvend == 0x1166 && pdid == 0x0132) ||
429b2fc195eSAndrew Gallatin 	    /* Ontario */ (pvend == 0x10b5 && pdid == 0x8532)) {
4305e7d8541SAndrew Gallatin 		if (mxge_verbose)
431b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
4325e7d8541SAndrew Gallatin 				      "Assuming aligned completions "
4335e7d8541SAndrew Gallatin 				      "(0x%x:0x%x)\n", pvend, pdid);
434b2fc195eSAndrew Gallatin 	}
435b2fc195eSAndrew Gallatin 
436b2fc195eSAndrew Gallatin abort:
437b2fc195eSAndrew Gallatin 	if (aligned) {
4386d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
439b2fc195eSAndrew Gallatin 		sc->tx.boundary = 4096;
440b2fc195eSAndrew Gallatin 	} else {
4416d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
442b2fc195eSAndrew Gallatin 		sc->tx.boundary = 2048;
443b2fc195eSAndrew Gallatin 	}
444b2fc195eSAndrew Gallatin }
445b2fc195eSAndrew Gallatin 
446b2fc195eSAndrew Gallatin union qualhack
447b2fc195eSAndrew Gallatin {
448b2fc195eSAndrew Gallatin         const char *ro_char;
449b2fc195eSAndrew Gallatin         char *rw_char;
450b2fc195eSAndrew Gallatin };
451b2fc195eSAndrew Gallatin 
4524da0d523SAndrew Gallatin static int
4534da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
4544da0d523SAndrew Gallatin {
4554da0d523SAndrew Gallatin 	int major, minor;
4564da0d523SAndrew Gallatin 
4574da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
4584da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
4594da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
4604da0d523SAndrew Gallatin 		return EIO;
4614da0d523SAndrew Gallatin 	}
4624da0d523SAndrew Gallatin 
4634da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
4644da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
4654da0d523SAndrew Gallatin 	if (mxge_verbose)
4664da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
4674da0d523SAndrew Gallatin 
4684da0d523SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d", &major, &minor);
4694da0d523SAndrew Gallatin 
4704da0d523SAndrew Gallatin 	if (!(major == MXGEFW_VERSION_MAJOR
4714da0d523SAndrew Gallatin 	      && minor == MXGEFW_VERSION_MINOR)) {
4724da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
4734da0d523SAndrew Gallatin 			      sc->fw_version);
4744da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
4754da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
4764da0d523SAndrew Gallatin 		return EINVAL;
4774da0d523SAndrew Gallatin 	}
4784da0d523SAndrew Gallatin 	return 0;
4794da0d523SAndrew Gallatin 
4804da0d523SAndrew Gallatin }
481b2fc195eSAndrew Gallatin 
482b2fc195eSAndrew Gallatin static int
4836d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
484b2fc195eSAndrew Gallatin {
485b2fc195eSAndrew Gallatin 	struct firmware *fw;
486b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
487b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
488b2fc195eSAndrew Gallatin 	const char *fw_data;
489b2fc195eSAndrew Gallatin 	union qualhack hack;
490b2fc195eSAndrew Gallatin 	int status;
4914da0d523SAndrew Gallatin 	unsigned int i;
4924da0d523SAndrew Gallatin 	char dummy;
493b2fc195eSAndrew Gallatin 
494b2fc195eSAndrew Gallatin 
495b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
496b2fc195eSAndrew Gallatin 
497b2fc195eSAndrew Gallatin 	if (fw == NULL) {
498b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
499b2fc195eSAndrew Gallatin 			      sc->fw_name);
500b2fc195eSAndrew Gallatin 		return ENOENT;
501b2fc195eSAndrew Gallatin 	}
502b2fc195eSAndrew Gallatin 	if (fw->datasize > *limit ||
503b2fc195eSAndrew Gallatin 	    fw->datasize < MCP_HEADER_PTR_OFFSET + 4) {
504b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n",
505b2fc195eSAndrew Gallatin 			      sc->fw_name, (int)fw->datasize, (int) *limit);
506b2fc195eSAndrew Gallatin 		status = ENOSPC;
507b2fc195eSAndrew Gallatin 		goto abort_with_fw;
508b2fc195eSAndrew Gallatin 	}
509b2fc195eSAndrew Gallatin 	*limit = fw->datasize;
510b2fc195eSAndrew Gallatin 
511b2fc195eSAndrew Gallatin 	/* check id */
512b2fc195eSAndrew Gallatin 	fw_data = (const char *)fw->data;
513b2fc195eSAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
514b2fc195eSAndrew Gallatin 			     (fw_data + MCP_HEADER_PTR_OFFSET));
515b2fc195eSAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) {
516b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
517b2fc195eSAndrew Gallatin 		status = EIO;
518b2fc195eSAndrew Gallatin 		goto abort_with_fw;
519b2fc195eSAndrew Gallatin 	}
520b2fc195eSAndrew Gallatin 	hdr = (const void*)(fw_data + hdr_offset);
521b2fc195eSAndrew Gallatin 
5224da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
5234da0d523SAndrew Gallatin 	if (status != 0)
5244da0d523SAndrew Gallatin 		goto abort_with_fw;
525b2fc195eSAndrew Gallatin 
526b2fc195eSAndrew Gallatin 	hack.ro_char = fw_data;
527b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
5284da0d523SAndrew Gallatin 	for (i = 0; i < *limit; i += 256) {
5294da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
5304da0d523SAndrew Gallatin 			      hack.rw_char + i,
5314da0d523SAndrew Gallatin 			      min(256U, (unsigned)(*limit - i)));
5324da0d523SAndrew Gallatin 		mb();
5334da0d523SAndrew Gallatin 		dummy = *sc->sram;
5344da0d523SAndrew Gallatin 		mb();
5354da0d523SAndrew Gallatin 	}
536b2fc195eSAndrew Gallatin 
537b2fc195eSAndrew Gallatin 	status = 0;
538b2fc195eSAndrew Gallatin abort_with_fw:
539b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
540b2fc195eSAndrew Gallatin 	return status;
541b2fc195eSAndrew Gallatin }
542b2fc195eSAndrew Gallatin 
543b2fc195eSAndrew Gallatin /*
544b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
545b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
546b2fc195eSAndrew Gallatin  */
547b2fc195eSAndrew Gallatin 
548b2fc195eSAndrew Gallatin static void
5496d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
550b2fc195eSAndrew Gallatin {
551b2fc195eSAndrew Gallatin 	char buf_bytes[72];
552b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
553b2fc195eSAndrew Gallatin 	volatile char *submit;
554b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
555b2fc195eSAndrew Gallatin 	int i;
556b2fc195eSAndrew Gallatin 
557b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
558b2fc195eSAndrew Gallatin 
559b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
560b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
561b2fc195eSAndrew Gallatin 	*confirm = 0;
562b2fc195eSAndrew Gallatin 	mb();
563b2fc195eSAndrew Gallatin 
564b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
565b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
566b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
567b2fc195eSAndrew Gallatin 	*/
568b2fc195eSAndrew Gallatin 
5696d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
5706d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
571b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
572b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
573b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
5746d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
5756d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
576b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
577b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
578b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
579b2fc195eSAndrew Gallatin 
580b2fc195eSAndrew Gallatin 
581b2fc195eSAndrew Gallatin 	submit = (volatile char *)(sc->sram + 0xfc01c0);
582b2fc195eSAndrew Gallatin 
5836d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
584b2fc195eSAndrew Gallatin 	mb();
585b2fc195eSAndrew Gallatin 	DELAY(1000);
586b2fc195eSAndrew Gallatin 	mb();
587b2fc195eSAndrew Gallatin 	i = 0;
588b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
589b2fc195eSAndrew Gallatin 		DELAY(1000);
590b2fc195eSAndrew Gallatin 		i++;
591b2fc195eSAndrew Gallatin 	}
592b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
593b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
594b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
595b2fc195eSAndrew Gallatin 			      *confirm);
596b2fc195eSAndrew Gallatin 	}
597b2fc195eSAndrew Gallatin 	return;
598b2fc195eSAndrew Gallatin }
599b2fc195eSAndrew Gallatin 
600b2fc195eSAndrew Gallatin static int
6016d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
602b2fc195eSAndrew Gallatin {
603b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
604b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
605b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
6065e7d8541SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_CMD_OFFSET;
607b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
608b2fc195eSAndrew Gallatin 	int sleep_total = 0;
609b2fc195eSAndrew Gallatin 
610b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
611b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
612b2fc195eSAndrew Gallatin 
613b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
614b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
615b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
616b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
6176d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
6186d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
619b2fc195eSAndrew Gallatin 
620b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
621b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
622b2fc195eSAndrew Gallatin 	mtx_lock(&sc->cmd_lock);
623b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
624b2fc195eSAndrew Gallatin 	mb();
6256d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
626b2fc195eSAndrew Gallatin 
6275e7d8541SAndrew Gallatin 	/* wait up to 20ms */
6285e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
629b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
630b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
631b2fc195eSAndrew Gallatin 		mb();
632b2fc195eSAndrew Gallatin 		if (response->result != 0xffffffff) {
633b2fc195eSAndrew Gallatin 			if (response->result == 0) {
634b2fc195eSAndrew Gallatin 				data->data0 = be32toh(response->data);
635b2fc195eSAndrew Gallatin 				mtx_unlock(&sc->cmd_lock);
636b2fc195eSAndrew Gallatin 				return 0;
637b2fc195eSAndrew Gallatin 			} else {
638b2fc195eSAndrew Gallatin 				device_printf(sc->dev,
6396d87a65dSAndrew Gallatin 					      "mxge: command %d "
640b2fc195eSAndrew Gallatin 					      "failed, result = %d\n",
641b2fc195eSAndrew Gallatin 					      cmd, be32toh(response->result));
642b2fc195eSAndrew Gallatin 				mtx_unlock(&sc->cmd_lock);
643b2fc195eSAndrew Gallatin 				return ENXIO;
644b2fc195eSAndrew Gallatin 			}
645b2fc195eSAndrew Gallatin 		}
6465e7d8541SAndrew Gallatin 		DELAY(1000);
647b2fc195eSAndrew Gallatin 	}
648b2fc195eSAndrew Gallatin 	mtx_unlock(&sc->cmd_lock);
6496d87a65dSAndrew Gallatin 	device_printf(sc->dev, "mxge: command %d timed out"
650b2fc195eSAndrew Gallatin 		      "result = %d\n",
651b2fc195eSAndrew Gallatin 		      cmd, be32toh(response->result));
652b2fc195eSAndrew Gallatin 	return EAGAIN;
653b2fc195eSAndrew Gallatin }
654b2fc195eSAndrew Gallatin 
6554da0d523SAndrew Gallatin static int
6564da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
6574da0d523SAndrew Gallatin {
6584da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
6594da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
6604da0d523SAndrew Gallatin 	size_t hdr_offset;
6614da0d523SAndrew Gallatin 	int status;
6624da0d523SAndrew Gallatin 
6634da0d523SAndrew Gallatin 	/* find running firmware header */
6644da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
6654da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
6664da0d523SAndrew Gallatin 
6674da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
6684da0d523SAndrew Gallatin 		device_printf(sc->dev,
6694da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
6704da0d523SAndrew Gallatin 			      (int)hdr_offset);
6714da0d523SAndrew Gallatin 		return EIO;
6724da0d523SAndrew Gallatin 	}
6734da0d523SAndrew Gallatin 
6744da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
6754da0d523SAndrew Gallatin 	 * validate firmware */
6764da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
6774da0d523SAndrew Gallatin 	if (hdr == NULL) {
6784da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
6794da0d523SAndrew Gallatin 		return ENOMEM;
6804da0d523SAndrew Gallatin 	}
6814da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
6824da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
6834da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
6844da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
6854da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
6864da0d523SAndrew Gallatin 	return status;
6874da0d523SAndrew Gallatin }
6884da0d523SAndrew Gallatin 
689b2fc195eSAndrew Gallatin 
690b2fc195eSAndrew Gallatin static int
6916d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc)
692b2fc195eSAndrew Gallatin {
693b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
694b2fc195eSAndrew Gallatin 	volatile char *submit;
695b2fc195eSAndrew Gallatin 	char buf_bytes[72];
696b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
697b2fc195eSAndrew Gallatin 	int status, i;
698b2fc195eSAndrew Gallatin 
699b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
700b2fc195eSAndrew Gallatin 
701b2fc195eSAndrew Gallatin 	size = sc->sram_size;
7026d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
703b2fc195eSAndrew Gallatin 	if (status) {
7044da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
7054da0d523SAndrew Gallatin 		   it is new enough */
7064da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
7074da0d523SAndrew Gallatin 		if (status) {
7084da0d523SAndrew Gallatin 			device_printf(sc->dev,
7094da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
710b2fc195eSAndrew Gallatin 			return status;
711b2fc195eSAndrew Gallatin 		}
7124da0d523SAndrew Gallatin 		device_printf(sc->dev,
7134da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
7144da0d523SAndrew Gallatin 		if (sc->tx.boundary == 4096) {
7154da0d523SAndrew Gallatin 			device_printf(sc->dev,
7164da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
7174da0d523SAndrew Gallatin 				 ".  For optimal\n");
7184da0d523SAndrew Gallatin 			device_printf(sc->dev,
7194da0d523SAndrew Gallatin 				 "performance consider loading optimized "
7204da0d523SAndrew Gallatin 				 "firmware\n");
7214da0d523SAndrew Gallatin 		}
7224da0d523SAndrew Gallatin 
7234da0d523SAndrew Gallatin 	}
724b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
725b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
726b2fc195eSAndrew Gallatin 	*confirm = 0;
727b2fc195eSAndrew Gallatin 	mb();
728b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
729b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
730b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
731b2fc195eSAndrew Gallatin 	*/
732b2fc195eSAndrew Gallatin 
7336d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7346d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
735b2fc195eSAndrew Gallatin 
736b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
737b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
738b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
739b2fc195eSAndrew Gallatin 
740b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
741b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
742b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
743b2fc195eSAndrew Gallatin 	*/
744b2fc195eSAndrew Gallatin 					/* where the code starts*/
7456d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
746b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
747b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
748b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
749b2fc195eSAndrew Gallatin 
750b2fc195eSAndrew Gallatin 	submit = (volatile char *)(sc->sram + 0xfc0000);
7516d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
752b2fc195eSAndrew Gallatin 	mb();
753b2fc195eSAndrew Gallatin 	DELAY(1000);
754b2fc195eSAndrew Gallatin 	mb();
755b2fc195eSAndrew Gallatin 	i = 0;
756b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
757b2fc195eSAndrew Gallatin 		DELAY(1000*10);
758b2fc195eSAndrew Gallatin 		i++;
759b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
760b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
761b2fc195eSAndrew Gallatin 	}
762b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
763b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
764b2fc195eSAndrew Gallatin 			confirm, *confirm);
765b2fc195eSAndrew Gallatin 
766b2fc195eSAndrew Gallatin 		return ENXIO;
767b2fc195eSAndrew Gallatin 	}
768b2fc195eSAndrew Gallatin 	return 0;
769b2fc195eSAndrew Gallatin }
770b2fc195eSAndrew Gallatin 
771b2fc195eSAndrew Gallatin static int
7726d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
773b2fc195eSAndrew Gallatin {
7746d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
775b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
776b2fc195eSAndrew Gallatin 	int status;
777b2fc195eSAndrew Gallatin 
778b2fc195eSAndrew Gallatin 
779b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
780b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
781b2fc195eSAndrew Gallatin 
782b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
783b2fc195eSAndrew Gallatin 
7845e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
785b2fc195eSAndrew Gallatin 	return status;
786b2fc195eSAndrew Gallatin }
787b2fc195eSAndrew Gallatin 
788b2fc195eSAndrew Gallatin static int
7896d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
790b2fc195eSAndrew Gallatin {
7916d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
792b2fc195eSAndrew Gallatin 	int status;
793b2fc195eSAndrew Gallatin 
794b2fc195eSAndrew Gallatin 	if (pause)
7955e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
796b2fc195eSAndrew Gallatin 				       &cmd);
797b2fc195eSAndrew Gallatin 	else
7985e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
799b2fc195eSAndrew Gallatin 				       &cmd);
800b2fc195eSAndrew Gallatin 
801b2fc195eSAndrew Gallatin 	if (status) {
802b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
803b2fc195eSAndrew Gallatin 		return ENXIO;
804b2fc195eSAndrew Gallatin 	}
805b2fc195eSAndrew Gallatin 	sc->pause = pause;
806b2fc195eSAndrew Gallatin 	return 0;
807b2fc195eSAndrew Gallatin }
808b2fc195eSAndrew Gallatin 
809b2fc195eSAndrew Gallatin static void
8106d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
811b2fc195eSAndrew Gallatin {
8126d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
813b2fc195eSAndrew Gallatin 	int status;
814b2fc195eSAndrew Gallatin 
815b2fc195eSAndrew Gallatin 	if (promisc)
8165e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
817b2fc195eSAndrew Gallatin 				       &cmd);
818b2fc195eSAndrew Gallatin 	else
8195e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
820b2fc195eSAndrew Gallatin 				       &cmd);
821b2fc195eSAndrew Gallatin 
822b2fc195eSAndrew Gallatin 	if (status) {
823b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
824b2fc195eSAndrew Gallatin 	}
825b2fc195eSAndrew Gallatin }
826b2fc195eSAndrew Gallatin 
827b2fc195eSAndrew Gallatin static int
8286d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc)
829b2fc195eSAndrew Gallatin {
830b2fc195eSAndrew Gallatin 
8316d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
8325e7d8541SAndrew Gallatin 	mxge_dma_t dmabench_dma;
8335e7d8541SAndrew Gallatin 	size_t bytes;
8345e7d8541SAndrew Gallatin 	int status;
835b2fc195eSAndrew Gallatin 
836b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
837b2fc195eSAndrew Gallatin 	   is alive */
838b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
8395e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
840b2fc195eSAndrew Gallatin 	if (status != 0) {
841b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
842b2fc195eSAndrew Gallatin 		return ENXIO;
843b2fc195eSAndrew Gallatin 	}
844b2fc195eSAndrew Gallatin 
845091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
846091feecdSAndrew Gallatin 
847b2fc195eSAndrew Gallatin 	/* Now exchange information about interrupts  */
8485e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\
8495e7d8541SAndrew Gallatin 	memset(sc->rx_done.entry, 0, bytes);
8505e7d8541SAndrew Gallatin 	cmd.data0 = (uint32_t)bytes;
8515e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
8525e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr);
8535e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr);
8545e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd);
855b2fc195eSAndrew Gallatin 
8566d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
8575e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
8585e7d8541SAndrew Gallatin 
8595e7d8541SAndrew Gallatin 
8605e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
8615e7d8541SAndrew Gallatin 
8625e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
8635e7d8541SAndrew Gallatin 	sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
8645e7d8541SAndrew Gallatin 
8655e7d8541SAndrew Gallatin 
8665e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
8676d87a65dSAndrew Gallatin 				&cmd);
8685e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
869b2fc195eSAndrew Gallatin 	if (status != 0) {
870b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
871b2fc195eSAndrew Gallatin 		return status;
872b2fc195eSAndrew Gallatin 	}
873b2fc195eSAndrew Gallatin 
8745e7d8541SAndrew Gallatin 
8755e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
8765e7d8541SAndrew Gallatin 
8775e7d8541SAndrew Gallatin 
8785e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
8795e7d8541SAndrew Gallatin 	sc->read_dma = sc->write_dma = sc->read_write_dma = 0;
8805e7d8541SAndrew Gallatin 	status = mxge_dma_alloc(sc, &dmabench_dma, 4096, 4096);
8815e7d8541SAndrew Gallatin 	if (status)
8825e7d8541SAndrew Gallatin 		goto dmabench_fail;
8835e7d8541SAndrew Gallatin 
8845e7d8541SAndrew Gallatin 	/* Read DMA */
8855e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
8865e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
8875e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10000;
8885e7d8541SAndrew Gallatin 
8895e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
8905e7d8541SAndrew Gallatin 	if (status != 0)
8915e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read dma benchmark failed\n");
8925e7d8541SAndrew Gallatin 	else
8935e7d8541SAndrew Gallatin 		sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
8945e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
8955e7d8541SAndrew Gallatin 
8965e7d8541SAndrew Gallatin 	/* Write DMA */
8975e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
8985e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
8995e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x1;
9005e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
9015e7d8541SAndrew Gallatin 	if (status != 0)
9025e7d8541SAndrew Gallatin 		device_printf(sc->dev, "write dma benchmark failed\n");
9035e7d8541SAndrew Gallatin 	else
9045e7d8541SAndrew Gallatin 		sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) /
9055e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
9065e7d8541SAndrew Gallatin 	/* Read/Write DMA */
9075e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr);
9085e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr);
9095e7d8541SAndrew Gallatin 	cmd.data2 = sc->tx.boundary * 0x10001;
9105e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd);
9115e7d8541SAndrew Gallatin 	if (status != 0)
9125e7d8541SAndrew Gallatin 		device_printf(sc->dev, "read/write dma benchmark failed\n");
9135e7d8541SAndrew Gallatin 	else
9145e7d8541SAndrew Gallatin 		sc->read_write_dma =
9155e7d8541SAndrew Gallatin 			((cmd.data0>>16) * sc->tx.boundary * 2 * 2) /
9165e7d8541SAndrew Gallatin 			(cmd.data0 & 0xffff);
9175e7d8541SAndrew Gallatin 
9185e7d8541SAndrew Gallatin 	mxge_dma_free(&dmabench_dma);
9195e7d8541SAndrew Gallatin 
9205e7d8541SAndrew Gallatin dmabench_fail:
921b2fc195eSAndrew Gallatin 	/* reset mcp/driver shared state back to 0 */
9225e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
9235e7d8541SAndrew Gallatin 	sc->rx_done.idx = 0;
9245e7d8541SAndrew Gallatin 	sc->rx_done.cnt = 0;
925b2fc195eSAndrew Gallatin 	sc->tx.req = 0;
926b2fc195eSAndrew Gallatin 	sc->tx.done = 0;
9275e7d8541SAndrew Gallatin 	sc->tx.pkt_done = 0;
928b2fc195eSAndrew Gallatin 	sc->rx_big.cnt = 0;
929b2fc195eSAndrew Gallatin 	sc->rx_small.cnt = 0;
930b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
9316d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
9326d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
9336d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
934b2fc195eSAndrew Gallatin 	return status;
935b2fc195eSAndrew Gallatin }
936b2fc195eSAndrew Gallatin 
937b2fc195eSAndrew Gallatin static int
9386d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
939b2fc195eSAndrew Gallatin {
9406d87a65dSAndrew Gallatin         mxge_softc_t *sc;
941b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
942b2fc195eSAndrew Gallatin         int err;
943b2fc195eSAndrew Gallatin 
944b2fc195eSAndrew Gallatin         sc = arg1;
945b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
946b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
947b2fc195eSAndrew Gallatin         if (err != 0) {
948b2fc195eSAndrew Gallatin                 return err;
949b2fc195eSAndrew Gallatin         }
950b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
951b2fc195eSAndrew Gallatin                 return 0;
952b2fc195eSAndrew Gallatin 
953b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
954b2fc195eSAndrew Gallatin                 return EINVAL;
955b2fc195eSAndrew Gallatin 
956b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
9575e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
958b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
9595e7d8541SAndrew Gallatin 
960b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
961b2fc195eSAndrew Gallatin         return err;
962b2fc195eSAndrew Gallatin }
963b2fc195eSAndrew Gallatin 
964b2fc195eSAndrew Gallatin static int
9656d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
966b2fc195eSAndrew Gallatin {
9676d87a65dSAndrew Gallatin         mxge_softc_t *sc;
968b2fc195eSAndrew Gallatin         unsigned int enabled;
969b2fc195eSAndrew Gallatin         int err;
970b2fc195eSAndrew Gallatin 
971b2fc195eSAndrew Gallatin         sc = arg1;
972b2fc195eSAndrew Gallatin         enabled = sc->pause;
973b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
974b2fc195eSAndrew Gallatin         if (err != 0) {
975b2fc195eSAndrew Gallatin                 return err;
976b2fc195eSAndrew Gallatin         }
977b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
978b2fc195eSAndrew Gallatin                 return 0;
979b2fc195eSAndrew Gallatin 
980b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
9816d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
982b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
983b2fc195eSAndrew Gallatin         return err;
984b2fc195eSAndrew Gallatin }
985b2fc195eSAndrew Gallatin 
986b2fc195eSAndrew Gallatin static int
9876d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
988b2fc195eSAndrew Gallatin {
989b2fc195eSAndrew Gallatin         int err;
990b2fc195eSAndrew Gallatin 
991b2fc195eSAndrew Gallatin         if (arg1 == NULL)
992b2fc195eSAndrew Gallatin                 return EFAULT;
993b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
994b2fc195eSAndrew Gallatin         arg1 = NULL;
995b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
996b2fc195eSAndrew Gallatin 
997b2fc195eSAndrew Gallatin         return err;
998b2fc195eSAndrew Gallatin }
999b2fc195eSAndrew Gallatin 
1000b2fc195eSAndrew Gallatin static void
10016d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1002b2fc195eSAndrew Gallatin {
1003b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1004b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
10055e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
1006b2fc195eSAndrew Gallatin 
1007b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1008b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
1009b2fc195eSAndrew Gallatin 	fw = sc->fw_stats;
1010b2fc195eSAndrew Gallatin 
10115e7d8541SAndrew Gallatin 	/* random information */
10125e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
10135e7d8541SAndrew Gallatin 		       "firmware_version",
10145e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
10155e7d8541SAndrew Gallatin 		       0, "firmware version");
10165e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
10175e7d8541SAndrew Gallatin 		       "serial_number",
10185e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
10195e7d8541SAndrew Gallatin 		       0, "serial number");
10205e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
10215e7d8541SAndrew Gallatin 		       "product_code",
10225e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
10235e7d8541SAndrew Gallatin 		       0, "product_code");
10245e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10255e7d8541SAndrew Gallatin 		       "tx_boundary",
10265e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.boundary,
10275e7d8541SAndrew Gallatin 		       0, "tx_boundary");
10285e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1029091feecdSAndrew Gallatin 		       "write_combine",
1030091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1031091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1032091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10335e7d8541SAndrew Gallatin 		       "read_dma_MBs",
10345e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
10355e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
10365e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10375e7d8541SAndrew Gallatin 		       "write_dma_MBs",
10385e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
10395e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
10405e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10415e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
10425e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
10435e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
10445e7d8541SAndrew Gallatin 
10455e7d8541SAndrew Gallatin 
10465e7d8541SAndrew Gallatin 	/* performance related tunables */
1047b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1048b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1049b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
10506d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1051b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1052b2fc195eSAndrew Gallatin 
1053b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1054b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1055b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
10566d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1057b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1058b2fc195eSAndrew Gallatin 
1059b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
10605e7d8541SAndrew Gallatin 		       "deassert_wait",
10615e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
10625e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1063b2fc195eSAndrew Gallatin 
1064b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1065b2fc195eSAndrew Gallatin 	   Need to swap it */
1066b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1067b2fc195eSAndrew Gallatin 			"link_up",
1068b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
10696d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1070b2fc195eSAndrew Gallatin 			"I", "link up");
1071b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1072b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1073b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
10746d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1075b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1076b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1077b2fc195eSAndrew Gallatin 			"dropped_link_overflow",
1078b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
10796d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1080b2fc195eSAndrew Gallatin 			"I", "dropped_link_overflow");
1081b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1082b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1083b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1084b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
10856d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1086b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1087b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1088b2fc195eSAndrew Gallatin 			"dropped_runt",
1089b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
10906d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1091b2fc195eSAndrew Gallatin 			"I", "dropped_runt");
1092b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1093b2fc195eSAndrew Gallatin 			"dropped_overrun",
1094b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
10956d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1096b2fc195eSAndrew Gallatin 			"I", "dropped_overrun");
1097b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1098b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1099b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1100b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
11016d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1102b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1103b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1104b2fc195eSAndrew Gallatin 			"dropped_no_big_buffer",
1105b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
11066d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1107b2fc195eSAndrew Gallatin 			"I", "dropped_no_big_buffer");
1108b2fc195eSAndrew Gallatin 
1109b2fc195eSAndrew Gallatin 	/* host counters exported for debugging */
1110b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11115e7d8541SAndrew Gallatin 		       "rx_small_cnt",
11125e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_small.cnt,
11135e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
11145e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11155e7d8541SAndrew Gallatin 		       "rx_big_cnt",
11165e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_big.cnt,
11175e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
11185e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1119b2fc195eSAndrew Gallatin 		       "tx_req",
1120b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.req,
1121b2fc195eSAndrew Gallatin 		       0, "tx_req");
1122b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1123b2fc195eSAndrew Gallatin 		       "tx_done",
1124b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.done,
1125b2fc195eSAndrew Gallatin 		       0, "tx_done");
1126b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11275e7d8541SAndrew Gallatin 		       "tx_pkt_done",
11285e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.pkt_done,
11295e7d8541SAndrew Gallatin 		       0, "tx_done");
11305e7d8541SAndrew Gallatin 
11315e7d8541SAndrew Gallatin 	/* verbose printing? */
1132b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
11335e7d8541SAndrew Gallatin 		       "verbose",
11345e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
11355e7d8541SAndrew Gallatin 		       0, "verbose printing");
1136b2fc195eSAndrew Gallatin 
1137b2fc195eSAndrew Gallatin }
1138b2fc195eSAndrew Gallatin 
1139b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1140b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1141b2fc195eSAndrew Gallatin 
1142b2fc195eSAndrew Gallatin static inline void
11436d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx,
1144b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1145b2fc195eSAndrew Gallatin {
1146b2fc195eSAndrew Gallatin         int idx, starting_slot;
1147b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1148b2fc195eSAndrew Gallatin         while (cnt > 1) {
1149b2fc195eSAndrew Gallatin                 cnt--;
1150b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
11516d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1152b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1153b2fc195eSAndrew Gallatin                 mb();
1154b2fc195eSAndrew Gallatin         }
1155b2fc195eSAndrew Gallatin }
1156b2fc195eSAndrew Gallatin 
1157b2fc195eSAndrew Gallatin /*
1158b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1159b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1160b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1161b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1162b2fc195eSAndrew Gallatin  */
1163b2fc195eSAndrew Gallatin 
1164b2fc195eSAndrew Gallatin static inline void
11656d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src,
1166b2fc195eSAndrew Gallatin                   int cnt)
1167b2fc195eSAndrew Gallatin {
1168b2fc195eSAndrew Gallatin         int idx, i;
1169b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1170b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1171b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1172b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
11735e7d8541SAndrew Gallatin 	uint8_t last_flags;
1174b2fc195eSAndrew Gallatin 
1175b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1176b2fc195eSAndrew Gallatin 
11775e7d8541SAndrew Gallatin 	last_flags = src->flags;
11785e7d8541SAndrew Gallatin 	src->flags = 0;
1179b2fc195eSAndrew Gallatin         mb();
1180b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1181b2fc195eSAndrew Gallatin         srcp = src;
1182b2fc195eSAndrew Gallatin 
1183b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1184b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
11856d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1186b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1187b2fc195eSAndrew Gallatin                         srcp += 2;
1188b2fc195eSAndrew Gallatin                         dstp += 2;
1189b2fc195eSAndrew Gallatin                 }
1190b2fc195eSAndrew Gallatin         } else {
1191b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1192b2fc195eSAndrew Gallatin                    that it is submitted below */
11936d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1194b2fc195eSAndrew Gallatin                 i = 0;
1195b2fc195eSAndrew Gallatin         }
1196b2fc195eSAndrew Gallatin         if (i < cnt) {
1197b2fc195eSAndrew Gallatin                 /* submit the first request */
11986d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1199b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1200b2fc195eSAndrew Gallatin         }
1201b2fc195eSAndrew Gallatin 
1202b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
12035e7d8541SAndrew Gallatin         src->flags = last_flags;
1204b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1205b2fc195eSAndrew Gallatin         src_ints+=3;
1206b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1207b2fc195eSAndrew Gallatin         dst_ints+=3;
1208b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1209b2fc195eSAndrew Gallatin         tx->req += cnt;
1210b2fc195eSAndrew Gallatin         mb();
1211b2fc195eSAndrew Gallatin }
1212b2fc195eSAndrew Gallatin 
1213b2fc195eSAndrew Gallatin static inline void
12146d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt)
1215b2fc195eSAndrew Gallatin {
1216b2fc195eSAndrew Gallatin     tx->req += cnt;
1217b2fc195eSAndrew Gallatin     mb();
1218b2fc195eSAndrew Gallatin     while (cnt >= 4) {
12196d87a65dSAndrew Gallatin 	    mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64);
1220b2fc195eSAndrew Gallatin 	    mb();
1221b2fc195eSAndrew Gallatin 	    src += 4;
1222b2fc195eSAndrew Gallatin 	    cnt -= 4;
1223b2fc195eSAndrew Gallatin     }
1224b2fc195eSAndrew Gallatin     if (cnt > 0) {
1225b2fc195eSAndrew Gallatin 	    /* pad it to 64 bytes.  The src is 64 bytes bigger than it
1226b2fc195eSAndrew Gallatin 	       needs to be so that we don't overrun it */
12276d87a65dSAndrew Gallatin 	    mxge_pio_copy(tx->wc_fifo + (cnt<<18), src, 64);
1228b2fc195eSAndrew Gallatin 	    mb();
1229b2fc195eSAndrew Gallatin     }
1230b2fc195eSAndrew Gallatin }
1231b2fc195eSAndrew Gallatin 
1232b2fc195eSAndrew Gallatin static void
12336d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m)
1234b2fc195eSAndrew Gallatin {
1235b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
12365e7d8541SAndrew Gallatin 	bus_dma_segment_t seg_list[MXGE_MAX_SEND_DESC];
1237b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1238b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1239b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
12406d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1241b2fc195eSAndrew Gallatin 	struct ether_header *eh;
1242b2fc195eSAndrew Gallatin 	struct ip *ip;
1243b2fc195eSAndrew Gallatin 	int cnt, cum_len, err, i, idx;
1244b2fc195eSAndrew Gallatin 	uint16_t flags, pseudo_hdr_offset;
1245b2fc195eSAndrew Gallatin         uint8_t cksum_offset;
1246b2fc195eSAndrew Gallatin 
1247b2fc195eSAndrew Gallatin 
1248b2fc195eSAndrew Gallatin 
1249b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1250b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1251b2fc195eSAndrew Gallatin 
1252b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1253b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1254b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1255b2fc195eSAndrew Gallatin 				      m, seg_list, &cnt,
1256b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1257b2fc195eSAndrew Gallatin 	if (err == EFBIG) {
1258b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1259b2fc195eSAndrew Gallatin 		   to defrag */
1260b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1261b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1262b2fc195eSAndrew Gallatin 			goto drop;
1263b2fc195eSAndrew Gallatin 		}
1264b2fc195eSAndrew Gallatin 		m = m_tmp;
1265b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1266b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1267b2fc195eSAndrew Gallatin 					      m, seg_list, &cnt,
1268b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1269b2fc195eSAndrew Gallatin 	}
1270b2fc195eSAndrew Gallatin 	if (err != 0) {
1271b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d\n",
1272b2fc195eSAndrew Gallatin 			      err);
1273b2fc195eSAndrew Gallatin 		goto drop;
1274b2fc195eSAndrew Gallatin 	}
1275b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1276b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
12775e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1278b2fc195eSAndrew Gallatin 
1279b2fc195eSAndrew Gallatin 	req = tx->req_list;
1280b2fc195eSAndrew Gallatin 	cksum_offset = 0;
12815e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
12825e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1283b2fc195eSAndrew Gallatin 
1284b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1285b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1286b2fc195eSAndrew Gallatin 		eh = mtod(m, struct ether_header *);
1287b2fc195eSAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1288b2fc195eSAndrew Gallatin 		cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1289b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
12905e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1291b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
12925e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1293b2fc195eSAndrew Gallatin 	}
12945e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
12955e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
1296b2fc195eSAndrew Gallatin 
1297b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
1298b2fc195eSAndrew Gallatin 	cum_len = 0;
1299b2fc195eSAndrew Gallatin 	seg = seg_list;
13005e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
1301b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1302b2fc195eSAndrew Gallatin 		req->addr_low =
13036d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1304b2fc195eSAndrew Gallatin 		req->addr_high =
13056d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1306b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
1307b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
1308b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
1309b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
1310b2fc195eSAndrew Gallatin 		else
1311b2fc195eSAndrew Gallatin 			cksum_offset = 0;
13125e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
13135e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
13145e7d8541SAndrew Gallatin 		req->rdma_count = 1;
13155e7d8541SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
1316b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
1317b2fc195eSAndrew Gallatin 		seg++;
1318b2fc195eSAndrew Gallatin 		req++;
1319b2fc195eSAndrew Gallatin 		req->flags = 0;
1320b2fc195eSAndrew Gallatin 	}
1321b2fc195eSAndrew Gallatin 	req--;
1322b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
1323b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
1324b2fc195eSAndrew Gallatin 		req++;
1325b2fc195eSAndrew Gallatin 		req->addr_low =
13266d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
1327b2fc195eSAndrew Gallatin 		req->addr_high =
13286d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
1329b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
13305e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
13315e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
13325e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
13335e7d8541SAndrew Gallatin 		req->rdma_count = 1;
13345e7d8541SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
1335b2fc195eSAndrew Gallatin 		cnt++;
1336b2fc195eSAndrew Gallatin 	}
13375e7d8541SAndrew Gallatin 
13385e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
13395e7d8541SAndrew Gallatin #if 0
13405e7d8541SAndrew Gallatin 	/* print what the firmware will see */
13415e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
13425e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
13435e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
13445e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
13455e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
13465e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
13475e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
13485e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
13495e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
13505e7d8541SAndrew Gallatin 	}
13515e7d8541SAndrew Gallatin 	printf("--------------\n");
13525e7d8541SAndrew Gallatin #endif
13535e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1354b2fc195eSAndrew Gallatin 	if (tx->wc_fifo == NULL)
13556d87a65dSAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1356b2fc195eSAndrew Gallatin 	else
13576d87a65dSAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1358b2fc195eSAndrew Gallatin 	return;
1359b2fc195eSAndrew Gallatin 
1360b2fc195eSAndrew Gallatin drop:
1361b2fc195eSAndrew Gallatin 	m_freem(m);
1362b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
1363b2fc195eSAndrew Gallatin 	return;
1364b2fc195eSAndrew Gallatin }
1365b2fc195eSAndrew Gallatin 
1366b2fc195eSAndrew Gallatin 
13676d914a32SAndrew Gallatin 
13686d914a32SAndrew Gallatin 
13696d914a32SAndrew Gallatin static inline void
13706d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc)
1371b2fc195eSAndrew Gallatin {
1372b2fc195eSAndrew Gallatin 	struct mbuf *m;
1373b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1374b2fc195eSAndrew Gallatin 
1375b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
13766d914a32SAndrew Gallatin 	while ((sc->tx.mask - (sc->tx.req - sc->tx.done))
13776d914a32SAndrew Gallatin 	       > MXGE_MAX_SEND_DESC) {
1378b2fc195eSAndrew Gallatin 
13796d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
13806d914a32SAndrew Gallatin 		if (m == NULL) {
13816d914a32SAndrew Gallatin 			return;
13826d914a32SAndrew Gallatin 		}
1383b2fc195eSAndrew Gallatin 		/* let BPF see it */
1384b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
1385b2fc195eSAndrew Gallatin 
1386b2fc195eSAndrew Gallatin 		/* give it to the nic */
13876d87a65dSAndrew Gallatin 		mxge_encap(sc, m);
13886d914a32SAndrew Gallatin 	}
13896d914a32SAndrew Gallatin 	/* ran out of transmit slots */
1390b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1391b2fc195eSAndrew Gallatin }
1392b2fc195eSAndrew Gallatin 
1393b2fc195eSAndrew Gallatin static void
13946d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
1395b2fc195eSAndrew Gallatin {
13966d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
1397b2fc195eSAndrew Gallatin 
1398b2fc195eSAndrew Gallatin 
1399b2fc195eSAndrew Gallatin 	mtx_lock(&sc->tx_lock);
14006d87a65dSAndrew Gallatin 	mxge_start_locked(sc);
1401b2fc195eSAndrew Gallatin 	mtx_unlock(&sc->tx_lock);
1402b2fc195eSAndrew Gallatin }
1403b2fc195eSAndrew Gallatin 
14045e7d8541SAndrew Gallatin /*
14055e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
14065e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
14075e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
14085e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
14095e7d8541SAndrew Gallatin  * in a burst
14105e7d8541SAndrew Gallatin  */
14115e7d8541SAndrew Gallatin static inline void
14125e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
14135e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
14145e7d8541SAndrew Gallatin {
14155e7d8541SAndrew Gallatin 	uint32_t low;
14165e7d8541SAndrew Gallatin 
14175e7d8541SAndrew Gallatin 	low = src->addr_low;
14185e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
14195e7d8541SAndrew Gallatin 	mxge_pio_copy(dst, src, 8 * sizeof (*src));
14205e7d8541SAndrew Gallatin 	mb();
14215e7d8541SAndrew Gallatin 	dst->addr_low = low;
14225e7d8541SAndrew Gallatin 	mb();
14235e7d8541SAndrew Gallatin }
14245e7d8541SAndrew Gallatin 
1425b2fc195eSAndrew Gallatin static int
14266d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1427b2fc195eSAndrew Gallatin {
1428b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1429b2fc195eSAndrew Gallatin 	struct mbuf *m;
14306d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_small;
1431b2fc195eSAndrew Gallatin 	int cnt, err;
1432b2fc195eSAndrew Gallatin 
1433b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
1434b2fc195eSAndrew Gallatin 	if (m == NULL) {
1435b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1436b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1437b2fc195eSAndrew Gallatin 		goto done;
1438b2fc195eSAndrew Gallatin 	}
1439b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
1440b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1441b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1442b2fc195eSAndrew Gallatin 	if (err != 0) {
1443b2fc195eSAndrew Gallatin 		m_free(m);
1444b2fc195eSAndrew Gallatin 		goto done;
1445b2fc195eSAndrew Gallatin 	}
1446b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1447b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
14486d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1449b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
14506d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1451b2fc195eSAndrew Gallatin 
1452b2fc195eSAndrew Gallatin done:
1453b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
14545e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
14555e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
14565e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
14575e7d8541SAndrew Gallatin 		else {
1458b2fc195eSAndrew Gallatin 			mb();
14595e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
14605e7d8541SAndrew Gallatin 		}
1461b2fc195eSAndrew Gallatin         }
1462b2fc195eSAndrew Gallatin 	return err;
1463b2fc195eSAndrew Gallatin }
1464b2fc195eSAndrew Gallatin 
1465b2fc195eSAndrew Gallatin static int
14666d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1467b2fc195eSAndrew Gallatin {
1468b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1469b2fc195eSAndrew Gallatin 	struct mbuf *m;
14706d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_big;
1471b2fc195eSAndrew Gallatin 	int cnt, err;
1472b2fc195eSAndrew Gallatin 
1473b2fc195eSAndrew Gallatin 	m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes);
1474b2fc195eSAndrew Gallatin 	if (m == NULL) {
1475b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1476b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1477b2fc195eSAndrew Gallatin 		goto done;
1478b2fc195eSAndrew Gallatin 	}
1479b2fc195eSAndrew Gallatin 	m->m_len = sc->big_bytes;
1480b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1481b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1482b2fc195eSAndrew Gallatin 	if (err != 0) {
1483b2fc195eSAndrew Gallatin 		m_free(m);
1484b2fc195eSAndrew Gallatin 		goto done;
1485b2fc195eSAndrew Gallatin 	}
1486b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1487b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
14886d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1489b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
14906d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1491b2fc195eSAndrew Gallatin 
1492b2fc195eSAndrew Gallatin done:
1493b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
14945e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
14955e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
14965e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
14975e7d8541SAndrew Gallatin 		else {
1498b2fc195eSAndrew Gallatin 			mb();
14995e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
15005e7d8541SAndrew Gallatin 		}
1501b2fc195eSAndrew Gallatin         }
1502b2fc195eSAndrew Gallatin 	return err;
1503b2fc195eSAndrew Gallatin }
1504b2fc195eSAndrew Gallatin 
1505b2fc195eSAndrew Gallatin static inline void
15065e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
15075e7d8541SAndrew Gallatin {
15085e7d8541SAndrew Gallatin 	struct ether_header *eh;
15095e7d8541SAndrew Gallatin 	struct ip *ip;
15105e7d8541SAndrew Gallatin 
15115e7d8541SAndrew Gallatin 	eh = mtod(m, struct ether_header *);
15125e7d8541SAndrew Gallatin 	if (__predict_true(eh->ether_type ==  htons(ETHERTYPE_IP))) {
15135e7d8541SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
15145e7d8541SAndrew Gallatin 		if (__predict_true(ip->ip_p == IPPROTO_TCP ||
15155e7d8541SAndrew Gallatin 				   ip->ip_p == IPPROTO_UDP)) {
15165e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_data = csum;
15175e7d8541SAndrew Gallatin 			m->m_pkthdr.csum_flags = CSUM_DATA_VALID;
15185e7d8541SAndrew Gallatin 		}
15195e7d8541SAndrew Gallatin 	}
15205e7d8541SAndrew Gallatin }
15215e7d8541SAndrew Gallatin 
15225e7d8541SAndrew Gallatin static inline void
15235e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum)
1524b2fc195eSAndrew Gallatin {
1525b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1526b2fc195eSAndrew Gallatin 	struct mbuf *m = 0; 		/* -Wunitialized */
1527b2fc195eSAndrew Gallatin 	struct mbuf *m_prev = 0;	/* -Wunitialized */
1528b2fc195eSAndrew Gallatin 	struct mbuf *m_head = 0;
1529b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
15306d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1531b2fc195eSAndrew Gallatin 	int idx;
1532b2fc195eSAndrew Gallatin 
1533b2fc195eSAndrew Gallatin 
1534b2fc195eSAndrew Gallatin 	rx = &sc->rx_big;
1535b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1536b2fc195eSAndrew Gallatin 	while (len > 0) {
1537b2fc195eSAndrew Gallatin 		idx = rx->cnt & rx->mask;
1538b2fc195eSAndrew Gallatin                 rx->cnt++;
1539b2fc195eSAndrew Gallatin 		/* save a pointer to the received mbuf */
1540b2fc195eSAndrew Gallatin 		m = rx->info[idx].m;
1541b2fc195eSAndrew Gallatin 		/* try to replace the received mbuf */
15426d87a65dSAndrew Gallatin 		if (mxge_get_buf_big(sc, rx->extra_map, idx)) {
1543b2fc195eSAndrew Gallatin 			goto drop;
1544b2fc195eSAndrew Gallatin 		}
1545b2fc195eSAndrew Gallatin 		/* unmap the received buffer */
1546b2fc195eSAndrew Gallatin 		old_map = rx->info[idx].map;
1547b2fc195eSAndrew Gallatin 		bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1548b2fc195eSAndrew Gallatin 		bus_dmamap_unload(rx->dmat, old_map);
1549b2fc195eSAndrew Gallatin 
1550b2fc195eSAndrew Gallatin 		/* swap the bus_dmamap_t's */
1551b2fc195eSAndrew Gallatin 		rx->info[idx].map = rx->extra_map;
1552b2fc195eSAndrew Gallatin 		rx->extra_map = old_map;
1553b2fc195eSAndrew Gallatin 
1554b2fc195eSAndrew Gallatin 		/* chain multiple segments together */
1555b2fc195eSAndrew Gallatin 		if (!m_head) {
1556b2fc195eSAndrew Gallatin 			m_head = m;
1557b2fc195eSAndrew Gallatin 			/* mcp implicitly skips 1st bytes so that
1558b2fc195eSAndrew Gallatin 			 * packet is properly aligned */
15595e7d8541SAndrew Gallatin 			m->m_data += MXGEFW_PAD;
1560b2fc195eSAndrew Gallatin 			m->m_pkthdr.len = len;
15615e7d8541SAndrew Gallatin 			m->m_len = sc->big_bytes - MXGEFW_PAD;
1562b2fc195eSAndrew Gallatin 		} else {
1563b2fc195eSAndrew Gallatin 			m->m_len = sc->big_bytes;
1564b2fc195eSAndrew Gallatin 			m->m_flags &= ~M_PKTHDR;
1565b2fc195eSAndrew Gallatin 			m_prev->m_next = m;
1566b2fc195eSAndrew Gallatin 		}
1567b2fc195eSAndrew Gallatin 		len -= m->m_len;
1568b2fc195eSAndrew Gallatin 		m_prev = m;
1569b2fc195eSAndrew Gallatin 	}
1570b2fc195eSAndrew Gallatin 
1571b2fc195eSAndrew Gallatin 	/* trim trailing garbage from the last mbuf in the chain.  If
1572b2fc195eSAndrew Gallatin 	 * there is any garbage, len will be negative */
1573b2fc195eSAndrew Gallatin 	m->m_len += len;
1574b2fc195eSAndrew Gallatin 
1575b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
15765e7d8541SAndrew Gallatin 	if (sc->csum_flag)
15775e7d8541SAndrew Gallatin 		mxge_rx_csum(m_head, csum);
1578b2fc195eSAndrew Gallatin 
1579b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1580b2fc195eSAndrew Gallatin 	m_head->m_pkthdr.rcvif = ifp;
1581b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1582b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m_head);
1583b2fc195eSAndrew Gallatin 	return;
1584b2fc195eSAndrew Gallatin 
1585b2fc195eSAndrew Gallatin drop:
1586b2fc195eSAndrew Gallatin 	/* drop the frame -- the old mbuf(s) are re-cycled by running
1587b2fc195eSAndrew Gallatin 	   every slot through the allocator */
1588b2fc195eSAndrew Gallatin         if (m_head) {
1589b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1590b2fc195eSAndrew Gallatin                 m_freem(m_head);
1591b2fc195eSAndrew Gallatin         } else {
15925e7d8541SAndrew Gallatin                 len -= (sc->big_bytes + MXGEFW_PAD);
1593b2fc195eSAndrew Gallatin         }
1594b2fc195eSAndrew Gallatin         while ((int)len > 0) {
1595b2fc195eSAndrew Gallatin                 idx = rx->cnt & rx->mask;
1596b2fc195eSAndrew Gallatin                 rx->cnt++;
1597b2fc195eSAndrew Gallatin                 m = rx->info[idx].m;
15986d87a65dSAndrew Gallatin                 if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) {
1599b2fc195eSAndrew Gallatin 			m_freem(m);
1600b2fc195eSAndrew Gallatin 			/* unmap the received buffer */
1601b2fc195eSAndrew Gallatin 			old_map = rx->info[idx].map;
1602b2fc195eSAndrew Gallatin 			bus_dmamap_sync(rx->dmat, old_map,
1603b2fc195eSAndrew Gallatin 					BUS_DMASYNC_POSTREAD);
1604b2fc195eSAndrew Gallatin 			bus_dmamap_unload(rx->dmat, old_map);
1605b2fc195eSAndrew Gallatin 
1606b2fc195eSAndrew Gallatin 			/* swap the bus_dmamap_t's */
1607b2fc195eSAndrew Gallatin 			rx->info[idx].map = rx->extra_map;
1608b2fc195eSAndrew Gallatin 			rx->extra_map = old_map;
1609b2fc195eSAndrew Gallatin 		}
1610b2fc195eSAndrew Gallatin                 len -= sc->big_bytes;
1611b2fc195eSAndrew Gallatin         }
1612b2fc195eSAndrew Gallatin 
1613b2fc195eSAndrew Gallatin 	ifp->if_ierrors++;
1614b2fc195eSAndrew Gallatin 
1615b2fc195eSAndrew Gallatin }
1616b2fc195eSAndrew Gallatin 
1617b2fc195eSAndrew Gallatin static inline void
16185e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum)
1619b2fc195eSAndrew Gallatin {
1620b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1621b2fc195eSAndrew Gallatin 	struct mbuf *m;
16226d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
1623b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
1624b2fc195eSAndrew Gallatin 	int idx;
1625b2fc195eSAndrew Gallatin 
1626b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1627b2fc195eSAndrew Gallatin 	rx = &sc->rx_small;
1628b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
1629b2fc195eSAndrew Gallatin 	rx->cnt++;
1630b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
1631b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
1632b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
16336d87a65dSAndrew Gallatin 	if (mxge_get_buf_small(sc, rx->extra_map, idx)) {
1634b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
1635b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
1636b2fc195eSAndrew Gallatin 		return;
1637b2fc195eSAndrew Gallatin 	}
1638b2fc195eSAndrew Gallatin 
1639b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
1640b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
1641b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
1642b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
1643b2fc195eSAndrew Gallatin 
1644b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
1645b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
1646b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
1647b2fc195eSAndrew Gallatin 
1648b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
1649b2fc195eSAndrew Gallatin 	 * aligned */
16505e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
1651b2fc195eSAndrew Gallatin 
1652b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
16535e7d8541SAndrew Gallatin 	if (sc->csum_flag)
16545e7d8541SAndrew Gallatin 		mxge_rx_csum(m, csum);
1655b2fc195eSAndrew Gallatin 
1656b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
1657b2fc195eSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
1658b2fc195eSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
1659b2fc195eSAndrew Gallatin 	ifp->if_ipackets++;
1660b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
1661b2fc195eSAndrew Gallatin }
1662b2fc195eSAndrew Gallatin 
1663b2fc195eSAndrew Gallatin static inline void
16645e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc)
16655e7d8541SAndrew Gallatin {
16665e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
16675e7d8541SAndrew Gallatin 	int limit = 0;
16685e7d8541SAndrew Gallatin 	uint16_t length;
16695e7d8541SAndrew Gallatin 	uint16_t checksum;
16705e7d8541SAndrew Gallatin 
16715e7d8541SAndrew Gallatin 
16725e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
16735e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
16745e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
16755e7d8541SAndrew Gallatin 		checksum = ntohs(rx_done->entry[rx_done->idx].checksum);
16765e7d8541SAndrew Gallatin 		if (length <= MHLEN)
16775e7d8541SAndrew Gallatin 			mxge_rx_done_small(sc, length, checksum);
16785e7d8541SAndrew Gallatin 		else
16795e7d8541SAndrew Gallatin 			mxge_rx_done_big(sc, length, checksum);
16805e7d8541SAndrew Gallatin 		rx_done->cnt++;
16815e7d8541SAndrew Gallatin 		rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1);
16825e7d8541SAndrew Gallatin 
16835e7d8541SAndrew Gallatin 		/* limit potential for livelock */
16845e7d8541SAndrew Gallatin 		if (__predict_false(++limit > 2 * mxge_max_intr_slots))
16855e7d8541SAndrew Gallatin 			break;
16865e7d8541SAndrew Gallatin 
16875e7d8541SAndrew Gallatin 	}
16885e7d8541SAndrew Gallatin }
16895e7d8541SAndrew Gallatin 
16905e7d8541SAndrew Gallatin 
16915e7d8541SAndrew Gallatin static inline void
16926d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx)
1693b2fc195eSAndrew Gallatin {
1694b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
16956d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1696b2fc195eSAndrew Gallatin 	struct mbuf *m;
1697b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
16985e7d8541SAndrew Gallatin 	int idx, limit;
1699b2fc195eSAndrew Gallatin 
17005e7d8541SAndrew Gallatin 	limit = 0;
1701b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1702b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
17035e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
1704b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
1705b2fc195eSAndrew Gallatin 		tx->done++;
1706b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
1707b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
1708b2fc195eSAndrew Gallatin 		   segment per-mbuf */
1709b2fc195eSAndrew Gallatin 		if (m != NULL) {
1710b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
1711b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
1712b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
1713b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
1714b2fc195eSAndrew Gallatin 			m_freem(m);
1715b2fc195eSAndrew Gallatin 		}
17165e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
17175e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
17185e7d8541SAndrew Gallatin 			tx->pkt_done++;
17195e7d8541SAndrew Gallatin 		}
17205e7d8541SAndrew Gallatin 		/* limit potential for livelock by only handling
17215e7d8541SAndrew Gallatin 		   2 full tx rings per call */
17225e7d8541SAndrew Gallatin 		if (__predict_false(++limit >  2 * tx->mask))
17235e7d8541SAndrew Gallatin 			break;
1724b2fc195eSAndrew Gallatin 	}
1725b2fc195eSAndrew Gallatin 
1726b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
1727b2fc195eSAndrew Gallatin            its OK to send packets */
1728b2fc195eSAndrew Gallatin 
1729b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
1730b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
1731b2fc195eSAndrew Gallatin 		mtx_lock(&sc->tx_lock);
1732b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
17336d87a65dSAndrew Gallatin 		mxge_start_locked(sc);
1734b2fc195eSAndrew Gallatin 		mtx_unlock(&sc->tx_lock);
1735b2fc195eSAndrew Gallatin 	}
1736b2fc195eSAndrew Gallatin }
1737b2fc195eSAndrew Gallatin 
1738b2fc195eSAndrew Gallatin static void
17396d87a65dSAndrew Gallatin mxge_intr(void *arg)
1740b2fc195eSAndrew Gallatin {
17416d87a65dSAndrew Gallatin 	mxge_softc_t *sc = arg;
17425e7d8541SAndrew Gallatin 	mcp_irq_data_t *stats = sc->fw_stats;
17435e7d8541SAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
17445e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
17455e7d8541SAndrew Gallatin 	uint32_t send_done_count;
17465e7d8541SAndrew Gallatin 	uint8_t valid;
1747b2fc195eSAndrew Gallatin 
1748b2fc195eSAndrew Gallatin 
17495e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
17505e7d8541SAndrew Gallatin 	if (!stats->valid) {
17515e7d8541SAndrew Gallatin 		return;
1752b2fc195eSAndrew Gallatin 	}
17535e7d8541SAndrew Gallatin 	valid = stats->valid;
1754b2fc195eSAndrew Gallatin 
17555e7d8541SAndrew Gallatin 	/* lower legacy IRQ  */
17565e7d8541SAndrew Gallatin 	*sc->irq_deassert = 0;
1757b2fc195eSAndrew Gallatin 	mb();
17585e7d8541SAndrew Gallatin 	if (!mxge_deassert_wait)
17595e7d8541SAndrew Gallatin 		/* don't wait for conf. that irq is low */
17605e7d8541SAndrew Gallatin 		stats->valid = 0;
17615e7d8541SAndrew Gallatin 	do {
17625e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
17635e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
17645e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
17655e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
17665e7d8541SAndrew Gallatin 			mxge_tx_done(sc, (int)send_done_count);
17675e7d8541SAndrew Gallatin 			mxge_clean_rx_done(sc);
17685e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
1769b2fc195eSAndrew Gallatin 		}
17705e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
1771b2fc195eSAndrew Gallatin 
17725e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
17735e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
17745e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
1775b2fc195eSAndrew Gallatin 			if (sc->link_state) {
17765e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
17775e7d8541SAndrew Gallatin 				if (mxge_verbose)
17785e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
1779b2fc195eSAndrew Gallatin 			} else {
17805e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
17815e7d8541SAndrew Gallatin 				if (mxge_verbose)
17825e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
1783b2fc195eSAndrew Gallatin 			}
1784b2fc195eSAndrew Gallatin 		}
1785b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
1786b2fc195eSAndrew Gallatin 		    be32toh(sc->fw_stats->rdma_tags_available)) {
1787b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
1788b2fc195eSAndrew Gallatin 				be32toh(sc->fw_stats->rdma_tags_available);
17895e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
17905e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
17915e7d8541SAndrew Gallatin 		}
17925e7d8541SAndrew Gallatin 		sc->down_cnt += stats->link_down;
1793b2fc195eSAndrew Gallatin 	}
1794b2fc195eSAndrew Gallatin 
17955e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
17965e7d8541SAndrew Gallatin 	if (valid & 0x1)
17975e7d8541SAndrew Gallatin 	    *sc->irq_claim = be32toh(3);
17985e7d8541SAndrew Gallatin 	*(sc->irq_claim + 1) = be32toh(3);
1799b2fc195eSAndrew Gallatin }
1800b2fc195eSAndrew Gallatin 
1801b2fc195eSAndrew Gallatin static void
18026d87a65dSAndrew Gallatin mxge_watchdog(struct ifnet *ifp)
1803b2fc195eSAndrew Gallatin {
1804b2fc195eSAndrew Gallatin 	printf("%s called\n", __FUNCTION__);
1805b2fc195eSAndrew Gallatin }
1806b2fc195eSAndrew Gallatin 
1807b2fc195eSAndrew Gallatin static void
18086d87a65dSAndrew Gallatin mxge_init(void *arg)
1809b2fc195eSAndrew Gallatin {
1810b2fc195eSAndrew Gallatin }
1811b2fc195eSAndrew Gallatin 
1812b2fc195eSAndrew Gallatin 
1813b2fc195eSAndrew Gallatin 
1814b2fc195eSAndrew Gallatin static void
18156d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
1816b2fc195eSAndrew Gallatin {
1817b2fc195eSAndrew Gallatin 	int i;
1818b2fc195eSAndrew Gallatin 
1819b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
1820b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
1821b2fc195eSAndrew Gallatin 			continue;
1822b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
1823b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
1824b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
1825b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
1826b2fc195eSAndrew Gallatin 	}
1827b2fc195eSAndrew Gallatin 
1828b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
1829b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
1830b2fc195eSAndrew Gallatin 			continue;
1831b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
1832b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
1833b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
1834b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
1835b2fc195eSAndrew Gallatin 	}
1836b2fc195eSAndrew Gallatin 
1837b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
1838b2fc195eSAndrew Gallatin 		if (sc->tx.info[i].m == NULL)
1839b2fc195eSAndrew Gallatin 			continue;
1840b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->tx.dmat,
1841b2fc195eSAndrew Gallatin 				  sc->tx.info[i].map);
1842b2fc195eSAndrew Gallatin 		m_freem(sc->tx.info[i].m);
1843b2fc195eSAndrew Gallatin 		sc->tx.info[i].m = NULL;
1844b2fc195eSAndrew Gallatin 	}
1845b2fc195eSAndrew Gallatin }
1846b2fc195eSAndrew Gallatin 
1847b2fc195eSAndrew Gallatin static void
18486d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
1849b2fc195eSAndrew Gallatin {
1850b2fc195eSAndrew Gallatin 	int i;
1851b2fc195eSAndrew Gallatin 
1852b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes != NULL) {
1853b2fc195eSAndrew Gallatin 		free(sc->tx.req_bytes, M_DEVBUF);
1854b2fc195eSAndrew Gallatin 	}
1855b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow != NULL)
1856b2fc195eSAndrew Gallatin 		free(sc->rx_small.shadow, M_DEVBUF);
1857b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow != NULL)
1858b2fc195eSAndrew Gallatin 		free(sc->rx_big.shadow, M_DEVBUF);
1859b2fc195eSAndrew Gallatin 	if (sc->tx.info != NULL) {
1860b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->tx.mask; i++) {
1861b2fc195eSAndrew Gallatin 			if (sc->tx.info[i].map != NULL)
1862b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->tx.dmat,
1863b2fc195eSAndrew Gallatin 						   sc->tx.info[i].map);
1864b2fc195eSAndrew Gallatin 		}
1865b2fc195eSAndrew Gallatin 		free(sc->tx.info, M_DEVBUF);
1866b2fc195eSAndrew Gallatin 	}
1867b2fc195eSAndrew Gallatin 	if (sc->rx_small.info != NULL) {
1868b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->rx_small.mask; i++) {
1869b2fc195eSAndrew Gallatin 			if (sc->rx_small.info[i].map != NULL)
1870b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_small.dmat,
1871b2fc195eSAndrew Gallatin 						   sc->rx_small.info[i].map);
1872b2fc195eSAndrew Gallatin 		}
1873b2fc195eSAndrew Gallatin 		free(sc->rx_small.info, M_DEVBUF);
1874b2fc195eSAndrew Gallatin 	}
1875b2fc195eSAndrew Gallatin 	if (sc->rx_big.info != NULL) {
1876b2fc195eSAndrew Gallatin 		for (i = 0; i <= sc->rx_big.mask; i++) {
1877b2fc195eSAndrew Gallatin 			if (sc->rx_big.info[i].map != NULL)
1878b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_big.dmat,
1879b2fc195eSAndrew Gallatin 						   sc->rx_big.info[i].map);
1880b2fc195eSAndrew Gallatin 		}
1881b2fc195eSAndrew Gallatin 		free(sc->rx_big.info, M_DEVBUF);
1882b2fc195eSAndrew Gallatin 	}
1883b2fc195eSAndrew Gallatin 	if (sc->rx_big.extra_map != NULL)
1884b2fc195eSAndrew Gallatin 		bus_dmamap_destroy(sc->rx_big.dmat,
1885b2fc195eSAndrew Gallatin 				   sc->rx_big.extra_map);
1886b2fc195eSAndrew Gallatin 	if (sc->rx_small.extra_map != NULL)
1887b2fc195eSAndrew Gallatin 		bus_dmamap_destroy(sc->rx_small.dmat,
1888b2fc195eSAndrew Gallatin 				   sc->rx_small.extra_map);
1889b2fc195eSAndrew Gallatin 	if (sc->tx.dmat != NULL)
1890b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->tx.dmat);
1891b2fc195eSAndrew Gallatin 	if (sc->rx_small.dmat != NULL)
1892b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->rx_small.dmat);
1893b2fc195eSAndrew Gallatin 	if (sc->rx_big.dmat != NULL)
1894b2fc195eSAndrew Gallatin 		bus_dma_tag_destroy(sc->rx_big.dmat);
1895b2fc195eSAndrew Gallatin }
1896b2fc195eSAndrew Gallatin 
1897b2fc195eSAndrew Gallatin static int
18986d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
1899b2fc195eSAndrew Gallatin {
19006d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1901b2fc195eSAndrew Gallatin 	int tx_ring_size, rx_ring_size;
1902b2fc195eSAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
1903b2fc195eSAndrew Gallatin 	int i, err;
1904b2fc195eSAndrew Gallatin 	unsigned long bytes;
1905b2fc195eSAndrew Gallatin 
1906b2fc195eSAndrew Gallatin 	/* get ring sizes */
19075e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
1908b2fc195eSAndrew Gallatin 	tx_ring_size = cmd.data0;
19095e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
1910b2fc195eSAndrew Gallatin 	if (err != 0) {
1911b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Cannot determine ring sizes\n");
1912b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
1913b2fc195eSAndrew Gallatin 	}
1914b2fc195eSAndrew Gallatin 
1915b2fc195eSAndrew Gallatin 	rx_ring_size = cmd.data0;
1916b2fc195eSAndrew Gallatin 
1917b2fc195eSAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
1918b2fc195eSAndrew Gallatin 	rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t);
1919b2fc195eSAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
192076bb9c5eSAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
192176bb9c5eSAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
1922b2fc195eSAndrew Gallatin 
1923b2fc195eSAndrew Gallatin 	sc->tx.mask = tx_ring_entries - 1;
1924b2fc195eSAndrew Gallatin 	sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1;
1925b2fc195eSAndrew Gallatin 
1926b2fc195eSAndrew Gallatin 	err = ENOMEM;
1927b2fc195eSAndrew Gallatin 
1928b2fc195eSAndrew Gallatin 	/* allocate the tx request copy block */
1929b2fc195eSAndrew Gallatin 	bytes = 8 +
19305e7d8541SAndrew Gallatin 		sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4);
1931b2fc195eSAndrew Gallatin 	sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
1932b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes == NULL)
1933b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
1934b2fc195eSAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
1935b2fc195eSAndrew Gallatin 	sc->tx.req_list = (mcp_kreq_ether_send_t *)
1936b2fc195eSAndrew Gallatin 		((unsigned long)(sc->tx.req_bytes + 7) & ~7UL);
1937b2fc195eSAndrew Gallatin 
1938b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
1939b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow);
1940b2fc195eSAndrew Gallatin 	sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
1941b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow == NULL)
1942b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
1943b2fc195eSAndrew Gallatin 
1944b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow);
1945b2fc195eSAndrew Gallatin 	sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
1946b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow == NULL)
1947b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
1948b2fc195eSAndrew Gallatin 
1949b2fc195eSAndrew Gallatin 	/* allocate the host info rings */
1950b2fc195eSAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*sc->tx.info);
1951b2fc195eSAndrew Gallatin 	sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
1952b2fc195eSAndrew Gallatin 	if (sc->tx.info == NULL)
1953b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
1954b2fc195eSAndrew Gallatin 
1955b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.info);
1956b2fc195eSAndrew Gallatin 	sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
1957b2fc195eSAndrew Gallatin 	if (sc->rx_small.info == NULL)
1958b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
1959b2fc195eSAndrew Gallatin 
1960b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.info);
1961b2fc195eSAndrew Gallatin 	sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
1962b2fc195eSAndrew Gallatin 	if (sc->rx_big.info == NULL)
1963b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
1964b2fc195eSAndrew Gallatin 
1965b2fc195eSAndrew Gallatin 	/* allocate the busdma resources */
1966b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
1967b2fc195eSAndrew Gallatin 				 1,			/* alignment */
1968b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* boundary */
1969b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
1970b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
1971b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
19726d87a65dSAndrew Gallatin 				 MXGE_MAX_ETHER_MTU,	/* maxsize */
19735e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC,	/* num segs */
1974b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* maxsegsize */
1975b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
1976b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
1977b2fc195eSAndrew Gallatin 				 &sc->tx.dmat);		/* tag */
1978b2fc195eSAndrew Gallatin 
1979b2fc195eSAndrew Gallatin 	if (err != 0) {
1980b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
1981b2fc195eSAndrew Gallatin 			      err);
1982b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
1983b2fc195eSAndrew Gallatin 	}
1984b2fc195eSAndrew Gallatin 
1985b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
1986b2fc195eSAndrew Gallatin 				 1,			/* alignment */
1987b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
1988b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
1989b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
1990b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
1991b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
1992b2fc195eSAndrew Gallatin 				 1,			/* num segs */
1993b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
1994b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
1995b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
1996b2fc195eSAndrew Gallatin 				 &sc->rx_small.dmat);	/* tag */
1997b2fc195eSAndrew Gallatin 	if (err != 0) {
1998b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
1999b2fc195eSAndrew Gallatin 			      err);
2000b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2001b2fc195eSAndrew Gallatin 	}
2002b2fc195eSAndrew Gallatin 
2003b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2004b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2005b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2006b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2007b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2008b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2009b2fc195eSAndrew Gallatin 				 4096,			/* maxsize */
2010b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2011b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2012b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2013b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2014b2fc195eSAndrew Gallatin 				 &sc->rx_big.dmat);	/* tag */
2015b2fc195eSAndrew Gallatin 	if (err != 0) {
2016b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2017b2fc195eSAndrew Gallatin 			      err);
2018b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2019b2fc195eSAndrew Gallatin 	}
2020b2fc195eSAndrew Gallatin 
2021b2fc195eSAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
2022b2fc195eSAndrew Gallatin 	   in each ring */
2023b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2024b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->tx.dmat, 0,
2025b2fc195eSAndrew Gallatin 					&sc->tx.info[i].map);
2026b2fc195eSAndrew Gallatin 		if (err != 0) {
2027b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
2028b2fc195eSAndrew Gallatin 			      err);
2029b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2030b2fc195eSAndrew Gallatin 		}
2031b2fc195eSAndrew Gallatin 	}
2032b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2033b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_small.dmat, 0,
2034b2fc195eSAndrew Gallatin 					&sc->rx_small.info[i].map);
2035b2fc195eSAndrew Gallatin 		if (err != 0) {
2036b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2037b2fc195eSAndrew Gallatin 				      err);
2038b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2039b2fc195eSAndrew Gallatin 		}
2040b2fc195eSAndrew Gallatin 	}
2041b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_small.dmat, 0,
2042b2fc195eSAndrew Gallatin 				&sc->rx_small.extra_map);
2043b2fc195eSAndrew Gallatin 	if (err != 0) {
2044b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2045b2fc195eSAndrew Gallatin 			      err);
2046b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2047b2fc195eSAndrew Gallatin 	}
2048b2fc195eSAndrew Gallatin 
2049b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2050b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_big.dmat, 0,
2051b2fc195eSAndrew Gallatin 					&sc->rx_big.info[i].map);
2052b2fc195eSAndrew Gallatin 		if (err != 0) {
2053b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2054b2fc195eSAndrew Gallatin 			      err);
2055b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2056b2fc195eSAndrew Gallatin 		}
2057b2fc195eSAndrew Gallatin 	}
2058b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_big.dmat, 0,
2059b2fc195eSAndrew Gallatin 				&sc->rx_big.extra_map);
2060b2fc195eSAndrew Gallatin 	if (err != 0) {
2061b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2062b2fc195eSAndrew Gallatin 			      err);
2063b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2064b2fc195eSAndrew Gallatin 	}
2065b2fc195eSAndrew Gallatin 	return 0;
2066b2fc195eSAndrew Gallatin 
2067b2fc195eSAndrew Gallatin abort_with_alloc:
20686d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2069b2fc195eSAndrew Gallatin 
2070b2fc195eSAndrew Gallatin abort_with_nothing:
2071b2fc195eSAndrew Gallatin 	return err;
2072b2fc195eSAndrew Gallatin }
2073b2fc195eSAndrew Gallatin 
2074b2fc195eSAndrew Gallatin static int
20756d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc)
2076b2fc195eSAndrew Gallatin {
20776d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2078b2fc195eSAndrew Gallatin 	int i, err;
2079b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2080b2fc195eSAndrew Gallatin 
2081b2fc195eSAndrew Gallatin 
20826d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2083b2fc195eSAndrew Gallatin 	if (err != 0) {
2084b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
2085b2fc195eSAndrew Gallatin 		return EIO;
2086b2fc195eSAndrew Gallatin 	}
2087b2fc195eSAndrew Gallatin 
2088b2fc195eSAndrew Gallatin 	if (MCLBYTES >=
20895e7d8541SAndrew Gallatin 	    sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD)
2090b2fc195eSAndrew Gallatin 		sc->big_bytes = MCLBYTES;
2091b2fc195eSAndrew Gallatin 	else
2092b2fc195eSAndrew Gallatin 		sc->big_bytes = MJUMPAGESIZE;
2093b2fc195eSAndrew Gallatin 
20946d87a65dSAndrew Gallatin 	err = mxge_alloc_rings(sc);
2095b2fc195eSAndrew Gallatin 	if (err != 0) {
2096b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
2097b2fc195eSAndrew Gallatin 		return err;
2098b2fc195eSAndrew Gallatin 	}
2099b2fc195eSAndrew Gallatin 
2100b2fc195eSAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
2101b2fc195eSAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
21026d87a65dSAndrew Gallatin 			     mxge_intr, sc, &sc->ih);
2103b2fc195eSAndrew Gallatin 	if (err != 0) {
2104b2fc195eSAndrew Gallatin 		goto abort_with_rings;
2105b2fc195eSAndrew Gallatin 	}
2106b2fc195eSAndrew Gallatin 
2107b2fc195eSAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
2108b2fc195eSAndrew Gallatin 
21095e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
2110b2fc195eSAndrew Gallatin 	sc->tx.lanai =
2111b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
21126d87a65dSAndrew Gallatin 	err |= mxge_send_cmd(sc,
21135e7d8541SAndrew Gallatin 				 MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
2114b2fc195eSAndrew Gallatin 	sc->rx_small.lanai =
2115b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
21165e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
2117b2fc195eSAndrew Gallatin 	sc->rx_big.lanai =
2118b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
2119b2fc195eSAndrew Gallatin 
2120b2fc195eSAndrew Gallatin 	if (err != 0) {
2121b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
2122b2fc195eSAndrew Gallatin 			      "failed to get ring sizes or locations\n");
2123b2fc195eSAndrew Gallatin 		err = EIO;
2124b2fc195eSAndrew Gallatin 		goto abort_with_irq;
2125b2fc195eSAndrew Gallatin 	}
2126b2fc195eSAndrew Gallatin 
2127b2fc195eSAndrew Gallatin 	if (sc->wc) {
2128b2fc195eSAndrew Gallatin 		sc->tx.wc_fifo = sc->sram + 0x200000;
2129b2fc195eSAndrew Gallatin 		sc->rx_small.wc_fifo = sc->sram + 0x300000;
2130b2fc195eSAndrew Gallatin 		sc->rx_big.wc_fifo = sc->sram + 0x340000;
2131b2fc195eSAndrew Gallatin 	} else {
2132b2fc195eSAndrew Gallatin 		sc->tx.wc_fifo = 0;
2133b2fc195eSAndrew Gallatin 		sc->rx_small.wc_fifo = 0;
2134b2fc195eSAndrew Gallatin 		sc->rx_big.wc_fifo = 0;
2135b2fc195eSAndrew Gallatin 	}
2136b2fc195eSAndrew Gallatin 
2137b2fc195eSAndrew Gallatin 
2138b2fc195eSAndrew Gallatin 	/* stock receive rings */
2139b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2140b2fc195eSAndrew Gallatin 		map = sc->rx_small.info[i].map;
21416d87a65dSAndrew Gallatin 		err = mxge_get_buf_small(sc, map, i);
2142b2fc195eSAndrew Gallatin 		if (err) {
2143b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
2144b2fc195eSAndrew Gallatin 				      i, sc->rx_small.mask + 1);
2145b2fc195eSAndrew Gallatin 			goto abort;
2146b2fc195eSAndrew Gallatin 		}
2147b2fc195eSAndrew Gallatin 	}
2148b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2149b2fc195eSAndrew Gallatin 		map = sc->rx_big.info[i].map;
21506d87a65dSAndrew Gallatin 		err = mxge_get_buf_big(sc, map, i);
2151b2fc195eSAndrew Gallatin 		if (err) {
2152b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
2153b2fc195eSAndrew Gallatin 				      i, sc->rx_big.mask + 1);
2154b2fc195eSAndrew Gallatin 			goto abort;
2155b2fc195eSAndrew Gallatin 		}
2156b2fc195eSAndrew Gallatin 	}
2157b2fc195eSAndrew Gallatin 
2158b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
2159b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
2160b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
2161b2fc195eSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN;
21625e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
2163b2fc195eSAndrew Gallatin 	cmd.data0 = MHLEN;
21645e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
2165b2fc195eSAndrew Gallatin 			     &cmd);
2166b2fc195eSAndrew Gallatin 	cmd.data0 = sc->big_bytes;
21675e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
2168b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
21696d87a65dSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr);
21706d87a65dSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr);
21715e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA, &cmd);
2172b2fc195eSAndrew Gallatin 
2173b2fc195eSAndrew Gallatin 	if (err != 0) {
2174b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
2175b2fc195eSAndrew Gallatin 		goto abort;
2176b2fc195eSAndrew Gallatin 	}
2177b2fc195eSAndrew Gallatin 
2178b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
21795e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
2180b2fc195eSAndrew Gallatin 	if (err) {
2181b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
2182b2fc195eSAndrew Gallatin 		goto abort;
2183b2fc195eSAndrew Gallatin 	}
2184b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
2185b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2186b2fc195eSAndrew Gallatin 
2187b2fc195eSAndrew Gallatin 	return 0;
2188b2fc195eSAndrew Gallatin 
2189b2fc195eSAndrew Gallatin 
2190b2fc195eSAndrew Gallatin abort:
21916d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2192b2fc195eSAndrew Gallatin abort_with_irq:
2193b2fc195eSAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
2194b2fc195eSAndrew Gallatin abort_with_rings:
21956d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2196b2fc195eSAndrew Gallatin 	return err;
2197b2fc195eSAndrew Gallatin }
2198b2fc195eSAndrew Gallatin 
2199b2fc195eSAndrew Gallatin static int
22006d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
2201b2fc195eSAndrew Gallatin {
22026d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2203b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
2204b2fc195eSAndrew Gallatin 
2205b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2206b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
2207b2fc195eSAndrew Gallatin 	mb();
22085e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
2209b2fc195eSAndrew Gallatin 	if (err) {
2210b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
2211b2fc195eSAndrew Gallatin 	}
2212b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2213b2fc195eSAndrew Gallatin 		/* wait for down irq */
22146d87a65dSAndrew Gallatin 		(void)tsleep(&sc->down_cnt, PWAIT, "down mxge", hz);
2215b2fc195eSAndrew Gallatin 	}
2216b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2217b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
2218b2fc195eSAndrew Gallatin 	}
2219b2fc195eSAndrew Gallatin 	if (sc->ih != NULL)
2220b2fc195eSAndrew Gallatin 		bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
22216d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
22226d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2223b2fc195eSAndrew Gallatin 	return 0;
2224b2fc195eSAndrew Gallatin }
2225b2fc195eSAndrew Gallatin 
2226b2fc195eSAndrew Gallatin 
2227b2fc195eSAndrew Gallatin static int
22286d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
2229b2fc195eSAndrew Gallatin {
2230b2fc195eSAndrew Gallatin 	return EINVAL;
2231b2fc195eSAndrew Gallatin }
2232b2fc195eSAndrew Gallatin 
2233b2fc195eSAndrew Gallatin static int
22346d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
2235b2fc195eSAndrew Gallatin {
2236b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
2237b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
2238b2fc195eSAndrew Gallatin 	int err = 0;
2239b2fc195eSAndrew Gallatin 
2240b2fc195eSAndrew Gallatin 
2241b2fc195eSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN;
22426d87a65dSAndrew Gallatin 	if ((real_mtu > MXGE_MAX_ETHER_MTU) ||
2243b2fc195eSAndrew Gallatin 	    real_mtu < 60)
2244b2fc195eSAndrew Gallatin 		return EINVAL;
2245b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
2246b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
2247b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
2248b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
22496d87a65dSAndrew Gallatin 		mxge_close(sc);
22506d87a65dSAndrew Gallatin 		err = mxge_open(sc);
2251b2fc195eSAndrew Gallatin 		if (err != 0) {
2252b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
22536d87a65dSAndrew Gallatin 			mxge_close(sc);
22546d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
2255b2fc195eSAndrew Gallatin 		}
2256b2fc195eSAndrew Gallatin 	}
2257b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
2258b2fc195eSAndrew Gallatin 	return err;
2259b2fc195eSAndrew Gallatin }
2260b2fc195eSAndrew Gallatin 
2261b2fc195eSAndrew Gallatin static void
22626d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
2263b2fc195eSAndrew Gallatin {
22646d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2265b2fc195eSAndrew Gallatin 
2266b2fc195eSAndrew Gallatin 
2267b2fc195eSAndrew Gallatin 	if (sc == NULL)
2268b2fc195eSAndrew Gallatin 		return;
2269b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
2270b2fc195eSAndrew Gallatin 	ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0;
2271b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
2272b2fc195eSAndrew Gallatin 	ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0;
2273b2fc195eSAndrew Gallatin }
2274b2fc195eSAndrew Gallatin 
2275b2fc195eSAndrew Gallatin static int
22766d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
2277b2fc195eSAndrew Gallatin {
22786d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2279b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
2280b2fc195eSAndrew Gallatin 	int err, mask;
2281b2fc195eSAndrew Gallatin 
2282b2fc195eSAndrew Gallatin 	err = 0;
2283b2fc195eSAndrew Gallatin 	switch (command) {
2284b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
2285b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
2286b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
2287b2fc195eSAndrew Gallatin 		break;
2288b2fc195eSAndrew Gallatin 
2289b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
22906d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
2291b2fc195eSAndrew Gallatin 		break;
2292b2fc195eSAndrew Gallatin 
2293b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
2294b2fc195eSAndrew Gallatin 		sx_xlock(&sc->driver_lock);
2295b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
2296b2fc195eSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
22976d87a65dSAndrew Gallatin 				err = mxge_open(sc);
2298b2fc195eSAndrew Gallatin 		} else {
2299b2fc195eSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
23006d87a65dSAndrew Gallatin 				mxge_close(sc);
2301b2fc195eSAndrew Gallatin 		}
2302b2fc195eSAndrew Gallatin 		sx_xunlock(&sc->driver_lock);
2303b2fc195eSAndrew Gallatin 		break;
2304b2fc195eSAndrew Gallatin 
2305b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
2306b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
2307b2fc195eSAndrew Gallatin 		err = 0;
2308b2fc195eSAndrew Gallatin 		break;
2309b2fc195eSAndrew Gallatin 
2310b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
2311b2fc195eSAndrew Gallatin 		sx_xlock(&sc->driver_lock);
2312b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
2313b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
2314b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
2315b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TXCSUM;
2316b2fc195eSAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
2317b2fc195eSAndrew Gallatin 			} else {
2318b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
2319b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
2320b2fc195eSAndrew Gallatin 			}
2321b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
2322b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
2323b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
23245e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
2325b2fc195eSAndrew Gallatin 			} else {
2326b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
23275e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
2328b2fc195eSAndrew Gallatin 			}
2329b2fc195eSAndrew Gallatin 		}
2330b2fc195eSAndrew Gallatin 		sx_xunlock(&sc->driver_lock);
2331b2fc195eSAndrew Gallatin 		break;
2332b2fc195eSAndrew Gallatin 
2333b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
2334b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
2335b2fc195eSAndrew Gallatin 				    &sc->media, command);
2336b2fc195eSAndrew Gallatin                 break;
2337b2fc195eSAndrew Gallatin 
2338b2fc195eSAndrew Gallatin 	default:
2339b2fc195eSAndrew Gallatin 		err = ENOTTY;
2340b2fc195eSAndrew Gallatin         }
2341b2fc195eSAndrew Gallatin 	return err;
2342b2fc195eSAndrew Gallatin }
2343b2fc195eSAndrew Gallatin 
2344b2fc195eSAndrew Gallatin static void
23456d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
2346b2fc195eSAndrew Gallatin {
2347b2fc195eSAndrew Gallatin 
23486d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
23496d87a65dSAndrew Gallatin 			  &mxge_flow_control);
23506d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
23516d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
23526d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
23536d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
23545e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
23555e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
23565e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
23575e7d8541SAndrew Gallatin 			  &mxge_verbose);
2358b2fc195eSAndrew Gallatin 
23595e7d8541SAndrew Gallatin 	if (bootverbose)
23605e7d8541SAndrew Gallatin 		mxge_verbose = 1;
23616d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
23626d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
23636d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
2364b2fc195eSAndrew Gallatin }
2365b2fc195eSAndrew Gallatin 
2366b2fc195eSAndrew Gallatin static int
23676d87a65dSAndrew Gallatin mxge_attach(device_t dev)
2368b2fc195eSAndrew Gallatin {
23696d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
2370b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2371b2fc195eSAndrew Gallatin 	size_t bytes;
23725e7d8541SAndrew Gallatin 	int rid, err;
2373b2fc195eSAndrew Gallatin 	uint16_t cmd;
2374b2fc195eSAndrew Gallatin 
2375b2fc195eSAndrew Gallatin 	sc->dev = dev;
23766d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
2377b2fc195eSAndrew Gallatin 
2378b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
2379b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2380b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2381b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2382b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2383b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
23846d87a65dSAndrew Gallatin 				 MXGE_MAX_ETHER_MTU,	/* maxsize */
23855e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
2386b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2387b2fc195eSAndrew Gallatin 				 0,			/* flags */
2388b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2389b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
2390b2fc195eSAndrew Gallatin 
2391b2fc195eSAndrew Gallatin 	if (err != 0) {
2392b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
2393b2fc195eSAndrew Gallatin 			      err);
2394b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2395b2fc195eSAndrew Gallatin 	}
2396b2fc195eSAndrew Gallatin 
2397b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
2398b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
2399b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
2400b2fc195eSAndrew Gallatin 		err = ENOSPC;
2401b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
2402b2fc195eSAndrew Gallatin 	}
2403b2fc195eSAndrew Gallatin 	mtx_init(&sc->cmd_lock, NULL,
2404b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
2405b2fc195eSAndrew Gallatin 	mtx_init(&sc->tx_lock, device_get_nameunit(dev),
2406b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
2407b2fc195eSAndrew Gallatin 	sx_init(&sc->driver_lock, device_get_nameunit(dev));
2408b2fc195eSAndrew Gallatin 
2409b2fc195eSAndrew Gallatin 	/* Enable DMA and Memory space access */
2410b2fc195eSAndrew Gallatin 	pci_enable_busmaster(dev);
2411b2fc195eSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2412b2fc195eSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
2413b2fc195eSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2414b2fc195eSAndrew Gallatin 
2415b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
2416b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
2417b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
2418b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
2419b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
2420b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
2421b2fc195eSAndrew Gallatin 		err = ENXIO;
2422b2fc195eSAndrew Gallatin 		goto abort_with_lock;
2423b2fc195eSAndrew Gallatin 	}
2424b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
2425b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
2426b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
2427b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
2428b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
2429b2fc195eSAndrew Gallatin 		err = ENXIO;
2430b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2431b2fc195eSAndrew Gallatin 	}
2432b2fc195eSAndrew Gallatin 
2433b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
2434b2fc195eSAndrew Gallatin 	   lanai SRAM */
24356d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
2436b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
2437b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
24386d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
2439b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
24406d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
24416d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
2442b2fc195eSAndrew Gallatin 	if (err != 0)
2443b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2444b2fc195eSAndrew Gallatin 
2445b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
24466d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
2447b2fc195eSAndrew Gallatin 
2448b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
24496d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
24506d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
2451b2fc195eSAndrew Gallatin 	if (err != 0)
2452b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
2453b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
24546d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
2455b2fc195eSAndrew Gallatin 	if (err != 0)
2456b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
2457b2fc195eSAndrew Gallatin 
24586d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->fw_stats_dma,
2459b2fc195eSAndrew Gallatin 			     sizeof (*sc->fw_stats), 64);
2460b2fc195eSAndrew Gallatin 	if (err != 0)
2461b2fc195eSAndrew Gallatin 		goto abort_with_zeropad_dma;
24625e7d8541SAndrew Gallatin 	sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr;
2463b2fc195eSAndrew Gallatin 
2464b2fc195eSAndrew Gallatin 
2465b2fc195eSAndrew Gallatin 	/* allocate interrupt queues */
24665e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);
24675e7d8541SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096);
2468b2fc195eSAndrew Gallatin 	if (err != 0)
24695e7d8541SAndrew Gallatin 		goto abort_with_fw_stats;
24705e7d8541SAndrew Gallatin 	sc->rx_done.entry = sc->rx_done.dma.addr;
24715e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
2472b2fc195eSAndrew Gallatin 	/* Add our ithread  */
2473b2fc195eSAndrew Gallatin 	rid = 0;
2474b2fc195eSAndrew Gallatin 	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0,
2475b2fc195eSAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
2476b2fc195eSAndrew Gallatin 	if (sc->irq_res == NULL) {
2477b2fc195eSAndrew Gallatin 		device_printf(dev, "could not alloc interrupt\n");
24785e7d8541SAndrew Gallatin 		goto abort_with_rx_done;
2479b2fc195eSAndrew Gallatin 	}
2480b2fc195eSAndrew Gallatin 
2481b2fc195eSAndrew Gallatin 	/* load the firmware */
24826d87a65dSAndrew Gallatin 	mxge_select_firmware(sc);
2483b2fc195eSAndrew Gallatin 
24846d87a65dSAndrew Gallatin 	err = mxge_load_firmware(sc);
2485b2fc195eSAndrew Gallatin 	if (err != 0)
2486b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
24875e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
24886d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2489b2fc195eSAndrew Gallatin 	if (err != 0)
2490b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
2491b2fc195eSAndrew Gallatin 
2492b2fc195eSAndrew Gallatin 	/* hook into the network stack */
2493b2fc195eSAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
2494b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
2495b2fc195eSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM;
2496b2fc195eSAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
2497b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
24985e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
24996d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
2500b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
2501b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
25026d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
25036d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
25046d87a65dSAndrew Gallatin 	ifp->if_watchdog = mxge_watchdog;
2505b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
2506b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
25076d87a65dSAndrew Gallatin 	ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN;
2508b2fc195eSAndrew Gallatin 
2509b2fc195eSAndrew Gallatin 	/* Initialise the ifmedia structure */
25106d87a65dSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
25116d87a65dSAndrew Gallatin 		     mxge_media_status);
2512b2fc195eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
25136d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
2514b2fc195eSAndrew Gallatin 	return 0;
2515b2fc195eSAndrew Gallatin 
2516b2fc195eSAndrew Gallatin abort_with_irq_res:
2517b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
25185e7d8541SAndrew Gallatin abort_with_rx_done:
25195e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
25205e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
25215e7d8541SAndrew Gallatin abort_with_fw_stats:
25226d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
2523b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
25246d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
2525b2fc195eSAndrew Gallatin abort_with_cmd_dma:
25266d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
2527b2fc195eSAndrew Gallatin abort_with_mem_res:
2528b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
2529b2fc195eSAndrew Gallatin abort_with_lock:
2530b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
2531b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->cmd_lock);
2532b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->tx_lock);
2533b2fc195eSAndrew Gallatin 	sx_destroy(&sc->driver_lock);
2534b2fc195eSAndrew Gallatin 	if_free(ifp);
2535b2fc195eSAndrew Gallatin abort_with_parent_dmat:
2536b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
2537b2fc195eSAndrew Gallatin 
2538b2fc195eSAndrew Gallatin abort_with_nothing:
2539b2fc195eSAndrew Gallatin 	return err;
2540b2fc195eSAndrew Gallatin }
2541b2fc195eSAndrew Gallatin 
2542b2fc195eSAndrew Gallatin static int
25436d87a65dSAndrew Gallatin mxge_detach(device_t dev)
2544b2fc195eSAndrew Gallatin {
25456d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
2546b2fc195eSAndrew Gallatin 
2547b2fc195eSAndrew Gallatin 	sx_xlock(&sc->driver_lock);
2548b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
25496d87a65dSAndrew Gallatin 		mxge_close(sc);
2550b2fc195eSAndrew Gallatin 	sx_xunlock(&sc->driver_lock);
2551b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
2552091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
2553b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
25545e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
25555e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
25566d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
25576d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
25586d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
2559b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
2560b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
2561b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->cmd_lock);
2562b2fc195eSAndrew Gallatin 	mtx_destroy(&sc->tx_lock);
2563b2fc195eSAndrew Gallatin 	sx_destroy(&sc->driver_lock);
2564b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
2565b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
2566b2fc195eSAndrew Gallatin 	return 0;
2567b2fc195eSAndrew Gallatin }
2568b2fc195eSAndrew Gallatin 
2569b2fc195eSAndrew Gallatin static int
25706d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
2571b2fc195eSAndrew Gallatin {
2572b2fc195eSAndrew Gallatin 	return 0;
2573b2fc195eSAndrew Gallatin }
2574b2fc195eSAndrew Gallatin 
2575b2fc195eSAndrew Gallatin /*
2576b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
2577b2fc195eSAndrew Gallatin 
2578b2fc195eSAndrew Gallatin   Local Variables:
2579b2fc195eSAndrew Gallatin   c-file-style:"linux"
2580b2fc195eSAndrew Gallatin   tab-width:8
2581b2fc195eSAndrew Gallatin   End:
2582b2fc195eSAndrew Gallatin */
2583