xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 0d151103078066fe3272c3fecc174dcdaaca11d0)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
31e413cf9SAndrew Gallatin Copyright (c) 2006-2008, 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/socket.h>
46b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
47b2fc195eSAndrew Gallatin #include <sys/sx.h>
48b2fc195eSAndrew Gallatin 
49b2fc195eSAndrew Gallatin #include <net/if.h>
50b2fc195eSAndrew Gallatin #include <net/if_arp.h>
51b2fc195eSAndrew Gallatin #include <net/ethernet.h>
52b2fc195eSAndrew Gallatin #include <net/if_dl.h>
53b2fc195eSAndrew Gallatin #include <net/if_media.h>
54b2fc195eSAndrew Gallatin 
55b2fc195eSAndrew Gallatin #include <net/bpf.h>
56b2fc195eSAndrew Gallatin 
57b2fc195eSAndrew Gallatin #include <net/if_types.h>
58b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
59b2fc195eSAndrew Gallatin #include <net/zlib.h>
60b2fc195eSAndrew Gallatin 
61b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
62b2fc195eSAndrew Gallatin #include <netinet/in.h>
63b2fc195eSAndrew Gallatin #include <netinet/ip.h>
64aed8e389SAndrew Gallatin #include <netinet/tcp.h>
65b2fc195eSAndrew Gallatin 
66b2fc195eSAndrew Gallatin #include <machine/bus.h>
67053e637fSAndrew Gallatin #include <machine/in_cksum.h>
68b2fc195eSAndrew Gallatin #include <machine/resource.h>
69b2fc195eSAndrew Gallatin #include <sys/bus.h>
70b2fc195eSAndrew Gallatin #include <sys/rman.h>
711e413cf9SAndrew Gallatin #include <sys/smp.h>
72b2fc195eSAndrew Gallatin 
73b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
74b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
75e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
76b2fc195eSAndrew Gallatin 
77b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
78b2fc195eSAndrew Gallatin #include <vm/pmap.h>
79b2fc195eSAndrew Gallatin 
80c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
81c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
82c2c14a69SAndrew Gallatin #endif
83c2c14a69SAndrew Gallatin 
846d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
856d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
861e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
876d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
88b2fc195eSAndrew Gallatin 
89b2fc195eSAndrew Gallatin /* tunable params */
906d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
91d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
926d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
935e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
946d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
955e7d8541SAndrew Gallatin static int mxge_verbose = 0;
96f04b33f8SAndrew Gallatin static int mxge_lro_cnt = 8;
97dce01b9bSAndrew Gallatin static int mxge_ticks;
981e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
991e413cf9SAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
1001e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
1016d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1026d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1031e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1041e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
105b2fc195eSAndrew Gallatin 
1066d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1076d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1086d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1096d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1106d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
111b2fc195eSAndrew Gallatin 
1126d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
113b2fc195eSAndrew Gallatin {
114b2fc195eSAndrew Gallatin   /* Device interface */
1156d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1166d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1176d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1186d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
119b2fc195eSAndrew Gallatin   {0, 0}
120b2fc195eSAndrew Gallatin };
121b2fc195eSAndrew Gallatin 
1226d87a65dSAndrew Gallatin static driver_t mxge_driver =
123b2fc195eSAndrew Gallatin {
1246d87a65dSAndrew Gallatin   "mxge",
1256d87a65dSAndrew Gallatin   mxge_methods,
1266d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
127b2fc195eSAndrew Gallatin };
128b2fc195eSAndrew Gallatin 
1296d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
130b2fc195eSAndrew Gallatin 
131b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1326d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1336d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
134f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
135b2fc195eSAndrew Gallatin 
1361e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1378fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
138276edd10SAndrew Gallatin static int mxge_close(mxge_softc_t *sc);
139276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
140276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1418fe615baSAndrew Gallatin 
142b2fc195eSAndrew Gallatin static int
1436d87a65dSAndrew Gallatin mxge_probe(device_t dev)
144b2fc195eSAndrew Gallatin {
1456d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
146f1544498SAndrew Gallatin       ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
147f1544498SAndrew Gallatin        (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
148b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
149b2fc195eSAndrew Gallatin 	  return 0;
150b2fc195eSAndrew Gallatin   }
151b2fc195eSAndrew Gallatin   return ENXIO;
152b2fc195eSAndrew Gallatin }
153b2fc195eSAndrew Gallatin 
154b2fc195eSAndrew Gallatin static void
1556d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
156b2fc195eSAndrew Gallatin {
157f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
158b2fc195eSAndrew Gallatin 	vm_offset_t len;
15947c2e987SAndrew Gallatin 	int err;
160b2fc195eSAndrew Gallatin 
1614d69a9d0SAndrew Gallatin 	sc->wc = 1;
162b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
163c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
164c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
16547c2e987SAndrew Gallatin 	if (err != 0) {
166c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
167c2c14a69SAndrew Gallatin 			      err);
1684d69a9d0SAndrew Gallatin 		sc->wc = 0;
169b2fc195eSAndrew Gallatin 	}
170f9ae0280SAndrew Gallatin #endif
171b2fc195eSAndrew Gallatin }
172b2fc195eSAndrew Gallatin 
173b2fc195eSAndrew Gallatin 
174b2fc195eSAndrew Gallatin /* callback to get our DMA address */
175b2fc195eSAndrew Gallatin static void
1766d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
177b2fc195eSAndrew Gallatin 			 int error)
178b2fc195eSAndrew Gallatin {
179b2fc195eSAndrew Gallatin 	if (error == 0) {
180b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
181b2fc195eSAndrew Gallatin 	}
182b2fc195eSAndrew Gallatin }
183b2fc195eSAndrew Gallatin 
184b2fc195eSAndrew Gallatin static int
1856d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
186b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
187b2fc195eSAndrew Gallatin {
188b2fc195eSAndrew Gallatin 	int err;
189b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
1901e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
1911e413cf9SAndrew Gallatin 
1921e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
1931e413cf9SAndrew Gallatin 		boundary = 0;
1941e413cf9SAndrew Gallatin 		maxsegsize = bytes;
1951e413cf9SAndrew Gallatin 	} else {
1961e413cf9SAndrew Gallatin 		boundary = 4096;
1971e413cf9SAndrew Gallatin 		maxsegsize = 4096;
1981e413cf9SAndrew Gallatin 	}
199b2fc195eSAndrew Gallatin 
200b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
201b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
202b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2031e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
204b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
205b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
206b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
207b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
208b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2091e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
210b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
211b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
212b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
213b2fc195eSAndrew Gallatin 	if (err != 0) {
214b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
215b2fc195eSAndrew Gallatin 		return err;
216b2fc195eSAndrew Gallatin 	}
217b2fc195eSAndrew Gallatin 
218b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
219b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
220b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
221b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
222b2fc195eSAndrew Gallatin 	if (err != 0) {
223b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
224b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
225b2fc195eSAndrew Gallatin 	}
226b2fc195eSAndrew Gallatin 
227b2fc195eSAndrew Gallatin 	/* load the memory */
228b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2296d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
230b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
231b2fc195eSAndrew Gallatin 	if (err != 0) {
232b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
233b2fc195eSAndrew Gallatin 		goto abort_with_mem;
234b2fc195eSAndrew Gallatin 	}
235b2fc195eSAndrew Gallatin 	return 0;
236b2fc195eSAndrew Gallatin 
237b2fc195eSAndrew Gallatin abort_with_mem:
238b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
239b2fc195eSAndrew Gallatin abort_with_dmat:
240b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
241b2fc195eSAndrew Gallatin 	return err;
242b2fc195eSAndrew Gallatin }
243b2fc195eSAndrew Gallatin 
244b2fc195eSAndrew Gallatin 
245b2fc195eSAndrew Gallatin static void
2466d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
247b2fc195eSAndrew Gallatin {
248b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
249b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
250b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
251b2fc195eSAndrew Gallatin }
252b2fc195eSAndrew Gallatin 
253b2fc195eSAndrew Gallatin /*
254b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
255b2fc195eSAndrew Gallatin  * SN=x\0
256b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
257b2fc195eSAndrew Gallatin  * PC=text\0
258b2fc195eSAndrew Gallatin  */
259b2fc195eSAndrew Gallatin 
260b2fc195eSAndrew Gallatin static int
2616d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
262b2fc195eSAndrew Gallatin {
2636d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
264b2fc195eSAndrew Gallatin 
265b2fc195eSAndrew Gallatin 	char *ptr, *limit;
266b2fc195eSAndrew Gallatin 	int i, found_mac;
267b2fc195eSAndrew Gallatin 
268b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2696d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
270b2fc195eSAndrew Gallatin 	found_mac = 0;
271b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
272b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2735e7d8541SAndrew Gallatin 			ptr += 1;
274b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
275b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2765e7d8541SAndrew Gallatin 				ptr += 3;
277b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
278b2fc195eSAndrew Gallatin 					goto abort;
279b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
280b2fc195eSAndrew Gallatin 				found_mac = 1;
281b2fc195eSAndrew Gallatin 			}
2825e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2835e7d8541SAndrew Gallatin 			ptr += 3;
2845e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2855e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2865e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2875e7d8541SAndrew Gallatin 			ptr += 3;
2885e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2895e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
290b2fc195eSAndrew Gallatin 		}
2916d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
292b2fc195eSAndrew Gallatin 	}
293b2fc195eSAndrew Gallatin 
294b2fc195eSAndrew Gallatin 	if (found_mac)
295b2fc195eSAndrew Gallatin 		return 0;
296b2fc195eSAndrew Gallatin 
297b2fc195eSAndrew Gallatin  abort:
298b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
299b2fc195eSAndrew Gallatin 
300b2fc195eSAndrew Gallatin 	return ENXIO;
301b2fc195eSAndrew Gallatin }
302b2fc195eSAndrew Gallatin 
3030d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3048fe615baSAndrew Gallatin static void
3058fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
306b2fc195eSAndrew Gallatin {
307b2fc195eSAndrew Gallatin 	uint32_t val;
3088fe615baSAndrew Gallatin 	unsigned long base, off;
309b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3108fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3118fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
312b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
313b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
314b2fc195eSAndrew Gallatin 
3158fe615baSAndrew Gallatin 
3168fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3178fe615baSAndrew Gallatin 		return;
3188fe615baSAndrew Gallatin 
3198fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3208fe615baSAndrew Gallatin 	if (pdev == NULL) {
3218fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3228fe615baSAndrew Gallatin 		return;
3238fe615baSAndrew Gallatin 	}
3248fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3258fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3268fe615baSAndrew Gallatin 
3278fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3288fe615baSAndrew Gallatin 		return;
3298fe615baSAndrew Gallatin 
3308fe615baSAndrew Gallatin 	base = 0;
3318fe615baSAndrew Gallatin 
3328fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3338fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3348fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3358fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3368fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3378fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3388fe615baSAndrew Gallatin 		if (mcp55 &&
3398fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3408fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3418fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3428fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3438fe615baSAndrew Gallatin 		}
3448fe615baSAndrew Gallatin 	}
3458fe615baSAndrew Gallatin 	if (!base)
3468fe615baSAndrew Gallatin 		return;
3478fe615baSAndrew Gallatin 
348b2fc195eSAndrew Gallatin 	/* XXXX
349b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
350b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
351b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
352b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
353b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
354b2fc195eSAndrew Gallatin 	*/
355b2fc195eSAndrew Gallatin #if 0
356b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
357b2fc195eSAndrew Gallatin 	   config space */
358b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
359b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
360b2fc195eSAndrew Gallatin 		val |= 0x40;
361b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3628fe615baSAndrew Gallatin 		return;
363b2fc195eSAndrew Gallatin 	}
364b2fc195eSAndrew Gallatin #endif
365b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
366b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
367b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
368b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
369b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
370b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
371b2fc195eSAndrew Gallatin 	 */
372b2fc195eSAndrew Gallatin 
373b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
374b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
375b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
376b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
377b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
378b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
379b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
380b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
381b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
382b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
383b2fc195eSAndrew Gallatin 
3848fe615baSAndrew Gallatin 	off =  base
385b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
386b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
387b2fc195eSAndrew Gallatin 						 + 8 * slot);
388b2fc195eSAndrew Gallatin 
389b2fc195eSAndrew Gallatin 	/* map it into the kernel */
390b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
391b2fc195eSAndrew Gallatin 
392b2fc195eSAndrew Gallatin 
393b2fc195eSAndrew Gallatin 	if (va == NULL) {
394b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3958fe615baSAndrew Gallatin 		return;
396b2fc195eSAndrew Gallatin 	}
397b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
398b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
399b2fc195eSAndrew Gallatin 
400b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
401b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
402b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
403b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
404b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
405b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
406b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4078fe615baSAndrew Gallatin 		return;
408b2fc195eSAndrew Gallatin 	}
409b2fc195eSAndrew Gallatin 
410b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
411b2fc195eSAndrew Gallatin 	val = *ptr32;
412b2fc195eSAndrew Gallatin 
413b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
414b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
415b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4168fe615baSAndrew Gallatin 		return;
417b2fc195eSAndrew Gallatin 	}
418b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
419b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4205e7d8541SAndrew Gallatin 	if (mxge_verbose)
421b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4225e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4235e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
424b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4258fe615baSAndrew Gallatin 	return;
426b2fc195eSAndrew Gallatin }
427b2fc195eSAndrew Gallatin #else
4288fe615baSAndrew Gallatin static void
429f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
430b2fc195eSAndrew Gallatin {
431b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
432b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4338fe615baSAndrew Gallatin 	return;
434b2fc195eSAndrew Gallatin }
435b2fc195eSAndrew Gallatin #endif
4368fe615baSAndrew Gallatin 
4378fe615baSAndrew Gallatin 
4388fe615baSAndrew Gallatin static int
4398fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4408fe615baSAndrew Gallatin {
4418fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4428fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4438fe615baSAndrew Gallatin 	int status;
4448fe615baSAndrew Gallatin 	uint32_t len;
4458fe615baSAndrew Gallatin 	char *test = " ";
4468fe615baSAndrew Gallatin 
4478fe615baSAndrew Gallatin 
4488fe615baSAndrew Gallatin 	/* Run a small DMA test.
4498fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4508fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4518fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4528fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4538fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4548fe615baSAndrew Gallatin 	 * transfers took to complete.
4558fe615baSAndrew Gallatin 	 */
4568fe615baSAndrew Gallatin 
4571e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4588fe615baSAndrew Gallatin 
4598fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4608fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4618fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4628fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4638fe615baSAndrew Gallatin 	if (status != 0) {
4648fe615baSAndrew Gallatin 		test = "read";
4658fe615baSAndrew Gallatin 		goto abort;
4668fe615baSAndrew Gallatin 	}
4678fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4688fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4698fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4708fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4718fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4728fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4738fe615baSAndrew Gallatin 	if (status != 0) {
4748fe615baSAndrew Gallatin 		test = "write";
4758fe615baSAndrew Gallatin 		goto abort;
4768fe615baSAndrew Gallatin 	}
4778fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
4788fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4798fe615baSAndrew Gallatin 
4808fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4818fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4828fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
4838fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4848fe615baSAndrew Gallatin 	if (status != 0) {
4858fe615baSAndrew Gallatin 		test = "read/write";
4868fe615baSAndrew Gallatin 		goto abort;
4878fe615baSAndrew Gallatin 	}
4888fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4898fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4908fe615baSAndrew Gallatin 
4918fe615baSAndrew Gallatin abort:
4928fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
4938fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4948fe615baSAndrew Gallatin 			      test, status);
4958fe615baSAndrew Gallatin 
4968fe615baSAndrew Gallatin 	return status;
4978fe615baSAndrew Gallatin }
4988fe615baSAndrew Gallatin 
499b2fc195eSAndrew Gallatin /*
500b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
501b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
502b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
503b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
504b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
505b2fc195eSAndrew Gallatin  *
506b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
507b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
508b2fc195eSAndrew Gallatin  *
509b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
510b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
511b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
512b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5131e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
514b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5151e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
516b2fc195eSAndrew Gallatin  */
517b2fc195eSAndrew Gallatin 
5188fe615baSAndrew Gallatin static int
5198fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5208fe615baSAndrew Gallatin {
5218fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5228fe615baSAndrew Gallatin 	int reg, status;
5238fe615baSAndrew Gallatin 	uint16_t pectl;
5248fe615baSAndrew Gallatin 
5251e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5268fe615baSAndrew Gallatin 	/*
5278fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5288fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5298fe615baSAndrew Gallatin 	 */
5308fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5318fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5328fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5338fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5348fe615baSAndrew Gallatin 				      pectl);
5351e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5368fe615baSAndrew Gallatin 		}
5378fe615baSAndrew Gallatin 	}
5388fe615baSAndrew Gallatin 
5398fe615baSAndrew Gallatin 	/*
5408fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5418fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5428fe615baSAndrew Gallatin 	 */
5438fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5441e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5458fe615baSAndrew Gallatin 	if (status != 0) {
5468fe615baSAndrew Gallatin 		return status;
5478fe615baSAndrew Gallatin 	}
5488fe615baSAndrew Gallatin 
5498fe615baSAndrew Gallatin 	/*
5508fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5518fe615baSAndrew Gallatin 	 */
5528fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5538fe615baSAndrew Gallatin 
5548fe615baSAndrew Gallatin 	/*
5558fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5568fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5578fe615baSAndrew Gallatin 	 */
5588fe615baSAndrew Gallatin 
5598fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5608fe615baSAndrew Gallatin 	if (status == 0)
5618fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5628fe615baSAndrew Gallatin 
5638fe615baSAndrew Gallatin 	if (status != E2BIG)
5648fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5658fe615baSAndrew Gallatin 	if (status == ENOSYS)
5668fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5678fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5688fe615baSAndrew Gallatin 	return status;
5698fe615baSAndrew Gallatin }
5708fe615baSAndrew Gallatin 
5718fe615baSAndrew Gallatin static int
5726d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
573b2fc195eSAndrew Gallatin {
5748fe615baSAndrew Gallatin 	int aligned = 0;
575b2fc195eSAndrew Gallatin 
576d91b1b49SAndrew Gallatin 
577d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
578d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
579d91b1b49SAndrew Gallatin 			aligned = 1;
580d91b1b49SAndrew Gallatin 		else
581d91b1b49SAndrew Gallatin 			aligned = 0;
582d91b1b49SAndrew Gallatin 		if (mxge_verbose)
583d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
584d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
585d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
586d91b1b49SAndrew Gallatin 		goto abort;
587d91b1b49SAndrew Gallatin 	}
588d91b1b49SAndrew Gallatin 
589d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
590d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
591d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
592d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
593d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
594d91b1b49SAndrew Gallatin 			      sc->link_width);
595d91b1b49SAndrew Gallatin 		aligned = 1;
596d91b1b49SAndrew Gallatin 		goto abort;
597d91b1b49SAndrew Gallatin 	}
598d91b1b49SAndrew Gallatin 
5998fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6008fe615baSAndrew Gallatin 		return 0;
601b2fc195eSAndrew Gallatin 
602b2fc195eSAndrew Gallatin abort:
603b2fc195eSAndrew Gallatin 	if (aligned) {
6046d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6051e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
606b2fc195eSAndrew Gallatin 	} else {
6076d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6081e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
609b2fc195eSAndrew Gallatin 	}
6101e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
611b2fc195eSAndrew Gallatin }
612b2fc195eSAndrew Gallatin 
613b2fc195eSAndrew Gallatin union qualhack
614b2fc195eSAndrew Gallatin {
615b2fc195eSAndrew Gallatin         const char *ro_char;
616b2fc195eSAndrew Gallatin         char *rw_char;
617b2fc195eSAndrew Gallatin };
618b2fc195eSAndrew Gallatin 
6194da0d523SAndrew Gallatin static int
6204da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6214da0d523SAndrew Gallatin {
622b824b7d8SAndrew Gallatin 
6234da0d523SAndrew Gallatin 
6244da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6254da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6264da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6274da0d523SAndrew Gallatin 		return EIO;
6284da0d523SAndrew Gallatin 	}
6294da0d523SAndrew Gallatin 
6304da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6314da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6324da0d523SAndrew Gallatin 	if (mxge_verbose)
6334da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6344da0d523SAndrew Gallatin 
635b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
636b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6374da0d523SAndrew Gallatin 
638b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
639b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6404da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6414da0d523SAndrew Gallatin 			      sc->fw_version);
6424da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6434da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6444da0d523SAndrew Gallatin 		return EINVAL;
6454da0d523SAndrew Gallatin 	}
6464da0d523SAndrew Gallatin 	return 0;
6474da0d523SAndrew Gallatin 
6484da0d523SAndrew Gallatin }
649b2fc195eSAndrew Gallatin 
650f9ae0280SAndrew Gallatin static void *
651f9ae0280SAndrew Gallatin z_alloc(void *nil, u_int items, u_int size)
652f9ae0280SAndrew Gallatin {
653f9ae0280SAndrew Gallatin         void *ptr;
654f9ae0280SAndrew Gallatin 
655f9ae0280SAndrew Gallatin         ptr = malloc(items * size, M_TEMP, M_NOWAIT);
656f9ae0280SAndrew Gallatin         return ptr;
657f9ae0280SAndrew Gallatin }
658f9ae0280SAndrew Gallatin 
659f9ae0280SAndrew Gallatin static void
660f9ae0280SAndrew Gallatin z_free(void *nil, void *ptr)
661f9ae0280SAndrew Gallatin {
662f9ae0280SAndrew Gallatin         free(ptr, M_TEMP);
663f9ae0280SAndrew Gallatin }
664f9ae0280SAndrew Gallatin 
665f9ae0280SAndrew Gallatin 
666b2fc195eSAndrew Gallatin static int
6676d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
668b2fc195eSAndrew Gallatin {
669f9ae0280SAndrew Gallatin 	z_stream zs;
670f9ae0280SAndrew Gallatin 	char *inflate_buffer;
67133d54970SLuigi Rizzo 	const struct firmware *fw;
672b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
673b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
674b2fc195eSAndrew Gallatin 	int status;
6754da0d523SAndrew Gallatin 	unsigned int i;
6764da0d523SAndrew Gallatin 	char dummy;
677f9ae0280SAndrew Gallatin 	size_t fw_len;
678b2fc195eSAndrew Gallatin 
679b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
680b2fc195eSAndrew Gallatin 	if (fw == NULL) {
681b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
682b2fc195eSAndrew Gallatin 			      sc->fw_name);
683b2fc195eSAndrew Gallatin 		return ENOENT;
684b2fc195eSAndrew Gallatin 	}
685b2fc195eSAndrew Gallatin 
686f9ae0280SAndrew Gallatin 
687f9ae0280SAndrew Gallatin 
688f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
689f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
690f9ae0280SAndrew Gallatin 	zs.zalloc = z_alloc;
691f9ae0280SAndrew Gallatin 	zs.zfree = z_free;
692f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
693f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
694b2fc195eSAndrew Gallatin 		status = EIO;
695b2fc195eSAndrew Gallatin 		goto abort_with_fw;
696b2fc195eSAndrew Gallatin 	}
697f9ae0280SAndrew Gallatin 
698f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
699f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
700f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
701f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
702f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
703f9ae0280SAndrew Gallatin 		goto abort_with_zs;
704f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
705f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
706f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
707f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
708f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
709f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
710f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
711f9ae0280SAndrew Gallatin 		status = EIO;
712f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
713f9ae0280SAndrew Gallatin 	}
714f9ae0280SAndrew Gallatin 
715f9ae0280SAndrew Gallatin 	/* check id */
716f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
717f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
718f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
719f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
720f9ae0280SAndrew Gallatin 		status = EIO;
721f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
722f9ae0280SAndrew Gallatin 	}
723f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
724b2fc195eSAndrew Gallatin 
7254da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7264da0d523SAndrew Gallatin 	if (status != 0)
727f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
728b2fc195eSAndrew Gallatin 
729b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
730f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7314da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
732f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
733f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
73473c7c83fSAndrew Gallatin 		wmb();
7354da0d523SAndrew Gallatin 		dummy = *sc->sram;
73673c7c83fSAndrew Gallatin 		wmb();
7374da0d523SAndrew Gallatin 	}
738b2fc195eSAndrew Gallatin 
739f9ae0280SAndrew Gallatin 	*limit = fw_len;
740b2fc195eSAndrew Gallatin 	status = 0;
741f9ae0280SAndrew Gallatin abort_with_buffer:
742f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
743f9ae0280SAndrew Gallatin abort_with_zs:
744f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
745b2fc195eSAndrew Gallatin abort_with_fw:
746b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
747b2fc195eSAndrew Gallatin 	return status;
748b2fc195eSAndrew Gallatin }
749b2fc195eSAndrew Gallatin 
750b2fc195eSAndrew Gallatin /*
751b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
752b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
753b2fc195eSAndrew Gallatin  */
754b2fc195eSAndrew Gallatin 
755b2fc195eSAndrew Gallatin static void
7566d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
757b2fc195eSAndrew Gallatin {
758b2fc195eSAndrew Gallatin 	char buf_bytes[72];
759b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
760b2fc195eSAndrew Gallatin 	volatile char *submit;
761b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
762b2fc195eSAndrew Gallatin 	int i;
763b2fc195eSAndrew Gallatin 
764b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
765b2fc195eSAndrew Gallatin 
766b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
767b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
768b2fc195eSAndrew Gallatin 	*confirm = 0;
76973c7c83fSAndrew Gallatin 	wmb();
770b2fc195eSAndrew Gallatin 
771b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
772b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
773b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
774b2fc195eSAndrew Gallatin 	*/
775b2fc195eSAndrew Gallatin 
7766d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7776d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
778b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
779b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
780b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7816d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7826d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
783b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
784b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
785b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
786b2fc195eSAndrew Gallatin 
787b2fc195eSAndrew Gallatin 
7880fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
789b2fc195eSAndrew Gallatin 
7906d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
79173c7c83fSAndrew Gallatin 	wmb();
792b2fc195eSAndrew Gallatin 	DELAY(1000);
79373c7c83fSAndrew Gallatin 	wmb();
794b2fc195eSAndrew Gallatin 	i = 0;
795b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
796b2fc195eSAndrew Gallatin 		DELAY(1000);
797b2fc195eSAndrew Gallatin 		i++;
798b2fc195eSAndrew Gallatin 	}
799b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
800b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
801b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
802b2fc195eSAndrew Gallatin 			      *confirm);
803b2fc195eSAndrew Gallatin 	}
804b2fc195eSAndrew Gallatin 	return;
805b2fc195eSAndrew Gallatin }
806b2fc195eSAndrew Gallatin 
807b2fc195eSAndrew Gallatin static int
8086d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
809b2fc195eSAndrew Gallatin {
810b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
811b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
812b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8130fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
814b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
815e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
816b2fc195eSAndrew Gallatin 
817b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
818b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
819b2fc195eSAndrew Gallatin 
820b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
821b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
822b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
823b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8246d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8256d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
826b2fc195eSAndrew Gallatin 
827b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
828b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
829a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
830b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
83173c7c83fSAndrew Gallatin 	wmb();
8326d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
833b2fc195eSAndrew Gallatin 
8345e7d8541SAndrew Gallatin 	/* wait up to 20ms */
835e0501fd0SAndrew Gallatin 	err = EAGAIN;
8365e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
837b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
838b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
83973c7c83fSAndrew Gallatin 		wmb();
840e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
841e0501fd0SAndrew Gallatin 		case 0:
842b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
843e0501fd0SAndrew Gallatin 			err = 0;
844e0501fd0SAndrew Gallatin 			break;
845e0501fd0SAndrew Gallatin 		case 0xffffffff:
846e0501fd0SAndrew Gallatin 			DELAY(1000);
847e0501fd0SAndrew Gallatin 			break;
848e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
849e0501fd0SAndrew Gallatin 			err = ENOSYS;
850e0501fd0SAndrew Gallatin 			break;
851e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
852e0501fd0SAndrew Gallatin 			err = E2BIG;
853e0501fd0SAndrew Gallatin 			break;
854c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
855c587e59fSAndrew Gallatin 			err = EBUSY;
856c587e59fSAndrew Gallatin 			break;
857e0501fd0SAndrew Gallatin 		default:
858b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8596d87a65dSAndrew Gallatin 				      "mxge: command %d "
860b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
861b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
862e0501fd0SAndrew Gallatin 			err = ENXIO;
863e0501fd0SAndrew Gallatin 			break;
864b2fc195eSAndrew Gallatin 		}
865e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
866e0501fd0SAndrew Gallatin 			break;
867b2fc195eSAndrew Gallatin 	}
868e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8696d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
870b2fc195eSAndrew Gallatin 			      "result = %d\n",
871b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
872e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
873e0501fd0SAndrew Gallatin 	return err;
874b2fc195eSAndrew Gallatin }
875b2fc195eSAndrew Gallatin 
8764da0d523SAndrew Gallatin static int
8774da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8784da0d523SAndrew Gallatin {
8794da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8804da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8814da0d523SAndrew Gallatin 	size_t hdr_offset;
8824da0d523SAndrew Gallatin 	int status;
8834da0d523SAndrew Gallatin 
8844da0d523SAndrew Gallatin 	/* find running firmware header */
8854da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
8864da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
8874da0d523SAndrew Gallatin 
8884da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8894da0d523SAndrew Gallatin 		device_printf(sc->dev,
8904da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
8914da0d523SAndrew Gallatin 			      (int)hdr_offset);
8924da0d523SAndrew Gallatin 		return EIO;
8934da0d523SAndrew Gallatin 	}
8944da0d523SAndrew Gallatin 
8954da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
8964da0d523SAndrew Gallatin 	 * validate firmware */
8974da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
8984da0d523SAndrew Gallatin 	if (hdr == NULL) {
8994da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9004da0d523SAndrew Gallatin 		return ENOMEM;
9014da0d523SAndrew Gallatin 	}
9024da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9034da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9044da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9054da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9064da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
907b824b7d8SAndrew Gallatin 
908b824b7d8SAndrew Gallatin 	/*
909b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
910b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
911b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
912b824b7d8SAndrew Gallatin 	 */
913b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
914b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
915b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
916b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
917b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
918b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
919b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
920b824b7d8SAndrew Gallatin 	}
921b824b7d8SAndrew Gallatin 
9224da0d523SAndrew Gallatin 	return status;
9234da0d523SAndrew Gallatin }
9244da0d523SAndrew Gallatin 
925b2fc195eSAndrew Gallatin 
926b2fc195eSAndrew Gallatin static int
9271e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
928b2fc195eSAndrew Gallatin {
929b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
930b2fc195eSAndrew Gallatin 	volatile char *submit;
931b2fc195eSAndrew Gallatin 	char buf_bytes[72];
932b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
933b2fc195eSAndrew Gallatin 	int status, i;
934b2fc195eSAndrew Gallatin 
935b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
936b2fc195eSAndrew Gallatin 
937b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9386d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
939b2fc195eSAndrew Gallatin 	if (status) {
9401e413cf9SAndrew Gallatin 		if (!adopt)
9411e413cf9SAndrew Gallatin 			return status;
9424da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9434da0d523SAndrew Gallatin 		   it is new enough */
9444da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9454da0d523SAndrew Gallatin 		if (status) {
9464da0d523SAndrew Gallatin 			device_printf(sc->dev,
9474da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
948b2fc195eSAndrew Gallatin 			return status;
949b2fc195eSAndrew Gallatin 		}
9504da0d523SAndrew Gallatin 		device_printf(sc->dev,
9514da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9521e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9534da0d523SAndrew Gallatin 			device_printf(sc->dev,
9544da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9554da0d523SAndrew Gallatin 				 ".  For optimal\n");
9564da0d523SAndrew Gallatin 			device_printf(sc->dev,
9574da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9584da0d523SAndrew Gallatin 				 "firmware\n");
9594da0d523SAndrew Gallatin 		}
960d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9611e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
962d91b1b49SAndrew Gallatin 		return 0;
9634da0d523SAndrew Gallatin 	}
964b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
965b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
966b2fc195eSAndrew Gallatin 	*confirm = 0;
96773c7c83fSAndrew Gallatin 	wmb();
968b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
969b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
970b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
971b2fc195eSAndrew Gallatin 	*/
972b2fc195eSAndrew Gallatin 
9736d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9746d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
975b2fc195eSAndrew Gallatin 
976b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
977b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
978b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
979b2fc195eSAndrew Gallatin 
980b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
981b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
982b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
983b2fc195eSAndrew Gallatin 	*/
984b2fc195eSAndrew Gallatin 					/* where the code starts*/
9856d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
986b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
987b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
988b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
989b2fc195eSAndrew Gallatin 
9900fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9916d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
99273c7c83fSAndrew Gallatin 	wmb();
993b2fc195eSAndrew Gallatin 	DELAY(1000);
99473c7c83fSAndrew Gallatin 	wmb();
995b2fc195eSAndrew Gallatin 	i = 0;
996b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
997b2fc195eSAndrew Gallatin 		DELAY(1000*10);
998b2fc195eSAndrew Gallatin 		i++;
999b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1000b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1001b2fc195eSAndrew Gallatin 	}
1002b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1003b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1004b2fc195eSAndrew Gallatin 			confirm, *confirm);
1005b2fc195eSAndrew Gallatin 
1006b2fc195eSAndrew Gallatin 		return ENXIO;
1007b2fc195eSAndrew Gallatin 	}
1008b2fc195eSAndrew Gallatin 	return 0;
1009b2fc195eSAndrew Gallatin }
1010b2fc195eSAndrew Gallatin 
1011b2fc195eSAndrew Gallatin static int
10126d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1013b2fc195eSAndrew Gallatin {
10146d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1015b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1016b2fc195eSAndrew Gallatin 	int status;
1017b2fc195eSAndrew Gallatin 
1018b2fc195eSAndrew Gallatin 
1019b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1020b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1021b2fc195eSAndrew Gallatin 
1022b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1023b2fc195eSAndrew Gallatin 
10245e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1025b2fc195eSAndrew Gallatin 	return status;
1026b2fc195eSAndrew Gallatin }
1027b2fc195eSAndrew Gallatin 
1028b2fc195eSAndrew Gallatin static int
10296d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1030b2fc195eSAndrew Gallatin {
10316d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1032b2fc195eSAndrew Gallatin 	int status;
1033b2fc195eSAndrew Gallatin 
1034b2fc195eSAndrew Gallatin 	if (pause)
10355e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1036b2fc195eSAndrew Gallatin 				       &cmd);
1037b2fc195eSAndrew Gallatin 	else
10385e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1039b2fc195eSAndrew Gallatin 				       &cmd);
1040b2fc195eSAndrew Gallatin 
1041b2fc195eSAndrew Gallatin 	if (status) {
1042b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1043b2fc195eSAndrew Gallatin 		return ENXIO;
1044b2fc195eSAndrew Gallatin 	}
1045b2fc195eSAndrew Gallatin 	sc->pause = pause;
1046b2fc195eSAndrew Gallatin 	return 0;
1047b2fc195eSAndrew Gallatin }
1048b2fc195eSAndrew Gallatin 
1049b2fc195eSAndrew Gallatin static void
10506d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1051b2fc195eSAndrew Gallatin {
10526d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1053b2fc195eSAndrew Gallatin 	int status;
1054b2fc195eSAndrew Gallatin 
10551e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10561e413cf9SAndrew Gallatin 		promisc = 1;
10571e413cf9SAndrew Gallatin 
1058b2fc195eSAndrew Gallatin 	if (promisc)
10595e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1060b2fc195eSAndrew Gallatin 				       &cmd);
1061b2fc195eSAndrew Gallatin 	else
10625e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1063b2fc195eSAndrew Gallatin 				       &cmd);
1064b2fc195eSAndrew Gallatin 
1065b2fc195eSAndrew Gallatin 	if (status) {
1066b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1067b2fc195eSAndrew Gallatin 	}
1068b2fc195eSAndrew Gallatin }
1069b2fc195eSAndrew Gallatin 
10700fa7f681SAndrew Gallatin static void
10710fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10720fa7f681SAndrew Gallatin {
10730fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
10740fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
10750fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
10760fa7f681SAndrew Gallatin 	int err;
10770fa7f681SAndrew Gallatin 
10780fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
10790fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
10800fa7f681SAndrew Gallatin 		return;
10810fa7f681SAndrew Gallatin 
10820fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
10830fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10840fa7f681SAndrew Gallatin 	if (err != 0) {
10850fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
10860fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
10870fa7f681SAndrew Gallatin 		return;
10880fa7f681SAndrew Gallatin 	}
10890fa7f681SAndrew Gallatin 
1090b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1091b824b7d8SAndrew Gallatin 		return;
10920fa7f681SAndrew Gallatin 
10930fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
10940fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
10950fa7f681SAndrew Gallatin 		return;
10960fa7f681SAndrew Gallatin 
10970fa7f681SAndrew Gallatin 	/* Flush all the filters */
10980fa7f681SAndrew Gallatin 
10990fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11000fa7f681SAndrew Gallatin 	if (err != 0) {
11010fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11020fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11030fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11040fa7f681SAndrew Gallatin 		return;
11050fa7f681SAndrew Gallatin 	}
11060fa7f681SAndrew Gallatin 
11070fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11080fa7f681SAndrew Gallatin 
11090fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
11100fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11110fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11120fa7f681SAndrew Gallatin 			continue;
11130fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11140fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11150fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11160fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11170fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11180fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11190fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11200fa7f681SAndrew Gallatin 		if (err != 0) {
11210fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11220fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11230fa7f681SAndrew Gallatin 			       "%d\t", err);
11240fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
11250fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
11260fa7f681SAndrew Gallatin 			return;
11270fa7f681SAndrew Gallatin 		}
11280fa7f681SAndrew Gallatin 	}
11290fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
11300fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11310fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11320fa7f681SAndrew Gallatin 	if (err != 0) {
11330fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11340fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11350fa7f681SAndrew Gallatin 	}
11360fa7f681SAndrew Gallatin }
11370fa7f681SAndrew Gallatin 
1138b2fc195eSAndrew Gallatin static int
1139053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1140053e637fSAndrew Gallatin {
1141053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1142053e637fSAndrew Gallatin 	int status;
1143053e637fSAndrew Gallatin 
1144c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1145c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1146053e637fSAndrew Gallatin 
1147053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1148053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1149053e637fSAndrew Gallatin 	cmd.data0 = 0;
1150053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1151053e637fSAndrew Gallatin 			       &cmd);
1152053e637fSAndrew Gallatin 	if (status == 0)
1153c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1154053e637fSAndrew Gallatin 
1155053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1156053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1157053e637fSAndrew Gallatin }
1158053e637fSAndrew Gallatin 
1159053e637fSAndrew Gallatin static int
1160adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1161b2fc195eSAndrew Gallatin {
11621e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11631e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11641e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11656d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11661e413cf9SAndrew Gallatin 	int slice, status;
1167b2fc195eSAndrew Gallatin 
1168b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1169b2fc195eSAndrew Gallatin 	   is alive */
1170b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11715e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1172b2fc195eSAndrew Gallatin 	if (status != 0) {
1173b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1174b2fc195eSAndrew Gallatin 		return ENXIO;
1175b2fc195eSAndrew Gallatin 	}
1176b2fc195eSAndrew Gallatin 
1177091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1178091feecdSAndrew Gallatin 
11791e413cf9SAndrew Gallatin 
11801e413cf9SAndrew Gallatin 	/* set the intrq size */
11811e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
11821e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11831e413cf9SAndrew Gallatin 
11841e413cf9SAndrew Gallatin 	/*
11851e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
11861e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
11871e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
11881e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
11891e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
11901e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
11911e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
11921e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
11931e413cf9SAndrew Gallatin 	 */
11941e413cf9SAndrew Gallatin 
11951e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
11961e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
11971e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
11981e413cf9SAndrew Gallatin 					   &cmd);
11991e413cf9SAndrew Gallatin 		if (status != 0) {
12001e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12011e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12021e413cf9SAndrew Gallatin 			return status;
12031e413cf9SAndrew Gallatin 		}
12041e413cf9SAndrew Gallatin 		/*
12051e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12061e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12071e413cf9SAndrew Gallatin 		 */
12081e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12091e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
12101e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12111e413cf9SAndrew Gallatin 					   &cmd);
12121e413cf9SAndrew Gallatin 		if (status != 0) {
12131e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12141e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12151e413cf9SAndrew Gallatin 			return status;
12161e413cf9SAndrew Gallatin 		}
12171e413cf9SAndrew Gallatin 	}
12181e413cf9SAndrew Gallatin 
12191e413cf9SAndrew Gallatin 
1220adae7080SAndrew Gallatin 	if (interrupts_setup) {
1221b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12221e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12231e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12241e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12251e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12261e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12271e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12281e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12291e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12301e413cf9SAndrew Gallatin 						&cmd);
12311e413cf9SAndrew Gallatin 		}
1232adae7080SAndrew Gallatin 	}
1233b2fc195eSAndrew Gallatin 
12346d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12355e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12365e7d8541SAndrew Gallatin 
12375e7d8541SAndrew Gallatin 
12385e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12395e7d8541SAndrew Gallatin 
12405e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12411e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12425e7d8541SAndrew Gallatin 
12435e7d8541SAndrew Gallatin 
12445e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12456d87a65dSAndrew Gallatin 				&cmd);
12465e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1247b2fc195eSAndrew Gallatin 	if (status != 0) {
1248b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1249b2fc195eSAndrew Gallatin 		return status;
1250b2fc195eSAndrew Gallatin 	}
1251b2fc195eSAndrew Gallatin 
12525e7d8541SAndrew Gallatin 
12535e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12545e7d8541SAndrew Gallatin 
12555e7d8541SAndrew Gallatin 
12565e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12578fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12585e7d8541SAndrew Gallatin 
12591e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12601e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12611e413cf9SAndrew Gallatin 
12621e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1263b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12641e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12651e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12661e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12671e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12681e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
12691e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
12701e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
12711e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
12721e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
12731e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
12741e413cf9SAndrew Gallatin 		ss->lro_bad_csum = 0;
12751e413cf9SAndrew Gallatin 		ss->lro_queued = 0;
12761e413cf9SAndrew Gallatin 		ss->lro_flushed = 0;
12771e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
12781e413cf9SAndrew Gallatin 			ss->fw_stats->valid = 0;
12791e413cf9SAndrew Gallatin 			ss->fw_stats->send_done_count = 0;
12801e413cf9SAndrew Gallatin 		}
12811e413cf9SAndrew Gallatin 	}
1282b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
12836d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
12846d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
12856d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
12860fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1287b2fc195eSAndrew Gallatin 	return status;
1288b2fc195eSAndrew Gallatin }
1289b2fc195eSAndrew Gallatin 
1290b2fc195eSAndrew Gallatin static int
12916d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1292b2fc195eSAndrew Gallatin {
12936d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1294b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1295b2fc195eSAndrew Gallatin         int err;
1296b2fc195eSAndrew Gallatin 
1297b2fc195eSAndrew Gallatin         sc = arg1;
1298b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1299b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1300b2fc195eSAndrew Gallatin         if (err != 0) {
1301b2fc195eSAndrew Gallatin                 return err;
1302b2fc195eSAndrew Gallatin         }
1303b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1304b2fc195eSAndrew Gallatin                 return 0;
1305b2fc195eSAndrew Gallatin 
1306b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1307b2fc195eSAndrew Gallatin                 return EINVAL;
1308b2fc195eSAndrew Gallatin 
1309a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13105e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1311b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13125e7d8541SAndrew Gallatin 
1313a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1314b2fc195eSAndrew Gallatin         return err;
1315b2fc195eSAndrew Gallatin }
1316b2fc195eSAndrew Gallatin 
1317b2fc195eSAndrew Gallatin static int
13186d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1319b2fc195eSAndrew Gallatin {
13206d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1321b2fc195eSAndrew Gallatin         unsigned int enabled;
1322b2fc195eSAndrew Gallatin         int err;
1323b2fc195eSAndrew Gallatin 
1324b2fc195eSAndrew Gallatin         sc = arg1;
1325b2fc195eSAndrew Gallatin         enabled = sc->pause;
1326b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1327b2fc195eSAndrew Gallatin         if (err != 0) {
1328b2fc195eSAndrew Gallatin                 return err;
1329b2fc195eSAndrew Gallatin         }
1330b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1331b2fc195eSAndrew Gallatin                 return 0;
1332b2fc195eSAndrew Gallatin 
1333a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13346d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1335a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1336b2fc195eSAndrew Gallatin         return err;
1337b2fc195eSAndrew Gallatin }
1338b2fc195eSAndrew Gallatin 
1339b2fc195eSAndrew Gallatin static int
1340f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1341f04b33f8SAndrew Gallatin {
1342f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1343c587e59fSAndrew Gallatin 	int err = 0;
1344f04b33f8SAndrew Gallatin 
1345f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1346f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1347f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1348f04b33f8SAndrew Gallatin 	else
1349f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1350f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1351c587e59fSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1352f04b33f8SAndrew Gallatin 		mxge_close(sc);
1353f04b33f8SAndrew Gallatin 		err = mxge_open(sc);
1354c587e59fSAndrew Gallatin 	}
1355f04b33f8SAndrew Gallatin 	return err;
1356f04b33f8SAndrew Gallatin }
1357f04b33f8SAndrew Gallatin 
1358f04b33f8SAndrew Gallatin static int
1359276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1360276edd10SAndrew Gallatin {
1361276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1362276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1363276edd10SAndrew Gallatin 	int err;
1364276edd10SAndrew Gallatin 
1365276edd10SAndrew Gallatin 	sc = arg1;
1366276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1367276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1368276edd10SAndrew Gallatin 	if (err != 0)
1369276edd10SAndrew Gallatin 		return err;
1370276edd10SAndrew Gallatin 
1371276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1372276edd10SAndrew Gallatin 		return 0;
1373276edd10SAndrew Gallatin 
1374276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1375276edd10SAndrew Gallatin 		return EINVAL;
1376276edd10SAndrew Gallatin 
1377276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1378f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1379276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1380276edd10SAndrew Gallatin 	return err;
1381276edd10SAndrew Gallatin }
1382276edd10SAndrew Gallatin 
1383276edd10SAndrew Gallatin static int
13846d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1385b2fc195eSAndrew Gallatin {
1386b2fc195eSAndrew Gallatin         int err;
1387b2fc195eSAndrew Gallatin 
1388b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1389b2fc195eSAndrew Gallatin                 return EFAULT;
1390b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1391b2fc195eSAndrew Gallatin         arg1 = NULL;
1392b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1393b2fc195eSAndrew Gallatin 
1394b2fc195eSAndrew Gallatin         return err;
1395b2fc195eSAndrew Gallatin }
1396b2fc195eSAndrew Gallatin 
1397b2fc195eSAndrew Gallatin static void
13981e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
13991e413cf9SAndrew Gallatin {
14001e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14011e413cf9SAndrew Gallatin 	int slice;
14021e413cf9SAndrew Gallatin 
14031e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14041e413cf9SAndrew Gallatin 		return;
14051e413cf9SAndrew Gallatin 
14061e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14071e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14081e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14091e413cf9SAndrew Gallatin 			continue;
14101e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14111e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14121e413cf9SAndrew Gallatin 	}
14131e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14141e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14151e413cf9SAndrew Gallatin }
14161e413cf9SAndrew Gallatin 
14171e413cf9SAndrew Gallatin static void
14186d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1419b2fc195eSAndrew Gallatin {
1420b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1421b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14225e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14231e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14241e413cf9SAndrew Gallatin 	int slice;
14251e413cf9SAndrew Gallatin 	char slice_num[8];
1426b2fc195eSAndrew Gallatin 
1427b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1428b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14291e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1430b2fc195eSAndrew Gallatin 
14315e7d8541SAndrew Gallatin 	/* random information */
14325e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14335e7d8541SAndrew Gallatin 		       "firmware_version",
14345e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
14355e7d8541SAndrew Gallatin 		       0, "firmware version");
14365e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14375e7d8541SAndrew Gallatin 		       "serial_number",
14385e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
14395e7d8541SAndrew Gallatin 		       0, "serial number");
14405e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14415e7d8541SAndrew Gallatin 		       "product_code",
14425e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
14435e7d8541SAndrew Gallatin 		       0, "product_code");
14445e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1445d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1446d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1447d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1448d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14495e7d8541SAndrew Gallatin 		       "tx_boundary",
14501e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14515e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14525e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1453091feecdSAndrew Gallatin 		       "write_combine",
1454091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1455091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1456091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14575e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14585e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14595e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14605e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14615e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14625e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14635e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14645e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14655e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14665e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14675e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
14685e7d8541SAndrew Gallatin 
14695e7d8541SAndrew Gallatin 
14705e7d8541SAndrew Gallatin 	/* performance related tunables */
1471b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1472b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1473b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
14746d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1475b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1476b2fc195eSAndrew Gallatin 
1477b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1478b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1479b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
14806d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1481b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1482b2fc195eSAndrew Gallatin 
1483b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14845e7d8541SAndrew Gallatin 		       "deassert_wait",
14855e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
14865e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1487b2fc195eSAndrew Gallatin 
1488b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1489b2fc195eSAndrew Gallatin 	   Need to swap it */
1490b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1491b2fc195eSAndrew Gallatin 			"link_up",
1492b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
14936d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1494b2fc195eSAndrew Gallatin 			"I", "link up");
1495b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1496b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1497b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
14986d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1499b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1500b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1501adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1502adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1503adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15046d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1505adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1506adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1507adae7080SAndrew Gallatin 			"dropped_bad_phy",
1508adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1509adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1510adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1511adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1512b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1513b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1514b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1515b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
15166d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1517b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1518b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1519adae7080SAndrew Gallatin 			"dropped_link_overflow",
1520adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1521adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1522adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1523adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15240fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
15250fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
15260fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
15270fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
15280fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
15290fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1530adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1531adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
15326d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1533adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1534b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1535b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1536b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1537b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
15386d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1539b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1540b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1541adae7080SAndrew Gallatin 			"dropped_overrun",
1542adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
15436d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1544adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1545adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1546adae7080SAndrew Gallatin 			"dropped_pause",
1547adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1548adae7080SAndrew Gallatin 			&fw->dropped_pause,
1549adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1550adae7080SAndrew Gallatin 			"I", "dropped_pause");
1551adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1552adae7080SAndrew Gallatin 			"dropped_runt",
1553adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1554adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1555adae7080SAndrew Gallatin 			"I", "dropped_runt");
1556b2fc195eSAndrew Gallatin 
1557a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1558a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1559a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1560a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1561a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1562a0394e33SAndrew Gallatin 
15635e7d8541SAndrew Gallatin 	/* verbose printing? */
1564b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15655e7d8541SAndrew Gallatin 		       "verbose",
15665e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15675e7d8541SAndrew Gallatin 		       0, "verbose printing");
1568b2fc195eSAndrew Gallatin 
1569053e637fSAndrew Gallatin 	/* lro */
1570276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1571276edd10SAndrew Gallatin 			"lro_cnt",
1572276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1573276edd10SAndrew Gallatin 			0, mxge_change_lro,
1574276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1575053e637fSAndrew Gallatin 
15761e413cf9SAndrew Gallatin 
15771e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
15781e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15791e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
15801e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15811e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
15821e413cf9SAndrew Gallatin 
15831e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
15841e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
15851e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
15861e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
15871e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
15881e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
15891e413cf9SAndrew Gallatin 		ss->sysctl_tree =
15901e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
15911e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
15921e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1593053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15941e413cf9SAndrew Gallatin 			       "rx_small_cnt",
15951e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
15961e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
15971e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15981e413cf9SAndrew Gallatin 			       "rx_big_cnt",
15991e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16001e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16011e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16021e413cf9SAndrew Gallatin 			       "tx_req",
16031e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
16041e413cf9SAndrew Gallatin 			       0, "tx_req");
16051e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16061e413cf9SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
1607053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1608053e637fSAndrew Gallatin 
1609053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16101e413cf9SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16111e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16121e413cf9SAndrew Gallatin 			       "queues");
1613053e637fSAndrew Gallatin 
16141e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16151e413cf9SAndrew Gallatin 		if (slice > 0)
16161e413cf9SAndrew Gallatin 			continue;
16171e413cf9SAndrew Gallatin 
16181e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16191e413cf9SAndrew Gallatin 			       "tx_done",
16201e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16211e413cf9SAndrew Gallatin 			       0, "tx_done");
16221e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16231e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16241e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16251e413cf9SAndrew Gallatin 			       0, "tx_done");
16261e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16271e413cf9SAndrew Gallatin 			       "tx_stall",
16281e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16291e413cf9SAndrew Gallatin 			       0, "tx_stall");
16301e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16311e413cf9SAndrew Gallatin 			       "tx_wake",
16321e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16331e413cf9SAndrew Gallatin 			       0, "tx_wake");
16341e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16351e413cf9SAndrew Gallatin 			       "tx_defrag",
16361e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16371e413cf9SAndrew Gallatin 			       0, "tx_defrag");
16381e413cf9SAndrew Gallatin 	}
1639b2fc195eSAndrew Gallatin }
1640b2fc195eSAndrew Gallatin 
1641b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1642b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1643b2fc195eSAndrew Gallatin 
1644b2fc195eSAndrew Gallatin static inline void
16451e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1646b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1647b2fc195eSAndrew Gallatin {
1648b2fc195eSAndrew Gallatin         int idx, starting_slot;
1649b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1650b2fc195eSAndrew Gallatin         while (cnt > 1) {
1651b2fc195eSAndrew Gallatin                 cnt--;
1652b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
16536d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1654b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
165573c7c83fSAndrew Gallatin                 wmb();
1656b2fc195eSAndrew Gallatin         }
1657b2fc195eSAndrew Gallatin }
1658b2fc195eSAndrew Gallatin 
1659b2fc195eSAndrew Gallatin /*
1660b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1661b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1662b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1663b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1664b2fc195eSAndrew Gallatin  */
1665b2fc195eSAndrew Gallatin 
1666b2fc195eSAndrew Gallatin static inline void
16671e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1668b2fc195eSAndrew Gallatin                   int cnt)
1669b2fc195eSAndrew Gallatin {
1670b2fc195eSAndrew Gallatin         int idx, i;
1671b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1672b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1673b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1674b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
16755e7d8541SAndrew Gallatin 	uint8_t last_flags;
1676b2fc195eSAndrew Gallatin 
1677b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1678b2fc195eSAndrew Gallatin 
16795e7d8541SAndrew Gallatin 	last_flags = src->flags;
16805e7d8541SAndrew Gallatin 	src->flags = 0;
168173c7c83fSAndrew Gallatin         wmb();
1682b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1683b2fc195eSAndrew Gallatin         srcp = src;
1684b2fc195eSAndrew Gallatin 
1685b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1686b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
16876d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
168873c7c83fSAndrew Gallatin                         wmb(); /* force write every 32 bytes */
1689b2fc195eSAndrew Gallatin                         srcp += 2;
1690b2fc195eSAndrew Gallatin                         dstp += 2;
1691b2fc195eSAndrew Gallatin                 }
1692b2fc195eSAndrew Gallatin         } else {
1693b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1694b2fc195eSAndrew Gallatin                    that it is submitted below */
16956d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1696b2fc195eSAndrew Gallatin                 i = 0;
1697b2fc195eSAndrew Gallatin         }
1698b2fc195eSAndrew Gallatin         if (i < cnt) {
1699b2fc195eSAndrew Gallatin                 /* submit the first request */
17006d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
170173c7c83fSAndrew Gallatin                 wmb(); /* barrier before setting valid flag */
1702b2fc195eSAndrew Gallatin         }
1703b2fc195eSAndrew Gallatin 
1704b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
17055e7d8541SAndrew Gallatin         src->flags = last_flags;
1706b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1707b2fc195eSAndrew Gallatin         src_ints+=3;
1708b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1709b2fc195eSAndrew Gallatin         dst_ints+=3;
1710b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1711b2fc195eSAndrew Gallatin         tx->req += cnt;
171273c7c83fSAndrew Gallatin         wmb();
1713b2fc195eSAndrew Gallatin }
1714b2fc195eSAndrew Gallatin 
171537d89b0cSAndrew Gallatin #if IFCAP_TSO4
171637d89b0cSAndrew Gallatin 
1717b2fc195eSAndrew Gallatin static void
17181e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
17191e413cf9SAndrew Gallatin 	       int busdma_seg_cnt, int ip_off)
1720aed8e389SAndrew Gallatin {
17211e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1722aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1723aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1724aed8e389SAndrew Gallatin 	struct ip *ip;
1725aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1726aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1727aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1728aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1729aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1730aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1731aed8e389SAndrew Gallatin 	static int once;
1732aed8e389SAndrew Gallatin 
1733aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1734aed8e389SAndrew Gallatin 
1735aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1736aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1737aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1738aed8e389SAndrew Gallatin 	 */
1739aed8e389SAndrew Gallatin 
1740aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1741aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1742aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1743c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1744c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
17451e413cf9SAndrew Gallatin 			   ss->scratch);
17461e413cf9SAndrew Gallatin 		ip = (struct ip *)(ss->scratch + ip_off);
1747aed8e389SAndrew Gallatin 	} else {
1748c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1749aed8e389SAndrew Gallatin 	}
1750c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1751aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1752c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
17531e413cf9SAndrew Gallatin 			   + sizeof (*tcp),  ss->scratch);
1754c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1755aed8e389SAndrew Gallatin 	}
1756aed8e389SAndrew Gallatin 
1757aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1758c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1759aed8e389SAndrew Gallatin 
1760aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1761c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1762aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1763aed8e389SAndrew Gallatin 
1764aed8e389SAndrew Gallatin 
1765aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1766aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1767aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1768aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1769aed8e389SAndrew Gallatin 
17701e413cf9SAndrew Gallatin 	tx = &ss->tx;
1771aed8e389SAndrew Gallatin 	req = tx->req_list;
1772aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1773aed8e389SAndrew Gallatin 	cnt = 0;
1774aed8e389SAndrew Gallatin 	rdma_count = 0;
1775aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1776aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1777aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1778aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1779aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1780aed8e389SAndrew Gallatin 	 *
1781aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1782aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1783aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1784aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1785aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1786aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1787aed8e389SAndrew Gallatin 	 *
1788aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1789aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1790aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1791aed8e389SAndrew Gallatin 	 */
1792aed8e389SAndrew Gallatin 
1793aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1794aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1795aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1796aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1797e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1798aed8e389SAndrew Gallatin 
1799aed8e389SAndrew Gallatin 		while (len) {
1800aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1801e39a0a37SAndrew Gallatin 			seglen = len;
1802aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1803aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1804aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1805aed8e389SAndrew Gallatin 				/* payload */
1806aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1807aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1808aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1809aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1810aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1811aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1812aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1813aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1814aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1815aed8e389SAndrew Gallatin 				/* header ends */
1816aed8e389SAndrew Gallatin 				rdma_count = -1;
1817aed8e389SAndrew Gallatin 				cum_len_next = 0;
1818aed8e389SAndrew Gallatin 				seglen = -cum_len;
1819aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1820aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1821aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1822aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1823aed8e389SAndrew Gallatin 			    }
1824aed8e389SAndrew Gallatin 
1825aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1826aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1827aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1828aed8e389SAndrew Gallatin 			req->pad = 0;
1829aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1830aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1831aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1832aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1833aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1834aed8e389SAndrew Gallatin 			low += seglen;
1835aed8e389SAndrew Gallatin 			len -= seglen;
1836aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1837aed8e389SAndrew Gallatin 			flags = flags_next;
1838aed8e389SAndrew Gallatin 			req++;
1839aed8e389SAndrew Gallatin 			cnt++;
1840aed8e389SAndrew Gallatin 			rdma_count++;
1841aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1842aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1843aed8e389SAndrew Gallatin 			else
1844aed8e389SAndrew Gallatin 				cksum_offset = 0;
1845adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1846aed8e389SAndrew Gallatin 				goto drop;
1847aed8e389SAndrew Gallatin 		}
1848aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1849aed8e389SAndrew Gallatin 		seg++;
1850aed8e389SAndrew Gallatin 	}
1851aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1852aed8e389SAndrew Gallatin 
1853aed8e389SAndrew Gallatin 	do {
1854aed8e389SAndrew Gallatin 		req--;
1855aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1856aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1857aed8e389SAndrew Gallatin 
1858aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1859aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1860aed8e389SAndrew Gallatin 	return;
1861aed8e389SAndrew Gallatin 
1862aed8e389SAndrew Gallatin drop:
1863e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1864aed8e389SAndrew Gallatin 	m_freem(m);
18651e413cf9SAndrew Gallatin 	ss->sc->ifp->if_oerrors++;
1866aed8e389SAndrew Gallatin 	if (!once) {
1867adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1868adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1869adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1870aed8e389SAndrew Gallatin 		once = 1;
1871aed8e389SAndrew Gallatin 	}
1872aed8e389SAndrew Gallatin 	return;
1873aed8e389SAndrew Gallatin 
1874aed8e389SAndrew Gallatin }
1875aed8e389SAndrew Gallatin 
187637d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
187737d89b0cSAndrew Gallatin 
187837d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1879c792928fSAndrew Gallatin /*
1880c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1881c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1882c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1883c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1884c792928fSAndrew Gallatin  */
1885c792928fSAndrew Gallatin static struct mbuf *
1886c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1887c792928fSAndrew Gallatin {
1888c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1889c792928fSAndrew Gallatin 
1890c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1891c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1892c792928fSAndrew Gallatin 		return NULL;
1893c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1894c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1895c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1896c792928fSAndrew Gallatin 			return NULL;
1897c792928fSAndrew Gallatin 	}
1898c792928fSAndrew Gallatin 	/*
1899c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
1900c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
1901c792928fSAndrew Gallatin 	 */
1902c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
1903c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
1904c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
1905c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
1906c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
1907c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
1908c792928fSAndrew Gallatin 	return m;
1909c792928fSAndrew Gallatin }
191037d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
1911c792928fSAndrew Gallatin 
1912aed8e389SAndrew Gallatin static void
19131e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
1914b2fc195eSAndrew Gallatin {
19151e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
1916b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1917b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1918b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1919b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
19201e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1921b2fc195eSAndrew Gallatin 	struct ip *ip;
1922c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
1923aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1924aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1925b2fc195eSAndrew Gallatin 
1926b2fc195eSAndrew Gallatin 
19271e413cf9SAndrew Gallatin 	sc = ss->sc;
1928b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
19291e413cf9SAndrew Gallatin 	tx = &ss->tx;
1930b2fc195eSAndrew Gallatin 
1931c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
193237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1933c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
1934c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
1935c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1936c792928fSAndrew Gallatin 			goto drop;
1937c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
1938c792928fSAndrew Gallatin 	}
193937d89b0cSAndrew Gallatin #endif
1940b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1941b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1942b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1943aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1944b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1945adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
1946b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1947b2fc195eSAndrew Gallatin 		   to defrag */
1948b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1949b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1950b2fc195eSAndrew Gallatin 			goto drop;
1951b2fc195eSAndrew Gallatin 		}
19521e413cf9SAndrew Gallatin 		ss->tx.defrag++;
1953b2fc195eSAndrew Gallatin 		m = m_tmp;
1954b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1955b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1956aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1957b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1958b2fc195eSAndrew Gallatin 	}
1959adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
1960aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1961aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1962b2fc195eSAndrew Gallatin 		goto drop;
1963b2fc195eSAndrew Gallatin 	}
1964b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1965b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
19665e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1967b2fc195eSAndrew Gallatin 
196837d89b0cSAndrew Gallatin #if IFCAP_TSO4
1969aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1970aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
19711e413cf9SAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, ip_off);
1972aed8e389SAndrew Gallatin 		return;
1973aed8e389SAndrew Gallatin 	}
197437d89b0cSAndrew Gallatin #endif
1975aed8e389SAndrew Gallatin 
1976b2fc195eSAndrew Gallatin 	req = tx->req_list;
1977b2fc195eSAndrew Gallatin 	cksum_offset = 0;
19785e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
19795e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1980b2fc195eSAndrew Gallatin 
1981b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1982b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1983aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
1984aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
1985c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1986c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
19871e413cf9SAndrew Gallatin 				   ss->scratch);
19881e413cf9SAndrew Gallatin 			ip = (struct ip *)(ss->scratch + ip_off);
1989aed8e389SAndrew Gallatin 		} else {
1990c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
1991aed8e389SAndrew Gallatin 		}
1992c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
1993b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
19945e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1995b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
19965e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1997aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
1998aed8e389SAndrew Gallatin 	} else {
1999aed8e389SAndrew Gallatin 		odd_flag = 0;
2000b2fc195eSAndrew Gallatin 	}
20015e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
20025e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2003b2fc195eSAndrew Gallatin 
2004b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2005b2fc195eSAndrew Gallatin 	cum_len = 0;
2006aed8e389SAndrew Gallatin 	seg = tx->seg_list;
20075e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2008b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2009b2fc195eSAndrew Gallatin 		req->addr_low =
20106d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2011b2fc195eSAndrew Gallatin 		req->addr_high =
20126d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2013b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2014b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2015b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2016b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2017b2fc195eSAndrew Gallatin 		else
2018b2fc195eSAndrew Gallatin 			cksum_offset = 0;
20195e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20205e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20215e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2022aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2023b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2024b2fc195eSAndrew Gallatin 		seg++;
2025b2fc195eSAndrew Gallatin 		req++;
2026b2fc195eSAndrew Gallatin 		req->flags = 0;
2027b2fc195eSAndrew Gallatin 	}
2028b2fc195eSAndrew Gallatin 	req--;
2029b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2030b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2031b2fc195eSAndrew Gallatin 		req++;
2032b2fc195eSAndrew Gallatin 		req->addr_low =
20336d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2034b2fc195eSAndrew Gallatin 		req->addr_high =
20356d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2036b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
20375e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
20385e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20395e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20405e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2041aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2042b2fc195eSAndrew Gallatin 		cnt++;
2043b2fc195eSAndrew Gallatin 	}
20445e7d8541SAndrew Gallatin 
20455e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
20465e7d8541SAndrew Gallatin #if 0
20475e7d8541SAndrew Gallatin 	/* print what the firmware will see */
20485e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
20495e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
20505e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
20515e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
20525e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
20535e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
20545e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
20555e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
20565e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
20575e7d8541SAndrew Gallatin 	}
20585e7d8541SAndrew Gallatin 	printf("--------------\n");
20595e7d8541SAndrew Gallatin #endif
20605e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
20616d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2062b2fc195eSAndrew Gallatin 	return;
2063b2fc195eSAndrew Gallatin 
2064b2fc195eSAndrew Gallatin drop:
2065b2fc195eSAndrew Gallatin 	m_freem(m);
2066b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
2067b2fc195eSAndrew Gallatin 	return;
2068b2fc195eSAndrew Gallatin }
2069b2fc195eSAndrew Gallatin 
2070b2fc195eSAndrew Gallatin 
20716d914a32SAndrew Gallatin 
20726d914a32SAndrew Gallatin 
20736d914a32SAndrew Gallatin static inline void
20741e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2075b2fc195eSAndrew Gallatin {
20761e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2077b2fc195eSAndrew Gallatin 	struct mbuf *m;
2078b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20791e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2080b2fc195eSAndrew Gallatin 
20811e413cf9SAndrew Gallatin 	sc = ss->sc;
2082b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20831e413cf9SAndrew Gallatin 	tx = &ss->tx;
2084adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
20856d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
20866d914a32SAndrew Gallatin 		if (m == NULL) {
20876d914a32SAndrew Gallatin 			return;
20886d914a32SAndrew Gallatin 		}
2089b2fc195eSAndrew Gallatin 		/* let BPF see it */
2090b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2091b2fc195eSAndrew Gallatin 
2092b2fc195eSAndrew Gallatin 		/* give it to the nic */
20931e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
20946d914a32SAndrew Gallatin 	}
20956d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2096a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2097b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2098adae7080SAndrew Gallatin 		tx->stall++;
2099a82c2581SAndrew Gallatin 	}
2100b2fc195eSAndrew Gallatin }
2101b2fc195eSAndrew Gallatin 
2102b2fc195eSAndrew Gallatin static void
21036d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2104b2fc195eSAndrew Gallatin {
21056d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
21061e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2107b2fc195eSAndrew Gallatin 
21081e413cf9SAndrew Gallatin 	/* only use the first slice for now */
21091e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
21101e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
21111e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
21121e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2113b2fc195eSAndrew Gallatin }
2114b2fc195eSAndrew Gallatin 
21155e7d8541SAndrew Gallatin /*
21165e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
21175e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
21185e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
21195e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
21205e7d8541SAndrew Gallatin  * in a burst
21215e7d8541SAndrew Gallatin  */
21225e7d8541SAndrew Gallatin static inline void
21235e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
21245e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
21255e7d8541SAndrew Gallatin {
21265e7d8541SAndrew Gallatin 	uint32_t low;
21275e7d8541SAndrew Gallatin 
21285e7d8541SAndrew Gallatin 	low = src->addr_low;
21295e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2130a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
213173c7c83fSAndrew Gallatin 	wmb();
2132a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
213373c7c83fSAndrew Gallatin 	wmb();
213440385a5fSAndrew Gallatin 	src->addr_low = low;
21355e7d8541SAndrew Gallatin 	dst->addr_low = low;
213673c7c83fSAndrew Gallatin 	wmb();
21375e7d8541SAndrew Gallatin }
21385e7d8541SAndrew Gallatin 
2139b2fc195eSAndrew Gallatin static int
21401e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2141b2fc195eSAndrew Gallatin {
2142b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2143b2fc195eSAndrew Gallatin 	struct mbuf *m;
21441e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2145b2fc195eSAndrew Gallatin 	int cnt, err;
2146b2fc195eSAndrew Gallatin 
2147b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
2148b2fc195eSAndrew Gallatin 	if (m == NULL) {
2149b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2150b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2151b2fc195eSAndrew Gallatin 		goto done;
2152b2fc195eSAndrew Gallatin 	}
2153b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2154b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2155b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2156b2fc195eSAndrew Gallatin 	if (err != 0) {
2157b2fc195eSAndrew Gallatin 		m_free(m);
2158b2fc195eSAndrew Gallatin 		goto done;
2159b2fc195eSAndrew Gallatin 	}
2160b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2161b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
21626d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2163b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
21646d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2165b2fc195eSAndrew Gallatin 
2166b2fc195eSAndrew Gallatin done:
2167adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2168adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2169b2fc195eSAndrew Gallatin 	return err;
2170b2fc195eSAndrew Gallatin }
2171b2fc195eSAndrew Gallatin 
2172b2fc195eSAndrew Gallatin static int
21731e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2174b2fc195eSAndrew Gallatin {
2175053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2176b2fc195eSAndrew Gallatin 	struct mbuf *m;
21771e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2178053e637fSAndrew Gallatin 	int cnt, err, i;
2179b2fc195eSAndrew Gallatin 
2180a0394e33SAndrew Gallatin 	if (rx->cl_size == MCLBYTES)
2181a0394e33SAndrew Gallatin 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2182a0394e33SAndrew Gallatin 	else
2183053e637fSAndrew Gallatin 		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2184b2fc195eSAndrew Gallatin 	if (m == NULL) {
2185b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2186b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2187b2fc195eSAndrew Gallatin 		goto done;
2188b2fc195eSAndrew Gallatin 	}
2189053e637fSAndrew Gallatin 	m->m_len = rx->cl_size;
2190b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2191053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2192b2fc195eSAndrew Gallatin 	if (err != 0) {
2193b2fc195eSAndrew Gallatin 		m_free(m);
2194b2fc195eSAndrew Gallatin 		goto done;
2195b2fc195eSAndrew Gallatin 	}
2196b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2197b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2198b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2199b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2200b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2201053e637fSAndrew Gallatin 
2202b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2203b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2204053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2205053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2206053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2207053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2208053e637fSAndrew Gallatin        }
2209b0f7b922SAndrew Gallatin #endif
2210b2fc195eSAndrew Gallatin 
2211b2fc195eSAndrew Gallatin done:
2212053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2213b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
22145e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
22155e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2216b2fc195eSAndrew Gallatin 		}
2217053e637fSAndrew Gallatin 		idx++;
2218053e637fSAndrew Gallatin 	}
2219b2fc195eSAndrew Gallatin 	return err;
2220b2fc195eSAndrew Gallatin }
2221b2fc195eSAndrew Gallatin 
22229b03b0f3SAndrew Gallatin /*
22239b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
22249b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
22259b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
22269b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2227053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2228053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
22299b03b0f3SAndrew Gallatin  */
22309b03b0f3SAndrew Gallatin 
2231053e637fSAndrew Gallatin static inline uint16_t
2232053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2233053e637fSAndrew Gallatin {
2234053e637fSAndrew Gallatin 	struct ether_header *eh;
2235053e637fSAndrew Gallatin 	struct ip *ip;
2236053e637fSAndrew Gallatin 	uint16_t c;
2237053e637fSAndrew Gallatin 
2238053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2239053e637fSAndrew Gallatin 
2240053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2241053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2242053e637fSAndrew Gallatin 		return 1;
2243053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2244053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2245053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2246053e637fSAndrew Gallatin 		return 1;
2247053e637fSAndrew Gallatin 
2248053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2249053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2250053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2251053e637fSAndrew Gallatin 	c ^= 0xffff;
2252053e637fSAndrew Gallatin 	return (c);
22535e7d8541SAndrew Gallatin }
2254053e637fSAndrew Gallatin 
2255c792928fSAndrew Gallatin static void
2256c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2257c792928fSAndrew Gallatin {
2258c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2259c792928fSAndrew Gallatin 	struct ether_header *eh;
2260c792928fSAndrew Gallatin 	uint32_t partial;
2261c792928fSAndrew Gallatin 
2262c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2263c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2264c792928fSAndrew Gallatin 
2265c792928fSAndrew Gallatin 	/*
2266c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2267c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2268c792928fSAndrew Gallatin 	 * header.
2269c792928fSAndrew Gallatin 	 */
2270c792928fSAndrew Gallatin 
2271c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2272c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2273c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2274c792928fSAndrew Gallatin 	(*csum) += ~partial;
2275c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2276c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2277c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2278c792928fSAndrew Gallatin 
2279c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2280c792928fSAndrew Gallatin 	   later consumers expect this */
2281c792928fSAndrew Gallatin 	*csum = htons(*csum);
2282c792928fSAndrew Gallatin 
2283c792928fSAndrew Gallatin 	/* save the tag */
228437d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2285c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
228637d89b0cSAndrew Gallatin #else
228737d89b0cSAndrew Gallatin 	{
228837d89b0cSAndrew Gallatin 		struct m_tag *mtag;
228937d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
229037d89b0cSAndrew Gallatin 				   M_NOWAIT);
229137d89b0cSAndrew Gallatin 		if (mtag == NULL)
229237d89b0cSAndrew Gallatin 			return;
229337d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
229437d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
229537d89b0cSAndrew Gallatin 	}
229637d89b0cSAndrew Gallatin 
229737d89b0cSAndrew Gallatin #endif
229837d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2299c792928fSAndrew Gallatin 
2300c792928fSAndrew Gallatin 	/*
2301c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2302c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2303c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2304c792928fSAndrew Gallatin 	 * type field is already in place.
2305c792928fSAndrew Gallatin 	 */
2306c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2307c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2308c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2309c792928fSAndrew Gallatin }
2310c792928fSAndrew Gallatin 
23115e7d8541SAndrew Gallatin 
23125e7d8541SAndrew Gallatin static inline void
23131e413cf9SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2314b2fc195eSAndrew Gallatin {
23151e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2316b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2317053e637fSAndrew Gallatin 	struct mbuf *m;
2318c792928fSAndrew Gallatin 	struct ether_header *eh;
23191e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2320053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2321b2fc195eSAndrew Gallatin 	int idx;
2322053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2323b2fc195eSAndrew Gallatin 
23241e413cf9SAndrew Gallatin 	sc = ss->sc;
2325b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23261e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2327b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2328053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2329b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2330b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2331b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
23321e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2333053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2334053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2335053e637fSAndrew Gallatin 		return;
2336b2fc195eSAndrew Gallatin 	}
2337053e637fSAndrew Gallatin 
2338b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2339b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2340b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2341b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2342b2fc195eSAndrew Gallatin 
2343b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2344b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2345b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2346b2fc195eSAndrew Gallatin 
2347053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2348053e637fSAndrew Gallatin 	 * aligned */
23495e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2350b2fc195eSAndrew Gallatin 
2351053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2352053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
23531e413cf9SAndrew Gallatin 	ss->ipackets++;
2354c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2355c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2356c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2357c792928fSAndrew Gallatin 	}
2358b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2359053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
23601e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2361b2fc195eSAndrew Gallatin 			return;
2362053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2363053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2364053e637fSAndrew Gallatin 		   checksum is good */
2365053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2366053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2367b2fc195eSAndrew Gallatin 	}
2368053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2369053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2370b2fc195eSAndrew Gallatin }
2371b2fc195eSAndrew Gallatin 
2372b2fc195eSAndrew Gallatin static inline void
23731e413cf9SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2374b2fc195eSAndrew Gallatin {
23751e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2376b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2377c792928fSAndrew Gallatin 	struct ether_header *eh;
2378b2fc195eSAndrew Gallatin 	struct mbuf *m;
23791e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2380b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2381b2fc195eSAndrew Gallatin 	int idx;
2382053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2383b2fc195eSAndrew Gallatin 
23841e413cf9SAndrew Gallatin 	sc = ss->sc;
2385b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23861e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2387b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2388b2fc195eSAndrew Gallatin 	rx->cnt++;
2389b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2390b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2391b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
23921e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2393b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2394b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2395b2fc195eSAndrew Gallatin 		return;
2396b2fc195eSAndrew Gallatin 	}
2397b2fc195eSAndrew Gallatin 
2398b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2399b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2400b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2401b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2402b2fc195eSAndrew Gallatin 
2403b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2404b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2405b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2406b2fc195eSAndrew Gallatin 
2407b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2408b2fc195eSAndrew Gallatin 	 * aligned */
24095e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2410b2fc195eSAndrew Gallatin 
24119b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
24129b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
24131e413cf9SAndrew Gallatin 	ss->ipackets++;
2414c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2415c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2416c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2417c792928fSAndrew Gallatin 	}
2418b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2419053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
24201e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2421053e637fSAndrew Gallatin 			return;
2422053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2423053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2424053e637fSAndrew Gallatin 		   checksum is good */
2425053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2426053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2427053e637fSAndrew Gallatin 	}
2428b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2429b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2430b2fc195eSAndrew Gallatin }
2431b2fc195eSAndrew Gallatin 
2432b2fc195eSAndrew Gallatin static inline void
24331e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
24345e7d8541SAndrew Gallatin {
24351e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
2436053e637fSAndrew Gallatin 	struct lro_entry *lro;
24375e7d8541SAndrew Gallatin 	int limit = 0;
24385e7d8541SAndrew Gallatin 	uint16_t length;
24395e7d8541SAndrew Gallatin 	uint16_t checksum;
24405e7d8541SAndrew Gallatin 
24415e7d8541SAndrew Gallatin 
24425e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
24435e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
24445e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2445053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2446b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
24471e413cf9SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum);
24485e7d8541SAndrew Gallatin 		else
24491e413cf9SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum);
24505e7d8541SAndrew Gallatin 		rx_done->cnt++;
2451adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
24525e7d8541SAndrew Gallatin 
24535e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2454f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
24555e7d8541SAndrew Gallatin 			break;
2456053e637fSAndrew Gallatin 	}
24571e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_active)) {
24581e413cf9SAndrew Gallatin 		lro = SLIST_FIRST(&ss->lro_active);
24591e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
24601e413cf9SAndrew Gallatin 		mxge_lro_flush(ss, lro);
24615e7d8541SAndrew Gallatin 	}
24625e7d8541SAndrew Gallatin }
24635e7d8541SAndrew Gallatin 
24645e7d8541SAndrew Gallatin 
24655e7d8541SAndrew Gallatin static inline void
24661e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2467b2fc195eSAndrew Gallatin {
2468b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
24691e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2470b2fc195eSAndrew Gallatin 	struct mbuf *m;
2471b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2472f616ebc7SAndrew Gallatin 	int idx;
2473b2fc195eSAndrew Gallatin 
24741e413cf9SAndrew Gallatin 	tx = &ss->tx;
24751e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
24765e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2477b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2478b2fc195eSAndrew Gallatin 		tx->done++;
2479b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2480b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2481b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2482b2fc195eSAndrew Gallatin 		if (m != NULL) {
2483b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2484b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2485b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2486b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2487b2fc195eSAndrew Gallatin 			m_freem(m);
2488b2fc195eSAndrew Gallatin 		}
24895e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
24905e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
24915e7d8541SAndrew Gallatin 			tx->pkt_done++;
24925e7d8541SAndrew Gallatin 		}
2493b2fc195eSAndrew Gallatin 	}
2494b2fc195eSAndrew Gallatin 
2495b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2496b2fc195eSAndrew Gallatin            its OK to send packets */
2497b2fc195eSAndrew Gallatin 
2498b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2499b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
25001e413cf9SAndrew Gallatin 		mtx_lock(&ss->tx.mtx);
2501b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
25021e413cf9SAndrew Gallatin 		ss->tx.wake++;
25031e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
25041e413cf9SAndrew Gallatin 		mtx_unlock(&ss->tx.mtx);
2505b2fc195eSAndrew Gallatin 	}
2506b2fc195eSAndrew Gallatin }
2507b2fc195eSAndrew Gallatin 
2508c587e59fSAndrew Gallatin static struct mxge_media_type mxge_media_types[] =
2509c587e59fSAndrew Gallatin {
2510c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2511c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2512c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2513c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
2514c587e59fSAndrew Gallatin 	{0,		(1 << 4),	"10GBASE-LRM"},
2515c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2516c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2517c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2518c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2519c587e59fSAndrew Gallatin };
2520c587e59fSAndrew Gallatin 
2521c587e59fSAndrew Gallatin static void
2522c587e59fSAndrew Gallatin mxge_set_media(mxge_softc_t *sc, int type)
2523c587e59fSAndrew Gallatin {
2524c587e59fSAndrew Gallatin 	sc->media_flags |= type;
2525c587e59fSAndrew Gallatin 	ifmedia_add(&sc->media, sc->media_flags, 0, NULL);
2526c587e59fSAndrew Gallatin 	ifmedia_set(&sc->media, sc->media_flags);
2527c587e59fSAndrew Gallatin }
2528c587e59fSAndrew Gallatin 
2529c587e59fSAndrew Gallatin 
2530c587e59fSAndrew Gallatin /*
2531c587e59fSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2532c587e59fSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2533c587e59fSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2534c587e59fSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2535c587e59fSAndrew Gallatin  * than in the interrupt handler itself.   This need only be done
2536c587e59fSAndrew Gallatin  * once, not each time the link is up.
2537c587e59fSAndrew Gallatin  */
2538c587e59fSAndrew Gallatin static void
2539c587e59fSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2540c587e59fSAndrew Gallatin {
2541c587e59fSAndrew Gallatin 	mxge_cmd_t cmd;
2542c587e59fSAndrew Gallatin 	char *ptr;
2543c587e59fSAndrew Gallatin 	int i, err, ms;
2544c587e59fSAndrew Gallatin 
2545c587e59fSAndrew Gallatin 	sc->need_media_probe = 0;
2546c587e59fSAndrew Gallatin 
2547c587e59fSAndrew Gallatin 	/* if we've already set a media type, we're done */
2548c587e59fSAndrew Gallatin 	if (sc->media_flags  != (IFM_ETHER | IFM_AUTO))
2549c587e59fSAndrew Gallatin 		return;
2550c587e59fSAndrew Gallatin 
2551c587e59fSAndrew Gallatin 	/*
2552c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2553c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2554c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2555c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2556c587e59fSAndrew Gallatin 	 */
2557c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2558c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2559c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2560c587e59fSAndrew Gallatin 	}
2561c587e59fSAndrew Gallatin 
2562c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
256337d89b0cSAndrew Gallatin 		ptr = index(ptr, '-');
2564c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2565c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2566c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2567c587e59fSAndrew Gallatin 			return;
2568c587e59fSAndrew Gallatin 		}
2569c587e59fSAndrew Gallatin 	}
2570c587e59fSAndrew Gallatin 	if (*ptr == 'C') {
2571c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2572c587e59fSAndrew Gallatin 		return;
2573c587e59fSAndrew Gallatin 	}
2574c587e59fSAndrew Gallatin 	else if (*ptr == 'Q') {
2575c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2576c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2577c587e59fSAndrew Gallatin 		return;
2578c587e59fSAndrew Gallatin 	}
2579c587e59fSAndrew Gallatin 
2580c587e59fSAndrew Gallatin 	if (*ptr != 'R') {
2581c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2582c587e59fSAndrew Gallatin 		return;
2583c587e59fSAndrew Gallatin 	}
2584c587e59fSAndrew Gallatin 
2585c587e59fSAndrew Gallatin 	/*
2586c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2587c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2588c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2589c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2590c587e59fSAndrew Gallatin 	 * a millisecond
2591c587e59fSAndrew Gallatin 	 */
2592c587e59fSAndrew Gallatin 
2593c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
2594c587e59fSAndrew Gallatin 	cmd.data1 = MXGE_XFP_COMPLIANCE_BYTE; /* the byte we want */
2595c587e59fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_XFP_I2C_READ, &cmd);
2596c587e59fSAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_XFP_FAILURE) {
2597c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2598c587e59fSAndrew Gallatin 	}
2599c587e59fSAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_XFP_ABSENT) {
2600c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Type R with no XFP!?!?\n");
2601c587e59fSAndrew Gallatin 	}
2602c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2603c587e59fSAndrew Gallatin 		return;
2604c587e59fSAndrew Gallatin 	}
2605c587e59fSAndrew Gallatin 
2606c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
2607c587e59fSAndrew Gallatin 	cmd.data0 = MXGE_XFP_COMPLIANCE_BYTE;
2608c587e59fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_XFP_BYTE, &cmd);
2609c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2610c587e59fSAndrew Gallatin 		DELAY(1000);
2611c587e59fSAndrew Gallatin 		cmd.data0 = MXGE_XFP_COMPLIANCE_BYTE;
2612c587e59fSAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_XFP_BYTE, &cmd);
2613c587e59fSAndrew Gallatin 	}
2614c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2615c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP (%d, %dms)\n",
2616c587e59fSAndrew Gallatin 			      err, ms);
2617c587e59fSAndrew Gallatin 		return;
2618c587e59fSAndrew Gallatin 	}
2619c587e59fSAndrew Gallatin 
2620c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2621c587e59fSAndrew Gallatin 		if (mxge_verbose)
2622c587e59fSAndrew Gallatin 			device_printf(sc->dev, "XFP:%s\n",
2623c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2624c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2625c587e59fSAndrew Gallatin 		return;
2626c587e59fSAndrew Gallatin 	}
2627c587e59fSAndrew Gallatin 	for (i = 1;
2628c587e59fSAndrew Gallatin 	     i < sizeof (mxge_media_types) / sizeof (mxge_media_types[0]);
2629c587e59fSAndrew Gallatin 	     i++) {
2630c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2631c587e59fSAndrew Gallatin 			if (mxge_verbose)
2632c587e59fSAndrew Gallatin 				device_printf(sc->dev, "XFP:%s\n",
2633c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2634c587e59fSAndrew Gallatin 
2635c587e59fSAndrew Gallatin 			mxge_set_media(sc, mxge_media_types[i].flag);
2636c587e59fSAndrew Gallatin 			return;
2637c587e59fSAndrew Gallatin 		}
2638c587e59fSAndrew Gallatin 	}
2639c587e59fSAndrew Gallatin 	device_printf(sc->dev, "XFP media 0x%x unknown\n", cmd.data0);
2640c587e59fSAndrew Gallatin 
2641c587e59fSAndrew Gallatin 	return;
2642c587e59fSAndrew Gallatin }
2643c587e59fSAndrew Gallatin 
2644b2fc195eSAndrew Gallatin static void
26456d87a65dSAndrew Gallatin mxge_intr(void *arg)
2646b2fc195eSAndrew Gallatin {
26471e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
26481e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
26491e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
26501e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
26511e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26525e7d8541SAndrew Gallatin 	uint32_t send_done_count;
26535e7d8541SAndrew Gallatin 	uint8_t valid;
2654b2fc195eSAndrew Gallatin 
2655b2fc195eSAndrew Gallatin 
26561e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
26571e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
26581e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
26591e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
26601e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
26611e413cf9SAndrew Gallatin 		return;
26621e413cf9SAndrew Gallatin 	}
26631e413cf9SAndrew Gallatin 
26645e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
26655e7d8541SAndrew Gallatin 	if (!stats->valid) {
26665e7d8541SAndrew Gallatin 		return;
2667b2fc195eSAndrew Gallatin 	}
26685e7d8541SAndrew Gallatin 	valid = stats->valid;
2669b2fc195eSAndrew Gallatin 
267091ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
26715e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
26725e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
26735e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
26745e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
26755e7d8541SAndrew Gallatin 			stats->valid = 0;
2676dc8731d4SAndrew Gallatin 	} else {
2677dc8731d4SAndrew Gallatin 		stats->valid = 0;
2678dc8731d4SAndrew Gallatin 	}
2679dc8731d4SAndrew Gallatin 
2680dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
26815e7d8541SAndrew Gallatin 	do {
26825e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
26835e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
26845e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
26855e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
26861e413cf9SAndrew Gallatin 			mxge_tx_done(ss, (int)send_done_count);
26871e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
26885e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2689b2fc195eSAndrew Gallatin 		}
269091ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
269173c7c83fSAndrew Gallatin 			wmb();
26925e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2693b2fc195eSAndrew Gallatin 
26945e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
26955e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
26965e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2697b2fc195eSAndrew Gallatin 			if (sc->link_state) {
26985e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
26995e7d8541SAndrew Gallatin 				if (mxge_verbose)
27005e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2701b2fc195eSAndrew Gallatin 			} else {
27025e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
27035e7d8541SAndrew Gallatin 				if (mxge_verbose)
27045e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2705b2fc195eSAndrew Gallatin 			}
2706c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
2707b2fc195eSAndrew Gallatin 		}
2708b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
27091e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
2710b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
27111e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
27125e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
27135e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
27145e7d8541SAndrew Gallatin 		}
2715c587e59fSAndrew Gallatin 
2716c587e59fSAndrew Gallatin 		if (stats->link_down) {
27175e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
2718c587e59fSAndrew Gallatin 			sc->link_state = 0;
2719c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
2720c587e59fSAndrew Gallatin 		}
2721b2fc195eSAndrew Gallatin 	}
2722b2fc195eSAndrew Gallatin 
27235e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
27245e7d8541SAndrew Gallatin 	if (valid & 0x1)
27251e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
27261e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
2727b2fc195eSAndrew Gallatin }
2728b2fc195eSAndrew Gallatin 
2729b2fc195eSAndrew Gallatin static void
27306d87a65dSAndrew Gallatin mxge_init(void *arg)
2731b2fc195eSAndrew Gallatin {
2732b2fc195eSAndrew Gallatin }
2733b2fc195eSAndrew Gallatin 
2734b2fc195eSAndrew Gallatin 
2735b2fc195eSAndrew Gallatin 
2736b2fc195eSAndrew Gallatin static void
27371e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
27381e413cf9SAndrew Gallatin {
27391e413cf9SAndrew Gallatin 	struct lro_entry *lro_entry;
27401e413cf9SAndrew Gallatin 	int i;
27411e413cf9SAndrew Gallatin 
27421e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_free)) {
27431e413cf9SAndrew Gallatin 		lro_entry = SLIST_FIRST(&ss->lro_free);
27441e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
27451e413cf9SAndrew Gallatin 		free(lro_entry, M_DEVBUF);
27461e413cf9SAndrew Gallatin 	}
27471e413cf9SAndrew Gallatin 
27481e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
27491e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
27501e413cf9SAndrew Gallatin 			continue;
27511e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
27521e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
27531e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
27541e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
27551e413cf9SAndrew Gallatin 	}
27561e413cf9SAndrew Gallatin 
27571e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
27581e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
27591e413cf9SAndrew Gallatin 			continue;
27601e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
27611e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
27621e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
27631e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
27641e413cf9SAndrew Gallatin 	}
27651e413cf9SAndrew Gallatin 
27661e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
27671e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
27681e413cf9SAndrew Gallatin 		return;
27691e413cf9SAndrew Gallatin 
27701e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
27711e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
27721e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
27731e413cf9SAndrew Gallatin 			continue;
27741e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
27751e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
27761e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
27771e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
27781e413cf9SAndrew Gallatin 	}
27791e413cf9SAndrew Gallatin }
27801e413cf9SAndrew Gallatin 
27811e413cf9SAndrew Gallatin static void
27826d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2783b2fc195eSAndrew Gallatin {
27841e413cf9SAndrew Gallatin 	int slice;
27851e413cf9SAndrew Gallatin 
27861e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
27871e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
27881e413cf9SAndrew Gallatin }
27891e413cf9SAndrew Gallatin 
27901e413cf9SAndrew Gallatin static void
27911e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
27921e413cf9SAndrew Gallatin {
2793b2fc195eSAndrew Gallatin 	int i;
2794b2fc195eSAndrew Gallatin 
2795b2fc195eSAndrew Gallatin 
27961e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
27971e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
27981e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
2799b2fc195eSAndrew Gallatin 
28001e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
28011e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
28021e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
28031e413cf9SAndrew Gallatin 
28041e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
28051e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
28061e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
28071e413cf9SAndrew Gallatin 
28081e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
28091e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
28101e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
28111e413cf9SAndrew Gallatin 
28121e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
28131e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
28141e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
28151e413cf9SAndrew Gallatin 
28161e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
28171e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
28181e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
28191e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
28201e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
2821b2fc195eSAndrew Gallatin 			}
28221e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
28231e413cf9SAndrew Gallatin 		}
28241e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
28251e413cf9SAndrew Gallatin 	}
28261e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
28271e413cf9SAndrew Gallatin 
28281e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
28291e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
28301e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
28311e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
28321e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
28331e413cf9SAndrew Gallatin 			}
28341e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
28351e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
28361e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
28371e413cf9SAndrew Gallatin 		}
28381e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
28391e413cf9SAndrew Gallatin 	}
28401e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
28411e413cf9SAndrew Gallatin 
28421e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
28431e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
28441e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
28451e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
28461e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
28471e413cf9SAndrew Gallatin 			}
28481e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
28491e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
28501e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
28511e413cf9SAndrew Gallatin 		}
28521e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
28531e413cf9SAndrew Gallatin 	}
28541e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
2855b2fc195eSAndrew Gallatin }
2856b2fc195eSAndrew Gallatin 
2857b2fc195eSAndrew Gallatin static void
28586d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2859b2fc195eSAndrew Gallatin {
28601e413cf9SAndrew Gallatin 	int slice;
2861b2fc195eSAndrew Gallatin 
28621e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
28631e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
2864c2657176SAndrew Gallatin }
2865b2fc195eSAndrew Gallatin 
2866b2fc195eSAndrew Gallatin static int
28671e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
28681e413cf9SAndrew Gallatin 		       int tx_ring_entries)
2869b2fc195eSAndrew Gallatin {
28701e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
28711e413cf9SAndrew Gallatin 	size_t bytes;
28721e413cf9SAndrew Gallatin 	int err, i;
2873b2fc195eSAndrew Gallatin 
2874b2fc195eSAndrew Gallatin 	err = ENOMEM;
2875b2fc195eSAndrew Gallatin 
28761e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
2877adae7080SAndrew Gallatin 
28781e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
28791e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
2880aed8e389SAndrew Gallatin 
2881b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
28821e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
28831e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28841e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow == NULL)
28851e413cf9SAndrew Gallatin 		return err;;
2886b2fc195eSAndrew Gallatin 
28871e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
28881e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28891e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow == NULL)
28901e413cf9SAndrew Gallatin 		return err;;
2891b2fc195eSAndrew Gallatin 
28921e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
28931e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
28941e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28951e413cf9SAndrew Gallatin 	if (ss->rx_small.info == NULL)
28961e413cf9SAndrew Gallatin 		return err;;
2897b2fc195eSAndrew Gallatin 
28981e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
28991e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29001e413cf9SAndrew Gallatin 	if (ss->rx_big.info == NULL)
29011e413cf9SAndrew Gallatin 		return err;;
2902b2fc195eSAndrew Gallatin 
29031e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
2904b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2905b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2906b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2907b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2908b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2909b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2910b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2911b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2912b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2913b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2914b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
29151e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
2916b2fc195eSAndrew Gallatin 	if (err != 0) {
2917b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2918b2fc195eSAndrew Gallatin 			      err);
29191e413cf9SAndrew Gallatin 		return err;;
2920b2fc195eSAndrew Gallatin 	}
2921b2fc195eSAndrew Gallatin 
2922b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2923b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2924b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2925b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2926b0f7b922SAndrew Gallatin #else
2927b0f7b922SAndrew Gallatin 				 0,			/* boundary */
2928b0f7b922SAndrew Gallatin #endif
2929b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2930b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2931b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2932053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
2933b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2934053e637fSAndrew Gallatin 				 3,			/* num segs */
2935b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
2936b0f7b922SAndrew Gallatin #else
2937b0f7b922SAndrew Gallatin 				 1,			/* num segs */
2938b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
2939b0f7b922SAndrew Gallatin #endif
2940b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2941b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
29421e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
2943b2fc195eSAndrew Gallatin 	if (err != 0) {
2944b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2945b2fc195eSAndrew Gallatin 			      err);
29461e413cf9SAndrew Gallatin 		return err;;
2947b2fc195eSAndrew Gallatin 	}
29481e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
29491e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
29501e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
2951b2fc195eSAndrew Gallatin 		if (err != 0) {
2952b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2953b2fc195eSAndrew Gallatin 				      err);
29541e413cf9SAndrew Gallatin 			return err;;
2955b2fc195eSAndrew Gallatin 		}
2956b2fc195eSAndrew Gallatin 	}
29571e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
29581e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
2959b2fc195eSAndrew Gallatin 	if (err != 0) {
2960b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2961b2fc195eSAndrew Gallatin 			      err);
29621e413cf9SAndrew Gallatin 		return err;;
2963b2fc195eSAndrew Gallatin 	}
2964b2fc195eSAndrew Gallatin 
29651e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
29661e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
29671e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
2968b2fc195eSAndrew Gallatin 		if (err != 0) {
2969b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2970b2fc195eSAndrew Gallatin 				      err);
29711e413cf9SAndrew Gallatin 			return err;;
2972b2fc195eSAndrew Gallatin 		}
2973b2fc195eSAndrew Gallatin 	}
29741e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
29751e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
2976b2fc195eSAndrew Gallatin 	if (err != 0) {
2977b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2978b2fc195eSAndrew Gallatin 			      err);
29791e413cf9SAndrew Gallatin 		return err;;
29801e413cf9SAndrew Gallatin 	}
29811e413cf9SAndrew Gallatin 
29821e413cf9SAndrew Gallatin 	/* now allocate TX resouces */
29831e413cf9SAndrew Gallatin 
29841e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
29851e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
29861e413cf9SAndrew Gallatin 		return 0;
29871e413cf9SAndrew Gallatin 
29881e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
29891e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
29901e413cf9SAndrew Gallatin 
29911e413cf9SAndrew Gallatin 
29921e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
29931e413cf9SAndrew Gallatin 	bytes = 8 +
29941e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
29951e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
29961e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes == NULL)
29971e413cf9SAndrew Gallatin 		return err;;
29981e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
29991e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
30001e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
30011e413cf9SAndrew Gallatin 
30021e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
30031e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
30041e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
30051e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
30061e413cf9SAndrew Gallatin 	if (ss->tx.seg_list == NULL)
30071e413cf9SAndrew Gallatin 		return err;;
30081e413cf9SAndrew Gallatin 
30091e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
30101e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
30111e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30121e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30131e413cf9SAndrew Gallatin 		return err;;
30141e413cf9SAndrew Gallatin 
30151e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
30161e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30171e413cf9SAndrew Gallatin 				 1,			/* alignment */
30181e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
30191e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
30201e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
30211e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
30221e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
30231e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
30241e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
30251e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
30261e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
30271e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
30281e413cf9SAndrew Gallatin 
30291e413cf9SAndrew Gallatin 	if (err != 0) {
30301e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
30311e413cf9SAndrew Gallatin 			      err);
30321e413cf9SAndrew Gallatin 		return err;;
30331e413cf9SAndrew Gallatin 	}
30341e413cf9SAndrew Gallatin 
30351e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
30361e413cf9SAndrew Gallatin 	   in the ring */
30371e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30381e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
30391e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
30401e413cf9SAndrew Gallatin 		if (err != 0) {
30411e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
30421e413cf9SAndrew Gallatin 				      err);
30431e413cf9SAndrew Gallatin 			return err;;
30441e413cf9SAndrew Gallatin 		}
3045b2fc195eSAndrew Gallatin 	}
3046b2fc195eSAndrew Gallatin 	return 0;
3047b2fc195eSAndrew Gallatin 
3048b2fc195eSAndrew Gallatin }
3049b2fc195eSAndrew Gallatin 
30501e413cf9SAndrew Gallatin static int
30511e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
30521e413cf9SAndrew Gallatin {
30531e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
30541e413cf9SAndrew Gallatin 	int tx_ring_size;
30551e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
30561e413cf9SAndrew Gallatin 	int err, slice;
30571e413cf9SAndrew Gallatin 
30581e413cf9SAndrew Gallatin 	/* get ring sizes */
30591e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
30601e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
30611e413cf9SAndrew Gallatin 	if (err != 0) {
30621e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
30631e413cf9SAndrew Gallatin 		goto abort;
30641e413cf9SAndrew Gallatin 	}
30651e413cf9SAndrew Gallatin 
30661e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
30671e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
30681e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
30691e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
30701e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
30711e413cf9SAndrew Gallatin 
30721e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
30731e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
30741e413cf9SAndrew Gallatin 					     rx_ring_entries,
30751e413cf9SAndrew Gallatin 					     tx_ring_entries);
30761e413cf9SAndrew Gallatin 		if (err != 0)
30771e413cf9SAndrew Gallatin 			goto abort;
30781e413cf9SAndrew Gallatin 	}
30791e413cf9SAndrew Gallatin 	return 0;
30801e413cf9SAndrew Gallatin 
30811e413cf9SAndrew Gallatin abort:
30821e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
30831e413cf9SAndrew Gallatin 	return err;
30841e413cf9SAndrew Gallatin 
30851e413cf9SAndrew Gallatin }
30861e413cf9SAndrew Gallatin 
30871e413cf9SAndrew Gallatin 
3088053e637fSAndrew Gallatin static void
3089053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3090053e637fSAndrew Gallatin {
3091c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3092053e637fSAndrew Gallatin 
3093053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3094053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3095053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3096053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3097053e637fSAndrew Gallatin 		*nbufs = 1;
3098053e637fSAndrew Gallatin 		return;
3099053e637fSAndrew Gallatin 	}
3100053e637fSAndrew Gallatin 
3101053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3102053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3103053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3104053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3105053e637fSAndrew Gallatin 		*nbufs = 1;
3106053e637fSAndrew Gallatin 		return;
3107053e637fSAndrew Gallatin 	}
3108b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3109053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3110053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3111053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3112053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3113053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3114053e637fSAndrew Gallatin 	if (*nbufs == 3)
3115053e637fSAndrew Gallatin 		*nbufs = 4;
3116b0f7b922SAndrew Gallatin #else
3117b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3118b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3119b0f7b922SAndrew Gallatin 	*nbufs = 1;
3120b0f7b922SAndrew Gallatin #endif
3121053e637fSAndrew Gallatin }
3122053e637fSAndrew Gallatin 
3123b2fc195eSAndrew Gallatin static int
31241e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3125b2fc195eSAndrew Gallatin {
31261e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
31276d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3128b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
3129053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
31301e413cf9SAndrew Gallatin 	int err, i, slice;
3131b2fc195eSAndrew Gallatin 
31321e413cf9SAndrew Gallatin 
31331e413cf9SAndrew Gallatin 	sc = ss->sc;
31341e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
31351e413cf9SAndrew Gallatin 
31361e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_free);
31371e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_active);
3138053e637fSAndrew Gallatin 
3139053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
3140053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
31411e413cf9SAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF,
31421e413cf9SAndrew Gallatin 			       M_NOWAIT | M_ZERO);
3143053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
3144053e637fSAndrew Gallatin 			sc->lro_cnt = i;
3145053e637fSAndrew Gallatin 			break;
3146053e637fSAndrew Gallatin 		}
31471e413cf9SAndrew Gallatin 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
3148053e637fSAndrew Gallatin 	}
31491e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
31501e413cf9SAndrew Gallatin 
31511e413cf9SAndrew Gallatin 	err = 0;
31521e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
31531e413cf9SAndrew Gallatin 	if (slice == 0) {
31541e413cf9SAndrew Gallatin 		cmd.data0 = slice;
31551e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
31561e413cf9SAndrew Gallatin 		ss->tx.lanai =
31571e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
31581e413cf9SAndrew Gallatin 	}
31591e413cf9SAndrew Gallatin 	cmd.data0 = slice;
31601e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
31611e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
31621e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
31631e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
31641e413cf9SAndrew Gallatin 	cmd.data0 = slice;
31651e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
31661e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
31671e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
31681e413cf9SAndrew Gallatin 
31691e413cf9SAndrew Gallatin 	if (err != 0) {
31701e413cf9SAndrew Gallatin 		device_printf(sc->dev,
31711e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
31721e413cf9SAndrew Gallatin 		return EIO;
31731e413cf9SAndrew Gallatin 	}
31741e413cf9SAndrew Gallatin 
31751e413cf9SAndrew Gallatin 	/* stock receive rings */
31761e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
31771e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
31781e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
31791e413cf9SAndrew Gallatin 		if (err) {
31801e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
31811e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
31821e413cf9SAndrew Gallatin 			return ENOMEM;
31831e413cf9SAndrew Gallatin 		}
31841e413cf9SAndrew Gallatin 	}
31851e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
31861e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
31871e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
31881e413cf9SAndrew Gallatin 	}
31891e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
31901e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
31911e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
31921e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
31931e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
31941e413cf9SAndrew Gallatin 		if (err) {
31951e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
31961e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
31971e413cf9SAndrew Gallatin 			return ENOMEM;
31981e413cf9SAndrew Gallatin 		}
31991e413cf9SAndrew Gallatin 	}
32001e413cf9SAndrew Gallatin 	return 0;
32011e413cf9SAndrew Gallatin }
32021e413cf9SAndrew Gallatin 
32031e413cf9SAndrew Gallatin static int
32041e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
32051e413cf9SAndrew Gallatin {
32061e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
32071e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
32081e413cf9SAndrew Gallatin 	bus_addr_t bus;
32091e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3210b2fc195eSAndrew Gallatin 
32117d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
32127d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
32137d542e2dSAndrew Gallatin 
3214adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3215b2fc195eSAndrew Gallatin 	if (err != 0) {
3216b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3217b2fc195eSAndrew Gallatin 		return EIO;
3218b2fc195eSAndrew Gallatin 	}
3219b2fc195eSAndrew Gallatin 
32201e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
32211e413cf9SAndrew Gallatin 		/* setup the indirection table */
32221e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
32231e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
32241e413cf9SAndrew Gallatin 				    &cmd);
3225b2fc195eSAndrew Gallatin 
32261e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
32271e413cf9SAndrew Gallatin 				     &cmd);
32281e413cf9SAndrew Gallatin 		if (err != 0) {
32291e413cf9SAndrew Gallatin 			device_printf(sc->dev,
32301e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
32311e413cf9SAndrew Gallatin 			return err;
32321e413cf9SAndrew Gallatin 		}
32331e413cf9SAndrew Gallatin 
32341e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
32351e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
32361e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
32371e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
32381e413cf9SAndrew Gallatin 
32391e413cf9SAndrew Gallatin 		cmd.data0 = 1;
32401e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
32411e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
32421e413cf9SAndrew Gallatin 		if (err != 0) {
32431e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
32441e413cf9SAndrew Gallatin 			return err;
32451e413cf9SAndrew Gallatin 		}
32461e413cf9SAndrew Gallatin 	}
32471e413cf9SAndrew Gallatin 
32481e413cf9SAndrew Gallatin 
32491e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
32501e413cf9SAndrew Gallatin 
32511e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3252053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3253053e637fSAndrew Gallatin 			    &cmd);
3254053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3255053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
32561e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3257053e637fSAndrew Gallatin 		device_printf(sc->dev,
3258053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
32591e413cf9SAndrew Gallatin 			      nbufs);
3260053e637fSAndrew Gallatin 		return EIO;
3261053e637fSAndrew Gallatin 	}
3262b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3263b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3264b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3265c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
32665e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3267b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
32685e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3269b2fc195eSAndrew Gallatin 			     &cmd);
3270053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
32715e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
32720fa7f681SAndrew Gallatin 
32730fa7f681SAndrew Gallatin 	if (err != 0) {
32740fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
32750fa7f681SAndrew Gallatin 		goto abort;
32760fa7f681SAndrew Gallatin 	}
32770fa7f681SAndrew Gallatin 
3278b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
32791e413cf9SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->ss->fw_stats_dma.bus_addr);
32801e413cf9SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->ss->fw_stats_dma.bus_addr);
32810fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
32820fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
32830fa7f681SAndrew Gallatin 
32840fa7f681SAndrew Gallatin 	if (err != 0) {
32851e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
32860fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
32870fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
32880fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
32890fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
32900fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
32910fa7f681SAndrew Gallatin 				    &cmd);
32920fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
32930fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
32940fa7f681SAndrew Gallatin 	} else {
32950fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
32960fa7f681SAndrew Gallatin 	}
3297b2fc195eSAndrew Gallatin 
3298b2fc195eSAndrew Gallatin 	if (err != 0) {
3299b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3300b2fc195eSAndrew Gallatin 		goto abort;
3301b2fc195eSAndrew Gallatin 	}
3302b2fc195eSAndrew Gallatin 
33031e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33041e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
33051e413cf9SAndrew Gallatin 		if (err != 0) {
33061e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
33071e413cf9SAndrew Gallatin 				      slice);
33081e413cf9SAndrew Gallatin 			goto abort;
33091e413cf9SAndrew Gallatin 		}
33101e413cf9SAndrew Gallatin 	}
33111e413cf9SAndrew Gallatin 
3312b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
33135e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3314b2fc195eSAndrew Gallatin 	if (err) {
3315b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3316b2fc195eSAndrew Gallatin 		goto abort;
3317b2fc195eSAndrew Gallatin 	}
3318b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3319b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3320e749ef6bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3321b2fc195eSAndrew Gallatin 
3322b2fc195eSAndrew Gallatin 	return 0;
3323b2fc195eSAndrew Gallatin 
3324b2fc195eSAndrew Gallatin 
3325b2fc195eSAndrew Gallatin abort:
33266d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3327a98d6cd7SAndrew Gallatin 
3328b2fc195eSAndrew Gallatin 	return err;
3329b2fc195eSAndrew Gallatin }
3330b2fc195eSAndrew Gallatin 
3331b2fc195eSAndrew Gallatin static int
33326d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
3333b2fc195eSAndrew Gallatin {
33346d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3335b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3336b2fc195eSAndrew Gallatin 
3337e749ef6bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3338b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3339b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
334073c7c83fSAndrew Gallatin 	wmb();
33415e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3342b2fc195eSAndrew Gallatin 	if (err) {
3343b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
3344b2fc195eSAndrew Gallatin 	}
3345b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3346b2fc195eSAndrew Gallatin 		/* wait for down irq */
3347dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
3348b2fc195eSAndrew Gallatin 	}
334973c7c83fSAndrew Gallatin 	wmb();
3350b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3351b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
3352b2fc195eSAndrew Gallatin 	}
3353a98d6cd7SAndrew Gallatin 
33546d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3355a98d6cd7SAndrew Gallatin 
3356b2fc195eSAndrew Gallatin 	return 0;
3357b2fc195eSAndrew Gallatin }
3358b2fc195eSAndrew Gallatin 
3359dce01b9bSAndrew Gallatin static void
3360dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3361dce01b9bSAndrew Gallatin {
3362dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3363dce01b9bSAndrew Gallatin 	int reg;
3364dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
3365dce01b9bSAndrew Gallatin 
3366dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
3367dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
3368dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3369dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3370dce01b9bSAndrew Gallatin 
3371dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
3372dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
3373dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
3374dce01b9bSAndrew Gallatin 	}
3375dce01b9bSAndrew Gallatin 
3376dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3377dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3378dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
3379dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
3380dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
3381dce01b9bSAndrew Gallatin }
3382dce01b9bSAndrew Gallatin 
3383dce01b9bSAndrew Gallatin static uint32_t
3384dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3385dce01b9bSAndrew Gallatin {
3386dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3387dce01b9bSAndrew Gallatin 	uint32_t vs;
3388dce01b9bSAndrew Gallatin 
3389dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
3390dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
3391dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3392dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3393dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3394dce01b9bSAndrew Gallatin 	}
3395dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3396dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3397dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3398dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3399dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3400dce01b9bSAndrew Gallatin }
3401dce01b9bSAndrew Gallatin 
3402e749ef6bSAndrew Gallatin static int
3403dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3404dce01b9bSAndrew Gallatin {
3405e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3406dce01b9bSAndrew Gallatin 	int err;
3407dce01b9bSAndrew Gallatin 	uint32_t reboot;
3408dce01b9bSAndrew Gallatin 	uint16_t cmd;
3409dce01b9bSAndrew Gallatin 
3410dce01b9bSAndrew Gallatin 	err = ENXIO;
3411dce01b9bSAndrew Gallatin 
3412dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3413dce01b9bSAndrew Gallatin 
3414dce01b9bSAndrew Gallatin 	/*
3415dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3416dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3417dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3418dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3419dce01b9bSAndrew Gallatin 	 * again
3420dce01b9bSAndrew Gallatin 	 */
3421dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3422dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3423dce01b9bSAndrew Gallatin 		/*
3424dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3425dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3426dce01b9bSAndrew Gallatin 		 * back, then give up
3427dce01b9bSAndrew Gallatin 		 */
3428dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3429dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3430dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3431dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3432e749ef6bSAndrew Gallatin 			return (err);
3433dce01b9bSAndrew Gallatin 		}
3434dce01b9bSAndrew Gallatin 	}
3435dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3436dce01b9bSAndrew Gallatin 		/* print the reboot status */
3437dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3438dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3439dce01b9bSAndrew Gallatin 			      reboot);
3440dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3441e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3442e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3443dce01b9bSAndrew Gallatin 
3444dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3445dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
344610882804SAndrew Gallatin 
344710882804SAndrew Gallatin 		if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
344810882804SAndrew Gallatin 			mxge_close(sc);
344910882804SAndrew Gallatin 			err = mxge_open(sc);
345010882804SAndrew Gallatin 		}
3451dce01b9bSAndrew Gallatin 	} else {
3452dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC did not reboot, ring state:\n");
3453dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "tx.req=%d tx.done=%d\n",
34541e413cf9SAndrew Gallatin 			      sc->ss->tx.req, sc->ss->tx.done);
3455dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
34561e413cf9SAndrew Gallatin 			      sc->ss->tx.pkt_done,
34571e413cf9SAndrew Gallatin 			      be32toh(sc->ss->fw_stats->send_done_count));
345810882804SAndrew Gallatin 		device_printf(sc->dev, "not resetting\n");
3459dce01b9bSAndrew Gallatin 	}
3460e749ef6bSAndrew Gallatin 	return (err);
3461dce01b9bSAndrew Gallatin }
3462dce01b9bSAndrew Gallatin 
3463e749ef6bSAndrew Gallatin static int
3464dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3465dce01b9bSAndrew Gallatin {
34661e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &sc->ss->tx;
34671e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3468e749ef6bSAndrew Gallatin 	int err = 0;
3469dce01b9bSAndrew Gallatin 
3470dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3471dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3472dce01b9bSAndrew Gallatin 	if (tx->req != tx->done &&
3473dce01b9bSAndrew Gallatin 	    tx->watchdog_req != tx->watchdog_done &&
3474c587e59fSAndrew Gallatin 	    tx->done == tx->watchdog_done) {
3475c587e59fSAndrew Gallatin 		/* check for pause blocking before resetting */
3476c587e59fSAndrew Gallatin 		if (tx->watchdog_rx_pause == rx_pause)
3477e749ef6bSAndrew Gallatin 			err = mxge_watchdog_reset(sc);
3478c587e59fSAndrew Gallatin 		else
3479c587e59fSAndrew Gallatin 			device_printf(sc->dev, "Flow control blocking "
3480c587e59fSAndrew Gallatin 				      "xmits, check link partner\n");
3481c587e59fSAndrew Gallatin 	}
3482dce01b9bSAndrew Gallatin 
3483dce01b9bSAndrew Gallatin 	tx->watchdog_req = tx->req;
3484dce01b9bSAndrew Gallatin 	tx->watchdog_done = tx->done;
3485c587e59fSAndrew Gallatin 	tx->watchdog_rx_pause = rx_pause;
3486c587e59fSAndrew Gallatin 
3487c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3488c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3489e749ef6bSAndrew Gallatin 	return (err);
3490dce01b9bSAndrew Gallatin }
3491dce01b9bSAndrew Gallatin 
3492dce01b9bSAndrew Gallatin static void
34931e413cf9SAndrew Gallatin mxge_update_stats(mxge_softc_t *sc)
34941e413cf9SAndrew Gallatin {
34951e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
34961e413cf9SAndrew Gallatin 	u_long ipackets = 0;
34971e413cf9SAndrew Gallatin 	int slice;
34981e413cf9SAndrew Gallatin 
34991e413cf9SAndrew Gallatin 	for(slice = 0; slice < sc->num_slices; slice++) {
35001e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
35011e413cf9SAndrew Gallatin 		ipackets += ss->ipackets;
35021e413cf9SAndrew Gallatin 	}
35031e413cf9SAndrew Gallatin 	sc->ifp->if_ipackets = ipackets;
35041e413cf9SAndrew Gallatin 
35051e413cf9SAndrew Gallatin }
35061e413cf9SAndrew Gallatin static void
3507dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3508dce01b9bSAndrew Gallatin {
3509dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
3510e749ef6bSAndrew Gallatin 	int err = 0;
3511dce01b9bSAndrew Gallatin 
35121e413cf9SAndrew Gallatin 	/* aggregate stats from different slices */
35131e413cf9SAndrew Gallatin 	mxge_update_stats(sc);
35141e413cf9SAndrew Gallatin 	if (!sc->watchdog_countdown) {
3515e749ef6bSAndrew Gallatin 		err = mxge_watchdog(sc);
35161e413cf9SAndrew Gallatin 		sc->watchdog_countdown = 4;
35171e413cf9SAndrew Gallatin 	}
35181e413cf9SAndrew Gallatin 	sc->watchdog_countdown--;
3519e749ef6bSAndrew Gallatin 	if (err == 0)
3520e749ef6bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3521e749ef6bSAndrew Gallatin 
3522dce01b9bSAndrew Gallatin }
3523b2fc195eSAndrew Gallatin 
3524b2fc195eSAndrew Gallatin static int
35256d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
3526b2fc195eSAndrew Gallatin {
3527b2fc195eSAndrew Gallatin 	return EINVAL;
3528b2fc195eSAndrew Gallatin }
3529b2fc195eSAndrew Gallatin 
3530b2fc195eSAndrew Gallatin static int
35316d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3532b2fc195eSAndrew Gallatin {
3533b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
3534b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3535b2fc195eSAndrew Gallatin 	int err = 0;
3536b2fc195eSAndrew Gallatin 
3537b2fc195eSAndrew Gallatin 
3538c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3539053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3540b2fc195eSAndrew Gallatin 		return EINVAL;
3541a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3542b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
3543b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
3544b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
35456d87a65dSAndrew Gallatin 		mxge_close(sc);
35466d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3547b2fc195eSAndrew Gallatin 		if (err != 0) {
3548b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
35496d87a65dSAndrew Gallatin 			mxge_close(sc);
35506d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3551b2fc195eSAndrew Gallatin 		}
3552b2fc195eSAndrew Gallatin 	}
3553a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3554b2fc195eSAndrew Gallatin 	return err;
3555b2fc195eSAndrew Gallatin }
3556b2fc195eSAndrew Gallatin 
3557b2fc195eSAndrew Gallatin static void
35586d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
3559b2fc195eSAndrew Gallatin {
35606d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3561b2fc195eSAndrew Gallatin 
3562b2fc195eSAndrew Gallatin 
3563b2fc195eSAndrew Gallatin 	if (sc == NULL)
3564b2fc195eSAndrew Gallatin 		return;
3565b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3566c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3567b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
3568c587e59fSAndrew Gallatin 	ifmr->ifm_active |= sc->link_state ? IFM_FDX : 0;
3569b2fc195eSAndrew Gallatin }
3570b2fc195eSAndrew Gallatin 
3571b2fc195eSAndrew Gallatin static int
35726d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
3573b2fc195eSAndrew Gallatin {
35746d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3575b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
3576b2fc195eSAndrew Gallatin 	int err, mask;
3577b2fc195eSAndrew Gallatin 
3578b2fc195eSAndrew Gallatin 	err = 0;
3579b2fc195eSAndrew Gallatin 	switch (command) {
3580b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
3581b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
3582b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
3583b2fc195eSAndrew Gallatin 		break;
3584b2fc195eSAndrew Gallatin 
3585b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
35866d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
3587b2fc195eSAndrew Gallatin 		break;
3588b2fc195eSAndrew Gallatin 
3589b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
3590a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3591b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
3592dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
35936d87a65dSAndrew Gallatin 				err = mxge_open(sc);
3594dce01b9bSAndrew Gallatin 			} else {
35950fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
35960fa7f681SAndrew Gallatin 				   flag chages */
35970fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
35980fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
35990fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
36000fa7f681SAndrew Gallatin 			}
3601b2fc195eSAndrew Gallatin 		} else {
3602dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
36031e413cf9SAndrew Gallatin 				mxge_close(sc);
3604dce01b9bSAndrew Gallatin 			}
3605b2fc195eSAndrew Gallatin 		}
3606a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3607b2fc195eSAndrew Gallatin 		break;
3608b2fc195eSAndrew Gallatin 
3609b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
3610b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
3611a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
36120fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
3613a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3614b2fc195eSAndrew Gallatin 		break;
3615b2fc195eSAndrew Gallatin 
3616b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
3617a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3618b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
3619b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
3620b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
3621aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
3622aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3623aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3624b2fc195eSAndrew Gallatin 			} else {
3625b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3626b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3627b2fc195eSAndrew Gallatin 			}
3628b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3629b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
3630b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
36315e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
3632b2fc195eSAndrew Gallatin 			} else {
3633b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
36345e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
3635b2fc195eSAndrew Gallatin 			}
3636b2fc195eSAndrew Gallatin 		}
3637aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
3638aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
3639aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
3640aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
3641aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
3642aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
3643aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
3644aed8e389SAndrew Gallatin 			} else {
3645aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
3646aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
3647aed8e389SAndrew Gallatin 				err = EINVAL;
3648aed8e389SAndrew Gallatin 			}
3649aed8e389SAndrew Gallatin 		}
3650f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
3651f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
3652f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
3653f04b33f8SAndrew Gallatin 			else
3654f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
3655f04b33f8SAndrew Gallatin 		}
3656c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
3657c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
3658a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3659c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
3660c792928fSAndrew Gallatin 
3661b2fc195eSAndrew Gallatin 		break;
3662b2fc195eSAndrew Gallatin 
3663b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
3664b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
3665b2fc195eSAndrew Gallatin 				    &sc->media, command);
3666b2fc195eSAndrew Gallatin                 break;
3667b2fc195eSAndrew Gallatin 
3668b2fc195eSAndrew Gallatin 	default:
3669b2fc195eSAndrew Gallatin 		err = ENOTTY;
3670b2fc195eSAndrew Gallatin         }
3671b2fc195eSAndrew Gallatin 	return err;
3672b2fc195eSAndrew Gallatin }
3673b2fc195eSAndrew Gallatin 
3674b2fc195eSAndrew Gallatin static void
36756d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
3676b2fc195eSAndrew Gallatin {
3677b2fc195eSAndrew Gallatin 
36781e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
36796d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
36806d87a65dSAndrew Gallatin 			  &mxge_flow_control);
36816d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
36826d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
36836d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
36846d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
3685d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
3686d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
36875e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
36885e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
36895e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
36905e7d8541SAndrew Gallatin 			  &mxge_verbose);
3691dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
3692053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
36931e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
36941e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
3695f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
3696f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
3697b2fc195eSAndrew Gallatin 
36985e7d8541SAndrew Gallatin 	if (bootverbose)
36995e7d8541SAndrew Gallatin 		mxge_verbose = 1;
37006d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
37016d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
3702dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
37031e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
37046d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
37051e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
37061e413cf9SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_SRC_PORT) {
37071e413cf9SAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
3708b2fc195eSAndrew Gallatin 	}
37091e413cf9SAndrew Gallatin }
37101e413cf9SAndrew Gallatin 
37111e413cf9SAndrew Gallatin 
37121e413cf9SAndrew Gallatin static void
37131e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
37141e413cf9SAndrew Gallatin {
37151e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
37161e413cf9SAndrew Gallatin 	int i;
37171e413cf9SAndrew Gallatin 
37181e413cf9SAndrew Gallatin 
37191e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
37201e413cf9SAndrew Gallatin 		return;
37211e413cf9SAndrew Gallatin 
37221e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
37231e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
37241e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
37251e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
37261e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
37271e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
37281e413cf9SAndrew Gallatin 		}
37291e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
37301e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
37311e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
37321e413cf9SAndrew Gallatin 		}
37331e413cf9SAndrew Gallatin 	}
37341e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
37351e413cf9SAndrew Gallatin 	sc->ss = NULL;
37361e413cf9SAndrew Gallatin }
37371e413cf9SAndrew Gallatin 
37381e413cf9SAndrew Gallatin static int
37391e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
37401e413cf9SAndrew Gallatin {
37411e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
37421e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
37431e413cf9SAndrew Gallatin 	size_t bytes;
37441e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
37451e413cf9SAndrew Gallatin 
37461e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
37471e413cf9SAndrew Gallatin 	if (err != 0) {
37481e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
37491e413cf9SAndrew Gallatin 		return err;
37501e413cf9SAndrew Gallatin 	}
37511e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
37521e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
37531e413cf9SAndrew Gallatin 
37541e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
37551e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
37561e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
37571e413cf9SAndrew Gallatin 		return (ENOMEM);
37581e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
37591e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
37601e413cf9SAndrew Gallatin 
37611e413cf9SAndrew Gallatin 		ss->sc = sc;
37621e413cf9SAndrew Gallatin 
37631e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
37641e413cf9SAndrew Gallatin 
37651e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
37661e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
37671e413cf9SAndrew Gallatin 		if (err != 0)
37681e413cf9SAndrew Gallatin 			goto abort;
37691e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
37701e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
37711e413cf9SAndrew Gallatin 
37721e413cf9SAndrew Gallatin 		/*
37731e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
37741e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
37751e413cf9SAndrew Gallatin 		 * slice for now
37761e413cf9SAndrew Gallatin 		 */
37771e413cf9SAndrew Gallatin 		if (i > 0)
37781e413cf9SAndrew Gallatin 			continue;
37791e413cf9SAndrew Gallatin 
37801e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
37811e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
37821e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
37831e413cf9SAndrew Gallatin 		if (err != 0)
37841e413cf9SAndrew Gallatin 			goto abort;
37851e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
37861e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
37871e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
37881e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
37891e413cf9SAndrew Gallatin 	}
37901e413cf9SAndrew Gallatin 
37911e413cf9SAndrew Gallatin 	return (0);
37921e413cf9SAndrew Gallatin 
37931e413cf9SAndrew Gallatin abort:
37941e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
37951e413cf9SAndrew Gallatin 	return (ENOMEM);
37961e413cf9SAndrew Gallatin }
37971e413cf9SAndrew Gallatin 
37981e413cf9SAndrew Gallatin static void
37991e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
38001e413cf9SAndrew Gallatin {
38011e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
38021e413cf9SAndrew Gallatin 	char *old_fw;
38031e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
38041e413cf9SAndrew Gallatin 
38051e413cf9SAndrew Gallatin 	sc->num_slices = 1;
38061e413cf9SAndrew Gallatin 	/*
38071e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
38081e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
38091e413cf9SAndrew Gallatin 	 */
38101e413cf9SAndrew Gallatin 
38111e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
38121e413cf9SAndrew Gallatin 		return;
38131e413cf9SAndrew Gallatin 
38141e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
38151e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
38161e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
38171e413cf9SAndrew Gallatin 		return;
38181e413cf9SAndrew Gallatin 
38191e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
38201e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
38211e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
38221e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
38231e413cf9SAndrew Gallatin 	else
38241e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
38251e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
38261e413cf9SAndrew Gallatin 	if (status != 0) {
38271e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
38281e413cf9SAndrew Gallatin 		return;
38291e413cf9SAndrew Gallatin 	}
38301e413cf9SAndrew Gallatin 
38311e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
38321e413cf9SAndrew Gallatin 	   is alive */
38331e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
38341e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
38351e413cf9SAndrew Gallatin 	if (status != 0) {
38361e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
38371e413cf9SAndrew Gallatin 		goto abort_with_fw;
38381e413cf9SAndrew Gallatin 	}
38391e413cf9SAndrew Gallatin 
38401e413cf9SAndrew Gallatin 	/* get rx ring size */
38411e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
38421e413cf9SAndrew Gallatin 	if (status != 0) {
38431e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
38441e413cf9SAndrew Gallatin 		goto abort_with_fw;
38451e413cf9SAndrew Gallatin 	}
38461e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
38471e413cf9SAndrew Gallatin 
38481e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
38491e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
38501e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
38511e413cf9SAndrew Gallatin 	if (status != 0) {
38521e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
38531e413cf9SAndrew Gallatin 		goto abort_with_fw;
38541e413cf9SAndrew Gallatin 	}
38551e413cf9SAndrew Gallatin 
38561e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
38571e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
38581e413cf9SAndrew Gallatin 	if (status != 0) {
38591e413cf9SAndrew Gallatin 		device_printf(sc->dev,
38601e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
38611e413cf9SAndrew Gallatin 		goto abort_with_fw;
38621e413cf9SAndrew Gallatin 	}
38631e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
38641e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
38651e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
38661e413cf9SAndrew Gallatin 
38671e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
38681e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
38691e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
38701e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
38711e413cf9SAndrew Gallatin 	} else {
38721e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
38731e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
38741e413cf9SAndrew Gallatin 	}
38751e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
38761e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
38771e413cf9SAndrew Gallatin 		sc->num_slices--;
38781e413cf9SAndrew Gallatin 
38791e413cf9SAndrew Gallatin 	if (mxge_verbose)
38801e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
38811e413cf9SAndrew Gallatin 			      sc->num_slices);
38821e413cf9SAndrew Gallatin 
38831e413cf9SAndrew Gallatin 	return;
38841e413cf9SAndrew Gallatin 
38851e413cf9SAndrew Gallatin abort_with_fw:
38861e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
38871e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
38881e413cf9SAndrew Gallatin }
38891e413cf9SAndrew Gallatin 
38901e413cf9SAndrew Gallatin static int
38911e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
38921e413cf9SAndrew Gallatin {
38931e413cf9SAndrew Gallatin 	size_t bytes;
38941e413cf9SAndrew Gallatin 	int count, err, i, rid;
38951e413cf9SAndrew Gallatin 
38961e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
38971e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
38981e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
38991e413cf9SAndrew Gallatin 
39001e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
39011e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
39021e413cf9SAndrew Gallatin 		return ENXIO;
39031e413cf9SAndrew Gallatin 	}
39041e413cf9SAndrew Gallatin 
39051e413cf9SAndrew Gallatin 	count = sc->num_slices;
39061e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
39071e413cf9SAndrew Gallatin 	if (err != 0) {
39081e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
39091e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
39101e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
39111e413cf9SAndrew Gallatin 	}
39121e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
39131e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
39141e413cf9SAndrew Gallatin 			      count, sc->num_slices);
39151e413cf9SAndrew Gallatin 		device_printf(sc->dev,
39161e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
39171e413cf9SAndrew Gallatin 			      count);
39181e413cf9SAndrew Gallatin 		err = ENOSPC;
39191e413cf9SAndrew Gallatin 		goto abort_with_msix;
39201e413cf9SAndrew Gallatin 	}
39211e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
39221e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39231e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
39241e413cf9SAndrew Gallatin 		err = ENOMEM;
39251e413cf9SAndrew Gallatin 		goto abort_with_msix;
39261e413cf9SAndrew Gallatin 	}
39271e413cf9SAndrew Gallatin 
39281e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
39291e413cf9SAndrew Gallatin 		rid = i + 1;
39301e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
39311e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
39321e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
39331e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
39341e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
39351e413cf9SAndrew Gallatin 				      " for message %d\n", i);
39361e413cf9SAndrew Gallatin 			err = ENXIO;
39371e413cf9SAndrew Gallatin 			goto abort_with_res;
39381e413cf9SAndrew Gallatin 		}
39391e413cf9SAndrew Gallatin 	}
39401e413cf9SAndrew Gallatin 
39411e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
39421e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39431e413cf9SAndrew Gallatin 
39441e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
39451e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
39461e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
394737d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
394837d89b0cSAndrew Gallatin 				     NULL,
394937d89b0cSAndrew Gallatin #endif
395037d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
39511e413cf9SAndrew Gallatin 		if (err != 0) {
39521e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
39531e413cf9SAndrew Gallatin 				      "message %d\n", i);
39541e413cf9SAndrew Gallatin 			goto abort_with_intr;
39551e413cf9SAndrew Gallatin 		}
39561e413cf9SAndrew Gallatin 	}
39571e413cf9SAndrew Gallatin 
39581e413cf9SAndrew Gallatin 	if (mxge_verbose) {
39591e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
39601e413cf9SAndrew Gallatin 			      sc->num_slices);
39611e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
39621e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
39631e413cf9SAndrew Gallatin 		printf("\n");
39641e413cf9SAndrew Gallatin 	}
39651e413cf9SAndrew Gallatin 	return (0);
39661e413cf9SAndrew Gallatin 
39671e413cf9SAndrew Gallatin abort_with_intr:
39681e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
39691e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
39701e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
39711e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
39721e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
39731e413cf9SAndrew Gallatin 		}
39741e413cf9SAndrew Gallatin 	}
39751e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
39761e413cf9SAndrew Gallatin 
39771e413cf9SAndrew Gallatin 
39781e413cf9SAndrew Gallatin abort_with_res:
39791e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
39801e413cf9SAndrew Gallatin 		rid = i + 1;
39811e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
39821e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
39831e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
39841e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
39851e413cf9SAndrew Gallatin 	}
39861e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
39871e413cf9SAndrew Gallatin 
39881e413cf9SAndrew Gallatin 
39891e413cf9SAndrew Gallatin abort_with_msix:
39901e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
39911e413cf9SAndrew Gallatin 
39921e413cf9SAndrew Gallatin abort_with_msix_table:
39931e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
39941e413cf9SAndrew Gallatin 			     sc->msix_table_res);
39951e413cf9SAndrew Gallatin 
39961e413cf9SAndrew Gallatin 	return err;
39971e413cf9SAndrew Gallatin }
39981e413cf9SAndrew Gallatin 
39991e413cf9SAndrew Gallatin static int
40001e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
40011e413cf9SAndrew Gallatin {
40021e413cf9SAndrew Gallatin 	int count, err, rid;
40031e413cf9SAndrew Gallatin 
40041e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
40051e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
40061e413cf9SAndrew Gallatin 		rid = 1;
40071e413cf9SAndrew Gallatin 	} else {
40081e413cf9SAndrew Gallatin 		rid = 0;
400991ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
40101e413cf9SAndrew Gallatin 	}
40111e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
40121e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
40131e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
40141e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
40151e413cf9SAndrew Gallatin 		return ENXIO;
40161e413cf9SAndrew Gallatin 	}
40171e413cf9SAndrew Gallatin 	if (mxge_verbose)
40181e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
401991ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
40201e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
40211e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
40221e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
402337d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
402437d89b0cSAndrew Gallatin 			     NULL,
402537d89b0cSAndrew Gallatin #endif
402637d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
40271e413cf9SAndrew Gallatin 	if (err != 0) {
40281e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
402991ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
403091ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
40311e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
40321e413cf9SAndrew Gallatin 	}
40331e413cf9SAndrew Gallatin 	return err;
40341e413cf9SAndrew Gallatin }
40351e413cf9SAndrew Gallatin 
40361e413cf9SAndrew Gallatin static void
40371e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
40381e413cf9SAndrew Gallatin {
40391e413cf9SAndrew Gallatin 	int i, rid;
40401e413cf9SAndrew Gallatin 
40411e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40421e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
40431e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
40441e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
40451e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
40461e413cf9SAndrew Gallatin 		}
40471e413cf9SAndrew Gallatin 	}
40481e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
40491e413cf9SAndrew Gallatin 
40501e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40511e413cf9SAndrew Gallatin 		rid = i + 1;
40521e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
40531e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
40541e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
40551e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
40561e413cf9SAndrew Gallatin 	}
40571e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
40581e413cf9SAndrew Gallatin 
40591e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40601e413cf9SAndrew Gallatin 			     sc->msix_table_res);
40611e413cf9SAndrew Gallatin 
40621e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
40631e413cf9SAndrew Gallatin 	return;
40641e413cf9SAndrew Gallatin }
40651e413cf9SAndrew Gallatin 
40661e413cf9SAndrew Gallatin static void
40671e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
40681e413cf9SAndrew Gallatin {
40691e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
40701e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
407191ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
407291ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
40731e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
40741e413cf9SAndrew Gallatin }
40751e413cf9SAndrew Gallatin 
40761e413cf9SAndrew Gallatin static void
40771e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
40781e413cf9SAndrew Gallatin {
40791e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
40801e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
40811e413cf9SAndrew Gallatin 	else
40821e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
40831e413cf9SAndrew Gallatin }
40841e413cf9SAndrew Gallatin 
40851e413cf9SAndrew Gallatin static int
40861e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
40871e413cf9SAndrew Gallatin {
40881e413cf9SAndrew Gallatin 	int err;
40891e413cf9SAndrew Gallatin 
40901e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
40911e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
40921e413cf9SAndrew Gallatin 	else
40931e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
40941e413cf9SAndrew Gallatin 
40951e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
40961e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
40971e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
40981e413cf9SAndrew Gallatin 	}
40991e413cf9SAndrew Gallatin 	return err;
41001e413cf9SAndrew Gallatin }
41011e413cf9SAndrew Gallatin 
4102b2fc195eSAndrew Gallatin 
4103b2fc195eSAndrew Gallatin static int
41046d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4105b2fc195eSAndrew Gallatin {
41066d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4107b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
41081e413cf9SAndrew Gallatin 	int err, rid;
4109b2fc195eSAndrew Gallatin 
4110b2fc195eSAndrew Gallatin 	sc->dev = dev;
41116d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4112b2fc195eSAndrew Gallatin 
4113b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
4114b2fc195eSAndrew Gallatin 				 1,			/* alignment */
41151e413cf9SAndrew Gallatin 				 0,			/* boundary */
4116b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4117b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4118b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4119aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
41205e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
41211e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4122b2fc195eSAndrew Gallatin 				 0,			/* flags */
4123b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4124b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4125b2fc195eSAndrew Gallatin 
4126b2fc195eSAndrew Gallatin 	if (err != 0) {
4127b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4128b2fc195eSAndrew Gallatin 			      err);
4129b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
4130b2fc195eSAndrew Gallatin 	}
4131b2fc195eSAndrew Gallatin 
4132b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4133b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4134b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4135b2fc195eSAndrew Gallatin 		err = ENOSPC;
4136b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4137b2fc195eSAndrew Gallatin 	}
41381e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
41391e413cf9SAndrew Gallatin 
4140a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4141a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4142a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4143a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4144a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4145a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4146b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4147b2fc195eSAndrew Gallatin 
4148dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4149d91b1b49SAndrew Gallatin 
4150dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4151b2fc195eSAndrew Gallatin 
4152b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4153b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4154b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4155b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4156b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4157b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4158b2fc195eSAndrew Gallatin 		err = ENXIO;
4159b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4160b2fc195eSAndrew Gallatin 	}
4161b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4162b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4163b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4164b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4165b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4166b2fc195eSAndrew Gallatin 		err = ENXIO;
4167b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4168b2fc195eSAndrew Gallatin 	}
4169b2fc195eSAndrew Gallatin 
4170b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4171b2fc195eSAndrew Gallatin 	   lanai SRAM */
41726d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4173b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4174b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
41756d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4176b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
41776d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
41786d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4179b2fc195eSAndrew Gallatin 	if (err != 0)
4180b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4181b2fc195eSAndrew Gallatin 
4182b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
41836d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4184b2fc195eSAndrew Gallatin 
4185b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
41866d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
41876d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4188b2fc195eSAndrew Gallatin 	if (err != 0)
4189b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4190b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
41916d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4192b2fc195eSAndrew Gallatin 	if (err != 0)
4193b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4194b2fc195eSAndrew Gallatin 
4195a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4196a98d6cd7SAndrew Gallatin 	if (err != 0)
41971e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4198b2fc195eSAndrew Gallatin 
41998fe615baSAndrew Gallatin 	/* select & load the firmware */
42008fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4201b2fc195eSAndrew Gallatin 	if (err != 0)
42021e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
42035e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
42041e413cf9SAndrew Gallatin 
42051e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
42061e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
42071e413cf9SAndrew Gallatin 	if (err != 0)
42081e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
42091e413cf9SAndrew Gallatin 
4210adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4211b2fc195eSAndrew Gallatin 	if (err != 0)
42121e413cf9SAndrew Gallatin 		goto abort_with_slices;
4213b2fc195eSAndrew Gallatin 
4214a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4215a98d6cd7SAndrew Gallatin 	if (err != 0) {
4216a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
42171e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
4218a98d6cd7SAndrew Gallatin 	}
4219a98d6cd7SAndrew Gallatin 
42201e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4221a98d6cd7SAndrew Gallatin 	if (err != 0) {
42221e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4223a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4224a98d6cd7SAndrew Gallatin 	}
42251e413cf9SAndrew Gallatin 
4226e5062938SAndrew Gallatin 	ifp->if_baudrate = IF_Gbps(10UL);
4227c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
422837d89b0cSAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LRO;
422937d89b0cSAndrew Gallatin 
423037d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
423137d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
423237d89b0cSAndrew Gallatin #endif
4233c792928fSAndrew Gallatin 
4234053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4235053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4236053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4237053e637fSAndrew Gallatin 	else
4238053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4239adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4240053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4241aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
4242b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4243f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4244f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
42455e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
42466d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
4247b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
4248b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
42496d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
42506d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
4251c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4252c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4253c587e59fSAndrew Gallatin 		     mxge_media_status);
4254c587e59fSAndrew Gallatin 	mxge_set_media(sc, IFM_ETHER | IFM_AUTO);
4255c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
4256b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4257b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
4258053e637fSAndrew Gallatin 	if (ifp->if_capabilities & IFCAP_JUMBO_MTU)
4259c792928fSAndrew Gallatin 		ifp->if_mtu = 9000;
4260b2fc195eSAndrew Gallatin 
42616d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4262b2fc195eSAndrew Gallatin 	return 0;
4263b2fc195eSAndrew Gallatin 
4264a98d6cd7SAndrew Gallatin abort_with_rings:
4265a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
42661e413cf9SAndrew Gallatin abort_with_slices:
42671e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4268a98d6cd7SAndrew Gallatin abort_with_dmabench:
4269a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4270b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
42716d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4272b2fc195eSAndrew Gallatin abort_with_cmd_dma:
42736d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4274b2fc195eSAndrew Gallatin abort_with_mem_res:
4275b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4276b2fc195eSAndrew Gallatin abort_with_lock:
4277b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4278a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4279a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4280b2fc195eSAndrew Gallatin 	if_free(ifp);
4281b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4282b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4283b2fc195eSAndrew Gallatin 
4284b2fc195eSAndrew Gallatin abort_with_nothing:
4285b2fc195eSAndrew Gallatin 	return err;
4286b2fc195eSAndrew Gallatin }
4287b2fc195eSAndrew Gallatin 
4288b2fc195eSAndrew Gallatin static int
42896d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4290b2fc195eSAndrew Gallatin {
42916d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4292b2fc195eSAndrew Gallatin 
429337d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4294c792928fSAndrew Gallatin 		device_printf(sc->dev,
4295c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4296c792928fSAndrew Gallatin 		return EBUSY;
4297c792928fSAndrew Gallatin 	}
4298a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4299b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
43006d87a65dSAndrew Gallatin 		mxge_close(sc);
4301a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4302b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
4303e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4304dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4305091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
43061e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
43071e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4308a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
43091e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4310a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
43116d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
43126d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4313b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4314b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4315a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4316a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4317b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4318b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4319b2fc195eSAndrew Gallatin 	return 0;
4320b2fc195eSAndrew Gallatin }
4321b2fc195eSAndrew Gallatin 
4322b2fc195eSAndrew Gallatin static int
43236d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4324b2fc195eSAndrew Gallatin {
4325b2fc195eSAndrew Gallatin 	return 0;
4326b2fc195eSAndrew Gallatin }
4327b2fc195eSAndrew Gallatin 
4328b2fc195eSAndrew Gallatin /*
4329b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4330b2fc195eSAndrew Gallatin 
4331b2fc195eSAndrew Gallatin   Local Variables:
4332b2fc195eSAndrew Gallatin   c-file-style:"linux"
4333b2fc195eSAndrew Gallatin   tab-width:8
4334b2fc195eSAndrew Gallatin   End:
4335b2fc195eSAndrew Gallatin */
4336