xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 65c690668b1a1776a8754d9f0a3c39a49662f4d4)
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 
4971032832SAndrew Gallatin /* count xmits ourselves, rather than via drbr */
5071032832SAndrew Gallatin #define NO_SLOW_STATS
51b2fc195eSAndrew Gallatin #include <net/if.h>
52b2fc195eSAndrew Gallatin #include <net/if_arp.h>
53b2fc195eSAndrew Gallatin #include <net/ethernet.h>
54b2fc195eSAndrew Gallatin #include <net/if_dl.h>
55b2fc195eSAndrew Gallatin #include <net/if_media.h>
56b2fc195eSAndrew Gallatin 
57b2fc195eSAndrew Gallatin #include <net/bpf.h>
58b2fc195eSAndrew Gallatin 
59b2fc195eSAndrew Gallatin #include <net/if_types.h>
60b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
61b2fc195eSAndrew Gallatin #include <net/zlib.h>
62b2fc195eSAndrew Gallatin 
63b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
64b2fc195eSAndrew Gallatin #include <netinet/in.h>
65b2fc195eSAndrew Gallatin #include <netinet/ip.h>
66aed8e389SAndrew Gallatin #include <netinet/tcp.h>
67b2fc195eSAndrew Gallatin 
68b2fc195eSAndrew Gallatin #include <machine/bus.h>
69053e637fSAndrew Gallatin #include <machine/in_cksum.h>
70b2fc195eSAndrew Gallatin #include <machine/resource.h>
71b2fc195eSAndrew Gallatin #include <sys/bus.h>
72b2fc195eSAndrew Gallatin #include <sys/rman.h>
731e413cf9SAndrew Gallatin #include <sys/smp.h>
74b2fc195eSAndrew Gallatin 
75b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
76b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
77e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
78b2fc195eSAndrew Gallatin 
79b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
80b2fc195eSAndrew Gallatin #include <vm/pmap.h>
81b2fc195eSAndrew Gallatin 
82c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
83c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
84c2c14a69SAndrew Gallatin #endif
85c2c14a69SAndrew Gallatin 
866d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
876d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
881e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
896d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
90869c7348SAndrew Gallatin #ifdef IFNET_BUF_RING
91869c7348SAndrew Gallatin #include <sys/buf_ring.h>
92869c7348SAndrew Gallatin #endif
93b2fc195eSAndrew Gallatin 
94eb6219e3SAndrew Gallatin #include "opt_inet.h"
95eb6219e3SAndrew Gallatin 
96b2fc195eSAndrew Gallatin /* tunable params */
976d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
98d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
996d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1005e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1016d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1025e7d8541SAndrew Gallatin static int mxge_verbose = 0;
103f04b33f8SAndrew Gallatin static int mxge_lro_cnt = 8;
104dce01b9bSAndrew Gallatin static int mxge_ticks;
1051e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1061e413cf9SAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
1071e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
108f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
10965c69066SAndrew Gallatin static int mxge_throttle = 0;
1106d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1116d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1121e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1131e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
114b2fc195eSAndrew Gallatin 
1156d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1166d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1176d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1186d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1196d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
120b2fc195eSAndrew Gallatin 
1216d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
122b2fc195eSAndrew Gallatin {
123b2fc195eSAndrew Gallatin   /* Device interface */
1246d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1256d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1266d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1276d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
128b2fc195eSAndrew Gallatin   {0, 0}
129b2fc195eSAndrew Gallatin };
130b2fc195eSAndrew Gallatin 
1316d87a65dSAndrew Gallatin static driver_t mxge_driver =
132b2fc195eSAndrew Gallatin {
1336d87a65dSAndrew Gallatin   "mxge",
1346d87a65dSAndrew Gallatin   mxge_methods,
1356d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
136b2fc195eSAndrew Gallatin };
137b2fc195eSAndrew Gallatin 
1386d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
139b2fc195eSAndrew Gallatin 
140b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1416d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1426d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
143f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
144b2fc195eSAndrew Gallatin 
1451e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1468fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
147276edd10SAndrew Gallatin static int mxge_close(mxge_softc_t *sc);
148276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
149276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1508fe615baSAndrew Gallatin 
151b2fc195eSAndrew Gallatin static int
1526d87a65dSAndrew Gallatin mxge_probe(device_t dev)
153b2fc195eSAndrew Gallatin {
15401638550SAndrew Gallatin 	int rev;
15501638550SAndrew Gallatin 
15601638550SAndrew Gallatin 
1576d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
158f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
159f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
16001638550SAndrew Gallatin 		rev = pci_get_revid(dev);
16101638550SAndrew Gallatin 		switch (rev) {
16201638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
163b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
16401638550SAndrew Gallatin 			break;
16501638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
16601638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
16701638550SAndrew Gallatin 			break;
16801638550SAndrew Gallatin 		default:
16901638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
17001638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
17101638550SAndrew Gallatin 				      rev);
17201638550SAndrew Gallatin 			break;
17301638550SAndrew Gallatin 		}
174b2fc195eSAndrew Gallatin 		return 0;
175b2fc195eSAndrew Gallatin 	}
176b2fc195eSAndrew Gallatin 	return ENXIO;
177b2fc195eSAndrew Gallatin }
178b2fc195eSAndrew Gallatin 
179b2fc195eSAndrew Gallatin static void
1806d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
181b2fc195eSAndrew Gallatin {
182f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
183b2fc195eSAndrew Gallatin 	vm_offset_t len;
18447c2e987SAndrew Gallatin 	int err;
185b2fc195eSAndrew Gallatin 
1864d69a9d0SAndrew Gallatin 	sc->wc = 1;
187b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
188c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
189c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
19047c2e987SAndrew Gallatin 	if (err != 0) {
191c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
192c2c14a69SAndrew Gallatin 			      err);
1934d69a9d0SAndrew Gallatin 		sc->wc = 0;
194b2fc195eSAndrew Gallatin 	}
195f9ae0280SAndrew Gallatin #endif
196b2fc195eSAndrew Gallatin }
197b2fc195eSAndrew Gallatin 
198b2fc195eSAndrew Gallatin 
199b2fc195eSAndrew Gallatin /* callback to get our DMA address */
200b2fc195eSAndrew Gallatin static void
2016d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
202b2fc195eSAndrew Gallatin 			 int error)
203b2fc195eSAndrew Gallatin {
204b2fc195eSAndrew Gallatin 	if (error == 0) {
205b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
206b2fc195eSAndrew Gallatin 	}
207b2fc195eSAndrew Gallatin }
208b2fc195eSAndrew Gallatin 
209b2fc195eSAndrew Gallatin static int
2106d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
211b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
212b2fc195eSAndrew Gallatin {
213b2fc195eSAndrew Gallatin 	int err;
214b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2151e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2161e413cf9SAndrew Gallatin 
2171e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2181e413cf9SAndrew Gallatin 		boundary = 0;
2191e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2201e413cf9SAndrew Gallatin 	} else {
2211e413cf9SAndrew Gallatin 		boundary = 4096;
2221e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2231e413cf9SAndrew Gallatin 	}
224b2fc195eSAndrew Gallatin 
225b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
226b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
227b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2281e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
229b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
230b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
231b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
232b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
233b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2341e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
235b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
236b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
237b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
238b2fc195eSAndrew Gallatin 	if (err != 0) {
239b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
240b2fc195eSAndrew Gallatin 		return err;
241b2fc195eSAndrew Gallatin 	}
242b2fc195eSAndrew Gallatin 
243b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
244b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
245b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
246b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
247b2fc195eSAndrew Gallatin 	if (err != 0) {
248b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
249b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
250b2fc195eSAndrew Gallatin 	}
251b2fc195eSAndrew Gallatin 
252b2fc195eSAndrew Gallatin 	/* load the memory */
253b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2546d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
255b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
256b2fc195eSAndrew Gallatin 	if (err != 0) {
257b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
258b2fc195eSAndrew Gallatin 		goto abort_with_mem;
259b2fc195eSAndrew Gallatin 	}
260b2fc195eSAndrew Gallatin 	return 0;
261b2fc195eSAndrew Gallatin 
262b2fc195eSAndrew Gallatin abort_with_mem:
263b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
264b2fc195eSAndrew Gallatin abort_with_dmat:
265b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
266b2fc195eSAndrew Gallatin 	return err;
267b2fc195eSAndrew Gallatin }
268b2fc195eSAndrew Gallatin 
269b2fc195eSAndrew Gallatin 
270b2fc195eSAndrew Gallatin static void
2716d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
272b2fc195eSAndrew Gallatin {
273b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
274b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
275b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
276b2fc195eSAndrew Gallatin }
277b2fc195eSAndrew Gallatin 
278b2fc195eSAndrew Gallatin /*
279b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
280b2fc195eSAndrew Gallatin  * SN=x\0
281b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
282b2fc195eSAndrew Gallatin  * PC=text\0
283b2fc195eSAndrew Gallatin  */
284b2fc195eSAndrew Gallatin 
285b2fc195eSAndrew Gallatin static int
2866d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
287b2fc195eSAndrew Gallatin {
2886d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
289b2fc195eSAndrew Gallatin 
290b2fc195eSAndrew Gallatin 	char *ptr, *limit;
291b2fc195eSAndrew Gallatin 	int i, found_mac;
292b2fc195eSAndrew Gallatin 
293b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2946d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
295b2fc195eSAndrew Gallatin 	found_mac = 0;
296b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
297b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2985e7d8541SAndrew Gallatin 			ptr += 1;
299b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
300b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
3015e7d8541SAndrew Gallatin 				ptr += 3;
302b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
303b2fc195eSAndrew Gallatin 					goto abort;
304b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
305b2fc195eSAndrew Gallatin 				found_mac = 1;
306b2fc195eSAndrew Gallatin 			}
3075e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
3085e7d8541SAndrew Gallatin 			ptr += 3;
3095e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
3105e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
3115e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
3125e7d8541SAndrew Gallatin 			ptr += 3;
3135e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
3145e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
315b2fc195eSAndrew Gallatin 		}
3166d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
317b2fc195eSAndrew Gallatin 	}
318b2fc195eSAndrew Gallatin 
319b2fc195eSAndrew Gallatin 	if (found_mac)
320b2fc195eSAndrew Gallatin 		return 0;
321b2fc195eSAndrew Gallatin 
322b2fc195eSAndrew Gallatin  abort:
323b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
324b2fc195eSAndrew Gallatin 
325b2fc195eSAndrew Gallatin 	return ENXIO;
326b2fc195eSAndrew Gallatin }
327b2fc195eSAndrew Gallatin 
3280d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3298fe615baSAndrew Gallatin static void
3308fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
331b2fc195eSAndrew Gallatin {
332b2fc195eSAndrew Gallatin 	uint32_t val;
3338fe615baSAndrew Gallatin 	unsigned long base, off;
334b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3358fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3368fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
337b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
338b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
339b2fc195eSAndrew Gallatin 
3408fe615baSAndrew Gallatin 
3418fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3428fe615baSAndrew Gallatin 		return;
3438fe615baSAndrew Gallatin 
3448fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3458fe615baSAndrew Gallatin 	if (pdev == NULL) {
3468fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3478fe615baSAndrew Gallatin 		return;
3488fe615baSAndrew Gallatin 	}
3498fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3508fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3518fe615baSAndrew Gallatin 
3528fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3538fe615baSAndrew Gallatin 		return;
3548fe615baSAndrew Gallatin 
3558fe615baSAndrew Gallatin 	base = 0;
3568fe615baSAndrew Gallatin 
3578fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3588fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3598fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3608fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3618fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3628fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3638fe615baSAndrew Gallatin 		if (mcp55 &&
3648fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3658fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3668fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3678fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3688fe615baSAndrew Gallatin 		}
3698fe615baSAndrew Gallatin 	}
3708fe615baSAndrew Gallatin 	if (!base)
3718fe615baSAndrew Gallatin 		return;
3728fe615baSAndrew Gallatin 
373b2fc195eSAndrew Gallatin 	/* XXXX
374b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
375b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
376b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
377b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
378b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
379b2fc195eSAndrew Gallatin 	*/
380b2fc195eSAndrew Gallatin #if 0
381b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
382b2fc195eSAndrew Gallatin 	   config space */
383b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
384b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
385b2fc195eSAndrew Gallatin 		val |= 0x40;
386b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3878fe615baSAndrew Gallatin 		return;
388b2fc195eSAndrew Gallatin 	}
389b2fc195eSAndrew Gallatin #endif
390b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
391b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
392b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
393b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
394b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
395b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
396b2fc195eSAndrew Gallatin 	 */
397b2fc195eSAndrew Gallatin 
398b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
399b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
400b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
401b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
402b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
403b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
404b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
405b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
406b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
407b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
408b2fc195eSAndrew Gallatin 
4098fe615baSAndrew Gallatin 	off =  base
410b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
411b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
412b2fc195eSAndrew Gallatin 						 + 8 * slot);
413b2fc195eSAndrew Gallatin 
414b2fc195eSAndrew Gallatin 	/* map it into the kernel */
415b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
416b2fc195eSAndrew Gallatin 
417b2fc195eSAndrew Gallatin 
418b2fc195eSAndrew Gallatin 	if (va == NULL) {
419b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4208fe615baSAndrew Gallatin 		return;
421b2fc195eSAndrew Gallatin 	}
422b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
423b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
424b2fc195eSAndrew Gallatin 
425b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
426b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
427b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
428b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
429b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
430b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
431b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4328fe615baSAndrew Gallatin 		return;
433b2fc195eSAndrew Gallatin 	}
434b2fc195eSAndrew Gallatin 
435b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
436b2fc195eSAndrew Gallatin 	val = *ptr32;
437b2fc195eSAndrew Gallatin 
438b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
439b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
440b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4418fe615baSAndrew Gallatin 		return;
442b2fc195eSAndrew Gallatin 	}
443b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
444b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4455e7d8541SAndrew Gallatin 	if (mxge_verbose)
446b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4475e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4485e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
449b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4508fe615baSAndrew Gallatin 	return;
451b2fc195eSAndrew Gallatin }
452b2fc195eSAndrew Gallatin #else
4538fe615baSAndrew Gallatin static void
454f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
455b2fc195eSAndrew Gallatin {
456b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
457b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4588fe615baSAndrew Gallatin 	return;
459b2fc195eSAndrew Gallatin }
460b2fc195eSAndrew Gallatin #endif
4618fe615baSAndrew Gallatin 
4628fe615baSAndrew Gallatin 
4638fe615baSAndrew Gallatin static int
4648fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4658fe615baSAndrew Gallatin {
4668fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4678fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4688fe615baSAndrew Gallatin 	int status;
4698fe615baSAndrew Gallatin 	uint32_t len;
4708fe615baSAndrew Gallatin 	char *test = " ";
4718fe615baSAndrew Gallatin 
4728fe615baSAndrew Gallatin 
4738fe615baSAndrew Gallatin 	/* Run a small DMA test.
4748fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4758fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4768fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4778fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4788fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4798fe615baSAndrew Gallatin 	 * transfers took to complete.
4808fe615baSAndrew Gallatin 	 */
4818fe615baSAndrew Gallatin 
4821e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4838fe615baSAndrew Gallatin 
4848fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4858fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4868fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4878fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4888fe615baSAndrew Gallatin 	if (status != 0) {
4898fe615baSAndrew Gallatin 		test = "read";
4908fe615baSAndrew Gallatin 		goto abort;
4918fe615baSAndrew Gallatin 	}
4928fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4938fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4948fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4958fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4968fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4978fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4988fe615baSAndrew Gallatin 	if (status != 0) {
4998fe615baSAndrew Gallatin 		test = "write";
5008fe615baSAndrew Gallatin 		goto abort;
5018fe615baSAndrew Gallatin 	}
5028fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5038fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5048fe615baSAndrew Gallatin 
5058fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5068fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5078fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5088fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5098fe615baSAndrew Gallatin 	if (status != 0) {
5108fe615baSAndrew Gallatin 		test = "read/write";
5118fe615baSAndrew Gallatin 		goto abort;
5128fe615baSAndrew Gallatin 	}
5138fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5148fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5158fe615baSAndrew Gallatin 
5168fe615baSAndrew Gallatin abort:
5178fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5188fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5198fe615baSAndrew Gallatin 			      test, status);
5208fe615baSAndrew Gallatin 
5218fe615baSAndrew Gallatin 	return status;
5228fe615baSAndrew Gallatin }
5238fe615baSAndrew Gallatin 
524b2fc195eSAndrew Gallatin /*
525b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
526b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
527b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
528b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
529b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
530b2fc195eSAndrew Gallatin  *
531b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
532b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
533b2fc195eSAndrew Gallatin  *
534b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
535b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
536b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
537b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5381e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
539b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5401e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
541b2fc195eSAndrew Gallatin  */
542b2fc195eSAndrew Gallatin 
5438fe615baSAndrew Gallatin static int
5448fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5458fe615baSAndrew Gallatin {
5468fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5478fe615baSAndrew Gallatin 	int reg, status;
5488fe615baSAndrew Gallatin 	uint16_t pectl;
5498fe615baSAndrew Gallatin 
5501e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5518fe615baSAndrew Gallatin 	/*
5528fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5538fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5548fe615baSAndrew Gallatin 	 */
5558fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5568fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5578fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5588fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5598fe615baSAndrew Gallatin 				      pectl);
5601e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5618fe615baSAndrew Gallatin 		}
5628fe615baSAndrew Gallatin 	}
5638fe615baSAndrew Gallatin 
5648fe615baSAndrew Gallatin 	/*
5658fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5668fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5678fe615baSAndrew Gallatin 	 */
5688fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5691e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5708fe615baSAndrew Gallatin 	if (status != 0) {
5718fe615baSAndrew Gallatin 		return status;
5728fe615baSAndrew Gallatin 	}
5738fe615baSAndrew Gallatin 
5748fe615baSAndrew Gallatin 	/*
5758fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5768fe615baSAndrew Gallatin 	 */
5778fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5788fe615baSAndrew Gallatin 
5798fe615baSAndrew Gallatin 	/*
5808fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5818fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5828fe615baSAndrew Gallatin 	 */
5838fe615baSAndrew Gallatin 
5848fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5858fe615baSAndrew Gallatin 	if (status == 0)
5868fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5878fe615baSAndrew Gallatin 
5888fe615baSAndrew Gallatin 	if (status != E2BIG)
5898fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5908fe615baSAndrew Gallatin 	if (status == ENOSYS)
5918fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5928fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5938fe615baSAndrew Gallatin 	return status;
5948fe615baSAndrew Gallatin }
5958fe615baSAndrew Gallatin 
5968fe615baSAndrew Gallatin static int
5976d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
598b2fc195eSAndrew Gallatin {
5998fe615baSAndrew Gallatin 	int aligned = 0;
60065c69066SAndrew Gallatin 	int force_firmware = mxge_force_firmware;
601b2fc195eSAndrew Gallatin 
60265c69066SAndrew Gallatin 	if (sc->throttle)
60365c69066SAndrew Gallatin 		force_firmware = sc->throttle;
604d91b1b49SAndrew Gallatin 
60565c69066SAndrew Gallatin 	if (force_firmware != 0) {
60665c69066SAndrew Gallatin 		if (force_firmware == 1)
607d91b1b49SAndrew Gallatin 			aligned = 1;
608d91b1b49SAndrew Gallatin 		else
609d91b1b49SAndrew Gallatin 			aligned = 0;
610d91b1b49SAndrew Gallatin 		if (mxge_verbose)
611d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
612d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
613d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
614d91b1b49SAndrew Gallatin 		goto abort;
615d91b1b49SAndrew Gallatin 	}
616d91b1b49SAndrew Gallatin 
617d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
618d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
619d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
620d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
621d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
622d91b1b49SAndrew Gallatin 			      sc->link_width);
623d91b1b49SAndrew Gallatin 		aligned = 1;
624d91b1b49SAndrew Gallatin 		goto abort;
625d91b1b49SAndrew Gallatin 	}
626d91b1b49SAndrew Gallatin 
6278fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6288fe615baSAndrew Gallatin 		return 0;
629b2fc195eSAndrew Gallatin 
630b2fc195eSAndrew Gallatin abort:
631b2fc195eSAndrew Gallatin 	if (aligned) {
6326d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6331e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
634b2fc195eSAndrew Gallatin 	} else {
6356d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6361e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
637b2fc195eSAndrew Gallatin 	}
6381e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
639b2fc195eSAndrew Gallatin }
640b2fc195eSAndrew Gallatin 
641b2fc195eSAndrew Gallatin union qualhack
642b2fc195eSAndrew Gallatin {
643b2fc195eSAndrew Gallatin         const char *ro_char;
644b2fc195eSAndrew Gallatin         char *rw_char;
645b2fc195eSAndrew Gallatin };
646b2fc195eSAndrew Gallatin 
6474da0d523SAndrew Gallatin static int
6484da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6494da0d523SAndrew Gallatin {
650b824b7d8SAndrew Gallatin 
6514da0d523SAndrew Gallatin 
6524da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6534da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6544da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6554da0d523SAndrew Gallatin 		return EIO;
6564da0d523SAndrew Gallatin 	}
6574da0d523SAndrew Gallatin 
6584da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6594da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6604da0d523SAndrew Gallatin 	if (mxge_verbose)
6614da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6624da0d523SAndrew Gallatin 
663b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
664b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6654da0d523SAndrew Gallatin 
666b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
667b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6684da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6694da0d523SAndrew Gallatin 			      sc->fw_version);
6704da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6714da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6724da0d523SAndrew Gallatin 		return EINVAL;
6734da0d523SAndrew Gallatin 	}
6744da0d523SAndrew Gallatin 	return 0;
6754da0d523SAndrew Gallatin 
6764da0d523SAndrew Gallatin }
677b2fc195eSAndrew Gallatin 
678f9ae0280SAndrew Gallatin static void *
679f9ae0280SAndrew Gallatin z_alloc(void *nil, u_int items, u_int size)
680f9ae0280SAndrew Gallatin {
681f9ae0280SAndrew Gallatin         void *ptr;
682f9ae0280SAndrew Gallatin 
683f9ae0280SAndrew Gallatin         ptr = malloc(items * size, M_TEMP, M_NOWAIT);
684f9ae0280SAndrew Gallatin         return ptr;
685f9ae0280SAndrew Gallatin }
686f9ae0280SAndrew Gallatin 
687f9ae0280SAndrew Gallatin static void
688f9ae0280SAndrew Gallatin z_free(void *nil, void *ptr)
689f9ae0280SAndrew Gallatin {
690f9ae0280SAndrew Gallatin         free(ptr, M_TEMP);
691f9ae0280SAndrew Gallatin }
692f9ae0280SAndrew Gallatin 
693f9ae0280SAndrew Gallatin 
694b2fc195eSAndrew Gallatin static int
6956d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
696b2fc195eSAndrew Gallatin {
697f9ae0280SAndrew Gallatin 	z_stream zs;
698f9ae0280SAndrew Gallatin 	char *inflate_buffer;
69933d54970SLuigi Rizzo 	const struct firmware *fw;
700b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
701b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
702b2fc195eSAndrew Gallatin 	int status;
7034da0d523SAndrew Gallatin 	unsigned int i;
7044da0d523SAndrew Gallatin 	char dummy;
705f9ae0280SAndrew Gallatin 	size_t fw_len;
706b2fc195eSAndrew Gallatin 
707b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
708b2fc195eSAndrew Gallatin 	if (fw == NULL) {
709b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
710b2fc195eSAndrew Gallatin 			      sc->fw_name);
711b2fc195eSAndrew Gallatin 		return ENOENT;
712b2fc195eSAndrew Gallatin 	}
713b2fc195eSAndrew Gallatin 
714f9ae0280SAndrew Gallatin 
715f9ae0280SAndrew Gallatin 
716f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
717f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
718f9ae0280SAndrew Gallatin 	zs.zalloc = z_alloc;
719f9ae0280SAndrew Gallatin 	zs.zfree = z_free;
720f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
721f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
722b2fc195eSAndrew Gallatin 		status = EIO;
723b2fc195eSAndrew Gallatin 		goto abort_with_fw;
724b2fc195eSAndrew Gallatin 	}
725f9ae0280SAndrew Gallatin 
726f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
727f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
728f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
729f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
730f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
731f9ae0280SAndrew Gallatin 		goto abort_with_zs;
732f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
733f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
734f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
735f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
736f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
737f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
738f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
739f9ae0280SAndrew Gallatin 		status = EIO;
740f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
741f9ae0280SAndrew Gallatin 	}
742f9ae0280SAndrew Gallatin 
743f9ae0280SAndrew Gallatin 	/* check id */
744f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
745f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
746f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
747f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
748f9ae0280SAndrew Gallatin 		status = EIO;
749f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
750f9ae0280SAndrew Gallatin 	}
751f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
752b2fc195eSAndrew Gallatin 
7534da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7544da0d523SAndrew Gallatin 	if (status != 0)
755f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
756b2fc195eSAndrew Gallatin 
757b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
758f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7594da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
760f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
761f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
76273c7c83fSAndrew Gallatin 		wmb();
7634da0d523SAndrew Gallatin 		dummy = *sc->sram;
76473c7c83fSAndrew Gallatin 		wmb();
7654da0d523SAndrew Gallatin 	}
766b2fc195eSAndrew Gallatin 
767f9ae0280SAndrew Gallatin 	*limit = fw_len;
768b2fc195eSAndrew Gallatin 	status = 0;
769f9ae0280SAndrew Gallatin abort_with_buffer:
770f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
771f9ae0280SAndrew Gallatin abort_with_zs:
772f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
773b2fc195eSAndrew Gallatin abort_with_fw:
774b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
775b2fc195eSAndrew Gallatin 	return status;
776b2fc195eSAndrew Gallatin }
777b2fc195eSAndrew Gallatin 
778b2fc195eSAndrew Gallatin /*
779b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
780b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
781b2fc195eSAndrew Gallatin  */
782b2fc195eSAndrew Gallatin 
783b2fc195eSAndrew Gallatin static void
7846d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
785b2fc195eSAndrew Gallatin {
786b2fc195eSAndrew Gallatin 	char buf_bytes[72];
787b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
788b2fc195eSAndrew Gallatin 	volatile char *submit;
789b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
790b2fc195eSAndrew Gallatin 	int i;
791b2fc195eSAndrew Gallatin 
792b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
793b2fc195eSAndrew Gallatin 
794b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
795b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
796b2fc195eSAndrew Gallatin 	*confirm = 0;
79773c7c83fSAndrew Gallatin 	wmb();
798b2fc195eSAndrew Gallatin 
799b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
800b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
801b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
802b2fc195eSAndrew Gallatin 	*/
803b2fc195eSAndrew Gallatin 
8046d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8056d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
806b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
807b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
808b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
8096d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
8106d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
811b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
812b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
813b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
814b2fc195eSAndrew Gallatin 
815b2fc195eSAndrew Gallatin 
8160fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
817b2fc195eSAndrew Gallatin 
8186d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
81973c7c83fSAndrew Gallatin 	wmb();
820b2fc195eSAndrew Gallatin 	DELAY(1000);
82173c7c83fSAndrew Gallatin 	wmb();
822b2fc195eSAndrew Gallatin 	i = 0;
823b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
824b2fc195eSAndrew Gallatin 		DELAY(1000);
825b2fc195eSAndrew Gallatin 		i++;
826b2fc195eSAndrew Gallatin 	}
827b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
828b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
829b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
830b2fc195eSAndrew Gallatin 			      *confirm);
831b2fc195eSAndrew Gallatin 	}
832b2fc195eSAndrew Gallatin 	return;
833b2fc195eSAndrew Gallatin }
834b2fc195eSAndrew Gallatin 
835b2fc195eSAndrew Gallatin static int
8366d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
837b2fc195eSAndrew Gallatin {
838b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
839b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
840b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8410fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
842b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
843e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
844b2fc195eSAndrew Gallatin 
845b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
846b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
847b2fc195eSAndrew Gallatin 
848b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
849b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
850b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
851b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8526d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8536d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
854b2fc195eSAndrew Gallatin 
855b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
856b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
857a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
858b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
85973c7c83fSAndrew Gallatin 	wmb();
8606d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
861b2fc195eSAndrew Gallatin 
8625e7d8541SAndrew Gallatin 	/* wait up to 20ms */
863e0501fd0SAndrew Gallatin 	err = EAGAIN;
8645e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
865b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
866b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
86773c7c83fSAndrew Gallatin 		wmb();
868e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
869e0501fd0SAndrew Gallatin 		case 0:
870b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
871e0501fd0SAndrew Gallatin 			err = 0;
872e0501fd0SAndrew Gallatin 			break;
873e0501fd0SAndrew Gallatin 		case 0xffffffff:
874e0501fd0SAndrew Gallatin 			DELAY(1000);
875e0501fd0SAndrew Gallatin 			break;
876e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
877e0501fd0SAndrew Gallatin 			err = ENOSYS;
878e0501fd0SAndrew Gallatin 			break;
879e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
880e0501fd0SAndrew Gallatin 			err = E2BIG;
881e0501fd0SAndrew Gallatin 			break;
882c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
883c587e59fSAndrew Gallatin 			err = EBUSY;
884c587e59fSAndrew Gallatin 			break;
885e0501fd0SAndrew Gallatin 		default:
886b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8876d87a65dSAndrew Gallatin 				      "mxge: command %d "
888b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
889b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
890e0501fd0SAndrew Gallatin 			err = ENXIO;
891e0501fd0SAndrew Gallatin 			break;
892b2fc195eSAndrew Gallatin 		}
893e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
894e0501fd0SAndrew Gallatin 			break;
895b2fc195eSAndrew Gallatin 	}
896e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8976d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
898b2fc195eSAndrew Gallatin 			      "result = %d\n",
899b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
900e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
901e0501fd0SAndrew Gallatin 	return err;
902b2fc195eSAndrew Gallatin }
903b2fc195eSAndrew Gallatin 
9044da0d523SAndrew Gallatin static int
9054da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
9064da0d523SAndrew Gallatin {
9074da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
9084da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
9094da0d523SAndrew Gallatin 	size_t hdr_offset;
9104da0d523SAndrew Gallatin 	int status;
9114da0d523SAndrew Gallatin 
9124da0d523SAndrew Gallatin 	/* find running firmware header */
9134da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9144da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9154da0d523SAndrew Gallatin 
9164da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9174da0d523SAndrew Gallatin 		device_printf(sc->dev,
9184da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9194da0d523SAndrew Gallatin 			      (int)hdr_offset);
9204da0d523SAndrew Gallatin 		return EIO;
9214da0d523SAndrew Gallatin 	}
9224da0d523SAndrew Gallatin 
9234da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9244da0d523SAndrew Gallatin 	 * validate firmware */
9254da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9264da0d523SAndrew Gallatin 	if (hdr == NULL) {
9274da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9284da0d523SAndrew Gallatin 		return ENOMEM;
9294da0d523SAndrew Gallatin 	}
9304da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9314da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9324da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9334da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9344da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
935b824b7d8SAndrew Gallatin 
936b824b7d8SAndrew Gallatin 	/*
937b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
938b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
939b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
940b824b7d8SAndrew Gallatin 	 */
941b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
942b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
943b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
944b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
945b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
946b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
947b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
948b824b7d8SAndrew Gallatin 	}
949b824b7d8SAndrew Gallatin 
9504da0d523SAndrew Gallatin 	return status;
9514da0d523SAndrew Gallatin }
9524da0d523SAndrew Gallatin 
953b2fc195eSAndrew Gallatin 
954b2fc195eSAndrew Gallatin static int
9551e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
956b2fc195eSAndrew Gallatin {
957b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
958b2fc195eSAndrew Gallatin 	volatile char *submit;
959b2fc195eSAndrew Gallatin 	char buf_bytes[72];
960b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
961b2fc195eSAndrew Gallatin 	int status, i;
962b2fc195eSAndrew Gallatin 
963b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
964b2fc195eSAndrew Gallatin 
965b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9666d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
967b2fc195eSAndrew Gallatin 	if (status) {
9681e413cf9SAndrew Gallatin 		if (!adopt)
9691e413cf9SAndrew Gallatin 			return status;
9704da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9714da0d523SAndrew Gallatin 		   it is new enough */
9724da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9734da0d523SAndrew Gallatin 		if (status) {
9744da0d523SAndrew Gallatin 			device_printf(sc->dev,
9754da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
976b2fc195eSAndrew Gallatin 			return status;
977b2fc195eSAndrew Gallatin 		}
9784da0d523SAndrew Gallatin 		device_printf(sc->dev,
9794da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9801e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9814da0d523SAndrew Gallatin 			device_printf(sc->dev,
9824da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9834da0d523SAndrew Gallatin 				 ".  For optimal\n");
9844da0d523SAndrew Gallatin 			device_printf(sc->dev,
9854da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9864da0d523SAndrew Gallatin 				 "firmware\n");
9874da0d523SAndrew Gallatin 		}
988d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9891e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
990d91b1b49SAndrew Gallatin 		return 0;
9914da0d523SAndrew Gallatin 	}
992b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
993b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
994b2fc195eSAndrew Gallatin 	*confirm = 0;
99573c7c83fSAndrew Gallatin 	wmb();
996b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
997b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
998b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
999b2fc195eSAndrew Gallatin 	*/
1000b2fc195eSAndrew Gallatin 
10016d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
10026d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
1003b2fc195eSAndrew Gallatin 
1004b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
1005b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
1006b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
1007b2fc195eSAndrew Gallatin 
1008b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
1009b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
1010b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
1011b2fc195eSAndrew Gallatin 	*/
1012b2fc195eSAndrew Gallatin 					/* where the code starts*/
10136d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1014b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1015b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1016b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1017b2fc195eSAndrew Gallatin 
10180fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10196d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
102073c7c83fSAndrew Gallatin 	wmb();
1021b2fc195eSAndrew Gallatin 	DELAY(1000);
102273c7c83fSAndrew Gallatin 	wmb();
1023b2fc195eSAndrew Gallatin 	i = 0;
1024b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1025b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1026b2fc195eSAndrew Gallatin 		i++;
1027b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1028b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1029b2fc195eSAndrew Gallatin 	}
1030b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1031b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1032b2fc195eSAndrew Gallatin 			confirm, *confirm);
1033b2fc195eSAndrew Gallatin 
1034b2fc195eSAndrew Gallatin 		return ENXIO;
1035b2fc195eSAndrew Gallatin 	}
1036b2fc195eSAndrew Gallatin 	return 0;
1037b2fc195eSAndrew Gallatin }
1038b2fc195eSAndrew Gallatin 
1039b2fc195eSAndrew Gallatin static int
10406d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1041b2fc195eSAndrew Gallatin {
10426d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1043b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1044b2fc195eSAndrew Gallatin 	int status;
1045b2fc195eSAndrew Gallatin 
1046b2fc195eSAndrew Gallatin 
1047b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1048b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1049b2fc195eSAndrew Gallatin 
1050b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1051b2fc195eSAndrew Gallatin 
10525e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1053b2fc195eSAndrew Gallatin 	return status;
1054b2fc195eSAndrew Gallatin }
1055b2fc195eSAndrew Gallatin 
1056b2fc195eSAndrew Gallatin static int
10576d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1058b2fc195eSAndrew Gallatin {
10596d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1060b2fc195eSAndrew Gallatin 	int status;
1061b2fc195eSAndrew Gallatin 
1062b2fc195eSAndrew Gallatin 	if (pause)
10635e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1064b2fc195eSAndrew Gallatin 				       &cmd);
1065b2fc195eSAndrew Gallatin 	else
10665e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1067b2fc195eSAndrew Gallatin 				       &cmd);
1068b2fc195eSAndrew Gallatin 
1069b2fc195eSAndrew Gallatin 	if (status) {
1070b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1071b2fc195eSAndrew Gallatin 		return ENXIO;
1072b2fc195eSAndrew Gallatin 	}
1073b2fc195eSAndrew Gallatin 	sc->pause = pause;
1074b2fc195eSAndrew Gallatin 	return 0;
1075b2fc195eSAndrew Gallatin }
1076b2fc195eSAndrew Gallatin 
1077b2fc195eSAndrew Gallatin static void
10786d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1079b2fc195eSAndrew Gallatin {
10806d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1081b2fc195eSAndrew Gallatin 	int status;
1082b2fc195eSAndrew Gallatin 
10831e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10841e413cf9SAndrew Gallatin 		promisc = 1;
10851e413cf9SAndrew Gallatin 
1086b2fc195eSAndrew Gallatin 	if (promisc)
10875e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1088b2fc195eSAndrew Gallatin 				       &cmd);
1089b2fc195eSAndrew Gallatin 	else
10905e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1091b2fc195eSAndrew Gallatin 				       &cmd);
1092b2fc195eSAndrew Gallatin 
1093b2fc195eSAndrew Gallatin 	if (status) {
1094b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1095b2fc195eSAndrew Gallatin 	}
1096b2fc195eSAndrew Gallatin }
1097b2fc195eSAndrew Gallatin 
10980fa7f681SAndrew Gallatin static void
10990fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
11000fa7f681SAndrew Gallatin {
11010fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
11020fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
11030fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
11040fa7f681SAndrew Gallatin 	int err;
11050fa7f681SAndrew Gallatin 
11060fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11070fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11080fa7f681SAndrew Gallatin 		return;
11090fa7f681SAndrew Gallatin 
11100fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11110fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11120fa7f681SAndrew Gallatin 	if (err != 0) {
11130fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11140fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11150fa7f681SAndrew Gallatin 		return;
11160fa7f681SAndrew Gallatin 	}
11170fa7f681SAndrew Gallatin 
1118b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1119b824b7d8SAndrew Gallatin 		return;
11200fa7f681SAndrew Gallatin 
11210fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11220fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11230fa7f681SAndrew Gallatin 		return;
11240fa7f681SAndrew Gallatin 
11250fa7f681SAndrew Gallatin 	/* Flush all the filters */
11260fa7f681SAndrew Gallatin 
11270fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11280fa7f681SAndrew Gallatin 	if (err != 0) {
11290fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11300fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11310fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11320fa7f681SAndrew Gallatin 		return;
11330fa7f681SAndrew Gallatin 	}
11340fa7f681SAndrew Gallatin 
11350fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11360fa7f681SAndrew Gallatin 
1137eb956cd0SRobert Watson 	if_maddr_rlock(ifp);
11380fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11390fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11400fa7f681SAndrew Gallatin 			continue;
11410fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11420fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11430fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11440fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11450fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11460fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11470fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11480fa7f681SAndrew Gallatin 		if (err != 0) {
11490fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11500fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11510fa7f681SAndrew Gallatin 			       "%d\t", err);
11520fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
1153eb956cd0SRobert Watson 			if_maddr_runlock(ifp);
11540fa7f681SAndrew Gallatin 			return;
11550fa7f681SAndrew Gallatin 		}
11560fa7f681SAndrew Gallatin 	}
1157eb956cd0SRobert Watson 	if_maddr_runlock(ifp);
11580fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11590fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11600fa7f681SAndrew Gallatin 	if (err != 0) {
11610fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11620fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11630fa7f681SAndrew Gallatin 	}
11640fa7f681SAndrew Gallatin }
11650fa7f681SAndrew Gallatin 
1166b2fc195eSAndrew Gallatin static int
1167053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1168053e637fSAndrew Gallatin {
1169053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1170053e637fSAndrew Gallatin 	int status;
1171053e637fSAndrew Gallatin 
1172c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1173c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1174053e637fSAndrew Gallatin 
1175053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1176053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1177053e637fSAndrew Gallatin 	cmd.data0 = 0;
1178053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1179053e637fSAndrew Gallatin 			       &cmd);
1180053e637fSAndrew Gallatin 	if (status == 0)
1181c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1182053e637fSAndrew Gallatin 
1183053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1184053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1185053e637fSAndrew Gallatin }
1186053e637fSAndrew Gallatin 
1187053e637fSAndrew Gallatin static int
1188adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1189b2fc195eSAndrew Gallatin {
11901e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11911e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11921e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11936d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11941e413cf9SAndrew Gallatin 	int slice, status;
1195b2fc195eSAndrew Gallatin 
1196b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1197b2fc195eSAndrew Gallatin 	   is alive */
1198b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11995e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1200b2fc195eSAndrew Gallatin 	if (status != 0) {
1201b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1202b2fc195eSAndrew Gallatin 		return ENXIO;
1203b2fc195eSAndrew Gallatin 	}
1204b2fc195eSAndrew Gallatin 
1205091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1206091feecdSAndrew Gallatin 
12071e413cf9SAndrew Gallatin 
12081e413cf9SAndrew Gallatin 	/* set the intrq size */
12091e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12101e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12111e413cf9SAndrew Gallatin 
12121e413cf9SAndrew Gallatin 	/*
12131e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12141e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12151e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12161e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12171e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12181e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12191e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12201e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12211e413cf9SAndrew Gallatin 	 */
12221e413cf9SAndrew Gallatin 
12231e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12241e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12251e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12261e413cf9SAndrew Gallatin 					   &cmd);
12271e413cf9SAndrew Gallatin 		if (status != 0) {
12281e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12291e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12301e413cf9SAndrew Gallatin 			return status;
12311e413cf9SAndrew Gallatin 		}
12321e413cf9SAndrew Gallatin 		/*
12331e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12341e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12351e413cf9SAndrew Gallatin 		 */
12361e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12371e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1238c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1239c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1240c6cb3e3fSAndrew Gallatin #endif
12411e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12421e413cf9SAndrew Gallatin 					   &cmd);
12431e413cf9SAndrew Gallatin 		if (status != 0) {
12441e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12451e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12461e413cf9SAndrew Gallatin 			return status;
12471e413cf9SAndrew Gallatin 		}
12481e413cf9SAndrew Gallatin 	}
12491e413cf9SAndrew Gallatin 
12501e413cf9SAndrew Gallatin 
1251adae7080SAndrew Gallatin 	if (interrupts_setup) {
1252b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12531e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12541e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12551e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12561e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12571e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12581e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12591e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12601e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12611e413cf9SAndrew Gallatin 						&cmd);
12621e413cf9SAndrew Gallatin 		}
1263adae7080SAndrew Gallatin 	}
1264b2fc195eSAndrew Gallatin 
12656d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12665e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12675e7d8541SAndrew Gallatin 
12685e7d8541SAndrew Gallatin 
12695e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12705e7d8541SAndrew Gallatin 
12715e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12721e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12735e7d8541SAndrew Gallatin 
12745e7d8541SAndrew Gallatin 
12755e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12766d87a65dSAndrew Gallatin 				&cmd);
12775e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1278b2fc195eSAndrew Gallatin 	if (status != 0) {
1279b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1280b2fc195eSAndrew Gallatin 		return status;
1281b2fc195eSAndrew Gallatin 	}
1282b2fc195eSAndrew Gallatin 
12835e7d8541SAndrew Gallatin 
12845e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12855e7d8541SAndrew Gallatin 
12865e7d8541SAndrew Gallatin 
12875e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12888fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12895e7d8541SAndrew Gallatin 
12901e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12911e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12921e413cf9SAndrew Gallatin 
12931e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1294b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12951e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12961e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12971e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12981e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12991e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1300c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1301c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1302c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
13031e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
13041e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
13051e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
13061e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
13071e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
13081e413cf9SAndrew Gallatin 		ss->lro_bad_csum = 0;
13091e413cf9SAndrew Gallatin 		ss->lro_queued = 0;
13101e413cf9SAndrew Gallatin 		ss->lro_flushed = 0;
13111e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
13121e413cf9SAndrew Gallatin 			ss->fw_stats->valid = 0;
13131e413cf9SAndrew Gallatin 			ss->fw_stats->send_done_count = 0;
13141e413cf9SAndrew Gallatin 		}
13151e413cf9SAndrew Gallatin 	}
1316b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13176d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1318bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13196d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13200fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
132165c69066SAndrew Gallatin 	if (sc->throttle) {
132265c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
132365c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
132465c69066SAndrew Gallatin 				  &cmd)) {
132565c69066SAndrew Gallatin 			device_printf(sc->dev,
132665c69066SAndrew Gallatin 				      "can't enable throttle\n");
132765c69066SAndrew Gallatin 		}
132865c69066SAndrew Gallatin 	}
1329b2fc195eSAndrew Gallatin 	return status;
1330b2fc195eSAndrew Gallatin }
1331b2fc195eSAndrew Gallatin 
1332b2fc195eSAndrew Gallatin static int
133365c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
133465c69066SAndrew Gallatin {
133565c69066SAndrew Gallatin 	mxge_cmd_t cmd;
133665c69066SAndrew Gallatin 	mxge_softc_t *sc;
133765c69066SAndrew Gallatin 	int err;
133865c69066SAndrew Gallatin 	unsigned int throttle;
133965c69066SAndrew Gallatin 
134065c69066SAndrew Gallatin 	sc = arg1;
134165c69066SAndrew Gallatin 	throttle = sc->throttle;
134265c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
134365c69066SAndrew Gallatin         if (err != 0) {
134465c69066SAndrew Gallatin                 return err;
134565c69066SAndrew Gallatin         }
134665c69066SAndrew Gallatin 
134765c69066SAndrew Gallatin 	if (throttle == sc->throttle)
134865c69066SAndrew Gallatin 		return 0;
134965c69066SAndrew Gallatin 
135065c69066SAndrew Gallatin         if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
135165c69066SAndrew Gallatin                 return EINVAL;
135265c69066SAndrew Gallatin 
135365c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
135465c69066SAndrew Gallatin 	cmd.data0 = throttle;
135565c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
135665c69066SAndrew Gallatin 	if (err == 0)
135765c69066SAndrew Gallatin 		sc->throttle = throttle;
135865c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
135965c69066SAndrew Gallatin 	return err;
136065c69066SAndrew Gallatin }
136165c69066SAndrew Gallatin 
136265c69066SAndrew Gallatin static int
13636d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1364b2fc195eSAndrew Gallatin {
13656d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1366b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1367b2fc195eSAndrew Gallatin         int err;
1368b2fc195eSAndrew Gallatin 
1369b2fc195eSAndrew Gallatin         sc = arg1;
1370b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1371b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1372b2fc195eSAndrew Gallatin         if (err != 0) {
1373b2fc195eSAndrew Gallatin                 return err;
1374b2fc195eSAndrew Gallatin         }
1375b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1376b2fc195eSAndrew Gallatin                 return 0;
1377b2fc195eSAndrew Gallatin 
1378b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1379b2fc195eSAndrew Gallatin                 return EINVAL;
1380b2fc195eSAndrew Gallatin 
1381a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13825e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1383b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13845e7d8541SAndrew Gallatin 
1385a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1386b2fc195eSAndrew Gallatin         return err;
1387b2fc195eSAndrew Gallatin }
1388b2fc195eSAndrew Gallatin 
1389b2fc195eSAndrew Gallatin static int
13906d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1391b2fc195eSAndrew Gallatin {
13926d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1393b2fc195eSAndrew Gallatin         unsigned int enabled;
1394b2fc195eSAndrew Gallatin         int err;
1395b2fc195eSAndrew Gallatin 
1396b2fc195eSAndrew Gallatin         sc = arg1;
1397b2fc195eSAndrew Gallatin         enabled = sc->pause;
1398b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1399b2fc195eSAndrew Gallatin         if (err != 0) {
1400b2fc195eSAndrew Gallatin                 return err;
1401b2fc195eSAndrew Gallatin         }
1402b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1403b2fc195eSAndrew Gallatin                 return 0;
1404b2fc195eSAndrew Gallatin 
1405a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
14066d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1407a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1408b2fc195eSAndrew Gallatin         return err;
1409b2fc195eSAndrew Gallatin }
1410b2fc195eSAndrew Gallatin 
1411b2fc195eSAndrew Gallatin static int
1412f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1413f04b33f8SAndrew Gallatin {
1414f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1415c587e59fSAndrew Gallatin 	int err = 0;
1416f04b33f8SAndrew Gallatin 
1417f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1418f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1419f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1420f04b33f8SAndrew Gallatin 	else
1421f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1422f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1423c587e59fSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1424f04b33f8SAndrew Gallatin 		mxge_close(sc);
1425f04b33f8SAndrew Gallatin 		err = mxge_open(sc);
1426c587e59fSAndrew Gallatin 	}
1427f04b33f8SAndrew Gallatin 	return err;
1428f04b33f8SAndrew Gallatin }
1429f04b33f8SAndrew Gallatin 
1430f04b33f8SAndrew Gallatin static int
1431276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1432276edd10SAndrew Gallatin {
1433276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1434276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1435276edd10SAndrew Gallatin 	int err;
1436276edd10SAndrew Gallatin 
1437276edd10SAndrew Gallatin 	sc = arg1;
1438276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1439276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1440276edd10SAndrew Gallatin 	if (err != 0)
1441276edd10SAndrew Gallatin 		return err;
1442276edd10SAndrew Gallatin 
1443276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1444276edd10SAndrew Gallatin 		return 0;
1445276edd10SAndrew Gallatin 
1446276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1447276edd10SAndrew Gallatin 		return EINVAL;
1448276edd10SAndrew Gallatin 
1449276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1450f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1451276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1452276edd10SAndrew Gallatin 	return err;
1453276edd10SAndrew Gallatin }
1454276edd10SAndrew Gallatin 
1455276edd10SAndrew Gallatin static int
14566d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1457b2fc195eSAndrew Gallatin {
1458b2fc195eSAndrew Gallatin         int err;
1459b2fc195eSAndrew Gallatin 
1460b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1461b2fc195eSAndrew Gallatin                 return EFAULT;
1462b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1463b2fc195eSAndrew Gallatin         arg1 = NULL;
1464b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1465b2fc195eSAndrew Gallatin 
1466b2fc195eSAndrew Gallatin         return err;
1467b2fc195eSAndrew Gallatin }
1468b2fc195eSAndrew Gallatin 
1469b2fc195eSAndrew Gallatin static void
14701e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14711e413cf9SAndrew Gallatin {
14721e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14731e413cf9SAndrew Gallatin 	int slice;
14741e413cf9SAndrew Gallatin 
14751e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14761e413cf9SAndrew Gallatin 		return;
14771e413cf9SAndrew Gallatin 
14781e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14791e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14801e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14811e413cf9SAndrew Gallatin 			continue;
14821e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14831e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14841e413cf9SAndrew Gallatin 	}
14851e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14861e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14871e413cf9SAndrew Gallatin }
14881e413cf9SAndrew Gallatin 
14891e413cf9SAndrew Gallatin static void
14906d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1491b2fc195eSAndrew Gallatin {
1492b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1493b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14945e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14951e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14961e413cf9SAndrew Gallatin 	int slice;
14971e413cf9SAndrew Gallatin 	char slice_num[8];
1498b2fc195eSAndrew Gallatin 
1499b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1500b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
15011e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1502b2fc195eSAndrew Gallatin 
15035e7d8541SAndrew Gallatin 	/* random information */
15045e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15055e7d8541SAndrew Gallatin 		       "firmware_version",
15065e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
15075e7d8541SAndrew Gallatin 		       0, "firmware version");
15085e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15095e7d8541SAndrew Gallatin 		       "serial_number",
15105e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
15115e7d8541SAndrew Gallatin 		       0, "serial number");
15125e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
15135e7d8541SAndrew Gallatin 		       "product_code",
15145e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
15155e7d8541SAndrew Gallatin 		       0, "product_code");
15165e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1517d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1518d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1519d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1520d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15215e7d8541SAndrew Gallatin 		       "tx_boundary",
15221e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
15235e7d8541SAndrew Gallatin 		       0, "tx_boundary");
15245e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1525091feecdSAndrew Gallatin 		       "write_combine",
1526091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1527091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1528091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15295e7d8541SAndrew Gallatin 		       "read_dma_MBs",
15305e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
15315e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
15325e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15335e7d8541SAndrew Gallatin 		       "write_dma_MBs",
15345e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
15355e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
15365e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15375e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
15385e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
15395e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
15405e7d8541SAndrew Gallatin 
15415e7d8541SAndrew Gallatin 
15425e7d8541SAndrew Gallatin 	/* performance related tunables */
1543b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1544b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1545b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15466d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1547b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1548b2fc195eSAndrew Gallatin 
1549b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
155065c69066SAndrew Gallatin 			"throttle",
155165c69066SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
155265c69066SAndrew Gallatin 			0, mxge_change_throttle,
155365c69066SAndrew Gallatin 			"I", "transmit throttling");
155465c69066SAndrew Gallatin 
155565c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1556b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1557b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15586d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1559b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1560b2fc195eSAndrew Gallatin 
1561b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15625e7d8541SAndrew Gallatin 		       "deassert_wait",
15635e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15645e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1565b2fc195eSAndrew Gallatin 
1566b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1567b2fc195eSAndrew Gallatin 	   Need to swap it */
1568b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1569b2fc195eSAndrew Gallatin 			"link_up",
1570b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15716d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1572b2fc195eSAndrew Gallatin 			"I", "link up");
1573b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1574b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1575b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15766d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1577b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1578b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1579adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1580adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1581adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15826d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1583adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1584adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1585adae7080SAndrew Gallatin 			"dropped_bad_phy",
1586adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1587adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1588adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1589adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1590b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1591b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1592b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1593b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
15946d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1595b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1596b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1597adae7080SAndrew Gallatin 			"dropped_link_overflow",
1598adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1599adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1600adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1601adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
16020fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
16030fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
16040fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
16050fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
16060fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
16070fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1608adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1609adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
16106d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1611adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1612b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1613b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1614b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1615b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
16166d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1617b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1618b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1619adae7080SAndrew Gallatin 			"dropped_overrun",
1620adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
16216d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1622adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1623adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1624adae7080SAndrew Gallatin 			"dropped_pause",
1625adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1626adae7080SAndrew Gallatin 			&fw->dropped_pause,
1627adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1628adae7080SAndrew Gallatin 			"I", "dropped_pause");
1629adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1630adae7080SAndrew Gallatin 			"dropped_runt",
1631adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1632adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1633adae7080SAndrew Gallatin 			"I", "dropped_runt");
1634b2fc195eSAndrew Gallatin 
1635a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1636a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1637a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1638a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1639a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1640a0394e33SAndrew Gallatin 
16415e7d8541SAndrew Gallatin 	/* verbose printing? */
1642b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16435e7d8541SAndrew Gallatin 		       "verbose",
16445e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
16455e7d8541SAndrew Gallatin 		       0, "verbose printing");
1646b2fc195eSAndrew Gallatin 
1647053e637fSAndrew Gallatin 	/* lro */
1648276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1649276edd10SAndrew Gallatin 			"lro_cnt",
1650276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1651276edd10SAndrew Gallatin 			0, mxge_change_lro,
1652276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1653053e637fSAndrew Gallatin 
16541e413cf9SAndrew Gallatin 
16551e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
16561e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
16571e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
16581e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
16591e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
16601e413cf9SAndrew Gallatin 
16611e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16621e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16631e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16641e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16651e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16661e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16671e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16681e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16691e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
16701e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1671053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16721e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16731e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16741e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16751e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16761e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16771e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16781e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16791e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16801e413cf9SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
1681053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1682053e637fSAndrew Gallatin 
1683053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16841e413cf9SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16851e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16861e413cf9SAndrew Gallatin 			       "queues");
1687053e637fSAndrew Gallatin 
1688c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16891e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16901e413cf9SAndrew Gallatin 		if (slice > 0)
16911e413cf9SAndrew Gallatin 			continue;
1692c6cb3e3fSAndrew Gallatin #endif
1693c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1694c6cb3e3fSAndrew Gallatin 			       "tx_req",
1695c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1696c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16971e413cf9SAndrew Gallatin 
16981e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16991e413cf9SAndrew Gallatin 			       "tx_done",
17001e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
17011e413cf9SAndrew Gallatin 			       0, "tx_done");
17021e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17031e413cf9SAndrew Gallatin 			       "tx_pkt_done",
17041e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
17051e413cf9SAndrew Gallatin 			       0, "tx_done");
17061e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17071e413cf9SAndrew Gallatin 			       "tx_stall",
17081e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
17091e413cf9SAndrew Gallatin 			       0, "tx_stall");
17101e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17111e413cf9SAndrew Gallatin 			       "tx_wake",
17121e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
17131e413cf9SAndrew Gallatin 			       0, "tx_wake");
17141e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
17151e413cf9SAndrew Gallatin 			       "tx_defrag",
17161e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
17171e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1718c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1719c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1720c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1721c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1722c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1723c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1724c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1725c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1726c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1727c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1728c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1729c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
17301e413cf9SAndrew Gallatin 	}
1731b2fc195eSAndrew Gallatin }
1732b2fc195eSAndrew Gallatin 
1733b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1734b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1735b2fc195eSAndrew Gallatin 
1736b2fc195eSAndrew Gallatin static inline void
17371e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1738b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1739b2fc195eSAndrew Gallatin {
1740b2fc195eSAndrew Gallatin         int idx, starting_slot;
1741b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1742b2fc195eSAndrew Gallatin         while (cnt > 1) {
1743b2fc195eSAndrew Gallatin                 cnt--;
1744b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
17456d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1746b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
174773c7c83fSAndrew Gallatin                 wmb();
1748b2fc195eSAndrew Gallatin         }
1749b2fc195eSAndrew Gallatin }
1750b2fc195eSAndrew Gallatin 
1751b2fc195eSAndrew Gallatin /*
1752b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1753b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1754b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1755b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1756b2fc195eSAndrew Gallatin  */
1757b2fc195eSAndrew Gallatin 
1758b2fc195eSAndrew Gallatin static inline void
17591e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1760b2fc195eSAndrew Gallatin                   int cnt)
1761b2fc195eSAndrew Gallatin {
1762b2fc195eSAndrew Gallatin         int idx, i;
1763b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1764b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1765b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1766b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
17675e7d8541SAndrew Gallatin 	uint8_t last_flags;
1768b2fc195eSAndrew Gallatin 
1769b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1770b2fc195eSAndrew Gallatin 
17715e7d8541SAndrew Gallatin 	last_flags = src->flags;
17725e7d8541SAndrew Gallatin 	src->flags = 0;
177373c7c83fSAndrew Gallatin         wmb();
1774b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1775b2fc195eSAndrew Gallatin         srcp = src;
1776b2fc195eSAndrew Gallatin 
1777b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1778b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
17796d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
178073c7c83fSAndrew Gallatin                         wmb(); /* force write every 32 bytes */
1781b2fc195eSAndrew Gallatin                         srcp += 2;
1782b2fc195eSAndrew Gallatin                         dstp += 2;
1783b2fc195eSAndrew Gallatin                 }
1784b2fc195eSAndrew Gallatin         } else {
1785b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1786b2fc195eSAndrew Gallatin                    that it is submitted below */
17876d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1788b2fc195eSAndrew Gallatin                 i = 0;
1789b2fc195eSAndrew Gallatin         }
1790b2fc195eSAndrew Gallatin         if (i < cnt) {
1791b2fc195eSAndrew Gallatin                 /* submit the first request */
17926d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
179373c7c83fSAndrew Gallatin                 wmb(); /* barrier before setting valid flag */
1794b2fc195eSAndrew Gallatin         }
1795b2fc195eSAndrew Gallatin 
1796b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
17975e7d8541SAndrew Gallatin         src->flags = last_flags;
1798b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1799b2fc195eSAndrew Gallatin         src_ints+=3;
1800b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1801b2fc195eSAndrew Gallatin         dst_ints+=3;
1802b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1803b2fc195eSAndrew Gallatin         tx->req += cnt;
180473c7c83fSAndrew Gallatin         wmb();
1805b2fc195eSAndrew Gallatin }
1806b2fc195eSAndrew Gallatin 
180737d89b0cSAndrew Gallatin #if IFCAP_TSO4
180837d89b0cSAndrew Gallatin 
1809b2fc195eSAndrew Gallatin static void
18101e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
18111e413cf9SAndrew Gallatin 	       int busdma_seg_cnt, int ip_off)
1812aed8e389SAndrew Gallatin {
18131e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1814aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1815aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1816aed8e389SAndrew Gallatin 	struct ip *ip;
1817aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1818aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1819aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1820aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1821aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1822aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1823aed8e389SAndrew Gallatin 	static int once;
1824aed8e389SAndrew Gallatin 
1825aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1826aed8e389SAndrew Gallatin 
1827aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1828aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1829aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1830aed8e389SAndrew Gallatin 	 */
1831aed8e389SAndrew Gallatin 
1832aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1833aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1834aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1835c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1836c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
18371e413cf9SAndrew Gallatin 			   ss->scratch);
18381e413cf9SAndrew Gallatin 		ip = (struct ip *)(ss->scratch + ip_off);
1839aed8e389SAndrew Gallatin 	} else {
1840c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1841aed8e389SAndrew Gallatin 	}
1842c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1843aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1844c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
18451e413cf9SAndrew Gallatin 			   + sizeof (*tcp),  ss->scratch);
1846c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1847aed8e389SAndrew Gallatin 	}
1848aed8e389SAndrew Gallatin 
1849aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1850c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1851aed8e389SAndrew Gallatin 
1852aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1853c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1854aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1855aed8e389SAndrew Gallatin 
1856aed8e389SAndrew Gallatin 
1857aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1858aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1859aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1860aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1861aed8e389SAndrew Gallatin 
18621e413cf9SAndrew Gallatin 	tx = &ss->tx;
1863aed8e389SAndrew Gallatin 	req = tx->req_list;
1864aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1865aed8e389SAndrew Gallatin 	cnt = 0;
1866aed8e389SAndrew Gallatin 	rdma_count = 0;
1867aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1868aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1869aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1870aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1871aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1872aed8e389SAndrew Gallatin 	 *
1873aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1874aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1875aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1876aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1877aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1878aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1879aed8e389SAndrew Gallatin 	 *
1880aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1881aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1882aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1883aed8e389SAndrew Gallatin 	 */
1884aed8e389SAndrew Gallatin 
1885aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1886aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1887aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1888aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1889e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1890aed8e389SAndrew Gallatin 
1891aed8e389SAndrew Gallatin 		while (len) {
1892aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1893e39a0a37SAndrew Gallatin 			seglen = len;
1894aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1895aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1896aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1897aed8e389SAndrew Gallatin 				/* payload */
1898aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1899aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1900aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1901aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1902aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1903aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1904aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1905aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1906aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1907aed8e389SAndrew Gallatin 				/* header ends */
1908aed8e389SAndrew Gallatin 				rdma_count = -1;
1909aed8e389SAndrew Gallatin 				cum_len_next = 0;
1910aed8e389SAndrew Gallatin 				seglen = -cum_len;
1911aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1912aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1913aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1914aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1915aed8e389SAndrew Gallatin 			    }
1916aed8e389SAndrew Gallatin 
1917aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1918aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1919aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1920aed8e389SAndrew Gallatin 			req->pad = 0;
1921aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1922aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1923aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1924aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1925aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1926aed8e389SAndrew Gallatin 			low += seglen;
1927aed8e389SAndrew Gallatin 			len -= seglen;
1928aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1929aed8e389SAndrew Gallatin 			flags = flags_next;
1930aed8e389SAndrew Gallatin 			req++;
1931aed8e389SAndrew Gallatin 			cnt++;
1932aed8e389SAndrew Gallatin 			rdma_count++;
1933aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1934aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1935aed8e389SAndrew Gallatin 			else
1936aed8e389SAndrew Gallatin 				cksum_offset = 0;
1937adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1938aed8e389SAndrew Gallatin 				goto drop;
1939aed8e389SAndrew Gallatin 		}
1940aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1941aed8e389SAndrew Gallatin 		seg++;
1942aed8e389SAndrew Gallatin 	}
1943aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1944aed8e389SAndrew Gallatin 
1945aed8e389SAndrew Gallatin 	do {
1946aed8e389SAndrew Gallatin 		req--;
1947aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1948aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1949aed8e389SAndrew Gallatin 
1950aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1951aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1952c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1953c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1954c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1955c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1956c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1957c6cb3e3fSAndrew Gallatin 		tx->activate++;
1958c6cb3e3fSAndrew Gallatin 		wmb();
1959c6cb3e3fSAndrew Gallatin 	}
1960c6cb3e3fSAndrew Gallatin #endif
1961aed8e389SAndrew Gallatin 	return;
1962aed8e389SAndrew Gallatin 
1963aed8e389SAndrew Gallatin drop:
1964e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1965aed8e389SAndrew Gallatin 	m_freem(m);
1966c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1967aed8e389SAndrew Gallatin 	if (!once) {
1968adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1969adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1970adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1971aed8e389SAndrew Gallatin 		once = 1;
1972aed8e389SAndrew Gallatin 	}
1973aed8e389SAndrew Gallatin 	return;
1974aed8e389SAndrew Gallatin 
1975aed8e389SAndrew Gallatin }
1976aed8e389SAndrew Gallatin 
197737d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
197837d89b0cSAndrew Gallatin 
197937d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1980c792928fSAndrew Gallatin /*
1981c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1982c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1983c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1984c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1985c792928fSAndrew Gallatin  */
1986c792928fSAndrew Gallatin static struct mbuf *
1987c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1988c792928fSAndrew Gallatin {
1989c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1990c792928fSAndrew Gallatin 
1991c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1992c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1993c792928fSAndrew Gallatin 		return NULL;
1994c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1995c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1996c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1997c792928fSAndrew Gallatin 			return NULL;
1998c792928fSAndrew Gallatin 	}
1999c792928fSAndrew Gallatin 	/*
2000c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2001c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2002c792928fSAndrew Gallatin 	 */
2003c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2004c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2005c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2006c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2007c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2008c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2009c792928fSAndrew Gallatin 	return m;
2010c792928fSAndrew Gallatin }
201137d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2012c792928fSAndrew Gallatin 
2013aed8e389SAndrew Gallatin static void
20141e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2015b2fc195eSAndrew Gallatin {
20161e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2017b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2018b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2019b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
2020b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20211e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2022b2fc195eSAndrew Gallatin 	struct ip *ip;
2023c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
2024aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2025aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
2026b2fc195eSAndrew Gallatin 
2027b2fc195eSAndrew Gallatin 
20281e413cf9SAndrew Gallatin 	sc = ss->sc;
2029b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
20301e413cf9SAndrew Gallatin 	tx = &ss->tx;
2031b2fc195eSAndrew Gallatin 
2032c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
203337d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2034c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2035c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2036c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2037c792928fSAndrew Gallatin 			goto drop;
2038c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
2039c792928fSAndrew Gallatin 	}
204037d89b0cSAndrew Gallatin #endif
2041b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2042b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2043b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2044aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2045b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2046adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2047b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2048b2fc195eSAndrew Gallatin 		   to defrag */
2049b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2050b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2051b2fc195eSAndrew Gallatin 			goto drop;
2052b2fc195eSAndrew Gallatin 		}
20531e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2054b2fc195eSAndrew Gallatin 		m = m_tmp;
2055b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2056b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2057aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2058b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2059b2fc195eSAndrew Gallatin 	}
2060adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2061aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2062aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2063b2fc195eSAndrew Gallatin 		goto drop;
2064b2fc195eSAndrew Gallatin 	}
2065b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2066b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20675e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2068b2fc195eSAndrew Gallatin 
206937d89b0cSAndrew Gallatin #if IFCAP_TSO4
2070aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2071aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20721e413cf9SAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, ip_off);
2073aed8e389SAndrew Gallatin 		return;
2074aed8e389SAndrew Gallatin 	}
207537d89b0cSAndrew Gallatin #endif
2076aed8e389SAndrew Gallatin 
2077b2fc195eSAndrew Gallatin 	req = tx->req_list;
2078b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20795e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20805e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2081b2fc195eSAndrew Gallatin 
2082b2fc195eSAndrew Gallatin 	/* checksum offloading? */
2083b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
2084aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2085aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
2086c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
2087c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
20881e413cf9SAndrew Gallatin 				   ss->scratch);
20891e413cf9SAndrew Gallatin 			ip = (struct ip *)(ss->scratch + ip_off);
2090aed8e389SAndrew Gallatin 		} else {
2091c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
2092aed8e389SAndrew Gallatin 		}
2093c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
2094b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20955e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2096b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
20975e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2098aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2099aed8e389SAndrew Gallatin 	} else {
2100aed8e389SAndrew Gallatin 		odd_flag = 0;
2101b2fc195eSAndrew Gallatin 	}
21025e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21035e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2104b2fc195eSAndrew Gallatin 
2105b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2106b2fc195eSAndrew Gallatin 	cum_len = 0;
2107aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21085e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2109b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2110b2fc195eSAndrew Gallatin 		req->addr_low =
21116d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2112b2fc195eSAndrew Gallatin 		req->addr_high =
21136d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2114b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2115b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2116b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2117b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2118b2fc195eSAndrew Gallatin 		else
2119b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21205e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21215e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21225e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2123aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2124b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2125b2fc195eSAndrew Gallatin 		seg++;
2126b2fc195eSAndrew Gallatin 		req++;
2127b2fc195eSAndrew Gallatin 		req->flags = 0;
2128b2fc195eSAndrew Gallatin 	}
2129b2fc195eSAndrew Gallatin 	req--;
2130b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2131b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2132b2fc195eSAndrew Gallatin 		req++;
2133b2fc195eSAndrew Gallatin 		req->addr_low =
21346d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2135b2fc195eSAndrew Gallatin 		req->addr_high =
21366d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2137b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21385e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21395e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21405e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21415e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2142aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2143b2fc195eSAndrew Gallatin 		cnt++;
2144b2fc195eSAndrew Gallatin 	}
21455e7d8541SAndrew Gallatin 
21465e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
21475e7d8541SAndrew Gallatin #if 0
21485e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21495e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21505e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21515e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21525e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21535e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21545e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21555e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21565e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21575e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21585e7d8541SAndrew Gallatin 	}
21595e7d8541SAndrew Gallatin 	printf("--------------\n");
21605e7d8541SAndrew Gallatin #endif
21615e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21626d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2163c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2164c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2165c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2166c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2167c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2168c6cb3e3fSAndrew Gallatin 		tx->activate++;
2169c6cb3e3fSAndrew Gallatin 		wmb();
2170c6cb3e3fSAndrew Gallatin 	}
2171c6cb3e3fSAndrew Gallatin #endif
2172b2fc195eSAndrew Gallatin 	return;
2173b2fc195eSAndrew Gallatin 
2174b2fc195eSAndrew Gallatin drop:
2175b2fc195eSAndrew Gallatin 	m_freem(m);
2176c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2177b2fc195eSAndrew Gallatin 	return;
2178b2fc195eSAndrew Gallatin }
2179b2fc195eSAndrew Gallatin 
2180c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2181c6cb3e3fSAndrew Gallatin static void
2182c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2183c6cb3e3fSAndrew Gallatin {
2184c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2185c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2186c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2187c6cb3e3fSAndrew Gallatin 	int slice;
2188b2fc195eSAndrew Gallatin 
2189c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2190c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2191c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2192c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2193c6cb3e3fSAndrew Gallatin 			m_freem(m);
2194c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2195c6cb3e3fSAndrew Gallatin 	}
2196c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2197c6cb3e3fSAndrew Gallatin }
21986d914a32SAndrew Gallatin 
2199c6cb3e3fSAndrew Gallatin static inline void
2200c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2201c6cb3e3fSAndrew Gallatin {
2202c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2203c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2204c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2205c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2206c6cb3e3fSAndrew Gallatin 
2207c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2208c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2209c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2210c6cb3e3fSAndrew Gallatin 
2211c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2212c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2213c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2214c6cb3e3fSAndrew Gallatin 			return;
2215c6cb3e3fSAndrew Gallatin 		}
2216c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2217c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2218c6cb3e3fSAndrew Gallatin 
2219c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2220c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2221c6cb3e3fSAndrew Gallatin 	}
2222c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2223c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2224c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2225c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2226c6cb3e3fSAndrew Gallatin 		tx->stall++;
2227c6cb3e3fSAndrew Gallatin 	}
2228c6cb3e3fSAndrew Gallatin }
2229c6cb3e3fSAndrew Gallatin 
2230c6cb3e3fSAndrew Gallatin static int
2231c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2232c6cb3e3fSAndrew Gallatin {
2233c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2234c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2235c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2236c6cb3e3fSAndrew Gallatin 	int err;
2237c6cb3e3fSAndrew Gallatin 
2238c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2239c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2240c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2241c6cb3e3fSAndrew Gallatin 
2242c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2243c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2244c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2245c6cb3e3fSAndrew Gallatin 		return (err);
2246c6cb3e3fSAndrew Gallatin 	}
2247c6cb3e3fSAndrew Gallatin 
2248c6cb3e3fSAndrew Gallatin 	if (drbr_empty(ifp, tx->br) &&
2249c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2250c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2251c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2252c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2253c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2254c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2255c6cb3e3fSAndrew Gallatin 		return (err);
2256c6cb3e3fSAndrew Gallatin 	}
2257c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2258c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2259c6cb3e3fSAndrew Gallatin 	return (0);
2260c6cb3e3fSAndrew Gallatin }
2261c6cb3e3fSAndrew Gallatin 
2262c6cb3e3fSAndrew Gallatin static int
2263c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2264c6cb3e3fSAndrew Gallatin {
2265c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2266c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2267c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2268c6cb3e3fSAndrew Gallatin 	int err = 0;
2269c6cb3e3fSAndrew Gallatin 	int slice;
2270c6cb3e3fSAndrew Gallatin 
2271c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2272c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2273c6cb3e3fSAndrew Gallatin 
2274c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2275c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2276c6cb3e3fSAndrew Gallatin 
2277c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2278c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2279c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2280c6cb3e3fSAndrew Gallatin 	} else {
2281c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2282c6cb3e3fSAndrew Gallatin 	}
2283c6cb3e3fSAndrew Gallatin 
2284c6cb3e3fSAndrew Gallatin 	return (err);
2285c6cb3e3fSAndrew Gallatin }
2286c6cb3e3fSAndrew Gallatin 
2287c6cb3e3fSAndrew Gallatin #else
22886d914a32SAndrew Gallatin 
22896d914a32SAndrew Gallatin static inline void
22901e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2291b2fc195eSAndrew Gallatin {
22921e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2293b2fc195eSAndrew Gallatin 	struct mbuf *m;
2294b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
22951e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2296b2fc195eSAndrew Gallatin 
22971e413cf9SAndrew Gallatin 	sc = ss->sc;
2298b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
22991e413cf9SAndrew Gallatin 	tx = &ss->tx;
2300adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
23016d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
23026d914a32SAndrew Gallatin 		if (m == NULL) {
23036d914a32SAndrew Gallatin 			return;
23046d914a32SAndrew Gallatin 		}
2305b2fc195eSAndrew Gallatin 		/* let BPF see it */
2306b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2307b2fc195eSAndrew Gallatin 
2308b2fc195eSAndrew Gallatin 		/* give it to the nic */
23091e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
23106d914a32SAndrew Gallatin 	}
23116d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2312a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2313b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2314adae7080SAndrew Gallatin 		tx->stall++;
2315a82c2581SAndrew Gallatin 	}
2316b2fc195eSAndrew Gallatin }
2317c6cb3e3fSAndrew Gallatin #endif
2318b2fc195eSAndrew Gallatin static void
23196d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2320b2fc195eSAndrew Gallatin {
23216d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
23221e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2323b2fc195eSAndrew Gallatin 
23241e413cf9SAndrew Gallatin 	/* only use the first slice for now */
23251e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
23261e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
23271e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
23281e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2329b2fc195eSAndrew Gallatin }
2330b2fc195eSAndrew Gallatin 
23315e7d8541SAndrew Gallatin /*
23325e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23335e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23345e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23355e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23365e7d8541SAndrew Gallatin  * in a burst
23375e7d8541SAndrew Gallatin  */
23385e7d8541SAndrew Gallatin static inline void
23395e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23405e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
23415e7d8541SAndrew Gallatin {
23425e7d8541SAndrew Gallatin 	uint32_t low;
23435e7d8541SAndrew Gallatin 
23445e7d8541SAndrew Gallatin 	low = src->addr_low;
23455e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2346a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
234773c7c83fSAndrew Gallatin 	wmb();
2348a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
234973c7c83fSAndrew Gallatin 	wmb();
235040385a5fSAndrew Gallatin 	src->addr_low = low;
23515e7d8541SAndrew Gallatin 	dst->addr_low = low;
235273c7c83fSAndrew Gallatin 	wmb();
23535e7d8541SAndrew Gallatin }
23545e7d8541SAndrew Gallatin 
2355b2fc195eSAndrew Gallatin static int
23561e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2357b2fc195eSAndrew Gallatin {
2358b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2359b2fc195eSAndrew Gallatin 	struct mbuf *m;
23601e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2361b2fc195eSAndrew Gallatin 	int cnt, err;
2362b2fc195eSAndrew Gallatin 
2363b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
2364b2fc195eSAndrew Gallatin 	if (m == NULL) {
2365b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2366b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2367b2fc195eSAndrew Gallatin 		goto done;
2368b2fc195eSAndrew Gallatin 	}
2369b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2370b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2371b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2372b2fc195eSAndrew Gallatin 	if (err != 0) {
2373b2fc195eSAndrew Gallatin 		m_free(m);
2374b2fc195eSAndrew Gallatin 		goto done;
2375b2fc195eSAndrew Gallatin 	}
2376b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2377b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23786d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2379b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23806d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2381b2fc195eSAndrew Gallatin 
2382b2fc195eSAndrew Gallatin done:
2383adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2384adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2385b2fc195eSAndrew Gallatin 	return err;
2386b2fc195eSAndrew Gallatin }
2387b2fc195eSAndrew Gallatin 
2388b2fc195eSAndrew Gallatin static int
23891e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2390b2fc195eSAndrew Gallatin {
2391053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2392b2fc195eSAndrew Gallatin 	struct mbuf *m;
23931e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2394053e637fSAndrew Gallatin 	int cnt, err, i;
2395b2fc195eSAndrew Gallatin 
2396a0394e33SAndrew Gallatin 	if (rx->cl_size == MCLBYTES)
2397a0394e33SAndrew Gallatin 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2398a0394e33SAndrew Gallatin 	else
2399053e637fSAndrew Gallatin 		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2400b2fc195eSAndrew Gallatin 	if (m == NULL) {
2401b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2402b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2403b2fc195eSAndrew Gallatin 		goto done;
2404b2fc195eSAndrew Gallatin 	}
24054d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2406b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2407053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2408b2fc195eSAndrew Gallatin 	if (err != 0) {
2409b2fc195eSAndrew Gallatin 		m_free(m);
2410b2fc195eSAndrew Gallatin 		goto done;
2411b2fc195eSAndrew Gallatin 	}
2412b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2413b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2414b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2415b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2416b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2417053e637fSAndrew Gallatin 
2418b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2419b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2420053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2421053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2422053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2423053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2424053e637fSAndrew Gallatin        }
2425b0f7b922SAndrew Gallatin #endif
2426b2fc195eSAndrew Gallatin 
2427b2fc195eSAndrew Gallatin done:
2428053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2429b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
24305e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
24315e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2432b2fc195eSAndrew Gallatin 		}
2433053e637fSAndrew Gallatin 		idx++;
2434053e637fSAndrew Gallatin 	}
2435b2fc195eSAndrew Gallatin 	return err;
2436b2fc195eSAndrew Gallatin }
2437b2fc195eSAndrew Gallatin 
24389b03b0f3SAndrew Gallatin /*
24399b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
24409b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
24419b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
24429b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2443053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2444053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
24459b03b0f3SAndrew Gallatin  */
24469b03b0f3SAndrew Gallatin 
2447053e637fSAndrew Gallatin static inline uint16_t
2448053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2449053e637fSAndrew Gallatin {
2450053e637fSAndrew Gallatin 	struct ether_header *eh;
2451053e637fSAndrew Gallatin 	struct ip *ip;
2452053e637fSAndrew Gallatin 	uint16_t c;
2453053e637fSAndrew Gallatin 
2454053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2455053e637fSAndrew Gallatin 
2456053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2457053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2458053e637fSAndrew Gallatin 		return 1;
2459053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2460053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2461053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2462053e637fSAndrew Gallatin 		return 1;
2463eb6219e3SAndrew Gallatin #ifdef INET
2464053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2465053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2466053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2467eb6219e3SAndrew Gallatin #else
2468eb6219e3SAndrew Gallatin 	c = 1;
2469eb6219e3SAndrew Gallatin #endif
2470053e637fSAndrew Gallatin 	c ^= 0xffff;
2471053e637fSAndrew Gallatin 	return (c);
24725e7d8541SAndrew Gallatin }
2473053e637fSAndrew Gallatin 
2474c792928fSAndrew Gallatin static void
2475c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2476c792928fSAndrew Gallatin {
2477c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2478c792928fSAndrew Gallatin 	struct ether_header *eh;
2479c792928fSAndrew Gallatin 	uint32_t partial;
2480c792928fSAndrew Gallatin 
2481c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2482c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2483c792928fSAndrew Gallatin 
2484c792928fSAndrew Gallatin 	/*
2485c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2486c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2487c792928fSAndrew Gallatin 	 * header.
2488c792928fSAndrew Gallatin 	 */
2489c792928fSAndrew Gallatin 
2490c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2491c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2492c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2493c792928fSAndrew Gallatin 	(*csum) += ~partial;
2494c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2495c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2496c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2497c792928fSAndrew Gallatin 
2498c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2499c792928fSAndrew Gallatin 	   later consumers expect this */
2500c792928fSAndrew Gallatin 	*csum = htons(*csum);
2501c792928fSAndrew Gallatin 
2502c792928fSAndrew Gallatin 	/* save the tag */
250337d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2504c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
250537d89b0cSAndrew Gallatin #else
250637d89b0cSAndrew Gallatin 	{
250737d89b0cSAndrew Gallatin 		struct m_tag *mtag;
250837d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
250937d89b0cSAndrew Gallatin 				   M_NOWAIT);
251037d89b0cSAndrew Gallatin 		if (mtag == NULL)
251137d89b0cSAndrew Gallatin 			return;
251237d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
251337d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
251437d89b0cSAndrew Gallatin 	}
251537d89b0cSAndrew Gallatin 
251637d89b0cSAndrew Gallatin #endif
251737d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2518c792928fSAndrew Gallatin 
2519c792928fSAndrew Gallatin 	/*
2520c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2521c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2522c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2523c792928fSAndrew Gallatin 	 * type field is already in place.
2524c792928fSAndrew Gallatin 	 */
2525c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2526c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2527c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2528c792928fSAndrew Gallatin }
2529c792928fSAndrew Gallatin 
25305e7d8541SAndrew Gallatin 
25315e7d8541SAndrew Gallatin static inline void
25321e413cf9SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2533b2fc195eSAndrew Gallatin {
25341e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2535b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2536053e637fSAndrew Gallatin 	struct mbuf *m;
2537c792928fSAndrew Gallatin 	struct ether_header *eh;
25381e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2539053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2540b2fc195eSAndrew Gallatin 	int idx;
2541053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2542b2fc195eSAndrew Gallatin 
25431e413cf9SAndrew Gallatin 	sc = ss->sc;
2544b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
25451e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2546b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2547053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2548b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2549b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2550b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25511e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2552053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2553053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2554053e637fSAndrew Gallatin 		return;
2555b2fc195eSAndrew Gallatin 	}
2556053e637fSAndrew Gallatin 
2557b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2558b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2559b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2560b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2561b2fc195eSAndrew Gallatin 
2562b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2563b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2564b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2565b2fc195eSAndrew Gallatin 
2566053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2567053e637fSAndrew Gallatin 	 * aligned */
25685e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2569b2fc195eSAndrew Gallatin 
2570053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2571053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25721e413cf9SAndrew Gallatin 	ss->ipackets++;
2573c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2574c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2575c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2576c792928fSAndrew Gallatin 	}
2577b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2578053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
25791e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2580b2fc195eSAndrew Gallatin 			return;
2581053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2582053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2583053e637fSAndrew Gallatin 		   checksum is good */
2584053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2585053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2586b2fc195eSAndrew Gallatin 	}
2587c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2588c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2589c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2590c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2591c6cb3e3fSAndrew Gallatin 	}
2592053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2593053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2594b2fc195eSAndrew Gallatin }
2595b2fc195eSAndrew Gallatin 
2596b2fc195eSAndrew Gallatin static inline void
25971e413cf9SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2598b2fc195eSAndrew Gallatin {
25991e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2600b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2601c792928fSAndrew Gallatin 	struct ether_header *eh;
2602b2fc195eSAndrew Gallatin 	struct mbuf *m;
26031e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2604b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2605b2fc195eSAndrew Gallatin 	int idx;
2606053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2607b2fc195eSAndrew Gallatin 
26081e413cf9SAndrew Gallatin 	sc = ss->sc;
2609b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26101e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2611b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2612b2fc195eSAndrew Gallatin 	rx->cnt++;
2613b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2614b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2615b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26161e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2617b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2618b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2619b2fc195eSAndrew Gallatin 		return;
2620b2fc195eSAndrew Gallatin 	}
2621b2fc195eSAndrew Gallatin 
2622b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2623b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2624b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2625b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2626b2fc195eSAndrew Gallatin 
2627b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2628b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2629b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2630b2fc195eSAndrew Gallatin 
2631b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2632b2fc195eSAndrew Gallatin 	 * aligned */
26335e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2634b2fc195eSAndrew Gallatin 
26359b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
26369b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
26371e413cf9SAndrew Gallatin 	ss->ipackets++;
2638c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2639c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2640c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2641c792928fSAndrew Gallatin 	}
2642b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2643053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
26441e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2645053e637fSAndrew Gallatin 			return;
2646053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2647053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2648053e637fSAndrew Gallatin 		   checksum is good */
2649053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2650053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2651053e637fSAndrew Gallatin 	}
2652c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2653c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2654c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2655c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2656c6cb3e3fSAndrew Gallatin 	}
2657b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2658b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2659b2fc195eSAndrew Gallatin }
2660b2fc195eSAndrew Gallatin 
2661b2fc195eSAndrew Gallatin static inline void
26621e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
26635e7d8541SAndrew Gallatin {
26641e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26655e7d8541SAndrew Gallatin 	int limit = 0;
26665e7d8541SAndrew Gallatin 	uint16_t length;
26675e7d8541SAndrew Gallatin 	uint16_t checksum;
26685e7d8541SAndrew Gallatin 
26695e7d8541SAndrew Gallatin 
26705e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
26715e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
26725e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2673053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2674b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
26751e413cf9SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum);
26765e7d8541SAndrew Gallatin 		else
26771e413cf9SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum);
26785e7d8541SAndrew Gallatin 		rx_done->cnt++;
2679adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
26805e7d8541SAndrew Gallatin 
26815e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2682f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
26835e7d8541SAndrew Gallatin 			break;
2684053e637fSAndrew Gallatin 	}
2685eb6219e3SAndrew Gallatin #ifdef INET
26861e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_active)) {
2687eb6219e3SAndrew Gallatin 		struct lro_entry *lro = SLIST_FIRST(&ss->lro_active);
26881e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
26891e413cf9SAndrew Gallatin 		mxge_lro_flush(ss, lro);
26905e7d8541SAndrew Gallatin 	}
2691eb6219e3SAndrew Gallatin #endif
26925e7d8541SAndrew Gallatin }
26935e7d8541SAndrew Gallatin 
26945e7d8541SAndrew Gallatin 
26955e7d8541SAndrew Gallatin static inline void
26961e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2697b2fc195eSAndrew Gallatin {
2698b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
26991e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2700b2fc195eSAndrew Gallatin 	struct mbuf *m;
2701b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2702f616ebc7SAndrew Gallatin 	int idx;
2703c6cb3e3fSAndrew Gallatin 	int *flags;
2704b2fc195eSAndrew Gallatin 
27051e413cf9SAndrew Gallatin 	tx = &ss->tx;
27061e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
27075e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2708b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2709b2fc195eSAndrew Gallatin 		tx->done++;
2710b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2711b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2712b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2713b2fc195eSAndrew Gallatin 		if (m != NULL) {
271471032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
271571032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
271671032832SAndrew Gallatin 				ss->omcasts++;
2717c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2718b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2719b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2720b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2721b2fc195eSAndrew Gallatin 			m_freem(m);
2722b2fc195eSAndrew Gallatin 		}
27235e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
27245e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
27255e7d8541SAndrew Gallatin 			tx->pkt_done++;
27265e7d8541SAndrew Gallatin 		}
2727b2fc195eSAndrew Gallatin 	}
2728b2fc195eSAndrew Gallatin 
2729b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2730b2fc195eSAndrew Gallatin            its OK to send packets */
2731c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2732c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2733c6cb3e3fSAndrew Gallatin #else
2734c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2735c6cb3e3fSAndrew Gallatin #endif
27361e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2737c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2738c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2739c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
27401e413cf9SAndrew Gallatin 		ss->tx.wake++;
27411e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2742b2fc195eSAndrew Gallatin 	}
2743c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2744c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2745c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2746c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2747c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2748c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2749c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2750c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2751c6cb3e3fSAndrew Gallatin 			wmb();
2752c6cb3e3fSAndrew Gallatin 		}
2753c6cb3e3fSAndrew Gallatin 	}
2754c6cb3e3fSAndrew Gallatin #endif
2755c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2756c6cb3e3fSAndrew Gallatin 
2757b2fc195eSAndrew Gallatin }
2758b2fc195eSAndrew Gallatin 
275901638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2760c587e59fSAndrew Gallatin {
2761c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2762c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2763c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2764c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
276501638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2766c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2767c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2768c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2769c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2770c587e59fSAndrew Gallatin };
277101638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
277201638550SAndrew Gallatin {
27734ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
277401638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
277501638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
277601638550SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"}
277701638550SAndrew Gallatin };
2778c587e59fSAndrew Gallatin 
2779c587e59fSAndrew Gallatin static void
2780c587e59fSAndrew Gallatin mxge_set_media(mxge_softc_t *sc, int type)
2781c587e59fSAndrew Gallatin {
2782c587e59fSAndrew Gallatin 	sc->media_flags |= type;
2783c587e59fSAndrew Gallatin 	ifmedia_add(&sc->media, sc->media_flags, 0, NULL);
2784c587e59fSAndrew Gallatin 	ifmedia_set(&sc->media, sc->media_flags);
2785c587e59fSAndrew Gallatin }
2786c587e59fSAndrew Gallatin 
2787c587e59fSAndrew Gallatin 
2788c587e59fSAndrew Gallatin /*
2789c587e59fSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2790c587e59fSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2791c587e59fSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2792c587e59fSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2793c587e59fSAndrew Gallatin  * than in the interrupt handler itself.   This need only be done
2794c587e59fSAndrew Gallatin  * once, not each time the link is up.
2795c587e59fSAndrew Gallatin  */
2796c587e59fSAndrew Gallatin static void
2797c587e59fSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2798c587e59fSAndrew Gallatin {
2799c587e59fSAndrew Gallatin 	mxge_cmd_t cmd;
280001638550SAndrew Gallatin 	char *cage_type;
2801c587e59fSAndrew Gallatin 	char *ptr;
280201638550SAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
280301638550SAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
280401638550SAndrew Gallatin 	uint32_t byte;
2805c587e59fSAndrew Gallatin 
2806c587e59fSAndrew Gallatin 	sc->need_media_probe = 0;
2807c587e59fSAndrew Gallatin 
2808c587e59fSAndrew Gallatin 	/* if we've already set a media type, we're done */
2809c587e59fSAndrew Gallatin 	if (sc->media_flags  != (IFM_ETHER | IFM_AUTO))
2810c587e59fSAndrew Gallatin 		return;
2811c587e59fSAndrew Gallatin 
2812c587e59fSAndrew Gallatin 	/*
2813c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2814c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2815c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2816c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2817c587e59fSAndrew Gallatin 	 */
2818c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2819c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2820c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2821c587e59fSAndrew Gallatin 	}
2822c587e59fSAndrew Gallatin 
2823c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
282437d89b0cSAndrew Gallatin 		ptr = index(ptr, '-');
2825c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2826c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2827c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2828c587e59fSAndrew Gallatin 			return;
2829c587e59fSAndrew Gallatin 		}
2830c587e59fSAndrew Gallatin 	}
2831c587e59fSAndrew Gallatin 	if (*ptr == 'C') {
283201638550SAndrew Gallatin 		/* -C is CX4 */
2833c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2834c587e59fSAndrew Gallatin 		return;
2835c587e59fSAndrew Gallatin 	}
2836c587e59fSAndrew Gallatin 	else if (*ptr == 'Q') {
283701638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2838c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2839c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2840c587e59fSAndrew Gallatin 		return;
2841c587e59fSAndrew Gallatin 	}
2842c587e59fSAndrew Gallatin 
284301638550SAndrew Gallatin 	if (*ptr == 'R') {
284401638550SAndrew Gallatin 		/* -R is XFP */
284501638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
284601638550SAndrew Gallatin 		mxge_media_type_entries =
284701638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types) /
284801638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types[0]);
284901638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
285001638550SAndrew Gallatin 		cage_type = "XFP";
285101638550SAndrew Gallatin 	}
285201638550SAndrew Gallatin 
285301638550SAndrew Gallatin 	if (*ptr == 'S' || *(ptr +1) == 'S') {
285401638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
285501638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
285601638550SAndrew Gallatin 		mxge_media_type_entries =
285701638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types) /
285801638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types[0]);
285901638550SAndrew Gallatin 		cage_type = "SFP+";
286001638550SAndrew Gallatin 		byte = 3;
286101638550SAndrew Gallatin 	}
286201638550SAndrew Gallatin 
286301638550SAndrew Gallatin 	if (mxge_media_types == NULL) {
2864c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2865c587e59fSAndrew Gallatin 		return;
2866c587e59fSAndrew Gallatin 	}
2867c587e59fSAndrew Gallatin 
2868c587e59fSAndrew Gallatin 	/*
2869c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2870c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2871c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2872c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2873c587e59fSAndrew Gallatin 	 * a millisecond
2874c587e59fSAndrew Gallatin 	 */
2875c587e59fSAndrew Gallatin 
2876c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
287701638550SAndrew Gallatin 	cmd.data1 = byte;
287801638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
287901638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2880c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2881c587e59fSAndrew Gallatin 	}
288201638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
288301638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2884c587e59fSAndrew Gallatin 	}
2885c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2886c587e59fSAndrew Gallatin 		return;
2887c587e59fSAndrew Gallatin 	}
2888c587e59fSAndrew Gallatin 
2889c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
289001638550SAndrew Gallatin 	cmd.data0 = byte;
289101638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2892c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2893c587e59fSAndrew Gallatin 		DELAY(1000);
289401638550SAndrew Gallatin 		cmd.data0 = byte;
289501638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2896c587e59fSAndrew Gallatin 	}
2897c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
289801638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
289901638550SAndrew Gallatin 			      cage_type, err, ms);
2900c587e59fSAndrew Gallatin 		return;
2901c587e59fSAndrew Gallatin 	}
2902c587e59fSAndrew Gallatin 
2903c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2904c587e59fSAndrew Gallatin 		if (mxge_verbose)
290501638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2906c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2907c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2908c587e59fSAndrew Gallatin 		return;
2909c587e59fSAndrew Gallatin 	}
291001638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
2911c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2912c587e59fSAndrew Gallatin 			if (mxge_verbose)
291301638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
291401638550SAndrew Gallatin 					      cage_type,
2915c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2916c587e59fSAndrew Gallatin 
2917c587e59fSAndrew Gallatin 			mxge_set_media(sc, mxge_media_types[i].flag);
2918c587e59fSAndrew Gallatin 			return;
2919c587e59fSAndrew Gallatin 		}
2920c587e59fSAndrew Gallatin 	}
292101638550SAndrew Gallatin 	device_printf(sc->dev, "%s media 0x%x unknown\n", cage_type,
292201638550SAndrew Gallatin 		      cmd.data0);
2923c587e59fSAndrew Gallatin 
2924c587e59fSAndrew Gallatin 	return;
2925c587e59fSAndrew Gallatin }
2926c587e59fSAndrew Gallatin 
2927b2fc195eSAndrew Gallatin static void
29286d87a65dSAndrew Gallatin mxge_intr(void *arg)
2929b2fc195eSAndrew Gallatin {
29301e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
29311e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
29321e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
29331e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
29341e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
29355e7d8541SAndrew Gallatin 	uint32_t send_done_count;
29365e7d8541SAndrew Gallatin 	uint8_t valid;
2937b2fc195eSAndrew Gallatin 
2938b2fc195eSAndrew Gallatin 
2939c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
29401e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
29411e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
29421e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
29431e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
29441e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
29451e413cf9SAndrew Gallatin 		return;
29461e413cf9SAndrew Gallatin 	}
2947c6cb3e3fSAndrew Gallatin #endif
29481e413cf9SAndrew Gallatin 
29495e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
29505e7d8541SAndrew Gallatin 	if (!stats->valid) {
29515e7d8541SAndrew Gallatin 		return;
2952b2fc195eSAndrew Gallatin 	}
29535e7d8541SAndrew Gallatin 	valid = stats->valid;
2954b2fc195eSAndrew Gallatin 
295591ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
29565e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
29575e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
29585e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
29595e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
29605e7d8541SAndrew Gallatin 			stats->valid = 0;
2961dc8731d4SAndrew Gallatin 	} else {
2962dc8731d4SAndrew Gallatin 		stats->valid = 0;
2963dc8731d4SAndrew Gallatin 	}
2964dc8731d4SAndrew Gallatin 
2965dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
29665e7d8541SAndrew Gallatin 	do {
29675e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
29685e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
29695e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
29705e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
2971c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
29721e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
29731e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
29745e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2975b2fc195eSAndrew Gallatin 		}
297691ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
297773c7c83fSAndrew Gallatin 			wmb();
29785e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2979b2fc195eSAndrew Gallatin 
2980c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
2981c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
29825e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
29835e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2984b2fc195eSAndrew Gallatin 			if (sc->link_state) {
29855e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
29865e7d8541SAndrew Gallatin 				if (mxge_verbose)
29875e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2988b2fc195eSAndrew Gallatin 			} else {
29895e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
29905e7d8541SAndrew Gallatin 				if (mxge_verbose)
29915e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2992b2fc195eSAndrew Gallatin 			}
2993c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
2994b2fc195eSAndrew Gallatin 		}
2995b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
29961e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
2997b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
29981e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
29995e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
30005e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
30015e7d8541SAndrew Gallatin 		}
3002c587e59fSAndrew Gallatin 
3003c587e59fSAndrew Gallatin 		if (stats->link_down) {
30045e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3005c587e59fSAndrew Gallatin 			sc->link_state = 0;
3006c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3007c587e59fSAndrew Gallatin 		}
3008b2fc195eSAndrew Gallatin 	}
3009b2fc195eSAndrew Gallatin 
30105e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
30115e7d8541SAndrew Gallatin 	if (valid & 0x1)
30121e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
30131e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3014b2fc195eSAndrew Gallatin }
3015b2fc195eSAndrew Gallatin 
3016b2fc195eSAndrew Gallatin static void
30176d87a65dSAndrew Gallatin mxge_init(void *arg)
3018b2fc195eSAndrew Gallatin {
3019b2fc195eSAndrew Gallatin }
3020b2fc195eSAndrew Gallatin 
3021b2fc195eSAndrew Gallatin 
3022b2fc195eSAndrew Gallatin 
3023b2fc195eSAndrew Gallatin static void
30241e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
30251e413cf9SAndrew Gallatin {
30261e413cf9SAndrew Gallatin 	struct lro_entry *lro_entry;
30271e413cf9SAndrew Gallatin 	int i;
30281e413cf9SAndrew Gallatin 
30291e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_free)) {
30301e413cf9SAndrew Gallatin 		lro_entry = SLIST_FIRST(&ss->lro_free);
30311e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
30321e413cf9SAndrew Gallatin 		free(lro_entry, M_DEVBUF);
30331e413cf9SAndrew Gallatin 	}
30341e413cf9SAndrew Gallatin 
30351e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
30361e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
30371e413cf9SAndrew Gallatin 			continue;
30381e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
30391e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
30401e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
30411e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
30421e413cf9SAndrew Gallatin 	}
30431e413cf9SAndrew Gallatin 
30441e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
30451e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
30461e413cf9SAndrew Gallatin 			continue;
30471e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
30481e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
30491e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
30501e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
30511e413cf9SAndrew Gallatin 	}
30521e413cf9SAndrew Gallatin 
30531e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
30541e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30551e413cf9SAndrew Gallatin 		return;
30561e413cf9SAndrew Gallatin 
30571e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30581e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
30591e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
30601e413cf9SAndrew Gallatin 			continue;
30611e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
30621e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
30631e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
30641e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
30651e413cf9SAndrew Gallatin 	}
30661e413cf9SAndrew Gallatin }
30671e413cf9SAndrew Gallatin 
30681e413cf9SAndrew Gallatin static void
30696d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3070b2fc195eSAndrew Gallatin {
30711e413cf9SAndrew Gallatin 	int slice;
30721e413cf9SAndrew Gallatin 
30731e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
30741e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
30751e413cf9SAndrew Gallatin }
30761e413cf9SAndrew Gallatin 
30771e413cf9SAndrew Gallatin static void
30781e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
30791e413cf9SAndrew Gallatin {
3080b2fc195eSAndrew Gallatin 	int i;
3081b2fc195eSAndrew Gallatin 
3082b2fc195eSAndrew Gallatin 
30831e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
30841e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
30851e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3086b2fc195eSAndrew Gallatin 
30871e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
30881e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
30891e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
30901e413cf9SAndrew Gallatin 
30911e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
30921e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
30931e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
30941e413cf9SAndrew Gallatin 
30951e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
30961e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
30971e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
30981e413cf9SAndrew Gallatin 
30991e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
31001e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
31011e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
31021e413cf9SAndrew Gallatin 
31031e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
31041e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
31051e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
31061e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
31071e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3108b2fc195eSAndrew Gallatin 			}
31091e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
31101e413cf9SAndrew Gallatin 		}
31111e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
31121e413cf9SAndrew Gallatin 	}
31131e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
31141e413cf9SAndrew Gallatin 
31151e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
31161e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
31171e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
31181e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
31191e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
31201e413cf9SAndrew Gallatin 			}
31211e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
31221e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
31231e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
31241e413cf9SAndrew Gallatin 		}
31251e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
31261e413cf9SAndrew Gallatin 	}
31271e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
31281e413cf9SAndrew Gallatin 
31291e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
31301e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
31311e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
31321e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
31331e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
31341e413cf9SAndrew Gallatin 			}
31351e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
31361e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
31371e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
31381e413cf9SAndrew Gallatin 		}
31391e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
31401e413cf9SAndrew Gallatin 	}
31411e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3142b2fc195eSAndrew Gallatin }
3143b2fc195eSAndrew Gallatin 
3144b2fc195eSAndrew Gallatin static void
31456d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3146b2fc195eSAndrew Gallatin {
31471e413cf9SAndrew Gallatin 	int slice;
3148b2fc195eSAndrew Gallatin 
31491e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31501e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3151c2657176SAndrew Gallatin }
3152b2fc195eSAndrew Gallatin 
3153b2fc195eSAndrew Gallatin static int
31541e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
31551e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3156b2fc195eSAndrew Gallatin {
31571e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
31581e413cf9SAndrew Gallatin 	size_t bytes;
31591e413cf9SAndrew Gallatin 	int err, i;
3160b2fc195eSAndrew Gallatin 
3161b2fc195eSAndrew Gallatin 	err = ENOMEM;
3162b2fc195eSAndrew Gallatin 
31631e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3164adae7080SAndrew Gallatin 
31651e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
31661e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3167aed8e389SAndrew Gallatin 
3168b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
31691e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
31701e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31711e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow == NULL)
31721e413cf9SAndrew Gallatin 		return err;;
3173b2fc195eSAndrew Gallatin 
31741e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
31751e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31761e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow == NULL)
31771e413cf9SAndrew Gallatin 		return err;;
3178b2fc195eSAndrew Gallatin 
31791e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
31801e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
31811e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31821e413cf9SAndrew Gallatin 	if (ss->rx_small.info == NULL)
31831e413cf9SAndrew Gallatin 		return err;;
3184b2fc195eSAndrew Gallatin 
31851e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
31861e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31871e413cf9SAndrew Gallatin 	if (ss->rx_big.info == NULL)
31881e413cf9SAndrew Gallatin 		return err;;
3189b2fc195eSAndrew Gallatin 
31901e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3191b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3192b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3193b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3194b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3195b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3196b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3197b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3198b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3199b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3200b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3201b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32021e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3203b2fc195eSAndrew Gallatin 	if (err != 0) {
3204b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3205b2fc195eSAndrew Gallatin 			      err);
32061e413cf9SAndrew Gallatin 		return err;;
3207b2fc195eSAndrew Gallatin 	}
3208b2fc195eSAndrew Gallatin 
3209b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3210b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3211b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3212b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3213b0f7b922SAndrew Gallatin #else
3214b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3215b0f7b922SAndrew Gallatin #endif
3216b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3217b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3218b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3219053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3220b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3221053e637fSAndrew Gallatin 				 3,			/* num segs */
3222b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3223b0f7b922SAndrew Gallatin #else
3224b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3225b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3226b0f7b922SAndrew Gallatin #endif
3227b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3228b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32291e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3230b2fc195eSAndrew Gallatin 	if (err != 0) {
3231b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3232b2fc195eSAndrew Gallatin 			      err);
32331e413cf9SAndrew Gallatin 		return err;;
3234b2fc195eSAndrew Gallatin 	}
32351e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
32361e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
32371e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3238b2fc195eSAndrew Gallatin 		if (err != 0) {
3239b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3240b2fc195eSAndrew Gallatin 				      err);
32411e413cf9SAndrew Gallatin 			return err;;
3242b2fc195eSAndrew Gallatin 		}
3243b2fc195eSAndrew Gallatin 	}
32441e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
32451e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3246b2fc195eSAndrew Gallatin 	if (err != 0) {
3247b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3248b2fc195eSAndrew Gallatin 			      err);
32491e413cf9SAndrew Gallatin 		return err;;
3250b2fc195eSAndrew Gallatin 	}
3251b2fc195eSAndrew Gallatin 
32521e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32531e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
32541e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3255b2fc195eSAndrew Gallatin 		if (err != 0) {
3256b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3257b2fc195eSAndrew Gallatin 				      err);
32581e413cf9SAndrew Gallatin 			return err;;
3259b2fc195eSAndrew Gallatin 		}
3260b2fc195eSAndrew Gallatin 	}
32611e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
32621e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3263b2fc195eSAndrew Gallatin 	if (err != 0) {
3264b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3265b2fc195eSAndrew Gallatin 			      err);
32661e413cf9SAndrew Gallatin 		return err;;
32671e413cf9SAndrew Gallatin 	}
32681e413cf9SAndrew Gallatin 
32691e413cf9SAndrew Gallatin 	/* now allocate TX resouces */
32701e413cf9SAndrew Gallatin 
3271c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
32721e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
32731e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
32741e413cf9SAndrew Gallatin 		return 0;
3275c6cb3e3fSAndrew Gallatin #endif
32761e413cf9SAndrew Gallatin 
32771e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
32781e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
32791e413cf9SAndrew Gallatin 
32801e413cf9SAndrew Gallatin 
32811e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
32821e413cf9SAndrew Gallatin 	bytes = 8 +
32831e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
32841e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
32851e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes == NULL)
32861e413cf9SAndrew Gallatin 		return err;;
32871e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
32881e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
32891e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
32901e413cf9SAndrew Gallatin 
32911e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
32921e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
32931e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
32941e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
32951e413cf9SAndrew Gallatin 	if (ss->tx.seg_list == NULL)
32961e413cf9SAndrew Gallatin 		return err;;
32971e413cf9SAndrew Gallatin 
32981e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
32991e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
33001e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
33011e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
33021e413cf9SAndrew Gallatin 		return err;;
33031e413cf9SAndrew Gallatin 
33041e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
33051e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
33061e413cf9SAndrew Gallatin 				 1,			/* alignment */
33071e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
33081e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
33091e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
33101e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
33111e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
33121e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
33131e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
33141e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
33151e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
33161e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
33171e413cf9SAndrew Gallatin 
33181e413cf9SAndrew Gallatin 	if (err != 0) {
33191e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
33201e413cf9SAndrew Gallatin 			      err);
33211e413cf9SAndrew Gallatin 		return err;;
33221e413cf9SAndrew Gallatin 	}
33231e413cf9SAndrew Gallatin 
33241e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
33251e413cf9SAndrew Gallatin 	   in the ring */
33261e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
33271e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
33281e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
33291e413cf9SAndrew Gallatin 		if (err != 0) {
33301e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
33311e413cf9SAndrew Gallatin 				      err);
33321e413cf9SAndrew Gallatin 			return err;;
33331e413cf9SAndrew Gallatin 		}
3334b2fc195eSAndrew Gallatin 	}
3335b2fc195eSAndrew Gallatin 	return 0;
3336b2fc195eSAndrew Gallatin 
3337b2fc195eSAndrew Gallatin }
3338b2fc195eSAndrew Gallatin 
33391e413cf9SAndrew Gallatin static int
33401e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
33411e413cf9SAndrew Gallatin {
33421e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
33431e413cf9SAndrew Gallatin 	int tx_ring_size;
33441e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
33451e413cf9SAndrew Gallatin 	int err, slice;
33461e413cf9SAndrew Gallatin 
33471e413cf9SAndrew Gallatin 	/* get ring sizes */
33481e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33491e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
33501e413cf9SAndrew Gallatin 	if (err != 0) {
33511e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33521e413cf9SAndrew Gallatin 		goto abort;
33531e413cf9SAndrew Gallatin 	}
33541e413cf9SAndrew Gallatin 
33551e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
33561e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
33571e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
33581e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
33591e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
33601e413cf9SAndrew Gallatin 
33611e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33621e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
33631e413cf9SAndrew Gallatin 					     rx_ring_entries,
33641e413cf9SAndrew Gallatin 					     tx_ring_entries);
33651e413cf9SAndrew Gallatin 		if (err != 0)
33661e413cf9SAndrew Gallatin 			goto abort;
33671e413cf9SAndrew Gallatin 	}
33681e413cf9SAndrew Gallatin 	return 0;
33691e413cf9SAndrew Gallatin 
33701e413cf9SAndrew Gallatin abort:
33711e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
33721e413cf9SAndrew Gallatin 	return err;
33731e413cf9SAndrew Gallatin 
33741e413cf9SAndrew Gallatin }
33751e413cf9SAndrew Gallatin 
33761e413cf9SAndrew Gallatin 
3377053e637fSAndrew Gallatin static void
3378053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3379053e637fSAndrew Gallatin {
3380c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3381053e637fSAndrew Gallatin 
3382053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3383053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3384053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3385053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3386053e637fSAndrew Gallatin 		*nbufs = 1;
3387053e637fSAndrew Gallatin 		return;
3388053e637fSAndrew Gallatin 	}
3389053e637fSAndrew Gallatin 
3390053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3391053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3392053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3393053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3394053e637fSAndrew Gallatin 		*nbufs = 1;
3395053e637fSAndrew Gallatin 		return;
3396053e637fSAndrew Gallatin 	}
3397b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3398053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3399053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3400053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3401053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3402053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3403053e637fSAndrew Gallatin 	if (*nbufs == 3)
3404053e637fSAndrew Gallatin 		*nbufs = 4;
3405b0f7b922SAndrew Gallatin #else
3406b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3407b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3408b0f7b922SAndrew Gallatin 	*nbufs = 1;
3409b0f7b922SAndrew Gallatin #endif
3410053e637fSAndrew Gallatin }
3411053e637fSAndrew Gallatin 
3412b2fc195eSAndrew Gallatin static int
34131e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3414b2fc195eSAndrew Gallatin {
34151e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
34166d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3417b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
3418053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
34191e413cf9SAndrew Gallatin 	int err, i, slice;
3420b2fc195eSAndrew Gallatin 
34211e413cf9SAndrew Gallatin 
34221e413cf9SAndrew Gallatin 	sc = ss->sc;
34231e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
34241e413cf9SAndrew Gallatin 
34251e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_free);
34261e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_active);
3427053e637fSAndrew Gallatin 
3428053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
3429053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
34301e413cf9SAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF,
34311e413cf9SAndrew Gallatin 			       M_NOWAIT | M_ZERO);
3432053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
3433053e637fSAndrew Gallatin 			sc->lro_cnt = i;
3434053e637fSAndrew Gallatin 			break;
3435053e637fSAndrew Gallatin 		}
34361e413cf9SAndrew Gallatin 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
3437053e637fSAndrew Gallatin 	}
34381e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
34391e413cf9SAndrew Gallatin 
34401e413cf9SAndrew Gallatin 	err = 0;
3441c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
34421e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
34431e413cf9SAndrew Gallatin 	if (slice == 0) {
3444c6cb3e3fSAndrew Gallatin #endif
34451e413cf9SAndrew Gallatin 		cmd.data0 = slice;
34461e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
34471e413cf9SAndrew Gallatin 		ss->tx.lanai =
34481e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3449c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3450c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3451c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3452c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3453c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
34541e413cf9SAndrew Gallatin 	}
3455c6cb3e3fSAndrew Gallatin #endif
34561e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34571e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
34581e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
34591e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
34601e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34611e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34621e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
34631e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
34641e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34651e413cf9SAndrew Gallatin 
34661e413cf9SAndrew Gallatin 	if (err != 0) {
34671e413cf9SAndrew Gallatin 		device_printf(sc->dev,
34681e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
34691e413cf9SAndrew Gallatin 		return EIO;
34701e413cf9SAndrew Gallatin 	}
34711e413cf9SAndrew Gallatin 
34721e413cf9SAndrew Gallatin 	/* stock receive rings */
34731e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
34741e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
34751e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
34761e413cf9SAndrew Gallatin 		if (err) {
34771e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
34781e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
34791e413cf9SAndrew Gallatin 			return ENOMEM;
34801e413cf9SAndrew Gallatin 		}
34811e413cf9SAndrew Gallatin 	}
34821e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
34831e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
34841e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
34851e413cf9SAndrew Gallatin 	}
34861e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
34871e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
34884d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
34894d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
34901e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
34911e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
34921e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
34931e413cf9SAndrew Gallatin 		if (err) {
34941e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
34951e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
34961e413cf9SAndrew Gallatin 			return ENOMEM;
34971e413cf9SAndrew Gallatin 		}
34981e413cf9SAndrew Gallatin 	}
34991e413cf9SAndrew Gallatin 	return 0;
35001e413cf9SAndrew Gallatin }
35011e413cf9SAndrew Gallatin 
35021e413cf9SAndrew Gallatin static int
35031e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
35041e413cf9SAndrew Gallatin {
35051e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
35061e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
35071e413cf9SAndrew Gallatin 	bus_addr_t bus;
35081e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3509c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3510b2fc195eSAndrew Gallatin 
35117d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
35127d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
35137d542e2dSAndrew Gallatin 
3514adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3515b2fc195eSAndrew Gallatin 	if (err != 0) {
3516b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3517b2fc195eSAndrew Gallatin 		return EIO;
3518b2fc195eSAndrew Gallatin 	}
3519b2fc195eSAndrew Gallatin 
35201e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
35211e413cf9SAndrew Gallatin 		/* setup the indirection table */
35221e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
35231e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
35241e413cf9SAndrew Gallatin 				    &cmd);
3525b2fc195eSAndrew Gallatin 
35261e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
35271e413cf9SAndrew Gallatin 				     &cmd);
35281e413cf9SAndrew Gallatin 		if (err != 0) {
35291e413cf9SAndrew Gallatin 			device_printf(sc->dev,
35301e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
35311e413cf9SAndrew Gallatin 			return err;
35321e413cf9SAndrew Gallatin 		}
35331e413cf9SAndrew Gallatin 
35341e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
35351e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
35361e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
35371e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
35381e413cf9SAndrew Gallatin 
35391e413cf9SAndrew Gallatin 		cmd.data0 = 1;
35401e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
35411e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
35421e413cf9SAndrew Gallatin 		if (err != 0) {
35431e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
35441e413cf9SAndrew Gallatin 			return err;
35451e413cf9SAndrew Gallatin 		}
35461e413cf9SAndrew Gallatin 	}
35471e413cf9SAndrew Gallatin 
35481e413cf9SAndrew Gallatin 
35491e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
35501e413cf9SAndrew Gallatin 
35511e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3552053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3553053e637fSAndrew Gallatin 			    &cmd);
3554053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3555053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
35561e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3557053e637fSAndrew Gallatin 		device_printf(sc->dev,
3558053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
35591e413cf9SAndrew Gallatin 			      nbufs);
3560053e637fSAndrew Gallatin 		return EIO;
3561053e637fSAndrew Gallatin 	}
3562b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3563b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3564b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3565c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
35665e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3567b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
35685e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3569b2fc195eSAndrew Gallatin 			     &cmd);
3570053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
35715e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
35720fa7f681SAndrew Gallatin 
35730fa7f681SAndrew Gallatin 	if (err != 0) {
35740fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
35750fa7f681SAndrew Gallatin 		goto abort;
35760fa7f681SAndrew Gallatin 	}
35770fa7f681SAndrew Gallatin 
3578b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3579c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3580c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3581c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3582c6cb3e3fSAndrew Gallatin #else
3583c6cb3e3fSAndrew Gallatin 	     slice < 1;
3584c6cb3e3fSAndrew Gallatin #endif
3585c6cb3e3fSAndrew Gallatin 	     slice++) {
3586c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3587c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3588c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3589c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3590c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
35910fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3592c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3593c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3594c6cb3e3fSAndrew Gallatin 	}
35950fa7f681SAndrew Gallatin 
35960fa7f681SAndrew Gallatin 	if (err != 0) {
35971e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
35980fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
35990fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
36000fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
36010fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
36020fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
36030fa7f681SAndrew Gallatin 				    &cmd);
36040fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
36050fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
36060fa7f681SAndrew Gallatin 	} else {
36070fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
36080fa7f681SAndrew Gallatin 	}
3609b2fc195eSAndrew Gallatin 
3610b2fc195eSAndrew Gallatin 	if (err != 0) {
3611b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3612b2fc195eSAndrew Gallatin 		goto abort;
3613b2fc195eSAndrew Gallatin 	}
3614b2fc195eSAndrew Gallatin 
36151e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
36161e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
36171e413cf9SAndrew Gallatin 		if (err != 0) {
36181e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
36191e413cf9SAndrew Gallatin 				      slice);
36201e413cf9SAndrew Gallatin 			goto abort;
36211e413cf9SAndrew Gallatin 		}
36221e413cf9SAndrew Gallatin 	}
36231e413cf9SAndrew Gallatin 
3624b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
36255e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3626b2fc195eSAndrew Gallatin 	if (err) {
3627b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3628b2fc195eSAndrew Gallatin 		goto abort;
3629b2fc195eSAndrew Gallatin 	}
3630c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3631c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3632c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3633c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3634c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3635c6cb3e3fSAndrew Gallatin 	}
3636c6cb3e3fSAndrew Gallatin #endif
3637b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3638b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3639e749ef6bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3640b2fc195eSAndrew Gallatin 
3641b2fc195eSAndrew Gallatin 	return 0;
3642b2fc195eSAndrew Gallatin 
3643b2fc195eSAndrew Gallatin 
3644b2fc195eSAndrew Gallatin abort:
36456d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3646a98d6cd7SAndrew Gallatin 
3647b2fc195eSAndrew Gallatin 	return err;
3648b2fc195eSAndrew Gallatin }
3649b2fc195eSAndrew Gallatin 
3650b2fc195eSAndrew Gallatin static int
36516d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
3652b2fc195eSAndrew Gallatin {
36536d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3654b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3655c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3656c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3657c6cb3e3fSAndrew Gallatin 	int slice;
3658c6cb3e3fSAndrew Gallatin #endif
3659b2fc195eSAndrew Gallatin 
3660e749ef6bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3661c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3662c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3663c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3664c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3665c6cb3e3fSAndrew Gallatin 	}
3666c6cb3e3fSAndrew Gallatin #endif
3667b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3668b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
366973c7c83fSAndrew Gallatin 	wmb();
36705e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3671b2fc195eSAndrew Gallatin 	if (err) {
3672b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
3673b2fc195eSAndrew Gallatin 	}
3674b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3675b2fc195eSAndrew Gallatin 		/* wait for down irq */
3676dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
3677b2fc195eSAndrew Gallatin 	}
367873c7c83fSAndrew Gallatin 	wmb();
3679b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3680b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
3681b2fc195eSAndrew Gallatin 	}
3682a98d6cd7SAndrew Gallatin 
36836d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3684a98d6cd7SAndrew Gallatin 
3685b2fc195eSAndrew Gallatin 	return 0;
3686b2fc195eSAndrew Gallatin }
3687b2fc195eSAndrew Gallatin 
3688dce01b9bSAndrew Gallatin static void
3689dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3690dce01b9bSAndrew Gallatin {
3691dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3692dce01b9bSAndrew Gallatin 	int reg;
3693dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
3694dce01b9bSAndrew Gallatin 
3695dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
3696dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
3697dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3698dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3699dce01b9bSAndrew Gallatin 
3700dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
3701dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
3702dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
3703dce01b9bSAndrew Gallatin 	}
3704dce01b9bSAndrew Gallatin 
3705dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3706dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3707dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
3708dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
3709dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
3710dce01b9bSAndrew Gallatin }
3711dce01b9bSAndrew Gallatin 
3712dce01b9bSAndrew Gallatin static uint32_t
3713dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3714dce01b9bSAndrew Gallatin {
3715dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3716dce01b9bSAndrew Gallatin 	uint32_t vs;
3717dce01b9bSAndrew Gallatin 
3718dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
3719dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
3720dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3721dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3722dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3723dce01b9bSAndrew Gallatin 	}
3724dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3725dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3726dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3727dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3728dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3729dce01b9bSAndrew Gallatin }
3730dce01b9bSAndrew Gallatin 
3731e749ef6bSAndrew Gallatin static int
3732c6cb3e3fSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc, int slice)
3733dce01b9bSAndrew Gallatin {
3734e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3735c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
3736dce01b9bSAndrew Gallatin 	int err;
3737dce01b9bSAndrew Gallatin 	uint32_t reboot;
3738dce01b9bSAndrew Gallatin 	uint16_t cmd;
3739dce01b9bSAndrew Gallatin 
3740dce01b9bSAndrew Gallatin 	err = ENXIO;
3741dce01b9bSAndrew Gallatin 
3742dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3743dce01b9bSAndrew Gallatin 
3744dce01b9bSAndrew Gallatin 	/*
3745dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3746dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3747dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3748dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3749dce01b9bSAndrew Gallatin 	 * again
3750dce01b9bSAndrew Gallatin 	 */
3751dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3752dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3753dce01b9bSAndrew Gallatin 		/*
3754dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3755dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3756dce01b9bSAndrew Gallatin 		 * back, then give up
3757dce01b9bSAndrew Gallatin 		 */
3758dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3759dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3760dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3761dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3762e749ef6bSAndrew Gallatin 			return (err);
3763dce01b9bSAndrew Gallatin 		}
3764dce01b9bSAndrew Gallatin 	}
3765dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3766dce01b9bSAndrew Gallatin 		/* print the reboot status */
3767dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3768dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3769dce01b9bSAndrew Gallatin 			      reboot);
3770dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3771e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3772e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3773dce01b9bSAndrew Gallatin 
3774dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3775dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
377610882804SAndrew Gallatin 
377710882804SAndrew Gallatin 		if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
377810882804SAndrew Gallatin 			mxge_close(sc);
377910882804SAndrew Gallatin 			err = mxge_open(sc);
378010882804SAndrew Gallatin 		}
3781dce01b9bSAndrew Gallatin 	} else {
3782c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
3783c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
3784c6cb3e3fSAndrew Gallatin 			      "NIC did not reboot, slice %d ring state:\n",
3785c6cb3e3fSAndrew Gallatin 			      slice);
3786c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
3787c6cb3e3fSAndrew Gallatin 			      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3788c6cb3e3fSAndrew Gallatin 			      tx->req, tx->done, tx->queue_active);
3789c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3790c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3791dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3792c6cb3e3fSAndrew Gallatin 			      tx->pkt_done,
37931e413cf9SAndrew Gallatin 			      be32toh(sc->ss->fw_stats->send_done_count));
379410882804SAndrew Gallatin 		device_printf(sc->dev, "not resetting\n");
3795dce01b9bSAndrew Gallatin 	}
3796e749ef6bSAndrew Gallatin 	return (err);
3797dce01b9bSAndrew Gallatin }
3798dce01b9bSAndrew Gallatin 
3799e749ef6bSAndrew Gallatin static int
3800dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3801dce01b9bSAndrew Gallatin {
3802c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
38031e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3804c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3805dce01b9bSAndrew Gallatin 
3806dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3807dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3808c6cb3e3fSAndrew Gallatin 	for (i = 0;
3809c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3810c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
3811c6cb3e3fSAndrew Gallatin #else
3812c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
3813c6cb3e3fSAndrew Gallatin #endif
3814c6cb3e3fSAndrew Gallatin 	     i++) {
3815c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3816dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3817dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3818c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3819c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
3820c587e59fSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause)
3821c6cb3e3fSAndrew Gallatin 				err = mxge_watchdog_reset(sc, i);
3822c587e59fSAndrew Gallatin 			else
3823c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3824c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3825c587e59fSAndrew Gallatin 		}
3826dce01b9bSAndrew Gallatin 
3827dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3828dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3829c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3830c6cb3e3fSAndrew Gallatin 	}
3831c587e59fSAndrew Gallatin 
3832c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3833c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3834e749ef6bSAndrew Gallatin 	return (err);
3835dce01b9bSAndrew Gallatin }
3836dce01b9bSAndrew Gallatin 
3837dce01b9bSAndrew Gallatin static void
38381e413cf9SAndrew Gallatin mxge_update_stats(mxge_softc_t *sc)
38391e413cf9SAndrew Gallatin {
38401e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
38411e413cf9SAndrew Gallatin 	u_long ipackets = 0;
3842c6cb3e3fSAndrew Gallatin 	u_long opackets = 0;
384371032832SAndrew Gallatin #ifdef IFNET_BUF_RING
384471032832SAndrew Gallatin 	u_long obytes = 0;
384571032832SAndrew Gallatin 	u_long omcasts = 0;
384671032832SAndrew Gallatin 	u_long odrops = 0;
384771032832SAndrew Gallatin #endif
3848c6cb3e3fSAndrew Gallatin 	u_long oerrors = 0;
38491e413cf9SAndrew Gallatin 	int slice;
38501e413cf9SAndrew Gallatin 
38511e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
38521e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
38531e413cf9SAndrew Gallatin 		ipackets += ss->ipackets;
3854c6cb3e3fSAndrew Gallatin 		opackets += ss->opackets;
385571032832SAndrew Gallatin #ifdef IFNET_BUF_RING
385671032832SAndrew Gallatin 		obytes += ss->obytes;
385771032832SAndrew Gallatin 		omcasts += ss->omcasts;
385871032832SAndrew Gallatin 		odrops += ss->tx.br->br_drops;
385971032832SAndrew Gallatin #endif
3860c6cb3e3fSAndrew Gallatin 		oerrors += ss->oerrors;
38611e413cf9SAndrew Gallatin 	}
38621e413cf9SAndrew Gallatin 	sc->ifp->if_ipackets = ipackets;
3863c6cb3e3fSAndrew Gallatin 	sc->ifp->if_opackets = opackets;
386471032832SAndrew Gallatin #ifdef IFNET_BUF_RING
386571032832SAndrew Gallatin 	sc->ifp->if_obytes = obytes;
386671032832SAndrew Gallatin 	sc->ifp->if_omcasts = omcasts;
386771032832SAndrew Gallatin 	sc->ifp->if_snd.ifq_drops = odrops;
386871032832SAndrew Gallatin #endif
3869c6cb3e3fSAndrew Gallatin 	sc->ifp->if_oerrors = oerrors;
38701e413cf9SAndrew Gallatin }
3871c6cb3e3fSAndrew Gallatin 
38721e413cf9SAndrew Gallatin static void
3873dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3874dce01b9bSAndrew Gallatin {
3875dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
3876e749ef6bSAndrew Gallatin 	int err = 0;
3877dce01b9bSAndrew Gallatin 
38781e413cf9SAndrew Gallatin 	/* aggregate stats from different slices */
38791e413cf9SAndrew Gallatin 	mxge_update_stats(sc);
38801e413cf9SAndrew Gallatin 	if (!sc->watchdog_countdown) {
3881e749ef6bSAndrew Gallatin 		err = mxge_watchdog(sc);
38821e413cf9SAndrew Gallatin 		sc->watchdog_countdown = 4;
38831e413cf9SAndrew Gallatin 	}
38841e413cf9SAndrew Gallatin 	sc->watchdog_countdown--;
3885e749ef6bSAndrew Gallatin 	if (err == 0)
3886e749ef6bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3887e749ef6bSAndrew Gallatin 
3888dce01b9bSAndrew Gallatin }
3889b2fc195eSAndrew Gallatin 
3890b2fc195eSAndrew Gallatin static int
38916d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
3892b2fc195eSAndrew Gallatin {
3893b2fc195eSAndrew Gallatin 	return EINVAL;
3894b2fc195eSAndrew Gallatin }
3895b2fc195eSAndrew Gallatin 
3896b2fc195eSAndrew Gallatin static int
38976d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3898b2fc195eSAndrew Gallatin {
3899b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
3900b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3901b2fc195eSAndrew Gallatin 	int err = 0;
3902b2fc195eSAndrew Gallatin 
3903b2fc195eSAndrew Gallatin 
3904c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3905053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3906b2fc195eSAndrew Gallatin 		return EINVAL;
3907a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3908b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
3909b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
3910b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
39116d87a65dSAndrew Gallatin 		mxge_close(sc);
39126d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3913b2fc195eSAndrew Gallatin 		if (err != 0) {
3914b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
39156d87a65dSAndrew Gallatin 			mxge_close(sc);
39166d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3917b2fc195eSAndrew Gallatin 		}
3918b2fc195eSAndrew Gallatin 	}
3919a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3920b2fc195eSAndrew Gallatin 	return err;
3921b2fc195eSAndrew Gallatin }
3922b2fc195eSAndrew Gallatin 
3923b2fc195eSAndrew Gallatin static void
39246d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
3925b2fc195eSAndrew Gallatin {
39266d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3927b2fc195eSAndrew Gallatin 
3928b2fc195eSAndrew Gallatin 
3929b2fc195eSAndrew Gallatin 	if (sc == NULL)
3930b2fc195eSAndrew Gallatin 		return;
3931b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3932c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3933b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
3934c587e59fSAndrew Gallatin 	ifmr->ifm_active |= sc->link_state ? IFM_FDX : 0;
3935b2fc195eSAndrew Gallatin }
3936b2fc195eSAndrew Gallatin 
3937b2fc195eSAndrew Gallatin static int
39386d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
3939b2fc195eSAndrew Gallatin {
39406d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3941b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
3942b2fc195eSAndrew Gallatin 	int err, mask;
3943b2fc195eSAndrew Gallatin 
3944b2fc195eSAndrew Gallatin 	err = 0;
3945b2fc195eSAndrew Gallatin 	switch (command) {
3946b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
3947b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
3948b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
3949b2fc195eSAndrew Gallatin 		break;
3950b2fc195eSAndrew Gallatin 
3951b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
39526d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
3953b2fc195eSAndrew Gallatin 		break;
3954b2fc195eSAndrew Gallatin 
3955b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
3956a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
39578c5d766cSAndrew Gallatin 		if (sc->dying) {
39588c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
39598c5d766cSAndrew Gallatin 			return EINVAL;
39608c5d766cSAndrew Gallatin 		}
3961b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
3962dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
39636d87a65dSAndrew Gallatin 				err = mxge_open(sc);
3964dce01b9bSAndrew Gallatin 			} else {
39650fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
39660fa7f681SAndrew Gallatin 				   flag chages */
39670fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
39680fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
39690fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
39700fa7f681SAndrew Gallatin 			}
3971b2fc195eSAndrew Gallatin 		} else {
3972dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
39731e413cf9SAndrew Gallatin 				mxge_close(sc);
3974dce01b9bSAndrew Gallatin 			}
3975b2fc195eSAndrew Gallatin 		}
3976a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3977b2fc195eSAndrew Gallatin 		break;
3978b2fc195eSAndrew Gallatin 
3979b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
3980b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
3981a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
39820fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
3983a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3984b2fc195eSAndrew Gallatin 		break;
3985b2fc195eSAndrew Gallatin 
3986b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
3987a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3988b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
3989b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
3990b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
3991aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
3992aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3993aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3994b2fc195eSAndrew Gallatin 			} else {
3995b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3996b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3997b2fc195eSAndrew Gallatin 			}
3998b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3999b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
4000b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
40015e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
4002b2fc195eSAndrew Gallatin 			} else {
4003b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
40045e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
4005b2fc195eSAndrew Gallatin 			}
4006b2fc195eSAndrew Gallatin 		}
4007aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
4008aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
4009aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
4010aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
4011aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
4012aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
4013aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
4014aed8e389SAndrew Gallatin 			} else {
4015aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4016aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4017aed8e389SAndrew Gallatin 				err = EINVAL;
4018aed8e389SAndrew Gallatin 			}
4019aed8e389SAndrew Gallatin 		}
4020f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
4021f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
4022f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
4023f04b33f8SAndrew Gallatin 			else
4024f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
4025f04b33f8SAndrew Gallatin 		}
4026c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
4027c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
4028a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4029c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4030c792928fSAndrew Gallatin 
4031b2fc195eSAndrew Gallatin 		break;
4032b2fc195eSAndrew Gallatin 
4033b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4034b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4035b2fc195eSAndrew Gallatin 				    &sc->media, command);
4036b2fc195eSAndrew Gallatin                 break;
4037b2fc195eSAndrew Gallatin 
4038b2fc195eSAndrew Gallatin 	default:
4039b2fc195eSAndrew Gallatin 		err = ENOTTY;
4040b2fc195eSAndrew Gallatin         }
4041b2fc195eSAndrew Gallatin 	return err;
4042b2fc195eSAndrew Gallatin }
4043b2fc195eSAndrew Gallatin 
4044b2fc195eSAndrew Gallatin static void
40456d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4046b2fc195eSAndrew Gallatin {
4047b2fc195eSAndrew Gallatin 
40481e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
40496d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
40506d87a65dSAndrew Gallatin 			  &mxge_flow_control);
40516d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
40526d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
40536d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
40546d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4055d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4056d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
40575e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
40585e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
40595e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
40605e7d8541SAndrew Gallatin 			  &mxge_verbose);
4061dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
4062053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
40631e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
40641e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
406594c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4066f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
406765c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4068f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
4069f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
4070b2fc195eSAndrew Gallatin 
40715e7d8541SAndrew Gallatin 	if (bootverbose)
40725e7d8541SAndrew Gallatin 		mxge_verbose = 1;
40736d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
40746d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4075dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
40761e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
40776d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
40781e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4079bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
40801e413cf9SAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
4081b2fc195eSAndrew Gallatin 	}
4082f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4083f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4084f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
408565c69066SAndrew Gallatin 
408665c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
408765c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
408865c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
408965c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
409065c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
40911e413cf9SAndrew Gallatin }
40921e413cf9SAndrew Gallatin 
40931e413cf9SAndrew Gallatin 
40941e413cf9SAndrew Gallatin static void
40951e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
40961e413cf9SAndrew Gallatin {
40971e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
40981e413cf9SAndrew Gallatin 	int i;
40991e413cf9SAndrew Gallatin 
41001e413cf9SAndrew Gallatin 
41011e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
41021e413cf9SAndrew Gallatin 		return;
41031e413cf9SAndrew Gallatin 
41041e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
41051e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
41061e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
41071e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
41081e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4109c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4110c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4111c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4112c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4113c6cb3e3fSAndrew Gallatin 			}
4114c6cb3e3fSAndrew Gallatin #endif
41151e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
41161e413cf9SAndrew Gallatin 		}
41171e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
41181e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
41191e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
41201e413cf9SAndrew Gallatin 		}
41211e413cf9SAndrew Gallatin 	}
41221e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
41231e413cf9SAndrew Gallatin 	sc->ss = NULL;
41241e413cf9SAndrew Gallatin }
41251e413cf9SAndrew Gallatin 
41261e413cf9SAndrew Gallatin static int
41271e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
41281e413cf9SAndrew Gallatin {
41291e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
41301e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
41311e413cf9SAndrew Gallatin 	size_t bytes;
41321e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
41331e413cf9SAndrew Gallatin 
41341e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
41351e413cf9SAndrew Gallatin 	if (err != 0) {
41361e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
41371e413cf9SAndrew Gallatin 		return err;
41381e413cf9SAndrew Gallatin 	}
41391e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
41401e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
41411e413cf9SAndrew Gallatin 
41421e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
41431e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
41441e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
41451e413cf9SAndrew Gallatin 		return (ENOMEM);
41461e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
41471e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
41481e413cf9SAndrew Gallatin 
41491e413cf9SAndrew Gallatin 		ss->sc = sc;
41501e413cf9SAndrew Gallatin 
41511e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
41521e413cf9SAndrew Gallatin 
41531e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
41541e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
41551e413cf9SAndrew Gallatin 		if (err != 0)
41561e413cf9SAndrew Gallatin 			goto abort;
41571e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
41581e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
41591e413cf9SAndrew Gallatin 
41601e413cf9SAndrew Gallatin 		/*
41611e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
41621e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
41631e413cf9SAndrew Gallatin 		 * slice for now
41641e413cf9SAndrew Gallatin 		 */
4165c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
41661e413cf9SAndrew Gallatin 		if (i > 0)
41671e413cf9SAndrew Gallatin 			continue;
4168c6cb3e3fSAndrew Gallatin #endif
41691e413cf9SAndrew Gallatin 
41701e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
41711e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
41721e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
41731e413cf9SAndrew Gallatin 		if (err != 0)
41741e413cf9SAndrew Gallatin 			goto abort;
41751e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
41761e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
41771e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
41781e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4179c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4180c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4181c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4182c6cb3e3fSAndrew Gallatin #endif
41831e413cf9SAndrew Gallatin 	}
41841e413cf9SAndrew Gallatin 
41851e413cf9SAndrew Gallatin 	return (0);
41861e413cf9SAndrew Gallatin 
41871e413cf9SAndrew Gallatin abort:
41881e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
41891e413cf9SAndrew Gallatin 	return (ENOMEM);
41901e413cf9SAndrew Gallatin }
41911e413cf9SAndrew Gallatin 
41921e413cf9SAndrew Gallatin static void
41931e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
41941e413cf9SAndrew Gallatin {
41951e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
41961e413cf9SAndrew Gallatin 	char *old_fw;
41971e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
41981e413cf9SAndrew Gallatin 
41991e413cf9SAndrew Gallatin 	sc->num_slices = 1;
42001e413cf9SAndrew Gallatin 	/*
42011e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
42021e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
42031e413cf9SAndrew Gallatin 	 */
42041e413cf9SAndrew Gallatin 
42051e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
42061e413cf9SAndrew Gallatin 		return;
42071e413cf9SAndrew Gallatin 
42081e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
42091e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
42101e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
42111e413cf9SAndrew Gallatin 		return;
42121e413cf9SAndrew Gallatin 
42131e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
42141e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
42151e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
42161e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
42171e413cf9SAndrew Gallatin 	else
42181e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
42191e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
42201e413cf9SAndrew Gallatin 	if (status != 0) {
42211e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
42221e413cf9SAndrew Gallatin 		return;
42231e413cf9SAndrew Gallatin 	}
42241e413cf9SAndrew Gallatin 
42251e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
42261e413cf9SAndrew Gallatin 	   is alive */
42271e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
42281e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
42291e413cf9SAndrew Gallatin 	if (status != 0) {
42301e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
42311e413cf9SAndrew Gallatin 		goto abort_with_fw;
42321e413cf9SAndrew Gallatin 	}
42331e413cf9SAndrew Gallatin 
42341e413cf9SAndrew Gallatin 	/* get rx ring size */
42351e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
42361e413cf9SAndrew Gallatin 	if (status != 0) {
42371e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
42381e413cf9SAndrew Gallatin 		goto abort_with_fw;
42391e413cf9SAndrew Gallatin 	}
42401e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
42411e413cf9SAndrew Gallatin 
42421e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
42431e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
42441e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
42451e413cf9SAndrew Gallatin 	if (status != 0) {
42461e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
42471e413cf9SAndrew Gallatin 		goto abort_with_fw;
42481e413cf9SAndrew Gallatin 	}
42491e413cf9SAndrew Gallatin 
42501e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
42511e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
42521e413cf9SAndrew Gallatin 	if (status != 0) {
42531e413cf9SAndrew Gallatin 		device_printf(sc->dev,
42541e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
42551e413cf9SAndrew Gallatin 		goto abort_with_fw;
42561e413cf9SAndrew Gallatin 	}
42571e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
42581e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
42591e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
42601e413cf9SAndrew Gallatin 
42611e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
42621e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
42631e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
42641e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
42651e413cf9SAndrew Gallatin 	} else {
42661e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
42671e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
42681e413cf9SAndrew Gallatin 	}
42691e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
42701e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
42711e413cf9SAndrew Gallatin 		sc->num_slices--;
42721e413cf9SAndrew Gallatin 
42731e413cf9SAndrew Gallatin 	if (mxge_verbose)
42741e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
42751e413cf9SAndrew Gallatin 			      sc->num_slices);
42761e413cf9SAndrew Gallatin 
42771e413cf9SAndrew Gallatin 	return;
42781e413cf9SAndrew Gallatin 
42791e413cf9SAndrew Gallatin abort_with_fw:
42801e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
42811e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
42821e413cf9SAndrew Gallatin }
42831e413cf9SAndrew Gallatin 
42841e413cf9SAndrew Gallatin static int
42851e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
42861e413cf9SAndrew Gallatin {
42871e413cf9SAndrew Gallatin 	size_t bytes;
42881e413cf9SAndrew Gallatin 	int count, err, i, rid;
42891e413cf9SAndrew Gallatin 
42901e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
42911e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
42921e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
42931e413cf9SAndrew Gallatin 
42941e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
42951e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
42961e413cf9SAndrew Gallatin 		return ENXIO;
42971e413cf9SAndrew Gallatin 	}
42981e413cf9SAndrew Gallatin 
42991e413cf9SAndrew Gallatin 	count = sc->num_slices;
43001e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
43011e413cf9SAndrew Gallatin 	if (err != 0) {
43021e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
43031e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
43041e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
43051e413cf9SAndrew Gallatin 	}
43061e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
43071e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
43081e413cf9SAndrew Gallatin 			      count, sc->num_slices);
43091e413cf9SAndrew Gallatin 		device_printf(sc->dev,
43101e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
43111e413cf9SAndrew Gallatin 			      count);
43121e413cf9SAndrew Gallatin 		err = ENOSPC;
43131e413cf9SAndrew Gallatin 		goto abort_with_msix;
43141e413cf9SAndrew Gallatin 	}
43151e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
43161e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
43171e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
43181e413cf9SAndrew Gallatin 		err = ENOMEM;
43191e413cf9SAndrew Gallatin 		goto abort_with_msix;
43201e413cf9SAndrew Gallatin 	}
43211e413cf9SAndrew Gallatin 
43221e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43231e413cf9SAndrew Gallatin 		rid = i + 1;
43241e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
43251e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
43261e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
43271e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
43281e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
43291e413cf9SAndrew Gallatin 				      " for message %d\n", i);
43301e413cf9SAndrew Gallatin 			err = ENXIO;
43311e413cf9SAndrew Gallatin 			goto abort_with_res;
43321e413cf9SAndrew Gallatin 		}
43331e413cf9SAndrew Gallatin 	}
43341e413cf9SAndrew Gallatin 
43351e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
43361e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
43371e413cf9SAndrew Gallatin 
43381e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43391e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
43401e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
434137d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
434237d89b0cSAndrew Gallatin 				     NULL,
434337d89b0cSAndrew Gallatin #endif
434437d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
43451e413cf9SAndrew Gallatin 		if (err != 0) {
43461e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
43471e413cf9SAndrew Gallatin 				      "message %d\n", i);
43481e413cf9SAndrew Gallatin 			goto abort_with_intr;
43491e413cf9SAndrew Gallatin 		}
43501e413cf9SAndrew Gallatin 	}
43511e413cf9SAndrew Gallatin 
43521e413cf9SAndrew Gallatin 	if (mxge_verbose) {
43531e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
43541e413cf9SAndrew Gallatin 			      sc->num_slices);
43551e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
43561e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
43571e413cf9SAndrew Gallatin 		printf("\n");
43581e413cf9SAndrew Gallatin 	}
43591e413cf9SAndrew Gallatin 	return (0);
43601e413cf9SAndrew Gallatin 
43611e413cf9SAndrew Gallatin abort_with_intr:
43621e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43631e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
43641e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
43651e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
43661e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
43671e413cf9SAndrew Gallatin 		}
43681e413cf9SAndrew Gallatin 	}
43691e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
43701e413cf9SAndrew Gallatin 
43711e413cf9SAndrew Gallatin 
43721e413cf9SAndrew Gallatin abort_with_res:
43731e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43741e413cf9SAndrew Gallatin 		rid = i + 1;
43751e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
43761e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
43771e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
43781e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
43791e413cf9SAndrew Gallatin 	}
43801e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
43811e413cf9SAndrew Gallatin 
43821e413cf9SAndrew Gallatin 
43831e413cf9SAndrew Gallatin abort_with_msix:
43841e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
43851e413cf9SAndrew Gallatin 
43861e413cf9SAndrew Gallatin abort_with_msix_table:
43871e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
43881e413cf9SAndrew Gallatin 			     sc->msix_table_res);
43891e413cf9SAndrew Gallatin 
43901e413cf9SAndrew Gallatin 	return err;
43911e413cf9SAndrew Gallatin }
43921e413cf9SAndrew Gallatin 
43931e413cf9SAndrew Gallatin static int
43941e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
43951e413cf9SAndrew Gallatin {
43961e413cf9SAndrew Gallatin 	int count, err, rid;
43971e413cf9SAndrew Gallatin 
43981e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
43991e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
44001e413cf9SAndrew Gallatin 		rid = 1;
44011e413cf9SAndrew Gallatin 	} else {
44021e413cf9SAndrew Gallatin 		rid = 0;
440391ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
44041e413cf9SAndrew Gallatin 	}
44051e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
44061e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
44071e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
44081e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
44091e413cf9SAndrew Gallatin 		return ENXIO;
44101e413cf9SAndrew Gallatin 	}
44111e413cf9SAndrew Gallatin 	if (mxge_verbose)
44121e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
441391ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
44141e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
44151e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
44161e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
441737d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
441837d89b0cSAndrew Gallatin 			     NULL,
441937d89b0cSAndrew Gallatin #endif
442037d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
44211e413cf9SAndrew Gallatin 	if (err != 0) {
44221e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
442391ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
442491ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
44251e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
44261e413cf9SAndrew Gallatin 	}
44271e413cf9SAndrew Gallatin 	return err;
44281e413cf9SAndrew Gallatin }
44291e413cf9SAndrew Gallatin 
44301e413cf9SAndrew Gallatin static void
44311e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
44321e413cf9SAndrew Gallatin {
44331e413cf9SAndrew Gallatin 	int i, rid;
44341e413cf9SAndrew Gallatin 
44351e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44361e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
44371e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
44381e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
44391e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
44401e413cf9SAndrew Gallatin 		}
44411e413cf9SAndrew Gallatin 	}
44421e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
44431e413cf9SAndrew Gallatin 
44441e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44451e413cf9SAndrew Gallatin 		rid = i + 1;
44461e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
44471e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
44481e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
44491e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
44501e413cf9SAndrew Gallatin 	}
44511e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
44521e413cf9SAndrew Gallatin 
44531e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
44541e413cf9SAndrew Gallatin 			     sc->msix_table_res);
44551e413cf9SAndrew Gallatin 
44561e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
44571e413cf9SAndrew Gallatin 	return;
44581e413cf9SAndrew Gallatin }
44591e413cf9SAndrew Gallatin 
44601e413cf9SAndrew Gallatin static void
44611e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
44621e413cf9SAndrew Gallatin {
44631e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
44641e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
446591ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
446691ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
44671e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
44681e413cf9SAndrew Gallatin }
44691e413cf9SAndrew Gallatin 
44701e413cf9SAndrew Gallatin static void
44711e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
44721e413cf9SAndrew Gallatin {
44731e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
44741e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
44751e413cf9SAndrew Gallatin 	else
44761e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
44771e413cf9SAndrew Gallatin }
44781e413cf9SAndrew Gallatin 
44791e413cf9SAndrew Gallatin static int
44801e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
44811e413cf9SAndrew Gallatin {
44821e413cf9SAndrew Gallatin 	int err;
44831e413cf9SAndrew Gallatin 
44841e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
44851e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
44861e413cf9SAndrew Gallatin 	else
44871e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
44881e413cf9SAndrew Gallatin 
44891e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
44901e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
44911e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
44921e413cf9SAndrew Gallatin 	}
44931e413cf9SAndrew Gallatin 	return err;
44941e413cf9SAndrew Gallatin }
44951e413cf9SAndrew Gallatin 
4496b2fc195eSAndrew Gallatin 
4497b2fc195eSAndrew Gallatin static int
44986d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4499b2fc195eSAndrew Gallatin {
45006d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4501b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
45021e413cf9SAndrew Gallatin 	int err, rid;
4503b2fc195eSAndrew Gallatin 
4504b2fc195eSAndrew Gallatin 	sc->dev = dev;
45056d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4506b2fc195eSAndrew Gallatin 
4507b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
4508b2fc195eSAndrew Gallatin 				 1,			/* alignment */
45091e413cf9SAndrew Gallatin 				 0,			/* boundary */
4510b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4511b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4512b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4513aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
45145e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
45151e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4516b2fc195eSAndrew Gallatin 				 0,			/* flags */
4517b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4518b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4519b2fc195eSAndrew Gallatin 
4520b2fc195eSAndrew Gallatin 	if (err != 0) {
4521b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4522b2fc195eSAndrew Gallatin 			      err);
4523b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
4524b2fc195eSAndrew Gallatin 	}
4525b2fc195eSAndrew Gallatin 
4526b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4527b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4528b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4529b2fc195eSAndrew Gallatin 		err = ENOSPC;
4530b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4531b2fc195eSAndrew Gallatin 	}
45321e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
45331e413cf9SAndrew Gallatin 
4534a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4535a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4536a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4537a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4538a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4539a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4540b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4541b2fc195eSAndrew Gallatin 
4542dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4543d91b1b49SAndrew Gallatin 
4544dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4545b2fc195eSAndrew Gallatin 
4546b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4547b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4548b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4549b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4550b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4551b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4552b2fc195eSAndrew Gallatin 		err = ENXIO;
4553b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4554b2fc195eSAndrew Gallatin 	}
4555b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4556b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4557b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4558b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4559b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4560b2fc195eSAndrew Gallatin 		err = ENXIO;
4561b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4562b2fc195eSAndrew Gallatin 	}
4563b2fc195eSAndrew Gallatin 
4564b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4565b2fc195eSAndrew Gallatin 	   lanai SRAM */
45666d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4567b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4568b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
45696d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4570b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
45716d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
45726d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4573b2fc195eSAndrew Gallatin 	if (err != 0)
4574b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4575b2fc195eSAndrew Gallatin 
4576b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
45776d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4578b2fc195eSAndrew Gallatin 
4579b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
45806d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
45816d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4582b2fc195eSAndrew Gallatin 	if (err != 0)
4583b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4584b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
45856d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4586b2fc195eSAndrew Gallatin 	if (err != 0)
4587b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4588b2fc195eSAndrew Gallatin 
4589a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4590a98d6cd7SAndrew Gallatin 	if (err != 0)
45911e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4592b2fc195eSAndrew Gallatin 
45938fe615baSAndrew Gallatin 	/* select & load the firmware */
45948fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4595b2fc195eSAndrew Gallatin 	if (err != 0)
45961e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
45975e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
45981e413cf9SAndrew Gallatin 
45991e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
46001e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
46011e413cf9SAndrew Gallatin 	if (err != 0)
46021e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
46031e413cf9SAndrew Gallatin 
4604adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4605b2fc195eSAndrew Gallatin 	if (err != 0)
46061e413cf9SAndrew Gallatin 		goto abort_with_slices;
4607b2fc195eSAndrew Gallatin 
4608a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4609a98d6cd7SAndrew Gallatin 	if (err != 0) {
4610a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
46111e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
4612a98d6cd7SAndrew Gallatin 	}
4613a98d6cd7SAndrew Gallatin 
46141e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4615a98d6cd7SAndrew Gallatin 	if (err != 0) {
46161e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4617a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4618a98d6cd7SAndrew Gallatin 	}
46191e413cf9SAndrew Gallatin 
4620e5062938SAndrew Gallatin 	ifp->if_baudrate = IF_Gbps(10UL);
4621c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
4622eb6219e3SAndrew Gallatin 		IFCAP_VLAN_MTU;
4623eb6219e3SAndrew Gallatin #ifdef INET
4624eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4625eb6219e3SAndrew Gallatin #endif
462637d89b0cSAndrew Gallatin 
462737d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
462837d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
462937d89b0cSAndrew Gallatin #endif
4630c792928fSAndrew Gallatin 
4631053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4632053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4633053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4634053e637fSAndrew Gallatin 	else
4635053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4636adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4637053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4638aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
4639b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4640f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4641f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
46425e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
46436d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
4644b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
4645b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
46466d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
46476d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
4648c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4649c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4650c587e59fSAndrew Gallatin 		     mxge_media_status);
4651c587e59fSAndrew Gallatin 	mxge_set_media(sc, IFM_ETHER | IFM_AUTO);
4652c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
46538c5d766cSAndrew Gallatin 	sc->dying = 0;
4654b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4655f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4656f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4657f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4658b2fc195eSAndrew Gallatin 
46596d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4660c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4661c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4662c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4663c6cb3e3fSAndrew Gallatin #endif
4664b2fc195eSAndrew Gallatin 	return 0;
4665b2fc195eSAndrew Gallatin 
4666a98d6cd7SAndrew Gallatin abort_with_rings:
4667a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
46681e413cf9SAndrew Gallatin abort_with_slices:
46691e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4670a98d6cd7SAndrew Gallatin abort_with_dmabench:
4671a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4672b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
46736d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4674b2fc195eSAndrew Gallatin abort_with_cmd_dma:
46756d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4676b2fc195eSAndrew Gallatin abort_with_mem_res:
4677b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4678b2fc195eSAndrew Gallatin abort_with_lock:
4679b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4680a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4681a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4682b2fc195eSAndrew Gallatin 	if_free(ifp);
4683b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4684b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4685b2fc195eSAndrew Gallatin 
4686b2fc195eSAndrew Gallatin abort_with_nothing:
4687b2fc195eSAndrew Gallatin 	return err;
4688b2fc195eSAndrew Gallatin }
4689b2fc195eSAndrew Gallatin 
4690b2fc195eSAndrew Gallatin static int
46916d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4692b2fc195eSAndrew Gallatin {
46936d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4694b2fc195eSAndrew Gallatin 
469537d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4696c792928fSAndrew Gallatin 		device_printf(sc->dev,
4697c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4698c792928fSAndrew Gallatin 		return EBUSY;
4699c792928fSAndrew Gallatin 	}
4700a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
47018c5d766cSAndrew Gallatin 	sc->dying = 1;
4702b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
47036d87a65dSAndrew Gallatin 		mxge_close(sc);
4704a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4705b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
4706e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4707dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4708091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
47091e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
47101e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4711a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
47121e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4713a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
47146d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
47156d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4716b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4717b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4718a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4719a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4720b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4721b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4722b2fc195eSAndrew Gallatin 	return 0;
4723b2fc195eSAndrew Gallatin }
4724b2fc195eSAndrew Gallatin 
4725b2fc195eSAndrew Gallatin static int
47266d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4727b2fc195eSAndrew Gallatin {
4728b2fc195eSAndrew Gallatin 	return 0;
4729b2fc195eSAndrew Gallatin }
4730b2fc195eSAndrew Gallatin 
4731b2fc195eSAndrew Gallatin /*
4732b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4733b2fc195eSAndrew Gallatin 
4734b2fc195eSAndrew Gallatin   Local Variables:
4735b2fc195eSAndrew Gallatin   c-file-style:"linux"
4736b2fc195eSAndrew Gallatin   tab-width:8
4737b2fc195eSAndrew Gallatin   End:
4738b2fc195eSAndrew Gallatin */
4739