xref: /freebsd/sys/dev/mxge/if_mxge.c (revision eb8e82f5fdf848e79df21d7b6c1ee4d2312dc956)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
3eb8e82f5SAndrew Gallatin Copyright (c) 2006-2007, 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 
12eb8e82f5SAndrew Gallatin  2. Neither the name of the Myricom Inc, nor the names of its
13b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
14b2fc195eSAndrew Gallatin     this software without specific prior written permission.
15b2fc195eSAndrew Gallatin 
16b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
27b2fc195eSAndrew Gallatin 
28b2fc195eSAndrew Gallatin ***************************************************************************/
29b2fc195eSAndrew Gallatin 
30b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
31b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
32b2fc195eSAndrew Gallatin 
33b2fc195eSAndrew Gallatin #include <sys/param.h>
34b2fc195eSAndrew Gallatin #include <sys/systm.h>
35b2fc195eSAndrew Gallatin #include <sys/linker.h>
36b2fc195eSAndrew Gallatin #include <sys/firmware.h>
37b2fc195eSAndrew Gallatin #include <sys/endian.h>
38b2fc195eSAndrew Gallatin #include <sys/sockio.h>
39b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
40b2fc195eSAndrew Gallatin #include <sys/malloc.h>
41b2fc195eSAndrew Gallatin #include <sys/kdb.h>
42b2fc195eSAndrew Gallatin #include <sys/kernel.h>
434e7f640dSJohn Baldwin #include <sys/lock.h>
44b2fc195eSAndrew Gallatin #include <sys/module.h>
45b2fc195eSAndrew Gallatin #include <sys/memrange.h>
46b2fc195eSAndrew Gallatin #include <sys/socket.h>
47b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
48b2fc195eSAndrew Gallatin #include <sys/sx.h>
49b2fc195eSAndrew Gallatin 
50b2fc195eSAndrew Gallatin #include <net/if.h>
51b2fc195eSAndrew Gallatin #include <net/if_arp.h>
52b2fc195eSAndrew Gallatin #include <net/ethernet.h>
53b2fc195eSAndrew Gallatin #include <net/if_dl.h>
54b2fc195eSAndrew Gallatin #include <net/if_media.h>
55b2fc195eSAndrew Gallatin 
56b2fc195eSAndrew Gallatin #include <net/bpf.h>
57b2fc195eSAndrew Gallatin 
58b2fc195eSAndrew Gallatin #include <net/if_types.h>
59b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
60b2fc195eSAndrew Gallatin #include <net/zlib.h>
61b2fc195eSAndrew Gallatin 
62b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
63b2fc195eSAndrew Gallatin #include <netinet/in.h>
64b2fc195eSAndrew Gallatin #include <netinet/ip.h>
65aed8e389SAndrew Gallatin #include <netinet/tcp.h>
66b2fc195eSAndrew Gallatin 
67b2fc195eSAndrew Gallatin #include <machine/bus.h>
68053e637fSAndrew Gallatin #include <machine/in_cksum.h>
69b2fc195eSAndrew Gallatin #include <machine/resource.h>
70b2fc195eSAndrew Gallatin #include <sys/bus.h>
71b2fc195eSAndrew Gallatin #include <sys/rman.h>
72b2fc195eSAndrew Gallatin 
73b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
74b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
75b2fc195eSAndrew Gallatin 
76b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
77b2fc195eSAndrew Gallatin #include <vm/pmap.h>
78b2fc195eSAndrew Gallatin 
79c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
80c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
81c2c14a69SAndrew Gallatin #endif
82c2c14a69SAndrew Gallatin 
836d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
846d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
856d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
86b2fc195eSAndrew Gallatin 
87b2fc195eSAndrew Gallatin /* tunable params */
886d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
89d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
906d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
915e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
926d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
935e7d8541SAndrew Gallatin static int mxge_verbose = 0;
94f04b33f8SAndrew Gallatin static int mxge_lro_cnt = 8;
95dce01b9bSAndrew Gallatin static int mxge_ticks;
966d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
976d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
98b2fc195eSAndrew Gallatin 
996d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1006d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1016d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1026d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1036d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
104b2fc195eSAndrew Gallatin 
1056d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
106b2fc195eSAndrew Gallatin {
107b2fc195eSAndrew Gallatin   /* Device interface */
1086d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1096d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1106d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1116d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
112b2fc195eSAndrew Gallatin   {0, 0}
113b2fc195eSAndrew Gallatin };
114b2fc195eSAndrew Gallatin 
1156d87a65dSAndrew Gallatin static driver_t mxge_driver =
116b2fc195eSAndrew Gallatin {
1176d87a65dSAndrew Gallatin   "mxge",
1186d87a65dSAndrew Gallatin   mxge_methods,
1196d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
120b2fc195eSAndrew Gallatin };
121b2fc195eSAndrew Gallatin 
1226d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
123b2fc195eSAndrew Gallatin 
124b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1256d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1266d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
127b2fc195eSAndrew Gallatin 
1288fe615baSAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc);
1298fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
130276edd10SAndrew Gallatin static int mxge_close(mxge_softc_t *sc);
131276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
132276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1338fe615baSAndrew Gallatin 
134b2fc195eSAndrew Gallatin static int
1356d87a65dSAndrew Gallatin mxge_probe(device_t dev)
136b2fc195eSAndrew Gallatin {
1376d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1386d87a65dSAndrew Gallatin       (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) {
139b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
140b2fc195eSAndrew Gallatin 	  return 0;
141b2fc195eSAndrew Gallatin   }
142b2fc195eSAndrew Gallatin   return ENXIO;
143b2fc195eSAndrew Gallatin }
144b2fc195eSAndrew Gallatin 
145b2fc195eSAndrew Gallatin static void
1466d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
147b2fc195eSAndrew Gallatin {
148b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
149b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
150b2fc195eSAndrew Gallatin 	vm_offset_t len;
151b2fc195eSAndrew Gallatin 	int err, action;
152b2fc195eSAndrew Gallatin 
1534d69a9d0SAndrew Gallatin 	sc->wc = 1;
154b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
155c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
156c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
157c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
158c2c14a69SAndrew Gallatin 	if (err == 0)
159c2c14a69SAndrew Gallatin 		return;
160c2c14a69SAndrew Gallatin 	else
161c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
162c2c14a69SAndrew Gallatin 			      err);
163c2c14a69SAndrew Gallatin #endif
164c2c14a69SAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
165b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
166b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
167b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
168b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1696d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
170b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
171b2fc195eSAndrew Gallatin 	if (err != 0) {
1724d69a9d0SAndrew Gallatin 		sc->wc = 0;
173b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
174b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
175b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
176b2fc195eSAndrew Gallatin 	}
177b2fc195eSAndrew Gallatin }
178b2fc195eSAndrew Gallatin 
179b2fc195eSAndrew Gallatin 
180b2fc195eSAndrew Gallatin /* callback to get our DMA address */
181b2fc195eSAndrew Gallatin static void
1826d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
183b2fc195eSAndrew Gallatin 			 int error)
184b2fc195eSAndrew Gallatin {
185b2fc195eSAndrew Gallatin 	if (error == 0) {
186b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
187b2fc195eSAndrew Gallatin 	}
188b2fc195eSAndrew Gallatin }
189b2fc195eSAndrew Gallatin 
190b2fc195eSAndrew Gallatin static int
1916d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
192b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
193b2fc195eSAndrew Gallatin {
194b2fc195eSAndrew Gallatin 	int err;
195b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
196b2fc195eSAndrew Gallatin 
197b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
198b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
199b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
200b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
201b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
202b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
203b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
204b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
205b2fc195eSAndrew Gallatin 				 1,			/* num segs */
206b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
207b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
208b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
209b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
210b2fc195eSAndrew Gallatin 	if (err != 0) {
211b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
212b2fc195eSAndrew Gallatin 		return err;
213b2fc195eSAndrew Gallatin 	}
214b2fc195eSAndrew Gallatin 
215b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
216b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
217b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
218b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
219b2fc195eSAndrew Gallatin 	if (err != 0) {
220b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
221b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
222b2fc195eSAndrew Gallatin 	}
223b2fc195eSAndrew Gallatin 
224b2fc195eSAndrew Gallatin 	/* load the memory */
225b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2266d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
227b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
228b2fc195eSAndrew Gallatin 	if (err != 0) {
229b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
230b2fc195eSAndrew Gallatin 		goto abort_with_mem;
231b2fc195eSAndrew Gallatin 	}
232b2fc195eSAndrew Gallatin 	return 0;
233b2fc195eSAndrew Gallatin 
234b2fc195eSAndrew Gallatin abort_with_mem:
235b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
236b2fc195eSAndrew Gallatin abort_with_dmat:
237b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
238b2fc195eSAndrew Gallatin 	return err;
239b2fc195eSAndrew Gallatin }
240b2fc195eSAndrew Gallatin 
241b2fc195eSAndrew Gallatin 
242b2fc195eSAndrew Gallatin static void
2436d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
244b2fc195eSAndrew Gallatin {
245b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
246b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
247b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
248b2fc195eSAndrew Gallatin }
249b2fc195eSAndrew Gallatin 
250b2fc195eSAndrew Gallatin /*
251b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
252b2fc195eSAndrew Gallatin  * SN=x\0
253b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
254b2fc195eSAndrew Gallatin  * PC=text\0
255b2fc195eSAndrew Gallatin  */
256b2fc195eSAndrew Gallatin 
257b2fc195eSAndrew Gallatin static int
2586d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
259b2fc195eSAndrew Gallatin {
2606d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
261b2fc195eSAndrew Gallatin 
262b2fc195eSAndrew Gallatin 	char *ptr, *limit;
263b2fc195eSAndrew Gallatin 	int i, found_mac;
264b2fc195eSAndrew Gallatin 
265b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2666d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
267b2fc195eSAndrew Gallatin 	found_mac = 0;
268b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
269b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2705e7d8541SAndrew Gallatin 			ptr += 1;
271b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
272b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2735e7d8541SAndrew Gallatin 				ptr += 3;
274b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
275b2fc195eSAndrew Gallatin 					goto abort;
276b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
277b2fc195eSAndrew Gallatin 				found_mac = 1;
278b2fc195eSAndrew Gallatin 			}
2795e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2805e7d8541SAndrew Gallatin 			ptr += 3;
2815e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2825e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2835e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2845e7d8541SAndrew Gallatin 			ptr += 3;
2855e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2865e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
287b2fc195eSAndrew Gallatin 		}
2886d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
289b2fc195eSAndrew Gallatin 	}
290b2fc195eSAndrew Gallatin 
291b2fc195eSAndrew Gallatin 	if (found_mac)
292b2fc195eSAndrew Gallatin 		return 0;
293b2fc195eSAndrew Gallatin 
294b2fc195eSAndrew Gallatin  abort:
295b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
296b2fc195eSAndrew Gallatin 
297b2fc195eSAndrew Gallatin 	return ENXIO;
298b2fc195eSAndrew Gallatin }
299b2fc195eSAndrew Gallatin 
300b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
3018fe615baSAndrew Gallatin static void
3028fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
303b2fc195eSAndrew Gallatin {
304b2fc195eSAndrew Gallatin 	uint32_t val;
3058fe615baSAndrew Gallatin 	unsigned long base, off;
306b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3078fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3088fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
309b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
310b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
311b2fc195eSAndrew Gallatin 
3128fe615baSAndrew Gallatin 
3138fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3148fe615baSAndrew Gallatin 		return;
3158fe615baSAndrew Gallatin 
3168fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3178fe615baSAndrew Gallatin 	if (pdev == NULL) {
3188fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3198fe615baSAndrew Gallatin 		return;
3208fe615baSAndrew Gallatin 	}
3218fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3228fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3238fe615baSAndrew Gallatin 
3248fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3258fe615baSAndrew Gallatin 		return;
3268fe615baSAndrew Gallatin 
3278fe615baSAndrew Gallatin 	base = 0;
3288fe615baSAndrew Gallatin 
3298fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3308fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3318fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3328fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3338fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3348fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3358fe615baSAndrew Gallatin 		if (mcp55 &&
3368fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3378fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3388fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3398fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3408fe615baSAndrew Gallatin 		}
3418fe615baSAndrew Gallatin 	}
3428fe615baSAndrew Gallatin 	if (!base)
3438fe615baSAndrew Gallatin 		return;
3448fe615baSAndrew Gallatin 
345b2fc195eSAndrew Gallatin 	/* XXXX
346b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
347b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
348b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
349b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
350b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
351b2fc195eSAndrew Gallatin 	*/
352b2fc195eSAndrew Gallatin #if 0
353b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
354b2fc195eSAndrew Gallatin 	   config space */
355b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
356b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
357b2fc195eSAndrew Gallatin 		val |= 0x40;
358b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3598fe615baSAndrew Gallatin 		return;
360b2fc195eSAndrew Gallatin 	}
361b2fc195eSAndrew Gallatin #endif
362b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
363b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
364b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
365b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
366b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
367b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
368b2fc195eSAndrew Gallatin 	 */
369b2fc195eSAndrew Gallatin 
370b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
371b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
372b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
373b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
374b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
375b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
376b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
377b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
378b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
379b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
380b2fc195eSAndrew Gallatin 
3818fe615baSAndrew Gallatin 	off =  base
382b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
383b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
384b2fc195eSAndrew Gallatin 						 + 8 * slot);
385b2fc195eSAndrew Gallatin 
386b2fc195eSAndrew Gallatin 	/* map it into the kernel */
387b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
388b2fc195eSAndrew Gallatin 
389b2fc195eSAndrew Gallatin 
390b2fc195eSAndrew Gallatin 	if (va == NULL) {
391b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3928fe615baSAndrew Gallatin 		return;
393b2fc195eSAndrew Gallatin 	}
394b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
395b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
396b2fc195eSAndrew Gallatin 
397b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
398b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
399b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
400b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
401b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
402b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
403b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4048fe615baSAndrew Gallatin 		return;
405b2fc195eSAndrew Gallatin 	}
406b2fc195eSAndrew Gallatin 
407b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
408b2fc195eSAndrew Gallatin 	val = *ptr32;
409b2fc195eSAndrew Gallatin 
410b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
411b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
412b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4138fe615baSAndrew Gallatin 		return;
414b2fc195eSAndrew Gallatin 	}
415b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
416b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4175e7d8541SAndrew Gallatin 	if (mxge_verbose)
418b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4195e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4205e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
421b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4228fe615baSAndrew Gallatin 	return;
423b2fc195eSAndrew Gallatin }
424b2fc195eSAndrew Gallatin #else
4258fe615baSAndrew Gallatin static void
4266d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
427b2fc195eSAndrew Gallatin {
428b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
429b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4308fe615baSAndrew Gallatin 	return;
431b2fc195eSAndrew Gallatin }
432b2fc195eSAndrew Gallatin #endif
4338fe615baSAndrew Gallatin 
4348fe615baSAndrew Gallatin 
4358fe615baSAndrew Gallatin static int
4368fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4378fe615baSAndrew Gallatin {
4388fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4398fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4408fe615baSAndrew Gallatin 	int status;
4418fe615baSAndrew Gallatin 	uint32_t len;
4428fe615baSAndrew Gallatin 	char *test = " ";
4438fe615baSAndrew Gallatin 
4448fe615baSAndrew Gallatin 
4458fe615baSAndrew Gallatin 	/* Run a small DMA test.
4468fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4478fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4488fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4498fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4508fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4518fe615baSAndrew Gallatin 	 * transfers took to complete.
4528fe615baSAndrew Gallatin 	 */
4538fe615baSAndrew Gallatin 
4548fe615baSAndrew Gallatin 	len = sc->tx.boundary;
4558fe615baSAndrew Gallatin 
4568fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4578fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4588fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4598fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4608fe615baSAndrew Gallatin 	if (status != 0) {
4618fe615baSAndrew Gallatin 		test = "read";
4628fe615baSAndrew Gallatin 		goto abort;
4638fe615baSAndrew Gallatin 	}
4648fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4658fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4668fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4678fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4688fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4698fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4708fe615baSAndrew Gallatin 	if (status != 0) {
4718fe615baSAndrew Gallatin 		test = "write";
4728fe615baSAndrew Gallatin 		goto abort;
4738fe615baSAndrew Gallatin 	}
4748fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
4758fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4768fe615baSAndrew Gallatin 
4778fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4788fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4798fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
4808fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4818fe615baSAndrew Gallatin 	if (status != 0) {
4828fe615baSAndrew Gallatin 		test = "read/write";
4838fe615baSAndrew Gallatin 		goto abort;
4848fe615baSAndrew Gallatin 	}
4858fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4868fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4878fe615baSAndrew Gallatin 
4888fe615baSAndrew Gallatin abort:
4898fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
4908fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4918fe615baSAndrew Gallatin 			      test, status);
4928fe615baSAndrew Gallatin 
4938fe615baSAndrew Gallatin 	return status;
4948fe615baSAndrew Gallatin }
4958fe615baSAndrew Gallatin 
496b2fc195eSAndrew Gallatin /*
497b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
498b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
499b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
500b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
501b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
502b2fc195eSAndrew Gallatin  *
503b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
504b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
505b2fc195eSAndrew Gallatin  *
506b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
507b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
508b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
509b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
510b2fc195eSAndrew Gallatin  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
511b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
512b2fc195eSAndrew Gallatin  * firmware image, and set tx.boundary to 4KB.
513b2fc195eSAndrew Gallatin  */
514b2fc195eSAndrew Gallatin 
5158fe615baSAndrew Gallatin static int
5168fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5178fe615baSAndrew Gallatin {
5188fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5198fe615baSAndrew Gallatin 	int reg, status;
5208fe615baSAndrew Gallatin 	uint16_t pectl;
5218fe615baSAndrew Gallatin 
5228fe615baSAndrew Gallatin 	sc->tx.boundary = 4096;
5238fe615baSAndrew Gallatin 	/*
5248fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5258fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5268fe615baSAndrew Gallatin 	 */
5278fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5288fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5298fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5308fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5318fe615baSAndrew Gallatin 				      pectl);
5328fe615baSAndrew Gallatin 			sc->tx.boundary = 2048;
5338fe615baSAndrew Gallatin 		}
5348fe615baSAndrew Gallatin 	}
5358fe615baSAndrew Gallatin 
5368fe615baSAndrew Gallatin 	/*
5378fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5388fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5398fe615baSAndrew Gallatin 	 */
5408fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5418fe615baSAndrew Gallatin 	status = mxge_load_firmware(sc);
5428fe615baSAndrew Gallatin 	if (status != 0) {
5438fe615baSAndrew Gallatin 		return status;
5448fe615baSAndrew Gallatin 	}
5458fe615baSAndrew Gallatin 
5468fe615baSAndrew Gallatin 	/*
5478fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5488fe615baSAndrew Gallatin 	 */
5498fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5508fe615baSAndrew Gallatin 
5518fe615baSAndrew Gallatin 	/*
5528fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5538fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5548fe615baSAndrew Gallatin 	 */
5558fe615baSAndrew Gallatin 
5568fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5578fe615baSAndrew Gallatin 	if (status == 0)
5588fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5598fe615baSAndrew Gallatin 
5608fe615baSAndrew Gallatin 	if (status != E2BIG)
5618fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5628fe615baSAndrew Gallatin 	if (status == ENOSYS)
5638fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5648fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5658fe615baSAndrew Gallatin 	return status;
5668fe615baSAndrew Gallatin }
5678fe615baSAndrew Gallatin 
5688fe615baSAndrew Gallatin static int
5696d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
570b2fc195eSAndrew Gallatin {
5718fe615baSAndrew Gallatin 	int aligned = 0;
572b2fc195eSAndrew Gallatin 
573d91b1b49SAndrew Gallatin 
574d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
575d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
576d91b1b49SAndrew Gallatin 			aligned = 1;
577d91b1b49SAndrew Gallatin 		else
578d91b1b49SAndrew Gallatin 			aligned = 0;
579d91b1b49SAndrew Gallatin 		if (mxge_verbose)
580d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
581d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
582d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
583d91b1b49SAndrew Gallatin 		goto abort;
584d91b1b49SAndrew Gallatin 	}
585d91b1b49SAndrew Gallatin 
586d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
587d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
588d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
589d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
590d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
591d91b1b49SAndrew Gallatin 			      sc->link_width);
592d91b1b49SAndrew Gallatin 		aligned = 1;
593d91b1b49SAndrew Gallatin 		goto abort;
594d91b1b49SAndrew Gallatin 	}
595d91b1b49SAndrew Gallatin 
5968fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
5978fe615baSAndrew Gallatin 		return 0;
598b2fc195eSAndrew Gallatin 
599b2fc195eSAndrew Gallatin abort:
600b2fc195eSAndrew Gallatin 	if (aligned) {
6016d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
602b2fc195eSAndrew Gallatin 		sc->tx.boundary = 4096;
603b2fc195eSAndrew Gallatin 	} else {
6046d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
605b2fc195eSAndrew Gallatin 		sc->tx.boundary = 2048;
606b2fc195eSAndrew Gallatin 	}
6078fe615baSAndrew Gallatin 	return (mxge_load_firmware(sc));
608b2fc195eSAndrew Gallatin }
609b2fc195eSAndrew Gallatin 
610b2fc195eSAndrew Gallatin union qualhack
611b2fc195eSAndrew Gallatin {
612b2fc195eSAndrew Gallatin         const char *ro_char;
613b2fc195eSAndrew Gallatin         char *rw_char;
614b2fc195eSAndrew Gallatin };
615b2fc195eSAndrew Gallatin 
6164da0d523SAndrew Gallatin static int
6174da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6184da0d523SAndrew Gallatin {
619b824b7d8SAndrew Gallatin 
6204da0d523SAndrew Gallatin 
6214da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6224da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6234da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6244da0d523SAndrew Gallatin 		return EIO;
6254da0d523SAndrew Gallatin 	}
6264da0d523SAndrew Gallatin 
6274da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6284da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6294da0d523SAndrew Gallatin 	if (mxge_verbose)
6304da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6314da0d523SAndrew Gallatin 
632b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
633b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6344da0d523SAndrew Gallatin 
635b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
636b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6374da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6384da0d523SAndrew Gallatin 			      sc->fw_version);
6394da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6404da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6414da0d523SAndrew Gallatin 		return EINVAL;
6424da0d523SAndrew Gallatin 	}
6434da0d523SAndrew Gallatin 	return 0;
6444da0d523SAndrew Gallatin 
6454da0d523SAndrew Gallatin }
646b2fc195eSAndrew Gallatin 
647b2fc195eSAndrew Gallatin static int
6486d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
649b2fc195eSAndrew Gallatin {
65033d54970SLuigi Rizzo 	const struct firmware *fw;
651b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
652b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
653b2fc195eSAndrew Gallatin 	const char *fw_data;
654b2fc195eSAndrew Gallatin 	union qualhack hack;
655b2fc195eSAndrew Gallatin 	int status;
6564da0d523SAndrew Gallatin 	unsigned int i;
6574da0d523SAndrew Gallatin 	char dummy;
658b2fc195eSAndrew Gallatin 
659b2fc195eSAndrew Gallatin 
660b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
661b2fc195eSAndrew Gallatin 
662b2fc195eSAndrew Gallatin 	if (fw == NULL) {
663b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
664b2fc195eSAndrew Gallatin 			      sc->fw_name);
665b2fc195eSAndrew Gallatin 		return ENOENT;
666b2fc195eSAndrew Gallatin 	}
667b2fc195eSAndrew Gallatin 	if (fw->datasize > *limit ||
668b2fc195eSAndrew Gallatin 	    fw->datasize < MCP_HEADER_PTR_OFFSET + 4) {
669b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n",
670b2fc195eSAndrew Gallatin 			      sc->fw_name, (int)fw->datasize, (int) *limit);
671b2fc195eSAndrew Gallatin 		status = ENOSPC;
672b2fc195eSAndrew Gallatin 		goto abort_with_fw;
673b2fc195eSAndrew Gallatin 	}
674b2fc195eSAndrew Gallatin 	*limit = fw->datasize;
675b2fc195eSAndrew Gallatin 
676b2fc195eSAndrew Gallatin 	/* check id */
677b2fc195eSAndrew Gallatin 	fw_data = (const char *)fw->data;
678b2fc195eSAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
679b2fc195eSAndrew Gallatin 			     (fw_data + MCP_HEADER_PTR_OFFSET));
680b2fc195eSAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) {
681b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
682b2fc195eSAndrew Gallatin 		status = EIO;
683b2fc195eSAndrew Gallatin 		goto abort_with_fw;
684b2fc195eSAndrew Gallatin 	}
685b2fc195eSAndrew Gallatin 	hdr = (const void*)(fw_data + hdr_offset);
686b2fc195eSAndrew Gallatin 
6874da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
6884da0d523SAndrew Gallatin 	if (status != 0)
6894da0d523SAndrew Gallatin 		goto abort_with_fw;
690b2fc195eSAndrew Gallatin 
691b2fc195eSAndrew Gallatin 	hack.ro_char = fw_data;
692b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
6934da0d523SAndrew Gallatin 	for (i = 0; i < *limit; i += 256) {
6944da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
6954da0d523SAndrew Gallatin 			      hack.rw_char + i,
6964da0d523SAndrew Gallatin 			      min(256U, (unsigned)(*limit - i)));
6974da0d523SAndrew Gallatin 		mb();
6984da0d523SAndrew Gallatin 		dummy = *sc->sram;
6994da0d523SAndrew Gallatin 		mb();
7004da0d523SAndrew Gallatin 	}
701b2fc195eSAndrew Gallatin 
702b2fc195eSAndrew Gallatin 	status = 0;
703b2fc195eSAndrew Gallatin abort_with_fw:
704b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
705b2fc195eSAndrew Gallatin 	return status;
706b2fc195eSAndrew Gallatin }
707b2fc195eSAndrew Gallatin 
708b2fc195eSAndrew Gallatin /*
709b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
710b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
711b2fc195eSAndrew Gallatin  */
712b2fc195eSAndrew Gallatin 
713b2fc195eSAndrew Gallatin static void
7146d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
715b2fc195eSAndrew Gallatin {
716b2fc195eSAndrew Gallatin 	char buf_bytes[72];
717b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
718b2fc195eSAndrew Gallatin 	volatile char *submit;
719b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
720b2fc195eSAndrew Gallatin 	int i;
721b2fc195eSAndrew Gallatin 
722b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
723b2fc195eSAndrew Gallatin 
724b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
725b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
726b2fc195eSAndrew Gallatin 	*confirm = 0;
727b2fc195eSAndrew Gallatin 	mb();
728b2fc195eSAndrew Gallatin 
729b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
730b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
731b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
732b2fc195eSAndrew Gallatin 	*/
733b2fc195eSAndrew Gallatin 
7346d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7356d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
736b2fc195eSAndrew Gallatin 	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 */
7396d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7406d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
741b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
742b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
743b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
744b2fc195eSAndrew Gallatin 
745b2fc195eSAndrew Gallatin 
7460fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
747b2fc195eSAndrew Gallatin 
7486d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
749b2fc195eSAndrew Gallatin 	mb();
750b2fc195eSAndrew Gallatin 	DELAY(1000);
751b2fc195eSAndrew Gallatin 	mb();
752b2fc195eSAndrew Gallatin 	i = 0;
753b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
754b2fc195eSAndrew Gallatin 		DELAY(1000);
755b2fc195eSAndrew Gallatin 		i++;
756b2fc195eSAndrew Gallatin 	}
757b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
758b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
759b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
760b2fc195eSAndrew Gallatin 			      *confirm);
761b2fc195eSAndrew Gallatin 	}
762b2fc195eSAndrew Gallatin 	return;
763b2fc195eSAndrew Gallatin }
764b2fc195eSAndrew Gallatin 
765b2fc195eSAndrew Gallatin static int
7666d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
767b2fc195eSAndrew Gallatin {
768b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
769b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
770b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
7710fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
772b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
773e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
774b2fc195eSAndrew Gallatin 
775b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
776b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
777b2fc195eSAndrew Gallatin 
778b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
779b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
780b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
781b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
7826d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7836d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
784b2fc195eSAndrew Gallatin 
785b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
786b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
787a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
788b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
789b2fc195eSAndrew Gallatin 	mb();
7906d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
791b2fc195eSAndrew Gallatin 
7925e7d8541SAndrew Gallatin 	/* wait up to 20ms */
793e0501fd0SAndrew Gallatin 	err = EAGAIN;
7945e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
795b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
796b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
797b2fc195eSAndrew Gallatin 		mb();
798e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
799e0501fd0SAndrew Gallatin 		case 0:
800b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
801e0501fd0SAndrew Gallatin 			err = 0;
802e0501fd0SAndrew Gallatin 			break;
803e0501fd0SAndrew Gallatin 		case 0xffffffff:
804e0501fd0SAndrew Gallatin 			DELAY(1000);
805e0501fd0SAndrew Gallatin 			break;
806e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
807e0501fd0SAndrew Gallatin 			err = ENOSYS;
808e0501fd0SAndrew Gallatin 			break;
809e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
810e0501fd0SAndrew Gallatin 			err = E2BIG;
811e0501fd0SAndrew Gallatin 			break;
812e0501fd0SAndrew Gallatin 		default:
813b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8146d87a65dSAndrew Gallatin 				      "mxge: command %d "
815b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
816b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
817e0501fd0SAndrew Gallatin 			err = ENXIO;
818e0501fd0SAndrew Gallatin 			break;
819b2fc195eSAndrew Gallatin 		}
820e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
821e0501fd0SAndrew Gallatin 			break;
822b2fc195eSAndrew Gallatin 	}
823e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8246d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
825b2fc195eSAndrew Gallatin 			      "result = %d\n",
826b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
827e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
828e0501fd0SAndrew Gallatin 	return err;
829b2fc195eSAndrew Gallatin }
830b2fc195eSAndrew Gallatin 
8314da0d523SAndrew Gallatin static int
8324da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8334da0d523SAndrew Gallatin {
8344da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8354da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8364da0d523SAndrew Gallatin 	size_t hdr_offset;
8374da0d523SAndrew Gallatin 	int status;
8384da0d523SAndrew Gallatin 
8394da0d523SAndrew Gallatin 	/* find running firmware header */
8404da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
8414da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
8424da0d523SAndrew Gallatin 
8434da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8444da0d523SAndrew Gallatin 		device_printf(sc->dev,
8454da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
8464da0d523SAndrew Gallatin 			      (int)hdr_offset);
8474da0d523SAndrew Gallatin 		return EIO;
8484da0d523SAndrew Gallatin 	}
8494da0d523SAndrew Gallatin 
8504da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
8514da0d523SAndrew Gallatin 	 * validate firmware */
8524da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
8534da0d523SAndrew Gallatin 	if (hdr == NULL) {
8544da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
8554da0d523SAndrew Gallatin 		return ENOMEM;
8564da0d523SAndrew Gallatin 	}
8574da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8584da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
8594da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
8604da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
8614da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
862b824b7d8SAndrew Gallatin 
863b824b7d8SAndrew Gallatin 	/*
864b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
865b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
866b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
867b824b7d8SAndrew Gallatin 	 */
868b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
869b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
870b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
871b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
872b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
873b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
874b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
875b824b7d8SAndrew Gallatin 	}
876b824b7d8SAndrew Gallatin 
8774da0d523SAndrew Gallatin 	return status;
8784da0d523SAndrew Gallatin }
8794da0d523SAndrew Gallatin 
880b2fc195eSAndrew Gallatin 
881b2fc195eSAndrew Gallatin static int
8826d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc)
883b2fc195eSAndrew Gallatin {
884b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
885b2fc195eSAndrew Gallatin 	volatile char *submit;
886b2fc195eSAndrew Gallatin 	char buf_bytes[72];
887b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
888b2fc195eSAndrew Gallatin 	int status, i;
889b2fc195eSAndrew Gallatin 
890b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
891b2fc195eSAndrew Gallatin 
892b2fc195eSAndrew Gallatin 	size = sc->sram_size;
8936d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
894b2fc195eSAndrew Gallatin 	if (status) {
8954da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
8964da0d523SAndrew Gallatin 		   it is new enough */
8974da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
8984da0d523SAndrew Gallatin 		if (status) {
8994da0d523SAndrew Gallatin 			device_printf(sc->dev,
9004da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
901b2fc195eSAndrew Gallatin 			return status;
902b2fc195eSAndrew Gallatin 		}
9034da0d523SAndrew Gallatin 		device_printf(sc->dev,
9044da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9054da0d523SAndrew Gallatin 		if (sc->tx.boundary == 4096) {
9064da0d523SAndrew Gallatin 			device_printf(sc->dev,
9074da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9084da0d523SAndrew Gallatin 				 ".  For optimal\n");
9094da0d523SAndrew Gallatin 			device_printf(sc->dev,
9104da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9114da0d523SAndrew Gallatin 				 "firmware\n");
9124da0d523SAndrew Gallatin 		}
913d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
914d91b1b49SAndrew Gallatin 		sc->tx.boundary = 2048;
915d91b1b49SAndrew Gallatin 		return 0;
9164da0d523SAndrew Gallatin 	}
917b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
918b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
919b2fc195eSAndrew Gallatin 	*confirm = 0;
920b2fc195eSAndrew Gallatin 	mb();
921b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
922b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
923b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
924b2fc195eSAndrew Gallatin 	*/
925b2fc195eSAndrew Gallatin 
9266d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9276d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
928b2fc195eSAndrew Gallatin 
929b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
930b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
931b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
932b2fc195eSAndrew Gallatin 
933b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
934b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
935b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
936b2fc195eSAndrew Gallatin 	*/
937b2fc195eSAndrew Gallatin 					/* where the code starts*/
9386d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
939b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
940b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
941b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
942b2fc195eSAndrew Gallatin 
9430fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9446d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
945b2fc195eSAndrew Gallatin 	mb();
946b2fc195eSAndrew Gallatin 	DELAY(1000);
947b2fc195eSAndrew Gallatin 	mb();
948b2fc195eSAndrew Gallatin 	i = 0;
949b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
950b2fc195eSAndrew Gallatin 		DELAY(1000*10);
951b2fc195eSAndrew Gallatin 		i++;
952b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
953b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
954b2fc195eSAndrew Gallatin 	}
955b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
956b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
957b2fc195eSAndrew Gallatin 			confirm, *confirm);
958b2fc195eSAndrew Gallatin 
959b2fc195eSAndrew Gallatin 		return ENXIO;
960b2fc195eSAndrew Gallatin 	}
961b2fc195eSAndrew Gallatin 	return 0;
962b2fc195eSAndrew Gallatin }
963b2fc195eSAndrew Gallatin 
964b2fc195eSAndrew Gallatin static int
9656d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
966b2fc195eSAndrew Gallatin {
9676d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
968b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
969b2fc195eSAndrew Gallatin 	int status;
970b2fc195eSAndrew Gallatin 
971b2fc195eSAndrew Gallatin 
972b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
973b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
974b2fc195eSAndrew Gallatin 
975b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
976b2fc195eSAndrew Gallatin 
9775e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
978b2fc195eSAndrew Gallatin 	return status;
979b2fc195eSAndrew Gallatin }
980b2fc195eSAndrew Gallatin 
981b2fc195eSAndrew Gallatin static int
9826d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
983b2fc195eSAndrew Gallatin {
9846d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
985b2fc195eSAndrew Gallatin 	int status;
986b2fc195eSAndrew Gallatin 
987b2fc195eSAndrew Gallatin 	if (pause)
9885e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
989b2fc195eSAndrew Gallatin 				       &cmd);
990b2fc195eSAndrew Gallatin 	else
9915e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
992b2fc195eSAndrew Gallatin 				       &cmd);
993b2fc195eSAndrew Gallatin 
994b2fc195eSAndrew Gallatin 	if (status) {
995b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
996b2fc195eSAndrew Gallatin 		return ENXIO;
997b2fc195eSAndrew Gallatin 	}
998b2fc195eSAndrew Gallatin 	sc->pause = pause;
999b2fc195eSAndrew Gallatin 	return 0;
1000b2fc195eSAndrew Gallatin }
1001b2fc195eSAndrew Gallatin 
1002b2fc195eSAndrew Gallatin static void
10036d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1004b2fc195eSAndrew Gallatin {
10056d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1006b2fc195eSAndrew Gallatin 	int status;
1007b2fc195eSAndrew Gallatin 
1008b2fc195eSAndrew Gallatin 	if (promisc)
10095e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1010b2fc195eSAndrew Gallatin 				       &cmd);
1011b2fc195eSAndrew Gallatin 	else
10125e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1013b2fc195eSAndrew Gallatin 				       &cmd);
1014b2fc195eSAndrew Gallatin 
1015b2fc195eSAndrew Gallatin 	if (status) {
1016b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1017b2fc195eSAndrew Gallatin 	}
1018b2fc195eSAndrew Gallatin }
1019b2fc195eSAndrew Gallatin 
10200fa7f681SAndrew Gallatin static void
10210fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10220fa7f681SAndrew Gallatin {
10230fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
10240fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
10250fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
10260fa7f681SAndrew Gallatin 	int err;
10270fa7f681SAndrew Gallatin 
10280fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
10290fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
10300fa7f681SAndrew Gallatin 		return;
10310fa7f681SAndrew Gallatin 
10320fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
10330fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10340fa7f681SAndrew Gallatin 	if (err != 0) {
10350fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
10360fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
10370fa7f681SAndrew Gallatin 		return;
10380fa7f681SAndrew Gallatin 	}
10390fa7f681SAndrew Gallatin 
1040b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1041b824b7d8SAndrew Gallatin 		return;
10420fa7f681SAndrew Gallatin 
10430fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
10440fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
10450fa7f681SAndrew Gallatin 		return;
10460fa7f681SAndrew Gallatin 
10470fa7f681SAndrew Gallatin 	/* Flush all the filters */
10480fa7f681SAndrew Gallatin 
10490fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10500fa7f681SAndrew Gallatin 	if (err != 0) {
10510fa7f681SAndrew Gallatin 		device_printf(sc->dev,
10520fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
10530fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
10540fa7f681SAndrew Gallatin 		return;
10550fa7f681SAndrew Gallatin 	}
10560fa7f681SAndrew Gallatin 
10570fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
10580fa7f681SAndrew Gallatin 
10590fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
10600fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10610fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
10620fa7f681SAndrew Gallatin 			continue;
10630fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10640fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
10650fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10660fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
10670fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
10680fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
10690fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10700fa7f681SAndrew Gallatin 		if (err != 0) {
10710fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
10720fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
10730fa7f681SAndrew Gallatin 			       "%d\t", err);
10740fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
10750fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
10760fa7f681SAndrew Gallatin 			return;
10770fa7f681SAndrew Gallatin 		}
10780fa7f681SAndrew Gallatin 	}
10790fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
10800fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
10810fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10820fa7f681SAndrew Gallatin 	if (err != 0) {
10830fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
10840fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
10850fa7f681SAndrew Gallatin 	}
10860fa7f681SAndrew Gallatin }
10870fa7f681SAndrew Gallatin 
1088b2fc195eSAndrew Gallatin static int
1089053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1090053e637fSAndrew Gallatin {
1091053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1092053e637fSAndrew Gallatin 	int status;
1093053e637fSAndrew Gallatin 
1094c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1095c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1096053e637fSAndrew Gallatin 
1097053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1098053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1099053e637fSAndrew Gallatin 	cmd.data0 = 0;
1100053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1101053e637fSAndrew Gallatin 			       &cmd);
1102053e637fSAndrew Gallatin 	if (status == 0)
1103c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1104053e637fSAndrew Gallatin 
1105053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1106053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1107053e637fSAndrew Gallatin }
1108053e637fSAndrew Gallatin 
1109053e637fSAndrew Gallatin static int
1110adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1111b2fc195eSAndrew Gallatin {
1112b2fc195eSAndrew Gallatin 
11136d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11145e7d8541SAndrew Gallatin 	size_t bytes;
11155e7d8541SAndrew Gallatin 	int status;
1116b2fc195eSAndrew Gallatin 
1117b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1118b2fc195eSAndrew Gallatin 	   is alive */
1119b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11205e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1121b2fc195eSAndrew Gallatin 	if (status != 0) {
1122b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1123b2fc195eSAndrew Gallatin 		return ENXIO;
1124b2fc195eSAndrew Gallatin 	}
1125b2fc195eSAndrew Gallatin 
1126091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1127091feecdSAndrew Gallatin 
1128adae7080SAndrew Gallatin 	if (interrupts_setup) {
1129b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
1130adae7080SAndrew Gallatin 		bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry);
11315e7d8541SAndrew Gallatin 		memset(sc->rx_done.entry, 0, bytes);
11325e7d8541SAndrew Gallatin 		cmd.data0 = (uint32_t)bytes;
11335e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11345e7d8541SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr);
11355e7d8541SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr);
11365e7d8541SAndrew Gallatin 		status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd);
1137adae7080SAndrew Gallatin 	}
1138b2fc195eSAndrew Gallatin 
11396d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
11405e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
11415e7d8541SAndrew Gallatin 
11425e7d8541SAndrew Gallatin 
11435e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
11445e7d8541SAndrew Gallatin 
11455e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
11465e7d8541SAndrew Gallatin 	sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
11475e7d8541SAndrew Gallatin 
11485e7d8541SAndrew Gallatin 
11495e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
11506d87a65dSAndrew Gallatin 				&cmd);
11515e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1152b2fc195eSAndrew Gallatin 	if (status != 0) {
1153b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1154b2fc195eSAndrew Gallatin 		return status;
1155b2fc195eSAndrew Gallatin 	}
1156b2fc195eSAndrew Gallatin 
11575e7d8541SAndrew Gallatin 
11585e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
11595e7d8541SAndrew Gallatin 
11605e7d8541SAndrew Gallatin 
11615e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
11628fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
11635e7d8541SAndrew Gallatin 
1164b2fc195eSAndrew Gallatin 	/* reset mcp/driver shared state back to 0 */
11655e7d8541SAndrew Gallatin 	sc->rx_done.idx = 0;
11665e7d8541SAndrew Gallatin 	sc->rx_done.cnt = 0;
1167b2fc195eSAndrew Gallatin 	sc->tx.req = 0;
1168b2fc195eSAndrew Gallatin 	sc->tx.done = 0;
11695e7d8541SAndrew Gallatin 	sc->tx.pkt_done = 0;
1170a82c2581SAndrew Gallatin 	sc->tx.wake = 0;
1171adae7080SAndrew Gallatin 	sc->tx_defrag = 0;
1172a82c2581SAndrew Gallatin 	sc->tx.stall = 0;
1173b2fc195eSAndrew Gallatin 	sc->rx_big.cnt = 0;
1174b2fc195eSAndrew Gallatin 	sc->rx_small.cnt = 0;
1175b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
1176a98d6cd7SAndrew Gallatin 	sc->fw_stats->valid = 0;
1177a98d6cd7SAndrew Gallatin 	sc->fw_stats->send_done_count = 0;
1178053e637fSAndrew Gallatin 	sc->lro_bad_csum = 0;
1179053e637fSAndrew Gallatin 	sc->lro_queued = 0;
1180053e637fSAndrew Gallatin 	sc->lro_flushed = 0;
11816d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
11826d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
11836d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
11840fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1185b2fc195eSAndrew Gallatin 	return status;
1186b2fc195eSAndrew Gallatin }
1187b2fc195eSAndrew Gallatin 
1188b2fc195eSAndrew Gallatin static int
11896d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1190b2fc195eSAndrew Gallatin {
11916d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1192b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1193b2fc195eSAndrew Gallatin         int err;
1194b2fc195eSAndrew Gallatin 
1195b2fc195eSAndrew Gallatin         sc = arg1;
1196b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1197b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1198b2fc195eSAndrew Gallatin         if (err != 0) {
1199b2fc195eSAndrew Gallatin                 return err;
1200b2fc195eSAndrew Gallatin         }
1201b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1202b2fc195eSAndrew Gallatin                 return 0;
1203b2fc195eSAndrew Gallatin 
1204b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1205b2fc195eSAndrew Gallatin                 return EINVAL;
1206b2fc195eSAndrew Gallatin 
1207a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
12085e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1209b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
12105e7d8541SAndrew Gallatin 
1211a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1212b2fc195eSAndrew Gallatin         return err;
1213b2fc195eSAndrew Gallatin }
1214b2fc195eSAndrew Gallatin 
1215b2fc195eSAndrew Gallatin static int
12166d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1217b2fc195eSAndrew Gallatin {
12186d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1219b2fc195eSAndrew Gallatin         unsigned int enabled;
1220b2fc195eSAndrew Gallatin         int err;
1221b2fc195eSAndrew Gallatin 
1222b2fc195eSAndrew Gallatin         sc = arg1;
1223b2fc195eSAndrew Gallatin         enabled = sc->pause;
1224b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1225b2fc195eSAndrew Gallatin         if (err != 0) {
1226b2fc195eSAndrew Gallatin                 return err;
1227b2fc195eSAndrew Gallatin         }
1228b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1229b2fc195eSAndrew Gallatin                 return 0;
1230b2fc195eSAndrew Gallatin 
1231a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
12326d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1233a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1234b2fc195eSAndrew Gallatin         return err;
1235b2fc195eSAndrew Gallatin }
1236b2fc195eSAndrew Gallatin 
1237b2fc195eSAndrew Gallatin static int
1238f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1239f04b33f8SAndrew Gallatin {
1240f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1241f04b33f8SAndrew Gallatin 	int err;
1242f04b33f8SAndrew Gallatin 
1243f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1244f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1245f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1246f04b33f8SAndrew Gallatin 	else
1247f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1248f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1249f04b33f8SAndrew Gallatin 	callout_stop(&sc->co_hdl);
1250f04b33f8SAndrew Gallatin 	mxge_close(sc);
1251f04b33f8SAndrew Gallatin 	err = mxge_open(sc);
1252f04b33f8SAndrew Gallatin 	if (err == 0)
1253f04b33f8SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
1254f04b33f8SAndrew Gallatin 	return err;
1255f04b33f8SAndrew Gallatin }
1256f04b33f8SAndrew Gallatin 
1257f04b33f8SAndrew Gallatin static int
1258276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1259276edd10SAndrew Gallatin {
1260276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1261276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1262276edd10SAndrew Gallatin 	int err;
1263276edd10SAndrew Gallatin 
1264276edd10SAndrew Gallatin 	sc = arg1;
1265276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1266276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1267276edd10SAndrew Gallatin 	if (err != 0)
1268276edd10SAndrew Gallatin 		return err;
1269276edd10SAndrew Gallatin 
1270276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1271276edd10SAndrew Gallatin 		return 0;
1272276edd10SAndrew Gallatin 
1273276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1274276edd10SAndrew Gallatin 		return EINVAL;
1275276edd10SAndrew Gallatin 
1276276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1277f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1278276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1279276edd10SAndrew Gallatin 	return err;
1280276edd10SAndrew Gallatin }
1281276edd10SAndrew Gallatin 
1282276edd10SAndrew Gallatin static int
12836d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1284b2fc195eSAndrew Gallatin {
1285b2fc195eSAndrew Gallatin         int err;
1286b2fc195eSAndrew Gallatin 
1287b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1288b2fc195eSAndrew Gallatin                 return EFAULT;
1289b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1290b2fc195eSAndrew Gallatin         arg1 = NULL;
1291b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1292b2fc195eSAndrew Gallatin 
1293b2fc195eSAndrew Gallatin         return err;
1294b2fc195eSAndrew Gallatin }
1295b2fc195eSAndrew Gallatin 
1296b2fc195eSAndrew Gallatin static void
12976d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1298b2fc195eSAndrew Gallatin {
1299b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1300b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
13015e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
1302b2fc195eSAndrew Gallatin 
1303b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1304b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
1305b2fc195eSAndrew Gallatin 	fw = sc->fw_stats;
1306b2fc195eSAndrew Gallatin 
13075e7d8541SAndrew Gallatin 	/* random information */
13085e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
13095e7d8541SAndrew Gallatin 		       "firmware_version",
13105e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
13115e7d8541SAndrew Gallatin 		       0, "firmware version");
13125e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
13135e7d8541SAndrew Gallatin 		       "serial_number",
13145e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
13155e7d8541SAndrew Gallatin 		       0, "serial number");
13165e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
13175e7d8541SAndrew Gallatin 		       "product_code",
13185e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
13195e7d8541SAndrew Gallatin 		       0, "product_code");
13205e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1321d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1322d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1323d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1324d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13255e7d8541SAndrew Gallatin 		       "tx_boundary",
13265e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.boundary,
13275e7d8541SAndrew Gallatin 		       0, "tx_boundary");
13285e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1329091feecdSAndrew Gallatin 		       "write_combine",
1330091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1331091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1332091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13335e7d8541SAndrew Gallatin 		       "read_dma_MBs",
13345e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
13355e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
13365e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13375e7d8541SAndrew Gallatin 		       "write_dma_MBs",
13385e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
13395e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
13405e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13415e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
13425e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
13435e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
13445e7d8541SAndrew Gallatin 
13455e7d8541SAndrew Gallatin 
13465e7d8541SAndrew Gallatin 	/* performance related tunables */
1347b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1348b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1349b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
13506d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1351b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1352b2fc195eSAndrew Gallatin 
1353b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1354b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1355b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
13566d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1357b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1358b2fc195eSAndrew Gallatin 
1359b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13605e7d8541SAndrew Gallatin 		       "deassert_wait",
13615e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
13625e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1363b2fc195eSAndrew Gallatin 
1364b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1365b2fc195eSAndrew Gallatin 	   Need to swap it */
1366b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1367b2fc195eSAndrew Gallatin 			"link_up",
1368b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
13696d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1370b2fc195eSAndrew Gallatin 			"I", "link up");
1371b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1372b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1373b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
13746d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1375b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1376b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1377adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1378adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1379adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
13806d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1381adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1382adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1383adae7080SAndrew Gallatin 			"dropped_bad_phy",
1384adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1385adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1386adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1387adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1388b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1389b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1390b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1391b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
13926d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1393b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1394b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1395adae7080SAndrew Gallatin 			"dropped_link_overflow",
1396adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1397adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1398adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1399adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14000fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
14010fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
14020fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
14030fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
14040fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
14050fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1406adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1407adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
14086d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1409adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1410b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1411b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1412b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1413b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
14146d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1415b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1416b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1417adae7080SAndrew Gallatin 			"dropped_overrun",
1418adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
14196d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1420adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1421adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1422adae7080SAndrew Gallatin 			"dropped_pause",
1423adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1424adae7080SAndrew Gallatin 			&fw->dropped_pause,
1425adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1426adae7080SAndrew Gallatin 			"I", "dropped_pause");
1427adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1428adae7080SAndrew Gallatin 			"dropped_runt",
1429adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1430adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1431adae7080SAndrew Gallatin 			"I", "dropped_runt");
1432b2fc195eSAndrew Gallatin 
1433a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1434a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1435a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1436a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1437a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1438a0394e33SAndrew Gallatin 
1439b2fc195eSAndrew Gallatin 	/* host counters exported for debugging */
1440b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14415e7d8541SAndrew Gallatin 		       "rx_small_cnt",
14425e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_small.cnt,
14435e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
14445e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14455e7d8541SAndrew Gallatin 		       "rx_big_cnt",
14465e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_big.cnt,
14475e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
14485e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1449b2fc195eSAndrew Gallatin 		       "tx_req",
1450b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.req,
1451b2fc195eSAndrew Gallatin 		       0, "tx_req");
1452b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1453b2fc195eSAndrew Gallatin 		       "tx_done",
1454b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.done,
1455b2fc195eSAndrew Gallatin 		       0, "tx_done");
1456b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14575e7d8541SAndrew Gallatin 		       "tx_pkt_done",
14585e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.pkt_done,
14595e7d8541SAndrew Gallatin 		       0, "tx_done");
1460a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1461a82c2581SAndrew Gallatin 		       "tx_stall",
1462a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.stall,
1463a82c2581SAndrew Gallatin 		       0, "tx_stall");
1464a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1465a82c2581SAndrew Gallatin 		       "tx_wake",
1466a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.wake,
1467a82c2581SAndrew Gallatin 		       0, "tx_wake");
1468adae7080SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1469adae7080SAndrew Gallatin 		       "tx_defrag",
1470adae7080SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_defrag,
1471adae7080SAndrew Gallatin 		       0, "tx_defrag");
14725e7d8541SAndrew Gallatin 
14735e7d8541SAndrew Gallatin 	/* verbose printing? */
1474b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14755e7d8541SAndrew Gallatin 		       "verbose",
14765e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
14775e7d8541SAndrew Gallatin 		       0, "verbose printing");
1478b2fc195eSAndrew Gallatin 
1479053e637fSAndrew Gallatin 	/* lro */
1480276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1481276edd10SAndrew Gallatin 			"lro_cnt",
1482276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1483276edd10SAndrew Gallatin 			0, mxge_change_lro,
1484276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1485053e637fSAndrew Gallatin 
1486053e637fSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1487053e637fSAndrew Gallatin 		       "lro_flushed", CTLFLAG_RD, &sc->lro_flushed,
1488053e637fSAndrew Gallatin 		       0, "number of lro merge queues flushed");
1489053e637fSAndrew Gallatin 
1490053e637fSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1491053e637fSAndrew Gallatin 		       "lro_queued", CTLFLAG_RD, &sc->lro_queued,
1492053e637fSAndrew Gallatin 		       0, "number of frames appended to lro merge queues");
1493053e637fSAndrew Gallatin 
1494b2fc195eSAndrew Gallatin }
1495b2fc195eSAndrew Gallatin 
1496b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1497b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1498b2fc195eSAndrew Gallatin 
1499b2fc195eSAndrew Gallatin static inline void
15006d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx,
1501b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1502b2fc195eSAndrew Gallatin {
1503b2fc195eSAndrew Gallatin         int idx, starting_slot;
1504b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1505b2fc195eSAndrew Gallatin         while (cnt > 1) {
1506b2fc195eSAndrew Gallatin                 cnt--;
1507b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
15086d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1509b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1510b2fc195eSAndrew Gallatin                 mb();
1511b2fc195eSAndrew Gallatin         }
1512b2fc195eSAndrew Gallatin }
1513b2fc195eSAndrew Gallatin 
1514b2fc195eSAndrew Gallatin /*
1515b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1516b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1517b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1518b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1519b2fc195eSAndrew Gallatin  */
1520b2fc195eSAndrew Gallatin 
1521b2fc195eSAndrew Gallatin static inline void
15226d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src,
1523b2fc195eSAndrew Gallatin                   int cnt)
1524b2fc195eSAndrew Gallatin {
1525b2fc195eSAndrew Gallatin         int idx, i;
1526b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1527b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1528b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1529b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15305e7d8541SAndrew Gallatin 	uint8_t last_flags;
1531b2fc195eSAndrew Gallatin 
1532b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1533b2fc195eSAndrew Gallatin 
15345e7d8541SAndrew Gallatin 	last_flags = src->flags;
15355e7d8541SAndrew Gallatin 	src->flags = 0;
1536b2fc195eSAndrew Gallatin         mb();
1537b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1538b2fc195eSAndrew Gallatin         srcp = src;
1539b2fc195eSAndrew Gallatin 
1540b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1541b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
15426d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1543b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1544b2fc195eSAndrew Gallatin                         srcp += 2;
1545b2fc195eSAndrew Gallatin                         dstp += 2;
1546b2fc195eSAndrew Gallatin                 }
1547b2fc195eSAndrew Gallatin         } else {
1548b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1549b2fc195eSAndrew Gallatin                    that it is submitted below */
15506d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1551b2fc195eSAndrew Gallatin                 i = 0;
1552b2fc195eSAndrew Gallatin         }
1553b2fc195eSAndrew Gallatin         if (i < cnt) {
1554b2fc195eSAndrew Gallatin                 /* submit the first request */
15556d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1556b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1557b2fc195eSAndrew Gallatin         }
1558b2fc195eSAndrew Gallatin 
1559b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
15605e7d8541SAndrew Gallatin         src->flags = last_flags;
1561b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1562b2fc195eSAndrew Gallatin         src_ints+=3;
1563b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1564b2fc195eSAndrew Gallatin         dst_ints+=3;
1565b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1566b2fc195eSAndrew Gallatin         tx->req += cnt;
1567b2fc195eSAndrew Gallatin         mb();
1568b2fc195eSAndrew Gallatin }
1569b2fc195eSAndrew Gallatin 
1570b2fc195eSAndrew Gallatin static void
1571c792928fSAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt,
1572c792928fSAndrew Gallatin 	       int ip_off)
1573aed8e389SAndrew Gallatin {
1574aed8e389SAndrew Gallatin 	mxge_tx_buf_t *tx;
1575aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1576aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1577aed8e389SAndrew Gallatin 	struct ip *ip;
1578aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1579aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1580aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1581aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1582aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1583aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1584aed8e389SAndrew Gallatin 	static int once;
1585aed8e389SAndrew Gallatin 
1586aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1587aed8e389SAndrew Gallatin 
1588aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1589aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1590aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1591aed8e389SAndrew Gallatin 	 */
1592aed8e389SAndrew Gallatin 
1593aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1594aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1595aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1596c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1597c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
1598aed8e389SAndrew Gallatin 			   sc->scratch);
1599c792928fSAndrew Gallatin 		ip = (struct ip *)(sc->scratch + ip_off);
1600aed8e389SAndrew Gallatin 	} else {
1601c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1602aed8e389SAndrew Gallatin 	}
1603c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1604aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1605c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
1606aed8e389SAndrew Gallatin 			   + sizeof (*tcp),  sc->scratch);
1607c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1608aed8e389SAndrew Gallatin 	}
1609aed8e389SAndrew Gallatin 
1610aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1611c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1612aed8e389SAndrew Gallatin 
1613aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1614c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1615aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1616aed8e389SAndrew Gallatin 
1617aed8e389SAndrew Gallatin 
1618aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1619aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1620aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1621aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1622aed8e389SAndrew Gallatin 
1623aed8e389SAndrew Gallatin 	tx = &sc->tx;
1624aed8e389SAndrew Gallatin 	req = tx->req_list;
1625aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1626aed8e389SAndrew Gallatin 	cnt = 0;
1627aed8e389SAndrew Gallatin 	rdma_count = 0;
1628aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1629aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1630aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1631aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1632aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1633aed8e389SAndrew Gallatin 	 *
1634aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1635aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1636aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1637aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1638aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1639aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1640aed8e389SAndrew Gallatin 	 *
1641aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1642aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1643aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1644aed8e389SAndrew Gallatin 	 */
1645aed8e389SAndrew Gallatin 
1646aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1647aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1648aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1649aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1650e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1651aed8e389SAndrew Gallatin 
1652aed8e389SAndrew Gallatin 		while (len) {
1653aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1654e39a0a37SAndrew Gallatin 			seglen = len;
1655aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1656aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1657aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1658aed8e389SAndrew Gallatin 				/* payload */
1659aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1660aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1661aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1662aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1663aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1664aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1665aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1666aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1667aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1668aed8e389SAndrew Gallatin 				/* header ends */
1669aed8e389SAndrew Gallatin 				rdma_count = -1;
1670aed8e389SAndrew Gallatin 				cum_len_next = 0;
1671aed8e389SAndrew Gallatin 				seglen = -cum_len;
1672aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1673aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1674aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1675aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1676aed8e389SAndrew Gallatin 			    }
1677aed8e389SAndrew Gallatin 
1678aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1679aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1680aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1681aed8e389SAndrew Gallatin 			req->pad = 0;
1682aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1683aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1684aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1685aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1686aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1687aed8e389SAndrew Gallatin 			low += seglen;
1688aed8e389SAndrew Gallatin 			len -= seglen;
1689aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1690aed8e389SAndrew Gallatin 			flags = flags_next;
1691aed8e389SAndrew Gallatin 			req++;
1692aed8e389SAndrew Gallatin 			cnt++;
1693aed8e389SAndrew Gallatin 			rdma_count++;
1694aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1695aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1696aed8e389SAndrew Gallatin 			else
1697aed8e389SAndrew Gallatin 				cksum_offset = 0;
1698adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1699aed8e389SAndrew Gallatin 				goto drop;
1700aed8e389SAndrew Gallatin 		}
1701aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1702aed8e389SAndrew Gallatin 		seg++;
1703aed8e389SAndrew Gallatin 	}
1704aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1705aed8e389SAndrew Gallatin 
1706aed8e389SAndrew Gallatin 	do {
1707aed8e389SAndrew Gallatin 		req--;
1708aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1709aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1710aed8e389SAndrew Gallatin 
1711aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1712aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1713aed8e389SAndrew Gallatin 	return;
1714aed8e389SAndrew Gallatin 
1715aed8e389SAndrew Gallatin drop:
1716e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1717aed8e389SAndrew Gallatin 	m_freem(m);
1718aed8e389SAndrew Gallatin 	sc->ifp->if_oerrors++;
1719aed8e389SAndrew Gallatin 	if (!once) {
1720adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1721adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1722adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1723aed8e389SAndrew Gallatin 		once = 1;
1724aed8e389SAndrew Gallatin 	}
1725aed8e389SAndrew Gallatin 	return;
1726aed8e389SAndrew Gallatin 
1727aed8e389SAndrew Gallatin }
1728aed8e389SAndrew Gallatin 
1729c792928fSAndrew Gallatin /*
1730c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1731c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1732c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1733c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1734c792928fSAndrew Gallatin  */
1735c792928fSAndrew Gallatin static struct mbuf *
1736c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1737c792928fSAndrew Gallatin {
1738c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1739c792928fSAndrew Gallatin 
1740c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1741c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1742c792928fSAndrew Gallatin 		return NULL;
1743c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1744c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1745c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1746c792928fSAndrew Gallatin 			return NULL;
1747c792928fSAndrew Gallatin 	}
1748c792928fSAndrew Gallatin 	/*
1749c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
1750c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
1751c792928fSAndrew Gallatin 	 */
1752c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
1753c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
1754c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
1755c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
1756c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
1757c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
1758c792928fSAndrew Gallatin 	return m;
1759c792928fSAndrew Gallatin }
1760c792928fSAndrew Gallatin 
1761aed8e389SAndrew Gallatin static void
17626d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m)
1763b2fc195eSAndrew Gallatin {
1764b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1765b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1766b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1767b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
17686d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1769b2fc195eSAndrew Gallatin 	struct ip *ip;
1770c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
1771aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1772aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1773b2fc195eSAndrew Gallatin 
1774b2fc195eSAndrew Gallatin 
1775b2fc195eSAndrew Gallatin 
1776b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1777b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1778b2fc195eSAndrew Gallatin 
1779c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
1780c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
1781c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
1782c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1783c792928fSAndrew Gallatin 			goto drop;
1784c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
1785c792928fSAndrew Gallatin 	}
1786c792928fSAndrew Gallatin 
1787b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1788b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1789b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1790aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1791b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1792adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
1793b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1794b2fc195eSAndrew Gallatin 		   to defrag */
1795b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1796b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1797b2fc195eSAndrew Gallatin 			goto drop;
1798b2fc195eSAndrew Gallatin 		}
1799adae7080SAndrew Gallatin 		sc->tx_defrag++;
1800b2fc195eSAndrew Gallatin 		m = m_tmp;
1801b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1802b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1803aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1804b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1805b2fc195eSAndrew Gallatin 	}
1806adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
1807aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1808aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1809b2fc195eSAndrew Gallatin 		goto drop;
1810b2fc195eSAndrew Gallatin 	}
1811b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1812b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
18135e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1814b2fc195eSAndrew Gallatin 
1815aed8e389SAndrew Gallatin 
1816aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1817aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
1818c792928fSAndrew Gallatin 		mxge_encap_tso(sc, m, cnt, ip_off);
1819aed8e389SAndrew Gallatin 		return;
1820aed8e389SAndrew Gallatin 	}
1821aed8e389SAndrew Gallatin 
1822b2fc195eSAndrew Gallatin 	req = tx->req_list;
1823b2fc195eSAndrew Gallatin 	cksum_offset = 0;
18245e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
18255e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1826b2fc195eSAndrew Gallatin 
1827b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1828b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1829aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
1830aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
1831c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1832c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
1833aed8e389SAndrew Gallatin 				   sc->scratch);
1834c792928fSAndrew Gallatin 			ip = (struct ip *)(sc->scratch + ip_off);
1835aed8e389SAndrew Gallatin 		} else {
1836c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
1837aed8e389SAndrew Gallatin 		}
1838c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
1839b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18405e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1841b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
18425e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1843aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
1844aed8e389SAndrew Gallatin 	} else {
1845aed8e389SAndrew Gallatin 		odd_flag = 0;
1846b2fc195eSAndrew Gallatin 	}
18475e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18485e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
1849b2fc195eSAndrew Gallatin 
1850b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
1851b2fc195eSAndrew Gallatin 	cum_len = 0;
1852aed8e389SAndrew Gallatin 	seg = tx->seg_list;
18535e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
1854b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1855b2fc195eSAndrew Gallatin 		req->addr_low =
18566d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1857b2fc195eSAndrew Gallatin 		req->addr_high =
18586d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1859b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
1860b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
1861b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
1862b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
1863b2fc195eSAndrew Gallatin 		else
1864b2fc195eSAndrew Gallatin 			cksum_offset = 0;
18655e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18665e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
18675e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1868aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1869b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
1870b2fc195eSAndrew Gallatin 		seg++;
1871b2fc195eSAndrew Gallatin 		req++;
1872b2fc195eSAndrew Gallatin 		req->flags = 0;
1873b2fc195eSAndrew Gallatin 	}
1874b2fc195eSAndrew Gallatin 	req--;
1875b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
1876b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
1877b2fc195eSAndrew Gallatin 		req++;
1878b2fc195eSAndrew Gallatin 		req->addr_low =
18796d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
1880b2fc195eSAndrew Gallatin 		req->addr_high =
18816d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
1882b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
18835e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
18845e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18855e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
18865e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1887aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1888b2fc195eSAndrew Gallatin 		cnt++;
1889b2fc195eSAndrew Gallatin 	}
18905e7d8541SAndrew Gallatin 
18915e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
18925e7d8541SAndrew Gallatin #if 0
18935e7d8541SAndrew Gallatin 	/* print what the firmware will see */
18945e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
18955e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
18965e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
18975e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
18985e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
18995e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
19005e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
19015e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
19025e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
19035e7d8541SAndrew Gallatin 	}
19045e7d8541SAndrew Gallatin 	printf("--------------\n");
19055e7d8541SAndrew Gallatin #endif
19065e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
19076d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1908b2fc195eSAndrew Gallatin 	return;
1909b2fc195eSAndrew Gallatin 
1910b2fc195eSAndrew Gallatin drop:
1911b2fc195eSAndrew Gallatin 	m_freem(m);
1912b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
1913b2fc195eSAndrew Gallatin 	return;
1914b2fc195eSAndrew Gallatin }
1915b2fc195eSAndrew Gallatin 
1916b2fc195eSAndrew Gallatin 
19176d914a32SAndrew Gallatin 
19186d914a32SAndrew Gallatin 
19196d914a32SAndrew Gallatin static inline void
19206d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc)
1921b2fc195eSAndrew Gallatin {
1922b2fc195eSAndrew Gallatin 	struct mbuf *m;
1923b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1924adae7080SAndrew Gallatin 	mxge_tx_buf_t *tx;
1925b2fc195eSAndrew Gallatin 
1926b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1927adae7080SAndrew Gallatin 	tx = &sc->tx;
1928adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
19296d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
19306d914a32SAndrew Gallatin 		if (m == NULL) {
19316d914a32SAndrew Gallatin 			return;
19326d914a32SAndrew Gallatin 		}
1933b2fc195eSAndrew Gallatin 		/* let BPF see it */
1934b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
1935b2fc195eSAndrew Gallatin 
1936b2fc195eSAndrew Gallatin 		/* give it to the nic */
19376d87a65dSAndrew Gallatin 		mxge_encap(sc, m);
19386d914a32SAndrew Gallatin 	}
19396d914a32SAndrew Gallatin 	/* ran out of transmit slots */
1940a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
1941b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1942adae7080SAndrew Gallatin 		tx->stall++;
1943a82c2581SAndrew Gallatin 	}
1944b2fc195eSAndrew Gallatin }
1945b2fc195eSAndrew Gallatin 
1946b2fc195eSAndrew Gallatin static void
19476d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
1948b2fc195eSAndrew Gallatin {
19496d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
1950b2fc195eSAndrew Gallatin 
1951b2fc195eSAndrew Gallatin 
1952a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->tx_mtx);
19536d87a65dSAndrew Gallatin 	mxge_start_locked(sc);
1954a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->tx_mtx);
1955b2fc195eSAndrew Gallatin }
1956b2fc195eSAndrew Gallatin 
19575e7d8541SAndrew Gallatin /*
19585e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
19595e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
19605e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
19615e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
19625e7d8541SAndrew Gallatin  * in a burst
19635e7d8541SAndrew Gallatin  */
19645e7d8541SAndrew Gallatin static inline void
19655e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
19665e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
19675e7d8541SAndrew Gallatin {
19685e7d8541SAndrew Gallatin 	uint32_t low;
19695e7d8541SAndrew Gallatin 
19705e7d8541SAndrew Gallatin 	low = src->addr_low;
19715e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
1972a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
1973a1480dfbSAndrew Gallatin 	mb();
1974a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
19755e7d8541SAndrew Gallatin 	mb();
197640385a5fSAndrew Gallatin 	src->addr_low = low;
19775e7d8541SAndrew Gallatin 	dst->addr_low = low;
19785e7d8541SAndrew Gallatin 	mb();
19795e7d8541SAndrew Gallatin }
19805e7d8541SAndrew Gallatin 
1981b2fc195eSAndrew Gallatin static int
19826d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1983b2fc195eSAndrew Gallatin {
1984b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1985b2fc195eSAndrew Gallatin 	struct mbuf *m;
19866d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_small;
1987b2fc195eSAndrew Gallatin 	int cnt, err;
1988b2fc195eSAndrew Gallatin 
1989b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
1990b2fc195eSAndrew Gallatin 	if (m == NULL) {
1991b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1992b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1993b2fc195eSAndrew Gallatin 		goto done;
1994b2fc195eSAndrew Gallatin 	}
1995b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
1996b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1997b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1998b2fc195eSAndrew Gallatin 	if (err != 0) {
1999b2fc195eSAndrew Gallatin 		m_free(m);
2000b2fc195eSAndrew Gallatin 		goto done;
2001b2fc195eSAndrew Gallatin 	}
2002b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2003b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
20046d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2005b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
20066d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2007b2fc195eSAndrew Gallatin 
2008b2fc195eSAndrew Gallatin done:
2009adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2010adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2011b2fc195eSAndrew Gallatin 	return err;
2012b2fc195eSAndrew Gallatin }
2013b2fc195eSAndrew Gallatin 
2014b2fc195eSAndrew Gallatin static int
20156d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx)
2016b2fc195eSAndrew Gallatin {
2017053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2018b2fc195eSAndrew Gallatin 	struct mbuf *m;
20196d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_big;
2020053e637fSAndrew Gallatin 	int cnt, err, i;
2021b2fc195eSAndrew Gallatin 
2022a0394e33SAndrew Gallatin 	if (rx->cl_size == MCLBYTES)
2023a0394e33SAndrew Gallatin 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2024a0394e33SAndrew Gallatin 	else
2025053e637fSAndrew Gallatin 		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2026b2fc195eSAndrew Gallatin 	if (m == NULL) {
2027b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2028b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2029b2fc195eSAndrew Gallatin 		goto done;
2030b2fc195eSAndrew Gallatin 	}
2031053e637fSAndrew Gallatin 	m->m_len = rx->cl_size;
2032b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2033053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2034b2fc195eSAndrew Gallatin 	if (err != 0) {
2035b2fc195eSAndrew Gallatin 		m_free(m);
2036b2fc195eSAndrew Gallatin 		goto done;
2037b2fc195eSAndrew Gallatin 	}
2038b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2039053e637fSAndrew Gallatin 
2040053e637fSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2041053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2042053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2043053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2044053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2045053e637fSAndrew Gallatin        }
2046053e637fSAndrew Gallatin 
2047b2fc195eSAndrew Gallatin 
2048b2fc195eSAndrew Gallatin done:
2049053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2050b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
20515e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
20525e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2053b2fc195eSAndrew Gallatin 		}
2054053e637fSAndrew Gallatin 		idx++;
2055053e637fSAndrew Gallatin 	}
2056b2fc195eSAndrew Gallatin 	return err;
2057b2fc195eSAndrew Gallatin }
2058b2fc195eSAndrew Gallatin 
20599b03b0f3SAndrew Gallatin /*
20609b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
20619b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
20629b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
20639b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2064053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2065053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
20669b03b0f3SAndrew Gallatin  */
20679b03b0f3SAndrew Gallatin 
2068053e637fSAndrew Gallatin static inline uint16_t
2069053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2070053e637fSAndrew Gallatin {
2071053e637fSAndrew Gallatin 	struct ether_header *eh;
2072053e637fSAndrew Gallatin 	struct ip *ip;
2073053e637fSAndrew Gallatin 	uint16_t c;
2074053e637fSAndrew Gallatin 
2075053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2076053e637fSAndrew Gallatin 
2077053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2078053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2079053e637fSAndrew Gallatin 		return 1;
2080053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2081053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2082053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2083053e637fSAndrew Gallatin 		return 1;
2084053e637fSAndrew Gallatin 
2085053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2086053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2087053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2088053e637fSAndrew Gallatin 	c ^= 0xffff;
2089053e637fSAndrew Gallatin 	return (c);
20905e7d8541SAndrew Gallatin }
2091053e637fSAndrew Gallatin 
2092c792928fSAndrew Gallatin static void
2093c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2094c792928fSAndrew Gallatin {
2095c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2096c792928fSAndrew Gallatin 	struct ether_header *eh;
2097c792928fSAndrew Gallatin 	uint32_t partial;
2098c792928fSAndrew Gallatin 
2099c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2100c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2101c792928fSAndrew Gallatin 
2102c792928fSAndrew Gallatin 	/*
2103c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2104c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2105c792928fSAndrew Gallatin 	 * header.
2106c792928fSAndrew Gallatin 	 */
2107c792928fSAndrew Gallatin 
2108c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2109c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2110c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2111c792928fSAndrew Gallatin 	(*csum) += ~partial;
2112c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2113c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2114c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2115c792928fSAndrew Gallatin 
2116c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2117c792928fSAndrew Gallatin 	   later consumers expect this */
2118c792928fSAndrew Gallatin 	*csum = htons(*csum);
2119c792928fSAndrew Gallatin 
2120c792928fSAndrew Gallatin 	/* save the tag */
2121c792928fSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2122c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
2123c792928fSAndrew Gallatin 
2124c792928fSAndrew Gallatin 	/*
2125c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2126c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2127c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2128c792928fSAndrew Gallatin 	 * type field is already in place.
2129c792928fSAndrew Gallatin 	 */
2130c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2131c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2132c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2133c792928fSAndrew Gallatin }
2134c792928fSAndrew Gallatin 
21355e7d8541SAndrew Gallatin 
21365e7d8541SAndrew Gallatin static inline void
2137053e637fSAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, uint32_t len, uint32_t csum)
2138b2fc195eSAndrew Gallatin {
2139b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2140053e637fSAndrew Gallatin 	struct mbuf *m;
2141c792928fSAndrew Gallatin 	struct ether_header *eh;
21426d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
2143053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2144b2fc195eSAndrew Gallatin 	int idx;
2145053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2146b2fc195eSAndrew Gallatin 
2147b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
2148053e637fSAndrew Gallatin 	rx = &sc->rx_big;
2149b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2150053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2151b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2152b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2153b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
21546d87a65dSAndrew Gallatin 	if (mxge_get_buf_big(sc, rx->extra_map, idx)) {
2155053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2156053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2157053e637fSAndrew Gallatin 		return;
2158b2fc195eSAndrew Gallatin 	}
2159053e637fSAndrew Gallatin 
2160b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2161b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2162b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2163b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2164b2fc195eSAndrew Gallatin 
2165b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2166b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2167b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2168b2fc195eSAndrew Gallatin 
2169053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2170053e637fSAndrew Gallatin 	 * aligned */
21715e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2172b2fc195eSAndrew Gallatin 
2173053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2174053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
21759b03b0f3SAndrew Gallatin 	ifp->if_ipackets++;
2176c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2177c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2178c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2179c792928fSAndrew Gallatin 	}
2180b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2181053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
2182053e637fSAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum)))
2183b2fc195eSAndrew Gallatin 			return;
2184053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2185053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2186053e637fSAndrew Gallatin 		   checksum is good */
2187053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2188053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2189b2fc195eSAndrew Gallatin 	}
2190053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2191053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2192b2fc195eSAndrew Gallatin }
2193b2fc195eSAndrew Gallatin 
2194b2fc195eSAndrew Gallatin static inline void
21955e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum)
2196b2fc195eSAndrew Gallatin {
2197b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2198c792928fSAndrew Gallatin 	struct ether_header *eh;
2199b2fc195eSAndrew Gallatin 	struct mbuf *m;
22006d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
2201b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2202b2fc195eSAndrew Gallatin 	int idx;
2203053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2204b2fc195eSAndrew Gallatin 
2205b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
2206b2fc195eSAndrew Gallatin 	rx = &sc->rx_small;
2207b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2208b2fc195eSAndrew Gallatin 	rx->cnt++;
2209b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2210b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2211b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
22126d87a65dSAndrew Gallatin 	if (mxge_get_buf_small(sc, rx->extra_map, idx)) {
2213b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2214b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2215b2fc195eSAndrew Gallatin 		return;
2216b2fc195eSAndrew Gallatin 	}
2217b2fc195eSAndrew Gallatin 
2218b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2219b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2220b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2221b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2222b2fc195eSAndrew Gallatin 
2223b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2224b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2225b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2226b2fc195eSAndrew Gallatin 
2227b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2228b2fc195eSAndrew Gallatin 	 * aligned */
22295e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2230b2fc195eSAndrew Gallatin 
22319b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
22329b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
22339b03b0f3SAndrew Gallatin 	ifp->if_ipackets++;
2234c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2235c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2236c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2237c792928fSAndrew Gallatin 	}
2238b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2239053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
2240053e637fSAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum)))
2241053e637fSAndrew Gallatin 			return;
2242053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2243053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2244053e637fSAndrew Gallatin 		   checksum is good */
2245053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2246053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2247053e637fSAndrew Gallatin 	}
2248b2fc195eSAndrew Gallatin 
2249b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2250b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2251b2fc195eSAndrew Gallatin }
2252b2fc195eSAndrew Gallatin 
2253b2fc195eSAndrew Gallatin static inline void
22545e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc)
22555e7d8541SAndrew Gallatin {
22565e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
2257053e637fSAndrew Gallatin 	struct lro_entry *lro;
22585e7d8541SAndrew Gallatin 	int limit = 0;
22595e7d8541SAndrew Gallatin 	uint16_t length;
22605e7d8541SAndrew Gallatin 	uint16_t checksum;
22615e7d8541SAndrew Gallatin 
22625e7d8541SAndrew Gallatin 
22635e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
22645e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
22655e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2266053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2267b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
22685e7d8541SAndrew Gallatin 			mxge_rx_done_small(sc, length, checksum);
22695e7d8541SAndrew Gallatin 		else
22705e7d8541SAndrew Gallatin 			mxge_rx_done_big(sc, length, checksum);
22715e7d8541SAndrew Gallatin 		rx_done->cnt++;
2272adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
22735e7d8541SAndrew Gallatin 
22745e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2275f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
22765e7d8541SAndrew Gallatin 			break;
2277053e637fSAndrew Gallatin 	}
2278053e637fSAndrew Gallatin 	while(!SLIST_EMPTY(&sc->lro_active)) {
2279053e637fSAndrew Gallatin 		lro = SLIST_FIRST(&sc->lro_active);
2280053e637fSAndrew Gallatin 		SLIST_REMOVE_HEAD(&sc->lro_active, next);
2281053e637fSAndrew Gallatin 		mxge_lro_flush(sc, lro);
22825e7d8541SAndrew Gallatin 	}
22835e7d8541SAndrew Gallatin }
22845e7d8541SAndrew Gallatin 
22855e7d8541SAndrew Gallatin 
22865e7d8541SAndrew Gallatin static inline void
22876d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx)
2288b2fc195eSAndrew Gallatin {
2289b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
22906d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
2291b2fc195eSAndrew Gallatin 	struct mbuf *m;
2292b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2293f616ebc7SAndrew Gallatin 	int idx;
2294b2fc195eSAndrew Gallatin 
2295b2fc195eSAndrew Gallatin 	tx = &sc->tx;
2296b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
22975e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2298b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2299b2fc195eSAndrew Gallatin 		tx->done++;
2300b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2301b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2302b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2303b2fc195eSAndrew Gallatin 		if (m != NULL) {
2304b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2305b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2306b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2307b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2308b2fc195eSAndrew Gallatin 			m_freem(m);
2309b2fc195eSAndrew Gallatin 		}
23105e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
23115e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
23125e7d8541SAndrew Gallatin 			tx->pkt_done++;
23135e7d8541SAndrew Gallatin 		}
2314b2fc195eSAndrew Gallatin 	}
2315b2fc195eSAndrew Gallatin 
2316b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2317b2fc195eSAndrew Gallatin            its OK to send packets */
2318b2fc195eSAndrew Gallatin 
2319b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2320b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2321a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->tx_mtx);
2322b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2323a82c2581SAndrew Gallatin 		sc->tx.wake++;
23246d87a65dSAndrew Gallatin 		mxge_start_locked(sc);
2325a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->tx_mtx);
2326b2fc195eSAndrew Gallatin 	}
2327b2fc195eSAndrew Gallatin }
2328b2fc195eSAndrew Gallatin 
2329b2fc195eSAndrew Gallatin static void
23306d87a65dSAndrew Gallatin mxge_intr(void *arg)
2331b2fc195eSAndrew Gallatin {
23326d87a65dSAndrew Gallatin 	mxge_softc_t *sc = arg;
23335e7d8541SAndrew Gallatin 	mcp_irq_data_t *stats = sc->fw_stats;
23345e7d8541SAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
23355e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
23365e7d8541SAndrew Gallatin 	uint32_t send_done_count;
23375e7d8541SAndrew Gallatin 	uint8_t valid;
2338b2fc195eSAndrew Gallatin 
2339b2fc195eSAndrew Gallatin 
23405e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
23415e7d8541SAndrew Gallatin 	if (!stats->valid) {
23425e7d8541SAndrew Gallatin 		return;
2343b2fc195eSAndrew Gallatin 	}
23445e7d8541SAndrew Gallatin 	valid = stats->valid;
2345b2fc195eSAndrew Gallatin 
2346dc8731d4SAndrew Gallatin 	if (!sc->msi_enabled) {
23475e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
23485e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
23495e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
23505e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
23515e7d8541SAndrew Gallatin 			stats->valid = 0;
2352dc8731d4SAndrew Gallatin 	} else {
2353dc8731d4SAndrew Gallatin 		stats->valid = 0;
2354dc8731d4SAndrew Gallatin 	}
2355dc8731d4SAndrew Gallatin 
2356dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
23575e7d8541SAndrew Gallatin 	do {
23585e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
23595e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
23605e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
23615e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
23625e7d8541SAndrew Gallatin 			mxge_tx_done(sc, (int)send_done_count);
23635e7d8541SAndrew Gallatin 			mxge_clean_rx_done(sc);
23645e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2365b2fc195eSAndrew Gallatin 		}
23665e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2367b2fc195eSAndrew Gallatin 
23685e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
23695e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
23705e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2371b2fc195eSAndrew Gallatin 			if (sc->link_state) {
23725e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
23735e7d8541SAndrew Gallatin 				if (mxge_verbose)
23745e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2375b2fc195eSAndrew Gallatin 			} else {
23765e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
23775e7d8541SAndrew Gallatin 				if (mxge_verbose)
23785e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2379b2fc195eSAndrew Gallatin 			}
2380b2fc195eSAndrew Gallatin 		}
2381b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
2382b2fc195eSAndrew Gallatin 		    be32toh(sc->fw_stats->rdma_tags_available)) {
2383b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
2384b2fc195eSAndrew Gallatin 				be32toh(sc->fw_stats->rdma_tags_available);
23855e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
23865e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
23875e7d8541SAndrew Gallatin 		}
23885e7d8541SAndrew Gallatin 		sc->down_cnt += stats->link_down;
2389b2fc195eSAndrew Gallatin 	}
2390b2fc195eSAndrew Gallatin 
23915e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
23925e7d8541SAndrew Gallatin 	if (valid & 0x1)
23935e7d8541SAndrew Gallatin 	    *sc->irq_claim = be32toh(3);
23945e7d8541SAndrew Gallatin 	*(sc->irq_claim + 1) = be32toh(3);
2395b2fc195eSAndrew Gallatin }
2396b2fc195eSAndrew Gallatin 
2397b2fc195eSAndrew Gallatin static void
23986d87a65dSAndrew Gallatin mxge_init(void *arg)
2399b2fc195eSAndrew Gallatin {
2400b2fc195eSAndrew Gallatin }
2401b2fc195eSAndrew Gallatin 
2402b2fc195eSAndrew Gallatin 
2403b2fc195eSAndrew Gallatin 
2404b2fc195eSAndrew Gallatin static void
24056d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2406b2fc195eSAndrew Gallatin {
2407b2fc195eSAndrew Gallatin 	int i;
2408b2fc195eSAndrew Gallatin 
2409b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2410b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2411b2fc195eSAndrew Gallatin 			continue;
2412b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2413b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2414b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2415b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2416b2fc195eSAndrew Gallatin 	}
2417b2fc195eSAndrew Gallatin 
24189b03b0f3SAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
24199b03b0f3SAndrew Gallatin 		if (sc->rx_small.info[i].m == NULL)
2420b2fc195eSAndrew Gallatin 			continue;
24219b03b0f3SAndrew Gallatin 		bus_dmamap_unload(sc->rx_small.dmat,
24229b03b0f3SAndrew Gallatin 				  sc->rx_small.info[i].map);
24239b03b0f3SAndrew Gallatin 		m_freem(sc->rx_small.info[i].m);
24249b03b0f3SAndrew Gallatin 		sc->rx_small.info[i].m = NULL;
2425b2fc195eSAndrew Gallatin 	}
2426b2fc195eSAndrew Gallatin 
2427b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2428dce01b9bSAndrew Gallatin 		sc->tx.info[i].flag = 0;
2429b2fc195eSAndrew Gallatin 		if (sc->tx.info[i].m == NULL)
2430b2fc195eSAndrew Gallatin 			continue;
2431b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->tx.dmat,
2432b2fc195eSAndrew Gallatin 				  sc->tx.info[i].map);
2433b2fc195eSAndrew Gallatin 		m_freem(sc->tx.info[i].m);
2434b2fc195eSAndrew Gallatin 		sc->tx.info[i].m = NULL;
2435b2fc195eSAndrew Gallatin 	}
2436b2fc195eSAndrew Gallatin }
2437b2fc195eSAndrew Gallatin 
2438b2fc195eSAndrew Gallatin static void
24396d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2440b2fc195eSAndrew Gallatin {
2441b2fc195eSAndrew Gallatin 	int i;
2442b2fc195eSAndrew Gallatin 
2443adae7080SAndrew Gallatin 	if (sc->rx_done.entry != NULL)
2444adae7080SAndrew Gallatin 		mxge_dma_free(&sc->rx_done.dma);
2445adae7080SAndrew Gallatin 	sc->rx_done.entry = NULL;
2446aed8e389SAndrew Gallatin 	if (sc->tx.req_bytes != NULL)
2447b2fc195eSAndrew Gallatin 		free(sc->tx.req_bytes, M_DEVBUF);
2448aed8e389SAndrew Gallatin 	if (sc->tx.seg_list != NULL)
2449aed8e389SAndrew Gallatin 		free(sc->tx.seg_list, M_DEVBUF);
2450b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow != NULL)
2451b2fc195eSAndrew Gallatin 		free(sc->rx_small.shadow, M_DEVBUF);
2452b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow != NULL)
2453b2fc195eSAndrew Gallatin 		free(sc->rx_big.shadow, M_DEVBUF);
2454b2fc195eSAndrew Gallatin 	if (sc->tx.info != NULL) {
2455c2657176SAndrew Gallatin 		if (sc->tx.dmat != NULL) {
2456b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->tx.mask; i++) {
2457b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->tx.dmat,
2458b2fc195eSAndrew Gallatin 						   sc->tx.info[i].map);
2459b2fc195eSAndrew Gallatin 			}
2460c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->tx.dmat);
2461c2657176SAndrew Gallatin 		}
2462b2fc195eSAndrew Gallatin 		free(sc->tx.info, M_DEVBUF);
2463b2fc195eSAndrew Gallatin 	}
2464b2fc195eSAndrew Gallatin 	if (sc->rx_small.info != NULL) {
2465c2657176SAndrew Gallatin 		if (sc->rx_small.dmat != NULL) {
2466b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_small.mask; i++) {
2467b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_small.dmat,
2468b2fc195eSAndrew Gallatin 						   sc->rx_small.info[i].map);
2469b2fc195eSAndrew Gallatin 			}
2470c2657176SAndrew Gallatin 			bus_dmamap_destroy(sc->rx_small.dmat,
2471c2657176SAndrew Gallatin 					   sc->rx_small.extra_map);
2472c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_small.dmat);
2473c2657176SAndrew Gallatin 		}
2474b2fc195eSAndrew Gallatin 		free(sc->rx_small.info, M_DEVBUF);
2475b2fc195eSAndrew Gallatin 	}
2476b2fc195eSAndrew Gallatin 	if (sc->rx_big.info != NULL) {
2477c2657176SAndrew Gallatin 		if (sc->rx_big.dmat != NULL) {
2478b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_big.mask; i++) {
2479b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_big.dmat,
2480b2fc195eSAndrew Gallatin 						   sc->rx_big.info[i].map);
2481b2fc195eSAndrew Gallatin 			}
2482b2fc195eSAndrew Gallatin 			bus_dmamap_destroy(sc->rx_big.dmat,
2483b2fc195eSAndrew Gallatin 					   sc->rx_big.extra_map);
2484b2fc195eSAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_big.dmat);
2485b2fc195eSAndrew Gallatin 		}
2486c2657176SAndrew Gallatin 		free(sc->rx_big.info, M_DEVBUF);
2487c2657176SAndrew Gallatin 	}
2488c2657176SAndrew Gallatin }
2489b2fc195eSAndrew Gallatin 
2490b2fc195eSAndrew Gallatin static int
24916d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
2492b2fc195eSAndrew Gallatin {
24936d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2494b2fc195eSAndrew Gallatin 	int tx_ring_size, rx_ring_size;
2495b2fc195eSAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
2496b2fc195eSAndrew Gallatin 	int i, err;
2497b2fc195eSAndrew Gallatin 	unsigned long bytes;
2498b2fc195eSAndrew Gallatin 
2499b2fc195eSAndrew Gallatin 	/* get ring sizes */
25005e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
2501b2fc195eSAndrew Gallatin 	tx_ring_size = cmd.data0;
25025e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
2503b2fc195eSAndrew Gallatin 	if (err != 0) {
2504b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Cannot determine ring sizes\n");
2505b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2506b2fc195eSAndrew Gallatin 	}
2507b2fc195eSAndrew Gallatin 
2508b2fc195eSAndrew Gallatin 	rx_ring_size = cmd.data0;
2509b2fc195eSAndrew Gallatin 
2510b2fc195eSAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
2511b2fc195eSAndrew Gallatin 	rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t);
251276bb9c5eSAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
2513a82c2581SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
251476bb9c5eSAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
2515b2fc195eSAndrew Gallatin 
2516b2fc195eSAndrew Gallatin 	sc->tx.mask = tx_ring_entries - 1;
2517adae7080SAndrew Gallatin 	sc->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
2518b2fc195eSAndrew Gallatin 	sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1;
2519adae7080SAndrew Gallatin 	sc->rx_done.mask = (2 * rx_ring_entries) - 1;
2520b2fc195eSAndrew Gallatin 
2521b2fc195eSAndrew Gallatin 	err = ENOMEM;
2522b2fc195eSAndrew Gallatin 
2523adae7080SAndrew Gallatin 	/* allocate interrupt queues */
2524adae7080SAndrew Gallatin 	bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry);
2525adae7080SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096);
2526adae7080SAndrew Gallatin 	if (err != 0)
2527adae7080SAndrew Gallatin 		goto abort_with_nothing;
2528adae7080SAndrew Gallatin 	sc->rx_done.entry = sc->rx_done.dma.addr;
2529adae7080SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
2530adae7080SAndrew Gallatin 
2531b2fc195eSAndrew Gallatin 	/* allocate the tx request copy block */
2532b2fc195eSAndrew Gallatin 	bytes = 8 +
2533adae7080SAndrew Gallatin 		sizeof (*sc->tx.req_list) * (sc->tx.max_desc + 4);
2534b2fc195eSAndrew Gallatin 	sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
2535b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes == NULL)
2536adae7080SAndrew Gallatin 		goto abort_with_alloc;
2537b2fc195eSAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
2538b2fc195eSAndrew Gallatin 	sc->tx.req_list = (mcp_kreq_ether_send_t *)
2539b2fc195eSAndrew Gallatin 		((unsigned long)(sc->tx.req_bytes + 7) & ~7UL);
2540b2fc195eSAndrew Gallatin 
2541aed8e389SAndrew Gallatin 	/* allocate the tx busdma segment list */
2542adae7080SAndrew Gallatin 	bytes = sizeof (*sc->tx.seg_list) * sc->tx.max_desc;
2543aed8e389SAndrew Gallatin 	sc->tx.seg_list = (bus_dma_segment_t *)
2544aed8e389SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
2545aed8e389SAndrew Gallatin 	if (sc->tx.seg_list == NULL)
2546aed8e389SAndrew Gallatin 		goto abort_with_alloc;
2547aed8e389SAndrew Gallatin 
2548b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
2549b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow);
2550b2fc195eSAndrew Gallatin 	sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2551b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow == NULL)
2552b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2553b2fc195eSAndrew Gallatin 
2554b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow);
2555b2fc195eSAndrew Gallatin 	sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2556b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow == NULL)
2557b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2558b2fc195eSAndrew Gallatin 
2559b2fc195eSAndrew Gallatin 	/* allocate the host info rings */
2560b2fc195eSAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*sc->tx.info);
2561b2fc195eSAndrew Gallatin 	sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2562b2fc195eSAndrew Gallatin 	if (sc->tx.info == NULL)
2563b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2564b2fc195eSAndrew Gallatin 
2565b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.info);
2566b2fc195eSAndrew Gallatin 	sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2567b2fc195eSAndrew Gallatin 	if (sc->rx_small.info == NULL)
2568b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2569b2fc195eSAndrew Gallatin 
2570b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.info);
2571b2fc195eSAndrew Gallatin 	sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2572b2fc195eSAndrew Gallatin 	if (sc->rx_big.info == NULL)
2573b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2574b2fc195eSAndrew Gallatin 
2575b2fc195eSAndrew Gallatin 	/* allocate the busdma resources */
2576b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2577b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2578b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* boundary */
2579b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2580b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2581b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2582aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
2583adae7080SAndrew Gallatin 				 sc->tx.max_desc - 2,	/* num segs */
2584b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* maxsegsize */
2585b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2586b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2587b2fc195eSAndrew Gallatin 				 &sc->tx.dmat);		/* tag */
2588b2fc195eSAndrew Gallatin 
2589b2fc195eSAndrew Gallatin 	if (err != 0) {
2590b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
2591b2fc195eSAndrew Gallatin 			      err);
2592b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2593b2fc195eSAndrew Gallatin 	}
2594b2fc195eSAndrew Gallatin 
2595b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2596b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2597b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2598b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2599b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2600b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2601b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2602b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2603b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2604b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2605b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2606b2fc195eSAndrew Gallatin 				 &sc->rx_small.dmat);	/* tag */
2607b2fc195eSAndrew Gallatin 	if (err != 0) {
2608b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2609b2fc195eSAndrew Gallatin 			      err);
2610b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2611b2fc195eSAndrew Gallatin 	}
2612b2fc195eSAndrew Gallatin 
2613b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2614b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2615b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2616b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2617b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2618b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2619053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
2620053e637fSAndrew Gallatin 				 3,			/* num segs */
2621b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2622b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2623b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2624b2fc195eSAndrew Gallatin 				 &sc->rx_big.dmat);	/* tag */
2625b2fc195eSAndrew Gallatin 	if (err != 0) {
2626b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2627b2fc195eSAndrew Gallatin 			      err);
2628b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2629b2fc195eSAndrew Gallatin 	}
2630b2fc195eSAndrew Gallatin 
2631b2fc195eSAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
2632b2fc195eSAndrew Gallatin 	   in each ring */
2633b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2634b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->tx.dmat, 0,
2635b2fc195eSAndrew Gallatin 					&sc->tx.info[i].map);
2636b2fc195eSAndrew Gallatin 		if (err != 0) {
2637b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
2638b2fc195eSAndrew Gallatin 			      err);
2639b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2640b2fc195eSAndrew Gallatin 		}
2641b2fc195eSAndrew Gallatin 	}
2642b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2643b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_small.dmat, 0,
2644b2fc195eSAndrew Gallatin 					&sc->rx_small.info[i].map);
2645b2fc195eSAndrew Gallatin 		if (err != 0) {
2646b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2647b2fc195eSAndrew Gallatin 				      err);
2648b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2649b2fc195eSAndrew Gallatin 		}
2650b2fc195eSAndrew Gallatin 	}
2651b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_small.dmat, 0,
2652b2fc195eSAndrew Gallatin 				&sc->rx_small.extra_map);
2653b2fc195eSAndrew Gallatin 	if (err != 0) {
2654b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2655b2fc195eSAndrew Gallatin 			      err);
2656b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2657b2fc195eSAndrew Gallatin 	}
2658b2fc195eSAndrew Gallatin 
2659b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2660b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_big.dmat, 0,
2661b2fc195eSAndrew Gallatin 					&sc->rx_big.info[i].map);
2662b2fc195eSAndrew Gallatin 		if (err != 0) {
2663b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2664b2fc195eSAndrew Gallatin 			      err);
2665b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2666b2fc195eSAndrew Gallatin 		}
2667b2fc195eSAndrew Gallatin 	}
2668b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_big.dmat, 0,
2669b2fc195eSAndrew Gallatin 				&sc->rx_big.extra_map);
2670b2fc195eSAndrew Gallatin 	if (err != 0) {
2671b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2672b2fc195eSAndrew Gallatin 			      err);
2673b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2674b2fc195eSAndrew Gallatin 	}
2675b2fc195eSAndrew Gallatin 	return 0;
2676b2fc195eSAndrew Gallatin 
2677b2fc195eSAndrew Gallatin abort_with_alloc:
26786d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2679b2fc195eSAndrew Gallatin 
2680b2fc195eSAndrew Gallatin abort_with_nothing:
2681b2fc195eSAndrew Gallatin 	return err;
2682b2fc195eSAndrew Gallatin }
2683b2fc195eSAndrew Gallatin 
2684053e637fSAndrew Gallatin static void
2685053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
2686053e637fSAndrew Gallatin {
2687c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
2688053e637fSAndrew Gallatin 
2689053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
2690053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
2691053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
2692053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
2693053e637fSAndrew Gallatin 		*nbufs = 1;
2694053e637fSAndrew Gallatin 		return;
2695053e637fSAndrew Gallatin 	}
2696053e637fSAndrew Gallatin 
2697053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
2698053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
2699053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
2700053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
2701053e637fSAndrew Gallatin 		*nbufs = 1;
2702053e637fSAndrew Gallatin 		return;
2703053e637fSAndrew Gallatin 	}
2704053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
2705053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
2706053e637fSAndrew Gallatin 	*big_buf_size = 4096;
2707053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
2708053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
2709053e637fSAndrew Gallatin 	if (*nbufs == 3)
2710053e637fSAndrew Gallatin 		*nbufs = 4;
2711053e637fSAndrew Gallatin }
2712053e637fSAndrew Gallatin 
2713b2fc195eSAndrew Gallatin static int
27146d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc)
2715b2fc195eSAndrew Gallatin {
27166d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2717053e637fSAndrew Gallatin 	int i, err, big_bytes;
2718b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
27190fa7f681SAndrew Gallatin 	bus_addr_t bus;
2720053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
2721b2fc195eSAndrew Gallatin 
2722053e637fSAndrew Gallatin 	SLIST_INIT(&sc->lro_free);
2723053e637fSAndrew Gallatin 	SLIST_INIT(&sc->lro_active);
2724053e637fSAndrew Gallatin 
2725053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
2726053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
2727053e637fSAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
2728053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
2729053e637fSAndrew Gallatin 			sc->lro_cnt = i;
2730053e637fSAndrew Gallatin 			break;
2731053e637fSAndrew Gallatin 		}
2732053e637fSAndrew Gallatin 		SLIST_INSERT_HEAD(&sc->lro_free, lro_entry, next);
2733053e637fSAndrew Gallatin 	}
2734b2fc195eSAndrew Gallatin 
27357d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
27367d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
27377d542e2dSAndrew Gallatin 
2738adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
2739b2fc195eSAndrew Gallatin 	if (err != 0) {
2740b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
2741b2fc195eSAndrew Gallatin 		return EIO;
2742b2fc195eSAndrew Gallatin 	}
2743b2fc195eSAndrew Gallatin 
2744053e637fSAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes,
2745053e637fSAndrew Gallatin 			   &sc->rx_big.cl_size, &sc->rx_big.nbufs);
2746b2fc195eSAndrew Gallatin 
2747053e637fSAndrew Gallatin 	cmd.data0 = sc->rx_big.nbufs;
2748053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
2749053e637fSAndrew Gallatin 			    &cmd);
2750053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
2751053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
2752053e637fSAndrew Gallatin 	if (err && sc->rx_big.nbufs > 1) {
2753053e637fSAndrew Gallatin 		device_printf(sc->dev,
2754053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
2755053e637fSAndrew Gallatin 			      sc->rx_big.nbufs);
2756053e637fSAndrew Gallatin 		return EIO;
2757053e637fSAndrew Gallatin 	}
2758b2fc195eSAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
2759b2fc195eSAndrew Gallatin 
27605e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
2761b2fc195eSAndrew Gallatin 	sc->tx.lanai =
2762b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
27636d87a65dSAndrew Gallatin 	err |= mxge_send_cmd(sc,
27645e7d8541SAndrew Gallatin 				 MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
2765b2fc195eSAndrew Gallatin 	sc->rx_small.lanai =
2766b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
27675e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
2768b2fc195eSAndrew Gallatin 	sc->rx_big.lanai =
2769b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
2770b2fc195eSAndrew Gallatin 
2771b2fc195eSAndrew Gallatin 	if (err != 0) {
2772b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
2773b2fc195eSAndrew Gallatin 			      "failed to get ring sizes or locations\n");
2774a98d6cd7SAndrew Gallatin 		return EIO;
2775b2fc195eSAndrew Gallatin 	}
2776b2fc195eSAndrew Gallatin 
2777b2fc195eSAndrew Gallatin 	/* stock receive rings */
2778b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2779b2fc195eSAndrew Gallatin 		map = sc->rx_small.info[i].map;
27806d87a65dSAndrew Gallatin 		err = mxge_get_buf_small(sc, map, i);
2781b2fc195eSAndrew Gallatin 		if (err) {
2782b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
2783b2fc195eSAndrew Gallatin 				      i, sc->rx_small.mask + 1);
2784b2fc195eSAndrew Gallatin 			goto abort;
2785b2fc195eSAndrew Gallatin 		}
2786b2fc195eSAndrew Gallatin 	}
2787b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2788053e637fSAndrew Gallatin 		sc->rx_big.shadow[i].addr_low = 0xffffffff;
2789053e637fSAndrew Gallatin 		sc->rx_big.shadow[i].addr_high = 0xffffffff;
2790053e637fSAndrew Gallatin 	}
2791053e637fSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i += sc->rx_big.nbufs) {
2792b2fc195eSAndrew Gallatin 		map = sc->rx_big.info[i].map;
27936d87a65dSAndrew Gallatin 		err = mxge_get_buf_big(sc, map, i);
2794b2fc195eSAndrew Gallatin 		if (err) {
2795b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
2796b2fc195eSAndrew Gallatin 				      i, sc->rx_big.mask + 1);
2797b2fc195eSAndrew Gallatin 			goto abort;
2798b2fc195eSAndrew Gallatin 		}
2799b2fc195eSAndrew Gallatin 	}
2800b2fc195eSAndrew Gallatin 
2801b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
2802b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
2803b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
2804c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
28055e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
2806b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
28075e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
2808b2fc195eSAndrew Gallatin 			     &cmd);
2809053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
28105e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
28110fa7f681SAndrew Gallatin 
28120fa7f681SAndrew Gallatin 	if (err != 0) {
28130fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
28140fa7f681SAndrew Gallatin 		goto abort;
28150fa7f681SAndrew Gallatin 	}
28160fa7f681SAndrew Gallatin 
2817b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
28186d87a65dSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr);
28196d87a65dSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr);
28200fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
28210fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
28220fa7f681SAndrew Gallatin 
28230fa7f681SAndrew Gallatin 	if (err != 0) {
28240fa7f681SAndrew Gallatin 		bus = sc->fw_stats_dma.bus_addr;
28250fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
28260fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
28270fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
28280fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
28290fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
28300fa7f681SAndrew Gallatin 				    &cmd);
28310fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
28320fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
28330fa7f681SAndrew Gallatin 	} else {
28340fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
28350fa7f681SAndrew Gallatin 	}
2836b2fc195eSAndrew Gallatin 
2837b2fc195eSAndrew Gallatin 	if (err != 0) {
2838b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
2839b2fc195eSAndrew Gallatin 		goto abort;
2840b2fc195eSAndrew Gallatin 	}
2841b2fc195eSAndrew Gallatin 
2842b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
28435e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
2844b2fc195eSAndrew Gallatin 	if (err) {
2845b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
2846b2fc195eSAndrew Gallatin 		goto abort;
2847b2fc195eSAndrew Gallatin 	}
2848b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
2849b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2850b2fc195eSAndrew Gallatin 
2851b2fc195eSAndrew Gallatin 	return 0;
2852b2fc195eSAndrew Gallatin 
2853b2fc195eSAndrew Gallatin 
2854b2fc195eSAndrew Gallatin abort:
28556d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2856a98d6cd7SAndrew Gallatin 
2857b2fc195eSAndrew Gallatin 	return err;
2858b2fc195eSAndrew Gallatin }
2859b2fc195eSAndrew Gallatin 
2860b2fc195eSAndrew Gallatin static int
28616d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
2862b2fc195eSAndrew Gallatin {
2863053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
28646d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2865b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
2866b2fc195eSAndrew Gallatin 
2867b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2868b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
2869b2fc195eSAndrew Gallatin 	mb();
28705e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
2871b2fc195eSAndrew Gallatin 	if (err) {
2872b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
2873b2fc195eSAndrew Gallatin 	}
2874b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2875b2fc195eSAndrew Gallatin 		/* wait for down irq */
2876dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
2877b2fc195eSAndrew Gallatin 	}
2878b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2879b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
2880b2fc195eSAndrew Gallatin 	}
2881a98d6cd7SAndrew Gallatin 
28826d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2883a98d6cd7SAndrew Gallatin 
2884053e637fSAndrew Gallatin 	while (!SLIST_EMPTY(&sc->lro_free)) {
2885053e637fSAndrew Gallatin 		lro_entry = SLIST_FIRST(&sc->lro_free);
2886053e637fSAndrew Gallatin 		SLIST_REMOVE_HEAD(&sc->lro_free, next);
2887053e637fSAndrew Gallatin 	}
2888b2fc195eSAndrew Gallatin 	return 0;
2889b2fc195eSAndrew Gallatin }
2890b2fc195eSAndrew Gallatin 
2891dce01b9bSAndrew Gallatin static void
2892dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
2893dce01b9bSAndrew Gallatin {
2894dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2895dce01b9bSAndrew Gallatin 	int reg;
2896dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
2897dce01b9bSAndrew Gallatin 
2898dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
2899dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
2900dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
2901dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
2902dce01b9bSAndrew Gallatin 
2903dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
2904dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
2905dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
2906dce01b9bSAndrew Gallatin 	}
2907dce01b9bSAndrew Gallatin 
2908dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
2909dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
2910dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2911dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
2912dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2913dce01b9bSAndrew Gallatin }
2914dce01b9bSAndrew Gallatin 
2915dce01b9bSAndrew Gallatin static uint32_t
2916dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
2917dce01b9bSAndrew Gallatin {
2918dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2919dce01b9bSAndrew Gallatin 	uint32_t vs;
2920dce01b9bSAndrew Gallatin 
2921dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
2922dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
2923dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
2924dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
2925dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
2926dce01b9bSAndrew Gallatin 	}
2927dce01b9bSAndrew Gallatin 	/* enable read32 mode */
2928dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
2929dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
2930dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
2931dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
2932dce01b9bSAndrew Gallatin }
2933dce01b9bSAndrew Gallatin 
2934dce01b9bSAndrew Gallatin static void
2935dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
2936dce01b9bSAndrew Gallatin {
2937dce01b9bSAndrew Gallatin 	int err;
2938dce01b9bSAndrew Gallatin 	uint32_t reboot;
2939dce01b9bSAndrew Gallatin 	uint16_t cmd;
2940dce01b9bSAndrew Gallatin 
2941dce01b9bSAndrew Gallatin 	err = ENXIO;
2942dce01b9bSAndrew Gallatin 
2943dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
2944dce01b9bSAndrew Gallatin 
2945dce01b9bSAndrew Gallatin 	/*
2946dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
2947dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
2948dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
2949dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
2950dce01b9bSAndrew Gallatin 	 * again
2951dce01b9bSAndrew Gallatin 	 */
2952dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2953dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
2954dce01b9bSAndrew Gallatin 		/*
2955dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
2956dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
2957dce01b9bSAndrew Gallatin 		 * back, then give up
2958dce01b9bSAndrew Gallatin 		 */
2959dce01b9bSAndrew Gallatin 		DELAY(1000*100);
2960dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2961dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
2962dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
2963dce01b9bSAndrew Gallatin 			goto abort;
2964dce01b9bSAndrew Gallatin 		}
2965dce01b9bSAndrew Gallatin 	}
2966dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
2967dce01b9bSAndrew Gallatin 		/* print the reboot status */
2968dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
2969dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
2970dce01b9bSAndrew Gallatin 			      reboot);
2971dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
2972dce01b9bSAndrew Gallatin 
2973dce01b9bSAndrew Gallatin 		/* XXXX waiting for pci_cfg_restore() to be exported */
2974dce01b9bSAndrew Gallatin 		goto abort; /* just abort for now */
2975dce01b9bSAndrew Gallatin 
2976dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
2977dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
2978dce01b9bSAndrew Gallatin 	} else {
2979dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC did not reboot, ring state:\n");
2980dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "tx.req=%d tx.done=%d\n",
2981dce01b9bSAndrew Gallatin 			      sc->tx.req, sc->tx.done);
2982dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
2983dce01b9bSAndrew Gallatin 			      sc->tx.pkt_done,
2984dce01b9bSAndrew Gallatin 			      be32toh(sc->fw_stats->send_done_count));
2985dce01b9bSAndrew Gallatin 	}
2986dce01b9bSAndrew Gallatin 
2987dce01b9bSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
2988dce01b9bSAndrew Gallatin 		mxge_close(sc);
2989dce01b9bSAndrew Gallatin 		err = mxge_open(sc);
2990dce01b9bSAndrew Gallatin 	}
2991dce01b9bSAndrew Gallatin 
2992dce01b9bSAndrew Gallatin abort:
2993dce01b9bSAndrew Gallatin 	/*
2994dce01b9bSAndrew Gallatin 	 * stop the watchdog if the nic is dead, to avoid spamming the
2995dce01b9bSAndrew Gallatin 	 * console
2996dce01b9bSAndrew Gallatin 	 */
2997dce01b9bSAndrew Gallatin 	if (err != 0) {
2998dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
2999dce01b9bSAndrew Gallatin 	}
3000dce01b9bSAndrew Gallatin }
3001dce01b9bSAndrew Gallatin 
3002dce01b9bSAndrew Gallatin static void
3003dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3004dce01b9bSAndrew Gallatin {
3005dce01b9bSAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
3006dce01b9bSAndrew Gallatin 
3007dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3008dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3009dce01b9bSAndrew Gallatin 	if (tx->req != tx->done &&
3010dce01b9bSAndrew Gallatin 	    tx->watchdog_req != tx->watchdog_done &&
3011dce01b9bSAndrew Gallatin 	    tx->done == tx->watchdog_done)
3012dce01b9bSAndrew Gallatin 		mxge_watchdog_reset(sc);
3013dce01b9bSAndrew Gallatin 
3014dce01b9bSAndrew Gallatin 	tx->watchdog_req = tx->req;
3015dce01b9bSAndrew Gallatin 	tx->watchdog_done = tx->done;
3016dce01b9bSAndrew Gallatin }
3017dce01b9bSAndrew Gallatin 
3018dce01b9bSAndrew Gallatin static void
3019dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3020dce01b9bSAndrew Gallatin {
3021dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
3022dce01b9bSAndrew Gallatin 
3023dce01b9bSAndrew Gallatin 
3024dce01b9bSAndrew Gallatin 	/* Synchronize with possible callout reset/stop. */
3025dce01b9bSAndrew Gallatin 	if (callout_pending(&sc->co_hdl) ||
3026dce01b9bSAndrew Gallatin 	    !callout_active(&sc->co_hdl)) {
3027dce01b9bSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3028dce01b9bSAndrew Gallatin 		return;
3029dce01b9bSAndrew Gallatin 	}
3030dce01b9bSAndrew Gallatin 
3031dce01b9bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3032dce01b9bSAndrew Gallatin 	mxge_watchdog(sc);
3033dce01b9bSAndrew Gallatin }
3034b2fc195eSAndrew Gallatin 
3035b2fc195eSAndrew Gallatin static int
30366d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
3037b2fc195eSAndrew Gallatin {
3038b2fc195eSAndrew Gallatin 	return EINVAL;
3039b2fc195eSAndrew Gallatin }
3040b2fc195eSAndrew Gallatin 
3041b2fc195eSAndrew Gallatin static int
30426d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3043b2fc195eSAndrew Gallatin {
3044b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
3045b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3046b2fc195eSAndrew Gallatin 	int err = 0;
3047b2fc195eSAndrew Gallatin 
3048b2fc195eSAndrew Gallatin 
3049c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3050053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3051b2fc195eSAndrew Gallatin 		return EINVAL;
3052a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3053b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
3054b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
3055b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3056dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
30576d87a65dSAndrew Gallatin 		mxge_close(sc);
30586d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3059b2fc195eSAndrew Gallatin 		if (err != 0) {
3060b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
30616d87a65dSAndrew Gallatin 			mxge_close(sc);
30626d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3063b2fc195eSAndrew Gallatin 		}
3064dce01b9bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3065b2fc195eSAndrew Gallatin 	}
3066a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3067b2fc195eSAndrew Gallatin 	return err;
3068b2fc195eSAndrew Gallatin }
3069b2fc195eSAndrew Gallatin 
3070b2fc195eSAndrew Gallatin static void
30716d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
3072b2fc195eSAndrew Gallatin {
30736d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3074b2fc195eSAndrew Gallatin 
3075b2fc195eSAndrew Gallatin 
3076b2fc195eSAndrew Gallatin 	if (sc == NULL)
3077b2fc195eSAndrew Gallatin 		return;
3078b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3079b2fc195eSAndrew Gallatin 	ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0;
3080b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
3081b2fc195eSAndrew Gallatin 	ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0;
3082b2fc195eSAndrew Gallatin }
3083b2fc195eSAndrew Gallatin 
3084b2fc195eSAndrew Gallatin static int
30856d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
3086b2fc195eSAndrew Gallatin {
30876d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3088b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
3089b2fc195eSAndrew Gallatin 	int err, mask;
3090b2fc195eSAndrew Gallatin 
3091b2fc195eSAndrew Gallatin 	err = 0;
3092b2fc195eSAndrew Gallatin 	switch (command) {
3093b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
3094b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
3095b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
3096b2fc195eSAndrew Gallatin 		break;
3097b2fc195eSAndrew Gallatin 
3098b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
30996d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
3100b2fc195eSAndrew Gallatin 		break;
3101b2fc195eSAndrew Gallatin 
3102b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
3103a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3104b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
3105dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
31066d87a65dSAndrew Gallatin 				err = mxge_open(sc);
3107dce01b9bSAndrew Gallatin 				callout_reset(&sc->co_hdl, mxge_ticks,
3108dce01b9bSAndrew Gallatin 					      mxge_tick, sc);
3109dce01b9bSAndrew Gallatin 			} else {
31100fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
31110fa7f681SAndrew Gallatin 				   flag chages */
31120fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
31130fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
31140fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
31150fa7f681SAndrew Gallatin 			}
3116b2fc195eSAndrew Gallatin 		} else {
3117dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
31186d87a65dSAndrew Gallatin 				mxge_close(sc);
3119dce01b9bSAndrew Gallatin 				callout_stop(&sc->co_hdl);
3120dce01b9bSAndrew Gallatin 			}
3121b2fc195eSAndrew Gallatin 		}
3122a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3123b2fc195eSAndrew Gallatin 		break;
3124b2fc195eSAndrew Gallatin 
3125b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
3126b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
3127a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
31280fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
3129a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3130b2fc195eSAndrew Gallatin 		break;
3131b2fc195eSAndrew Gallatin 
3132b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
3133a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3134b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
3135b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
3136b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
3137aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
3138aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3139aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3140b2fc195eSAndrew Gallatin 			} else {
3141b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3142b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3143b2fc195eSAndrew Gallatin 			}
3144b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3145b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
3146b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
31475e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
3148b2fc195eSAndrew Gallatin 			} else {
3149b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
31505e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
3151b2fc195eSAndrew Gallatin 			}
3152b2fc195eSAndrew Gallatin 		}
3153aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
3154aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
3155aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
3156aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
3157aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
3158aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
3159aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
3160aed8e389SAndrew Gallatin 			} else {
3161aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
3162aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
3163aed8e389SAndrew Gallatin 				err = EINVAL;
3164aed8e389SAndrew Gallatin 			}
3165aed8e389SAndrew Gallatin 		}
3166f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
3167f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
3168f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
3169f04b33f8SAndrew Gallatin 			else
3170f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
3171f04b33f8SAndrew Gallatin 		}
3172c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
3173c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
3174a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3175c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
3176c792928fSAndrew Gallatin 
3177b2fc195eSAndrew Gallatin 		break;
3178b2fc195eSAndrew Gallatin 
3179b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
3180b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
3181b2fc195eSAndrew Gallatin 				    &sc->media, command);
3182b2fc195eSAndrew Gallatin                 break;
3183b2fc195eSAndrew Gallatin 
3184b2fc195eSAndrew Gallatin 	default:
3185b2fc195eSAndrew Gallatin 		err = ENOTTY;
3186b2fc195eSAndrew Gallatin         }
3187b2fc195eSAndrew Gallatin 	return err;
3188b2fc195eSAndrew Gallatin }
3189b2fc195eSAndrew Gallatin 
3190b2fc195eSAndrew Gallatin static void
31916d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
3192b2fc195eSAndrew Gallatin {
3193b2fc195eSAndrew Gallatin 
31946d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
31956d87a65dSAndrew Gallatin 			  &mxge_flow_control);
31966d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
31976d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
31986d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
31996d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
3200d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
3201d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
32025e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
32035e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
32045e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
32055e7d8541SAndrew Gallatin 			  &mxge_verbose);
3206dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
3207053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
3208f04b33f8SAndrew Gallatin 	printf("%d %d\n", sc->lro_cnt, mxge_lro_cnt);
3209f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
3210f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
3211b2fc195eSAndrew Gallatin 
32125e7d8541SAndrew Gallatin 	if (bootverbose)
32135e7d8541SAndrew Gallatin 		mxge_verbose = 1;
32146d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
32156d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
3216dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
3217dce01b9bSAndrew Gallatin 		mxge_ticks = hz;
32186d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
3219053e637fSAndrew Gallatin 
3220b2fc195eSAndrew Gallatin }
3221b2fc195eSAndrew Gallatin 
3222b2fc195eSAndrew Gallatin static int
32236d87a65dSAndrew Gallatin mxge_attach(device_t dev)
3224b2fc195eSAndrew Gallatin {
32256d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
3226b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
3227dce01b9bSAndrew Gallatin 	int count, rid, err;
3228b2fc195eSAndrew Gallatin 
3229b2fc195eSAndrew Gallatin 	sc->dev = dev;
32306d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
3231b2fc195eSAndrew Gallatin 
3232b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
3233b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3234b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3235b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3236b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3237b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3238aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
32395e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
3240b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
3241b2fc195eSAndrew Gallatin 				 0,			/* flags */
3242b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
3243b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
3244b2fc195eSAndrew Gallatin 
3245b2fc195eSAndrew Gallatin 	if (err != 0) {
3246b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
3247b2fc195eSAndrew Gallatin 			      err);
3248b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
3249b2fc195eSAndrew Gallatin 	}
3250b2fc195eSAndrew Gallatin 
3251b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
3252b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
3253b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
3254b2fc195eSAndrew Gallatin 		err = ENOSPC;
3255b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
3256b2fc195eSAndrew Gallatin 	}
3257a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
3258a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
3259a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
3260a98d6cd7SAndrew Gallatin 	snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx",
3261a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
3262a98d6cd7SAndrew Gallatin 	mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF);
3263a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
3264a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
3265a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
3266b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
3267b2fc195eSAndrew Gallatin 
3268dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
3269d91b1b49SAndrew Gallatin 
3270dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
3271b2fc195eSAndrew Gallatin 
3272b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
3273b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
3274b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
3275b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
3276b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
3277b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
3278b2fc195eSAndrew Gallatin 		err = ENXIO;
3279b2fc195eSAndrew Gallatin 		goto abort_with_lock;
3280b2fc195eSAndrew Gallatin 	}
3281b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
3282b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
3283b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
3284b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
3285b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
3286b2fc195eSAndrew Gallatin 		err = ENXIO;
3287b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3288b2fc195eSAndrew Gallatin 	}
3289b2fc195eSAndrew Gallatin 
3290b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
3291b2fc195eSAndrew Gallatin 	   lanai SRAM */
32926d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
3293b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
3294b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
32956d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
3296b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
32976d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
32986d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
3299b2fc195eSAndrew Gallatin 	if (err != 0)
3300b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3301b2fc195eSAndrew Gallatin 
3302b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
33036d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
3304b2fc195eSAndrew Gallatin 
3305b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
33066d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
33076d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
3308b2fc195eSAndrew Gallatin 	if (err != 0)
3309b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3310b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
33116d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
3312b2fc195eSAndrew Gallatin 	if (err != 0)
3313b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
3314b2fc195eSAndrew Gallatin 
33156d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->fw_stats_dma,
3316b2fc195eSAndrew Gallatin 			     sizeof (*sc->fw_stats), 64);
3317b2fc195eSAndrew Gallatin 	if (err != 0)
3318b2fc195eSAndrew Gallatin 		goto abort_with_zeropad_dma;
33195e7d8541SAndrew Gallatin 	sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr;
3320b2fc195eSAndrew Gallatin 
3321a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
3322a98d6cd7SAndrew Gallatin 	if (err != 0)
3323a98d6cd7SAndrew Gallatin 		goto abort_with_fw_stats;
3324b2fc195eSAndrew Gallatin 
3325b2fc195eSAndrew Gallatin 	/* Add our ithread  */
3326dc8731d4SAndrew Gallatin 	count = pci_msi_count(dev);
3327dc8731d4SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(dev, &count) == 0) {
3328dc8731d4SAndrew Gallatin 		rid = 1;
3329dc8731d4SAndrew Gallatin 		sc->msi_enabled = 1;
3330dc8731d4SAndrew Gallatin 	} else {
3331b2fc195eSAndrew Gallatin 		rid = 0;
3332dc8731d4SAndrew Gallatin 	}
3333b2fc195eSAndrew Gallatin 	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0,
3334b2fc195eSAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
3335b2fc195eSAndrew Gallatin 	if (sc->irq_res == NULL) {
3336b2fc195eSAndrew Gallatin 		device_printf(dev, "could not alloc interrupt\n");
3337adae7080SAndrew Gallatin 		goto abort_with_dmabench;
3338b2fc195eSAndrew Gallatin 	}
3339d91b1b49SAndrew Gallatin 	if (mxge_verbose)
3340dc8731d4SAndrew Gallatin 		device_printf(dev, "using %s irq %ld\n",
3341dc8731d4SAndrew Gallatin 			      sc->msi_enabled ? "MSI" : "INTx",
3342dc8731d4SAndrew Gallatin 			      rman_get_start(sc->irq_res));
33438fe615baSAndrew Gallatin 	/* select & load the firmware */
33448fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
3345b2fc195eSAndrew Gallatin 	if (err != 0)
3346b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
33475e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
3348adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
3349b2fc195eSAndrew Gallatin 	if (err != 0)
3350b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
3351b2fc195eSAndrew Gallatin 
3352a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
3353a98d6cd7SAndrew Gallatin 	if (err != 0) {
3354a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
3355a98d6cd7SAndrew Gallatin 		goto abort_with_irq_res;
3356a98d6cd7SAndrew Gallatin 	}
3357a98d6cd7SAndrew Gallatin 
3358a98d6cd7SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
3359a98d6cd7SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
3360ef544f63SPaolo Pisati 			     NULL, mxge_intr, sc, &sc->ih);
3361a98d6cd7SAndrew Gallatin 	if (err != 0) {
3362a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
3363a98d6cd7SAndrew Gallatin 	}
3364b2fc195eSAndrew Gallatin 	/* hook into the network stack */
3365b2fc195eSAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
3366b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
3367c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
3368f04b33f8SAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING |
3369f04b33f8SAndrew Gallatin 		IFCAP_VLAN_HWCSUM | IFCAP_LRO;
3370c792928fSAndrew Gallatin 
3371053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
3372053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
3373053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
3374053e637fSAndrew Gallatin 	else
3375053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
3376adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
3377053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
3378aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
3379b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
3380f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
3381f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
33825e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
33836d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
3384b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
3385b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
33866d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
33876d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
3388b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
3389b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
3390053e637fSAndrew Gallatin 	if (ifp->if_capabilities & IFCAP_JUMBO_MTU)
3391c792928fSAndrew Gallatin 		ifp->if_mtu = 9000;
3392b2fc195eSAndrew Gallatin 
3393b2fc195eSAndrew Gallatin 	/* Initialise the ifmedia structure */
33946d87a65dSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
33956d87a65dSAndrew Gallatin 		     mxge_media_status);
3396b2fc195eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
33976d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
3398b2fc195eSAndrew Gallatin 	return 0;
3399b2fc195eSAndrew Gallatin 
3400a98d6cd7SAndrew Gallatin abort_with_rings:
3401a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3402b2fc195eSAndrew Gallatin abort_with_irq_res:
3403dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3404dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3405dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3406dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
3407a98d6cd7SAndrew Gallatin abort_with_dmabench:
3408a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
34095e7d8541SAndrew Gallatin abort_with_fw_stats:
34106d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3411b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
34126d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
3413b2fc195eSAndrew Gallatin abort_with_cmd_dma:
34146d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3415b2fc195eSAndrew Gallatin abort_with_mem_res:
3416b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3417b2fc195eSAndrew Gallatin abort_with_lock:
3418b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3419a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3420a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3421a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3422b2fc195eSAndrew Gallatin 	if_free(ifp);
3423b2fc195eSAndrew Gallatin abort_with_parent_dmat:
3424b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3425b2fc195eSAndrew Gallatin 
3426b2fc195eSAndrew Gallatin abort_with_nothing:
3427b2fc195eSAndrew Gallatin 	return err;
3428b2fc195eSAndrew Gallatin }
3429b2fc195eSAndrew Gallatin 
3430b2fc195eSAndrew Gallatin static int
34316d87a65dSAndrew Gallatin mxge_detach(device_t dev)
3432b2fc195eSAndrew Gallatin {
34336d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
3434b2fc195eSAndrew Gallatin 
3435c792928fSAndrew Gallatin 	if (sc->ifp->if_vlantrunk != NULL) {
3436c792928fSAndrew Gallatin 		device_printf(sc->dev,
3437c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
3438c792928fSAndrew Gallatin 		return EBUSY;
3439c792928fSAndrew Gallatin 	}
3440a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3441b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
34426d87a65dSAndrew Gallatin 		mxge_close(sc);
3443dce01b9bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3444a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3445b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
3446dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
3447091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
3448a98d6cd7SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
3449a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3450dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3451dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3452dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3453dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
3454dc8731d4SAndrew Gallatin 
34555e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
34566d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3457a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
34586d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
34596d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3460b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3461b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3462a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3463a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3464a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3465b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
3466b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3467b2fc195eSAndrew Gallatin 	return 0;
3468b2fc195eSAndrew Gallatin }
3469b2fc195eSAndrew Gallatin 
3470b2fc195eSAndrew Gallatin static int
34716d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
3472b2fc195eSAndrew Gallatin {
3473b2fc195eSAndrew Gallatin 	return 0;
3474b2fc195eSAndrew Gallatin }
3475b2fc195eSAndrew Gallatin 
3476b2fc195eSAndrew Gallatin /*
3477b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
3478b2fc195eSAndrew Gallatin 
3479b2fc195eSAndrew Gallatin   Local Variables:
3480b2fc195eSAndrew Gallatin   c-file-style:"linux"
3481b2fc195eSAndrew Gallatin   tab-width:8
3482b2fc195eSAndrew Gallatin   End:
3483b2fc195eSAndrew Gallatin */
3484