xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 4ae3322f1a04ee51bfcd616a811d397aa06edaa8)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
301638550SAndrew Gallatin Copyright (c) 2006-2009, 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 {
14501638550SAndrew Gallatin 	int rev;
14601638550SAndrew Gallatin 
14701638550SAndrew Gallatin 
1486d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
149f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
150f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
15101638550SAndrew Gallatin 		rev = pci_get_revid(dev);
15201638550SAndrew Gallatin 		switch (rev) {
15301638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
154b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
15501638550SAndrew Gallatin 			break;
15601638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
15701638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
15801638550SAndrew Gallatin 			break;
15901638550SAndrew Gallatin 		default:
16001638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
16101638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
16201638550SAndrew Gallatin 				      rev);
16301638550SAndrew Gallatin 			break;
16401638550SAndrew Gallatin 		}
165b2fc195eSAndrew Gallatin 		return 0;
166b2fc195eSAndrew Gallatin 	}
167b2fc195eSAndrew Gallatin 	return ENXIO;
168b2fc195eSAndrew Gallatin }
169b2fc195eSAndrew Gallatin 
170b2fc195eSAndrew Gallatin static void
1716d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
172b2fc195eSAndrew Gallatin {
173f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
174b2fc195eSAndrew Gallatin 	vm_offset_t len;
17547c2e987SAndrew Gallatin 	int err;
176b2fc195eSAndrew Gallatin 
1774d69a9d0SAndrew Gallatin 	sc->wc = 1;
178b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
179c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
180c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
18147c2e987SAndrew Gallatin 	if (err != 0) {
182c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
183c2c14a69SAndrew Gallatin 			      err);
1844d69a9d0SAndrew Gallatin 		sc->wc = 0;
185b2fc195eSAndrew Gallatin 	}
186f9ae0280SAndrew Gallatin #endif
187b2fc195eSAndrew Gallatin }
188b2fc195eSAndrew Gallatin 
189b2fc195eSAndrew Gallatin 
190b2fc195eSAndrew Gallatin /* callback to get our DMA address */
191b2fc195eSAndrew Gallatin static void
1926d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
193b2fc195eSAndrew Gallatin 			 int error)
194b2fc195eSAndrew Gallatin {
195b2fc195eSAndrew Gallatin 	if (error == 0) {
196b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
197b2fc195eSAndrew Gallatin 	}
198b2fc195eSAndrew Gallatin }
199b2fc195eSAndrew Gallatin 
200b2fc195eSAndrew Gallatin static int
2016d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
202b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
203b2fc195eSAndrew Gallatin {
204b2fc195eSAndrew Gallatin 	int err;
205b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2061e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2071e413cf9SAndrew Gallatin 
2081e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2091e413cf9SAndrew Gallatin 		boundary = 0;
2101e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2111e413cf9SAndrew Gallatin 	} else {
2121e413cf9SAndrew Gallatin 		boundary = 4096;
2131e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2141e413cf9SAndrew Gallatin 	}
215b2fc195eSAndrew Gallatin 
216b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
217b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
218b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2191e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
220b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
221b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
222b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
223b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
224b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2251e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
226b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
227b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
228b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
229b2fc195eSAndrew Gallatin 	if (err != 0) {
230b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
231b2fc195eSAndrew Gallatin 		return err;
232b2fc195eSAndrew Gallatin 	}
233b2fc195eSAndrew Gallatin 
234b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
235b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
236b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
237b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
238b2fc195eSAndrew Gallatin 	if (err != 0) {
239b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
240b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
241b2fc195eSAndrew Gallatin 	}
242b2fc195eSAndrew Gallatin 
243b2fc195eSAndrew Gallatin 	/* load the memory */
244b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2456d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
246b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
247b2fc195eSAndrew Gallatin 	if (err != 0) {
248b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
249b2fc195eSAndrew Gallatin 		goto abort_with_mem;
250b2fc195eSAndrew Gallatin 	}
251b2fc195eSAndrew Gallatin 	return 0;
252b2fc195eSAndrew Gallatin 
253b2fc195eSAndrew Gallatin abort_with_mem:
254b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
255b2fc195eSAndrew Gallatin abort_with_dmat:
256b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
257b2fc195eSAndrew Gallatin 	return err;
258b2fc195eSAndrew Gallatin }
259b2fc195eSAndrew Gallatin 
260b2fc195eSAndrew Gallatin 
261b2fc195eSAndrew Gallatin static void
2626d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
263b2fc195eSAndrew Gallatin {
264b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
265b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
266b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
267b2fc195eSAndrew Gallatin }
268b2fc195eSAndrew Gallatin 
269b2fc195eSAndrew Gallatin /*
270b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
271b2fc195eSAndrew Gallatin  * SN=x\0
272b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
273b2fc195eSAndrew Gallatin  * PC=text\0
274b2fc195eSAndrew Gallatin  */
275b2fc195eSAndrew Gallatin 
276b2fc195eSAndrew Gallatin static int
2776d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
278b2fc195eSAndrew Gallatin {
2796d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
280b2fc195eSAndrew Gallatin 
281b2fc195eSAndrew Gallatin 	char *ptr, *limit;
282b2fc195eSAndrew Gallatin 	int i, found_mac;
283b2fc195eSAndrew Gallatin 
284b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2856d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
286b2fc195eSAndrew Gallatin 	found_mac = 0;
287b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
288b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2895e7d8541SAndrew Gallatin 			ptr += 1;
290b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
291b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2925e7d8541SAndrew Gallatin 				ptr += 3;
293b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
294b2fc195eSAndrew Gallatin 					goto abort;
295b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
296b2fc195eSAndrew Gallatin 				found_mac = 1;
297b2fc195eSAndrew Gallatin 			}
2985e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2995e7d8541SAndrew Gallatin 			ptr += 3;
3005e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
3015e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
3025e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
3035e7d8541SAndrew Gallatin 			ptr += 3;
3045e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
3055e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
306b2fc195eSAndrew Gallatin 		}
3076d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
308b2fc195eSAndrew Gallatin 	}
309b2fc195eSAndrew Gallatin 
310b2fc195eSAndrew Gallatin 	if (found_mac)
311b2fc195eSAndrew Gallatin 		return 0;
312b2fc195eSAndrew Gallatin 
313b2fc195eSAndrew Gallatin  abort:
314b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
315b2fc195eSAndrew Gallatin 
316b2fc195eSAndrew Gallatin 	return ENXIO;
317b2fc195eSAndrew Gallatin }
318b2fc195eSAndrew Gallatin 
3190d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3208fe615baSAndrew Gallatin static void
3218fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
322b2fc195eSAndrew Gallatin {
323b2fc195eSAndrew Gallatin 	uint32_t val;
3248fe615baSAndrew Gallatin 	unsigned long base, off;
325b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3268fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3278fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
328b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
329b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
330b2fc195eSAndrew Gallatin 
3318fe615baSAndrew Gallatin 
3328fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3338fe615baSAndrew Gallatin 		return;
3348fe615baSAndrew Gallatin 
3358fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3368fe615baSAndrew Gallatin 	if (pdev == NULL) {
3378fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3388fe615baSAndrew Gallatin 		return;
3398fe615baSAndrew Gallatin 	}
3408fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3418fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3428fe615baSAndrew Gallatin 
3438fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3448fe615baSAndrew Gallatin 		return;
3458fe615baSAndrew Gallatin 
3468fe615baSAndrew Gallatin 	base = 0;
3478fe615baSAndrew Gallatin 
3488fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3498fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3508fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3518fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3528fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3538fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3548fe615baSAndrew Gallatin 		if (mcp55 &&
3558fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3568fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3578fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3588fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3598fe615baSAndrew Gallatin 		}
3608fe615baSAndrew Gallatin 	}
3618fe615baSAndrew Gallatin 	if (!base)
3628fe615baSAndrew Gallatin 		return;
3638fe615baSAndrew Gallatin 
364b2fc195eSAndrew Gallatin 	/* XXXX
365b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
366b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
367b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
368b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
369b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
370b2fc195eSAndrew Gallatin 	*/
371b2fc195eSAndrew Gallatin #if 0
372b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
373b2fc195eSAndrew Gallatin 	   config space */
374b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
375b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
376b2fc195eSAndrew Gallatin 		val |= 0x40;
377b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3788fe615baSAndrew Gallatin 		return;
379b2fc195eSAndrew Gallatin 	}
380b2fc195eSAndrew Gallatin #endif
381b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
382b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
383b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
384b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
385b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
386b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
387b2fc195eSAndrew Gallatin 	 */
388b2fc195eSAndrew Gallatin 
389b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
390b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
391b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
392b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
393b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
394b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
395b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
396b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
397b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
398b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
399b2fc195eSAndrew Gallatin 
4008fe615baSAndrew Gallatin 	off =  base
401b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
402b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
403b2fc195eSAndrew Gallatin 						 + 8 * slot);
404b2fc195eSAndrew Gallatin 
405b2fc195eSAndrew Gallatin 	/* map it into the kernel */
406b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
407b2fc195eSAndrew Gallatin 
408b2fc195eSAndrew Gallatin 
409b2fc195eSAndrew Gallatin 	if (va == NULL) {
410b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4118fe615baSAndrew Gallatin 		return;
412b2fc195eSAndrew Gallatin 	}
413b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
414b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
415b2fc195eSAndrew Gallatin 
416b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
417b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
418b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
419b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
420b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
421b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
422b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4238fe615baSAndrew Gallatin 		return;
424b2fc195eSAndrew Gallatin 	}
425b2fc195eSAndrew Gallatin 
426b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
427b2fc195eSAndrew Gallatin 	val = *ptr32;
428b2fc195eSAndrew Gallatin 
429b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
430b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
431b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4328fe615baSAndrew Gallatin 		return;
433b2fc195eSAndrew Gallatin 	}
434b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
435b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4365e7d8541SAndrew Gallatin 	if (mxge_verbose)
437b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4385e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4395e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
440b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4418fe615baSAndrew Gallatin 	return;
442b2fc195eSAndrew Gallatin }
443b2fc195eSAndrew Gallatin #else
4448fe615baSAndrew Gallatin static void
445f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
446b2fc195eSAndrew Gallatin {
447b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
448b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4498fe615baSAndrew Gallatin 	return;
450b2fc195eSAndrew Gallatin }
451b2fc195eSAndrew Gallatin #endif
4528fe615baSAndrew Gallatin 
4538fe615baSAndrew Gallatin 
4548fe615baSAndrew Gallatin static int
4558fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4568fe615baSAndrew Gallatin {
4578fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4588fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4598fe615baSAndrew Gallatin 	int status;
4608fe615baSAndrew Gallatin 	uint32_t len;
4618fe615baSAndrew Gallatin 	char *test = " ";
4628fe615baSAndrew Gallatin 
4638fe615baSAndrew Gallatin 
4648fe615baSAndrew Gallatin 	/* Run a small DMA test.
4658fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4668fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4678fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4688fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4698fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4708fe615baSAndrew Gallatin 	 * transfers took to complete.
4718fe615baSAndrew Gallatin 	 */
4728fe615baSAndrew Gallatin 
4731e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4748fe615baSAndrew Gallatin 
4758fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4768fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4778fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4788fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4798fe615baSAndrew Gallatin 	if (status != 0) {
4808fe615baSAndrew Gallatin 		test = "read";
4818fe615baSAndrew Gallatin 		goto abort;
4828fe615baSAndrew Gallatin 	}
4838fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4848fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4858fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4868fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4878fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4888fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4898fe615baSAndrew Gallatin 	if (status != 0) {
4908fe615baSAndrew Gallatin 		test = "write";
4918fe615baSAndrew Gallatin 		goto abort;
4928fe615baSAndrew Gallatin 	}
4938fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
4948fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4958fe615baSAndrew Gallatin 
4968fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4978fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4988fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
4998fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5008fe615baSAndrew Gallatin 	if (status != 0) {
5018fe615baSAndrew Gallatin 		test = "read/write";
5028fe615baSAndrew Gallatin 		goto abort;
5038fe615baSAndrew Gallatin 	}
5048fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5058fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5068fe615baSAndrew Gallatin 
5078fe615baSAndrew Gallatin abort:
5088fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5098fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5108fe615baSAndrew Gallatin 			      test, status);
5118fe615baSAndrew Gallatin 
5128fe615baSAndrew Gallatin 	return status;
5138fe615baSAndrew Gallatin }
5148fe615baSAndrew Gallatin 
515b2fc195eSAndrew Gallatin /*
516b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
517b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
518b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
519b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
520b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
521b2fc195eSAndrew Gallatin  *
522b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
523b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
524b2fc195eSAndrew Gallatin  *
525b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
526b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
527b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
528b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5291e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
530b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5311e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
532b2fc195eSAndrew Gallatin  */
533b2fc195eSAndrew Gallatin 
5348fe615baSAndrew Gallatin static int
5358fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5368fe615baSAndrew Gallatin {
5378fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5388fe615baSAndrew Gallatin 	int reg, status;
5398fe615baSAndrew Gallatin 	uint16_t pectl;
5408fe615baSAndrew Gallatin 
5411e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5428fe615baSAndrew Gallatin 	/*
5438fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5448fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5458fe615baSAndrew Gallatin 	 */
5468fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5478fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5488fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5498fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5508fe615baSAndrew Gallatin 				      pectl);
5511e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5528fe615baSAndrew Gallatin 		}
5538fe615baSAndrew Gallatin 	}
5548fe615baSAndrew Gallatin 
5558fe615baSAndrew Gallatin 	/*
5568fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5578fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5588fe615baSAndrew Gallatin 	 */
5598fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5601e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5618fe615baSAndrew Gallatin 	if (status != 0) {
5628fe615baSAndrew Gallatin 		return status;
5638fe615baSAndrew Gallatin 	}
5648fe615baSAndrew Gallatin 
5658fe615baSAndrew Gallatin 	/*
5668fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5678fe615baSAndrew Gallatin 	 */
5688fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5698fe615baSAndrew Gallatin 
5708fe615baSAndrew Gallatin 	/*
5718fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5728fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5738fe615baSAndrew Gallatin 	 */
5748fe615baSAndrew Gallatin 
5758fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5768fe615baSAndrew Gallatin 	if (status == 0)
5778fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5788fe615baSAndrew Gallatin 
5798fe615baSAndrew Gallatin 	if (status != E2BIG)
5808fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5818fe615baSAndrew Gallatin 	if (status == ENOSYS)
5828fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5838fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5848fe615baSAndrew Gallatin 	return status;
5858fe615baSAndrew Gallatin }
5868fe615baSAndrew Gallatin 
5878fe615baSAndrew Gallatin static int
5886d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
589b2fc195eSAndrew Gallatin {
5908fe615baSAndrew Gallatin 	int aligned = 0;
591b2fc195eSAndrew Gallatin 
592d91b1b49SAndrew Gallatin 
593d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
594d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
595d91b1b49SAndrew Gallatin 			aligned = 1;
596d91b1b49SAndrew Gallatin 		else
597d91b1b49SAndrew Gallatin 			aligned = 0;
598d91b1b49SAndrew Gallatin 		if (mxge_verbose)
599d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
600d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
601d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
602d91b1b49SAndrew Gallatin 		goto abort;
603d91b1b49SAndrew Gallatin 	}
604d91b1b49SAndrew Gallatin 
605d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
606d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
607d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
608d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
609d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
610d91b1b49SAndrew Gallatin 			      sc->link_width);
611d91b1b49SAndrew Gallatin 		aligned = 1;
612d91b1b49SAndrew Gallatin 		goto abort;
613d91b1b49SAndrew Gallatin 	}
614d91b1b49SAndrew Gallatin 
6158fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6168fe615baSAndrew Gallatin 		return 0;
617b2fc195eSAndrew Gallatin 
618b2fc195eSAndrew Gallatin abort:
619b2fc195eSAndrew Gallatin 	if (aligned) {
6206d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6211e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
622b2fc195eSAndrew Gallatin 	} else {
6236d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6241e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
625b2fc195eSAndrew Gallatin 	}
6261e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
627b2fc195eSAndrew Gallatin }
628b2fc195eSAndrew Gallatin 
629b2fc195eSAndrew Gallatin union qualhack
630b2fc195eSAndrew Gallatin {
631b2fc195eSAndrew Gallatin         const char *ro_char;
632b2fc195eSAndrew Gallatin         char *rw_char;
633b2fc195eSAndrew Gallatin };
634b2fc195eSAndrew Gallatin 
6354da0d523SAndrew Gallatin static int
6364da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6374da0d523SAndrew Gallatin {
638b824b7d8SAndrew Gallatin 
6394da0d523SAndrew Gallatin 
6404da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6414da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6424da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6434da0d523SAndrew Gallatin 		return EIO;
6444da0d523SAndrew Gallatin 	}
6454da0d523SAndrew Gallatin 
6464da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6474da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6484da0d523SAndrew Gallatin 	if (mxge_verbose)
6494da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6504da0d523SAndrew Gallatin 
651b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
652b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6534da0d523SAndrew Gallatin 
654b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
655b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6564da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6574da0d523SAndrew Gallatin 			      sc->fw_version);
6584da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6594da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6604da0d523SAndrew Gallatin 		return EINVAL;
6614da0d523SAndrew Gallatin 	}
6624da0d523SAndrew Gallatin 	return 0;
6634da0d523SAndrew Gallatin 
6644da0d523SAndrew Gallatin }
665b2fc195eSAndrew Gallatin 
666f9ae0280SAndrew Gallatin static void *
667f9ae0280SAndrew Gallatin z_alloc(void *nil, u_int items, u_int size)
668f9ae0280SAndrew Gallatin {
669f9ae0280SAndrew Gallatin         void *ptr;
670f9ae0280SAndrew Gallatin 
671f9ae0280SAndrew Gallatin         ptr = malloc(items * size, M_TEMP, M_NOWAIT);
672f9ae0280SAndrew Gallatin         return ptr;
673f9ae0280SAndrew Gallatin }
674f9ae0280SAndrew Gallatin 
675f9ae0280SAndrew Gallatin static void
676f9ae0280SAndrew Gallatin z_free(void *nil, void *ptr)
677f9ae0280SAndrew Gallatin {
678f9ae0280SAndrew Gallatin         free(ptr, M_TEMP);
679f9ae0280SAndrew Gallatin }
680f9ae0280SAndrew Gallatin 
681f9ae0280SAndrew Gallatin 
682b2fc195eSAndrew Gallatin static int
6836d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
684b2fc195eSAndrew Gallatin {
685f9ae0280SAndrew Gallatin 	z_stream zs;
686f9ae0280SAndrew Gallatin 	char *inflate_buffer;
68733d54970SLuigi Rizzo 	const struct firmware *fw;
688b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
689b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
690b2fc195eSAndrew Gallatin 	int status;
6914da0d523SAndrew Gallatin 	unsigned int i;
6924da0d523SAndrew Gallatin 	char dummy;
693f9ae0280SAndrew Gallatin 	size_t fw_len;
694b2fc195eSAndrew Gallatin 
695b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
696b2fc195eSAndrew Gallatin 	if (fw == NULL) {
697b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
698b2fc195eSAndrew Gallatin 			      sc->fw_name);
699b2fc195eSAndrew Gallatin 		return ENOENT;
700b2fc195eSAndrew Gallatin 	}
701b2fc195eSAndrew Gallatin 
702f9ae0280SAndrew Gallatin 
703f9ae0280SAndrew Gallatin 
704f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
705f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
706f9ae0280SAndrew Gallatin 	zs.zalloc = z_alloc;
707f9ae0280SAndrew Gallatin 	zs.zfree = z_free;
708f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
709f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
710b2fc195eSAndrew Gallatin 		status = EIO;
711b2fc195eSAndrew Gallatin 		goto abort_with_fw;
712b2fc195eSAndrew Gallatin 	}
713f9ae0280SAndrew Gallatin 
714f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
715f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
716f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
717f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
718f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
719f9ae0280SAndrew Gallatin 		goto abort_with_zs;
720f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
721f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
722f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
723f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
724f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
725f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
726f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
727f9ae0280SAndrew Gallatin 		status = EIO;
728f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
729f9ae0280SAndrew Gallatin 	}
730f9ae0280SAndrew Gallatin 
731f9ae0280SAndrew Gallatin 	/* check id */
732f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
733f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
734f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
735f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
736f9ae0280SAndrew Gallatin 		status = EIO;
737f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
738f9ae0280SAndrew Gallatin 	}
739f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
740b2fc195eSAndrew Gallatin 
7414da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7424da0d523SAndrew Gallatin 	if (status != 0)
743f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
744b2fc195eSAndrew Gallatin 
745b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
746f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7474da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
748f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
749f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
75073c7c83fSAndrew Gallatin 		wmb();
7514da0d523SAndrew Gallatin 		dummy = *sc->sram;
75273c7c83fSAndrew Gallatin 		wmb();
7534da0d523SAndrew Gallatin 	}
754b2fc195eSAndrew Gallatin 
755f9ae0280SAndrew Gallatin 	*limit = fw_len;
756b2fc195eSAndrew Gallatin 	status = 0;
757f9ae0280SAndrew Gallatin abort_with_buffer:
758f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
759f9ae0280SAndrew Gallatin abort_with_zs:
760f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
761b2fc195eSAndrew Gallatin abort_with_fw:
762b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
763b2fc195eSAndrew Gallatin 	return status;
764b2fc195eSAndrew Gallatin }
765b2fc195eSAndrew Gallatin 
766b2fc195eSAndrew Gallatin /*
767b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
768b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
769b2fc195eSAndrew Gallatin  */
770b2fc195eSAndrew Gallatin 
771b2fc195eSAndrew Gallatin static void
7726d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
773b2fc195eSAndrew Gallatin {
774b2fc195eSAndrew Gallatin 	char buf_bytes[72];
775b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
776b2fc195eSAndrew Gallatin 	volatile char *submit;
777b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
778b2fc195eSAndrew Gallatin 	int i;
779b2fc195eSAndrew Gallatin 
780b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
781b2fc195eSAndrew Gallatin 
782b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
783b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
784b2fc195eSAndrew Gallatin 	*confirm = 0;
78573c7c83fSAndrew Gallatin 	wmb();
786b2fc195eSAndrew Gallatin 
787b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
788b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
789b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
790b2fc195eSAndrew Gallatin 	*/
791b2fc195eSAndrew Gallatin 
7926d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7936d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
794b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
795b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
796b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7976d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7986d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
799b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
800b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
801b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
802b2fc195eSAndrew Gallatin 
803b2fc195eSAndrew Gallatin 
8040fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
805b2fc195eSAndrew Gallatin 
8066d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
80773c7c83fSAndrew Gallatin 	wmb();
808b2fc195eSAndrew Gallatin 	DELAY(1000);
80973c7c83fSAndrew Gallatin 	wmb();
810b2fc195eSAndrew Gallatin 	i = 0;
811b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
812b2fc195eSAndrew Gallatin 		DELAY(1000);
813b2fc195eSAndrew Gallatin 		i++;
814b2fc195eSAndrew Gallatin 	}
815b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
816b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
817b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
818b2fc195eSAndrew Gallatin 			      *confirm);
819b2fc195eSAndrew Gallatin 	}
820b2fc195eSAndrew Gallatin 	return;
821b2fc195eSAndrew Gallatin }
822b2fc195eSAndrew Gallatin 
823b2fc195eSAndrew Gallatin static int
8246d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
825b2fc195eSAndrew Gallatin {
826b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
827b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
828b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8290fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
830b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
831e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
832b2fc195eSAndrew Gallatin 
833b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
834b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
835b2fc195eSAndrew Gallatin 
836b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
837b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
838b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
839b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8406d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8416d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
842b2fc195eSAndrew Gallatin 
843b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
844b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
845a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
846b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
84773c7c83fSAndrew Gallatin 	wmb();
8486d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
849b2fc195eSAndrew Gallatin 
8505e7d8541SAndrew Gallatin 	/* wait up to 20ms */
851e0501fd0SAndrew Gallatin 	err = EAGAIN;
8525e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
853b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
854b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
85573c7c83fSAndrew Gallatin 		wmb();
856e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
857e0501fd0SAndrew Gallatin 		case 0:
858b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
859e0501fd0SAndrew Gallatin 			err = 0;
860e0501fd0SAndrew Gallatin 			break;
861e0501fd0SAndrew Gallatin 		case 0xffffffff:
862e0501fd0SAndrew Gallatin 			DELAY(1000);
863e0501fd0SAndrew Gallatin 			break;
864e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
865e0501fd0SAndrew Gallatin 			err = ENOSYS;
866e0501fd0SAndrew Gallatin 			break;
867e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
868e0501fd0SAndrew Gallatin 			err = E2BIG;
869e0501fd0SAndrew Gallatin 			break;
870c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
871c587e59fSAndrew Gallatin 			err = EBUSY;
872c587e59fSAndrew Gallatin 			break;
873e0501fd0SAndrew Gallatin 		default:
874b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8756d87a65dSAndrew Gallatin 				      "mxge: command %d "
876b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
877b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
878e0501fd0SAndrew Gallatin 			err = ENXIO;
879e0501fd0SAndrew Gallatin 			break;
880b2fc195eSAndrew Gallatin 		}
881e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
882e0501fd0SAndrew Gallatin 			break;
883b2fc195eSAndrew Gallatin 	}
884e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8856d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
886b2fc195eSAndrew Gallatin 			      "result = %d\n",
887b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
888e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
889e0501fd0SAndrew Gallatin 	return err;
890b2fc195eSAndrew Gallatin }
891b2fc195eSAndrew Gallatin 
8924da0d523SAndrew Gallatin static int
8934da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8944da0d523SAndrew Gallatin {
8954da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8964da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8974da0d523SAndrew Gallatin 	size_t hdr_offset;
8984da0d523SAndrew Gallatin 	int status;
8994da0d523SAndrew Gallatin 
9004da0d523SAndrew Gallatin 	/* find running firmware header */
9014da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9024da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9034da0d523SAndrew Gallatin 
9044da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9054da0d523SAndrew Gallatin 		device_printf(sc->dev,
9064da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9074da0d523SAndrew Gallatin 			      (int)hdr_offset);
9084da0d523SAndrew Gallatin 		return EIO;
9094da0d523SAndrew Gallatin 	}
9104da0d523SAndrew Gallatin 
9114da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9124da0d523SAndrew Gallatin 	 * validate firmware */
9134da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9144da0d523SAndrew Gallatin 	if (hdr == NULL) {
9154da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9164da0d523SAndrew Gallatin 		return ENOMEM;
9174da0d523SAndrew Gallatin 	}
9184da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9194da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9204da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9214da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9224da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
923b824b7d8SAndrew Gallatin 
924b824b7d8SAndrew Gallatin 	/*
925b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
926b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
927b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
928b824b7d8SAndrew Gallatin 	 */
929b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
930b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
931b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
932b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
933b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
934b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
935b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
936b824b7d8SAndrew Gallatin 	}
937b824b7d8SAndrew Gallatin 
9384da0d523SAndrew Gallatin 	return status;
9394da0d523SAndrew Gallatin }
9404da0d523SAndrew Gallatin 
941b2fc195eSAndrew Gallatin 
942b2fc195eSAndrew Gallatin static int
9431e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
944b2fc195eSAndrew Gallatin {
945b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
946b2fc195eSAndrew Gallatin 	volatile char *submit;
947b2fc195eSAndrew Gallatin 	char buf_bytes[72];
948b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
949b2fc195eSAndrew Gallatin 	int status, i;
950b2fc195eSAndrew Gallatin 
951b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
952b2fc195eSAndrew Gallatin 
953b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9546d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
955b2fc195eSAndrew Gallatin 	if (status) {
9561e413cf9SAndrew Gallatin 		if (!adopt)
9571e413cf9SAndrew Gallatin 			return status;
9584da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9594da0d523SAndrew Gallatin 		   it is new enough */
9604da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9614da0d523SAndrew Gallatin 		if (status) {
9624da0d523SAndrew Gallatin 			device_printf(sc->dev,
9634da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
964b2fc195eSAndrew Gallatin 			return status;
965b2fc195eSAndrew Gallatin 		}
9664da0d523SAndrew Gallatin 		device_printf(sc->dev,
9674da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9681e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9694da0d523SAndrew Gallatin 			device_printf(sc->dev,
9704da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9714da0d523SAndrew Gallatin 				 ".  For optimal\n");
9724da0d523SAndrew Gallatin 			device_printf(sc->dev,
9734da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9744da0d523SAndrew Gallatin 				 "firmware\n");
9754da0d523SAndrew Gallatin 		}
976d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9771e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
978d91b1b49SAndrew Gallatin 		return 0;
9794da0d523SAndrew Gallatin 	}
980b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
981b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
982b2fc195eSAndrew Gallatin 	*confirm = 0;
98373c7c83fSAndrew Gallatin 	wmb();
984b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
985b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
986b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
987b2fc195eSAndrew Gallatin 	*/
988b2fc195eSAndrew Gallatin 
9896d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9906d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
991b2fc195eSAndrew Gallatin 
992b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
993b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
994b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
995b2fc195eSAndrew Gallatin 
996b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
997b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
998b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
999b2fc195eSAndrew Gallatin 	*/
1000b2fc195eSAndrew Gallatin 					/* where the code starts*/
10016d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1002b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1003b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1004b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1005b2fc195eSAndrew Gallatin 
10060fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10076d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
100873c7c83fSAndrew Gallatin 	wmb();
1009b2fc195eSAndrew Gallatin 	DELAY(1000);
101073c7c83fSAndrew Gallatin 	wmb();
1011b2fc195eSAndrew Gallatin 	i = 0;
1012b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1013b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1014b2fc195eSAndrew Gallatin 		i++;
1015b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1016b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1017b2fc195eSAndrew Gallatin 	}
1018b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1019b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1020b2fc195eSAndrew Gallatin 			confirm, *confirm);
1021b2fc195eSAndrew Gallatin 
1022b2fc195eSAndrew Gallatin 		return ENXIO;
1023b2fc195eSAndrew Gallatin 	}
1024b2fc195eSAndrew Gallatin 	return 0;
1025b2fc195eSAndrew Gallatin }
1026b2fc195eSAndrew Gallatin 
1027b2fc195eSAndrew Gallatin static int
10286d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1029b2fc195eSAndrew Gallatin {
10306d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1031b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1032b2fc195eSAndrew Gallatin 	int status;
1033b2fc195eSAndrew Gallatin 
1034b2fc195eSAndrew Gallatin 
1035b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1036b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1037b2fc195eSAndrew Gallatin 
1038b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1039b2fc195eSAndrew Gallatin 
10405e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1041b2fc195eSAndrew Gallatin 	return status;
1042b2fc195eSAndrew Gallatin }
1043b2fc195eSAndrew Gallatin 
1044b2fc195eSAndrew Gallatin static int
10456d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1046b2fc195eSAndrew Gallatin {
10476d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1048b2fc195eSAndrew Gallatin 	int status;
1049b2fc195eSAndrew Gallatin 
1050b2fc195eSAndrew Gallatin 	if (pause)
10515e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1052b2fc195eSAndrew Gallatin 				       &cmd);
1053b2fc195eSAndrew Gallatin 	else
10545e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1055b2fc195eSAndrew Gallatin 				       &cmd);
1056b2fc195eSAndrew Gallatin 
1057b2fc195eSAndrew Gallatin 	if (status) {
1058b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1059b2fc195eSAndrew Gallatin 		return ENXIO;
1060b2fc195eSAndrew Gallatin 	}
1061b2fc195eSAndrew Gallatin 	sc->pause = pause;
1062b2fc195eSAndrew Gallatin 	return 0;
1063b2fc195eSAndrew Gallatin }
1064b2fc195eSAndrew Gallatin 
1065b2fc195eSAndrew Gallatin static void
10666d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1067b2fc195eSAndrew Gallatin {
10686d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1069b2fc195eSAndrew Gallatin 	int status;
1070b2fc195eSAndrew Gallatin 
10711e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10721e413cf9SAndrew Gallatin 		promisc = 1;
10731e413cf9SAndrew Gallatin 
1074b2fc195eSAndrew Gallatin 	if (promisc)
10755e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1076b2fc195eSAndrew Gallatin 				       &cmd);
1077b2fc195eSAndrew Gallatin 	else
10785e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1079b2fc195eSAndrew Gallatin 				       &cmd);
1080b2fc195eSAndrew Gallatin 
1081b2fc195eSAndrew Gallatin 	if (status) {
1082b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1083b2fc195eSAndrew Gallatin 	}
1084b2fc195eSAndrew Gallatin }
1085b2fc195eSAndrew Gallatin 
10860fa7f681SAndrew Gallatin static void
10870fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10880fa7f681SAndrew Gallatin {
10890fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
10900fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
10910fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
10920fa7f681SAndrew Gallatin 	int err;
10930fa7f681SAndrew Gallatin 
10940fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
10950fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
10960fa7f681SAndrew Gallatin 		return;
10970fa7f681SAndrew Gallatin 
10980fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
10990fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11000fa7f681SAndrew Gallatin 	if (err != 0) {
11010fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11020fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11030fa7f681SAndrew Gallatin 		return;
11040fa7f681SAndrew Gallatin 	}
11050fa7f681SAndrew Gallatin 
1106b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1107b824b7d8SAndrew Gallatin 		return;
11080fa7f681SAndrew Gallatin 
11090fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11100fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11110fa7f681SAndrew Gallatin 		return;
11120fa7f681SAndrew Gallatin 
11130fa7f681SAndrew Gallatin 	/* Flush all the filters */
11140fa7f681SAndrew Gallatin 
11150fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11160fa7f681SAndrew Gallatin 	if (err != 0) {
11170fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11180fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11190fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11200fa7f681SAndrew Gallatin 		return;
11210fa7f681SAndrew Gallatin 	}
11220fa7f681SAndrew Gallatin 
11230fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11240fa7f681SAndrew Gallatin 
11250fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
11260fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11270fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11280fa7f681SAndrew Gallatin 			continue;
11290fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11300fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11310fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11320fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11330fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11340fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11350fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11360fa7f681SAndrew Gallatin 		if (err != 0) {
11370fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11380fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11390fa7f681SAndrew Gallatin 			       "%d\t", err);
11400fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
11410fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
11420fa7f681SAndrew Gallatin 			return;
11430fa7f681SAndrew Gallatin 		}
11440fa7f681SAndrew Gallatin 	}
11450fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
11460fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11470fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11480fa7f681SAndrew Gallatin 	if (err != 0) {
11490fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11500fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11510fa7f681SAndrew Gallatin 	}
11520fa7f681SAndrew Gallatin }
11530fa7f681SAndrew Gallatin 
1154b2fc195eSAndrew Gallatin static int
1155053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1156053e637fSAndrew Gallatin {
1157053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1158053e637fSAndrew Gallatin 	int status;
1159053e637fSAndrew Gallatin 
1160c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1161c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1162053e637fSAndrew Gallatin 
1163053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1164053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1165053e637fSAndrew Gallatin 	cmd.data0 = 0;
1166053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1167053e637fSAndrew Gallatin 			       &cmd);
1168053e637fSAndrew Gallatin 	if (status == 0)
1169c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1170053e637fSAndrew Gallatin 
1171053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1172053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1173053e637fSAndrew Gallatin }
1174053e637fSAndrew Gallatin 
1175053e637fSAndrew Gallatin static int
1176adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1177b2fc195eSAndrew Gallatin {
11781e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11791e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11801e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11816d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11821e413cf9SAndrew Gallatin 	int slice, status;
1183b2fc195eSAndrew Gallatin 
1184b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1185b2fc195eSAndrew Gallatin 	   is alive */
1186b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11875e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1188b2fc195eSAndrew Gallatin 	if (status != 0) {
1189b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1190b2fc195eSAndrew Gallatin 		return ENXIO;
1191b2fc195eSAndrew Gallatin 	}
1192b2fc195eSAndrew Gallatin 
1193091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1194091feecdSAndrew Gallatin 
11951e413cf9SAndrew Gallatin 
11961e413cf9SAndrew Gallatin 	/* set the intrq size */
11971e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
11981e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11991e413cf9SAndrew Gallatin 
12001e413cf9SAndrew Gallatin 	/*
12011e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12021e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12031e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12041e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12051e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12061e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12071e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12081e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12091e413cf9SAndrew Gallatin 	 */
12101e413cf9SAndrew Gallatin 
12111e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12121e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12131e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12141e413cf9SAndrew Gallatin 					   &cmd);
12151e413cf9SAndrew Gallatin 		if (status != 0) {
12161e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12171e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12181e413cf9SAndrew Gallatin 			return status;
12191e413cf9SAndrew Gallatin 		}
12201e413cf9SAndrew Gallatin 		/*
12211e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12221e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12231e413cf9SAndrew Gallatin 		 */
12241e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12251e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
12261e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12271e413cf9SAndrew Gallatin 					   &cmd);
12281e413cf9SAndrew Gallatin 		if (status != 0) {
12291e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12301e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12311e413cf9SAndrew Gallatin 			return status;
12321e413cf9SAndrew Gallatin 		}
12331e413cf9SAndrew Gallatin 	}
12341e413cf9SAndrew Gallatin 
12351e413cf9SAndrew Gallatin 
1236adae7080SAndrew Gallatin 	if (interrupts_setup) {
1237b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12381e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12391e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12401e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12411e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12421e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12431e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12441e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12451e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12461e413cf9SAndrew Gallatin 						&cmd);
12471e413cf9SAndrew Gallatin 		}
1248adae7080SAndrew Gallatin 	}
1249b2fc195eSAndrew Gallatin 
12506d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12515e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12525e7d8541SAndrew Gallatin 
12535e7d8541SAndrew Gallatin 
12545e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12555e7d8541SAndrew Gallatin 
12565e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12571e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12585e7d8541SAndrew Gallatin 
12595e7d8541SAndrew Gallatin 
12605e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12616d87a65dSAndrew Gallatin 				&cmd);
12625e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1263b2fc195eSAndrew Gallatin 	if (status != 0) {
1264b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1265b2fc195eSAndrew Gallatin 		return status;
1266b2fc195eSAndrew Gallatin 	}
1267b2fc195eSAndrew Gallatin 
12685e7d8541SAndrew Gallatin 
12695e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12705e7d8541SAndrew Gallatin 
12715e7d8541SAndrew Gallatin 
12725e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12738fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12745e7d8541SAndrew Gallatin 
12751e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12761e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12771e413cf9SAndrew Gallatin 
12781e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1279b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12801e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12811e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12821e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12831e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12841e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
12851e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
12861e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
12871e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
12881e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
12891e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
12901e413cf9SAndrew Gallatin 		ss->lro_bad_csum = 0;
12911e413cf9SAndrew Gallatin 		ss->lro_queued = 0;
12921e413cf9SAndrew Gallatin 		ss->lro_flushed = 0;
12931e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
12941e413cf9SAndrew Gallatin 			ss->fw_stats->valid = 0;
12951e413cf9SAndrew Gallatin 			ss->fw_stats->send_done_count = 0;
12961e413cf9SAndrew Gallatin 		}
12971e413cf9SAndrew Gallatin 	}
1298b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
12996d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
13006d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
13016d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13020fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1303b2fc195eSAndrew Gallatin 	return status;
1304b2fc195eSAndrew Gallatin }
1305b2fc195eSAndrew Gallatin 
1306b2fc195eSAndrew Gallatin static int
13076d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1308b2fc195eSAndrew Gallatin {
13096d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1310b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1311b2fc195eSAndrew Gallatin         int err;
1312b2fc195eSAndrew Gallatin 
1313b2fc195eSAndrew Gallatin         sc = arg1;
1314b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1315b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1316b2fc195eSAndrew Gallatin         if (err != 0) {
1317b2fc195eSAndrew Gallatin                 return err;
1318b2fc195eSAndrew Gallatin         }
1319b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1320b2fc195eSAndrew Gallatin                 return 0;
1321b2fc195eSAndrew Gallatin 
1322b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1323b2fc195eSAndrew Gallatin                 return EINVAL;
1324b2fc195eSAndrew Gallatin 
1325a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13265e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1327b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13285e7d8541SAndrew Gallatin 
1329a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1330b2fc195eSAndrew Gallatin         return err;
1331b2fc195eSAndrew Gallatin }
1332b2fc195eSAndrew Gallatin 
1333b2fc195eSAndrew Gallatin static int
13346d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1335b2fc195eSAndrew Gallatin {
13366d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1337b2fc195eSAndrew Gallatin         unsigned int enabled;
1338b2fc195eSAndrew Gallatin         int err;
1339b2fc195eSAndrew Gallatin 
1340b2fc195eSAndrew Gallatin         sc = arg1;
1341b2fc195eSAndrew Gallatin         enabled = sc->pause;
1342b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1343b2fc195eSAndrew Gallatin         if (err != 0) {
1344b2fc195eSAndrew Gallatin                 return err;
1345b2fc195eSAndrew Gallatin         }
1346b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1347b2fc195eSAndrew Gallatin                 return 0;
1348b2fc195eSAndrew Gallatin 
1349a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13506d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1351a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1352b2fc195eSAndrew Gallatin         return err;
1353b2fc195eSAndrew Gallatin }
1354b2fc195eSAndrew Gallatin 
1355b2fc195eSAndrew Gallatin static int
1356f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1357f04b33f8SAndrew Gallatin {
1358f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1359c587e59fSAndrew Gallatin 	int err = 0;
1360f04b33f8SAndrew Gallatin 
1361f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1362f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1363f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1364f04b33f8SAndrew Gallatin 	else
1365f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1366f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1367c587e59fSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1368f04b33f8SAndrew Gallatin 		mxge_close(sc);
1369f04b33f8SAndrew Gallatin 		err = mxge_open(sc);
1370c587e59fSAndrew Gallatin 	}
1371f04b33f8SAndrew Gallatin 	return err;
1372f04b33f8SAndrew Gallatin }
1373f04b33f8SAndrew Gallatin 
1374f04b33f8SAndrew Gallatin static int
1375276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1376276edd10SAndrew Gallatin {
1377276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1378276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1379276edd10SAndrew Gallatin 	int err;
1380276edd10SAndrew Gallatin 
1381276edd10SAndrew Gallatin 	sc = arg1;
1382276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1383276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1384276edd10SAndrew Gallatin 	if (err != 0)
1385276edd10SAndrew Gallatin 		return err;
1386276edd10SAndrew Gallatin 
1387276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1388276edd10SAndrew Gallatin 		return 0;
1389276edd10SAndrew Gallatin 
1390276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1391276edd10SAndrew Gallatin 		return EINVAL;
1392276edd10SAndrew Gallatin 
1393276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1394f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1395276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1396276edd10SAndrew Gallatin 	return err;
1397276edd10SAndrew Gallatin }
1398276edd10SAndrew Gallatin 
1399276edd10SAndrew Gallatin static int
14006d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1401b2fc195eSAndrew Gallatin {
1402b2fc195eSAndrew Gallatin         int err;
1403b2fc195eSAndrew Gallatin 
1404b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1405b2fc195eSAndrew Gallatin                 return EFAULT;
1406b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1407b2fc195eSAndrew Gallatin         arg1 = NULL;
1408b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1409b2fc195eSAndrew Gallatin 
1410b2fc195eSAndrew Gallatin         return err;
1411b2fc195eSAndrew Gallatin }
1412b2fc195eSAndrew Gallatin 
1413b2fc195eSAndrew Gallatin static void
14141e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14151e413cf9SAndrew Gallatin {
14161e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14171e413cf9SAndrew Gallatin 	int slice;
14181e413cf9SAndrew Gallatin 
14191e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14201e413cf9SAndrew Gallatin 		return;
14211e413cf9SAndrew Gallatin 
14221e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14231e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14241e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14251e413cf9SAndrew Gallatin 			continue;
14261e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14271e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14281e413cf9SAndrew Gallatin 	}
14291e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14301e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14311e413cf9SAndrew Gallatin }
14321e413cf9SAndrew Gallatin 
14331e413cf9SAndrew Gallatin static void
14346d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1435b2fc195eSAndrew Gallatin {
1436b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1437b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14385e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14391e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14401e413cf9SAndrew Gallatin 	int slice;
14411e413cf9SAndrew Gallatin 	char slice_num[8];
1442b2fc195eSAndrew Gallatin 
1443b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1444b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14451e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1446b2fc195eSAndrew Gallatin 
14475e7d8541SAndrew Gallatin 	/* random information */
14485e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14495e7d8541SAndrew Gallatin 		       "firmware_version",
14505e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
14515e7d8541SAndrew Gallatin 		       0, "firmware version");
14525e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14535e7d8541SAndrew Gallatin 		       "serial_number",
14545e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
14555e7d8541SAndrew Gallatin 		       0, "serial number");
14565e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14575e7d8541SAndrew Gallatin 		       "product_code",
14585e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
14595e7d8541SAndrew Gallatin 		       0, "product_code");
14605e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1461d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1462d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1463d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1464d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14655e7d8541SAndrew Gallatin 		       "tx_boundary",
14661e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14675e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14685e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1469091feecdSAndrew Gallatin 		       "write_combine",
1470091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1471091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1472091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14735e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14745e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14755e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14765e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14775e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14785e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14795e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14805e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14815e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14825e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14835e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
14845e7d8541SAndrew Gallatin 
14855e7d8541SAndrew Gallatin 
14865e7d8541SAndrew Gallatin 	/* performance related tunables */
1487b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1488b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1489b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
14906d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1491b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1492b2fc195eSAndrew Gallatin 
1493b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1494b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1495b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
14966d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1497b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1498b2fc195eSAndrew Gallatin 
1499b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15005e7d8541SAndrew Gallatin 		       "deassert_wait",
15015e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15025e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1503b2fc195eSAndrew Gallatin 
1504b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1505b2fc195eSAndrew Gallatin 	   Need to swap it */
1506b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1507b2fc195eSAndrew Gallatin 			"link_up",
1508b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15096d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1510b2fc195eSAndrew Gallatin 			"I", "link up");
1511b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1512b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1513b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15146d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1515b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1516b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1517adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1518adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1519adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15206d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1521adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1522adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1523adae7080SAndrew Gallatin 			"dropped_bad_phy",
1524adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1525adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1526adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1527adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1528b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1529b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1530b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1531b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
15326d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1533b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1534b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1535adae7080SAndrew Gallatin 			"dropped_link_overflow",
1536adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1537adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1538adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1539adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15400fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
15410fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
15420fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
15430fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
15440fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
15450fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1546adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1547adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
15486d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1549adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1550b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1551b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1552b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1553b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
15546d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1555b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1556b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1557adae7080SAndrew Gallatin 			"dropped_overrun",
1558adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
15596d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1560adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1561adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1562adae7080SAndrew Gallatin 			"dropped_pause",
1563adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1564adae7080SAndrew Gallatin 			&fw->dropped_pause,
1565adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1566adae7080SAndrew Gallatin 			"I", "dropped_pause");
1567adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1568adae7080SAndrew Gallatin 			"dropped_runt",
1569adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1570adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1571adae7080SAndrew Gallatin 			"I", "dropped_runt");
1572b2fc195eSAndrew Gallatin 
1573a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1574a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1575a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1576a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1577a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1578a0394e33SAndrew Gallatin 
15795e7d8541SAndrew Gallatin 	/* verbose printing? */
1580b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15815e7d8541SAndrew Gallatin 		       "verbose",
15825e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15835e7d8541SAndrew Gallatin 		       0, "verbose printing");
1584b2fc195eSAndrew Gallatin 
1585053e637fSAndrew Gallatin 	/* lro */
1586276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1587276edd10SAndrew Gallatin 			"lro_cnt",
1588276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1589276edd10SAndrew Gallatin 			0, mxge_change_lro,
1590276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1591053e637fSAndrew Gallatin 
15921e413cf9SAndrew Gallatin 
15931e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
15941e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15951e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
15961e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15971e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
15981e413cf9SAndrew Gallatin 
15991e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16001e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16011e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16021e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16031e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16041e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16051e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16061e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16071e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
16081e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1609053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16101e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16111e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16121e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16131e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16141e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16151e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16161e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16171e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16181e413cf9SAndrew Gallatin 			       "tx_req",
16191e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
16201e413cf9SAndrew Gallatin 			       0, "tx_req");
16211e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16221e413cf9SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
1623053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1624053e637fSAndrew Gallatin 
1625053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16261e413cf9SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16271e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16281e413cf9SAndrew Gallatin 			       "queues");
1629053e637fSAndrew Gallatin 
16301e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16311e413cf9SAndrew Gallatin 		if (slice > 0)
16321e413cf9SAndrew Gallatin 			continue;
16331e413cf9SAndrew Gallatin 
16341e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16351e413cf9SAndrew Gallatin 			       "tx_done",
16361e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16371e413cf9SAndrew Gallatin 			       0, "tx_done");
16381e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16391e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16401e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16411e413cf9SAndrew Gallatin 			       0, "tx_done");
16421e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16431e413cf9SAndrew Gallatin 			       "tx_stall",
16441e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16451e413cf9SAndrew Gallatin 			       0, "tx_stall");
16461e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16471e413cf9SAndrew Gallatin 			       "tx_wake",
16481e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16491e413cf9SAndrew Gallatin 			       0, "tx_wake");
16501e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16511e413cf9SAndrew Gallatin 			       "tx_defrag",
16521e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16531e413cf9SAndrew Gallatin 			       0, "tx_defrag");
16541e413cf9SAndrew Gallatin 	}
1655b2fc195eSAndrew Gallatin }
1656b2fc195eSAndrew Gallatin 
1657b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1658b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1659b2fc195eSAndrew Gallatin 
1660b2fc195eSAndrew Gallatin static inline void
16611e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1662b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1663b2fc195eSAndrew Gallatin {
1664b2fc195eSAndrew Gallatin         int idx, starting_slot;
1665b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1666b2fc195eSAndrew Gallatin         while (cnt > 1) {
1667b2fc195eSAndrew Gallatin                 cnt--;
1668b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
16696d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1670b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
167173c7c83fSAndrew Gallatin                 wmb();
1672b2fc195eSAndrew Gallatin         }
1673b2fc195eSAndrew Gallatin }
1674b2fc195eSAndrew Gallatin 
1675b2fc195eSAndrew Gallatin /*
1676b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1677b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1678b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1679b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1680b2fc195eSAndrew Gallatin  */
1681b2fc195eSAndrew Gallatin 
1682b2fc195eSAndrew Gallatin static inline void
16831e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1684b2fc195eSAndrew Gallatin                   int cnt)
1685b2fc195eSAndrew Gallatin {
1686b2fc195eSAndrew Gallatin         int idx, i;
1687b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1688b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1689b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1690b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
16915e7d8541SAndrew Gallatin 	uint8_t last_flags;
1692b2fc195eSAndrew Gallatin 
1693b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1694b2fc195eSAndrew Gallatin 
16955e7d8541SAndrew Gallatin 	last_flags = src->flags;
16965e7d8541SAndrew Gallatin 	src->flags = 0;
169773c7c83fSAndrew Gallatin         wmb();
1698b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1699b2fc195eSAndrew Gallatin         srcp = src;
1700b2fc195eSAndrew Gallatin 
1701b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1702b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
17036d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
170473c7c83fSAndrew Gallatin                         wmb(); /* force write every 32 bytes */
1705b2fc195eSAndrew Gallatin                         srcp += 2;
1706b2fc195eSAndrew Gallatin                         dstp += 2;
1707b2fc195eSAndrew Gallatin                 }
1708b2fc195eSAndrew Gallatin         } else {
1709b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1710b2fc195eSAndrew Gallatin                    that it is submitted below */
17116d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1712b2fc195eSAndrew Gallatin                 i = 0;
1713b2fc195eSAndrew Gallatin         }
1714b2fc195eSAndrew Gallatin         if (i < cnt) {
1715b2fc195eSAndrew Gallatin                 /* submit the first request */
17166d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
171773c7c83fSAndrew Gallatin                 wmb(); /* barrier before setting valid flag */
1718b2fc195eSAndrew Gallatin         }
1719b2fc195eSAndrew Gallatin 
1720b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
17215e7d8541SAndrew Gallatin         src->flags = last_flags;
1722b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1723b2fc195eSAndrew Gallatin         src_ints+=3;
1724b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1725b2fc195eSAndrew Gallatin         dst_ints+=3;
1726b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1727b2fc195eSAndrew Gallatin         tx->req += cnt;
172873c7c83fSAndrew Gallatin         wmb();
1729b2fc195eSAndrew Gallatin }
1730b2fc195eSAndrew Gallatin 
173137d89b0cSAndrew Gallatin #if IFCAP_TSO4
173237d89b0cSAndrew Gallatin 
1733b2fc195eSAndrew Gallatin static void
17341e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
17351e413cf9SAndrew Gallatin 	       int busdma_seg_cnt, int ip_off)
1736aed8e389SAndrew Gallatin {
17371e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1738aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1739aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1740aed8e389SAndrew Gallatin 	struct ip *ip;
1741aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1742aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1743aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1744aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1745aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1746aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1747aed8e389SAndrew Gallatin 	static int once;
1748aed8e389SAndrew Gallatin 
1749aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1750aed8e389SAndrew Gallatin 
1751aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1752aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1753aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1754aed8e389SAndrew Gallatin 	 */
1755aed8e389SAndrew Gallatin 
1756aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1757aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1758aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1759c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1760c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
17611e413cf9SAndrew Gallatin 			   ss->scratch);
17621e413cf9SAndrew Gallatin 		ip = (struct ip *)(ss->scratch + ip_off);
1763aed8e389SAndrew Gallatin 	} else {
1764c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1765aed8e389SAndrew Gallatin 	}
1766c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1767aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1768c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
17691e413cf9SAndrew Gallatin 			   + sizeof (*tcp),  ss->scratch);
1770c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1771aed8e389SAndrew Gallatin 	}
1772aed8e389SAndrew Gallatin 
1773aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1774c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1775aed8e389SAndrew Gallatin 
1776aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1777c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1778aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1779aed8e389SAndrew Gallatin 
1780aed8e389SAndrew Gallatin 
1781aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1782aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1783aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1784aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1785aed8e389SAndrew Gallatin 
17861e413cf9SAndrew Gallatin 	tx = &ss->tx;
1787aed8e389SAndrew Gallatin 	req = tx->req_list;
1788aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1789aed8e389SAndrew Gallatin 	cnt = 0;
1790aed8e389SAndrew Gallatin 	rdma_count = 0;
1791aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1792aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1793aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1794aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1795aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1796aed8e389SAndrew Gallatin 	 *
1797aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1798aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1799aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1800aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1801aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1802aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1803aed8e389SAndrew Gallatin 	 *
1804aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1805aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1806aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1807aed8e389SAndrew Gallatin 	 */
1808aed8e389SAndrew Gallatin 
1809aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1810aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1811aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1812aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1813e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1814aed8e389SAndrew Gallatin 
1815aed8e389SAndrew Gallatin 		while (len) {
1816aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1817e39a0a37SAndrew Gallatin 			seglen = len;
1818aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1819aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1820aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1821aed8e389SAndrew Gallatin 				/* payload */
1822aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1823aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1824aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1825aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1826aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1827aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1828aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1829aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1830aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1831aed8e389SAndrew Gallatin 				/* header ends */
1832aed8e389SAndrew Gallatin 				rdma_count = -1;
1833aed8e389SAndrew Gallatin 				cum_len_next = 0;
1834aed8e389SAndrew Gallatin 				seglen = -cum_len;
1835aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1836aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1837aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1838aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1839aed8e389SAndrew Gallatin 			    }
1840aed8e389SAndrew Gallatin 
1841aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1842aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1843aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1844aed8e389SAndrew Gallatin 			req->pad = 0;
1845aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1846aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1847aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1848aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1849aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1850aed8e389SAndrew Gallatin 			low += seglen;
1851aed8e389SAndrew Gallatin 			len -= seglen;
1852aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1853aed8e389SAndrew Gallatin 			flags = flags_next;
1854aed8e389SAndrew Gallatin 			req++;
1855aed8e389SAndrew Gallatin 			cnt++;
1856aed8e389SAndrew Gallatin 			rdma_count++;
1857aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1858aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1859aed8e389SAndrew Gallatin 			else
1860aed8e389SAndrew Gallatin 				cksum_offset = 0;
1861adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1862aed8e389SAndrew Gallatin 				goto drop;
1863aed8e389SAndrew Gallatin 		}
1864aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1865aed8e389SAndrew Gallatin 		seg++;
1866aed8e389SAndrew Gallatin 	}
1867aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1868aed8e389SAndrew Gallatin 
1869aed8e389SAndrew Gallatin 	do {
1870aed8e389SAndrew Gallatin 		req--;
1871aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1872aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1873aed8e389SAndrew Gallatin 
1874aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1875aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1876aed8e389SAndrew Gallatin 	return;
1877aed8e389SAndrew Gallatin 
1878aed8e389SAndrew Gallatin drop:
1879e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1880aed8e389SAndrew Gallatin 	m_freem(m);
18811e413cf9SAndrew Gallatin 	ss->sc->ifp->if_oerrors++;
1882aed8e389SAndrew Gallatin 	if (!once) {
1883adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1884adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1885adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1886aed8e389SAndrew Gallatin 		once = 1;
1887aed8e389SAndrew Gallatin 	}
1888aed8e389SAndrew Gallatin 	return;
1889aed8e389SAndrew Gallatin 
1890aed8e389SAndrew Gallatin }
1891aed8e389SAndrew Gallatin 
189237d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
189337d89b0cSAndrew Gallatin 
189437d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1895c792928fSAndrew Gallatin /*
1896c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1897c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1898c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1899c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1900c792928fSAndrew Gallatin  */
1901c792928fSAndrew Gallatin static struct mbuf *
1902c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1903c792928fSAndrew Gallatin {
1904c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1905c792928fSAndrew Gallatin 
1906c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1907c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1908c792928fSAndrew Gallatin 		return NULL;
1909c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1910c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1911c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1912c792928fSAndrew Gallatin 			return NULL;
1913c792928fSAndrew Gallatin 	}
1914c792928fSAndrew Gallatin 	/*
1915c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
1916c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
1917c792928fSAndrew Gallatin 	 */
1918c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
1919c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
1920c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
1921c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
1922c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
1923c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
1924c792928fSAndrew Gallatin 	return m;
1925c792928fSAndrew Gallatin }
192637d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
1927c792928fSAndrew Gallatin 
1928aed8e389SAndrew Gallatin static void
19291e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
1930b2fc195eSAndrew Gallatin {
19311e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
1932b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1933b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1934b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1935b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
19361e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1937b2fc195eSAndrew Gallatin 	struct ip *ip;
1938c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
1939aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1940aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1941b2fc195eSAndrew Gallatin 
1942b2fc195eSAndrew Gallatin 
19431e413cf9SAndrew Gallatin 	sc = ss->sc;
1944b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
19451e413cf9SAndrew Gallatin 	tx = &ss->tx;
1946b2fc195eSAndrew Gallatin 
1947c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
194837d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1949c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
1950c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
1951c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1952c792928fSAndrew Gallatin 			goto drop;
1953c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
1954c792928fSAndrew Gallatin 	}
195537d89b0cSAndrew Gallatin #endif
1956b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1957b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1958b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1959aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1960b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1961adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
1962b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1963b2fc195eSAndrew Gallatin 		   to defrag */
1964b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1965b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1966b2fc195eSAndrew Gallatin 			goto drop;
1967b2fc195eSAndrew Gallatin 		}
19681e413cf9SAndrew Gallatin 		ss->tx.defrag++;
1969b2fc195eSAndrew Gallatin 		m = m_tmp;
1970b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1971b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1972aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1973b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1974b2fc195eSAndrew Gallatin 	}
1975adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
1976aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1977aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1978b2fc195eSAndrew Gallatin 		goto drop;
1979b2fc195eSAndrew Gallatin 	}
1980b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1981b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
19825e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1983b2fc195eSAndrew Gallatin 
198437d89b0cSAndrew Gallatin #if IFCAP_TSO4
1985aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1986aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
19871e413cf9SAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, ip_off);
1988aed8e389SAndrew Gallatin 		return;
1989aed8e389SAndrew Gallatin 	}
199037d89b0cSAndrew Gallatin #endif
1991aed8e389SAndrew Gallatin 
1992b2fc195eSAndrew Gallatin 	req = tx->req_list;
1993b2fc195eSAndrew Gallatin 	cksum_offset = 0;
19945e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
19955e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1996b2fc195eSAndrew Gallatin 
1997b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1998b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1999aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2000aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
2001c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
2002c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
20031e413cf9SAndrew Gallatin 				   ss->scratch);
20041e413cf9SAndrew Gallatin 			ip = (struct ip *)(ss->scratch + ip_off);
2005aed8e389SAndrew Gallatin 		} else {
2006c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
2007aed8e389SAndrew Gallatin 		}
2008c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
2009b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20105e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2011b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
20125e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2013aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2014aed8e389SAndrew Gallatin 	} else {
2015aed8e389SAndrew Gallatin 		odd_flag = 0;
2016b2fc195eSAndrew Gallatin 	}
20175e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
20185e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2019b2fc195eSAndrew Gallatin 
2020b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2021b2fc195eSAndrew Gallatin 	cum_len = 0;
2022aed8e389SAndrew Gallatin 	seg = tx->seg_list;
20235e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2024b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2025b2fc195eSAndrew Gallatin 		req->addr_low =
20266d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2027b2fc195eSAndrew Gallatin 		req->addr_high =
20286d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2029b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2030b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2031b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2032b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2033b2fc195eSAndrew Gallatin 		else
2034b2fc195eSAndrew Gallatin 			cksum_offset = 0;
20355e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20365e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20375e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2038aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2039b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2040b2fc195eSAndrew Gallatin 		seg++;
2041b2fc195eSAndrew Gallatin 		req++;
2042b2fc195eSAndrew Gallatin 		req->flags = 0;
2043b2fc195eSAndrew Gallatin 	}
2044b2fc195eSAndrew Gallatin 	req--;
2045b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2046b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2047b2fc195eSAndrew Gallatin 		req++;
2048b2fc195eSAndrew Gallatin 		req->addr_low =
20496d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2050b2fc195eSAndrew Gallatin 		req->addr_high =
20516d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2052b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
20535e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
20545e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20555e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20565e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2057aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2058b2fc195eSAndrew Gallatin 		cnt++;
2059b2fc195eSAndrew Gallatin 	}
20605e7d8541SAndrew Gallatin 
20615e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
20625e7d8541SAndrew Gallatin #if 0
20635e7d8541SAndrew Gallatin 	/* print what the firmware will see */
20645e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
20655e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
20665e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
20675e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
20685e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
20695e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
20705e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
20715e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
20725e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
20735e7d8541SAndrew Gallatin 	}
20745e7d8541SAndrew Gallatin 	printf("--------------\n");
20755e7d8541SAndrew Gallatin #endif
20765e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
20776d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2078b2fc195eSAndrew Gallatin 	return;
2079b2fc195eSAndrew Gallatin 
2080b2fc195eSAndrew Gallatin drop:
2081b2fc195eSAndrew Gallatin 	m_freem(m);
2082b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
2083b2fc195eSAndrew Gallatin 	return;
2084b2fc195eSAndrew Gallatin }
2085b2fc195eSAndrew Gallatin 
2086b2fc195eSAndrew Gallatin 
20876d914a32SAndrew Gallatin 
20886d914a32SAndrew Gallatin 
20896d914a32SAndrew Gallatin static inline void
20901e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2091b2fc195eSAndrew Gallatin {
20921e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2093b2fc195eSAndrew Gallatin 	struct mbuf *m;
2094b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20951e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2096b2fc195eSAndrew Gallatin 
20971e413cf9SAndrew Gallatin 	sc = ss->sc;
2098b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20991e413cf9SAndrew Gallatin 	tx = &ss->tx;
2100adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
21016d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
21026d914a32SAndrew Gallatin 		if (m == NULL) {
21036d914a32SAndrew Gallatin 			return;
21046d914a32SAndrew Gallatin 		}
2105b2fc195eSAndrew Gallatin 		/* let BPF see it */
2106b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2107b2fc195eSAndrew Gallatin 
2108b2fc195eSAndrew Gallatin 		/* give it to the nic */
21091e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
21106d914a32SAndrew Gallatin 	}
21116d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2112a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2113b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2114adae7080SAndrew Gallatin 		tx->stall++;
2115a82c2581SAndrew Gallatin 	}
2116b2fc195eSAndrew Gallatin }
2117b2fc195eSAndrew Gallatin 
2118b2fc195eSAndrew Gallatin static void
21196d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2120b2fc195eSAndrew Gallatin {
21216d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
21221e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2123b2fc195eSAndrew Gallatin 
21241e413cf9SAndrew Gallatin 	/* only use the first slice for now */
21251e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
21261e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
21271e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
21281e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2129b2fc195eSAndrew Gallatin }
2130b2fc195eSAndrew Gallatin 
21315e7d8541SAndrew Gallatin /*
21325e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
21335e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
21345e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
21355e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
21365e7d8541SAndrew Gallatin  * in a burst
21375e7d8541SAndrew Gallatin  */
21385e7d8541SAndrew Gallatin static inline void
21395e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
21405e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
21415e7d8541SAndrew Gallatin {
21425e7d8541SAndrew Gallatin 	uint32_t low;
21435e7d8541SAndrew Gallatin 
21445e7d8541SAndrew Gallatin 	low = src->addr_low;
21455e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2146a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
214773c7c83fSAndrew Gallatin 	wmb();
2148a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
214973c7c83fSAndrew Gallatin 	wmb();
215040385a5fSAndrew Gallatin 	src->addr_low = low;
21515e7d8541SAndrew Gallatin 	dst->addr_low = low;
215273c7c83fSAndrew Gallatin 	wmb();
21535e7d8541SAndrew Gallatin }
21545e7d8541SAndrew Gallatin 
2155b2fc195eSAndrew Gallatin static int
21561e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2157b2fc195eSAndrew Gallatin {
2158b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2159b2fc195eSAndrew Gallatin 	struct mbuf *m;
21601e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2161b2fc195eSAndrew Gallatin 	int cnt, err;
2162b2fc195eSAndrew Gallatin 
2163b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
2164b2fc195eSAndrew Gallatin 	if (m == NULL) {
2165b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2166b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2167b2fc195eSAndrew Gallatin 		goto done;
2168b2fc195eSAndrew Gallatin 	}
2169b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2170b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2171b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2172b2fc195eSAndrew Gallatin 	if (err != 0) {
2173b2fc195eSAndrew Gallatin 		m_free(m);
2174b2fc195eSAndrew Gallatin 		goto done;
2175b2fc195eSAndrew Gallatin 	}
2176b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2177b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
21786d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2179b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
21806d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2181b2fc195eSAndrew Gallatin 
2182b2fc195eSAndrew Gallatin done:
2183adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2184adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2185b2fc195eSAndrew Gallatin 	return err;
2186b2fc195eSAndrew Gallatin }
2187b2fc195eSAndrew Gallatin 
2188b2fc195eSAndrew Gallatin static int
21891e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2190b2fc195eSAndrew Gallatin {
2191053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2192b2fc195eSAndrew Gallatin 	struct mbuf *m;
21931e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2194053e637fSAndrew Gallatin 	int cnt, err, i;
2195b2fc195eSAndrew Gallatin 
2196a0394e33SAndrew Gallatin 	if (rx->cl_size == MCLBYTES)
2197a0394e33SAndrew Gallatin 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2198a0394e33SAndrew Gallatin 	else
2199053e637fSAndrew Gallatin 		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2200b2fc195eSAndrew Gallatin 	if (m == NULL) {
2201b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2202b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2203b2fc195eSAndrew Gallatin 		goto done;
2204b2fc195eSAndrew Gallatin 	}
2205053e637fSAndrew Gallatin 	m->m_len = rx->cl_size;
2206b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2207053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2208b2fc195eSAndrew Gallatin 	if (err != 0) {
2209b2fc195eSAndrew Gallatin 		m_free(m);
2210b2fc195eSAndrew Gallatin 		goto done;
2211b2fc195eSAndrew Gallatin 	}
2212b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2213b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2214b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2215b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2216b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2217053e637fSAndrew Gallatin 
2218b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2219b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2220053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2221053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2222053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2223053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2224053e637fSAndrew Gallatin        }
2225b0f7b922SAndrew Gallatin #endif
2226b2fc195eSAndrew Gallatin 
2227b2fc195eSAndrew Gallatin done:
2228053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2229b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
22305e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
22315e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2232b2fc195eSAndrew Gallatin 		}
2233053e637fSAndrew Gallatin 		idx++;
2234053e637fSAndrew Gallatin 	}
2235b2fc195eSAndrew Gallatin 	return err;
2236b2fc195eSAndrew Gallatin }
2237b2fc195eSAndrew Gallatin 
22389b03b0f3SAndrew Gallatin /*
22399b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
22409b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
22419b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
22429b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2243053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2244053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
22459b03b0f3SAndrew Gallatin  */
22469b03b0f3SAndrew Gallatin 
2247053e637fSAndrew Gallatin static inline uint16_t
2248053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2249053e637fSAndrew Gallatin {
2250053e637fSAndrew Gallatin 	struct ether_header *eh;
2251053e637fSAndrew Gallatin 	struct ip *ip;
2252053e637fSAndrew Gallatin 	uint16_t c;
2253053e637fSAndrew Gallatin 
2254053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2255053e637fSAndrew Gallatin 
2256053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2257053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2258053e637fSAndrew Gallatin 		return 1;
2259053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2260053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2261053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2262053e637fSAndrew Gallatin 		return 1;
2263053e637fSAndrew Gallatin 
2264053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2265053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2266053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2267053e637fSAndrew Gallatin 	c ^= 0xffff;
2268053e637fSAndrew Gallatin 	return (c);
22695e7d8541SAndrew Gallatin }
2270053e637fSAndrew Gallatin 
2271c792928fSAndrew Gallatin static void
2272c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2273c792928fSAndrew Gallatin {
2274c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2275c792928fSAndrew Gallatin 	struct ether_header *eh;
2276c792928fSAndrew Gallatin 	uint32_t partial;
2277c792928fSAndrew Gallatin 
2278c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2279c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2280c792928fSAndrew Gallatin 
2281c792928fSAndrew Gallatin 	/*
2282c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2283c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2284c792928fSAndrew Gallatin 	 * header.
2285c792928fSAndrew Gallatin 	 */
2286c792928fSAndrew Gallatin 
2287c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2288c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2289c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2290c792928fSAndrew Gallatin 	(*csum) += ~partial;
2291c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2292c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2293c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2294c792928fSAndrew Gallatin 
2295c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2296c792928fSAndrew Gallatin 	   later consumers expect this */
2297c792928fSAndrew Gallatin 	*csum = htons(*csum);
2298c792928fSAndrew Gallatin 
2299c792928fSAndrew Gallatin 	/* save the tag */
230037d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2301c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
230237d89b0cSAndrew Gallatin #else
230337d89b0cSAndrew Gallatin 	{
230437d89b0cSAndrew Gallatin 		struct m_tag *mtag;
230537d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
230637d89b0cSAndrew Gallatin 				   M_NOWAIT);
230737d89b0cSAndrew Gallatin 		if (mtag == NULL)
230837d89b0cSAndrew Gallatin 			return;
230937d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
231037d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
231137d89b0cSAndrew Gallatin 	}
231237d89b0cSAndrew Gallatin 
231337d89b0cSAndrew Gallatin #endif
231437d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2315c792928fSAndrew Gallatin 
2316c792928fSAndrew Gallatin 	/*
2317c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2318c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2319c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2320c792928fSAndrew Gallatin 	 * type field is already in place.
2321c792928fSAndrew Gallatin 	 */
2322c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2323c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2324c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2325c792928fSAndrew Gallatin }
2326c792928fSAndrew Gallatin 
23275e7d8541SAndrew Gallatin 
23285e7d8541SAndrew Gallatin static inline void
23291e413cf9SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2330b2fc195eSAndrew Gallatin {
23311e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2332b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2333053e637fSAndrew Gallatin 	struct mbuf *m;
2334c792928fSAndrew Gallatin 	struct ether_header *eh;
23351e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2336053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2337b2fc195eSAndrew Gallatin 	int idx;
2338053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2339b2fc195eSAndrew Gallatin 
23401e413cf9SAndrew Gallatin 	sc = ss->sc;
2341b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23421e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2343b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2344053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2345b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2346b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2347b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
23481e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2349053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2350053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2351053e637fSAndrew Gallatin 		return;
2352b2fc195eSAndrew Gallatin 	}
2353053e637fSAndrew Gallatin 
2354b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2355b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2356b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2357b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2358b2fc195eSAndrew Gallatin 
2359b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2360b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2361b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2362b2fc195eSAndrew Gallatin 
2363053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2364053e637fSAndrew Gallatin 	 * aligned */
23655e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2366b2fc195eSAndrew Gallatin 
2367053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2368053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
23691e413cf9SAndrew Gallatin 	ss->ipackets++;
2370c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2371c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2372c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2373c792928fSAndrew Gallatin 	}
2374b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2375053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
23761e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2377b2fc195eSAndrew Gallatin 			return;
2378053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2379053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2380053e637fSAndrew Gallatin 		   checksum is good */
2381053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2382053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2383b2fc195eSAndrew Gallatin 	}
2384053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2385053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2386b2fc195eSAndrew Gallatin }
2387b2fc195eSAndrew Gallatin 
2388b2fc195eSAndrew Gallatin static inline void
23891e413cf9SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2390b2fc195eSAndrew Gallatin {
23911e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2392b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2393c792928fSAndrew Gallatin 	struct ether_header *eh;
2394b2fc195eSAndrew Gallatin 	struct mbuf *m;
23951e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2396b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2397b2fc195eSAndrew Gallatin 	int idx;
2398053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2399b2fc195eSAndrew Gallatin 
24001e413cf9SAndrew Gallatin 	sc = ss->sc;
2401b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
24021e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2403b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2404b2fc195eSAndrew Gallatin 	rx->cnt++;
2405b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2406b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2407b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
24081e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2409b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2410b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2411b2fc195eSAndrew Gallatin 		return;
2412b2fc195eSAndrew Gallatin 	}
2413b2fc195eSAndrew Gallatin 
2414b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2415b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2416b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2417b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2418b2fc195eSAndrew Gallatin 
2419b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2420b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2421b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2422b2fc195eSAndrew Gallatin 
2423b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2424b2fc195eSAndrew Gallatin 	 * aligned */
24255e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2426b2fc195eSAndrew Gallatin 
24279b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
24289b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
24291e413cf9SAndrew Gallatin 	ss->ipackets++;
2430c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2431c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2432c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2433c792928fSAndrew Gallatin 	}
2434b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2435053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
24361e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2437053e637fSAndrew Gallatin 			return;
2438053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2439053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2440053e637fSAndrew Gallatin 		   checksum is good */
2441053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2442053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2443053e637fSAndrew Gallatin 	}
2444b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2445b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2446b2fc195eSAndrew Gallatin }
2447b2fc195eSAndrew Gallatin 
2448b2fc195eSAndrew Gallatin static inline void
24491e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
24505e7d8541SAndrew Gallatin {
24511e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
2452053e637fSAndrew Gallatin 	struct lro_entry *lro;
24535e7d8541SAndrew Gallatin 	int limit = 0;
24545e7d8541SAndrew Gallatin 	uint16_t length;
24555e7d8541SAndrew Gallatin 	uint16_t checksum;
24565e7d8541SAndrew Gallatin 
24575e7d8541SAndrew Gallatin 
24585e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
24595e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
24605e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2461053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2462b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
24631e413cf9SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum);
24645e7d8541SAndrew Gallatin 		else
24651e413cf9SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum);
24665e7d8541SAndrew Gallatin 		rx_done->cnt++;
2467adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
24685e7d8541SAndrew Gallatin 
24695e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2470f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
24715e7d8541SAndrew Gallatin 			break;
2472053e637fSAndrew Gallatin 	}
24731e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_active)) {
24741e413cf9SAndrew Gallatin 		lro = SLIST_FIRST(&ss->lro_active);
24751e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
24761e413cf9SAndrew Gallatin 		mxge_lro_flush(ss, lro);
24775e7d8541SAndrew Gallatin 	}
24785e7d8541SAndrew Gallatin }
24795e7d8541SAndrew Gallatin 
24805e7d8541SAndrew Gallatin 
24815e7d8541SAndrew Gallatin static inline void
24821e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2483b2fc195eSAndrew Gallatin {
2484b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
24851e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2486b2fc195eSAndrew Gallatin 	struct mbuf *m;
2487b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2488f616ebc7SAndrew Gallatin 	int idx;
2489b2fc195eSAndrew Gallatin 
24901e413cf9SAndrew Gallatin 	tx = &ss->tx;
24911e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
24925e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2493b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2494b2fc195eSAndrew Gallatin 		tx->done++;
2495b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2496b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2497b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2498b2fc195eSAndrew Gallatin 		if (m != NULL) {
2499b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2500b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2501b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2502b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2503b2fc195eSAndrew Gallatin 			m_freem(m);
2504b2fc195eSAndrew Gallatin 		}
25055e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
25065e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
25075e7d8541SAndrew Gallatin 			tx->pkt_done++;
25085e7d8541SAndrew Gallatin 		}
2509b2fc195eSAndrew Gallatin 	}
2510b2fc195eSAndrew Gallatin 
2511b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2512b2fc195eSAndrew Gallatin            its OK to send packets */
2513b2fc195eSAndrew Gallatin 
2514b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2515b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
25161e413cf9SAndrew Gallatin 		mtx_lock(&ss->tx.mtx);
2517b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
25181e413cf9SAndrew Gallatin 		ss->tx.wake++;
25191e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
25201e413cf9SAndrew Gallatin 		mtx_unlock(&ss->tx.mtx);
2521b2fc195eSAndrew Gallatin 	}
2522b2fc195eSAndrew Gallatin }
2523b2fc195eSAndrew Gallatin 
252401638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2525c587e59fSAndrew Gallatin {
2526c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2527c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2528c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2529c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
253001638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2531c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2532c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2533c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2534c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2535c587e59fSAndrew Gallatin };
253601638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
253701638550SAndrew Gallatin {
25384ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
253901638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
254001638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
254101638550SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"}
254201638550SAndrew Gallatin };
2543c587e59fSAndrew Gallatin 
2544c587e59fSAndrew Gallatin static void
2545c587e59fSAndrew Gallatin mxge_set_media(mxge_softc_t *sc, int type)
2546c587e59fSAndrew Gallatin {
2547c587e59fSAndrew Gallatin 	sc->media_flags |= type;
2548c587e59fSAndrew Gallatin 	ifmedia_add(&sc->media, sc->media_flags, 0, NULL);
2549c587e59fSAndrew Gallatin 	ifmedia_set(&sc->media, sc->media_flags);
2550c587e59fSAndrew Gallatin }
2551c587e59fSAndrew Gallatin 
2552c587e59fSAndrew Gallatin 
2553c587e59fSAndrew Gallatin /*
2554c587e59fSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2555c587e59fSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2556c587e59fSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2557c587e59fSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2558c587e59fSAndrew Gallatin  * than in the interrupt handler itself.   This need only be done
2559c587e59fSAndrew Gallatin  * once, not each time the link is up.
2560c587e59fSAndrew Gallatin  */
2561c587e59fSAndrew Gallatin static void
2562c587e59fSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2563c587e59fSAndrew Gallatin {
2564c587e59fSAndrew Gallatin 	mxge_cmd_t cmd;
256501638550SAndrew Gallatin 	char *cage_type;
2566c587e59fSAndrew Gallatin 	char *ptr;
256701638550SAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
256801638550SAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
256901638550SAndrew Gallatin 	uint32_t byte;
2570c587e59fSAndrew Gallatin 
2571c587e59fSAndrew Gallatin 	sc->need_media_probe = 0;
2572c587e59fSAndrew Gallatin 
2573c587e59fSAndrew Gallatin 	/* if we've already set a media type, we're done */
2574c587e59fSAndrew Gallatin 	if (sc->media_flags  != (IFM_ETHER | IFM_AUTO))
2575c587e59fSAndrew Gallatin 		return;
2576c587e59fSAndrew Gallatin 
2577c587e59fSAndrew Gallatin 	/*
2578c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2579c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2580c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2581c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2582c587e59fSAndrew Gallatin 	 */
2583c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2584c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2585c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2586c587e59fSAndrew Gallatin 	}
2587c587e59fSAndrew Gallatin 
2588c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
258937d89b0cSAndrew Gallatin 		ptr = index(ptr, '-');
2590c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2591c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2592c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2593c587e59fSAndrew Gallatin 			return;
2594c587e59fSAndrew Gallatin 		}
2595c587e59fSAndrew Gallatin 	}
2596c587e59fSAndrew Gallatin 	if (*ptr == 'C') {
259701638550SAndrew Gallatin 		/* -C is CX4 */
2598c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2599c587e59fSAndrew Gallatin 		return;
2600c587e59fSAndrew Gallatin 	}
2601c587e59fSAndrew Gallatin 	else if (*ptr == 'Q') {
260201638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2603c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2604c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2605c587e59fSAndrew Gallatin 		return;
2606c587e59fSAndrew Gallatin 	}
2607c587e59fSAndrew Gallatin 
260801638550SAndrew Gallatin 	if (*ptr == 'R') {
260901638550SAndrew Gallatin 		/* -R is XFP */
261001638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
261101638550SAndrew Gallatin 		mxge_media_type_entries =
261201638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types) /
261301638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types[0]);
261401638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
261501638550SAndrew Gallatin 		cage_type = "XFP";
261601638550SAndrew Gallatin 	}
261701638550SAndrew Gallatin 
261801638550SAndrew Gallatin 	if (*ptr == 'S' || *(ptr +1) == 'S') {
261901638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
262001638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
262101638550SAndrew Gallatin 		mxge_media_type_entries =
262201638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types) /
262301638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types[0]);
262401638550SAndrew Gallatin 		cage_type = "SFP+";
262501638550SAndrew Gallatin 		byte = 3;
262601638550SAndrew Gallatin 	}
262701638550SAndrew Gallatin 
262801638550SAndrew Gallatin 	if (mxge_media_types == NULL) {
2629c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2630c587e59fSAndrew Gallatin 		return;
2631c587e59fSAndrew Gallatin 	}
2632c587e59fSAndrew Gallatin 
2633c587e59fSAndrew Gallatin 	/*
2634c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2635c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2636c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2637c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2638c587e59fSAndrew Gallatin 	 * a millisecond
2639c587e59fSAndrew Gallatin 	 */
2640c587e59fSAndrew Gallatin 
2641c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
264201638550SAndrew Gallatin 	cmd.data1 = byte;
264301638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
264401638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2645c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2646c587e59fSAndrew Gallatin 	}
264701638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
264801638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2649c587e59fSAndrew Gallatin 	}
2650c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2651c587e59fSAndrew Gallatin 		return;
2652c587e59fSAndrew Gallatin 	}
2653c587e59fSAndrew Gallatin 
2654c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
265501638550SAndrew Gallatin 	cmd.data0 = byte;
265601638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2657c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2658c587e59fSAndrew Gallatin 		DELAY(1000);
265901638550SAndrew Gallatin 		cmd.data0 = byte;
266001638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2661c587e59fSAndrew Gallatin 	}
2662c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
266301638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
266401638550SAndrew Gallatin 			      cage_type, err, ms);
2665c587e59fSAndrew Gallatin 		return;
2666c587e59fSAndrew Gallatin 	}
2667c587e59fSAndrew Gallatin 
2668c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2669c587e59fSAndrew Gallatin 		if (mxge_verbose)
267001638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2671c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2672c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2673c587e59fSAndrew Gallatin 		return;
2674c587e59fSAndrew Gallatin 	}
267501638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
2676c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2677c587e59fSAndrew Gallatin 			if (mxge_verbose)
267801638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
267901638550SAndrew Gallatin 					      cage_type,
2680c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2681c587e59fSAndrew Gallatin 
2682c587e59fSAndrew Gallatin 			mxge_set_media(sc, mxge_media_types[i].flag);
2683c587e59fSAndrew Gallatin 			return;
2684c587e59fSAndrew Gallatin 		}
2685c587e59fSAndrew Gallatin 	}
268601638550SAndrew Gallatin 	device_printf(sc->dev, "%s media 0x%x unknown\n", cage_type,
268701638550SAndrew Gallatin 		      cmd.data0);
2688c587e59fSAndrew Gallatin 
2689c587e59fSAndrew Gallatin 	return;
2690c587e59fSAndrew Gallatin }
2691c587e59fSAndrew Gallatin 
2692b2fc195eSAndrew Gallatin static void
26936d87a65dSAndrew Gallatin mxge_intr(void *arg)
2694b2fc195eSAndrew Gallatin {
26951e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
26961e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
26971e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
26981e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
26991e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
27005e7d8541SAndrew Gallatin 	uint32_t send_done_count;
27015e7d8541SAndrew Gallatin 	uint8_t valid;
2702b2fc195eSAndrew Gallatin 
2703b2fc195eSAndrew Gallatin 
27041e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
27051e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
27061e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
27071e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
27081e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
27091e413cf9SAndrew Gallatin 		return;
27101e413cf9SAndrew Gallatin 	}
27111e413cf9SAndrew Gallatin 
27125e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
27135e7d8541SAndrew Gallatin 	if (!stats->valid) {
27145e7d8541SAndrew Gallatin 		return;
2715b2fc195eSAndrew Gallatin 	}
27165e7d8541SAndrew Gallatin 	valid = stats->valid;
2717b2fc195eSAndrew Gallatin 
271891ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
27195e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
27205e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
27215e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
27225e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
27235e7d8541SAndrew Gallatin 			stats->valid = 0;
2724dc8731d4SAndrew Gallatin 	} else {
2725dc8731d4SAndrew Gallatin 		stats->valid = 0;
2726dc8731d4SAndrew Gallatin 	}
2727dc8731d4SAndrew Gallatin 
2728dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
27295e7d8541SAndrew Gallatin 	do {
27305e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
27315e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
27325e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
27335e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
27341e413cf9SAndrew Gallatin 			mxge_tx_done(ss, (int)send_done_count);
27351e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
27365e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2737b2fc195eSAndrew Gallatin 		}
273891ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
273973c7c83fSAndrew Gallatin 			wmb();
27405e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2741b2fc195eSAndrew Gallatin 
27425e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
27435e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
27445e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2745b2fc195eSAndrew Gallatin 			if (sc->link_state) {
27465e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
27475e7d8541SAndrew Gallatin 				if (mxge_verbose)
27485e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2749b2fc195eSAndrew Gallatin 			} else {
27505e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
27515e7d8541SAndrew Gallatin 				if (mxge_verbose)
27525e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2753b2fc195eSAndrew Gallatin 			}
2754c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
2755b2fc195eSAndrew Gallatin 		}
2756b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
27571e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
2758b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
27591e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
27605e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
27615e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
27625e7d8541SAndrew Gallatin 		}
2763c587e59fSAndrew Gallatin 
2764c587e59fSAndrew Gallatin 		if (stats->link_down) {
27655e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
2766c587e59fSAndrew Gallatin 			sc->link_state = 0;
2767c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
2768c587e59fSAndrew Gallatin 		}
2769b2fc195eSAndrew Gallatin 	}
2770b2fc195eSAndrew Gallatin 
27715e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
27725e7d8541SAndrew Gallatin 	if (valid & 0x1)
27731e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
27741e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
2775b2fc195eSAndrew Gallatin }
2776b2fc195eSAndrew Gallatin 
2777b2fc195eSAndrew Gallatin static void
27786d87a65dSAndrew Gallatin mxge_init(void *arg)
2779b2fc195eSAndrew Gallatin {
2780b2fc195eSAndrew Gallatin }
2781b2fc195eSAndrew Gallatin 
2782b2fc195eSAndrew Gallatin 
2783b2fc195eSAndrew Gallatin 
2784b2fc195eSAndrew Gallatin static void
27851e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
27861e413cf9SAndrew Gallatin {
27871e413cf9SAndrew Gallatin 	struct lro_entry *lro_entry;
27881e413cf9SAndrew Gallatin 	int i;
27891e413cf9SAndrew Gallatin 
27901e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_free)) {
27911e413cf9SAndrew Gallatin 		lro_entry = SLIST_FIRST(&ss->lro_free);
27921e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
27931e413cf9SAndrew Gallatin 		free(lro_entry, M_DEVBUF);
27941e413cf9SAndrew Gallatin 	}
27951e413cf9SAndrew Gallatin 
27961e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
27971e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
27981e413cf9SAndrew Gallatin 			continue;
27991e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
28001e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
28011e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
28021e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
28031e413cf9SAndrew Gallatin 	}
28041e413cf9SAndrew Gallatin 
28051e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
28061e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
28071e413cf9SAndrew Gallatin 			continue;
28081e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
28091e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
28101e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
28111e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
28121e413cf9SAndrew Gallatin 	}
28131e413cf9SAndrew Gallatin 
28141e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
28151e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
28161e413cf9SAndrew Gallatin 		return;
28171e413cf9SAndrew Gallatin 
28181e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
28191e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
28201e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
28211e413cf9SAndrew Gallatin 			continue;
28221e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
28231e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
28241e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
28251e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
28261e413cf9SAndrew Gallatin 	}
28271e413cf9SAndrew Gallatin }
28281e413cf9SAndrew Gallatin 
28291e413cf9SAndrew Gallatin static void
28306d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2831b2fc195eSAndrew Gallatin {
28321e413cf9SAndrew Gallatin 	int slice;
28331e413cf9SAndrew Gallatin 
28341e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
28351e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
28361e413cf9SAndrew Gallatin }
28371e413cf9SAndrew Gallatin 
28381e413cf9SAndrew Gallatin static void
28391e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
28401e413cf9SAndrew Gallatin {
2841b2fc195eSAndrew Gallatin 	int i;
2842b2fc195eSAndrew Gallatin 
2843b2fc195eSAndrew Gallatin 
28441e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
28451e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
28461e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
2847b2fc195eSAndrew Gallatin 
28481e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
28491e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
28501e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
28511e413cf9SAndrew Gallatin 
28521e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
28531e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
28541e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
28551e413cf9SAndrew Gallatin 
28561e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
28571e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
28581e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
28591e413cf9SAndrew Gallatin 
28601e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
28611e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
28621e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
28631e413cf9SAndrew Gallatin 
28641e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
28651e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
28661e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
28671e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
28681e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
2869b2fc195eSAndrew Gallatin 			}
28701e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
28711e413cf9SAndrew Gallatin 		}
28721e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
28731e413cf9SAndrew Gallatin 	}
28741e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
28751e413cf9SAndrew Gallatin 
28761e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
28771e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
28781e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
28791e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
28801e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
28811e413cf9SAndrew Gallatin 			}
28821e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
28831e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
28841e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
28851e413cf9SAndrew Gallatin 		}
28861e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
28871e413cf9SAndrew Gallatin 	}
28881e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
28891e413cf9SAndrew Gallatin 
28901e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
28911e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
28921e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
28931e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
28941e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
28951e413cf9SAndrew Gallatin 			}
28961e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
28971e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
28981e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
28991e413cf9SAndrew Gallatin 		}
29001e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
29011e413cf9SAndrew Gallatin 	}
29021e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
2903b2fc195eSAndrew Gallatin }
2904b2fc195eSAndrew Gallatin 
2905b2fc195eSAndrew Gallatin static void
29066d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2907b2fc195eSAndrew Gallatin {
29081e413cf9SAndrew Gallatin 	int slice;
2909b2fc195eSAndrew Gallatin 
29101e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
29111e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
2912c2657176SAndrew Gallatin }
2913b2fc195eSAndrew Gallatin 
2914b2fc195eSAndrew Gallatin static int
29151e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
29161e413cf9SAndrew Gallatin 		       int tx_ring_entries)
2917b2fc195eSAndrew Gallatin {
29181e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
29191e413cf9SAndrew Gallatin 	size_t bytes;
29201e413cf9SAndrew Gallatin 	int err, i;
2921b2fc195eSAndrew Gallatin 
2922b2fc195eSAndrew Gallatin 	err = ENOMEM;
2923b2fc195eSAndrew Gallatin 
29241e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
2925adae7080SAndrew Gallatin 
29261e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
29271e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
2928aed8e389SAndrew Gallatin 
2929b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
29301e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
29311e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29321e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow == NULL)
29331e413cf9SAndrew Gallatin 		return err;;
2934b2fc195eSAndrew Gallatin 
29351e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
29361e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29371e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow == NULL)
29381e413cf9SAndrew Gallatin 		return err;;
2939b2fc195eSAndrew Gallatin 
29401e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
29411e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
29421e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29431e413cf9SAndrew Gallatin 	if (ss->rx_small.info == NULL)
29441e413cf9SAndrew Gallatin 		return err;;
2945b2fc195eSAndrew Gallatin 
29461e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
29471e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29481e413cf9SAndrew Gallatin 	if (ss->rx_big.info == NULL)
29491e413cf9SAndrew Gallatin 		return err;;
2950b2fc195eSAndrew Gallatin 
29511e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
2952b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2953b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2954b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2955b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2956b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2957b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2958b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2959b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2960b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2961b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2962b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
29631e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
2964b2fc195eSAndrew Gallatin 	if (err != 0) {
2965b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2966b2fc195eSAndrew Gallatin 			      err);
29671e413cf9SAndrew Gallatin 		return err;;
2968b2fc195eSAndrew Gallatin 	}
2969b2fc195eSAndrew Gallatin 
2970b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2971b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2972b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2973b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2974b0f7b922SAndrew Gallatin #else
2975b0f7b922SAndrew Gallatin 				 0,			/* boundary */
2976b0f7b922SAndrew Gallatin #endif
2977b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2978b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2979b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2980053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
2981b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2982053e637fSAndrew Gallatin 				 3,			/* num segs */
2983b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
2984b0f7b922SAndrew Gallatin #else
2985b0f7b922SAndrew Gallatin 				 1,			/* num segs */
2986b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
2987b0f7b922SAndrew Gallatin #endif
2988b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2989b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
29901e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
2991b2fc195eSAndrew Gallatin 	if (err != 0) {
2992b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2993b2fc195eSAndrew Gallatin 			      err);
29941e413cf9SAndrew Gallatin 		return err;;
2995b2fc195eSAndrew Gallatin 	}
29961e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
29971e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
29981e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
2999b2fc195eSAndrew Gallatin 		if (err != 0) {
3000b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3001b2fc195eSAndrew Gallatin 				      err);
30021e413cf9SAndrew Gallatin 			return err;;
3003b2fc195eSAndrew Gallatin 		}
3004b2fc195eSAndrew Gallatin 	}
30051e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
30061e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3007b2fc195eSAndrew Gallatin 	if (err != 0) {
3008b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3009b2fc195eSAndrew Gallatin 			      err);
30101e413cf9SAndrew Gallatin 		return err;;
3011b2fc195eSAndrew Gallatin 	}
3012b2fc195eSAndrew Gallatin 
30131e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
30141e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
30151e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3016b2fc195eSAndrew Gallatin 		if (err != 0) {
3017b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3018b2fc195eSAndrew Gallatin 				      err);
30191e413cf9SAndrew Gallatin 			return err;;
3020b2fc195eSAndrew Gallatin 		}
3021b2fc195eSAndrew Gallatin 	}
30221e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
30231e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3024b2fc195eSAndrew Gallatin 	if (err != 0) {
3025b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3026b2fc195eSAndrew Gallatin 			      err);
30271e413cf9SAndrew Gallatin 		return err;;
30281e413cf9SAndrew Gallatin 	}
30291e413cf9SAndrew Gallatin 
30301e413cf9SAndrew Gallatin 	/* now allocate TX resouces */
30311e413cf9SAndrew Gallatin 
30321e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
30331e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
30341e413cf9SAndrew Gallatin 		return 0;
30351e413cf9SAndrew Gallatin 
30361e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
30371e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
30381e413cf9SAndrew Gallatin 
30391e413cf9SAndrew Gallatin 
30401e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
30411e413cf9SAndrew Gallatin 	bytes = 8 +
30421e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
30431e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
30441e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes == NULL)
30451e413cf9SAndrew Gallatin 		return err;;
30461e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
30471e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
30481e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
30491e413cf9SAndrew Gallatin 
30501e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
30511e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
30521e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
30531e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
30541e413cf9SAndrew Gallatin 	if (ss->tx.seg_list == NULL)
30551e413cf9SAndrew Gallatin 		return err;;
30561e413cf9SAndrew Gallatin 
30571e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
30581e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
30591e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30601e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30611e413cf9SAndrew Gallatin 		return err;;
30621e413cf9SAndrew Gallatin 
30631e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
30641e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30651e413cf9SAndrew Gallatin 				 1,			/* alignment */
30661e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
30671e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
30681e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
30691e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
30701e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
30711e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
30721e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
30731e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
30741e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
30751e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
30761e413cf9SAndrew Gallatin 
30771e413cf9SAndrew Gallatin 	if (err != 0) {
30781e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
30791e413cf9SAndrew Gallatin 			      err);
30801e413cf9SAndrew Gallatin 		return err;;
30811e413cf9SAndrew Gallatin 	}
30821e413cf9SAndrew Gallatin 
30831e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
30841e413cf9SAndrew Gallatin 	   in the ring */
30851e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30861e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
30871e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
30881e413cf9SAndrew Gallatin 		if (err != 0) {
30891e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
30901e413cf9SAndrew Gallatin 				      err);
30911e413cf9SAndrew Gallatin 			return err;;
30921e413cf9SAndrew Gallatin 		}
3093b2fc195eSAndrew Gallatin 	}
3094b2fc195eSAndrew Gallatin 	return 0;
3095b2fc195eSAndrew Gallatin 
3096b2fc195eSAndrew Gallatin }
3097b2fc195eSAndrew Gallatin 
30981e413cf9SAndrew Gallatin static int
30991e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
31001e413cf9SAndrew Gallatin {
31011e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
31021e413cf9SAndrew Gallatin 	int tx_ring_size;
31031e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
31041e413cf9SAndrew Gallatin 	int err, slice;
31051e413cf9SAndrew Gallatin 
31061e413cf9SAndrew Gallatin 	/* get ring sizes */
31071e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
31081e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
31091e413cf9SAndrew Gallatin 	if (err != 0) {
31101e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
31111e413cf9SAndrew Gallatin 		goto abort;
31121e413cf9SAndrew Gallatin 	}
31131e413cf9SAndrew Gallatin 
31141e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
31151e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
31161e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
31171e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
31181e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
31191e413cf9SAndrew Gallatin 
31201e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
31211e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
31221e413cf9SAndrew Gallatin 					     rx_ring_entries,
31231e413cf9SAndrew Gallatin 					     tx_ring_entries);
31241e413cf9SAndrew Gallatin 		if (err != 0)
31251e413cf9SAndrew Gallatin 			goto abort;
31261e413cf9SAndrew Gallatin 	}
31271e413cf9SAndrew Gallatin 	return 0;
31281e413cf9SAndrew Gallatin 
31291e413cf9SAndrew Gallatin abort:
31301e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
31311e413cf9SAndrew Gallatin 	return err;
31321e413cf9SAndrew Gallatin 
31331e413cf9SAndrew Gallatin }
31341e413cf9SAndrew Gallatin 
31351e413cf9SAndrew Gallatin 
3136053e637fSAndrew Gallatin static void
3137053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3138053e637fSAndrew Gallatin {
3139c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3140053e637fSAndrew Gallatin 
3141053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3142053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3143053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3144053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3145053e637fSAndrew Gallatin 		*nbufs = 1;
3146053e637fSAndrew Gallatin 		return;
3147053e637fSAndrew Gallatin 	}
3148053e637fSAndrew Gallatin 
3149053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3150053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3151053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3152053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3153053e637fSAndrew Gallatin 		*nbufs = 1;
3154053e637fSAndrew Gallatin 		return;
3155053e637fSAndrew Gallatin 	}
3156b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3157053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3158053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3159053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3160053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3161053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3162053e637fSAndrew Gallatin 	if (*nbufs == 3)
3163053e637fSAndrew Gallatin 		*nbufs = 4;
3164b0f7b922SAndrew Gallatin #else
3165b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3166b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3167b0f7b922SAndrew Gallatin 	*nbufs = 1;
3168b0f7b922SAndrew Gallatin #endif
3169053e637fSAndrew Gallatin }
3170053e637fSAndrew Gallatin 
3171b2fc195eSAndrew Gallatin static int
31721e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3173b2fc195eSAndrew Gallatin {
31741e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
31756d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3176b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
3177053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
31781e413cf9SAndrew Gallatin 	int err, i, slice;
3179b2fc195eSAndrew Gallatin 
31801e413cf9SAndrew Gallatin 
31811e413cf9SAndrew Gallatin 	sc = ss->sc;
31821e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
31831e413cf9SAndrew Gallatin 
31841e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_free);
31851e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_active);
3186053e637fSAndrew Gallatin 
3187053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
3188053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
31891e413cf9SAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF,
31901e413cf9SAndrew Gallatin 			       M_NOWAIT | M_ZERO);
3191053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
3192053e637fSAndrew Gallatin 			sc->lro_cnt = i;
3193053e637fSAndrew Gallatin 			break;
3194053e637fSAndrew Gallatin 		}
31951e413cf9SAndrew Gallatin 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
3196053e637fSAndrew Gallatin 	}
31971e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
31981e413cf9SAndrew Gallatin 
31991e413cf9SAndrew Gallatin 	err = 0;
32001e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
32011e413cf9SAndrew Gallatin 	if (slice == 0) {
32021e413cf9SAndrew Gallatin 		cmd.data0 = slice;
32031e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
32041e413cf9SAndrew Gallatin 		ss->tx.lanai =
32051e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
32061e413cf9SAndrew Gallatin 	}
32071e413cf9SAndrew Gallatin 	cmd.data0 = slice;
32081e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
32091e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
32101e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
32111e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
32121e413cf9SAndrew Gallatin 	cmd.data0 = slice;
32131e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
32141e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
32151e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
32161e413cf9SAndrew Gallatin 
32171e413cf9SAndrew Gallatin 	if (err != 0) {
32181e413cf9SAndrew Gallatin 		device_printf(sc->dev,
32191e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
32201e413cf9SAndrew Gallatin 		return EIO;
32211e413cf9SAndrew Gallatin 	}
32221e413cf9SAndrew Gallatin 
32231e413cf9SAndrew Gallatin 	/* stock receive rings */
32241e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
32251e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
32261e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
32271e413cf9SAndrew Gallatin 		if (err) {
32281e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
32291e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
32301e413cf9SAndrew Gallatin 			return ENOMEM;
32311e413cf9SAndrew Gallatin 		}
32321e413cf9SAndrew Gallatin 	}
32331e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32341e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
32351e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
32361e413cf9SAndrew Gallatin 	}
32371e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
32381e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
32391e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
32401e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
32411e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
32421e413cf9SAndrew Gallatin 		if (err) {
32431e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
32441e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
32451e413cf9SAndrew Gallatin 			return ENOMEM;
32461e413cf9SAndrew Gallatin 		}
32471e413cf9SAndrew Gallatin 	}
32481e413cf9SAndrew Gallatin 	return 0;
32491e413cf9SAndrew Gallatin }
32501e413cf9SAndrew Gallatin 
32511e413cf9SAndrew Gallatin static int
32521e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
32531e413cf9SAndrew Gallatin {
32541e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
32551e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
32561e413cf9SAndrew Gallatin 	bus_addr_t bus;
32571e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3258b2fc195eSAndrew Gallatin 
32597d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
32607d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
32617d542e2dSAndrew Gallatin 
3262adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3263b2fc195eSAndrew Gallatin 	if (err != 0) {
3264b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3265b2fc195eSAndrew Gallatin 		return EIO;
3266b2fc195eSAndrew Gallatin 	}
3267b2fc195eSAndrew Gallatin 
32681e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
32691e413cf9SAndrew Gallatin 		/* setup the indirection table */
32701e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
32711e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
32721e413cf9SAndrew Gallatin 				    &cmd);
3273b2fc195eSAndrew Gallatin 
32741e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
32751e413cf9SAndrew Gallatin 				     &cmd);
32761e413cf9SAndrew Gallatin 		if (err != 0) {
32771e413cf9SAndrew Gallatin 			device_printf(sc->dev,
32781e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
32791e413cf9SAndrew Gallatin 			return err;
32801e413cf9SAndrew Gallatin 		}
32811e413cf9SAndrew Gallatin 
32821e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
32831e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
32841e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
32851e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
32861e413cf9SAndrew Gallatin 
32871e413cf9SAndrew Gallatin 		cmd.data0 = 1;
32881e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
32891e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
32901e413cf9SAndrew Gallatin 		if (err != 0) {
32911e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
32921e413cf9SAndrew Gallatin 			return err;
32931e413cf9SAndrew Gallatin 		}
32941e413cf9SAndrew Gallatin 	}
32951e413cf9SAndrew Gallatin 
32961e413cf9SAndrew Gallatin 
32971e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
32981e413cf9SAndrew Gallatin 
32991e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3300053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3301053e637fSAndrew Gallatin 			    &cmd);
3302053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3303053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
33041e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3305053e637fSAndrew Gallatin 		device_printf(sc->dev,
3306053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
33071e413cf9SAndrew Gallatin 			      nbufs);
3308053e637fSAndrew Gallatin 		return EIO;
3309053e637fSAndrew Gallatin 	}
3310b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3311b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3312b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3313c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
33145e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3315b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
33165e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3317b2fc195eSAndrew Gallatin 			     &cmd);
3318053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
33195e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
33200fa7f681SAndrew Gallatin 
33210fa7f681SAndrew Gallatin 	if (err != 0) {
33220fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
33230fa7f681SAndrew Gallatin 		goto abort;
33240fa7f681SAndrew Gallatin 	}
33250fa7f681SAndrew Gallatin 
3326b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
33271e413cf9SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->ss->fw_stats_dma.bus_addr);
33281e413cf9SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->ss->fw_stats_dma.bus_addr);
33290fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
33300fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
33310fa7f681SAndrew Gallatin 
33320fa7f681SAndrew Gallatin 	if (err != 0) {
33331e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
33340fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
33350fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
33360fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
33370fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
33380fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
33390fa7f681SAndrew Gallatin 				    &cmd);
33400fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
33410fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
33420fa7f681SAndrew Gallatin 	} else {
33430fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
33440fa7f681SAndrew Gallatin 	}
3345b2fc195eSAndrew Gallatin 
3346b2fc195eSAndrew Gallatin 	if (err != 0) {
3347b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3348b2fc195eSAndrew Gallatin 		goto abort;
3349b2fc195eSAndrew Gallatin 	}
3350b2fc195eSAndrew Gallatin 
33511e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33521e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
33531e413cf9SAndrew Gallatin 		if (err != 0) {
33541e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
33551e413cf9SAndrew Gallatin 				      slice);
33561e413cf9SAndrew Gallatin 			goto abort;
33571e413cf9SAndrew Gallatin 		}
33581e413cf9SAndrew Gallatin 	}
33591e413cf9SAndrew Gallatin 
3360b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
33615e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3362b2fc195eSAndrew Gallatin 	if (err) {
3363b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3364b2fc195eSAndrew Gallatin 		goto abort;
3365b2fc195eSAndrew Gallatin 	}
3366b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3367b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3368e749ef6bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3369b2fc195eSAndrew Gallatin 
3370b2fc195eSAndrew Gallatin 	return 0;
3371b2fc195eSAndrew Gallatin 
3372b2fc195eSAndrew Gallatin 
3373b2fc195eSAndrew Gallatin abort:
33746d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3375a98d6cd7SAndrew Gallatin 
3376b2fc195eSAndrew Gallatin 	return err;
3377b2fc195eSAndrew Gallatin }
3378b2fc195eSAndrew Gallatin 
3379b2fc195eSAndrew Gallatin static int
33806d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
3381b2fc195eSAndrew Gallatin {
33826d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3383b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3384b2fc195eSAndrew Gallatin 
3385e749ef6bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3386b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3387b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
338873c7c83fSAndrew Gallatin 	wmb();
33895e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3390b2fc195eSAndrew Gallatin 	if (err) {
3391b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
3392b2fc195eSAndrew Gallatin 	}
3393b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3394b2fc195eSAndrew Gallatin 		/* wait for down irq */
3395dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
3396b2fc195eSAndrew Gallatin 	}
339773c7c83fSAndrew Gallatin 	wmb();
3398b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3399b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
3400b2fc195eSAndrew Gallatin 	}
3401a98d6cd7SAndrew Gallatin 
34026d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3403a98d6cd7SAndrew Gallatin 
3404b2fc195eSAndrew Gallatin 	return 0;
3405b2fc195eSAndrew Gallatin }
3406b2fc195eSAndrew Gallatin 
3407dce01b9bSAndrew Gallatin static void
3408dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3409dce01b9bSAndrew Gallatin {
3410dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3411dce01b9bSAndrew Gallatin 	int reg;
3412dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
3413dce01b9bSAndrew Gallatin 
3414dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
3415dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
3416dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3417dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3418dce01b9bSAndrew Gallatin 
3419dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
3420dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
3421dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
3422dce01b9bSAndrew Gallatin 	}
3423dce01b9bSAndrew Gallatin 
3424dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3425dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3426dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
3427dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
3428dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
3429dce01b9bSAndrew Gallatin }
3430dce01b9bSAndrew Gallatin 
3431dce01b9bSAndrew Gallatin static uint32_t
3432dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3433dce01b9bSAndrew Gallatin {
3434dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3435dce01b9bSAndrew Gallatin 	uint32_t vs;
3436dce01b9bSAndrew Gallatin 
3437dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
3438dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
3439dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3440dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3441dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3442dce01b9bSAndrew Gallatin 	}
3443dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3444dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3445dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3446dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3447dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3448dce01b9bSAndrew Gallatin }
3449dce01b9bSAndrew Gallatin 
3450e749ef6bSAndrew Gallatin static int
3451dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3452dce01b9bSAndrew Gallatin {
3453e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3454dce01b9bSAndrew Gallatin 	int err;
3455dce01b9bSAndrew Gallatin 	uint32_t reboot;
3456dce01b9bSAndrew Gallatin 	uint16_t cmd;
3457dce01b9bSAndrew Gallatin 
3458dce01b9bSAndrew Gallatin 	err = ENXIO;
3459dce01b9bSAndrew Gallatin 
3460dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3461dce01b9bSAndrew Gallatin 
3462dce01b9bSAndrew Gallatin 	/*
3463dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3464dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3465dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3466dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3467dce01b9bSAndrew Gallatin 	 * again
3468dce01b9bSAndrew Gallatin 	 */
3469dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3470dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3471dce01b9bSAndrew Gallatin 		/*
3472dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3473dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3474dce01b9bSAndrew Gallatin 		 * back, then give up
3475dce01b9bSAndrew Gallatin 		 */
3476dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3477dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3478dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3479dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3480e749ef6bSAndrew Gallatin 			return (err);
3481dce01b9bSAndrew Gallatin 		}
3482dce01b9bSAndrew Gallatin 	}
3483dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3484dce01b9bSAndrew Gallatin 		/* print the reboot status */
3485dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3486dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3487dce01b9bSAndrew Gallatin 			      reboot);
3488dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3489e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3490e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3491dce01b9bSAndrew Gallatin 
3492dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3493dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
349410882804SAndrew Gallatin 
349510882804SAndrew Gallatin 		if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
349610882804SAndrew Gallatin 			mxge_close(sc);
349710882804SAndrew Gallatin 			err = mxge_open(sc);
349810882804SAndrew Gallatin 		}
3499dce01b9bSAndrew Gallatin 	} else {
3500dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC did not reboot, ring state:\n");
3501dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "tx.req=%d tx.done=%d\n",
35021e413cf9SAndrew Gallatin 			      sc->ss->tx.req, sc->ss->tx.done);
3503dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
35041e413cf9SAndrew Gallatin 			      sc->ss->tx.pkt_done,
35051e413cf9SAndrew Gallatin 			      be32toh(sc->ss->fw_stats->send_done_count));
350610882804SAndrew Gallatin 		device_printf(sc->dev, "not resetting\n");
3507dce01b9bSAndrew Gallatin 	}
3508e749ef6bSAndrew Gallatin 	return (err);
3509dce01b9bSAndrew Gallatin }
3510dce01b9bSAndrew Gallatin 
3511e749ef6bSAndrew Gallatin static int
3512dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3513dce01b9bSAndrew Gallatin {
35141e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &sc->ss->tx;
35151e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3516e749ef6bSAndrew Gallatin 	int err = 0;
3517dce01b9bSAndrew Gallatin 
3518dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3519dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3520dce01b9bSAndrew Gallatin 	if (tx->req != tx->done &&
3521dce01b9bSAndrew Gallatin 	    tx->watchdog_req != tx->watchdog_done &&
3522c587e59fSAndrew Gallatin 	    tx->done == tx->watchdog_done) {
3523c587e59fSAndrew Gallatin 		/* check for pause blocking before resetting */
3524c587e59fSAndrew Gallatin 		if (tx->watchdog_rx_pause == rx_pause)
3525e749ef6bSAndrew Gallatin 			err = mxge_watchdog_reset(sc);
3526c587e59fSAndrew Gallatin 		else
3527c587e59fSAndrew Gallatin 			device_printf(sc->dev, "Flow control blocking "
3528c587e59fSAndrew Gallatin 				      "xmits, check link partner\n");
3529c587e59fSAndrew Gallatin 	}
3530dce01b9bSAndrew Gallatin 
3531dce01b9bSAndrew Gallatin 	tx->watchdog_req = tx->req;
3532dce01b9bSAndrew Gallatin 	tx->watchdog_done = tx->done;
3533c587e59fSAndrew Gallatin 	tx->watchdog_rx_pause = rx_pause;
3534c587e59fSAndrew Gallatin 
3535c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3536c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3537e749ef6bSAndrew Gallatin 	return (err);
3538dce01b9bSAndrew Gallatin }
3539dce01b9bSAndrew Gallatin 
3540dce01b9bSAndrew Gallatin static void
35411e413cf9SAndrew Gallatin mxge_update_stats(mxge_softc_t *sc)
35421e413cf9SAndrew Gallatin {
35431e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
35441e413cf9SAndrew Gallatin 	u_long ipackets = 0;
35451e413cf9SAndrew Gallatin 	int slice;
35461e413cf9SAndrew Gallatin 
35471e413cf9SAndrew Gallatin 	for(slice = 0; slice < sc->num_slices; slice++) {
35481e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
35491e413cf9SAndrew Gallatin 		ipackets += ss->ipackets;
35501e413cf9SAndrew Gallatin 	}
35511e413cf9SAndrew Gallatin 	sc->ifp->if_ipackets = ipackets;
35521e413cf9SAndrew Gallatin 
35531e413cf9SAndrew Gallatin }
35541e413cf9SAndrew Gallatin static void
3555dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3556dce01b9bSAndrew Gallatin {
3557dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
3558e749ef6bSAndrew Gallatin 	int err = 0;
3559dce01b9bSAndrew Gallatin 
35601e413cf9SAndrew Gallatin 	/* aggregate stats from different slices */
35611e413cf9SAndrew Gallatin 	mxge_update_stats(sc);
35621e413cf9SAndrew Gallatin 	if (!sc->watchdog_countdown) {
3563e749ef6bSAndrew Gallatin 		err = mxge_watchdog(sc);
35641e413cf9SAndrew Gallatin 		sc->watchdog_countdown = 4;
35651e413cf9SAndrew Gallatin 	}
35661e413cf9SAndrew Gallatin 	sc->watchdog_countdown--;
3567e749ef6bSAndrew Gallatin 	if (err == 0)
3568e749ef6bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3569e749ef6bSAndrew Gallatin 
3570dce01b9bSAndrew Gallatin }
3571b2fc195eSAndrew Gallatin 
3572b2fc195eSAndrew Gallatin static int
35736d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
3574b2fc195eSAndrew Gallatin {
3575b2fc195eSAndrew Gallatin 	return EINVAL;
3576b2fc195eSAndrew Gallatin }
3577b2fc195eSAndrew Gallatin 
3578b2fc195eSAndrew Gallatin static int
35796d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3580b2fc195eSAndrew Gallatin {
3581b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
3582b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3583b2fc195eSAndrew Gallatin 	int err = 0;
3584b2fc195eSAndrew Gallatin 
3585b2fc195eSAndrew Gallatin 
3586c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3587053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3588b2fc195eSAndrew Gallatin 		return EINVAL;
3589a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3590b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
3591b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
3592b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
35936d87a65dSAndrew Gallatin 		mxge_close(sc);
35946d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3595b2fc195eSAndrew Gallatin 		if (err != 0) {
3596b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
35976d87a65dSAndrew Gallatin 			mxge_close(sc);
35986d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3599b2fc195eSAndrew Gallatin 		}
3600b2fc195eSAndrew Gallatin 	}
3601a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3602b2fc195eSAndrew Gallatin 	return err;
3603b2fc195eSAndrew Gallatin }
3604b2fc195eSAndrew Gallatin 
3605b2fc195eSAndrew Gallatin static void
36066d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
3607b2fc195eSAndrew Gallatin {
36086d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3609b2fc195eSAndrew Gallatin 
3610b2fc195eSAndrew Gallatin 
3611b2fc195eSAndrew Gallatin 	if (sc == NULL)
3612b2fc195eSAndrew Gallatin 		return;
3613b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3614c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3615b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
3616c587e59fSAndrew Gallatin 	ifmr->ifm_active |= sc->link_state ? IFM_FDX : 0;
3617b2fc195eSAndrew Gallatin }
3618b2fc195eSAndrew Gallatin 
3619b2fc195eSAndrew Gallatin static int
36206d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
3621b2fc195eSAndrew Gallatin {
36226d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3623b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
3624b2fc195eSAndrew Gallatin 	int err, mask;
3625b2fc195eSAndrew Gallatin 
3626b2fc195eSAndrew Gallatin 	err = 0;
3627b2fc195eSAndrew Gallatin 	switch (command) {
3628b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
3629b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
3630b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
3631b2fc195eSAndrew Gallatin 		break;
3632b2fc195eSAndrew Gallatin 
3633b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
36346d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
3635b2fc195eSAndrew Gallatin 		break;
3636b2fc195eSAndrew Gallatin 
3637b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
3638a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3639b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
3640dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
36416d87a65dSAndrew Gallatin 				err = mxge_open(sc);
3642dce01b9bSAndrew Gallatin 			} else {
36430fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
36440fa7f681SAndrew Gallatin 				   flag chages */
36450fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
36460fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
36470fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
36480fa7f681SAndrew Gallatin 			}
3649b2fc195eSAndrew Gallatin 		} else {
3650dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
36511e413cf9SAndrew Gallatin 				mxge_close(sc);
3652dce01b9bSAndrew Gallatin 			}
3653b2fc195eSAndrew Gallatin 		}
3654a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3655b2fc195eSAndrew Gallatin 		break;
3656b2fc195eSAndrew Gallatin 
3657b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
3658b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
3659a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
36600fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
3661a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3662b2fc195eSAndrew Gallatin 		break;
3663b2fc195eSAndrew Gallatin 
3664b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
3665a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3666b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
3667b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
3668b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
3669aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
3670aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3671aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3672b2fc195eSAndrew Gallatin 			} else {
3673b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3674b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3675b2fc195eSAndrew Gallatin 			}
3676b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3677b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
3678b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
36795e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
3680b2fc195eSAndrew Gallatin 			} else {
3681b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
36825e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
3683b2fc195eSAndrew Gallatin 			}
3684b2fc195eSAndrew Gallatin 		}
3685aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
3686aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
3687aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
3688aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
3689aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
3690aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
3691aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
3692aed8e389SAndrew Gallatin 			} else {
3693aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
3694aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
3695aed8e389SAndrew Gallatin 				err = EINVAL;
3696aed8e389SAndrew Gallatin 			}
3697aed8e389SAndrew Gallatin 		}
3698f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
3699f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
3700f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
3701f04b33f8SAndrew Gallatin 			else
3702f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
3703f04b33f8SAndrew Gallatin 		}
3704c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
3705c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
3706a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3707c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
3708c792928fSAndrew Gallatin 
3709b2fc195eSAndrew Gallatin 		break;
3710b2fc195eSAndrew Gallatin 
3711b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
3712b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
3713b2fc195eSAndrew Gallatin 				    &sc->media, command);
3714b2fc195eSAndrew Gallatin                 break;
3715b2fc195eSAndrew Gallatin 
3716b2fc195eSAndrew Gallatin 	default:
3717b2fc195eSAndrew Gallatin 		err = ENOTTY;
3718b2fc195eSAndrew Gallatin         }
3719b2fc195eSAndrew Gallatin 	return err;
3720b2fc195eSAndrew Gallatin }
3721b2fc195eSAndrew Gallatin 
3722b2fc195eSAndrew Gallatin static void
37236d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
3724b2fc195eSAndrew Gallatin {
3725b2fc195eSAndrew Gallatin 
37261e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
37276d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
37286d87a65dSAndrew Gallatin 			  &mxge_flow_control);
37296d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
37306d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
37316d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
37326d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
3733d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
3734d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
37355e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
37365e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
37375e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
37385e7d8541SAndrew Gallatin 			  &mxge_verbose);
3739dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
3740053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
37411e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
37421e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
3743f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
3744f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
3745b2fc195eSAndrew Gallatin 
37465e7d8541SAndrew Gallatin 	if (bootverbose)
37475e7d8541SAndrew Gallatin 		mxge_verbose = 1;
37486d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
37496d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
3750dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
37511e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
37526d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
37531e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
37541e413cf9SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_SRC_PORT) {
37551e413cf9SAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
3756b2fc195eSAndrew Gallatin 	}
37571e413cf9SAndrew Gallatin }
37581e413cf9SAndrew Gallatin 
37591e413cf9SAndrew Gallatin 
37601e413cf9SAndrew Gallatin static void
37611e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
37621e413cf9SAndrew Gallatin {
37631e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
37641e413cf9SAndrew Gallatin 	int i;
37651e413cf9SAndrew Gallatin 
37661e413cf9SAndrew Gallatin 
37671e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
37681e413cf9SAndrew Gallatin 		return;
37691e413cf9SAndrew Gallatin 
37701e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
37711e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
37721e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
37731e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
37741e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
37751e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
37761e413cf9SAndrew Gallatin 		}
37771e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
37781e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
37791e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
37801e413cf9SAndrew Gallatin 		}
37811e413cf9SAndrew Gallatin 	}
37821e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
37831e413cf9SAndrew Gallatin 	sc->ss = NULL;
37841e413cf9SAndrew Gallatin }
37851e413cf9SAndrew Gallatin 
37861e413cf9SAndrew Gallatin static int
37871e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
37881e413cf9SAndrew Gallatin {
37891e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
37901e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
37911e413cf9SAndrew Gallatin 	size_t bytes;
37921e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
37931e413cf9SAndrew Gallatin 
37941e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
37951e413cf9SAndrew Gallatin 	if (err != 0) {
37961e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
37971e413cf9SAndrew Gallatin 		return err;
37981e413cf9SAndrew Gallatin 	}
37991e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
38001e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
38011e413cf9SAndrew Gallatin 
38021e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
38031e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
38041e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
38051e413cf9SAndrew Gallatin 		return (ENOMEM);
38061e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
38071e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
38081e413cf9SAndrew Gallatin 
38091e413cf9SAndrew Gallatin 		ss->sc = sc;
38101e413cf9SAndrew Gallatin 
38111e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
38121e413cf9SAndrew Gallatin 
38131e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
38141e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
38151e413cf9SAndrew Gallatin 		if (err != 0)
38161e413cf9SAndrew Gallatin 			goto abort;
38171e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
38181e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
38191e413cf9SAndrew Gallatin 
38201e413cf9SAndrew Gallatin 		/*
38211e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
38221e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
38231e413cf9SAndrew Gallatin 		 * slice for now
38241e413cf9SAndrew Gallatin 		 */
38251e413cf9SAndrew Gallatin 		if (i > 0)
38261e413cf9SAndrew Gallatin 			continue;
38271e413cf9SAndrew Gallatin 
38281e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
38291e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
38301e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
38311e413cf9SAndrew Gallatin 		if (err != 0)
38321e413cf9SAndrew Gallatin 			goto abort;
38331e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
38341e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
38351e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
38361e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
38371e413cf9SAndrew Gallatin 	}
38381e413cf9SAndrew Gallatin 
38391e413cf9SAndrew Gallatin 	return (0);
38401e413cf9SAndrew Gallatin 
38411e413cf9SAndrew Gallatin abort:
38421e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
38431e413cf9SAndrew Gallatin 	return (ENOMEM);
38441e413cf9SAndrew Gallatin }
38451e413cf9SAndrew Gallatin 
38461e413cf9SAndrew Gallatin static void
38471e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
38481e413cf9SAndrew Gallatin {
38491e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
38501e413cf9SAndrew Gallatin 	char *old_fw;
38511e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
38521e413cf9SAndrew Gallatin 
38531e413cf9SAndrew Gallatin 	sc->num_slices = 1;
38541e413cf9SAndrew Gallatin 	/*
38551e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
38561e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
38571e413cf9SAndrew Gallatin 	 */
38581e413cf9SAndrew Gallatin 
38591e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
38601e413cf9SAndrew Gallatin 		return;
38611e413cf9SAndrew Gallatin 
38621e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
38631e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
38641e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
38651e413cf9SAndrew Gallatin 		return;
38661e413cf9SAndrew Gallatin 
38671e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
38681e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
38691e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
38701e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
38711e413cf9SAndrew Gallatin 	else
38721e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
38731e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
38741e413cf9SAndrew Gallatin 	if (status != 0) {
38751e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
38761e413cf9SAndrew Gallatin 		return;
38771e413cf9SAndrew Gallatin 	}
38781e413cf9SAndrew Gallatin 
38791e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
38801e413cf9SAndrew Gallatin 	   is alive */
38811e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
38821e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
38831e413cf9SAndrew Gallatin 	if (status != 0) {
38841e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
38851e413cf9SAndrew Gallatin 		goto abort_with_fw;
38861e413cf9SAndrew Gallatin 	}
38871e413cf9SAndrew Gallatin 
38881e413cf9SAndrew Gallatin 	/* get rx ring size */
38891e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
38901e413cf9SAndrew Gallatin 	if (status != 0) {
38911e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
38921e413cf9SAndrew Gallatin 		goto abort_with_fw;
38931e413cf9SAndrew Gallatin 	}
38941e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
38951e413cf9SAndrew Gallatin 
38961e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
38971e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
38981e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
38991e413cf9SAndrew Gallatin 	if (status != 0) {
39001e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
39011e413cf9SAndrew Gallatin 		goto abort_with_fw;
39021e413cf9SAndrew Gallatin 	}
39031e413cf9SAndrew Gallatin 
39041e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
39051e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
39061e413cf9SAndrew Gallatin 	if (status != 0) {
39071e413cf9SAndrew Gallatin 		device_printf(sc->dev,
39081e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
39091e413cf9SAndrew Gallatin 		goto abort_with_fw;
39101e413cf9SAndrew Gallatin 	}
39111e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
39121e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
39131e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
39141e413cf9SAndrew Gallatin 
39151e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
39161e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
39171e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
39181e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
39191e413cf9SAndrew Gallatin 	} else {
39201e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
39211e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
39221e413cf9SAndrew Gallatin 	}
39231e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
39241e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
39251e413cf9SAndrew Gallatin 		sc->num_slices--;
39261e413cf9SAndrew Gallatin 
39271e413cf9SAndrew Gallatin 	if (mxge_verbose)
39281e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
39291e413cf9SAndrew Gallatin 			      sc->num_slices);
39301e413cf9SAndrew Gallatin 
39311e413cf9SAndrew Gallatin 	return;
39321e413cf9SAndrew Gallatin 
39331e413cf9SAndrew Gallatin abort_with_fw:
39341e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
39351e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
39361e413cf9SAndrew Gallatin }
39371e413cf9SAndrew Gallatin 
39381e413cf9SAndrew Gallatin static int
39391e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
39401e413cf9SAndrew Gallatin {
39411e413cf9SAndrew Gallatin 	size_t bytes;
39421e413cf9SAndrew Gallatin 	int count, err, i, rid;
39431e413cf9SAndrew Gallatin 
39441e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
39451e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
39461e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
39471e413cf9SAndrew Gallatin 
39481e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
39491e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
39501e413cf9SAndrew Gallatin 		return ENXIO;
39511e413cf9SAndrew Gallatin 	}
39521e413cf9SAndrew Gallatin 
39531e413cf9SAndrew Gallatin 	count = sc->num_slices;
39541e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
39551e413cf9SAndrew Gallatin 	if (err != 0) {
39561e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
39571e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
39581e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
39591e413cf9SAndrew Gallatin 	}
39601e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
39611e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
39621e413cf9SAndrew Gallatin 			      count, sc->num_slices);
39631e413cf9SAndrew Gallatin 		device_printf(sc->dev,
39641e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
39651e413cf9SAndrew Gallatin 			      count);
39661e413cf9SAndrew Gallatin 		err = ENOSPC;
39671e413cf9SAndrew Gallatin 		goto abort_with_msix;
39681e413cf9SAndrew Gallatin 	}
39691e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
39701e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39711e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
39721e413cf9SAndrew Gallatin 		err = ENOMEM;
39731e413cf9SAndrew Gallatin 		goto abort_with_msix;
39741e413cf9SAndrew Gallatin 	}
39751e413cf9SAndrew Gallatin 
39761e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
39771e413cf9SAndrew Gallatin 		rid = i + 1;
39781e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
39791e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
39801e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
39811e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
39821e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
39831e413cf9SAndrew Gallatin 				      " for message %d\n", i);
39841e413cf9SAndrew Gallatin 			err = ENXIO;
39851e413cf9SAndrew Gallatin 			goto abort_with_res;
39861e413cf9SAndrew Gallatin 		}
39871e413cf9SAndrew Gallatin 	}
39881e413cf9SAndrew Gallatin 
39891e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
39901e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39911e413cf9SAndrew Gallatin 
39921e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
39931e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
39941e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
399537d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
399637d89b0cSAndrew Gallatin 				     NULL,
399737d89b0cSAndrew Gallatin #endif
399837d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
39991e413cf9SAndrew Gallatin 		if (err != 0) {
40001e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
40011e413cf9SAndrew Gallatin 				      "message %d\n", i);
40021e413cf9SAndrew Gallatin 			goto abort_with_intr;
40031e413cf9SAndrew Gallatin 		}
40041e413cf9SAndrew Gallatin 	}
40051e413cf9SAndrew Gallatin 
40061e413cf9SAndrew Gallatin 	if (mxge_verbose) {
40071e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
40081e413cf9SAndrew Gallatin 			      sc->num_slices);
40091e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
40101e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
40111e413cf9SAndrew Gallatin 		printf("\n");
40121e413cf9SAndrew Gallatin 	}
40131e413cf9SAndrew Gallatin 	return (0);
40141e413cf9SAndrew Gallatin 
40151e413cf9SAndrew Gallatin abort_with_intr:
40161e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40171e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
40181e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
40191e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
40201e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
40211e413cf9SAndrew Gallatin 		}
40221e413cf9SAndrew Gallatin 	}
40231e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
40241e413cf9SAndrew Gallatin 
40251e413cf9SAndrew Gallatin 
40261e413cf9SAndrew Gallatin abort_with_res:
40271e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40281e413cf9SAndrew Gallatin 		rid = i + 1;
40291e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
40301e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
40311e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
40321e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
40331e413cf9SAndrew Gallatin 	}
40341e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
40351e413cf9SAndrew Gallatin 
40361e413cf9SAndrew Gallatin 
40371e413cf9SAndrew Gallatin abort_with_msix:
40381e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
40391e413cf9SAndrew Gallatin 
40401e413cf9SAndrew Gallatin abort_with_msix_table:
40411e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40421e413cf9SAndrew Gallatin 			     sc->msix_table_res);
40431e413cf9SAndrew Gallatin 
40441e413cf9SAndrew Gallatin 	return err;
40451e413cf9SAndrew Gallatin }
40461e413cf9SAndrew Gallatin 
40471e413cf9SAndrew Gallatin static int
40481e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
40491e413cf9SAndrew Gallatin {
40501e413cf9SAndrew Gallatin 	int count, err, rid;
40511e413cf9SAndrew Gallatin 
40521e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
40531e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
40541e413cf9SAndrew Gallatin 		rid = 1;
40551e413cf9SAndrew Gallatin 	} else {
40561e413cf9SAndrew Gallatin 		rid = 0;
405791ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
40581e413cf9SAndrew Gallatin 	}
40591e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
40601e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
40611e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
40621e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
40631e413cf9SAndrew Gallatin 		return ENXIO;
40641e413cf9SAndrew Gallatin 	}
40651e413cf9SAndrew Gallatin 	if (mxge_verbose)
40661e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
406791ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
40681e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
40691e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
40701e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
407137d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
407237d89b0cSAndrew Gallatin 			     NULL,
407337d89b0cSAndrew Gallatin #endif
407437d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
40751e413cf9SAndrew Gallatin 	if (err != 0) {
40761e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
407791ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
407891ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
40791e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
40801e413cf9SAndrew Gallatin 	}
40811e413cf9SAndrew Gallatin 	return err;
40821e413cf9SAndrew Gallatin }
40831e413cf9SAndrew Gallatin 
40841e413cf9SAndrew Gallatin static void
40851e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
40861e413cf9SAndrew Gallatin {
40871e413cf9SAndrew Gallatin 	int i, rid;
40881e413cf9SAndrew Gallatin 
40891e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40901e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
40911e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
40921e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
40931e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
40941e413cf9SAndrew Gallatin 		}
40951e413cf9SAndrew Gallatin 	}
40961e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
40971e413cf9SAndrew Gallatin 
40981e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40991e413cf9SAndrew Gallatin 		rid = i + 1;
41001e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
41011e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
41021e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
41031e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
41041e413cf9SAndrew Gallatin 	}
41051e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
41061e413cf9SAndrew Gallatin 
41071e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
41081e413cf9SAndrew Gallatin 			     sc->msix_table_res);
41091e413cf9SAndrew Gallatin 
41101e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
41111e413cf9SAndrew Gallatin 	return;
41121e413cf9SAndrew Gallatin }
41131e413cf9SAndrew Gallatin 
41141e413cf9SAndrew Gallatin static void
41151e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
41161e413cf9SAndrew Gallatin {
41171e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
41181e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
411991ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
412091ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
41211e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
41221e413cf9SAndrew Gallatin }
41231e413cf9SAndrew Gallatin 
41241e413cf9SAndrew Gallatin static void
41251e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
41261e413cf9SAndrew Gallatin {
41271e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
41281e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
41291e413cf9SAndrew Gallatin 	else
41301e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
41311e413cf9SAndrew Gallatin }
41321e413cf9SAndrew Gallatin 
41331e413cf9SAndrew Gallatin static int
41341e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
41351e413cf9SAndrew Gallatin {
41361e413cf9SAndrew Gallatin 	int err;
41371e413cf9SAndrew Gallatin 
41381e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
41391e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
41401e413cf9SAndrew Gallatin 	else
41411e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
41421e413cf9SAndrew Gallatin 
41431e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
41441e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
41451e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
41461e413cf9SAndrew Gallatin 	}
41471e413cf9SAndrew Gallatin 	return err;
41481e413cf9SAndrew Gallatin }
41491e413cf9SAndrew Gallatin 
4150b2fc195eSAndrew Gallatin 
4151b2fc195eSAndrew Gallatin static int
41526d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4153b2fc195eSAndrew Gallatin {
41546d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4155b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
41561e413cf9SAndrew Gallatin 	int err, rid;
4157b2fc195eSAndrew Gallatin 
4158b2fc195eSAndrew Gallatin 	sc->dev = dev;
41596d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4160b2fc195eSAndrew Gallatin 
4161b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
4162b2fc195eSAndrew Gallatin 				 1,			/* alignment */
41631e413cf9SAndrew Gallatin 				 0,			/* boundary */
4164b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4165b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4166b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4167aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
41685e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
41691e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4170b2fc195eSAndrew Gallatin 				 0,			/* flags */
4171b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4172b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4173b2fc195eSAndrew Gallatin 
4174b2fc195eSAndrew Gallatin 	if (err != 0) {
4175b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4176b2fc195eSAndrew Gallatin 			      err);
4177b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
4178b2fc195eSAndrew Gallatin 	}
4179b2fc195eSAndrew Gallatin 
4180b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4181b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4182b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4183b2fc195eSAndrew Gallatin 		err = ENOSPC;
4184b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4185b2fc195eSAndrew Gallatin 	}
41861e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
41871e413cf9SAndrew Gallatin 
4188a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4189a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4190a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4191a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4192a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4193a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4194b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4195b2fc195eSAndrew Gallatin 
4196dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4197d91b1b49SAndrew Gallatin 
4198dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4199b2fc195eSAndrew Gallatin 
4200b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4201b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4202b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4203b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4204b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4205b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4206b2fc195eSAndrew Gallatin 		err = ENXIO;
4207b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4208b2fc195eSAndrew Gallatin 	}
4209b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4210b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4211b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4212b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4213b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4214b2fc195eSAndrew Gallatin 		err = ENXIO;
4215b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4216b2fc195eSAndrew Gallatin 	}
4217b2fc195eSAndrew Gallatin 
4218b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4219b2fc195eSAndrew Gallatin 	   lanai SRAM */
42206d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4221b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4222b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
42236d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4224b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
42256d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
42266d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4227b2fc195eSAndrew Gallatin 	if (err != 0)
4228b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4229b2fc195eSAndrew Gallatin 
4230b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
42316d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4232b2fc195eSAndrew Gallatin 
4233b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
42346d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
42356d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4236b2fc195eSAndrew Gallatin 	if (err != 0)
4237b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4238b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
42396d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4240b2fc195eSAndrew Gallatin 	if (err != 0)
4241b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4242b2fc195eSAndrew Gallatin 
4243a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4244a98d6cd7SAndrew Gallatin 	if (err != 0)
42451e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4246b2fc195eSAndrew Gallatin 
42478fe615baSAndrew Gallatin 	/* select & load the firmware */
42488fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4249b2fc195eSAndrew Gallatin 	if (err != 0)
42501e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
42515e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
42521e413cf9SAndrew Gallatin 
42531e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
42541e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
42551e413cf9SAndrew Gallatin 	if (err != 0)
42561e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
42571e413cf9SAndrew Gallatin 
4258adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4259b2fc195eSAndrew Gallatin 	if (err != 0)
42601e413cf9SAndrew Gallatin 		goto abort_with_slices;
4261b2fc195eSAndrew Gallatin 
4262a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4263a98d6cd7SAndrew Gallatin 	if (err != 0) {
4264a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
42651e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
4266a98d6cd7SAndrew Gallatin 	}
4267a98d6cd7SAndrew Gallatin 
42681e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4269a98d6cd7SAndrew Gallatin 	if (err != 0) {
42701e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4271a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4272a98d6cd7SAndrew Gallatin 	}
42731e413cf9SAndrew Gallatin 
4274e5062938SAndrew Gallatin 	ifp->if_baudrate = IF_Gbps(10UL);
4275c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
427637d89b0cSAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LRO;
427737d89b0cSAndrew Gallatin 
427837d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
427937d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
428037d89b0cSAndrew Gallatin #endif
4281c792928fSAndrew Gallatin 
4282053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4283053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4284053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4285053e637fSAndrew Gallatin 	else
4286053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4287adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4288053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4289aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
4290b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4291f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4292f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
42935e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
42946d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
4295b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
4296b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
42976d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
42986d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
4299c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4300c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4301c587e59fSAndrew Gallatin 		     mxge_media_status);
4302c587e59fSAndrew Gallatin 	mxge_set_media(sc, IFM_ETHER | IFM_AUTO);
4303c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
4304b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4305b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
4306053e637fSAndrew Gallatin 	if (ifp->if_capabilities & IFCAP_JUMBO_MTU)
4307c792928fSAndrew Gallatin 		ifp->if_mtu = 9000;
4308b2fc195eSAndrew Gallatin 
43096d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4310b2fc195eSAndrew Gallatin 	return 0;
4311b2fc195eSAndrew Gallatin 
4312a98d6cd7SAndrew Gallatin abort_with_rings:
4313a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
43141e413cf9SAndrew Gallatin abort_with_slices:
43151e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4316a98d6cd7SAndrew Gallatin abort_with_dmabench:
4317a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4318b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
43196d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4320b2fc195eSAndrew Gallatin abort_with_cmd_dma:
43216d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4322b2fc195eSAndrew Gallatin abort_with_mem_res:
4323b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4324b2fc195eSAndrew Gallatin abort_with_lock:
4325b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4326a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4327a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4328b2fc195eSAndrew Gallatin 	if_free(ifp);
4329b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4330b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4331b2fc195eSAndrew Gallatin 
4332b2fc195eSAndrew Gallatin abort_with_nothing:
4333b2fc195eSAndrew Gallatin 	return err;
4334b2fc195eSAndrew Gallatin }
4335b2fc195eSAndrew Gallatin 
4336b2fc195eSAndrew Gallatin static int
43376d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4338b2fc195eSAndrew Gallatin {
43396d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4340b2fc195eSAndrew Gallatin 
434137d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4342c792928fSAndrew Gallatin 		device_printf(sc->dev,
4343c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4344c792928fSAndrew Gallatin 		return EBUSY;
4345c792928fSAndrew Gallatin 	}
4346a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4347b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
43486d87a65dSAndrew Gallatin 		mxge_close(sc);
4349a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4350b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
4351e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4352dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4353091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
43541e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
43551e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4356a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
43571e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4358a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
43596d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
43606d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4361b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4362b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4363a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4364a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4365b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4366b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4367b2fc195eSAndrew Gallatin 	return 0;
4368b2fc195eSAndrew Gallatin }
4369b2fc195eSAndrew Gallatin 
4370b2fc195eSAndrew Gallatin static int
43716d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4372b2fc195eSAndrew Gallatin {
4373b2fc195eSAndrew Gallatin 	return 0;
4374b2fc195eSAndrew Gallatin }
4375b2fc195eSAndrew Gallatin 
4376b2fc195eSAndrew Gallatin /*
4377b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4378b2fc195eSAndrew Gallatin 
4379b2fc195eSAndrew Gallatin   Local Variables:
4380b2fc195eSAndrew Gallatin   c-file-style:"linux"
4381b2fc195eSAndrew Gallatin   tab-width:8
4382b2fc195eSAndrew Gallatin   End:
4383b2fc195eSAndrew Gallatin */
4384